diff --git a/src/fft.cpp b/src/fft.cpp index 441604b..8df5204 100644 --- a/src/fft.cpp +++ b/src/fft.cpp @@ -1,78 +1,28 @@ #include "fft.h" #include -static float window[FFT_SIZE]; +constexpr float PI = 3.14159; -constexpr float M_PI = 3.14159; +// In-place FFT on array of Complex numbers +void fft(Complex* x, int N, Complex* buffer) { + if (N <= 1) return; + Complex* even = buffer; + Complex* odd = buffer + N / 2; -// Call once before use -void init_hamming_window() { - for (int i = 0; i < FFT_SIZE; i++) { - window[i] = 0.54f - 0.46f * cosf(2.0f * (float)M_PI * i / (FFT_SIZE - 1)); - } -} - -static unsigned int bit_reverse(unsigned int x, int log2n) { - unsigned int n = 0; - for (int i = 0; i < log2n; i++) { - n <<= 1; - n |= (x & 1); - x >>= 1; - } - return n; -} - -void compute_fft(float* time_data, float* freq_out) { - static float real[FFT_SIZE]; - static float imag[FFT_SIZE]; - - int log2n = 0; - for (int t = FFT_SIZE; t > 1; t >>= 1) ++log2n; - - // Apply Hamming window - for (int i = 0; i < FFT_SIZE; i++) { - real[i] = time_data[i] * window[i]; - imag[i] = 0.0f; + for (int i = 0; i < N / 2; ++i) { + even[i] = x[i * 2]; + odd[i] = x[i * 2 + 1]; } - // Bit reversal - for (int i = 0; i < FFT_SIZE; ++i) { - int j = bit_reverse(i, log2n); - if (j > i) { - float tmp_re = real[i], tmp_im = imag[i]; - real[i] = real[j]; imag[i] = imag[j]; - real[j] = tmp_re; imag[j] = tmp_im; - } + fft(even, N / 2, buffer + N); // deeper even + fft(odd, N / 2, buffer + N + N / 2); // deeper odd + + for (int k = 0; k < N / 2; ++k) { + double angle = -2 * PI * k / N; + Complex twiddle(cos(angle), sin(angle)); + Complex t = twiddle * odd[k]; + x[k] = even[k] + t; + x[k + N / 2] = even[k] - t; } - - // Cooley-Tukey FFT - for (int s = 1; s <= log2n; ++s) { - int m = 1 << s; - for (int k = 0; k < FFT_SIZE; k += m) { - for (int j = 0; j < m / 2; ++j) { - int t = k + j; - int u = t + m / 2; - - float angle = -2.0f * (float)M_PI * j / m; - float w_real = cosf(angle); - float w_imag = sinf(angle); - - float re = w_real * real[u] - w_imag * imag[u]; - float im = w_real * imag[u] + w_imag * real[u]; - - real[u] = real[t] - re; - imag[u] = imag[t] - im; - real[t] += re; - imag[t] += im; - } - } - } - - for (int i = 0; i < FFT_SIZE / 2; ++i) { - float mag = sqrtf(real[i] * real[i] + imag[i] * imag[i]) / FFT_SIZE; - float db = 20.0f * log10f(mag + 1e-6f); // Decibels - float normalized = (db + 60.0f) / 60.0f; // [0,1] - freq_out[i] = mag; - } -} +} \ No newline at end of file diff --git a/src/fft.h b/src/fft.h index 27a108e..80f96f6 100644 --- a/src/fft.h +++ b/src/fft.h @@ -3,5 +3,16 @@ #include #define FFT_SIZE 2048 -void init_hamming_window(); -void compute_fft(float* time_data, float* freq_out); \ No newline at end of file +// Simple complex number struct +struct Complex { + float re, im; + Complex(float r = 0, float i = 0) : re(r), im(i) {} + Complex operator+(const Complex& o) const { return { re + o.re, im + o.im }; } + Complex operator-(const Complex& o) const { return { re - o.re, im - o.im }; } + Complex operator*(const Complex& o) const { + return { re * o.re - im * o.im, re * o.im + im * o.re }; + } +}; + +void fft(Complex* x, int N, Complex* buffer); + diff --git a/src/main.cpp b/src/main.cpp index b5c2b70..c22d8c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,14 +36,25 @@ #include "fft.h" + #pragma data_seg(".pids") // static allocation saves a few bytes static int pidMain; static int pidPost; // static HDC hDC; + + + #ifndef EDITOR_CONTROLS #pragma code_seg(".main") +// FFT buffers +static Complex signal[FFT_SIZE]; +static Complex buffer[3 * FFT_SIZE]; + +static float fft_uniform[FFT_SIZE / 4]; +static float shapesData[SHAPES_TEX_SIZE]; + void entrypoint(void) #else #include "editor.h" @@ -112,7 +123,7 @@ int __cdecl main(int argc, char* argv[]) track.play(); double position = 0.0; #endif - + static float syncs[1 + SU_NUMSYNCS]; long playCursor = 0; long lastPlayCursor = -1; volatile float maximum = 0.0; // Helper variable to calculate maximum fft output for normalization @@ -123,17 +134,6 @@ int __cdecl main(int argc, char* argv[]) // Play sound direct_sound_buffer->Play(0, 0, 0); - static float syncs[1 + SU_NUMSYNCS]; - - // Init FFT - init_hamming_window(); - // FFT buffers - static float fft_input[FFT_SIZE]; - static float fft_output[FFT_SIZE / 2]; // Magnitudes - static float fft_uniform[FFT_SIZE / 4]; - - static float shapesData[SHAPES_TEX_SIZE]; - // main note effect boolean beenPlaying = false; const int SHAPES_SIZE = 15; @@ -221,7 +221,7 @@ int __cdecl main(int argc, char* argv[]) { SUsample* samples = (SUsample*)audio_ptr; for (int i = 0; i < FFT_SIZE; ++i) { - fft_input[i] = (float)samples[i]; + signal[i] = Complex((float)samples[i], 0.0); } } @@ -229,20 +229,23 @@ int __cdecl main(int argc, char* argv[]) } // Calculate FFT - compute_fft(fft_input, fft_output); + fft(signal, FFT_SIZE, buffer); // Normalize output for (int i = 0; i < (FFT_SIZE / 4); i++) { - float gain = 50.0f; - float alpha = 0.10f; // "Hidastaa" FFT:n piikkej� + float gain = 0.025f; + float alpha = 0.15f; // "Hidastaa" FFT:n piikkejä float threshhold = 0.015f; // Alin arvo mik� p��stet��n shaderille (v�hent�� "noisea") - float x_t = (fft_output[i] < threshhold) ? 0.f : fft_output[i] * gain; + float magnitude = sqrt(signal[i].re * signal[i].re + signal[i].im * signal[i].im); + + float x_t = (magnitude < threshhold) ? 0.f : magnitude * gain; // Exponential smoothing kaava // s(t) = alpha*x(t)+(1-alpha)*s(t-1) fft_uniform[i] = alpha * (x_t)+(1 - alpha) * fft_uniform[i]; } } + syncs[0] = (float)playCursor / (SU_SAMPLE_RATE * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE); // Aika sekunteina. for (int i = 0; i < SU_NUMSYNCS; ++i)