├── demo.cpp ├── fft.hpp └── readme.md /demo.cpp: -------------------------------------------------------------------------------- 1 | #include "fft.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | double lerp(double a, double b, double i) 9 | { 10 | return a*(1-i) + b*i; 11 | } 12 | 13 | enum { 14 | TEST_IMPULSE 15 | ,TEST_DC 16 | ,TEST_NYQUIST 17 | ,TEST_HALF 18 | ,TEST_SQUARE 19 | ,TEST_SQUAREDOUBLE 20 | ,TEST_SAW 21 | ,TEST_NOISE 22 | ,TEST_COMPLEXIMPULSE 23 | ,TEST_COMPLEXDC 24 | ,TEST_COMPLEXNOISE 25 | ,TEST_BLANK 26 | }; 27 | 28 | #define TEST TEST_IMPULSE 29 | 30 | void init(double* in_real, double* in_imag, uint64_t size) 31 | { 32 | for(uint64_t i = 0; i < size; i++) 33 | { 34 | switch(TEST) 35 | { 36 | case TEST_IMPULSE: 37 | in_real[i] = 0; 38 | in_imag[i] = 0; 39 | in_real[0] = 1; 40 | break; 41 | case TEST_DC: 42 | in_real[i] = 1; 43 | in_imag[i] = 0; 44 | break; 45 | case TEST_NYQUIST: 46 | in_real[i] = cos(M_PI*i); 47 | in_imag[i] = 0; 48 | break; 49 | case TEST_HALF: 50 | in_real[i] = cos(M_PI*i/2); 51 | in_imag[i] = 0; 52 | break; 53 | case TEST_SQUARE: 54 | in_real[i] = (i TEST_COMPLEXNOISE); 111 | 112 | puts("transform"); 113 | puts("bin\treal\timag\tmag"); 114 | for(int i = 0; i < SIZE; i++) 115 | { 116 | // boost nonunique frequencies for real inputs on display 117 | double factor = (i>0 and i= SIZE/2 and signal_was_real) 121 | break; 122 | } 123 | puts(""); 124 | 125 | ifft(out_real, out_imag, SIZE, in_real, in_imag); 126 | 127 | puts("inverse"); 128 | puts("sample\treal\timag\tmag"); 129 | for(int i = 0; i < SIZE; i++) 130 | printf("%d\t%.2f\t%.2f\t%.2f \n", i, in_real[i], in_imag[i], sqrt(in_real[i]*in_real[i]+in_imag[i]*in_imag[i])); 131 | puts(""); 132 | } 133 | -------------------------------------------------------------------------------- /fft.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FFT_SINGLEHEADER_INCLUDED 2 | #define FFT_SINGLEHEADER_INCLUDED 3 | 4 | /* fft.hpp ========================= 5 | Public-domain single-header library 6 | implementing radix-2 decimation-in-time FFT (i.e. FFT for powers of 2) 7 | 8 | This software is dual-licensed to the public domain and under the following 9 | license: you are granted a perpetual, irrevocable license to copy, modify, 10 | publish, and distribute this file as you see fit. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 13 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 14 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 15 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 16 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 17 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 18 | THIS SOFTWARE. 19 | */ 20 | 21 | 22 | /* 23 | fft_core 24 | (in_real[], in_imag[], size, gap, out_real[], out_imag[], forwards) 25 | in_real: pointer to real-valued spatial samples (for audio, this is where your entire audio signal goes) 26 | in_imag: pointer to imaginary-valued ones (not useful for audio) 27 | in_imag is allowed to be nullptr. If so, it will be treated as if it were all zeroes. 28 | size: number of complex samples per domain. for audio, this is the number of real samples you have. must be a power of 2. Algorithm will definitely fail and possibly crash otherwise, not tested. 29 | gap: must be 1 for outside callers. used for recursion. 30 | out_real: pointer to space for real-valued output. does not need to be initialized. must be allocated. 31 | out_imag: same as above, for imaginary. not optional. 32 | out_real and out_imag work together to store a complex number (2d vector) representing the phase and amplitude of the given frequency band, even for wholly real inputs. 33 | forwards: if true, transform is forwards (fft). if false, transform is backwards (ifft). 34 | fft 35 | () 36 | compute forwards fft. 37 | ifft 38 | () 39 | compute backwards fft (inverse fft, ifft) 40 | normalize_fft 41 | (in_real[], in_imag[], size) 42 | divide the amplitude of each bin by the number of bins. obligatory after fft() for audio. modifies the input. 43 | sanitize_fft 44 | (in_real[], in_imag[], size) 45 | moves all data to positive-frequency bins. yes, FFTs have negative frequencies for some reason. they're used to retain correlation data for complex inputs. for real inputs, the negative frequencies just mirror the positive ones and sap half their amplitude, therefore this function. for an explanation of what negative frequencies mean, see http://dsp.stackexchange.com/questions/431/what-is-the-physical-significance-of-negative-frequencies . 46 | unsanitize_fft 47 | (in_real[], in_imag[], size) 48 | undo the above. note again that these two fuctions are not sensical for complex inputs. 49 | */ 50 | 51 | #include 52 | #include 53 | #include 54 | #ifndef M_PI 55 | #define M_PI 3.14159265358979323846 56 | #endif // ifndef M_PI 57 | 58 | // address of cell if base adderss not nullptr, nullptr otherwise 59 | #define fft_private_safe_addrof(ptr,i) ((ptr!=nullptr)?(&(ptr[i])):(nullptr)) 60 | 61 | // For a 8-sample input, the FFT's last three bins contain "negative" frequencies. (So, the last (size/2)-1 bins.) They are only meaningful for complex inputs. 62 | void fft_core(double* input_real, double* input_imag, uint64_t size, uint64_t gap, double* output_real, double* output_imag, bool forwards) 63 | { 64 | if(size == 1) 65 | { 66 | output_real[0] = input_real[0]; 67 | if(input_imag != nullptr) 68 | output_imag[0] = input_imag[0]; 69 | else 70 | output_imag[0] = 0; 71 | } 72 | else 73 | { 74 | // This algorithm works by extending the concept of how two-bin DFTs (discrete fourier transform) work, in order to correlate decimated DFTs, recursively. 75 | // No, I'm not your guy if you want a proof of why it works, but it does. 76 | fft_core(input_real , input_imag , size/2, gap*2, output_real , output_imag , forwards); 77 | fft_core(&(input_real[gap]), fft_private_safe_addrof(input_imag,gap), size/2, gap*2, &(output_real[size/2]), &(output_imag[size/2]), forwards); 78 | // non-combed decimated output to non-combed correlated output 79 | for(uint64_t i = 0; i < size/2; i++) 80 | { 81 | double a_real = output_real[i]; 82 | double a_imag = output_imag[i]; 83 | double b_real = output_real[i+size/2]; 84 | double b_imag = output_imag[i+size/2]; 85 | 86 | double twiddle_real = cos(2*M_PI*i/size); 87 | double twiddle_imag = sin(2*M_PI*i/size)*(forwards?-1:1); 88 | // complex multiplication (vector angle summing and length multiplication) 89 | double bias_real = b_real*twiddle_real - b_imag*twiddle_imag; 90 | double bias_imag = b_imag*twiddle_real + b_real*twiddle_imag; 91 | // real output (sum of real parts) 92 | output_real[i ] = a_real + bias_real; 93 | output_real[i+size/2] = a_real - bias_real; 94 | // imag output (sum of imaginary parts) 95 | output_imag[i ] = a_imag + bias_imag; 96 | output_imag[i+size/2] = a_imag - bias_imag; 97 | } 98 | } 99 | } 100 | 101 | #undef fft_private_safe_addrof 102 | 103 | #ifndef FFT_CORE_ONLY 104 | 105 | void normalize_fft(double* input_real, double* input_imag, uint64_t size) 106 | { 107 | for(uint64_t i = 0; i < size; i++) 108 | { 109 | input_real[i] /= size; 110 | input_imag[i] /= size; 111 | } 112 | } 113 | void half_normalize_fft(double* input_real, double* input_imag, uint64_t size) 114 | { 115 | for(uint64_t i = 0; i < size; i++) 116 | { 117 | input_real[i] /= sqrt(size); 118 | input_imag[i] /= sqrt(size); 119 | } 120 | } 121 | void fft(double* input_real, double* input_imag, uint64_t size, double* output_real, double* output_imag) 122 | { 123 | fft_core(input_real, input_imag, size, 1, output_real, output_imag, 1); 124 | half_normalize_fft(output_real, output_imag, size); // allows calling fft() four times to result in the original signal with no amplitude change 125 | } 126 | void ifft(double* input_real, double* input_imag, uint64_t size, double* output_real, double* output_imag) 127 | { 128 | fft_core(input_real, input_imag, size, 1, output_real, output_imag, 0); 129 | half_normalize_fft(output_real, output_imag, size); // see above, also causes ifft(fft(x)) to result in the original signal with no amplitude change 130 | } 131 | 132 | // boost bins that are split into positive (A-handed spin) and negative (B-handed spin) parts 133 | // only useful if former input signal was not complex, for only needing to look at one bin to get the magnitude 134 | // FIXME or HELPME: How come the nyquist frequency is quiet in saw waves, but loud in pure signal? 135 | void sanitize_fft(double* input_real, double* input_imag, uint64_t size) 136 | { 137 | for(uint64_t i = 1; i < size/2; i++) 138 | { 139 | input_real[i] *= 2; 140 | input_imag[i] *= 2; 141 | input_real[size-i] *= 2; 142 | input_imag[size-i] *= 2; 143 | } 144 | } 145 | // opposite of above 146 | void unsanitize_fft(double* input_real, double* input_imag, uint64_t size) 147 | { 148 | for(uint64_t i = 1; i < size/2; i++) 149 | { 150 | input_real[i] /= 2; 151 | input_imag[i] /= 2; 152 | input_real[size-i] /= 2; 153 | input_imag[size-i] /= 2; 154 | } 155 | } 156 | 157 | #endif // ifndef FFT_CORE_ONLY 158 | 159 | #endif // FFT_SINGLEHEADER_INCLUDED 160 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # fft.hpp 2 | 3 | Public-domain single-header library implementing radix-2 decimation-in-time FFT (i.e. FFT for powers of 2) 4 | 5 | This software is dual-licensed to the public domain and under the following 6 | license: you are granted a perpetual, irrevocable license to copy, modify, 7 | publish, and distribute this file as you see fit. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 15 | THIS SOFTWARE. 16 | 17 | ## fft_core 18 | 19 | (in_real[], in_imag[], size, gap, out_real[], out_imag[], forwards) 20 | 21 | * in_real: pointer to real-valued spatial samples (for audio, this is where your entire audio signal goes) 22 | * in_imag: pointer to imaginary-valued ones (not useful for audio) 23 | * in_imag is allowed to be nullptr. If so, it will be treated as if it were all zeroes. 24 | * size: number of complex samples per domain. for audio, this is the number of real samples you have. must be a power of 2. Algorithm will definitely fail and possibly crash otherwise, not tested. 25 | * gap: must be 1 for outside callers. used for recursion. 26 | * out_real: pointer to space for real-valued output. does not need to be initialized. must be allocated. 27 | * out_imag: same as above, for imaginary. not optional. 28 | * out_real and out_imag work together to store a complex number (2d vector) representing the phase and amplitude of the given frequency band, even for wholly real inputs. 29 | * forwards: if true, transform is forwards (fft). if false, transform is backwards (ifft). 30 | 31 | For a 8-sample input, the FFT's last three bins contain "negative" frequencies. (So, the last (size/2)-1 bins.) They are only meaningful for complex inputs. 32 | 33 | Only does the hard part, which means that the output will be louder if you keep applying this recursively. 34 | 35 | ## fft 36 | 37 | (\) 38 | 39 | * compute forwards fft, normalized by 1/sqrt(size) 40 | 41 | ## ifft 42 | 43 | (\) 44 | 45 | * above (including normalization) but inverse fft 46 | 47 | ## normalize_fft 48 | 49 | (in_real[], in_imag[], size) 50 | 51 | * divide the amplitude of each bin by the number of bins. modifies the input. only needed if you're using fft_core. 52 | 53 | ## half_normalize_fft 54 | 55 | (in_real[], in_imag[], size) 56 | 57 | * above, but uses the square root of the number of bins. done by default for fft() and ifft(), only needed if you're using fft_core. 58 | 59 | You may want to normalize by sqrt(size), but if so, that's up to you. Just copy the code or make your own. 60 | 61 | ## sanitize_fft 62 | 63 | (in_real[], in_imag[], size) 64 | 65 | * moves all data to positive-frequency bins. yes, FFTs have negative frequencies for some reason. they're used to retain correlation data for complex inputs. for real inputs, the negative frequencies just mirror the positive ones and sap half their amplitude, therefore this function. for an explanation of what negative frequencies mean, see http://dsp.stackexchange.com/questions/431/what-is-the-physical-significance-of-negative-frequencies . 66 | 67 | ## unsanitize_fft 68 | 69 | (in_real[], in_imag[], size) 70 | 71 | * undo the above. note again that these two fuctions are not sensical for complex inputs. 72 | --------------------------------------------------------------------------------