Files
soothe2-re/README.md
T

212 lines
15 KiB
Markdown
Raw Permalink 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++ с **bit-exact** целью.
Цель — понять, как именно плагин считает подавление резонансов (уровневый детектор,
маска, фильтр), и воспроизвести это дословно. Текущий активный канон — C++
`FramedDetector` (`dsp/framed_model.cpp`): real mask-apply цепь с live-таблицами.
Канонные команды и метрика — в [`AGENTS.md`](AGENTS.md).
---
## Статус (24kk2, 2026-08-25)
**Цель — bit-exact реверс** (гейт смены канона зафиксирован пользователем: только
после прослеживания всех параметров до декомпа и схождения корпуса в шумовой пол).
Декомпиляция DSP-ядра закрыта (~95%); дизассемблы в `handoff/nls_dasm/` (~140).
**Применение декодировано до формул** (сессия 24j…24kk2):
```
mask(b) = 10^(cut_D(b)/20) ← вещественная, per-bin multiply кадра
cut_D(b) = α·ln(1+lvl_raw(b)/β)+c [+Δ у вторых пиков]
lvl_raw = am/res·scale (наш детектор-фронтенд, float-parity ✓)
слой = STFT БЕЗ синтез-окна (RT_SYN=1)
```
Калибровки формы (три независимых семейства, rms ≤0.016 дБ): α/β/c зависят от
контента (α удваивается с числом тонов — частотное смешение шаблонно-локальное),
q НЕ влияет на закон, sens входит линейно через lvl_raw.
- **dual-семейство решено**: корпус 22 случая **mean 0.193 / max 0.438 dB**
(канон 3.264). Флаги: `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0`.
- **Канон не тронут**: TOTAL 2.286 (env-gated эксперименты живут рядом).
- **Буфер FIR@540668** = промежуточное мин.-фазовое представление
(`exp(si·H(s))`, Гильберт по частоте) — аудио слышит `exp(scratch)` напрямую.
- **Тела bigkernel'ов найдены** (рантайм-резолв IAT): 1803a06a0 / 180296c80 /
180323f20 / 1802dc0e0 — x87-трансценденты (exp-семейство).
### Главное за 2026-08-24…25 (сессии 24j24kk2)
1. **Применение = побиновный complex-multiply кадра** на маску; «магический ×1.805»
оказался произведением экспонент стадий построения буфера (0.984×1.8345).
2. **Закон уровня универсальной формы** `α·ln(1+L/β)+c` — подтверждён тремя
независимыми калибровками; константы зависят от контента (число тонов) и слабо от fc.
3. **Слой STFT**: плагин НЕ домножает выход обратного FFT на окно
(`RT_SYN=1`); WIN_WINDOW движка — фейд 0.5→0.8 ровно за 2049 сэмплов (=бинам кернела).
4. **GUI/аудио разделение**: LUT-строитель FUN_180563a60 — GUI-ветка; аудио-компрессия
живёт в семантиках шагов 9–19 BLOCKMAP.
5. **Dataflow шагов 9–16 декодирован**: vec6f8=bandsACC; fma тройками
(re,im,coef) с ATT/REL; th2000=поэлементное умножение массивов (не axpy!);
шаг 12=COPY (исправлен старый BLOCKMAP).
6. **Инструменты**: rendersnap2 v7 (мягкий STOP-снаппер со слотами+скалярами,
RENDER_FILE-фикс), patchparam.py (правка VST-чанка RPP!), campaign.py
(ячейка параметризации), disasm_func.py (capstone с RIP-константами),
iat_name.py (рантайм-резолв импортов через PE-экспорты).
```bash
# Сборка и канонные команды:
cmake -S dsp -B dsp/build && cmake --build dsp/build --target framed_test render48k
./dsp/build/render48k /home/m/soothe-bt/tone1kq.wav /tmp/o48.wav 1000,0.99999785,12
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
```
Детальная метрика и история — в `AGENTS.md`, `BITEXACT_PLAN.md`, `handoff/NOTES_LEVEL.md`
(апдейты 20j…24kk2).
### Открытые bit-exact пробелы
**Приоритет №1 — каскадный симулятор шагов 9–19**: dataflow декодирован
(24hh/24ii: vec6f8=bandsACC, fma-тройки att/rel, ×track, ×warp, центрирование −1),
тела bigkernel'ов найдены по рантайм-адресам — осталось сложить оп-за-опом и
проверить на датасетах sc_* (rms каскада сейчас ~0.42 на угаданных формах).
Далее: k-маппинг фронтенда (twin/am формулы), Δ-правило вторых пиков из pre-combine.
Полный список — `AGENTS.md`, `BITEXACT_PLAN.md`, `NOTES_LEVEL.md` (24bb→24kk2).
> Исторический блок (поведенческая/численная модель 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
├── Параметр-мост и live-capture: setparam.lua, dump_params.lua,
│ play.lua, scripts/step7_capture.py, scripts/rpp_setparam.py,
│ scripts/corpus{,_structural}.py, scripts/phaseA/B*.py
└── summary.md, notes_giant_fft.md
```
## Документация
| Документ | Содержание |
|---|---|
| [AGENTS.md](AGENTS.md) | **Старт для агента**: сборка, метрика, bit-exact пробелы, структура |
| [BITEXACT_PLAN.md](BITEXACT_PLAN.md) | **Путь к bit-exact**: порядок работ (8 шагов), критерии, риски, точка входа |
| [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)
Текущий фокус — **детекторный фронт** (Шаг 9 плана): амплитудная нормировка am,
форма res_k, сила IIR1/2 вдоль частоты — модель теряет ×2.8 уровня на изолированных
пиках относительно реального плагина (NOTES_LEVEL 22g/22i). Открытое:
- **Детекторный фронт** (приоритет №1, Шаг 9) — см. выше.
- **PRNG-пролог** FUN_180529fe0 (LCG+LUT → fVar30) — per-frame рандомизация scale/dry-wet.
- **FFT-conv** (0x535a70) — сглаживание маски перед FIR (P3).
- **Бит-экзактный exp2** (0x26b820) вместо `std::exp2`.
- Точная обратная связь combine/аккумулятора `0x5407c8` (консюмер не найден).
- **SR-mismatch**: внутренний DSP 48000/N=4096 против хоста 44100/2048.
- Стерео-путь M8 (link/balance/LR-vs-MS), межполосное суммирование (Шаг 8).