Files
soothe2-re/README.md
T

148 lines
8.9 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.1 dB на формах полос).
---
## Статус (B.10, август 2026)
**Детектор расшифрован структурно и проверен численно на двух независимых наборах
измерений (31 точка, совместный фит rmse = 0.109 dB).**
- **Freq-path (форма полосы)** — близнец `FUN_180535880`/`FUN_180536f90` (резонатор
2-го порядка). Коэффициенты case8 (mode=1 исполняется как case8!):
`w0=2π·fc/fs; p=(sin(w0)·0.5)/Q; A=[p·gain+1, 2cos(w0), 1p·gain];
B=[p/gain+1, 2cos(w0), 1p/gain]; res(f)=|2·B/A|(f)`.
- **Level-path (глубина)** — per-band маска `= depth·tilt(f)·D0·(L0/res(f))^p`,
где `tilt(f)=1w(f)` — per-bin спектральный вес из `FUN_180530d30`
(`w[i] = log10(1/exp(...·exp(2000/f_bin))...)`), `L0` — уровень входа, `p≈0.085`.
- **Ключевые факты**:
- `gain ≈ 10^(sens/20)` (sens=12 → gain≈4.1);
- частотная селективность — НЕ notch самой маски, а резонанс в знаменателе:
врез глубже там, где `L0/res` больше (на центре полосы);
- «парадокс dual_b1q» (red@2000 > red@500 при низком Q) решён: это частотный
наклон `tilt(f)` (растёт с частотой), а не ширина резонанса;
- warp-ось `0x540748` = сигмоида `8.3 7/(1+exp((x·20000120)·(0.01)))`,
а НЕ `f/(f+K)`, как ошибочно предполагалось раньше.
Подробная история, гипотезы и все промежуточные находки — в [`roadmap.md`](roadmap.md).
---
## Структура репозитория
```
re-tools/
├── README.md ← вы здесь
├── roadmap.md ← журнал всех результатов/гипотез (B.1…B.10)
├── model_dual.py ← КАНОНИЧЕСКАЯ верифицированная модель (B.10)
├── dsp/ ← реконструкция DSP-пайплайна на C++17
│ ├── rotor_kernel.hpp ← бит-точное ядро ротора (близнец детектора)
│ ├── 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 ← быстрый exp/log (CodyWaite)
│ ├── soothe_constants.hpp ← константы из дампа
│ ├── harness.cpp ← CLI: input.wav → output.wav (24-bit)
│ └── CMakeLists.txt
├── *.java ← Ghidra-скрипты (analyzeHeadless, пост-скрипты)
│ ├── ListFuns / DumpFuns / DumpList ← списки функций/декомпиляция
│ ├── DumpVtables / DumpDsp / DumpCandidates
│ ├── ImportRtti, SearchRefs, Diag ← RTTI/перекрёстные ссылки
│ └── ...
├── ghidra-proj/ ← Ghidra-проект (soothe2.gpr/.rep), вне git
├── soothe_mem.bin ← дамп памяти плагина (frida), вне git
├── regions/, regions_* ← регионы памяти из дампа
├── phase_table_1024.f64/.json← таблица фаз (FFT)
├── Измерение и фиты полосы:
│ ├── measure.py ← анализ WAV: глубина вреза, attack/release тау
│ ├── probe.py ← спектральный анализ тонов
│ ├── bandshape.py ← эмпирическая форма полосы из рендеров
│ ├── fit_bandshape.py ← фит формы полосы
│ ├── fit_curves.py ← фит кривых (глубина/частота)
│ ├── fit_maskaxis.py ← brute-force подбор формулы маски (B.7/B.8)
│ ├── model_dual.py ← ВЕРИФИКАЦИЯ модели (dual_b1q + t1kq)
│ └── notch.py ← расчёт/проверка notch
│ ── Поведенческие симуляторы:
│ ├── sim.py, sim_v5.py ← STFT-детектор (эволюция гипотез о архитектуре)
│ └── verify_sim.py
│ ── Инструменты REAPER-рендеров:
│ ├── sweep.py ← генерит .rpp с изменением параметров плагина
│ ├── run_sweep.py, tt_sweep.py, run_verif*.py
│ ├── patchparam.py ← патчит PARAM в base64-блоке состояния
│ └── addfx.lua ← добавление soothe2 в проект
│ ── Живая трассировка (yabridge-host + Frida):
│ ├── dump_soothe.py ← дамп всей памяти плагина → soothe_mem.bin
│ ├── rtall.py / rtscan.py / rtsig.py / rttbl.py / rtver.py
│ ├── rtone.py / rtsig.py / rtwin.py / rtdeep*.py / rtdump*.py
│ └── probe.py, procdump.py, ghidra_diag.py, mkbase.py
├── *.txt / *.log ← декомпиляции, лямбли-дампы, xrefs (архив)
├── summary.md, notes_giant_fft.md
└── curve_fits.npz, depthcurve.npy, consts*.txt
```
### Тестовый корпус `/home/m/soothe-bt/` (вне git)
~600 рендеров REAPER (`.rpp` + `.wav`), сгенерированы скриптами выше. Ключевые наборы:
| Префикс | Что измеряет |
|---------|-------------|
| `burst500_b1` | основной эталон: burst 500 Гц, band1 fc=500 mode=1 sens=12 depth=0.864 |
| `dual_b1q_*` | 2 тона (500+2000), q полосы 0.1…10 → **парадокс red2000>red500** |
| `t1kq_only1_*`| 1 тон 1 кГц, fc-скан → форма полосы (rmse 0.047) |
| `t1k_b1f_*`, `t1kq_b1f_*` | fc-сканы, другие q/сенс |
| `tone1k`, `tone1kq`, `dual`, `n5_b1q_*`, `b2*`, `al_*` | уровни/частоты/сенсы |
Входы: `dual.wav` (500+2000 Гц, 7.1 dBFS), `tone1k.wav` (0 dBFS), `tone1kq.wav` (18 dBFS).
---
## Модель (B.10) в двух строках
```python
red(f) = -20*log10(1 - C(f))
C(f) = depth * tilt(f) * D0 * (L0 / res(f))**p
# res(f) = |2B/A|(f; fc, Q, gain) — case8, tilt(f) = 1 - w(f) — веса 0x530d30
# p ≈ 0.085, gain ≈ 10**(sens/20), tilt ratio(2000/500) ≈ 1.27
```
Проверить: `python3 model_dual.py` (31 точка, rmse 0.109 dB).
---
## Как всё это воспроизвести
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. **Анализ**: `measure.py fx.wav dry.wav --fc 500` → глубина/тау;
`bandshape.py` → форма полосы; `model_dual.py` → верификация модели.
---
## Чего не хватает / следующие шаги
- Бит-точная сверка рендера: собрать `dsp/harness.cpp`, прогнать `burst500_b1.wav`
и сверить байт-в-байт (сейчас модель — численная, а не побайтовая).
- Семантика IAT-хелперов `0x181a14xxx` (exp/log/pow) и AVX-ядер (`FUN_18001a5a0`
и др.) — целевые адреса вне дампа.
- Точная функция LUT `level→mask` (кривая `param_1+0x188`) вместо аппроксимации `^p`.
- Полный конвейер WOLA/oversample (offline 3×) и sidechain/стерео-путь.