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
This commit is contained in:
+66
-2
@@ -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<float>(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<float> raw_level(nbin);
|
||||
for (size_t k = 0; k < nbin; k++) {
|
||||
@@ -410,6 +436,8 @@ void FramedDetector::setParams(const std::vector<DetectorBand>& bands) {
|
||||
size_t half = nfft_ / 2;
|
||||
res_.clear();
|
||||
track_.clear();
|
||||
twin_resp_complex_.clear();
|
||||
cascade_states_.clear();
|
||||
|
||||
// RT_DUMPRESPATH=<file> (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<DetectorBand>& 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<std::complex<double>> complex_resp(half + 1);
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
complex_resp[k] = std::complex<double>(out[k].re, out[k].im);
|
||||
}
|
||||
twin_resp_complex_.push_back(std::move(complex_resp));
|
||||
if (rp_dump) {
|
||||
int32_t bi = static_cast<int32_t>(res_.size());
|
||||
int32_t nb = static_cast<int32_t>(r.size());
|
||||
@@ -445,6 +480,7 @@ void FramedDetector::setParams(const std::vector<DetectorBand>& bands) {
|
||||
}
|
||||
if (rp_dump) fclose(rp_dump);
|
||||
track_.assign(bands_.size(), std::vector<float>(half + 1, 1.0f));
|
||||
cascade_states_.assign(bands_.size(), fn529fe0::CascadeState());
|
||||
}
|
||||
|
||||
void FramedDetector::processFrame(const std::complex<double>* spectrum, float* mask) {
|
||||
@@ -481,6 +517,10 @@ void FramedDetector::processFrame(const std::complex<double>* 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<double>* 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<float> complex_input(2 * nbin);
|
||||
std::vector<float> curve_output(nbin);
|
||||
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
complex_input[2*k] = static_cast<float>(twin_resp_complex_[b][k].real());
|
||||
complex_input[2*k+1] = static_cast<float>(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]);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <cstddef>
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
#include "fn529fe0.hpp"
|
||||
|
||||
struct DetectorBand {
|
||||
float fc; // band center freq (Hz)
|
||||
@@ -71,6 +72,11 @@ public:
|
||||
|
||||
void processFrame(const std::complex<double>* spectrum, float* mask);
|
||||
|
||||
// Cascade state access for per-band detector cascade
|
||||
std::vector<fn529fe0::CascadeState>& cascadeStates() { return cascade_states_; }
|
||||
const std::vector<std::vector<std::complex<double>>>& twinRespComplex() const { return twin_resp_complex_; }
|
||||
std::vector<std::vector<std::complex<double>>>& twinRespComplex() { return twin_resp_complex_; }
|
||||
|
||||
private:
|
||||
size_t nfft_;
|
||||
float sample_rate_;
|
||||
@@ -82,4 +88,10 @@ private:
|
||||
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
|
||||
|
||||
// For cascade 529c60: per-band complex twin filter responses
|
||||
std::vector<std::vector<std::complex<double>>> twin_resp_complex_;
|
||||
|
||||
// Per-band cascade states
|
||||
std::vector<fn529fe0::CascadeState> cascade_states_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user