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.AudioFormat; 036 037/** 038 * A default non-isoceles triangular filter definition, where the low, mid 039 * and top frequencies are defined. 040 * 041 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 042 * @created 13 Dec 2012 043 * @version $Author$, $Revision$, $Date$ 044 */ 045public class TriangularFilter 046{ 047 /** The lowest frequency of the filter */ 048 protected double lowFrequency = 0; 049 050 /** The highest frequency of the filter */ 051 protected double highFrequency = 20000; 052 053 /** The centre (peak) frequency of the filter */ 054 protected double centreFrequency = 10000; 055 056 /** The height of the filter */ 057 protected double filterAmplitude; 058 059 /** The slope of first half of the filter. */ 060 protected double lowSlope; 061 062 /** The slope of the second half of the filter */ 063 protected double highSlope; 064 065 /** 066 * 067 * @param low 068 * @param centre 069 * @param high 070 */ 071 public TriangularFilter( final double low, final double centre, final double high ) 072 { 073 // Ensure the end frequency is greater than the start frequency 074 if( high <= low ) 075 throw new IllegalArgumentException( "Triangular Filter start and end " + 076 "frequencies are incorrect: "+high+" <= "+low ); 077 078 this.lowFrequency = low; 079 this.highFrequency = high; 080 this.centreFrequency = centre; 081 082 this.filterAmplitude = 2f / (this.highFrequency - this.lowFrequency); 083 this.lowSlope = this.filterAmplitude / (this.centreFrequency - this.lowFrequency); 084 this.highSlope = this.filterAmplitude / (this.highFrequency - this.centreFrequency); 085 086 } 087 088 /** 089 * Returns the lowest frequency of the filter 090 * @return The low frequency of the filter 091 */ 092 public double getLowFrequency() 093 { 094 return this.lowFrequency; 095 } 096 097 /** 098 * Sets the low frequency 099 * @param lowFrequency The new low frequency 100 */ 101 public void setLowFrequency( final double lowFrequency ) 102 { 103 this.lowFrequency = lowFrequency; 104 if( this.highFrequency <= this.lowFrequency ) 105 throw new IllegalArgumentException( "Triangular Filter start and end " + 106 "frequencies are incorrect: "+this.highFrequency+" <= "+this.lowFrequency ); 107 } 108 109 /** 110 * Gets the high frequency 111 * @return The high frequency 112 */ 113 public double getHighFrequency() 114 { 115 return this.highFrequency; 116 } 117 118 /** 119 * Set the high frequency of the filter 120 * @param highFrequency The new high frequency 121 */ 122 public void setHighFrequency( final double highFrequency ) 123 { 124 this.highFrequency = highFrequency; 125 if( this.highFrequency <= this.lowFrequency ) 126 throw new IllegalArgumentException( "Triangular Filter start and end " + 127 "frequencies are incorrect: "+highFrequency+" <= "+this.lowFrequency ); 128 } 129 130 /** 131 * Get the centre frequency 132 * @return The centre frequency 133 */ 134 public double getCentreFrequency() 135 { 136 return this.centreFrequency; 137 } 138 139 /** 140 * Set the new centre frequency 141 * @param centreFrequency The new centre frequency 142 */ 143 public void setCentreFrequency( final double centreFrequency ) 144 { 145 this.centreFrequency = centreFrequency; 146 if( this.centreFrequency <= this.lowFrequency || this.centreFrequency >= this.highFrequency ) 147 throw new IllegalArgumentException( "Triangular Filter start and end " + 148 "frequencies are incorrect: centre frequency "+centreFrequency ); 149 } 150 151 /** 152 * Requires a power spectrum and produces an output the filter 153 * 154 * @param frequencySpectrum The power spectrum 155 * @param format The format of the samples used to create the spectrum 156 * @return The output power for the filter 157 */ 158 public double process( final float[] frequencySpectrum, final AudioFormat format ) 159 { 160 double output = 0d; 161 162 // The size of each bin in Hz (using the first channel as examplar) 163 final double binSize = (format.getSampleRateKHz()*1000) 164 / (frequencySpectrum.length/2); 165 166 final int startBin = (int)(this.lowFrequency / binSize); 167 final int endBin = (int)(this.highFrequency / binSize); 168 169 // Now apply the filter to the spectrum and accumulate the output 170 for( int x = startBin; x < endBin; x++ ) 171 { 172 // Ensure we're within the bounds of the spectrum 173 if( x >= 0 && x < frequencySpectrum.length ) 174 { 175 final double binFreq = binSize * x; 176 final double weight = this.getWeightAt(binFreq); 177 output += weight * frequencySpectrum[x]; 178 } 179 } 180 181 return output; 182 } 183 184 /** 185 * Returns a set of values that represent the response of this filter 186 * when the linear frequency is split in the given number of bins. The 187 * result will have <code>nSpectrumBins</code> length. 188 * 189 * @param nSpectrumBins The number of bins in a spectrum. 190 * @param maxFreq The maximum frequency (sample rate) 191 * @return The response curve. 192 */ 193 public double[] getResponseCurve( final int nSpectrumBins, final double maxFreq ) 194 { 195 final double[] curve = new double[nSpectrumBins]; 196 final double binSize = maxFreq / nSpectrumBins; 197 198 for( int x = 0; x < nSpectrumBins; x++ ) 199 curve[x] = this.getWeightAt( binSize * x); 200 201 return curve; 202 } 203 204 /** 205 * Returns the weighting provided by this filter at the given frequency (Hz). 206 * @param frequency The frequency (Hz) to get the weight for 207 * @return The weight at the given frequency (Hz) for this filter 208 */ 209 public double getWeightAt( final double frequency ) 210 { 211 // Up or down slope depending on whether we're left or 212 // right of the centre frequency 213 double weight = 0; 214 if( frequency < this.centreFrequency ) 215 weight = this.filterAmplitude - this.lowSlope * (this.centreFrequency - frequency); 216 else weight = this.filterAmplitude - this.highSlope * (frequency - this.centreFrequency); 217 218 if( weight < 0 ) weight = 0; 219 220 return weight; 221 } 222 223 /** 224 * Returns the filter amplitude 225 * @return The filter amplitude 226 */ 227 public double getFilterAmplitude() 228 { 229 return this.filterAmplitude; 230 } 231 232 /** 233 * Set the filter amplitude 234 * @param fa The filter amplitude 235 */ 236 public void setFilterAmplitude( final double fa ) 237 { 238 this.filterAmplitude = fa; 239 this.lowSlope = this.filterAmplitude / (this.centreFrequency - this.lowFrequency); 240 this.highSlope = this.filterAmplitude / (this.highFrequency - this.centreFrequency); 241 } 242 243 /** 244 * {@inheritDoc} 245 * @see java.lang.Object#toString() 246 */ 247 @Override 248 public String toString() 249 { 250// return ""+this.centreFrequency; 251 return "tf{"+this.lowFrequency+"->"+this.centreFrequency+"->"+this.highFrequency+"}"; 252 } 253 254}