FFT.java (6946B)
1 /* 2 * _______ _____ _____ _____ 3 * |__ __| | __ \ / ____| __ \ 4 * | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | 5 * | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ 6 * | | (_| | | \__ \ (_) \__ \ |__| |____) | | 7 * |_|\__,_|_| |___/\___/|___/_____/|_____/|_| 8 * 9 * ------------------------------------------------------------- 10 * 11 * TarsosDSP is developed by Joren Six at IPEM, University Ghent 12 * 13 * ------------------------------------------------------------- 14 * 15 * Info: http://0110.be/tag/TarsosDSP 16 * Github: https://github.com/JorenSix/TarsosDSP 17 * Releases: http://0110.be/releases/TarsosDSP/ 18 * 19 * TarsosDSP includes modified source code by various authors, 20 * for credits and info, see README. 21 * 22 */ 23 24 25 package be.tarsos.dsp.util.fft; 26 27 28 /** 29 * Wrapper for calling a hopefully Fast Fourier transform. Makes it easy to 30 * switch FFT algorithm with minimal overhead. 31 * Support for window functions is also present. 32 * 33 * @author Joren Six 34 */ 35 public class FFT { 36 37 /** 38 * Forward FFT. 39 */ 40 private final FloatFFT fft; 41 private final WindowFunction windowFunction; 42 private final int fftSize; 43 private final float[] window; 44 45 public FFT(final int size) { 46 this(size,null); 47 } 48 49 50 51 /** 52 * Create a new fft of the specified size. Apply the specified window on the samples before a forward transform. 53 * arning: the window is not applied in reverse when a backwards transform is requested. 54 * @param size The size of the fft. 55 * @param windowFunction Apply the specified window on the samples before a forward transform. 56 * arning: the window is not applied in reverse when a backwards transform is requested. 57 */ 58 public FFT(final int size, final WindowFunction windowFunction){ 59 fft = new FloatFFT(size); 60 fftSize = size; 61 this.windowFunction = windowFunction; 62 if(windowFunction==null) 63 window = null; 64 else 65 window = windowFunction.generateCurve(size); 66 } 67 68 /** 69 * Computes forward DFT. 70 * 71 * @param data 72 * data to transform. 73 */ 74 public void forwardTransform(final float[] data) { 75 if(windowFunction!=null){ 76 for(int i = 0 ; i < data.length ; i++){ 77 data[i] = data[i] * window[i]; 78 } 79 //windowFunction.apply(data); 80 } 81 fft.realForward(data); 82 } 83 84 public void complexForwardTransform(final float[] data) { 85 if(windowFunction!=null){ 86 for(int i = 0 ; i < data.length ; i++){ 87 data[i] = data[i] * window[i]; 88 } 89 //windowFunction.apply(data); 90 } 91 fft.complexForward(data); 92 } 93 94 /** 95 * Computes inverse DFT. 96 * Warning, does not reverse the window function. 97 * @param data 98 * data to transform 99 */ 100 public void backwardsTransform(final float[] data) { 101 fft.realInverse(data, true); 102 } 103 104 public double binToHz(final int binIndex, final float sampleRate) { 105 return binIndex * sampleRate / (double) fftSize; 106 } 107 108 public int size(){ 109 return fftSize; 110 } 111 112 /** 113 * Returns the modulus of the element at index bufferCount. The modulus, 114 * magnitude or absolute value is (a²+b²) ^ 0.5 with a being the real part 115 * and b the imaginary part of a complex number. 116 * 117 * @param data 118 * The FFT transformed data. 119 * @param index 120 * The index of the element. 121 * @return The modulus, magnitude or absolute value of the element at index 122 * bufferCount 123 */ 124 public float modulus(final float[] data, final int index) { 125 final int realIndex = 2 * index; 126 final int imgIndex = 2 * index + 1; 127 final float modulus = data[realIndex] * data[realIndex] + data[imgIndex] * data[imgIndex]; 128 return (float) Math.sqrt(modulus); 129 } 130 131 /** 132 * Calculates the the modulus for each element in data and stores the result 133 * in amplitudes. 134 * 135 * @param data 136 * The input data. 137 * @param amplitudes 138 * The output modulus info or amplitude. 139 */ 140 public void modulus(final float[] data, final float[] amplitudes) { 141 assert data.length / 2 == amplitudes.length; 142 for (int i = 0; i < amplitudes.length; i++) { 143 amplitudes[i] = modulus(data, i); 144 } 145 } 146 147 /** 148 * Computes an FFT and converts the results to polar coordinates (power and 149 * phase). Both the power and phase arrays must be the same length, data 150 * should be double the length. 151 * 152 * @param data 153 * The input audio signal. 154 * @param power 155 * The power (modulus) of the data. 156 * @param phase 157 * The phase of the data 158 */ 159 public void powerPhaseFFT(float[] data,float[] power, float[] phase) { 160 assert data.length / 2 == power.length; 161 assert data.length / 2 == phase.length; 162 if(windowFunction!=null){ 163 windowFunction.apply(data); 164 } 165 fft.realForward(data); 166 powerAndPhaseFromFFT(data, power, phase); 167 } 168 169 170 /** 171 * Returns magnitude (or power) and phase for the FFT transformed data. 172 * @param data The FFT transformed data. 173 * @param power The array where the magnitudes or powers are going to be stored. It is half the length of data (FFT size). 174 * @param phase The array where the phases are going to be stored. It is half the length of data (FFT size). 175 */ 176 public void powerAndPhaseFromFFT(float[] data,float[] power, float[] phase){ 177 phase[0] = (float) Math.PI; 178 power[0] = -data[0]; 179 for (int i = 1; i < power.length; i++) { 180 int realIndex = 2 * i; 181 int imgIndex = 2 * i + 1; 182 power[i] = (float) Math.sqrt(data[realIndex] * data[realIndex] + data[imgIndex] * data[imgIndex]); 183 phase[i] = (float) Math.atan2(data[imgIndex], data[realIndex]); 184 } 185 } 186 187 public void powerPhaseFFTBeatRootOnset(float[] data,float[] power, float[] phase) { 188 powerPhaseFFT(data, power, phase); 189 power[0] = (float) Math.sqrt(data[0] * data[0] + data[1] * data[1]); 190 } 191 192 /** 193 * Multiplies to arrays containing imaginary numbers. The data in the first argument 194 * is modified! The real part is stored at <code>2*i</code>, the imaginary part <code>2*i+i</code> 195 * @param data The array with imaginary numbers that is modified. 196 * @param other The array with imaginary numbers that is not modified. 197 * Data and other need to be the same length. 198 */ 199 public void multiply(float[] data, float[] other){ 200 assert data.length == other.length; 201 if(data.length!=other.length){ 202 throw new IllegalArgumentException("Both arrays with imaginary numbers shouldb e of equal length"); 203 } 204 for (int i = 1; i < data.length-1; i+=2) { 205 int realIndex = i; 206 int imgIndex = i + 1; 207 float tempReal = data[realIndex] * other[realIndex] + -1 * data[imgIndex] * other[imgIndex]; 208 float tempImg = data[realIndex] * other[imgIndex] + data[imgIndex] * other[realIndex]; 209 data[realIndex] = tempReal; 210 data[imgIndex] = tempImg; 211 //fix by perfecthu 212 //data[realIndex] = data[realIndex] * other[realIndex] + -1 * data[imgIndex] * other[imgIndex]; 213 //data[imgIndex] = data[realIndex] * other[imgIndex] + data[imgIndex] * other[realIndex]; 214 } 215 } 216 }