Files
soothe2-re/README.md
T

145 lines
9.0 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 на формах
полос и уровнях).
---
## Статус (B.15, август 2026)
**Полный STFT-рендер** пайплайна работает в `framed_render.py` (N=2048, hop=512,
sqrt-Hann, per-bin twin-envelope 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) метрика** — длина выхода = длина входа (не-тримнутые замеры
давали ложный сдвиг ~0.2 dB, исправлено в B.14). Базлайн для scalar-rp = 0.280 dB.
- **Q-dependent rp** `rp(Q)=rp0·Q^drp` (rp0=0.0275, drp=0.2159) → **mean=0.175 dB**:
q0.1 и q10 почти идеальны, боттлнек q1@2000 (0.71 dB).
- **JOINT free-knot LUT** (8 узлов Pchip, G/W/A/rp0/drp) → **dual mean≈0.027 dB**,
q1@2000 закрыт до 0.000; остаток al_* lv24 dC=+0.10 (зона xv<0.3).
- **Декомп DSP-ядра в основном закрыт**: twin-резонатор, генератор case8, level-weight
(0x530d30 — численно НЕ create tilt, подтверждено при N=2048), LUT-кривая FUN_180563440
(linear/power-law по флагу), IIR-трекеры FUN_180563ce0, FFT-conv 0x535a70 —
всё размаплено, `.dis` в `handoff/nls_dasm/`.
```bash
# Текущий канон (Q-dep rp, trimmed):
python3 framed_render.py dual
```
---
## Структура репозитория
```
re-tools/
├── README.md ← вы здесь
├── roadmap.md ← журнал всех результатов/гипотез (B.1…B.15)
├── framed_render.py ← КАНОНИЧЕСКИЙ frame-рендер (STFT+LUT+warp+res_power)
├── 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, LUT-нога, res_power протокол
│ └── nls_dasm/ ← 120+ дизассемблей (f_563440, f_563ce0, f529fe0, twin…)
├── dsp/ ← реконструкция DSP-пайплайна на C++17
│ ├── twin{.hpp,.cpp} ← бит-точный twin-резонатор (FUN_180535880)
│ ├── spectral.cpp/.hpp ← WOLA/STFT-обработчик
│ ├── detect.cpp/.hpp ← детектор резонансов
│ ├── filter.cpp/.hpp ← биквад-фильтры
│ ├── 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 ← дамп памяти плагина (frida), вне 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
├── Поведенческие симуляторы: 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
```
## Документация
| Документ | Содержание |
|---|---|
| [roadmap.md](roadmap.md) | Журнал всех результатов/гипотез (B.1…B.15), статус по фазам, риски, открытые вопросы |
| [handoff/SESSION_HANDOFF.md](handoff/SESSION_HANDOFF.md) | Инвентарь декомпа (§0), трансляция/ключевые адреса (§2), Phase-5 план (§6) |
| [handoff/NOTES_TWIN.md](handoff/NOTES_TWIN.md) | Twin-резонатор (FUN_180535880/536f90), caller, grid/oversample |
| [handoff/NOTES_LEVEL.md](handoff/NOTES_LEVEL.md) | Level-path, LUT-нога, res_power протокол, спектральные веса |
| [handoff/nls_dasm/](handoff/nls_dasm/) | 120+ дизассемблей (f_563440, f_563ce0, f529fe0, twin, iface, fft) |
| [notes_giant_fft.md](notes_giant_fft.md) | FFT-планировщики/ядра/twiddle/Cody-Waite |
| [summary.md](summary.md) | Сводка по реверсу и реконструкции |
### Тестовый корпус `/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) |
---
## Модель в трёх строках
```python
# per-frame, per-bin (framed_render.py — канон)
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].
Проверить: `python3 framed_render.py dual` (mean 0.175 для Q-dep rp).
---
## Как всё это воспроизвести
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-замер, тримнутая метрика).
---
## Чего не хватает / следующие шаги
- **Структурная LUT FUN_180563440** вместо Pchip: закрыть остаток al_* lv24 (dC≈+0.10,
зона xv<0.3) и q1@500 (+0.06).
- **Бит-точная сверка**: собрать `dsp/harness.cpp`, прогнать `burst500_b1.wav`
и сверить байт-в-байт (сейчас модель — численная, rmse 0.030.18 dB).
- C++ порт res_power + Q-dep rp + свободных LUT-узлов (тривиально: `gain *= pow(res, rp0·Q^drp)`).
- Семантика IAT-хелперов `0x181a14xxx` (exp/log/pow) и AVX-ядер — целевые адреса вне дампа.
- Полный конвейер WOLA/oversample (offline 3×) и sidechain/стерео-путь.