#pragma once #include #include #include struct DetectorBand { float fc; // band center freq (Hz) float q; // resonance Q float sens; // XML sens (dB); internal sens_stored = sens * 2.054 float level_scale = 1.0f; // calibration: level = am * res * level_scale // Per-band mask LUT: gain = (1/(1+K*acc))^n (live-fit defaults band0). float lut_k = 9.8026f; float lut_n = 0.25966f; // Dry/wet depth (XML "depth", 0.864 in reference renders). Applied only when // set >= 0; default -1 keeps the live LUT fit intact (fit already absorbed // band0's dry/wet blend). float depth = -1.0f; }; // FramedDetector — C++ port of the real soothe mask chain (FUN_180529fe0). // Per band, per bin: // level_k = am_k * res_k (twin resonance x smoothed amplitude) // track += w_k * (level - track) (per-bin level tracker, attack/release // weights w from live capture rt_weights) // acc_k = 2*level_k - track_k (accumulator, live peak ~10.08 at tone) // mask_k = LUT(acc_k) (fitted mask curve from live capture) // final mask = min over bands (max suppression), like soothe's band combine. class FramedDetector { public: FramedDetector(size_t nfft, float sample_rate); ~FramedDetector(); void setParams(const std::vector& bands); void processFrame(const std::complex* spectrum, float* mask); private: size_t nfft_; float sample_rate_; double wsum_; std::vector bands_; std::vector> res_; // per band, per bin |2B/A| std::vector warp_; // per-bin warp tilt std::vector am_; // smoothed per-bin amplitude std::vector> track_; // per band, per bin level tracker };