# 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(s−i·H(s))`, Гильберт по частоте) — аудио слышит `exp(scratch)` напрямую. - **Тела bigkernel'ов найдены** (рантайм-резолв IAT): 1803a06a0 / 180296c80 / 180323f20 / 1802dc0e0 — x87-трансценденты (exp-семейство). ### Главное за 2026-08-24…25 (сессии 24j–24kk2) 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=bands−ACC; 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=bands−ACC, 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`; самодостаточен как справочник, но не канон.
Было (B.15 — историческая численная модель) `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=(1−C)·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) ```
--- ## Структура репозитория ``` 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 soothe_x64 -process soothe_mem.bin -noanalysis -postScript .java` (image_base=0, блок `ram`=[0x180000000,…], адреса = RVA; смещение в файле = VA−0x180000000). 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).