Files
soothe2-re/dsp/framed_model.hpp
T
Matiq f8ecf6a1af P4: Phase B — framed_model re-transcribed to the confirmed chain (NOTES:199-226)
Fixes structural divergences: IIR1 into shared 0x5406f8 buffer + bridge to band
mask (FUN_18052d650, 0x5160); IIR2 on band mask; Hermitian mirror (0x11940);
blend step f6f8=axis(1-mix)+mix*0.8, mask=exp2(-mask)*f6f8; real combine via
kRTAtt/kRTRel weights (0x5406c8/6e8); warp, IIR3x2, dry/wet.

Validation: t1kq only1 fc1000 -21.1 vs -22.1 (OK); fc-scan shape intact;
dual @500 matches (s30 -51.5 vs -53.7), @2000 gap -4..-13 vs -29.6 remains.
That gap = dB-domain band LUT (FUN_180563a60) not yet in chain -> Phase A.
2026-08-20 17:16:22 +03:00

51 lines
2.1 KiB
C++

#pragma once
#include <cstddef>
#include <complex>
#include <vector>
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
};
// FramedDetector — C++ transcription of the real soothe2 mask-apply chain
// (FUN_180529fe0 mono path, 0x5408b8==0), bit-exact structure.
//
// Per band, per bin (exact decomp /tmp/consumers_out.txt:638-1111):
// 1. scale: x = level * (fVar30/0x1a0) * 0x540870 * 0x54088c
// 2. IIR1 leaky: y[i] = A1[i]*acc + B1[i]*x[i] (A1/B1 ramp attack, live tables)
// 3. IIR2 leaky: same with A2/B2 (slow release)
// 4. blend: b = freqaxis*(1-mix) + mix*0.8; mask = exp2(0.5*(mask-b))
// 5. combine: acc[band] = mask - b; mirror; += w_att*upper; += w_rel*lower; += mask
// 6. warp: mask *= 0x540768[band]; mask *= warp (dual tilt)
// 7. IIR3 leaky: twice with A3/B3
// 8. dry/wet: mask = mask*(fVar30*0x540888) + (1-fVar30)
// final = min over bands.
// PRNG (FUN_180529fe0 prologue :515-583): LCG state 0x2404e0 advances by round
// offsets; fVar30 (scale coeff) = (int)(LUT[s+1]*LUT[s]+0.001), DAT_18262b5c8/
// b704/b700 == 1 (VA-linear dump; earlier 0.4552/0.6089/0.6070 was a bad offset).
// At live state 112 this yields fVar30 == 1.0 deterministically over many frames.
class FramedDetector {
public:
FramedDetector(size_t nfft, float sample_rate);
~FramedDetector();
void setParams(const std::vector<DetectorBand>& bands);
void processFrame(const std::complex<double>* spectrum, float* mask);
private:
size_t nfft_;
float sample_rate_;
double wsum_;
int prng_state_ = 112; // 0x2404e0 (live snapshot value; advances per frame)
std::vector<DetectorBand> bands_;
std::vector<std::vector<float>> res_; // per band, per bin |2B/A|
std::vector<float> am_; // smoothed per-bin amplitude
std::vector<float> f6f8_; // shared 0x5406f8 blend buffer (IIR1 out)
std::vector<std::vector<float>> track_; // per band, per bin accumulator 0x5407c8
};