plectrum

Plectrum: instrument tuner for Android
Log | Files | Refs | README | LICENSE

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 }