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.


Структура репозитория

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) в двух строках

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.pysoothe_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/стерео-путь.
S
Description
Reverse engineering of oeksound soothe2 VST3 DSP core → bit-exact buildable C++
Readme
12 MiB
Languages
C++ 59.3%
Python 36.7%
Java 3.7%
Lua 0.2%
CMake 0.1%