/*
*
      
_______
                       
_____
   
_____ _____
  

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

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

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

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

*
                                                         

* -------------------------------------------------------------
*
* 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.util.fft;


/**
 
* Wrapper for calling a hopefully Fast Fourier transform. Makes it easy to
 
* switch FFT algorithm with minimal overhead.
 
* Support for window functions is also present.
 
*
 
* @author Joren Six
 
*/

public class FFT {

	
/**
	 
* Forward FFT.
	 
*/
	
private final FloatFFT fft;
	
private final WindowFunction windowFunction;
	
private final int fftSize;
	
private final float[] window;

	
public FFT(final int size) {
		
this(size,null);
	
}
	


	

	
/**
	 
* Create a new fft of the specified size. Apply the specified window on the samples before a forward transform.
 

	 
* arning: the window is not applied in reverse when a backwards transform is requested.
	 
* @param size The size of the fft.
	 
* @param windowFunction Apply the specified window on the samples before a forward transform.
 

	 
* arning: the window is not applied in reverse when a backwards transform is requested.
	 
*/

	
public FFT(final int size, final WindowFunction windowFunction){
		
fft = new FloatFFT(size);
		
fftSize = size;
		
this.windowFunction = windowFunction;
		
if(windowFunction==null)
			
window = null;
		
else
		   
window = windowFunction.generateCurve(size);
	
}

	
/**
	 
* Computes forward DFT.
	 
*
 

	 
* @param data
	 
*
            
data to transform.
	 
*/
	
public void forwardTransform(final float[] data) {
		
if(windowFunction!=null){
			
for(int i = 0 ; i < data.length ; i++){
				
data[i] = data[i] * window[i];
			
}
			
//windowFunction.apply(data);
		
}
		
fft.realForward(data);
	
}
	

	
public void complexForwardTransform(final float[] data) {
		
if(windowFunction!=null){
			
for(int i = 0 ; i < data.length ; i++){
				
data[i] = data[i] * window[i];
			
}
			
//windowFunction.apply(data);
		
}
		
fft.complexForward(data);
	
}

	
/**
	 
* Computes inverse DFT.
	 
* Warning, does not reverse the window function.
	 
* @param data
	 
*
            
data to transform
	 
*/

	
public void backwardsTransform(final float[] data) {
		
fft.realInverse(data, true);
	
}

	
public double binToHz(final int binIndex, final float sampleRate) {
		
return binIndex * sampleRate / (double) fftSize;
	
}
	

	
public int size(){
		
return fftSize;
	
}

	
/**
	 
* Returns the modulus of the element at index bufferCount. The modulus,
	 
* magnitude or absolute value is (a²+b²) ^ 0.5 with a being the real part
	 
* and b the imaginary part of a complex number.
	 
*
 

	 
* @param data
	 
*
            
The FFT transformed data.
	 
* @param index
	 
*
            
The index of the element.
	 
* @return The modulus, magnitude or absolute value of the element at index
	 
*
         
bufferCount
	 
*/

	
public float modulus(final float[] data, final int index) {
		
final int realIndex = 2 * index;
		
final int imgIndex =
  
2 * index + 1;
		
final float modulus = data[realIndex] * data[realIndex] + data[imgIndex] * data[imgIndex];
		
return (float) Math.sqrt(modulus);
	
}

	
/**
	 
* Calculates the the modulus for each element in data and stores the result
	 
* in amplitudes.
	 
*
 

	 
* @param data
	 
*
            
The input data.
	 
* @param amplitudes
	 
*
            
The output modulus info or amplitude.
	 
*/

	
public void modulus(final float[] data, final float[] amplitudes) {
		
assert data.length / 2 == amplitudes.length;
		
for (int i = 0; i < amplitudes.length; i++) {
			
amplitudes[i] = modulus(data, i);
		
}
	
}
	

	
/**
	 
* Computes an FFT and converts the results to polar coordinates (power and
	 
* phase). Both the power and phase arrays must be the same length, data
	 
* should be double the length.
	 
*
 

	 
* @param data
	 
*
            
The input audio signal.
	 
* @param power
	 
*
            
The power (modulus) of the data.
	 
* @param phase
	 
*
            
The phase of the data
	 
*/

	
public void powerPhaseFFT(float[] data,float[] power, float[] phase) {
		
assert data.length / 2 == power.length;
		
assert data.length / 2 == phase.length;
		
if(windowFunction!=null){
			
windowFunction.apply(data);
		
}
		
fft.realForward(data);
		
powerAndPhaseFromFFT(data, power, phase);
	
}
	

	

	
/**
	 
* Returns magnitude (or power) and phase for the FFT transformed data.
 

	 
* @param data The FFT transformed data.
	 
* @param power The array where the magnitudes or powers are going to be stored. It is half the length of data (FFT size).
	 
* @param phase The array where the phases are going to be stored. It is half the length of data (FFT size).
	 
*/

	
public void powerAndPhaseFromFFT(float[] data,float[] power, float[] phase){
		
phase[0] = (float) Math.PI;
		
power[0] = -data[0];
		
for (int i = 1; i < power.length; i++) {
			
int realIndex = 2 * i;
			
int imgIndex
  
= 2 * i + 1;
			
power[i] = (float) Math.sqrt(data[realIndex] * data[realIndex] + data[imgIndex] * data[imgIndex]);
			
phase[i] = (float) Math.atan2(data[imgIndex], data[realIndex]);
		
}
	
}
	

	
public void powerPhaseFFTBeatRootOnset(float[] data,float[] power, float[] phase) {
		
powerPhaseFFT(data, power, phase);
		
power[0] = (float) Math.sqrt(data[0] * data[0] + data[1] * data[1]);
	
}
	

	
/**
	 
* Multiplies to arrays containing imaginary numbers. The data in the first argument
	 
* is modified! The real part is stored at <code>2*i</code>, the imaginary part <code>2*i+i</code>
	 
* @param data The array with imaginary numbers that is modified.
	 
* @param other The array with imaginary numbers that is not modified.
	 
* Data and other need to be the same length.
	 
*/

	
public void multiply(float[] data, float[] other){
		
assert data.length == other.length;
		
if(data.length!=other.length){
			
throw new IllegalArgumentException("Both arrays with imaginary numbers shouldb e of equal length");
		
}
		
for (int i = 1; i < data.length-1; i+=2) {
			
int realIndex = i;
			
int imgIndex = i + 1;
			
float tempReal = data[realIndex] * other[realIndex] + -1 * data[imgIndex] * other[imgIndex];
			
float tempImg = data[realIndex] * other[imgIndex] + data[imgIndex] * other[realIndex];
			
data[realIndex] = tempReal;
			
data[imgIndex] = tempImg;
			
//fix by perfecthu
 

			
//data[realIndex] = data[realIndex] * other[realIndex] + -1 * data[imgIndex] * other[imgIndex];
			
//data[imgIndex] = data[realIndex] * other[imgIndex] + data[imgIndex] * other[realIndex];
			
}
	
}
}