Files
soothe2-re/AGENTS.md
T

186 lines
14 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.
# AGENTS.md — guide for AI agents working in this repo
Bit-exact реверс DSP-ядра oeksound soothe2 (VST3) → транскрипция на C++18 в `dsp/`.
Полное журналирование — в `handoff/NOTES_LEVEL.md`, `handoff/NOTES_TWIN.md`,
`handoff/NOTES_CAPTURE.md`, `roadmap.md`.
> **Текущая фаза (2026-08-25, после 24kk2):** ПРИМЕНЕНИЕ ДЕКОДИРОВАНО ФОРМУЛАМИ:
> аудио = побиновное умножение кадра на вещественную маску `10^(cut_D/20)`,
> `cut_D = α·ln1p(lvl_raw/β)+c` (+Δ у вторых пиков). Калибровки (rms ≤0.016 дБ):
> dual(q0.5,s12,2 тона)=3.2193/0.4927/+0.54; fc1000(1 тон,q0.5)=1.6151/0.3645/+0.48;
> fc500(1 тон)=1.1530/0.4038/+0.33. lvl_raw — НАШ фронтенд (float-parity ✓).
> Слой STFT = БЕЗ синтез-окна (`RT_SYN=1`). **dual-корпус 0.193 max 0.438**
> (канон 3.264); канон TOTAL 2.286 нетронут. ГЕЙТ СМЕНЫ КАНОНА = BIT EXACT
> (решение пользователя: все параметры прослежены до декомпа + шумовой пол корпуса).
>
> Ключевые факты: q НЕ влияет на закон (доказано 24kk); sens линейно через
> lvl_raw; далёкий контент не влияет — смешение шаблонно-локальное (24ll);
> аудио = exp(deepest-scratch)+const; буфер FIR@540668 = мин.-фазовое
> представление (exp(s−iH(s))) — не для транскрипции. Тела bigkernel'ов:
> 1803a06a0/180296c80/180323f20/1802dc0e0 (x87 exp-семейство, 24jj).
>
> ОСТАТОК: каскадный симулятор шагов 9–19 по dataflow (24hh/24ii) →
> параметризация α(контент)/fc через campaign.py (датасеты готовы) →
> Δ-правило из pre-combine → полный корпус. Журнал: NOTES_LEVEL 24j24kk2;
> карта метода: handoff/BLOCKMAP_529fe0.md. ЭТОТ ФАЙЛ ЧИТАЙ ПЕРВЫМ.
## Золотое правило (обязательно)
1. **Цель — bit-exact реверс кода**, НЕ эмпирическая подгонка кривых. Каждый параметр
должен иметь источник (decomp адрес / live-таблица), а не быть подогнанным числом.
Если берёшь эмпирику — явно пометь и занеси в «осталось».
2. Источник истины — **декомпиляция** (`decomp_funs.txt`, `decomp_funs2.txt`,
`handoff/nls_dasm/*.dis`) и **live-снимки** (`/tmp/snap_rt.bin`). Обновлять заметки
при любом новом декоде.
3. Команды через `rtk` (токен-фильтр), включая цепочки `&&`.
## Сборка и тесты (канон)
```bash
# собрать библиотеку + framed_test (bridge, 44.1k) + render48k (структурная цепь, 48k/4096)
cmake -S dsp -B dsp/build
cmake --build dsp/build --target framed_test render48k
# bridge-рендер (N=2048, hop=512, SR 44100, sqrt-Hann OLA)
# формат полосы: fc,q,sens,level_scale (одна полоса в comma-форме)
./dsp/build/framed_test /home/m/soothe-bt/tone1kq.wav /tmp/o.wav 678.7611083984375,0.99999785,12,600
# многополосный: через отдельные аргументы fc q sens scale (см. framed_test.cpp)
# структурный рендер (внутренняя сетка 48000/4096: resample -> FUN_180529fe0 chain -> resample)
./dsp/build/render48k /home/m/soothe-bt/tone1kq.wav /tmp/o48.wav 1000,0.99999785,12
# корпусные прогонки (62 случая, честная 24-bit метрика)
python3 scripts/corpus.py # bridge + guard --compare
python3 scripts/corpus_structural.py # структурная цепь
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
# отдельные модули (bit-exact черные проверки)
cmake --build dsp/build --target twin_check tables_check fftconv_check vlog_check leveltrack_check levelpath_check fn529fe0_check exp2_check
./dsp/build/twin_check # float-parity twin-резонатора
```
> **TOOLING HAZARD**: cmake пропускает пересборку при изменении исходника в ту же секунду —
> параметрические свипы молча гоняют STALE бинарь. Протокол: `touch` исходника перед сборкой +
> проверять свежесть mtime бинаря (пример: /tmp/sweep_fresh.py паттерн в NOTES_LEVEL:21a).
### Метрика (tone-cmp, Goertzel stead-state)
Сравнивать **тримкнутый** выход (длина = длина входа) с референсом `*_ref.wav` / `t1kq_b1f_*`:
```python
# ref: t1kq_b1f_678.7611083984375.wav (или t1kq_b1f_1000.wav и т.д. по fc полосы)
# вход: tone1kq.wav (тон 1000 Гц)
import wave, numpy as np
def load(p):
w=wave.open(p,'rb'); n=w.getnframes(); d=w.readframes(n)
return np.frombuffer(d,dtype=np.int16).astype(np.float64).reshape(-1,w.getnchannels()).mean(1)/32768
def ta(x,f,sr=44100):
x=x[-int(0.75*sr):]; t=np.arange(len(x))/sr; w=2*np.pi*f
return np.hypot(2*np.sum(x*np.cos(w*t))/len(x), 2*np.sum(x*np.sin(w*t))/len(x))
def db(a): return 20*np.log10(max(a,1e-9))
inp=load('/home/m/soothe-bt/tone1kq.wav')
out=load('/tmp/o.wav')
ref=load('/home/m/soothe-bt/t1kq_b1f_678.7611083984375.wav') # fc полосы = 678.76
err = db(ta(out,1000)/ta(ref,1000)) # err в dB, цель <0.1
```
Тестовый корпус (`/home/m/soothe-bt/`, вне git): `tone1kq.wav`(вход),
`t1kq_b1f_<fc>.wav` (рефы), `comb.wav`/`comb_ref.wav` (мультиполосный).
## Текущее состояние (2026-08-21, P4)
**ДВА канона в `dsp/framed_model.cpp`:**
1. **BRIDGE** (эмпирическая погона, путь framed_test 44.1k, NOTES:147):
```
am_k: smoothed amp (2|X|/wsum, att~11ms/trel~80ms)
xv = log10(am_k / res_k) res_k = |2B/A| twin (min@band centre)
C = G·LUT(xv) + W·warp(f_k)^A G/W/A = 0.9963/0.3335/0.9807 (fit)
gain_k = (1C) · res_k^rp(Q) rp(Q) = 0.0275·Q^0.2159
LUT = monotone cubic (FritschCarlson) через joint-fit узлы (al_* + B.11 anchors)
```
2. **СТРУКТУРНАЯ цепь FUN_180529fe0** (`process_band_structural`, внутренняя сетка
48000/4096 через `dsp/build/render48k`, NOTES_LEVEL:820-840 + апдейты 20j21c):
```
scale → LUT level-domain (t^γ·MULT, γ=0.344 decomp / MULT=4.2 placeholder)
→ IIR1 → IIR2 → mirror → blend(0.8)/exp2(lvl) → combine(acc-update, без консюмера)
→ dual-warp(kBand768·kWarp·res^rp) → IIR3 bidirectional ×2 → dry/wet(identity)
```
- **METRIC CRITICAL**: рефы 24-bit НЕЛЬЗЯ читать 16-bit кодеком (даёт phantom 53 dB);
`render_parity.load` (sw handling) + окно 3.5s — канон. dual honest ref = 10.2 dB FLAT.
- **Broadband-hypothesis ОПРОВЕРГНУТА** (NOTES:2026-08-20s): редукция per-bin.
- **Честные результаты**: bridge корпус mean 1.594 (comb 10.15 сломан); структурная цепь:
comb **6.12** и res **0.44** — лучше bridge; t1kq 0.77 / t1k 2.11 / al 0.99 / dual 3.26 — хуже.
Полная таблица: BITEXACT_PLAN §0 / NOTES_LEVEL:21c.
- **ГЛАВНЫЙ ОТКРЫТЫЙ РАЗРЫВ**: насыщение кривой редукции — реальная упирается в C_max≈0.70,
exp2(−lvl) не ограничен (dual@500: константный −6.7 dB при всех q). Лечится Шагом 7
(live-захват A/B/γ) или статическим hunt'ом clamp'а в level-пути. Слепая подкрутка
γ/MULT исчерпана (свип 21c).
- **21d — закон редукции найден, канон НЕ сменён**: эффективное ослабление аффинно
в dB(lvl) (маска ≈ 0.8·lvl^0.354; al-свееп ±0.13 dB). Scalar-семейство (X0,S,floor,
CMAX) не закрывает тон+шум одновременно → res/dual регресс у любой точки; ищем
контент-зависимый механизм. Инструментарий RT_DUMP_BIN / RT_DUMP_ALL в коде (opt-in).
### Открытые пробелы (после 22z; детали — NOTES_LEVEL 22s22z)
1. **Семантика входов `bands[]` в FUN_180529fe0** — главный вопрос. Отвергнуто:
lvl=f(am,res^α) с единым законом [22x, теорема], форма-постобработка маски [22w],
спрединг IDFT→окно→DFT [22z]. Живой захват: кривая редукции R=1/mask @слот
0x5407f8 (пик следует за fc полосы), acc/f6f8 нули в стационаре.
Пути: (а) декод тел conv 0x1802a24c0 + complex-op шагов с буферами
0x540548/550/598; (б) синхронная трасса буферов в аудио-колбеке.
2. dual-таблица по q (22x): cut@центра константен при любом q, Δcut скайрта ≈
линейно по −ln res с b≈2.25 — геометрия twin-шаблона на сетке 0.25 Гц.
3. FFT-conv: последовательность вызовов декодирована (BLOCKMAP/22z), тело
0x1802a24c0 (AVX2) и complex-op шаги — не расшифрованы.
4. Бит-экзактный exp2 (0x26b820); бит-точная DSP-FFT (P3); стерео M8 (P5) — отложены.
5. Источник констант RT_LAWAFFINE A/S в декомпе не найден (помечено EMPIRICAL).
## Структура ключевых файлов
- `dsp/framed_model.{cpp,hpp}` — C++ порт mask-apply цепи: bridge-канон + структурная
`process_band_structural` (ГЛАВНЫЙ активный файл).
- `dsp/render48k.cpp` — пайплайн внутренней сетки 48000/4096 (resample → структурная цепь → resample).
- `dsp/framed_test.cpp` — CLI bridge-рендер входа (метрика см. выше).
- `scripts/corpus.py` — корпусный харнесс bridge + guard `--compare baseline_bridge.json`.
- `scripts/corpus_structural.py` — тот же корпус через render48k + режим `--vs-bridge`.
- `dsp/{twin,levelpath,freqpath,fftconv,vlog,leveltrack,fn529fe0}.cpp` — расшифрованные модули.
- `dsp/rt_mask_tables.{hpp,cpp}`, `rt_weights.{hpp,cpp}` — live-таблицы.
- `handoff/NOTES_LEVEL.md` — полный журнал уровня/маски (самый актуальный).
- `handoff/nls_dasm/` — 134 дизассембла декомпа.
- `/tmp/consumers_out.txt` — полный decomp `FUN_180529fe0` (уникален, вне git).
- `/tmp/snap_rt.bin` — live-снимок (ctx 0x2370040, SR 48000).
- `handoff/BLOCKMAP_529fe0.md` — полная блок-карта FUN_180529fe0 по raw asm (22v).
- `scripts/lawfit22r.py` — офлайн реплей-сим пайплайна (~2 c корпус), фиты законов.
- `scripts/resalpha.py` — сбор трактов + теорема об отсутствии α (22x).
- `scripts/dualtrace.py` + `play_loop.lua` — живой захват ctx при realtime-playback (22y).
## Env-флаги экспериментов (24j–24kk2)
| флаг | действие |
|------|----------|
| `RT_VLAW=1` | закон применённой стадии: mask=10^((α·ln1p(lvl/β)+c)/20) |
| `RT_SYN=1` | STFT БЕЗ синтез-окна (найденный слой плагина) |
| `RT_WIN=0/1/2` | окно анализа: sym-hann / periodic / rect |
| `RT_NOWARP=1` | отключить warp-модуляцию маски |
| `RT_NOIIR3=1` | отключить IIR3 ×2 |
| `RT_IIR12=0` | отключить частотные IIR1/2 (КРИТИЧНО с RT_VLAW — иначе размывают дипы) |
| `RT_DUMP_BIN=<f>` (+`RT_DUMP_FRAME=N`) | дамп тракта бина N: am/res/lvl_raw/band_level/prewarp/w |
| `RT_VDBG=1` | stderr-печать vlaw-вычислений |
Полный набор dual-решения: `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0`.
## Инструменты сессии 24j–24kk2
| скрипт | назначение |
|--------|-----------|
| `scripts/rendersnap2.py <rpp> [cap] [outdir]` | мягкий STOP-снаппер: слоты FIR/scratch/bands/R + скалярный банк + t_snap; НЕ убивает reaper; RENDER_FILE из rpp (не удалять чужие рефы!) |
| `scripts/campaign.py <base> <fc> <q> <sens> <in_prefix> <drives> <out>` | ячейка параметризации: клоны rpp+рефы (~8 мин) |
| `scripts/disasm_func.py <VA> [len]` | capstone-дизасм с инлайн-резолвом RIP-констант |
| `scripts/iat_name.py` | рантайм-резолв импортов bigkernel'ов через PE-экспорты (SIGSTOP!) |
| `scripts/probe_states.py` / `probe_mem.py` / `dump_dispatch.py` | живые state-заголовки / память / таблицы диспатча |
| `scripts/hunt2.py` | перебор всех ctx-инстансов |
| `scripts/scan_pairs.py`, `scan_lutsub.py` | диагностика памяти по сигнатурам float-пар |
Датасеты кампании: `/tmp/opencode/sc_{q,sens,qmap,k,f,d}*` + `tract_*` +
`{level_pairs,f_series,q_series,k_series}.pkl/.npz` (см. NOTES 24bb24kk2).
## Чистая работа
- Не коммитить: `*.bin`(дампы 112М), `*.wav/rpp`, `*.log`, `dsp/build/`, `ghidra-proj/`,
`dl/lib/bin/include/`, `regions*/`. См. `.gitignore`.
- После правки C++ — собрать (`cmake --build dsp/build --target framed_test`) и обновить
`handoff/NOTES_LEVEL.md`. Fasta метрику держать честной (тримнутая длина).