/*
*
      
_______
                       
_____
   
_____ _____
  

*
     
|__
   
__|
                     
|
  
__ \ / ____|__ \
 

*
        
| | __ _ _ __ ___
  
______| || | (___ | |__) |
*
        
| |/ _` | '__/ __|/ _ \/ __| |
  
| |\___ \|___/
 

*
        
| | (_| | |
  
\__ \ (_) \__ \ |__| |____) | |
     

*
        
|_|\__,_|_|
  
|___/\___/|___/_____/|_____/|_|
     

*
                                                         

* -------------------------------------------------------------
*
* TarsosDSP is developed by Joren Six at IPEM, University Ghent
*
  

* -------------------------------------------------------------
*
*
  
Info:
 
http://0110.be/tag/TarsosDSP
*
  
Github:
 
https://github.com/JorenSix/TarsosDSP
*
  
Releases:
 
http://0110.be/releases/TarsosDSP/
*
  

*
  
TarsosDSP includes modified source code by various authors,
*
  
for credits and info, see README.
*
 

*/


package be.tarsos.dsp.pitch;

import be.tarsos.dsp.AudioEvent;
import be.tarsos.dsp.AudioProcessor;

/**
 
* Contains an implementation of the Goertzel algorithm. It can be used to
 
* detect if one or more predefined frequencies are present in a signal. E.g. to
 
* do DTMF decoding.
 
*
 
* @author Joren Six
 
*/

public class Goertzel implements AudioProcessor {

	
/**
	 
* If the power in dB is higher than this threshold, the frequency is
	 
* present in the signal.
	 
*/

	
private static final double POWER_THRESHOLD = 35;// in dB

	
/**
	 
* A list of frequencies to detect.
	 
*/
	
private final double[] frequenciesToDetect;
	
/**
	 
* Cached cosine calculations for each frequency to detect.
	 
*/

	
private final double[] precalculatedCosines;
	
/**
	 
* Cached wnk calculations for each frequency to detect.
	 
*/
	
private final double[] precalculatedWnk;
	
/**
	 
* A calculated power for each frequency to detect. This array is reused for
	 
* performance reasons.
	 
*/

	
private final double[] calculatedPowers;

	
private final FrequenciesDetectedHandler handler;

	
public Goertzel(final float audioSampleRate, final int bufferSize,
			
double[] frequencies, FrequenciesDetectedHandler handler) {

		
frequenciesToDetect = frequencies;
		
precalculatedCosines = new double[frequencies.length];
		
precalculatedWnk = new double[frequencies.length];
		
this.handler = handler;

		
calculatedPowers = new double[frequencies.length];

		
for (int i = 0; i < frequenciesToDetect.length; i++) {
			
precalculatedCosines[i] = 2 * Math.cos(2 * Math.PI
					
* frequenciesToDetect[i] / audioSampleRate);
			
precalculatedWnk[i] = Math.exp(-2 * Math.PI
					
* frequenciesToDetect[i] / audioSampleRate);
		
}
	
}

	
/**
	 
* An interface used to react on detected frequencies.
	 
*
 

	 
* @author Joren Six
	 
*/

	
public static interface FrequenciesDetectedHandler {
		
/**
		 
* React on detected frequencies.
		 
*
 

		 
* @param frequencies
		 
*
            
A list of detected frequencies.
		 
* @param powers
		 
*
            
A list of powers of the detected frequencies.
		 
* @param allFrequencies
		 
*
            
A list of all frequencies that were checked.
		 
* @param allPowers
		 
*
            
A list of powers of all frequencies that were checked.
		 
*/

		
void handleDetectedFrequencies(final double timestamp,final double[] frequencies,
				
final double[] powers, final double[] allFrequencies,
				
final double allPowers[]);
	
}

	
@Override
	
public boolean process(AudioEvent audioEvent) {
		
float[] audioFloatBuffer = audioEvent.getFloatBuffer();
		
double skn0, skn1, skn2;
		
int numberOfDetectedFrequencies = 0;
		
for (int j = 0; j < frequenciesToDetect.length; j++) {
			
skn0 = skn1 = skn2 = 0;
			
for (int i = 0; i < audioFloatBuffer.length; i++) {
				
skn2 = skn1;
				
skn1 = skn0;
				
skn0 = precalculatedCosines[j] * skn1 - skn2
						
+ audioFloatBuffer[i];
			
}
			
double wnk = precalculatedWnk[j];
			
calculatedPowers[j] = 20 * Math.log10(Math.abs(skn0 - wnk * skn1));
			
if (calculatedPowers[j] > POWER_THRESHOLD) {
				
numberOfDetectedFrequencies++;
			
}
		
}

		
if (numberOfDetectedFrequencies > 0) {
			
double[] frequencies = new double[numberOfDetectedFrequencies];
			
double[] powers = new double[numberOfDetectedFrequencies];
			
int index = 0;
			
for (int j = 0; j < frequenciesToDetect.length; j++) {
				
if (calculatedPowers[j] > POWER_THRESHOLD) {
					
frequencies[index] = frequenciesToDetect[j];
					
powers[index] = calculatedPowers[j];
					
index++;
				
}
			
}
			
handler.handleDetectedFrequencies(audioEvent.getTimeStamp(),frequencies, powers,
					
frequenciesToDetect.clone(), calculatedPowers.clone());
		
}

		
return true;
	
}



	
@Override
	
public void processingFinished() {
	
}

}