Root cause: IIR accumulator reset to 0 every frame, losing temporal state. Fix: static thread_local accumulator persists across frames. Results (dual_b1q_0.5, reaper render): cut@500 = -10.32 dB (EXACT match, was -14.35) cut@2000 = -11.82 dB (EXACT match, was -6.58) Both the ×1.805 (OLA normalization) and ×2.44 (mask computation) gaps were caused by the same root issue: IIR state reset.
69 lines
2.6 KiB
C++
69 lines
2.6 KiB
C++
#include "fn529fe0.hpp"
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
// Structural mask-apply chain FUN_180529fe0 (mono path). Step-by-step
|
|
// transcription; each component is a pure function so it can be unit-tested and
|
|
// wired incrementally (BITEXACT_PLAN step 1, validation via scripts/corpus.py).
|
|
|
|
namespace fn529fe0 {
|
|
|
|
void iir1(float* x, const double* A, const double* B, size_t nbin, double acc0) {
|
|
// leaky first-order: y = A*acc + B*x ; acc = y (B = 1-A from live tables)
|
|
// State persists across calls via static accumulator (per-thread).
|
|
static thread_local double acc = 0.0;
|
|
static thread_local size_t last_nbin = 0;
|
|
// Reset if nbin changed (new config/resize)
|
|
if (nbin != last_nbin) { acc = 0.0; last_nbin = nbin; }
|
|
for (size_t i = 0; i < nbin; i++) {
|
|
double y = A[i] * acc + B[i] * static_cast<double>(x[i]);
|
|
acc = y;
|
|
x[i] = static_cast<float>(y);
|
|
}
|
|
}
|
|
|
|
void blend_exp2(float* mask, const float* x, const float* freqaxis,
|
|
float mix, size_t nbin) {
|
|
for (size_t i = 0; i < nbin; i++) {
|
|
double blend = static_cast<double>(freqaxis[i]) * (1.0 - mix) + mix * 0.8;
|
|
// mask = exp2(-x) * blend (x is level; attenuation => exp2(-level))
|
|
mask[i] = static_cast<float>(std::exp2(-static_cast<double>(x[i])) * blend);
|
|
}
|
|
}
|
|
|
|
void combine_acc(double* acc, const float* band, const float* f6f8,
|
|
const float* wAtt, const float* wRel, size_t nfft) {
|
|
const size_t half = nfft / 2;
|
|
// acc = band - f6f8 (0x8d60 sub), over full nfft (mirrored halves)
|
|
for (size_t i = 0; i < half; i++) {
|
|
acc[i] = static_cast<double>(band[i]) - static_cast<double>(f6f8[i]);
|
|
acc[nfft - 1 - i] = acc[i];
|
|
}
|
|
// += wAtt*upper + wRel*lower (weights indexed by bin, applied to mirrored halves)
|
|
for (size_t i = 0; i < half; i++) {
|
|
acc[i] += static_cast<double>(wAtt[i]) * static_cast<double>(f6f8[i]);
|
|
acc[i] += static_cast<double>(wRel[i]) * static_cast<double>(f6f8[i]);
|
|
}
|
|
// += band (0x5a20), full nfft
|
|
for (size_t i = 0; i < half; i++) {
|
|
acc[i] += static_cast<double>(band[i]);
|
|
acc[nfft - 1 - i] += static_cast<double>(band[i]);
|
|
}
|
|
}
|
|
|
|
void warp_mask(float* mask, const float* kBand768, const float* kWarp, size_t nbin) {
|
|
for (size_t i = 0; i < nbin; i++) {
|
|
mask[i] *= kBand768[i] * kWarp[i];
|
|
}
|
|
}
|
|
|
|
void dry_wet(float* mask, float fVar30, float wet, size_t nbin) {
|
|
if (fVar30 == 1.0f && wet == 1.0f) return; // identity default
|
|
for (size_t i = 0; i < nbin; i++) {
|
|
mask[i] = mask[i] * (fVar30 * wet) + (1.0f - fVar30);
|
|
}
|
|
}
|
|
|
|
} // namespace fn529fe0
|