# 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`. ## Золотое правило (обязательно) 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 cmake -S dsp -B dsp/build cmake --build dsp/build --target framed_test # рендер через каркас (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) # отдельные модули (bit-exact черные проверки) cmake --build dsp/build --target twin_check tables_check fftconv_check vlog_check leveltrack_check levelpath_check ./dsp/build/twin_check # float-parity twin-резонатора ``` ### Метрика (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_.wav` (рефы), `comb.wav`/`comb_ref.wav` (мультиполосный). ## Текущее состояние (2026-08-20, P4) **Декодировано и транскрибировано в `dsp/framed_model.cpp`** (mask-apply цепь `FUN_180529fe0`, mono-path 0x5408b8==0): ``` level=am*res*level_scale -> scale(0x540870*0x54088c/0x1a0) -> IIR1 leaky(A1/B1) -> IIR2(A2/B2) -> mask=exp2(-mask)*blend (bigkernel 0x26b820) -> combine/acc(0x5407c8) -> warp(mask*=0x540768, *=warp) -> IIR3(A3/B3)x2 -> dry/wet(mask*(fVar30*0x540888)+(1-fVar30)) -> [FFT-conv] ``` - **bigkernel 0x26b820 = векторизованный exp2** (log2e=1.4424, floor −708.9, N-таблицы); семантика залочена: `mask = exp2(-level) * blend`, blend=0.8 при mix=1.0. - **Level-tracker = би-направленный leaky-IIR** `y=A[i]*acc+B[i]*x`, B=1−A (НЕ per-bin независимый трекер). - **Live-таблицы** в `dsp/rt_mask_tables.{hpp,cpp}`: kIIR_A1/B1..A3/B3 (2049 doubles), kWarp (8193), kBand768, kPRNGLut. `dsp/rt_weights.{hpp,cpp}` = acc коды весов. - **Результаты**: t1kq err −0.43 dB (scale=600); comb per-tone err ≤6.4 dB. ### Открытые bit-exact пробелы (см. NOTES_LEVEL.md) 1. PRNG-пролог (LCG+LUT → fVar30), per-frame рандомизация scale/dry-wet. 2. FFT-conv (0x535a70) сглаживание маски перед FIR. 3. Бит-экзактный exp2 (0x26b820) вместо std::exp2. 4. combine/аккумулятор 0x5407c8 точная обратная связь. 5. **SR-mismatch**: внутренний DSP SR=48000/N=4096 (спейс 11.713 Гц) против хоста 44100/2048; twin per-bin IIR статически невидим (FUN_180535880 tail-call). ## Структура ключевых файлов - `dsp/framed_model.{cpp,hpp}` — C++ порт mask-apply цепи (ГЛАВНЫЙ активный файл). - `dsp/framed_test.cpp` — CLI рендер входа (3010 метрика см. выше). - `dsp/{twin,levelpath,freqpath,fftconv,vlog,leveltrack}.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). ## Чистая работа - Не коммитить: `*.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 метрику держать честной (тримнутая длина).