res_power breakthrough: 500Hz residual solved (q0.1 err +0.00), decomp inventory, FUN_180563440 decoded

This commit is contained in:
2026-08-19 09:56:19 +03:00
parent 1a57c48318
commit 627e8373e6
9 changed files with 1595 additions and 23 deletions
+118 -2
View File
@@ -2,6 +2,52 @@
Prepared: 2026-08-18 (checkpoint end-of-session: commits c8f97e4 + 60bf3a2 pushed). Start: READ THIS FIRST.
## 0. DECOMPILATION INVENTORY (2026-08-19 — what's decoded, where, and what's missing)
Goal: bit-exact parity is gated by EXACT tables/window/constants. The chunk-level model hits
err ≤0.62 dB (dual) / ≤0.19 dB (al_*) — to go sample-exact we need precise values from the binary.
### Static decomp assets IN REPO (use these, don't re-decompile):
- `ghidra-proj/soothe2.rep` — full Ghidra project (vst3 at ImageBase 0x180000000).
- `decomp_funs.txt` (312K lines, ~1640 functions), `fun_map.txt` (2285 addr→FUN), `consts.txt` (11602),
`decomp_dsp.txt` / `decomp_vtables.txt` / `decomp_candidates.txt` / `focus_decomp.txt`. Generators:
`Dump*.java`, `ImportRtti*.java`, `ListFuns.java`, `SearchRefs.java` (+ headless logs).
- RTTI: `rtti_dsp.json` / `rtti_full.json` — class hierarchy
`SpectralProcessor<float,7,1>`, `Soothe2ModuleBase<float,1>`, `FilterGraph<float,6,0x400>`,
`DigitalFilter<float,0xBA,1>`, `IIRFilterExtended<float,1>`, `AudioProcessingModule<float,1>`.
- `handoff/nls_dasm/` — 120 hand-picked `.dis` (twin, iface_18052da00/dbc0, fft, generator, ctor).
- `dsp/` — working C++ transcription (twin, detect, freqpath, spectral, fft_stage) + harness;
`build/twin_check` passes float-parity gate (§2).
### KEY DSP ADDRESSES — decoded / not decoded:
DECODED (formula-level, notes at NOTES_TWIN.md / NOTES_LEVEL.md):
- twin kernel `FUN_180535880`; generator `FUN_180533ec0`; caller `FUN_180536300`; dead sibling `180536f90`.
- level-weight formula `FUN_180530d30` (0x540880/884, warp 0x5406a8=0.87·x/(1+x/7.942), w=0.1^(...)).
- mask-apply entry `FUN_180529fe0` (accumulator 0x5407c8 += w·res; mask *= warp; FIR *= 0x540658).
- FFT-conv loop 0x52b550-0x52b8b5 (plan 0x540530, windows 0x540548/550/598, freq-axis 0x540698=offline const).
- level-path map: `0x563440` (LUT curve +0x188, 6 band-slots, combine→+0x2198), `0x56e3e0` (twin-mask factory),
`0x563ce0` (IIR level-tracker INIT only).
NOT DECODED / MISSING FROM decomp_funs.txt (critical):
- `FUN_180529fe0` body — mask/fir apply; only reachable via runtime + vtable slot `180529fe0` in fun_map.txt.
- `FUN_180563440` (LUT curve + gamma + combine), `FUN_180563ce0` (IIR level-tracker UPDATE loop).
Full disasms exist ONLY in `/tmp/opencode/f_563440.dis`, `f_563ce0.dis`, `f529fe0.dis`**/tmp is
ephemeral; COPY INTO handoff/nls_dasm/ on next session start.**
- Window `0x540658` (single indirect write — statically invisible); live-captured copies saved as
`rwin_A0/A1/B0/C0.npy` + `r_freqaxis.npy` (see NOTES_LEVEL.md §2026-08-18h3). Live data at SR=48000
(offline renders 44100 → renormalize warp x by actual Nyquist).
- sens source: XML 12.0 → runtime sens_dB≈24.65 (host ×2) not found in dump; `IAT\*0x181bab370` outside dump.
### Bridge model STATUS (2026-08-19):
- `framed_render.py` full STFT frame-render (N=2048, hop=512, sqrt-Hann, twin env tatt=11ms/trel=80ms):
`C(f)=G·LUT(xv)+W·warp(f)^A`, G=1.0850, W=0.2819, A=1.1377 (joint dual+al_* refit).
- Validation: dual q0.1/1/10 @500+2000 err ≤0.62 dB, envRmse@steady ≤0.78 dB; al_* lv3..24 err ≤0.19 dB.
- LUT = slanted al_*-leg (0.366@xv=-0.50 → 0.636@xv=+0.55), replaces flat B.12 (~0.5). Bugfix: clip range
must be [LY.min(), LY.max()] not [LY[0], LY[-1]].
- Remaining structural residual: 0.6 dB systematic on dual 500Hz (q0.1/q1). Live evidence (avg per-bin
gain H=|Y|/|X|): reference mask is FLAT ~-10.2dB across 100-540Hz regardless of Q — model produces
res-shaped notch. Hypo tested: freq-smoothing of C(f) fails (kills 2000Hz). NEXT: scalar per-frame xv
(broadband level, not per-bin am/res) — see /tmp/smoothtest.py (+ edit scalar=True).
## 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`
@@ -9,7 +55,72 @@ C++ and reach **bit-exact render parity** with the Reaper reference wavs in `/ho
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)
## 2. DECOMPILATION FINDINGS (2026-08-19 — FUN_180563440, FUN_180563ce0 decoded)
### FUN_180563440 — LUT curve evaluation + band combine (222 lines disasm)
Structure: **3 phases per frame**:
1. **1024-bin LUT loop** (0x400 iterations): for each bin k:
- x = clamp(k * 0.0009775, 0, 1.0) = k/1024
- Band config at +0x188: {A(+0x00), B(+0x04), threshold(+0x0c), flag(+0x10), callback(+0x50)}
- **Path 1** (callback exists): vtable call → dynamic LUT
- **Path 2** (flag=1, threshold≠1.0): **power-law** → centered = 2*x - 1, then `sign(x) * 10^(log10(|x|) / threshold)` — this is a **compression curve** controlled by sharpness/threshold
- **Path 3** (default): **linear interpolation**`(B - A) * x + A`
- Output: double-precision at +0x198, stride 8
2. **Twin-mask factory** (FUN_18056e3e0): 6 bands × 1024 bins, stride 0x2000
3. **Combine loop**: stereo (max 2 channels), 6 bands, `1 - sum(band_masks)`
### FUN_180563ce0 — IIR level-tracker INIT (163 lines disasm)
- **341 bins** (0x155 iterations), **order-3 IIR** (3 coefficients per bin)
- Coefficient: **0.1** (`0x3dcccccd` = IEEE 754 float 0.1)
- Initial state: [1.0, 0, 0, 0] and [-1.0, 0, 0, 0] (identity + zero)
- Buffer layout: 3 × (16 bytes coeff) per bin, stored at rcx+0x28/+0x40/+0x58
- **Not the update loop** — init only; UPDATE is elsewhere
### FUN_180529fe0 — Coefficient setup (2051 instructions, in decomp_funs.txt)
- **vtable method** on Soothe2Module<M,1>
- **Lock** at +0x2404dc (atomic test-and-set)
- **PRNG state** at +0x2404e0, LCG with offset 0x3cdca
- **6-iteration coefficient generation** from 0x5408b0 buffer (LCG-indexed)
- **Depth scaling**: `powf(normalized, depth)` at +0x2c
- **Mask assembly**: normalize by 0x1a0 (NFFT), × 0x540870 (level weight), × 0x54088c (sharpness), invert
- **Copy output** via SIMD memcpy (thunk 0x181ba94b0)
### FUN_18052e9b0 — SpectralProcessor main (3167 instructions, in decomp_funs.txt)
- **Same PRNG + coefficient setup** as FUN_180529fe0
- **Band chain**: FUN_18052f500 (interleave) → FUN_18052ee70 (per-bin gain) → FUN_18052d650 (setup) → FUN_18052d920 (window)
- **Buffer alloc**: FUN_18052e190 for 0x540668, 0x540698, 0x5406a8, 0x5406b8-e8 (6 bands)
- **Depth scaling**: `powf(normalized, depth)` with depth at +0x2c
- **Final mask**: `1 - C` (inversion)
- **Window application**: `FIR *= 0x540658` (the live-captured window table)
### Constants verified from binary:
| Constant | Address | Value | Meaning |
|---|---|---|---|
| SCALE | 0x24c3c54 | 0.000977517 | 1/1024 (bin→x) |
| ONE | 0x24c3ea4 | 1.0 | clamping max |
| TWO | 0x24c41e0 | 2.0 | centering (2*x-1) |
| NEG1 | 0x24c4680 | -1.0 | sign flip |
| HALF | 0x24c3d8c | 0.5 | threshold |
| DEPTH_SCALE | 0x24c4334 | 4.0 | depth range |
| DB_CONV | 0x24c43e0 | 8.6859 | 20/ln(10) |
| FLOOR | 0x24c4704 | -6.9078 | ln(0.001) |
| IIR_COEFF | embedded | 0.1 | attack/release per bin |
| IIR_ORDER | embedded | 3.0 | IIR filter order |
| LCG_OFFSET | embedded | 0x3cdca | PRNG state advance |
### Live-captured tables (re-verified):
- `rwin_A0.npy` (0x1930100): 0.5→0.8, **frequency window** (NOT warp formula)
- `rwin_B0.npy` (0x1938180): 0→3.899, **power-law depth curve** (exponent ~0.66)
- `rwin_C0.npy` (0x19401c0): 0.596→0.126, **level-dependent weight**
- `r_freqaxis.npy`: 0→23988.3 Hz, 11.71 Hz spacing (48000/4096)
### Key insight: The warp formula is NOT a table — it's computed at runtime
The empirical `0.87*7.942*x/(7.942+x)` is an approximation of a runtime computation
in FUN_180563440 that evaluates the LUT curve parametrically. The actual LUT has TWO modes:
- **Linear** (default): simple interpolation between A and B
- **Power-law** (flag=1): `sign(x) * 10^(log10(|x|) / C)` — compression curve
## 3. PROOF OF STATE — run this first (5 min)
Everything below must reproduce. If `twin_check` fails, the transcription moved stale.
```bash
@@ -131,8 +242,13 @@ IAT\*0x181bab370 outside dump (non-blocking), final z¹ phase proof, the 0x56344
- `/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/framed_render.py` — full frame-render pilot (2026-08-19 params; modes dual|al)
- `/home/m/re-tools/rwin_A0/A1/B0/C0.npy`, `r_freqaxis.npy`, `rwin_warp.npy` — live-window tables (48k)
- `/home/m/re-tools/rtsnap.py` — live-process page snapshotter; snapshots /tmp/rt{A,B,C,D}.{raw,idx}
- `/home/m/re-tools/handoff/decode_rpp_full.py`, `handoff/rpp_allparams.py` — RPP b64-XML full decoder
(trim to len%4==0, `<?xml`@92, incl. nested processorStateData)
- `/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
FUN_180533ec0 (123204), FUN_180529fe0 (missing body! vtable-only)
- `/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