/* * _______ _____ _____ _____ * |__ __| | __ \ / ____| __ \ * | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | * | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ * | | (_| | | \__ \ (_) \__ \ |__| |____) | | * |_|\__,_|_| |___/\___/|___/_____/|_____/|_| * * ------------------------------------------------------------- * * 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]; } } }