P2: level-tracker UPDATE-loop located (inline bidirectional IIR in FUN_180529fe0), structural module leveltrack (iir_first_order/bidirectional/unrolled4); scalar==unrolled verified; remaining = live A[] coeffs
This commit is contained in:
@@ -19,15 +19,18 @@ add_library(soothe2_dsp SHARED
|
||||
levelpath.cpp
|
||||
phase_table.cpp
|
||||
fftconv.cpp
|
||||
leveltrack.cpp
|
||||
)
|
||||
|
||||
add_executable(soothe2_harness harness.cpp)
|
||||
add_executable(twin_check twin_check.cpp)
|
||||
add_executable(tables_check tables_check.cpp)
|
||||
add_executable(fftconv_check fftconv_check.cpp)
|
||||
add_executable(leveltrack_check leveltrack_check.cpp)
|
||||
target_link_libraries(twin_check soothe2_dsp)
|
||||
target_link_libraries(tables_check soothe2_dsp)
|
||||
target_link_libraries(fftconv_check soothe2_dsp)
|
||||
target_link_libraries(leveltrack_check soothe2_dsp)
|
||||
target_link_libraries(soothe2_harness soothe2_dsp)
|
||||
|
||||
target_include_directories(soothe2_dsp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "leveltrack.hpp"
|
||||
|
||||
namespace leveltrack {
|
||||
|
||||
void iir_first_order(float* x, const double* A, size_t n) {
|
||||
double acc = 0.0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
double y = static_cast<double>(x[i]) * A[i] + acc;
|
||||
acc = y;
|
||||
x[i] = static_cast<float>(y);
|
||||
}
|
||||
}
|
||||
|
||||
void iir_bidirectional(float* x, const double* A, size_t n) {
|
||||
iir_first_order(x, A, n);
|
||||
// reverse pass over reversed indices back into x (keep array order).
|
||||
double acc = 0.0;
|
||||
for (size_t k = n; k-- > 0;) {
|
||||
double y = static_cast<double>(x[k]) * A[k] + acc;
|
||||
acc = y;
|
||||
x[k] = static_cast<float>(y);
|
||||
}
|
||||
}
|
||||
|
||||
void iir_first_order_unrolled4(float* x, const double* A, size_t n) {
|
||||
double acc = 0.0;
|
||||
size_t i = 0;
|
||||
for (; i + 4 <= n;) {
|
||||
for (int u = 0; u < 4; u++, i++) {
|
||||
double y = static_cast<double>(x[i]) * A[i] + acc;
|
||||
acc = y;
|
||||
x[i] = static_cast<float>(y);
|
||||
}
|
||||
}
|
||||
for (; i < n; i++) {
|
||||
double y = static_cast<double>(x[i]) * A[i] + acc;
|
||||
acc = y;
|
||||
x[i] = static_cast<float>(y);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace leveltrack
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
|
||||
// Level-tracker UPDATE (structural transcription, NOTES_LEVEL 2026-08-19f).
|
||||
// The plugin smooths the per-bin level mask with first-order "leaky-integrator"
|
||||
// IIR stages (cumulative running-acc) in FUN_180529fe0. Three stages run in the
|
||||
// real per-band loop; the coefficient arrays A[] are runtime values (from the
|
||||
// level sidechain / smoothing-param builder). This module provides the exact
|
||||
// scalar and vectorized forms as pure functions; callers supply A[].
|
||||
namespace leveltrack {
|
||||
|
||||
// Scalar first-order stage: acc=0; y[i]=x[i]*A[i]+acc; acc=y; x[i]=y
|
||||
// Mirrors consumers_out.txt scalar loop. In-place on x.
|
||||
void iir_first_order(float* x, const double* A, size_t n);
|
||||
|
||||
// Bidirectional stage: forward then reverse pass over x with same A.
|
||||
void iir_bidirectional(float* x, const double* A, size_t n);
|
||||
|
||||
// Vectorized-wide variant handling 4 elements per iteration (like the plugin's
|
||||
// unrolled loop). Same math, provided for parity tests.
|
||||
void iir_first_order_unrolled4(float* x, const double* A, size_t n);
|
||||
|
||||
} // namespace leveltrack
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include "leveltrack.hpp"
|
||||
|
||||
int main() {
|
||||
const size_t n = 37; // odd size to exercise the unrolled tail.
|
||||
std::vector<double> A(n);
|
||||
std::vector<float> x1(n), x2(n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
A[i] = 0.05 + 0.9 * double(1 + i % 3) / 3.0; // nontrivial profile
|
||||
x1[i] = static_cast<float>(0.5 * std::sin(0.3 * i) * (1 + i * 0.01));
|
||||
}
|
||||
std::memcpy(x2.data(), x1.data(), n * sizeof(float));
|
||||
|
||||
leveltrack::iir_first_order(x1.data(), A.data(), n);
|
||||
leveltrack::iir_first_order_unrolled4(x2.data(), A.data(), n);
|
||||
|
||||
double maxd = 0.0;
|
||||
for (size_t i = 0; i < n; i++) maxd = std::fmax(maxd, std::fabs(x1[i] - x2[i]));
|
||||
std::printf("scalar vs unrolled4 max|dx| = %.3e (%s)\n",
|
||||
maxd, maxd < 1e-6 ? "OK" : "MISMATCH");
|
||||
|
||||
// bidirectional: equals applying forward then reverse.
|
||||
std::vector<float> rx(n);
|
||||
leveltrack::iir_bidirectional(rx.data(), A.data(), n);
|
||||
|
||||
std::printf("leveltrack integration check done (n=%zu)\n", n);
|
||||
return maxd < 1e-6 ? 0 : 1;
|
||||
}
|
||||
+17
-1
@@ -534,7 +534,23 @@ getFunctionContaining(0x52ac64) and has the complete per-band loop + FFT-conv).
|
||||
|
||||
### Next: (1) hammer the residual -0.6 dB on dual 500Hz cases (structural); (2) multi-band combos (band>=2); (3) C++ port.
|
||||
|
||||
## ============ 2026-08-19 (continuation): LEVEL-PATH DECOMPILED — FUN_180563440 + FUN_180563ce0 ============
|
||||
## ============ UPDATE 2026-08-19f: LEVEL-TRACKER UPDATE-LOOP LOCATED (structural) ============
|
||||
The UPDATE loop is NOT a separate function — it is the inline bidirectional IIR smoothing
|
||||
inside FUN_180529fe0 per-band loop (consumers_out.txt). Three first-order IIR stages:
|
||||
- (a) FUN_18052d650(state 0x440518, out 0x5406f8, in 0x540678[band]) — outer smoother
|
||||
- (b) inline: coeff-A array 0x4c0528, scalar-acc state 0x540528, mask src/dst 0x5406f8 (count iVar19)
|
||||
- (c) inline: coeff-A array 0x3c0510 / 0x2c04f8, acc 0x440510 / 0x3404f8+0x2404e8 (two more)
|
||||
Scalar form (proven, consumers 745-1015):
|
||||
acc = 0; for i in 0..N-1: y = x[i]*A[i] + acc; acc = y; x[i] = (float)y
|
||||
i.e. cumulative leaky-integrator over A[i] (running-acc coefficient). Vector variant does 4/iter.
|
||||
=> The sample-path smoothing the tracker needs is STATICALLY known in structure; only the
|
||||
coefficient arrays A (0x4c0528/0x3c0510/0x2c04f8) + the 0x530d30 per-bin level-weight caps
|
||||
remain runtime values (0x530d30 shown numerically flat w~=0, NOT the tilt source).
|
||||
Bit-exact port of the UPDATE requires A[] live capture (or FUN_18052d650 body) — next P1.5-ish.
|
||||
Boundary: structural scaffolding now complete (levelpath + fftconv + mask-canon + tracker form);
|
||||
all remaining unknowns are runtime scalar/array values, not structure.
|
||||
|
||||
## ============ 2026-08-19 continuation: LEVEL-PATH DECOMPILED — FUN_180563440 + FUN_180563ce0 ============
|
||||
|
||||
### FUN_180563440 (dsp/levelpath.cpp dump, f_563440.dis 222 lines) — LUT curve + twin-mask + combine
|
||||
Three-phase per-frame structure:
|
||||
|
||||
Reference in New Issue
Block a user