116 lines
11 KiB
Markdown
116 lines
11 KiB
Markdown
# Roadmap: bit-exact реверс DSP-ядра oeksound soothe2 (v1.1.2)
|
||
|
||
## Цель
|
||
Воспроизвести DSP-путь soothe2 в виде **собираемого standalone C++** с **побайтово идентичным**
|
||
выходом (bit-exact) на верификационных свипах, с пониманием логики и подписанными функциями.
|
||
|
||
## Результат
|
||
`re-tools/dsp/` — `*.cpp/*.h` (классы DSP, подписаны), `CMakeLists.txt`, `harness.cpp`
|
||
(WAV16 → рендер → WAV24), `dsp_notes.md` (карта функция↔смысл, константы, LUT),
|
||
`verify_bit_exact.py` (прогон всех свипов, побайтовое сравнение).
|
||
|
||
## Задел (уже есть)
|
||
- Ghidra-проект `ghidra-proj/soothe2.rep` + скрипты `Dump*.java`/`ImportRtti*.java`/`Diag.java`.
|
||
- RTTI иерархия DSP: `SpectralProcessor<float,7,1>`, `Soothe2ModuleBase<float,1>`,
|
||
`Soothe2Module<float,1>`, `FilterGraph<float,6,0x400>`, `FilterGraphGrid<float>`,
|
||
`DigitalFilter<float,0xBA,1>`, `IIRFilterExtended<float,1>`, `AudioProcessingModule<float,1>`
|
||
(`rtti_dsp.json`, `rtti_full.json`).
|
||
- Декомпиляция vtable-слотов DSP-классов (`decomp_dsp.txt`, `decomp_vtables.txt`,
|
||
`decomp_candidates.txt`) — 6634 строки C, 127 неразрешённых `FUN_*`, ~30 констант `DAT_1824c*`.
|
||
- Поведенческая модель `sim_v5.py` (RMSE ≈ 0.3dB): base_red, W(f)=6.02·min(1,sens/12)·H,
|
||
H=1/√(1+(Qeff·A)²), Qeff=1.54·q^1.33, sat(level), Q_notch=qn·(1−g)+1, per-frame env, WOLA-маска.
|
||
- LUT: depthcurve (207 float @0x1826170e8, `0.302+0.698·sin(π/2·x)^0.94`, r²=0.99999).
|
||
- Рендер-свипы в `/home/m/soothe-bt/` (506 wav + 609 rpp) — эталон для bit-exact.
|
||
- BFS-замыкание call-graph от сидов: `fun_map.txt` (2035 функций), `decomp_funs.txt` (1640 декомпилировано,
|
||
312K), `consts.txt` (11602 констант) — после фикса `getInstructions()` в `DumpFuns.java`.
|
||
|
||
## Контракт из manual (`soothe2_ManualFAQ.pdf`, v1.0.0)
|
||
|
||
| # | Факт | Следствие для DSP |
|
||
|---|---|---|
|
||
| M1 | Soft/Hard: выбор режима меняет всё; depth 3.0 в soft ≠ hard (стр.7). Soft менее level-dependent | mode = глобальный препроцессор детектора (перемасштаб глубины/масштаба) |
|
||
| M2 | Depth — референтный dB (−18..18), реальная глубина до 60dB (стр.8) | вход depthcurve-LUT; clip 60dB = `red.clip(0,60)` в sim |
|
||
| M3 | Sharpness: выше = глубже и уже (стр.8) | Q_notch растёт с sharp; подтверждает `Q≈qn·(1−g)` |
|
||
| M4 | Selectivity: выше = только сильные резонансы; 0 = «everything must go» (стр.9) | selectivity = отбор/порог пиков, число нотчей НЕ ограничивает |
|
||
| M5 | Attack/Release: референтные константы, **реальные времена частотно-зависимы**, атака быстрее на ВЧ (стр.11) | Env НЕ один one-pole; нужен частотный масштаб времени — замер τ(f) + код |
|
||
| M6 | Oversample = «расчёт reduction-фильтра в более высоком спектральном разрешении» (стр.11) | это интерполяция детекторной сетки → фильтра, НЕ up/downsample аудио |
|
||
| M7 | Resolution = «частота обновления детекции и фильтра» (стр.11), eco/high/ultra | период пересчёта коэффициентов фильтра |
|
||
| M8 | Stereo: link 100% = сумма каналов для анализа + общий фикс; 0% = dual mono; balance глоб+per-band; L/R vs M/S | детектор на sum; коррекция симметрична/раздельна |
|
||
| M9 | Trim: «регулирует ТОЛЬКО wet»; Delta = x−wet; Mix 0..100% (стр.14) | порядок: detect→notch→trim→mix/delta/bypass |
|
||
| M10 | Банды: 2 cut + 4 general; general: peak/shelf/reject/tilt (стр.17-19) | `FilterGraph<float,6,0x400>`, `DigitalFilter` мультитипный |
|
||
| M11 | Кривая = «inverse EQ / side-chain EQ», сумма band-кривых = белая кривая (стр.17) | `red(f)=base+ΣW_i·H_i` подтверждён |
|
||
| M12 | Sidechain: детект по второму входу; Input trim — скрытый предусилитель анализа (стр.26) | analysis имеет отдельный вход/гейн — граф с 2 входами |
|
||
|
||
## Топология пайплайна (Приложение A, стр.30 — OCR `pipeline_ocr.txt`)
|
||
|
||
```
|
||
main inputs ──┐
|
||
sidechain ────┤ (sidechain enabled?)
|
||
▼
|
||
input trim ──► analysis (детекция; depth/sharpness/selectivity; EQ curves из per-band balances)
|
||
│
|
||
stereo apply + balance (channel link + global balance)
|
||
│
|
||
ms encode? ──► processing (нотч-синтез в M/S) ──► ms decode?
|
||
│
|
||
mix ──► bypass? ──► output
|
||
```
|
||
- **mid/side ручка**: обработка в M/S-области (`ms encode?`/`ms decode?`) — стерео-путь подтверждён диаграммой.
|
||
- GUI/visualisation (depth/sharpness/selectivity, EQ curves, per-band balances) — побочная ветвь, в DSP-ядро не входит (кроме параметров).
|
||
|
||
## Фазы
|
||
|
||
### Фаза 0 — Снятие диаграммы пайплайна (сделано)
|
||
0.1. OCR стр.30 (`tesseract`, PNG в `/tmp/opencode/manual/p30_s4.png`) → `pipeline_ocr.txt`. ✅
|
||
|
||
### Фаза A — Полный статический RE (Ghidra headless)
|
||
- A.1. Декомпилировать все 127 `FUN_*` из vtable-слотов 8 DSP-классов → полный `processBlock`-путь.
|
||
- A.2. Извлечь float-константы `DAT_1824c*` + все LUT-таблицы в `.data` (поиск массивов float).
|
||
- A.3. **Идентифицировать FFT** (twiddle/бит-реверс/радикс) и окно (формула `sin²` vs LUT) — ключ bit-exact.
|
||
- A.4. Найти частотно-зависимые attack/release (M5): функции масштабирования времени по частоте.
|
||
- A.5. Найти oversample/resolution-путь (M6/M7): интерполяция сетки, период обновления.
|
||
- A.6. Сопоставить топологию с диаграммой (0.1) и stereo/link/balance (M8).
|
||
|
||
### Статус фазы A (~40%)
|
||
- **A.2 константы — готово**: `consts.txt` (11602 из инструкций BFS-замыкания, 2670 в DSP-диапазоне
|
||
0x1824c0000–0x182700000) из повторного прогона `DumpFuns.java`. Точная double/f32-интерпретация —
|
||
`consts_double.txt` (чтение PE напрямую, `.data` vs `.rdata` через RVA).
|
||
- **A.3 ОКНО — найден**: `FUN_1805356f0` генерирует **Hann** `w(i)=0.5·(1−cos(2πi/N))`
|
||
(double 2π @0x1824c4e00/4248, 1.0 @4da0/4140, 0.5 @4c90/4050; хвост через `cos`, чётные пары через
|
||
SIMD `divpd`+`thunk_FUN_181a114e0(=cos)`). Размер N: `FUN_18052e130` →
|
||
`N = 2^floor(log2(sr/44100))·N0` (44100 @0x1824c45ec, 2.0 @41e8). **Нормализация окна** в
|
||
`FUN_18052df60`: сумма по окну → `scale=1/(mean/N)`, потом `/= (fsize @+0x1ac)`.
|
||
- **A.3 FFT — НЕ подтверждён для DSP**: найденный планировщик `FUN_181384180` (radix-4/8 стадии,
|
||
cos/sin twiddle, бит-реверс, log2(N)) — это **Vorbis-декодер** (строка `s_vorbis_1821e47fc`);
|
||
в DSP-пути не участвует. В бинарнике нет kissfft/fftw/MKL-FFT строк. DSP-STFT-FFT ищется дальше.
|
||
- **Константы детектора**: `10^((dB)/20)` через `expf((30·X−90)·0.115129255)`,
|
||
`0.115129255=1/(20·log10e)`, `−6.907755=−ln1000`, `0.707`/`0.51`/`0.54`/`0.001`,
|
||
маппинг резонанса `(x−0.707)·0.5+1` в `FUN_1805316e0`.
|
||
|
||
### Фаза B — Реконструкция C++ (bit-exact)
|
||
- B.1. Классы 1:1: `Soothe2FilterGraph::processBlock`, `FilterGraphGrid`, `DigitalFilter`,
|
||
`SpectralProcessor` (STFT), `AudioProcessingModule`, `IIRFilterExtended`, env-модуль с
|
||
частотным масштабом (M5), интерполятор oversample (M6), периодичность resolution (M7).
|
||
- B.2. Точный порядок FP-операций float32, без перестановок; буферный фрейминг независимо от блока хоста.
|
||
- B.3. Имена по смыслу + подписи, сверяемые с моделью sim_v5 (base_red, W, Qeff, sat, Q_notch).
|
||
- B.4. Модули по manual: mode (M1), input trim/sidechain (M12), trim/delta/mix/bypass-порядок (M9),
|
||
stereo/link/balance + ms encode/decode (M8).
|
||
|
||
### Фаза C — Сборка и bit-exact-верификация
|
||
- C.1. `libsoothe2_dsp.so` + CLI-харнесс (WAV16→рендер→WAV24, как Reaper).
|
||
- C.2. Критерий: каждый свип (`trk_*`, `wf_*`, `qmap`, `sens`, `lvl`, `neut`, `al`, `psh`,
|
||
`dual`, `pair`, `chirp`, `burst`, `tj`) — **побайтовая идентичность** int24 PCM с рендерами.
|
||
- C.3. Итеративный цикл diff → локализация блока → фикс → повтор.
|
||
|
||
## Риски
|
||
- **FFT bit-exact**: свой планировщик есть только у Vorbis-декодера; DSP-STFT-FFT ещё не найден (A.3) —
|
||
если свой radix-2 — воспроизводим напрямую; главный нерешённый риск фазы A.
|
||
- **Хост-зависимость**: уточнить точный размер буфера/фрейма (рендеры при разных RENDER_RANGE должны давать одинаковые байты).
|
||
- **Stereo**: все текущие свипы mono; для бит-exact графа M8 нужны стерео-рендеры (link/balance/ms).
|
||
|
||
## Открытые вопросы
|
||
1. Где реальный DSP-STFT FFT (не Vorbis/не внешний lib)? Искать большие функции с butterfly/стадиями
|
||
в 18052*/18053* или через трассировку вызовов от `processBlock` спектрального процессора.
|
||
2. Окна/oversample детали — из декомпиляции (A.6) или требуется доп. замеры.
|
||
3. Стерео-верификация (M8) — приоритет mono-путь или сразу стерео-граф.
|