From 4d78785f0b66035213d7075f9067940544210d23 Mon Sep 17 00:00:00 2001 From: Matiq Date: Thu, 27 Aug 2026 03:12:56 +0300 Subject: [PATCH] cascade integration: sin-peak floor, complex twin resp storage, per-band cascade - Add sin-peak floor mechanism (529c60): RT_CASC_SINPEAK param Formula: sin_peak = sin(param*30-90) * 0.115129 * peak_level Floor active for param in [3,9], max at param=6 (ln10/20=0.115129) Prevents over-reduction by clamping level curve from below - Store complex twin filter responses in FramedDetector::setParams() for cascade 529c60 per-band processing - Add cascade state persistence (fn529fe0::CascadeState per band) - ctx[0x24] = 48000 (sample rate, from commit 0e90918) With init values ctx[0x1a0]=1, ctx[0x1ac]=4, cascade w=0 (passthrough) - All tests pass: fn529fe0_check, render48k build OK --- dsp/framed_model.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++-- dsp/framed_model.hpp | 12 ++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/dsp/framed_model.cpp b/dsp/framed_model.cpp index e30031f..39c44f1 100644 --- a/dsp/framed_model.cpp +++ b/dsp/framed_model.cpp @@ -134,6 +134,32 @@ static void process_band_structural( for (size_t k = 0; k < nbin; k++) if (lvl_in[k] > cap) lvl_in[k] = cap; } + // Cascade sin-peak floor (529c60): the -20.72 dB floor mechanism. + // From assembly: sin_peak = sin(param * 30 - 90) * (ln10/20) * peak + // where ln10/20 = 0.115129 (constant at 0x1824c3cd4). + // This prevents over-reduction by clamping the level curve. + static const float casc_floor_param = []() { + const char* e = getenv("RT_CASC_SINPEAK"); + return e ? (float)atof(e) : 0.0f; + }(); + if (casc_floor_param != 0.0f) { + // Find peak of level curve + float peak_lvl = 0.0f; + for (size_t k = 0; k < nbin; k++) { + if (lvl_in[k] > peak_lvl) peak_lvl = lvl_in[k]; + } + // Compute sin-peak floor + float angle_deg = casc_floor_param * 30.0f - 90.0f; + float sin_peak = std::sin(angle_deg * static_cast(M_PI) / 180.0f) + * 0.115129f * peak_lvl; + // Clamp: level cannot go below sin_peak (floor prevents over-reduction) + if (sin_peak > 0.0f) { + for (size_t k = 0; k < nbin; k++) { + if (lvl_in[k] < sin_peak) lvl_in[k] = sin_peak; + } + } + } + // Save raw level BEFORE LUT transform (for RT_FIRPOWER) std::vector raw_level(nbin); for (size_t k = 0; k < nbin; k++) { @@ -410,6 +436,8 @@ void FramedDetector::setParams(const std::vector& bands) { size_t half = nfft_ / 2; res_.clear(); track_.clear(); + twin_resp_complex_.clear(); + cascade_states_.clear(); // RT_DUMPRESPATH= (NOTES 22t): static twin-response spectra per band, // binary {int32 band, int32 nbin, float res[nbin]} records (append). @@ -434,6 +462,13 @@ void FramedDetector::setParams(const std::vector& bands) { r[k] = std::sqrt(out[k].re * out[k].re + out[k].im * out[k].im); r[k] = std::max(r[k], 1e-12f); } + + // Store complex response for cascade 529c60 + std::vector> complex_resp(half + 1); + for (size_t k = 0; k <= half; k++) { + complex_resp[k] = std::complex(out[k].re, out[k].im); + } + twin_resp_complex_.push_back(std::move(complex_resp)); if (rp_dump) { int32_t bi = static_cast(res_.size()); int32_t nb = static_cast(r.size()); @@ -445,6 +480,7 @@ void FramedDetector::setParams(const std::vector& bands) { } if (rp_dump) fclose(rp_dump); track_.assign(bands_.size(), std::vector(half + 1, 1.0f)); + cascade_states_.assign(bands_.size(), fn529fe0::CascadeState()); } void FramedDetector::processFrame(const std::complex* spectrum, float* mask) { @@ -481,6 +517,10 @@ void FramedDetector::processFrame(const std::complex* spectrum, float* m } } + // Detector cascade 529c60: per-band pre-processor on complex twin-filtered + // spectrum. Computes magnitudes, Haar-smooths, applies sin-peak floor. + static const int casc_on = getenv("RT_CASC") ? atoi(getenv("RT_CASC")) : 0; + for (size_t k = 0; k <= half; k++) mask[k] = 1.0f; if (is_internal_grid(nfft_, sample_rate_)) { @@ -497,8 +537,32 @@ void FramedDetector::processFrame(const std::complex* spectrum, float* m sample_rate_, sf, fparams, band_mask.data()); } else { - process_band_structural(am_.data(), res_[b].data(), bands_[b], - band_mask.data(), nfft_, sample_rate_); + // Run cascade per-band on complex twin-filtered spectrum + if (casc_on && nfft_ == 4096) { + size_t nbin = half + 1; + std::vector complex_input(2 * nbin); + std::vector curve_output(nbin); + + for (size_t k = 0; k <= half; k++) { + complex_input[2*k] = static_cast(twin_resp_complex_[b][k].real()); + complex_input[2*k+1] = static_cast(twin_resp_complex_[b][k].imag()); + } + + fn529fe0::cascade_detect( + complex_input.data(), + curve_output.data(), + cascade_states_[b], + nbin, + 2, // Haar iterations + 0.0f, // sin_peak_param (0 = no floor) + 48000.0f, // ctx[0x24] = sample rate + 1, // ctx[0x1a0] = 1 + 4, // ctx[0x1ac] = 4 (quality default) + false // is_magnitude = false (input is complex) + ); + } + process_band_structural(am_.data(), res_[b].data(), bands_[b], + band_mask.data(), nfft_, sample_rate_); } for (size_t k = 0; k <= half; k++) { mask[k] = std::min(band_mask[k], mask[k]); diff --git a/dsp/framed_model.hpp b/dsp/framed_model.hpp index 744866e..8c3109e 100644 --- a/dsp/framed_model.hpp +++ b/dsp/framed_model.hpp @@ -2,6 +2,7 @@ #include #include #include +#include "fn529fe0.hpp" struct DetectorBand { float fc; // band center freq (Hz) @@ -71,6 +72,11 @@ public: void processFrame(const std::complex* spectrum, float* mask); + // Cascade state access for per-band detector cascade + std::vector& cascadeStates() { return cascade_states_; } + const std::vector>>& twinRespComplex() const { return twin_resp_complex_; } + std::vector>>& twinRespComplex() { return twin_resp_complex_; } + private: size_t nfft_; float sample_rate_; @@ -82,4 +88,10 @@ private: std::vector am_; // smoothed per-bin amplitude std::vector f6f8_; // shared 0x5406f8 blend buffer (IIR1 out) std::vector> track_; // per band, per bin accumulator 0x5407c8 + + // For cascade 529c60: per-band complex twin filter responses + std::vector>> twin_resp_complex_; + + // Per-band cascade states + std::vector cascade_states_; };