Files
soothe2-re/handoff/SESSION_HANDOFF.md
T

9.3 KiB
Raw Blame History

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, 1dVar5], B=[1+param_4, dVar2, 1param_4], dVar2=cos(w0)·−2.0 (DAT_1824c46b0=2.0).
    • cplx_div_exact (rcpps+Newton: ref=r0·(2den·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 → generator fs_total=os·sr=176400; per-cell phase 2π/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 stored sens_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+1
  • w8=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.001
  • w=0.1^(1/(max(q·v,floor)·dVar1)); bufs: 0x5406b8=w, 0x5406c8=1w (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 1w ≈ 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 as mask *= 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 needs C=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).

  1. (15 min) Proof-of-state above; confirm twin + mdl files complete; repo at 60bf3a2.
  2. Replace empirical tilt in model_lut.py with g·LUT(xv) + w·warp(f)^aDONE, rmse 0.236 dB reproduced, committed c8f97e4. Optional refinement fit (2 LUT-knots) reaches 0.167 but is overfit.
  3. 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.
  4. Runtime values: per-bin IIR level trackers (0x563ce0), 0x540880/884, [0x1a0]/[0x1ac], cell count N.
  5. 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.

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, committed b1066f3)
  • /home/m/re-tools/soothe_mem.bin — memory dump, offset=VA0x180000000
  • /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/...; also decode_rpp4.py referenced) + *.wav refs