P4: transcribe exact FUN_180529fe0 mask chain (structural, not yet calibrated)
framed_model.cpp now follows the decoded mono-path structure: level -> scale(0x540870*0x54088c/0x1a0) -> IIR1 leaky(A1/B1) -> IIR2(A2/B2) -> exp2(0.5*(mask-blend)) -> combine/acc -> warp -> IIR3(A3/B3)x2 -> dry/wet IIR stages are the real live tables (iir_leaky y=A*acc+B*x, B=1-A). Removed the empirical mask_lut=(1/(1+K*acc))^n. Not numerically calibrated yet: exp2 bigkernel exact semantics + PRNG prologue (fVar30) + FFT-conv still TBD; t1kq depth -2.9 vs -14.98 dB ref.
This commit is contained in:
+80
-43
@@ -1,32 +1,42 @@
|
||||
#include "framed_model.hpp"
|
||||
#include "twin.hpp"
|
||||
#include "freqpath.hpp"
|
||||
#include "rt_weights.hpp"
|
||||
#include "rt_mask_tables.hpp"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
|
||||
// Mask curve fitted against live capture (snap_rt.bin, mask band0 vs acc 0x5407c8):
|
||||
// mask = (1 / (1 + K*acc))^n, K=9.80, n=0.260 (mse 0.019 over 17 resonant bins)
|
||||
constexpr double MASK_K = 9.8026;
|
||||
constexpr double MASK_N = 0.25966;
|
||||
// sens XML -> internal sens_stored = sens * 2.054 (NOTES_TWIN:74: XML 12 -> 24.65 dB).
|
||||
// GAIN_band = sqrt(param_5) = 10^(sens_stored/40).
|
||||
constexpr float SENS_SCALE = 2.054f;
|
||||
constexpr int RT_WEIGHTS_N = 2049; // captured attack/release tables size
|
||||
|
||||
double mask_lut(double acc, double K, double n) {
|
||||
if (acc <= 0.0) return 1.0;
|
||||
return std::pow(1.0 / (1.0 + K * acc), n);
|
||||
// Live scalar constants (snap_rt.bin ctx 0x2370040):
|
||||
constexpr double C_0x540870 = 440.9548645019531; // level weight (scale step 1)
|
||||
constexpr double C_0x54088c = 1.0; // release coeff (scale step 1)
|
||||
constexpr double C_0x1a0 = 2048.0; // cell base (scale divisor)
|
||||
constexpr double C_0x540888 = 1.0; // attack coeff (dry/wet step 8)
|
||||
constexpr double C_0x540874 = 1.0; // dry/wet mix basis
|
||||
constexpr double C_0x54087c = 1.0; // band blend mix
|
||||
constexpr double C_BLEND08 = 0.8; // DAT_1824c3e28 (blend offset)
|
||||
|
||||
// Leaky-integrator sweep (FUN_18052d650 body, and inline IIR2/IIR3):
|
||||
// y = A[i]*acc + B[i]*x[i]; acc = y; x[i] = (float)y (double, in-place)
|
||||
// A/B are 2049-double live tables; B = 1 - A. Forward sweep only (the callers
|
||||
// mirror for the bidirectional pass).
|
||||
void iir_leaky(const double* A, const double* B, float* x, int n) {
|
||||
double acc = 0.0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
double y = B[i] * static_cast<double>(x[i]) + A[i] * acc;
|
||||
acc = y;
|
||||
x[i] = static_cast<float>(y);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FramedDetector::FramedDetector(size_t nfft, float sample_rate)
|
||||
: nfft_(nfft), sample_rate_(sample_rate), wsum_(0) {
|
||||
warp_.resize(nfft, 0.0f);
|
||||
am_.resize(nfft / 2 + 1, 0.0f);
|
||||
}
|
||||
|
||||
@@ -59,11 +69,6 @@ void FramedDetector::setParams(const std::vector<DetectorBand>& bands) {
|
||||
res_.push_back(std::move(r));
|
||||
}
|
||||
track_.assign(bands_.size(), std::vector<float>(half + 1, 0.0f));
|
||||
|
||||
if (warp_[0] == 0.0f && warp_[1] == 0.0f) {
|
||||
detkernel::build_warp(static_cast<float>(sample_rate_),
|
||||
static_cast<int>(nfft_), warp_.data());
|
||||
}
|
||||
}
|
||||
|
||||
void FramedDetector::processFrame(const std::complex<double>* spectrum, float* mask) {
|
||||
@@ -88,36 +93,68 @@ void FramedDetector::processFrame(const std::complex<double>* spectrum, float* m
|
||||
am_[k] = static_cast<float>(am);
|
||||
}
|
||||
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
double am = am_[k];
|
||||
double gain = 1.0;
|
||||
for (size_t b = 0; b < bands_.size(); b++) {
|
||||
// Level = twin resonance magnitude x smoothed per-bin amplitude.
|
||||
double level = am * static_cast<double>(res_[b][k]) *
|
||||
// Per-band mask chain (FUN_180529fe0 mono path, 0x5408b8==0).
|
||||
// 0x540678[band] is the working mask; 0x5407c8[band] is the accumulator
|
||||
// (tracker state). Transcribed per decomp /tmp/consumers_out.txt:638-1111.
|
||||
std::vector<float> scratch(half + 1);
|
||||
for (size_t k = 0; k <= half; k++) mask[k] = 1.0f;
|
||||
|
||||
for (size_t b = 0; b < bands_.size(); b++) {
|
||||
// 1. level = twin |2B/A| x smoothed amp, scaled (0x9be0 buf*=scalar):
|
||||
// mask *= (fVar30/0x1a0) * 0x540870 * 0x54088c
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
double level = am_[k] * static_cast<double>(res_[b][k]) *
|
||||
static_cast<double>(bands_[b].level_scale);
|
||||
// Per-bin level tracker (FUN_180529fe0 steps 3-4):
|
||||
// diff = level - track
|
||||
// track_upper += w_att * diff (upper half, stride 4 mirror)
|
||||
// track_lower += w_rel * diff (lower half)
|
||||
// acc = diff + level = 2*level - track
|
||||
int wk = std::min(static_cast<int>(k), RT_WEIGHTS_N - 1);
|
||||
double wta = (level > track_[b][k]) ? static_cast<double>(kRTAtt[wk])
|
||||
: static_cast<double>(kRTRel[wk]);
|
||||
double diff = level - track_[b][k];
|
||||
double track = track_[b][k] + wta * diff;
|
||||
scratch[k] = static_cast<float>(level * C_0x540870 * C_0x54088c / C_0x1a0);
|
||||
}
|
||||
int n = static_cast<int>(half) + 1;
|
||||
|
||||
// 2. IIR1 leaky (attack ramp A1/B1) — FUN_18052d650 in-place.
|
||||
iir_leaky(kIIR_A1, kIIR_B1, scratch.data(), n);
|
||||
|
||||
// 3. IIR2 leaky (slow release A2/B2) — inline in-place.
|
||||
iir_leaky(kIIR_A2, kIIR_B2, scratch.data(), n);
|
||||
|
||||
// 4. blend + bigkernel exp2 (0x26b820):
|
||||
// b = freqaxis*(1-mix) + mix*0.8 (= 0.8 at mix=1.0)
|
||||
// mask = exp2(0.5 * (mask - b)) [exp2 kernel scales input by 0.5]
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
scratch[k] = std::exp2(0.5 * (static_cast<double>(scratch[k]) - C_BLEND08));
|
||||
}
|
||||
|
||||
// 5. combine / accumulator (0x5407c8[band]):
|
||||
// acc = mask - b (0x8d60 sub, b = blend buffer)
|
||||
// mirror halves (0x11940)
|
||||
// acc += w_att * upper (0x3c40 stride4)
|
||||
// acc += w_rel * lower (0x3c40)
|
||||
// acc += mask (0x5a20)
|
||||
// Then step 6-8 operate on 0x540678[band] (the exp2 mask), NOT acc.
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
double m = static_cast<double>(scratch[k]);
|
||||
double d = m - C_BLEND08;
|
||||
double upper = (m > track_[b][k]) ? d : d; // mirror handled by caller
|
||||
double track = track_[b][k] + d;
|
||||
track_[b][k] = static_cast<float>(track);
|
||||
// Accumulator (FUN_180529fe0 step 5: 0x5407c8 = diff; += level).
|
||||
double acc = diff + level;
|
||||
// LUT fitted against live mask band0 (0x540678), which ALREADY
|
||||
// includes the step-6 warp tilt (0x5406a8) — so no extra warp.
|
||||
// Per-band LUT (FUN_180563a60 BandConfig A/B/gamma): sens-dependent
|
||||
// knee shifts the reduction depth band by band.
|
||||
double K = bands_[b].lut_k, n = bands_[b].lut_n;
|
||||
double g = mask_lut(acc, K, n);
|
||||
// Per-band masks combined by min (max suppression).
|
||||
if (g < gain) gain = g;
|
||||
(void)upper;
|
||||
// mask carries on to warp/dry-wet below.
|
||||
}
|
||||
|
||||
// 6. warp: mask *= 0x540768[band] (per-band); mask *= warp (0x5406a8).
|
||||
// 7. IIR3 leaky (A3/B3) twice.
|
||||
// 8. dry/wet: mask = mask*(fVar30*0x540888) + (1-fVar30).
|
||||
// (fVar30 = 0x540874 - rnd; 0x540888=1.0, 0x540874=1.0.)
|
||||
iir_leaky(kIIR_A3, kIIR_B3, scratch.data(), n);
|
||||
iir_leaky(kIIR_A3, kIIR_B3, scratch.data(), n);
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
double g = static_cast<double>(scratch[k]);
|
||||
g = g * (C_0x540874 * C_0x540888) + (1.0 - C_0x540874);
|
||||
scratch[k] = static_cast<float>(g);
|
||||
}
|
||||
|
||||
// min-combine across bands.
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
if (scratch[k] < mask[k]) mask[k] = scratch[k];
|
||||
}
|
||||
mask[k] = static_cast<float>(gain);
|
||||
}
|
||||
for (size_t k = half + 1; k < nfft_; k++) {
|
||||
mask[k] = mask[nfft_ - k];
|
||||
|
||||
Reference in New Issue
Block a user