Files
soothe2-re/README.md
T
Matiq bdf9f21a60 docs: AGENTS.md + refresh README/roadmap/summary/SESSION_HANDOFF to P4 canon; rm empty logs
- AGENTS.md: canonical build/test commands, honest trimmed tone metric, current
  bit-exact state (mask-apply chain), open gaps (PRNG/FFT-conv/exp2/SR-mismatch).
- README.md: status moved to P4 (C++ FramedDetector canon, framed_test), historical
  B.1..B.15 numerical model demoted to <details>; structure/doc table updated.
- roadmap.md: P4 phase marked progress, P4.1/P4.2/P4.3 milestones, top status refresh.
- summary.md: marked historical (behavioral v4).
- SESSION_HANDOFF.md: fixed stale "NOT DECODED" (now decoded), copy note done.
- Removed empty tmp_spec.txt/giant3.txt and untracked .log/__pycache__ clutter.
2026-08-20 15:50:07 +03:00

175 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# soothe2-re
Обратный инжиниринг DSP-ядра **oeksound soothe2** (VST3, Windows x64) → проверяемая
численная модель и реконструкция на C++.
Цель — понять, как именно плагин считает подавление резонансов, и воспроизвести
его поведение (в идеале бит-точно, но пока — с точностью ~0.03–0.18 dB на формах
полос и уровнях).
---
## Статус (P4, 2026-08-20)
**Цель — bit-exact реверс.** Декомпиляция DSP-ядра закрыта (~95%): twin-резонатор,
генератор case8, level-path, LUT-кривая (FUN_180563440/563a60), mask-apply
(FUN_180529fe0), FFT-conv — всё расшифровано; дизассемблы в `handoff/nls_dasm/`.
**Активный канон — C++ `FramedDetector`** (`dsp/framed_model.cpp`): точная mask-apply
цепь (не эмпирическая подгонка):
```
level=am*res*scale -> IIR1/IIR2 leaky -> mask=exp2(-mask)*blend -> combine/acc
-> warp(mask*=0x540768, *=warp) -> IIR3x2 -> dry/wet -> FFT-conv
```
- **bigkernel 0x26b820 = векторизованный exp2**; семантика залочена (`mask=exp2(-level)*blend`).
- **Level-tracker = би-направленный leaky-IIR** `y=A[i]·acc+B[i]·x` (B=1A), live-таблицы
в `dsp/rt_mask_tables.{hpp,cpp}`, `dsp/rt_weights.{hpp,cpp}`.
- **Результаты**: t1kq err **0.43 dB** (level_scale=600); comb per-tone err ≤6.4 dB.
```bash
# Канон сборки и рендера:
cmake -S dsp -B dsp/build && cmake --build dsp/build --target framed_test
./dsp/build/framed_test /home/m/soothe-bt/tone1kq.wav /tmp/o.wav 678.7611083984375,0.99999785,12,600
```
Детальная метрика и история — в `AGENTS.md`, `roadmap.md`, `handoff/NOTES_LEVEL.md`.
### Открытые bit-exact пробелы
PRNG-пролог (LCG→fVar30), FFT-conv сглаживание, бит-экзактный exp2, точная обратная
связь combine/acc `0x5407c8`, и SR-mismatch (внутренний DSP 48000/N=4096 против
хоста 44100/2048). Полный список — в `AGENTS.md` и `NOTES_LEVEL.md`.
> Исторический блок (поведенческая/численная модель B.1…B.15, `framed_render.py`,
> Pchip LUT, res_power) — см. `roadmap.md`; самодостаточен как справочник, но не канон.
<details><summary>Было (B.15 — историческая численная модель)</summary>
`framed_render.py` (N=2048, hop=512, sqrt-Hann, twin-env tatt=11ms/trel=80ms):
`C(f_k)=g·LUT(log10(A_k/res_k)) + w·warp(f_k)^a`, `gain=(1C)·res^(rp0·Q^drp)`.
- Честная (trimmed) метрика: Q-dep rp → mean 0.175 dB; B.15 joint free-knot LUT → dual 0.027 dB.
- Декомп DSP-ядра закрыт, `.dis` в `handoff/nls_dasm/`.
```bash
python3 framed_render.py dual # (исторический канон B.15)
```
</details>
---
## Структура репозитория
```
re-tools/
├── README.md ← вы здесь
├── AGENTS.md ← гайд для агентов: сборка, метрика, bit-exact пробелы
├── roadmap.md ← журнал всех результатов/гипотез (B.1…B.15, P4)
├── framed_render.py ← историческая численная модель (B.15, не канон)
├── render_parity.py ← dB-parity харнесс (Goertzel steady-state замер)
├── model_lut.py, model_dual.py ← исторические модели B.10/B.11 (collapse-фиты)
├── model_fir.py ← bridge-модель B.12 (C=g·LUT+w·warp^a)
├── handoff/ ← журнал сессий
│ ├── SESSION_HANDOFF.md ← инвентарь декомпа, трансляция, Phase-5 план
│ ├── NOTES_TWIN.md ← twin-резонатор + caller + grid
│ ├── NOTES_LEVEL.md ← level-path, mask-цепь, live-таблицы (САМЫЙ АКТУАЛЬНЫЙ)
│ ├── NOTES_CAPTURE.md ← live-захват таблиц (registry heartbeat)
│ └── nls_dasm/ ← 134 дизассембла декомпа (f_563440, f_563a60, f529fe0, twin, fft…)
├── dsp/ ← реконструкция DSP-пайплайна на C++17
│ ├── framed_model{.cpp,.hpp} ← ГЛАВНЫЙ: mask-apply цепь (P4, активный канон)
│ ├── framed_test.cpp ← CLI рендер входа (N=2048, SR 44100) + метрика
│ ├── rt_mask_tables{.hpp,.cpp}, rt_weights{.hpp,.cpp} ← live-таблицы (IIR A/B, warp…)
│ ├── twin{.hpp,.cpp} ← бит-точный twin-резонатор (FUN_180535880)
│ ├── levelpath.cpp/.hpp ← LUT-кривая FUN_180563440/563a60 + combine-ядра
│ ├── freqpath.cpp/.hpp ← warp FUN_180530850 (0.87·x/(1+x/K))
│ ├── fftconv.cpp/.hpp ← FFT-conv (0x535a70)
│ ├── spectral.cpp/.hpp ← WOLA/STFT-обработчик
│ ├── fft*.cpp, twiddle_*.cpp/hpp, phase_table.* ← FFT-планы/твилдлы
│ ├── ms.hpp ← encode/decode mid/side
│ ├── cody_waite.hpp ← быстрый sin/cos (FUN_1801de760/1e3f20)
│ ├── soothe_constants.hpp ← константы из дампа
│ ├── harness.cpp ← CLI: input.wav → output.wav (24-bit)
│ └── CMakeLists.txt
├── *.java ← Ghidra-скрипты (analyzeHeadless, пост-скрипты)
├── ghidra-proj/ ← Ghidra-проект (soothe2.gpr/.rep), вне git
├── soothe_mem.bin ← дамп памяти плагина, вне git
├── rwin_{A0,A1,B0,C0}.npy, r_freqaxis.npy ← живые таблицы (48k) из runtime-снимков
├── Измерение и фиты полосы: measure.py, probe.py, bandshape.py, fit_*.py,
│ model_lut.py, notch.py (исторические, B-модели)
├── Поведенческие симуляторы: sim.py, sim_v5.py, verify_sim.py (исторические)
├── Инструменты REAPER-рендеров: sweep.py, run_sweep.py, tt_sweep.py,
│ patchparam.py, addfx.lua
├── Живая трассировка (yabridge-host + Frida): dump_soothe.py, rtall/rtscan/
│ rtsig/rttbl/rtver/rtone/rtwin/rtdeep*.py, probe.py, procdump.py
└── summary.md, notes_giant_fft.md
```
## Документация
| Документ | Содержание |
|---|---|
| [AGENTS.md](AGENTS.md) | **Старт для агента**: сборка, метрика, bit-exact пробелы, структура |
| [roadmap.md](roadmap.md) | Журнал всех результатов/гипотез (B.1…B.15, P4), статус по фазам, риски |
| [handoff/SESSION_HANDOFF.md](handoff/SESSION_HANDOFF.md) | Инвентарь декомпа (§0), трансляция/ключевые адреса (§2), Phase-5 план (§6) |
| [handoff/NOTES_LEVEL.md](handoff/NOTES_LEVEL.md) | **Level-path/mask-цепь, live-таблицы, bit-exact протокол** (самый актуальный) |
| [handoff/NOTES_TWIN.md](handoff/NOTES_TWIN.md) | Twin-резонатор (FUN_180535880/536f90), caller, grid/oversample |
| [handoff/NOTES_CAPTURE.md](handoff/NOTES_CAPTURE.md) | Live-захват таблиц (registry heartbeat, SR 48000) |
| [handoff/nls_dasm/](handoff/nls_dasm/) | 134 дизассембла (f_563440, f_563a60, f529fe0, twin, iface, fft) |
| [notes_giant_fft.md](notes_giant_fft.md) | FFT-планировщики/ядра/twiddle/Cody-Waite |
| [summary.md](summary.md) | **Историческая** сводка поведенческой модели (v4, sim.py) |
### Тестовый корпус `/home/m/soothe-bt/` (вне git)
~600 рендеров REAPER (`.rpp` + `.wav`). Ключевые наборы:
| Префикс | Что измеряет |
|---------|-------------|
| `dual_b1q_*` | 2 тона (500+2000), q 0.1…10 → **парадокс red2000>red500** (решён: аддитивный warp) |
| `al_*` | уровневой свип (fc=1000) → прямое измерение LUT-кривой |
| `t1kq_*`, `t1k_*` | 1 тон, fc-скан, уровни −18/0 dBFS → форма полосы |
| `burst500_b1` | основной эталон: burst 500 Гц, band1 fc=500 mode=1 sens=12 |
| `tone1k`, `tone1kq`, `dual` | входные тоны (0 / 18 / 7.1 dBFS) |
---
## Историческая численная модель (B-фазы; канон теперь — C++ FramedDetector, см. выше)
```python
# per-frame, per-bin (framed_render.py — исторический канон B.15)
xv = log10(A_k / res_k) # A_k = 2|X_k|/wsum (twin-env), res = |2B/A| case8
C = G * LUT(xv) + W * warp(f_k)**A # additive mask (НЕ мультипликация warp·LUT)
gain = max(1 - C, eps) * res_k**rp # rp = rp0 * Q**drp (res_power)
```
- LUT — Pchip, узлы из joint-фита (dual+al_*) **или** свободные 8 узлов (B.15 → mean 0.027);
- warp — эмпирический `0.87·7.942·x/(7.942+x)`, x=f/2000 (аппроксимация runtime-вычисления FUN_180563440);
- rp(Q)=0.0275·Q^0.2159 (Q-dep rp, B.14);
- B.15: G/W/A/rp0/drp = 0.9752/0.3394/1.0222/0.0254/0.2231; LUT KX=[0.8..1.0].
---
## Как всё это воспроизвести
1. **Достать бинарь**: установить soothe2 (Windows VST3) под yabridge, запустить
`dump_soothe.py``soothe_mem.bin` (или взять `.vst3`, выдернуть секции).
2. **Декомпиляция**: `analyzeHeadless <proj> soothe_x64 -process soothe_mem.bin
-noanalysis -postScript <X>.java` (image_base=0, блок `ram`=[0x180000000,…],
адреса = RVA; смещение в файле = VA0x180000000).
3. **Рендеры**: `sweep.py` генерит `.rpp`, `reaper -renderproject` выдаёт `.wav`.
4. **Анализ**: `render_parity.py` / `framed_render.py` сравнивают модель с реальными
рендерами (steady-state tone-замер, тримнутая метрика).
---
## Чего не хватает / следующие шаги (bit-exact)
Текущий фокус — C++ `FramedDetector` (`dsp/framed_model.cpp`). Открытое (см. AGENTS.md,
NOTES_LEVEL.md §2026-08-20m):
- **PRNG-пролог** FUN_180529fe0 (LCG+LUT → fVar30) — per-frame рандомизация scale/dry-wet.
- **FFT-conv** (0x535a70) — сглаживание маски перед FIR.
- **Бит-экзактный exp2** (0x26b820) вместо `std::exp2`.
- Точная обратная связь combine/аккумулятора `0x5407c8`.
- **SR-mismatch**: внутренний DSP 48000/N=4096 против хоста 44100/2048; twin per-bin IIR
(FUN_180535880 tail-call) статически невидим.
- Стерео-путь M8 (link/balance/LR-vs-MS), multiband combine 5316e0.