- fft.cpp: build_twiddle via soothe::twiddle_load (Cody-Waite sin/cos); drop dup init_plan - fft_stage.cpp: cplx_mul/stage_complex/stage_double kernels -> phase fixed (corr +0.995) - detect.cpp: level-dependent regional floor (no bell-boost), mask 10^(-1.041*depth*floor/20) - burst500 metrics: ref -26.07 / ours -26.13 dBFS, corr 0.99475, SNR 19.80 dB, diff -0.065 dB - KEY: FUN_180535880/536f90 bodies are decrypted real SSE in soothe_mem.bin; dispatch table 0x182616008[0]=idx=4 -> 0x180009860 -> FUN_180040d40; region 0x18004xxxx = full detector algorithm, absent from prior fun_map/decomp (Ghidra ran on encrypted file)
99 lines
2.9 KiB
C++
99 lines
2.9 KiB
C++
#include "fft.hpp"
|
|
#include "fft_stage.hpp"
|
|
#include "twiddle_loader.hpp"
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
namespace fft {
|
|
|
|
// twiddle loader drops angles modulo — matches soothe: angles arrive as
|
|
// float pairs, loader computes cos/sin per float
|
|
void build_twiddle(FFTPlan* plan, double* scratch) {
|
|
uint32_t N = plan->N;
|
|
uint32_t half = N / 2;
|
|
std::vector<float> angles(half);
|
|
for (uint32_t k = 0; k < half; k++) {
|
|
angles[k] = static_cast<float>(-2.0 * M_PI * k / N);
|
|
}
|
|
std::vector<double> cosv(half), sinv(half);
|
|
soothe::twiddle_load(angles.data(), cosv.data(), sinv.data(), half);
|
|
for (uint32_t k = 0; k < half; k++) {
|
|
scratch[k * 2 + 0] = cosv[k];
|
|
scratch[k * 2 + 1] = sinv[k];
|
|
}
|
|
}
|
|
|
|
void bit_reverse(std::complex<double>* buf, uint32_t N) {
|
|
uint32_t log2N = 0;
|
|
for (uint32_t t = N; t > 1; t >>= 1) log2N++;
|
|
for (uint32_t i = 0; i < N; i++) {
|
|
uint32_t rev = 0;
|
|
uint32_t x = i;
|
|
for (uint32_t j = 0; j < log2N; j++) {
|
|
rev = (rev << 1) | (x & 1);
|
|
x >>= 1;
|
|
}
|
|
if (rev > i) std::swap(buf[i], buf[rev]);
|
|
}
|
|
}
|
|
|
|
void execute_forward(const FFTPlan* plan, std::complex<double>* buf) {
|
|
uint32_t N = plan->N;
|
|
bit_reverse(buf, N);
|
|
|
|
for (uint32_t stage = 1; stage <= plan->log2N; stage++) {
|
|
uint32_t half = 1 << (stage - 1);
|
|
uint32_t full = half * 2;
|
|
double angle_step = -M_PI / half;
|
|
|
|
for (uint32_t k = 0; k < N; k += full) {
|
|
for (uint32_t j = 0; j < half; j++) {
|
|
double angle = angle_step * j;
|
|
double tw_re = std::cos(angle);
|
|
double tw_im = std::sin(angle);
|
|
|
|
auto t = buf[k + j + half] * std::complex<double>(tw_re, tw_im);
|
|
auto u = buf[k + j];
|
|
buf[k + j] = u + t;
|
|
buf[k + j + half] = u - t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void execute_inverse(const FFTPlan* plan, std::complex<double>* buf) {
|
|
uint32_t N = plan->N;
|
|
bit_reverse(buf, N);
|
|
|
|
for (uint32_t stage = 1; stage <= plan->log2N; stage++) {
|
|
uint32_t half = 1 << (stage - 1);
|
|
uint32_t full = half * 2;
|
|
double angle_step = M_PI / half;
|
|
|
|
for (uint32_t k = 0; k < N; k += full) {
|
|
for (uint32_t j = 0; j < half; j++) {
|
|
double angle = angle_step * j;
|
|
double tw_re = std::cos(angle);
|
|
double tw_im = std::sin(angle);
|
|
|
|
auto t = buf[k + j + half] * std::complex<double>(tw_re, tw_im);
|
|
auto u = buf[k + j];
|
|
buf[k + j] = u + t;
|
|
buf[k + j + half] = u - t;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < N; i++) {
|
|
buf[i] /= N;
|
|
}
|
|
}
|
|
|
|
void execute(const FFTPlan* plan, std::complex<double>* buf) {
|
|
execute_forward(plan, buf);
|
|
}
|
|
|
|
}
|