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.analysis.FourierTransform; 038import org.openimaj.audio.processor.AudioProcessor; 039 040/** 041 * Naive band pass filter that uses the Fourier transform to filter 042 * unwanted frequencies. The high and low pass filter values will be 043 * rounded to the nearest bin in the frequency domain, so may not exactly 044 * match the specified output. If the high pass filter frequency is greater 045 * than the low pass filter frequency, then the class will output empty 046 * audio. 047 * <p> 048 * If you want to process the samples in the chain, override the 049 * {@link #processSamples(SampleChunk)} method. This method will be called 050 * even if the FFT fails and it will be called with the input samples. 051 * <p> 052 * The class makes the assumption that all channels have the same length 053 * of data. 054 * <p> 055 * Note that this class will produce inconsistencies at the frame 056 * boundaries, so cannot sensibly be used for filtering audio that is to 057 * be replayed. 058 * <p> 059 * Also check out the {@link EQFilter} which contains high and low pass filter 060 * implementations which can also provide band-pass filter functionality. 061 * 062 * @see EQFilter 063 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 064 * @created 11 Jul 2012 065 * @version $Author$, $Revision$, $Date$ 066 */ 067public class FFTBandPassFilter extends AudioProcessor 068{ 069 /** The Fourier transformer */ 070 private FourierTransform ft = null; 071 072 /** Hanning window audio processor */ 073 private HanningAudioProcessor hap = null; 074 075 /** The lowest frequency at which audio will pass */ 076 private int highPassHz = 500; 077 078 /** The highest frequency at which audio will pass */ 079 private int lowPassHz = 5000; 080 081 /** 082 * Chainable constructor 083 * 084 * @param as The audio stream to process 085 * @param highPassHz The frequency of the high pass filter. 086 * @param lowPassHz The frequency of the low pass filter. 087 */ 088 public FFTBandPassFilter( final AudioStream as, final int highPassHz, final int lowPassHz ) 089 { 090 super( as ); 091 this.ft = new FourierTransform(); 092 this.hap = new HanningAudioProcessor( 1024 ); 093 this.highPassHz = highPassHz; 094 this.lowPassHz = lowPassHz; 095 } 096 097 /** 098 * Default constructor 099 * 100 * @param highPassHz The frequency of the high pass filter 101 * @param lowPassHz The frequency of the low pass filter. 102 */ 103 public FFTBandPassFilter( final int highPassHz, final int lowPassHz ) 104 { 105 this.ft = new FourierTransform(); 106 this.hap = new HanningAudioProcessor( 1024 ); 107 this.highPassHz = highPassHz; 108 this.lowPassHz = lowPassHz; 109 } 110 111 /** 112 * {@inheritDoc} 113 * @see org.openimaj.audio.processor.AudioProcessor#process(org.openimaj.audio.SampleChunk) 114 */ 115 @Override 116 final public SampleChunk process( final SampleChunk sample ) throws Exception 117 { 118 // Perform an FFT and get the data. 119 this.ft.process( this.hap.process( sample ) ); 120 final float[][] transformedData = this.ft.getLastFFT(); 121 122 // Number of channels to process 123 final int nc = transformedData.length; 124 125 // If the FFT failed we'll not try to process anything 126 if( nc > 0 ) 127 { 128 // The size of each bin in Hz (using the first channel as examplar) 129 final double binSize = (sample.getFormat().getSampleRateKHz()*1000) 130 / (transformedData[0].length/2); 131 132 // Work out which bins we will wipe out 133 final int highPassBin = (int)Math.floor( this.highPassHz / binSize ); 134 final int lowPassBin = (int)Math.ceil( this.lowPassHz / binSize ); 135 136 // Loop through the channels. 137 for( int c = 0; c < nc; c++ ) 138 { 139 // Process the samples 140 for( int i = 0; i < transformedData[c].length; i++ ) 141 if( i < highPassBin || i > lowPassBin ) 142 transformedData[c][i] = 0; 143 } 144 145 // Do the inverse transform from frequency to time. 146 final SampleChunk s = FourierTransform.inverseTransform( 147 sample.getFormat(), transformedData ); 148 149 return this.processSamples( s ); 150 } 151 else return this.processSamples( sample ); 152 } 153 154 /** 155 * Process the band-filtered samples. 156 * @param sc The band-filtered samples 157 * @return Processed samples 158 */ 159 public SampleChunk processSamples( final SampleChunk sc ) 160 { 161 return sc; 162 } 163}