001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030/** 031 * 032 */ 033package org.openimaj.audio.filters; 034 035import org.openimaj.audio.AudioStream; 036import org.openimaj.audio.SampleChunk; 037import org.openimaj.audio.processor.FixedSizeSampleAudioProcessor; 038import org.openimaj.audio.samples.SampleBuffer; 039 040/** 041 * Applies a weighted window on top of the audio signal. 042 * Assumes that all incoming audio samples will be the same size (or smaller) 043 * than the initial sample. This is because the weights function is cached 044 * the first time that the function is called. 045 * <p> 046 * Just one method needs to be overridden in the implementing class and that 047 * is the method that generates the window of weights to be applied to the 048 * signal. The weighting is then applied automatically. The method will only 049 * be called once and lazily. 050 * 051 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 052 * @created 31 Oct 2011 053 */ 054public abstract class WeightedWindowedAudioProcessor 055 extends FixedSizeSampleAudioProcessor 056{ 057 /** A table of weights */ 058 protected double[] weightTable = null; 059 060 /** Whether to apply the weights to the incoming signal */ 061 protected boolean useWeights = true; 062 063 /** 064 * Default constructor for non chainable processing. 065 * @param sizeRequired Size of the window required 066 */ 067 public WeightedWindowedAudioProcessor( final int sizeRequired ) 068 { 069 super( sizeRequired ); 070 } 071 072 /** 073 * Chainable constructor. Takes the audio stream and the size required. 074 * 075 * @param stream The audio stream to process 076 * @param sizeRequired The size of window required. 077 */ 078 public WeightedWindowedAudioProcessor( final AudioStream stream, final int sizeRequired ) 079 { 080 super( stream, sizeRequired ); 081 } 082 083 /** 084 * Constructor that takes the size of the window and the number of samples 085 * overlap. 086 * 087 * @param nSamplesInWindow Samples in window 088 * @param nSamplesOverlap Samples in window overlap 089 */ 090 public WeightedWindowedAudioProcessor( final int nSamplesInWindow, 091 final int nSamplesOverlap ) 092 { 093 super( nSamplesInWindow, nSamplesOverlap ); 094 } 095 096 /** 097 * Chainable constructor that takes the size of the window and 098 * the number of samples overlap. 099 * 100 * @param as The chained audio stream 101 * @param nSamplesInWindow Samples in window 102 * @param nSamplesOverlap Samples in window overlap 103 */ 104 public WeightedWindowedAudioProcessor( final AudioStream as, 105 final int nSamplesInWindow, final int nSamplesOverlap ) 106 { 107 super( as, nSamplesInWindow, nSamplesOverlap ); 108 } 109 110 /** 111 * Generate a cache of the COS function used for the Hanning. 112 * @param sample An example of the sample that will have the Hanning 113 * function applied. 114 */ 115 private void generateWeightTableCache( final SampleChunk sample ) 116 { 117 this.generateWeightTableCache( 118 sample.getNumberOfSamples()/sample.getFormat().getNumChannels(), 119 sample.getFormat().getNumChannels() ); 120 } 121 122 /** 123 * Generate the cos table cache 124 * @param length The length of the window to generate (per channel) 125 * @param nc The number of channels for which to generate a weight table 126 */ 127 protected abstract void generateWeightTableCache( final int length, final int nc ); 128 129 /** 130 * Process the given sample chunk. Note that it is expected that the 131 * sample will be the correct length (as given in the constructor). If it 132 * is not, the window will not be applied correctly. 133 * 134 * {@inheritDoc} 135 * @see org.openimaj.audio.processor.AudioProcessor#process(org.openimaj.audio.SampleChunk) 136 */ 137 @Override 138 final public SampleChunk process( final SampleChunk sample ) 139 { 140 if( sample == null ) return null; 141 if( this.weightTable == null ) 142 this.generateWeightTableCache( sample ); 143 144 // Apply the Hanning weights 145 this.process( sample.getSampleBuffer() ); 146 147 return this.processSamples( sample ); 148 } 149 150 /** 151 * Process the given sample buffer with the Hanning window. 152 * @param b The sample buffer 153 * @return The sample buffer 154 */ 155 final public SampleBuffer process( final SampleBuffer b ) 156 { 157 final int nc = b.getFormat().getNumChannels(); 158 if( this.weightTable == null ) 159 this.generateWeightTableCache( b.size()/nc, nc ); 160 161 for( int c = 0; c < nc; c++ ) 162 { 163 for( int n = 0; n < b.size()/nc; n++ ) 164 { 165 final float x = b.get(n*nc+c); 166 float v = (float)(x * this.weightTable[n*nc+c]); 167 if( !this.useWeights ) v = x; 168 b.set( n*nc+c, v ); 169 } 170 } 171 172 return b; 173 } 174 175 /** 176 * Process the Hanning samples. 177 * 178 * @param sample The samples to process 179 * @return The processed samples 180 */ 181 public SampleChunk processSamples( final SampleChunk sample ) 182 { 183 return sample; 184 } 185 186 /** 187 * The sum of the Hanning window 188 * @param samples A representative sample 189 * @return The sum of the hanning window 190 */ 191 public double getWindowSum( final SampleChunk samples ) 192 { 193 return this.getWindowSum( 194 samples.getNumberOfSamples() / samples.getFormat().getNumChannels(), 195 samples.getFormat().getNumChannels() ); 196 } 197 198 /** 199 * Get the sum of the Hanning window for the given parameters 200 * @param length The length of the window 201 * @param numChannels The number of channels in the window 202 * @return The sum of the window 203 */ 204 public double getWindowSum( final int length, final int numChannels ) 205 { 206 if( this.weightTable == null ) 207 this.generateWeightTableCache( length, numChannels ); 208 209 double sum = 0; 210 for( int i = 0; i < this.weightTable.length; i += numChannels ) 211 sum += this.weightTable[i]; 212 return sum; 213 } 214 215 /** 216 * Get the weights used. 217 * @return The weights 218 */ 219 public double[] getWeights() 220 { 221 return this.weightTable; 222 } 223}