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}