docs: roadmap B.12-B.15 journal + README rewrite (framed_render canonical, honest metric)

- roadmap: current status header (decomp closed, frame-render model, honest metric),
  new assets (fraed_render/render_parity/handoff/nls_dasm/rwin tables), journal
  B.12 (bridge), Phase5 (dB parity 0.268), B.13 (res_power+decomp LUT/IIR),
  B.14 (Q-dep rp + metric artifact correction), B.15 (joint free-knot LUT, dual mean 0.027).
- README: rewrite to framed_render.py as canonical model; 3-line model formula;
  structure + corpus tables updated; honest-metric warning; next steps focused.
This commit is contained in:
2026-08-19 17:09:21 +03:00
parent 28cd3b7c62
commit 59619e4648
2 changed files with 167 additions and 90 deletions
+98 -7
View File
@@ -9,6 +9,25 @@
(WAV16 → рендер → WAV24), `dsp_notes.md` (карта функция↔смысл, константы, LUT),
`verify_bit_exact.py` (прогон всех свипов, побайтовое сравнение).
## Текущий статус (2026-08-19)
**Статический декомп DSP-ядра — В ОСНОВНОМ ЗАКРЫТ** (A.2/A.3/FFT/twin/generator/level-path/LUT/IIR декодированы,
`.dis` в `handoff/nls_dasm/`). **Численная модель — frame-рендер `framed_render.py`** (STFT N=2048, hop=512,
sqrt-Hann, twin-env tatt=11ms/trel=80ms, LUT+additive-warp+res_power). **Честная тримнутая метрика**
(длина выхода = длина входа): Q-dep rp → **mean=0.175 dB** (q0.1/q10 идеальны, боттлнек q1@2000=0.71);
joint free-knot LUT + dual+al_* фит → **dual mean≈0.027 dB** (B.15). Цель bit-exact (Phase C) — в работе;
остаток структурный (форма LUT в зоне xv<0.3, warp-терм).
```bash
# Простейший запуск текущего канона (Q-dep rp):
cd /home/m/re-tools && python3 framed_render.py dual
# q0.1: 500 +0.00 / 2000 +0.00 · q1: 0.26/0.71 · q10: +0.09/+0.00 · mean 0.175
```
**ВАЖНО про метрику**: ранние записи "mean=0.160/0.144" считались на выходе НЕтримнутой длины
(`nfr*HOP+N`) и оказались артефактом; честный сравнительный базлайн для scalar `rp=0.0169` = **0.280**
(не 0.160). Все новые числа ниже — на ТРИМНУТОЙ метрике.
## Задел (уже есть)
- Ghidra-проект `ghidra-proj/soothe2.rep` + скрипты `Dump*.java`/`ImportRtti*.java`/`Diag.java`.
- RTTI иерархия DSP: `SpectralProcessor<float,7,1>`, `Soothe2ModuleBase<float,1>`,
@@ -23,6 +42,13 @@
- Рендер-свипы в `/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`.
- **Фрейм-рендер**: `framed_render.py` — полный STFT-рендер (N=2048, hop=512, sqrt-Hann,
twin-env, `C(f)=G·LUT(xv)+W·warp(f)^A`, `gain=(1C)·res^(rp0·Q^drp)`); режимы `dual|al`.
- **dB-parity харнесс**: `render_parity.py` (Goertzel-замер steady-state тона, 16/24-бит, mono/stereo).
- **Критичные дизассембли**: `handoff/nls_dasm/{f_563440,f_563ce0,f529fe0}.dis` (защищены из /tmp).
- **Живые таблицы** (48k): `rwin_{A0,A1,B0,C0}.npy` (частотное окно/power-law/level-weight), `r_freqaxis.npy`.
- **Handoff-документация**: `handoff/SESSION_HANDOFF.md` (инвентарь декомпа, трансляция),
`handoff/NOTES_TWIN.md`, `handoff/NOTES_LEVEL.md` (LUT-нога + res_power протокол).
## Контракт из manual (`soothe2_ManualFAQ.pdf`, v1.0.0)
@@ -71,7 +97,7 @@
- A.5. Найти oversample/resolution-путь (M6/M7): интерполяция сетки, период обновления.
- A.6. Сопоставить топологию с диаграммой (0.1) и stereo/link/balance (M8).
### Статус фазы A (~40%)
### Статус фазы A (~90% — декомп закрыт, остались runtime-значения/окно 0x540658)
- **A.2 константы — готово**: `consts.txt` (11602 из инструкций BFS-замыкания, 2670 в DSP-диапазоне
0x1824c00000x182700000) из повторного прогона `DumpFuns.java`. Точная double/f32-интерпретация —
`consts_double.txt` (чтение PE напрямую, `.data` vs `.rdata` через RVA).
@@ -111,16 +137,21 @@
- C.3. Итеративный цикл diff → локализация блока → фикс → повтор.
## Риски
- **FFT bit-exact**: свой планировщик есть только у Vorbis-декодера; DSP-STFT-FFT ещё не найден (A.3) —
если свой radix-2 — воспроизводим напрямую; главный нерешённый риск фазы A.
- **FFT bit-exact**: DSP-STFT FFT найден (планировщики-гиганты + stage-ядра, см. recap ниже);
воспроизводим напрямую. Главный нерешённый риск bit-exact — порядок double-операций в dispatch-ядрах.
- **Хост-зависимость**: уточнить точный размер буфера/фрейма (рендеры при разных RENDER_RANGE должны давать одинаковые байты).
- **Stereo**: все текущие свипы mono; для бит-exact графа M8 нужны стерео-рендеры (link/balance/ms).
- **Честная метрика**: тримнутая длина выхода обязательна; НЕ-тримнутые замеры дают ложный сдвиг ~0.2 dB
(артефакт B.14). Все fit-цели перепроверять на `render_trim`/`framed_render.synthe`.
## Открытые вопросы
1. Где реальный DSP-STFT FFT (не Vorbis/не внешний lib)? Искать большие функции с butterfly/стадиями
в 18052*/18053* или через трассировку вызовов от `processBlock` спектрального процессора.
2. Окна/oversample детали — из декомпиляции (A.6) или требуется доп. замеры.
3. Стерео-верификация (M8) — приоритет mono-путь или сразу стерео-граф.
1. **Форма LUT в зоне xv<0.3**: joint-fit свободных узлов даёт dual≤0.07, но al_* lv24 dC=+0.10 —
нужна структурная кривая FUN_180563440 (linear/power-law по флагу полосы) вместо Pchip-эмпирики.
2. **warp-терм**: эмпирический `0.87·7.942·x/(7.942+x)` — аппроксимация runtime-вычисления
(не таблица); для bit-exact заменить на реальный расчёт FUN_180563440.
3. **Стерео-верификация (M8)** — приоритет mono-путь или сразу стерео-граф.
4. **0x540658 window** — статически невидим; живые копии в `rwin_*.npy` (48k) требуют
warp-нормализации под 44.1k рендеры.
## Phase A progress update (recap)
@@ -456,3 +487,63 @@
- **Файл**: `/home/m/re-tools/model_lut.py` — КАНОНИЧЕСКАЯ модель B.11 (узлы LUT,
red(), rmse). Вспомогательные: fit_level.py (отказ p-степени), fit_lut*.py
(логистика/gamma — хуже), extract_lut.py (коллапс), verify_lut*.py (PCHIP-фит).
### B.12 — Bridge-модель + диагностика уровня (2026-08-18, handoff §5)
- **Модель** (36 точек, rmse=0.236 dB): `C(f) = g·LUT(log10(L0/res(f))) + w·warp(f)^a`,
g=1.221, w=0.358, **a=3.143≈π** (кратный каскад freq-axis ∏0x540688/FFT-conv).
- **Модель аддитивная**, НЕ мультипликация warp·LUT (та проваливается >10 dB).
warp-терм даёт +0.15 на 2000 Гц и ≤0.02 на 1000 (иначе t1k рушится).
- **FFT-conv полностью размаплен** (0x52b550..0x52b8b5, plan 0x540530, окна 0x540548/550/598);
freq-axis 0x540698 = OFFLINE константа (exp/rand = online-дитер).
- **Остаток**: 0.7 dB при Q=0.1 — форма LUT-колена x≈0.574, не закрывается per-bin.
### Phase 5 — dB-parity на реальных wav (2026-08-18, commit f96bb42)
- **`render_parity.py`**: Goertzel steady-state замер прямым корреляционным детектором
(см. handoff §5; трим по моно-сумме каналов).
- **Результат** (канонический B.12, 36 точек): **TOTAL rmse=0.268 dB**
(dual500 0.130, dual2000 0.387, t1kq 0.320, t1k 0.076); рефит (g,w,a)=(1.224,0.379,3.405)→0.264,
+2 LUT-узла→0.203 (плато). Остаток структурный (0x540658 FFT-шейпинг + level-область).
### B.13 — res_power + полный декомп LUT/IIR (2026-08-19, commits 627e837/9542201/a909aae)
- **res_power breakthrough**: `gain = (1C) · res^rp` решает 500Hz-residual для q0.1/q1
(envRmse q0.1@500 0.64→0.05 dB). Источник — res-зависимый gain в res-weighted пути
в twin-mask factory FUN_18056e3e0.
- **FUN_180563440 декодирована** (222 строки): 3 фазы/фрейм:
1) 1024-bin LUT loop (`x=k·0.0009775`, кривая +0x188: min/max/gamma/flag/nodes);
2) twin-mask factory FUN_18056e3e0 (6 полос × 1024 бин, stride 0x2000, combine 1−Σ);
3) combine loop с 0x198 (double, stride 8).
Paths: callback-dynamic / **power-law** `sign(x)·10^(log10|x|/threshold)` (flag=1) / **linear** `(BA)x+A`.
- **FUN_180563ce0 декодирована** (ИНИТ per-bin IIR level-трекеров): 341 бин, order-3,
α=0.1, инициализация [1,0,0,0]/[1,0,0,0]; UPDATE-loop — в другом месте.
- **FUN_180529fe0** (коэффициенты): PRNG-LCG, depth `powf(normalized, depth)`, mask 1C.
- **Константы из дампа**: SCALE=1/1024 (0x24c3c54), ONE=1.0, TWO=2.0, NEG1=1.0, HALF=0.5,
DEPTH_SCALE=4.0, DB_CONV=8.6859, FLOOR=6.9078, IIR α=0.1.
- **G/W/A/rp=1.0066/0.3261/1.0764/0.0169** — dual-only (mean=0.160 по СТАРОЙ метрике).
### B.14 — Q-dep rp + ЧЕСТНАЯ метрика (2026-08-19, commit 28cd3b7) — артефакт-коррекция
- **АРТЕФАКТ**: ранние fit-скрипты рендерили выход длины `nfr·HOP+N` (не тримнутый),
tone-замер шёл по последним 0.75s этого расширенного буфера → систематический сдвиг ~+0.2 dB.
Честный (trimmed, длина=len(x)) базлайн для scalar rp=0.0169 = **0.280**, НЕ 0.160.
- **Q-dependent rp** (физический warp сохранён): `rp(Q)=rp0·Q^drp`, G/W/A/rp0/drp =
0.9963/0.3335/0.9807/0.0275/0.2159 → **trimmed mean=0.175**, max=0.707:
q0.1: +0.000/+0.001 · q1: 0.256/0.707 · q10: +0.086/+0.001.
q0.1 и q10 почти идеальны; боттлнек q1@2000 (over-reduction).
- Pефрактор: weighted-фиты ошибку перекладывают (q1@20000.71→q0.1@2000+0.89), max не идёт ниже 0.7.
- Free-warp (8 параметров, alpha=3.19): mean=0.139, но warp НЕ физичен и q1@2000=0.53 — отброшен.
- **Спектральные веса FUN_180530d30 — повторно NEGATIVE при N=2048** (наш рендерный N):
w(500)=w(1000)=w(2000)≈10⁻⁴⁰…10⁻²⁰⁰ → 1w→1.0, тилта 2000-vs-500 нет (подтверждает вердикт 2026-08-18).
### B.15 — Диагностика LUT-form + JOINT free-knot LUT (2026-08-19) — dual прорван
- **Диагноз q1@2000**: наш Pchip-LUT **немонотонен** (провал 0.366@xv=0.5012 → пик 0.4552@xv=0.5);
замеренная al_*-референс-кривая монотонно убывает, at xv=0.419 LUT_ref=0.362 vs ours=0.457
**dC=0.095 (over-reduction)** — ровно зона q1@2000 (xv≈−0.57).
- **FIX: совместный фит LUT-узлов** (8 узлов Pchip, KX=[0.8..1.0]) + G/W/A/rp0/drp
по dual (6 точек, trimmed-рендер) + al_* (6 точек, прямой C-замер):
**G/W/A = 0.9752/0.3394/1.0222, rp0=0.0254, drp=0.2231; LUT строго монотонен.**
**dual errs: q0.1 +0.07/0.00 · q1 0.06/0.00 · q10 0.00/0.00 → mean≈0.027 dB!!**
(q1@2000 закрыт до 0.000; единственный остаток q0.1@500 +0.07).
- Cross-check с фиксированной измеренной al_*-LUT (без свободы узлов): dualmean=0.081,
но al_* dC до +0.16 (узлы не совпадают с dual-поведением) → свободные узлы нужны.
- **Остаток**: al_* lv24 dC=+0.10 (наша LUT всё ещё завышает на xv<−0.3).
Следующий шаг: form-констрейн (линейный/мультисегментный LUT без провалов) или
структурная runtime-кривая FUN_180563440 вместо Pchip.