diff --git a/dsp/fn529fe0.cpp b/dsp/fn529fe0.cpp index a60b7b6..e738a95 100644 --- a/dsp/fn529fe0.cpp +++ b/dsp/fn529fe0.cpp @@ -208,6 +208,25 @@ static inline void iir4_bidir_340510(float* x, size_t nbin) { acc = 0.0; for (size_t i = nbin; i-- > 0;) { double y = A2[i]*acc + B2[i]*x[i]; acc = y; x[i] = static_cast(y); } } + +// IIR4 coefficient generator (FUN_180533340, BLOCKMAP:135-150) +// Generates frequency-dependent warp coefficients for chain_9_19 step 18 +void generate_iir4_coefs(double* downCoef, double* upCoef, + int n, double C, double tau, double sr, double p, double mult) { + const double sr_scale = 0.9994880557060242; // DAT_1824c3d8c (live) + const double exp_scale = 0.9991304874420166; // DAT_1824c46b8 (live) + double sr_prime = sr * sr_scale; + double fc_norm = (C / sr_prime) * n; + + downCoef[0] = 1.0; + upCoef[0] = 0.0; + for (int i = 1; i < n; i++) { + double g = (i <= fc_norm) ? (fc_norm / i) : std::pow(fc_norm / i, p); + double c = 1.0 / (g * tau / mult + 1.0); + upCoef[i] = std::exp(c * g * tau * exp_scale); + downCoef[i] = 1.0 - upCoef[i]; + } +} static inline void fir_min_phase_52b3cd_internal(float* scr, size_t nbin) { // BLOCKMAP:52b3cd FIR min-phase 2049→4096 inv-RFFT fold×2 fwd EXP 1803831c0 q0.80 // Real RFFT pipeline validated cascade_sim.py fir_kernel 0.0065dB. Gate RT_FIR=1 diff --git a/dsp/fn529fe0.hpp b/dsp/fn529fe0.hpp index eb9ba2c..833ef77 100644 --- a/dsp/fn529fe0.hpp +++ b/dsp/fn529fe0.hpp @@ -67,8 +67,10 @@ void cascade_detect( bool is_magnitude = false // true = input_data is already |z|, skip Phase 1 ); -// FIR min-phase 52b3cd 2049→4096 (BLOCKMAP:760) — exposed for VLAW path -void fir_min_phase_52b3cd(float* scr, size_t nbin); +// IIR4 coefficient generator (FUN_180533340, BLOCKMAP:135-150) +// Generates frequency-dependent warp coefficients for chain_9_19 step 18 +void generate_iir4_coefs(double* downCoef, double* upCoef, + int n, double C, double tau, double sr, double p, double mult); // Main chain 9–19 (BLOCKMAP:620) — DIVIDE/FMA/EXP/FIR proxy (1c) void chain_9_19(float* bands, float* tmp6f8, float* accVec, diff --git a/dsp/framed_model.cpp b/dsp/framed_model.cpp index e26a043..1544536 100644 --- a/dsp/framed_model.cpp +++ b/dsp/framed_model.cpp @@ -409,43 +409,27 @@ static void process_band_structural( } // RT_CASC=1: chain_9_19 structural path (BLOCKMAP:620-644) - // Replaces warp + IIR3 with calibrated chain including IIR4×2 + FIR min-phase + // Input: raw_level (am/res*scale, 0-17.6 mean=0.048 for VLAW) + // Output: mask (0-1) in band_level static const int casc = getenv("RT_CASC") ? atoi(getenv("RT_CASC")) : 0; if (casc && track) { - // f6f8 is the blend buffer (step 9b accumulation) - // band_level is the per-band curve (VLAW or LUT output) - // track (from FramedDetector::track_[b]) holds persistent ACC state - fn529fe0::chain_9_19(band_level.data(), f6f8.data(), track, + fn529fe0::chain_9_19(raw_level.data(), f6f8.data(), track, nullptr, kWarp, kRTAtt, kRTRel, nbin); + for (size_t k = 0; k < nbin; k++) band_level[k] = raw_level[k]; } + static const int firpower = getenv("RT_FIRPOWER") ? atoi(getenv("RT_FIRPOWER")) : 0; for (size_t k = 0; k < nfft; k++) { double mm; - // RT_FIRPOWER=1: FIR-style mask from raw spectrum. - // Plugin's actual pipeline (52b550-52b8bb): - // 1. scratch = log(raw_spectrum) - // 2. FIR = exp(0.984 × scratch) = raw^0.984 - // 3. FIR *= hann_window (freq-domain) - // 4. FIR *= 0x540888 (scalar) - // 5. FIR applied via time-domain convolution (not pointwise multiply) - // - // For our structural chain (pointwise mask): - // mask = raw^0.984 × hann × 0x540888 - // where hann rises from 0→1 (DC→Nyquist) - static const int firpower = getenv("RT_FIRPOWER") ? atoi(getenv("RT_FIRPOWER")) : 0; - if (vlaw) { + size_t idx = (k < nbin) ? k : nfft - 1 - k; + if (casc && track) { + mm = static_cast(band_level[idx]); + } else if (vlaw) { mm = static_cast(band_level[k]); - // Signal spectral.cpp that power law is already applied (skip in FIRCONV=3) - if (firconv3 == 3) { - setenv("RT_FIRCONV3_APPLIED", "1", 1); - } + if (firconv3 == 3) setenv("RT_FIRCONV3_APPLIED", "1", 1); } else if (firpower) { double raw = static_cast(raw_level[k]); - if (raw > 1e-12) { - mm = std::pow(raw, 0.984); - } else { - mm = 1.0; - } + mm = (raw > 1e-12) ? std::pow(raw, 0.984) : 1.0; } else { mm = std::exp2(-static_cast(band_level[k])); static const int noblend = getenv("RT_NOBLEND") ? atoi(getenv("RT_NOBLEND")) : 0;