GeneralizedGoertzel.java (3174B)
1 package be.tarsos.dsp.pitch; 2 3 import be.tarsos.dsp.AudioEvent; 4 import be.tarsos.dsp.AudioProcessor; 5 import be.tarsos.dsp.pitch.Goertzel.FrequenciesDetectedHandler; 6 import be.tarsos.dsp.util.Complex; 7 import be.tarsos.dsp.util.fft.HammingWindow; 8 import be.tarsos.dsp.util.fft.WindowFunction; 9 10 /** 11 * <a href="http://download.springer.com/static/pdf/14/art%253A10.1186%252F1687-6180-2012-56.pdf?auth66=1409747532_189c92c583694c81b3a0095e2f665c9e&ext=.pdf">Goertzel algorithm generalized to non-integer multiples of fundamental frequency</a> 12 * Petr Sysel and Pavel Rajmic 13 * 14 * 15 * @author Joren Six 16 * 17 */ 18 public class GeneralizedGoertzel implements AudioProcessor{ 19 20 /** 21 * A list of frequencies to detect. 22 */ 23 private final double[] frequenciesToDetect; 24 25 private final double[] indvec; 26 27 /** 28 * Cached cosine calculations for each frequency to detect. 29 */ 30 private final double[] precalculatedCosines; 31 /** 32 * Cached wnk calculations for each frequency to detect. 33 */ 34 private final double[] precalculatedWnk; 35 /** 36 * A calculated power for each frequency to detect. This array is reused for 37 * performance reasons. 38 */ 39 private final double[] calculatedPowers; 40 private final Complex[] calculatedComplex; 41 42 private final FrequenciesDetectedHandler handler; 43 44 45 46 public GeneralizedGoertzel(final float audioSampleRate, final int bufferSize, 47 double[] frequencies, FrequenciesDetectedHandler handler){ 48 frequenciesToDetect = frequencies; 49 50 indvec = new double[frequenciesToDetect.length]; 51 for (int j = 0; j < frequenciesToDetect.length; j++) { 52 indvec[j] = frequenciesToDetect[j]/(audioSampleRate/(float)bufferSize); 53 } 54 55 56 precalculatedCosines = new double[frequencies.length]; 57 precalculatedWnk = new double[frequencies.length]; 58 this.handler = handler; 59 60 calculatedPowers = new double[frequencies.length]; 61 calculatedComplex = new Complex[frequencies.length]; 62 63 for (int i = 0; i < frequenciesToDetect.length; i++) { 64 precalculatedCosines[i] = 2 * Math.cos(2 * Math.PI 65 * frequenciesToDetect[i] / audioSampleRate); 66 precalculatedWnk[i] = Math.exp(-2 * Math.PI 67 * frequenciesToDetect[i] / audioSampleRate); 68 } 69 70 } 71 72 @Override 73 public boolean process(AudioEvent audioEvent) { 74 75 float[] x = audioEvent.getFloatBuffer(); 76 WindowFunction f = new HammingWindow(); 77 f.apply(x); 78 for (int j = 0; j < frequenciesToDetect.length; j++) { 79 double pik_term = 2 * Math.PI * indvec[j]/(float) audioEvent.getBufferSize(); 80 double cos_pik_term2 = Math.cos(pik_term) * 2; 81 Complex cc = new Complex(0,-1*pik_term).exp(); 82 double s0=0; 83 double s1=0; 84 double s2=0; 85 86 for(int i = 0 ; i < audioEvent.getBufferSize() ; i++ ){ 87 s0 = x[i]+cos_pik_term2*s1-s2; 88 s2=s1; 89 s1=s0; 90 } 91 s0 = cos_pik_term2 * s1 - s2; 92 calculatedComplex[j] = cc.times(new Complex(-s1,0)).plus(new Complex(s0,0)); 93 calculatedPowers[j] = calculatedComplex[j].mod(); 94 } 95 96 handler.handleDetectedFrequencies(audioEvent.getTimeStamp(),frequenciesToDetect.clone(), calculatedPowers.clone(), 97 frequenciesToDetect.clone(), calculatedPowers.clone()); 98 99 return true; 100 } 101 102 103 @Override 104 public void processingFinished() { 105 106 } 107 108 }