11 KiB
SESSION HANDOFF — soothe2 detector reverse → bit-exact render
Prepared: 2026-08-18 (checkpoint end-of-session: commits c8f97e4 + 60bf3a2 pushed). Start: READ THIS FIRST.
1. Objective (unchanged since session 1)
Transcribe the decoded soothe2 detector ("twins" 0x180535880/0x180536f90, 2nd-order resonator) into
C++ and reach bit-exact render parity with the Reaper reference wavs in /home/m/soothe-bt/*.wav
(checking dB/magnitude parity first, then byte-diff).
Path A (bit-exact) chosen. Phase 0,1,2 done. Phase 3,4 advanced. Phase 5 (render diff) is next.
2. PROOF OF STATE — run this first (5 min)
Everything below must reproduce. If twin_check fails, the transcription moved stale.
cd /home/m/re-tools/dsp && cmake --build build && ./build/twin_check
# expect:
# max rel err = 1.268e-05, PASS = yes (float-parity)
# max sweep rel err = 1.233e-05, SWEEP PASS = yes, exit 0
build/soothe2_dsp.so and build/twin_check are current. CMakeLists already has twin.cpp + twin_check executable.
3. TRANSCRIPTION STATE (summary — details in NOTES_TWIN.md)
dsp/twin.hpp+dsp/twin.cpp= twin kernel FUN_180535880:- build_twin_coeff (FUN_180533ec0 double pipeline → cvtpd2ps floats)
- generator formula (confirmed):
fVar1=sqrtf(param_5);w0=max(2.0,freq)·2π/fs_total;p=(sin(w0)·0.5)/Q;A=[1+dVar5, dVar2, 1−dVar5],B=[1+param_4, dVar2, 1−param_4],dVar2=cos(w0)·−2.0(DAT_1824c46b0=−2.0). - cplx_div_exact (rcpps+Newton:
ref=r0·(2−den·r0), NaN-guard cmpeqps+movmskps, numerator B·conj(A)) - cplx_mul_exact (vfmaddsub213ps form
re=fma(br,ar,−bi·ai),im=fma(br,ai,bi·ar)) - twin_apply: conj(z1) mirroring 0x1800018b0, z2=z1·z1, out=2·B/A
- OPEN phase question (byte-exactness): conjugate shaping — (-α+π/2) vs (-α) axis. Magnitudes match either way; final proof needed via phase of a single-bin run before claiming byte-exactness.
4. GRID + CALLER (Phase 3 — details in NOTES_TWIN.md §Phase 3)
- Caller FUN_180536300 (decomp 6218,
caller_536300.dis), scratch ≥52·N complex bytes: +0..8N A-accum, +8N..16N B-accum, +16N..24N z-powers, +24N..32N rotor-in (filled 1.0), +32N..36N phase ramp lVar1, +36N..44N rotor-out z1, +44N..52N lVar3(1.0)/lVar4(0.0) 0x18052da00(lVar1, scale=2π/(os·sr), N)= ramp fill;0x18052dbc0(param_2, lVar4, N)cplx-interleave → out slots get (cos,sin) = e^{+iθ}.- Grid law:
os=*(int*)(cfg+0x240080)=4(mov DWORD PTR [rbx+0x240080],0x4 @0x18052ce3e),sr=*(float*)(cfg+0x24)=44100.0→ generatorfs_total=os·sr=176400; per-cell phase2π/176400; cell k ↔ freq k/os Hz (0.25 Hz steps). Phase-1 FS=44100 was a valid normalization (res depends on f/fc). - twin always = FUN_180535880 (type-info compare equal); sibling FUN_180536f90 dead.
- case8: numBands
+0x814=1,+0x103=1;param_5=10^(sens_dB/20),gain=sqrtf(param_5)=10^(sens_dB/40)→ fit gain=4.132 means storedsens_dB@+0x101 ≈ 24.65; freq=[band+0x804]=1000.0, Q=[band+0x80c].
5. LEVEL-WEIGHT FORMULA (Phase 4 — DECODED, NEGATIVE RESULT — NOTES_LEVEL.md §UPDATE 2026-08-18)
Exact formula from decomp 22330 (FUN_180530d30):
base=(2000/(sr·0.5))·iVar5/(bin+1), iVar5=NFFT/2+1w8=powf(base,0.25)v=[0x540880]·0.25·w8·fVar12(fVar12=4.0 if offline flag [0x5408b8], else 1.0)q=1/(1+v/([0x540880]·4))(2nd pair 0x540884: q2=1/(1+v2/[0x540884]))dVar1=(sr/[0x1a0])·[0x1ac]·0.001w=0.1^(1/(max(q·v,floor)·dVar1)); bufs: 0x5406b8=w, 0x5406c8=1−w (and 0x5406d8/6e8 w/ 0x540884)- consts: c3d3c=0.25, c4334=4.0, c3d8c=0.5, c45b4=2000.0, c3f70=0.1 f32, c3e30=0.001 f64, c3f70=0.1
- Numerically: w(500)=w(1000)=w(2000)≈0 (1e-20..1e-0.07), complements≈1 → but the weights are STILL
APPLIED (as 1−w ≈ 1) by FUN_180529fe0 (
0x5407c8[band] += 0x5406c8·res_upper + 0x5406e8·res_lower, thunk 003c40, kernel 0x1800752e0 = vfmadd213pd) — the form is weight·resonance, not dead code. - TILT analysis (2026-08-18, checkpoint NEGATIVE result): warp
0x5406a8 = 0.87·x/(1+x/K),K=exp(2.0723267)≈7.942,x=f/2000, applied by FUN_180529fe0 asmask *= 0x5406a8(float, thunk 008700) BEFORE the FFT-conv. warp: 500→0.211, 1000→0.409, 2000→0.773 (lin ratio 2000/500=3.67). BUT warp ≠ empirical tilt (ratio 1.27):C(dB2000)=depth·warp(2000)·LUT_max = 0.864·0.772·0.667=0.445, yet red2000=15.2 dB needsC=0.826⇒ warp alone CANNOT produce deep-2000 (dual_b1q). Empirical tilt is the envelope of the BLEND term (0x5407c8 += weights·res + freq-axis 0x540698) + FFT-conv shaping, NOT warp — simple tilt→warp substitution in model_lut.py is INVALID. Nothing else decoded yet closes dual_b1q. - FUN_180563ce0 decoded (2026-08-18): INIT of per-bin IIR level-trackers (0x156=342 bins, order=3.0f
0x40400000, coeff A/B from 0x24c4780/4790, strided fill; third pass +0xffd0 fills 0x40e00000=7.0f).
Not the update loop. Full disasm:
/tmp/opencode/f_563ce0.dis. - Level-path map (2026-08-18, NOTES_LEVEL.md): 0x563440 (LUT curve +0x188, gamma, 6 band-slots, combine →+0x2198, LUT pairs +0x98 at 0x3ff pts) + 0x56e3e0 twin-mask factory (N=1024, DOUBLE, LUT-ramp input) + 0x563ce0 IIR init. 0x563440 NOT in decomp_funs.txt (search via /tmp/opencode/f_563440.dis + level_notes.md).
- DIAGNOSTIC BRIDGE DONE (2026-08-18) — model_fir.py: реальная цепочка из FUN_180529fe0
воспроизводит 36 точек до rmse=0.236 dB БЕЗ эмпирического tilt:
C(f) = g·LUT(log10(L0/res(f))) + w·warp(f)^a, g=1.221, w=0.358, a=3.143.- dual500-константа = res_band(500;fc=500)=0.117 Q-НЕЗАВИСИМ, не tilt;
- dual2000-глубина = АДДИТИВНЫЙ терм
w·warp^3.14(на 1000 Гц вклад ≤0.021 — иначе t1k рушится; на 2000 = 0.149) — это аккумулятор0x5407c8 += weights·res + mask(шаг 5), НЕ мультипликация warp·LUT (та проваливается >10 dB); - warp^~3.14 ≈ π — подозрение на кратный каскад (0x540698 freq-axis, ∏0x540688, двойной FFT);
- остаток 0.7 dB при Q=0.1 = форма LUT-колена 0.574, не закрывается per-bin → нужен FFT-уровень.
- FFT-CONV FULLY MAPPED (2026-08-18, NOTES_LEVEL.md §2026-08-18c, /tmp/opencode/f_52b570.dis):
0x535a70 = dispatcher to base-CRT FFT (0x140a10/0x140a70); 0x540548/0x540550/0x540598 = FFT-plan
tables (plan 0x540530, built by 0x52dc30), NOT windows. Per-band loop 0x52b550-0x52b8b5:
mask→fwdFFT→invFFT→fill low/high (xmm13/xmm9 consts)→inv→cplx-op→fwd→
FIR *= 0x540658→fill→inv→FIR[0]=1,FIR[1]=0→*=0x540888→copy out. freq-axis 0x540698: OFFLINE = constant scalar (NOT exp-formula — exp/rand = online dither). Window 0x540658: only ONE direct ref in dump (read at 0x52b771), written only indirectly — STATICALLY INVISIBLE → the only plausible source of warp^3.14/LUT-knee. Need runtime capture or full-pipeline empirical closure.
6. PHASE 5 PLAN (updated 2026-08-18 — bridge done, tilt replaced, FFT mined out)
Decision (user): bridge FIRST. DONE — bridge result above in §5. Status: step 2 DONE, step 3 PARTIAL (static analysis exhausted).
- (15 min) Proof-of-state above; confirm twin + mdl files complete; repo at
60bf3a2. - Replace empirical tilt in model_lut.py with
g·LUT(xv) + w·warp(f)^a— DONE, rmse 0.236 dB reproduced, committedc8f97e4. Optional refinement fit (2 LUT-knots) reaches 0.167 but is overfit. - Close the 0.7-dB gap: FFT-conv loop mapped + freq-axis cleared (offline const) — DONE/PARTIAL,
committed
60bf3a2. Remaining unknown = 0x540658 window content (static-invisible). Options: (a) runtime capture of 0x540658, (b) skip and close empirically in full-pipeline step 5. - Runtime values: per-bin IIR level trackers (0x563ce0), 0x540880/884,
[0x1a0]/[0x1ac], cell count N. - Full pipeline + render diff vs
/home/m/soothe-bt/*.wav: dB parity → byte parity.
NEXT SESSION START (recommendation): skip deeper FFT mining; go straight to step 5 full pipeline (mask-chain already in model_lut.py B.12 at 0.236; freq-axis const; FFT-conv reduces to a per-bin smoothing + 0x540658 shaping that can be fit/absorbed). Runtime capture of 0x540658 only if step 5 stalls on the Q=0.1 knee.
STEP 5 STARTED (2026-08-18) — dB-parity harness DONE (commit f96bb42)
/home/m/re-tools/render_parity.py: measures steady-state per-tone reduction DIRECTLY on the reference wavs (Goertzel tone-amp over late steady window; handles 16/24-bit, mono/stereo). NOTE: Goertzel must use the SEQUENTIAL update form (s2=s1;s1=s0;s0=v+cw*s1-s2) — the tuple-unpack form is numerically unstable (overflows ~1500 steps).- Validation: measured dual Q-scan == stored dataset (dual.wav is 4s mono 16-bit; renders 6s stereo 24-bit).
- dB-PARITY RESULT (canonical B.12, 36 pts, real wavs): TOTAL rmse=0.268 dB (dual500 0.130, dual2000 0.387, t1kq 0.320, t1k 0.076). residual structure: dual2000 -0.80@Q0.1..+0.36@Q10 (LUT-knee), t1kq +0.36..+0.26 (level region), t1k ±0.1 (good). Refit (g,w,a)=(1.224,0.379,3.405)→0.264; +2 LUT-knots→0.203 (plateau).
- CONCLUSION: B.12 form plateau ~0.20-0.27 dB vs real renders; residual is structural (0x540658 FFT shaping + level-curve region), matches plan expectation. Re-measured-on-wav numbers differ slightly from the stored model_lut.py dataset (t1kq ~+0.3 dB) — different steady-window method.
- Open: full PIPELINE frame-render (STFT + per-frame mask + time constants) is the remaining big lift for byte-parity; dB-parity milestone achieved at 0.268 dB.
Candidates that may consume Phase 5 time: source of sens≈24.65 (host ×2 over XML 12.0 — not found in dump), IAT*0x181bab370 outside dump (non-blocking), final z¹ phase proof, the 0x563440 LUT curve (+0x188) values.
7. FILES LAYOUT
/home/m/re-tools/dsp/— twin.{hpp,cpp}, twin_check.cpp, CMakeLists.txt, build//home/m/re-tools/model_lut.py— model B.12 (Q=xmlq, gain=10^(sens/20), depth 0.8639736175537109,C=g·LUT(xv)+w·warp^a, g=1.221/w=0.358/a=3.143) — 0.236 dB bridge model; PCHIP LUT nodes frozen/home/m/re-tools/model_fir.py— bridge canonical source (rmse 0.236, committedb1066f3)/home/m/re-tools/soothe_mem.bin— memory dump, offset=VA−0x180000000/home/m/re-tools/decomp_funs.txt— decomp: FUN_180536300 (6218), FUN_180530d30 (22330), FUN_180533ec0 (123204), FUN_180529fe0/home/m/re-tools/handoff/— THIS DOC, NOTES_TWIN.md, NOTES_LEVEL.md, phase1/.py, nls_dasm/.dis/home/m/soothe-bt/*.rpp(decoder/home/m/re-tools/dsp/...; alsodecode_rpp4.pyreferenced) +*.wavrefs