127 lines
8.6 KiB
Markdown
127 lines
8.6 KiB
Markdown
# RUNTIME CAPTURE — LIVE DSP TABLES (2026-08-19)
|
||
|
||
## BREAKTHROUGH: heap "registry" object found & read during offline render
|
||
- During `reaper -nosplash -renderproject render_long.rpp`, the yabridge-host
|
||
allocates a DSP arena containing a **registry array**: a run of `{u64 count, u64 ptr}`
|
||
pairs (stride 0x10) pointing at every DSP buffer. At capture time:
|
||
registry base = **0x29b06c0** (in anon region 0x2922000, 0x77e000).
|
||
(GUI-session registry was 0x28b06c0 — same object, shifted by arena layout.)
|
||
- Finding it live: scan host-readable memory (chunked, 8MB) for `u64==8193` (0x2001)
|
||
followed by a readable ptr, then require a run of count/ptr pairs at stride 0x10.
|
||
- Registry owner chain: 0 hits for a u64==registry address, so it is reached
|
||
structurally (object member at some fixed offset), not via an explicit global.
|
||
|
||
## Captured tables (SR=44100 project, but internal freq-axis = 48000!)
|
||
| reg idx | count | ptr | content |
|
||
|---------|-------|----------|---------|
|
||
| [00] | 8193 | 0x2a72600 | identity ~1.0 |
|
||
| [01] | 8193 | 0x2111140 | **WIN_freq: 0.5 -> 1.0 (saturates; idx1024=0.68, idx2048=0.8)** — the FFT-conv window (0x540658) |
|
||
| [02] | 8193 | 0x29f2280 | 0 -> ~0.01 (levels/curve) |
|
||
| [03] | 8193 | 0x29fa300 | **0.596 -> 0.126** = matches known rwin_C0 |
|
||
| [04] | 8193 | 0x2a02340 | **0.404 -> 0.874** = 1-[03] complement |
|
||
| [05] | 8193 | 0x2a0a3c0 | 0.0435 -> ~0 (weight, small) |
|
||
| [06] | 8193 | 0x2a12400 | 0.9565 -> ~1.0 (1-[05]) |
|
||
| [07] | 8193 | 0x2aca740 | zeros + small negatives |
|
||
| [0d] | 2049 | 0x2cd0fc0 | **freq-axis 0..23988.3 Hz, spacing 11.713 = 48000/4096** → internal SR=48000 |
|
||
| [0e] | 8193 | 0x2cd9000 | 2.017 -> 0 (LUT/knee?) |
|
||
| [0f] | 16384 | 0x2ce9080 | same as [0e] doubled |
|
||
| [10] | 16384 | 0x2cf90c0 | 1.2914 -> 0 |
|
||
| [12] | 16384 | 0x2a62540 | 11.29.. (scattering) |
|
||
| [14]/[15] | 8193 | .. | first-fire IR? 0,0.022,0.104,0.084,0.018,0 |
|
||
| [17] | 32768 | 0x2d29140 | 0.9999 -> ~1 (ramp) |
|
||
| [19] | 32768 | 0x2d69200 | 1.2915 -> 1.0 |
|
||
| others | 32768/65536 | .. | ones / ramps (FFT plans, mirrors) |
|
||
|
||
- Key numeric check: registry[03] head 0.5960761, idx1024 0.168, idx2048 0.1257 —
|
||
byte-identical to earlier GUI rwin_C0 (0.596 -> 0.126). Confirms registry IS the
|
||
authoritative per-bin weight source; tables are stable across sessions/SR.
|
||
- **FREQ-AXIS uses internal SR=48000 regardless of project 44100** (spacing 11.713).
|
||
This reconciles "rwin tables at 48k" even when rendering 44.1k projects.
|
||
|
||
## Files saved (handoff/)
|
||
- `rtwin_freq_44100.npy` — WIN_freq (8193 f32): 0.5 -> 1.0 (this is live 0x540658 window)
|
||
- `rtfreqaxis_48000_internal.npy` — freq-axis (2048 f32, 0..23988.3, spacing 11.713)
|
||
- `rtwa_596.npy` — [03] 0.596->0.126
|
||
- `rtwb_404.npy` — [04] 0.404->0.874
|
||
- `rtwc_043.npy` — [05]
|
||
- `rtwd_956.npy` — [06]
|
||
- Full raw snapshot: /tmp/snap_all.bin (318MB, entries {lo,sz,bytes}), registry.txt list.
|
||
|
||
## Method notes (repro)
|
||
- rtsnap_fast.py: spawn reaper render_long, find host (soothe2 in maps, not reaper),
|
||
sleep 6s (tables built), pread ALL readable maps chunked 8MB -> snap_all.bin.
|
||
- pread of large anon regions can EIO -> MUST chunk (8MB); whole-region pread loses data.
|
||
- Scan ~0.1s for 318MB once chunked; far cheaper than object-base scan.
|
||
- Earlier vptr-based (rtobj/rtdump2/rtall) and 44100-marker scans all failed because
|
||
the DSP object has NO static vptr match in a fresh render (host dies / fields live
|
||
only during audio) and ctor field +0x24 != 44100 live. The registry run is the
|
||
reliable beacon.
|
||
|
||
## Remaining (for twin IIR attack/release per-bin)
|
||
- twin state A/B (342 double per-bin IIR states) still not uniquely located live;
|
||
short renders keep them ~0. They are NOT the registry tables.
|
||
- Next: render_long + capture at t=10-20s into sustain, then locate the per-bin
|
||
attack/release smoothing coefficients (0x540888/88c set, converted via ln(10)/20)
|
||
inside the arena near registry.
|
||
|
||
## 2026-08-19b (two-point snapshots t1=8s, t2=40s of render_long 180s)
|
||
- rtsnap2.py: TWO snapshots of the SAME 180s render at t1/t2; each 318MB/547regs ~0.2s.
|
||
- Registry tables byte-identical between t1/t2 (stable per-band coeffs; confirmed authoritative).
|
||
- Diff of arbitrary 342-double windows = pure audio-buffer noise (21981 phantom matches; twin
|
||
per-band state is NOT a 342-dbl array a level away). Real twin kernel state per NOTES_TWIN:64
|
||
is {double A[3], double B[3]} per band (6 doubles), seeded in build_twin_coeff FUN_180533ec0.
|
||
- Conclusion: live per-note-band twin state not usefully separable via full-heap diff; the
|
||
attack/release input coeffs live in the DSP ctx scalars: 0x540888/0x54088c = expf(p*0.11513)
|
||
(static-derived), registry holds the per-bin WEIGHT tables (already captured). Twin kernels
|
||
themselves validated statically (twin_check max|err|=1.27e-5). => Step C goal (window + axes +
|
||
weights + kernel parity) is effectively CLOSED; only scalar A/R params remain, derived from RPP
|
||
params, no live capture needed.
|
||
|
||
## 2026-08-20 (P1.5: live level-tracker A[] / ctx — CONFIRMED UNREACHABLE)
|
||
- rtctx.py (repo): full pipeline = spawn render_long offline → find yabridge-host → chunked
|
||
snapshot → registry-scan → ctx discovery (marker base+0x24==40000.0f ∧ base+0x540658==window).
|
||
- Two FRESH captures (host live mid-render): registry found (0x28b06c0 GUI / 0x29b06c0 offline,
|
||
[00].cnt=8193 identity, [01]/[03] = window/weights). But:
|
||
- the window ptr (0x2962??? live) appears as a heap value ONLY inside the registry entry
|
||
(0x???6c8 self-field), never as a field of a larger ctx object;
|
||
- bases where +0x24==0x472c4400 (40000.0f) exist only in the plugin image (.data/rodata
|
||
0x180535d62/0x1805607e5/0x181414341) — inline code constants, no live DSP object.
|
||
- => A standalone DSP ctx object with {+0x24==40000, +0x540658==winptr} does NOT exist in the
|
||
heap (the DSP object is the registry itself / its buffers are the registry targets).
|
||
- CONFIRMS the 2026-08-19b conclusion: per-bin level-tracker A[] (0x4c0528/0x3c0510/0x2c04f8)
|
||
and mask scalars are NOT live-separable with the current registry/ctx tools; they are
|
||
STATIC/DERIVED from RPP params (0x540888/88c = 10^(att/20), 0x540870 = expf((p·c4348+c44a4)·0.11513),
|
||
0x54087c = raw band/mix). Two unknown constants remain from the (lost) binary: 0x24c4348, 0x24c44a4.
|
||
- => P1.5 "live capture" is a dead end; scalars must be derived statically or the two missing
|
||
constants recovered from the original soothing_mem.bin (not currently present in workspace).
|
||
|
||
## 2026-08-20b (P1.5 SOLVED: live ctx + level-tracker A[] captured via realtime playback)
|
||
The dead end above was wrong — the missing piece was REALTIME audio playback, not more scanning.
|
||
`-renderproject` uses the OFFLINE audio engine (fields live "only during audio", per earlier note);
|
||
the ctx object only materializes during a realtime transport play.
|
||
|
||
### Method (works)
|
||
- play.lua (repo): `reaper.Main_OnCommand(1007)` (Transport:Play) + hold ~300s.
|
||
- rtctx_rt.py (repo): `reaper render_long.rpp play.lua` → find yabridge-host → chunked snapshot
|
||
(~796MB, 1056 regs) DURING playback → scan heap for the ctx.
|
||
- ctx marker that WORKS live: **+0x24 == 48000.0f (0x473b8000)**, NOT 40000.0f (40000 was the
|
||
static ctor rodata value; live it is the internal SR = 48000, confirming NOTES_CAPTURE SR=48000).
|
||
|
||
### Result (captured live, saved handoff/rtctx_live.json)
|
||
- **ctx = 0x2370040** (region 0x2022000). Field pointers (all point at registry tables):
|
||
0x540688=identity([00] 0x2962580), 0x540698=window([01] 0x14b4240), 0x5406a8=levels([02]),
|
||
0x5406b8=WA([03]), 0x5406c8=WB([04]), 0x5406d8=WC([05]), 0x5406e8=WD([06]),
|
||
0x540748=warp([12] 0x14bc280), 0x540768=LUT-knee([14]). (NOTE: offsets +0x40 from the
|
||
earlier static table — window is 0x540698 live, not 0x540658 as in the f_52b570 disasm label.)
|
||
- **Mask scalars (float)**: 0x540870=440.955 (sens), 0x540874=1.0, 0x540878=1.0, 0x54087c=1.0,
|
||
0x540880=25.0, 0x540884=10.0, 0x540888=1.0 (attack=10^0), 0x54088c=1.0 (release=10^0),
|
||
0x540890=0, 0x540894=1200.0.
|
||
- **level-tracker A[] (341 double each, per-bin IIR attack/release coeffs)**:
|
||
- 0x4c0528 (attack): 0 → 0.340, 0.348, ... monotonic rising, plateau 0.6921 @bin>=319.
|
||
- 0x3c0510 == 0x2c04f8 (release): 0 → 0.000753, 0.000885, ... slow small rise.
|
||
Full arrays in handoff/rtctx_live.json (keys A_4c0528, A_3c0510, A_2c04f8).
|
||
- BandConfig @ctx+0x188 is NOT populated here (zeros) — it lives at a different offset or only
|
||
during band processing; still TBD (but LUT curve params A/B/γ are RPP-derived per roadmap).
|
||
- Two more ctx-like bases found (0x2120040, 0x1780040) also have +0x24==48000; 0x2370040 is the
|
||
populated one (0x2120040 has 0x540658..698 = 1.0 fill pattern — likely a second/free instance).
|