Compare commits
174
Commits
3b9158e63e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627c35dd53 | ||
|
|
3fe44255fa | ||
|
|
a96e46bb69 | ||
|
|
a562f6f56a | ||
|
|
c85b888745 | ||
|
|
a881ee280a | ||
|
|
2f854cd1da | ||
|
|
e4c53480ad | ||
|
|
197f5edea4 | ||
|
|
b41d7a40cb | ||
|
|
f2cc0aeaa9 | ||
|
|
2201a57ffa | ||
|
|
96816f9921 | ||
|
|
2a295af6f3 | ||
|
|
9ed22d3476 | ||
|
|
e343b0a0d0 | ||
|
|
e9d6f2ca72 | ||
|
|
59fcb7eb56 | ||
|
|
9d10169157 | ||
|
|
c23646d74f | ||
|
|
d80137c31d | ||
|
|
281f4079c2 | ||
|
|
22ff0e3ee9 | ||
|
|
c393b6f6be | ||
|
|
7b479f5357 | ||
|
|
359aaeb7cb | ||
|
|
a208d1b27e | ||
|
|
a8191d4bc9 | ||
|
|
6bf2363c1a | ||
|
|
8fbbb66e3c | ||
|
|
4411e617a6 | ||
|
|
6f32b26d6a | ||
|
|
d88e8ab7bf | ||
|
|
1d6c5a4355 | ||
|
|
c7d6fde2dc | ||
|
|
e39c97e014 | ||
|
|
a7e1a661bd | ||
|
|
9311e7397f | ||
|
|
85317ed674 | ||
|
|
5e8c3d8af4 | ||
|
|
d025fa5a23 | ||
|
|
04c51e5be0 | ||
|
|
ab9334f8ab | ||
|
|
031f7d21af | ||
|
|
69a6809a80 | ||
|
|
d7089c62fc | ||
|
|
f40f41ef71 | ||
|
|
f3e11d410a | ||
|
|
8aa25b36a8 | ||
|
|
968e90e65f | ||
|
|
5e9c41ad2b | ||
|
|
746b51c047 | ||
|
|
803c100091 | ||
|
|
febda55234 | ||
|
|
3411e9b42e | ||
|
|
f73724fee7 | ||
|
|
2eb3b690c9 | ||
|
|
bbf3cf044b | ||
|
|
09a0bfb63b | ||
|
|
e76b0ba8a4 | ||
|
|
b4d75f4d22 | ||
|
|
588d2dcc36 | ||
|
|
4ed3481166 | ||
|
|
8805a8f183 | ||
|
|
d7cbab3e4c | ||
|
|
1ea4bf6480 | ||
|
|
f689023089 | ||
|
|
575d26a771 | ||
|
|
4d78785f0b | ||
|
|
b6e7fdc289 | ||
|
|
f68081f694 | ||
|
|
c18f4b3ef4 | ||
|
|
7202b8d6a4 | ||
|
|
943e781720 | ||
|
|
60421c32c9 | ||
|
|
6bc0120286 | ||
|
|
c69257a551 | ||
|
|
50d7ab0d05 | ||
|
|
3c5e276fc5 | ||
|
|
f0cfec8af7 | ||
|
|
c2da7495c0 | ||
|
|
a281f6a721 | ||
|
|
d97dcffaa7 | ||
|
|
e9125d4024 | ||
|
|
a79c8f4934 | ||
|
|
17f25089c6 | ||
|
|
5c29e2cd4f | ||
|
|
e958b3d3d6 | ||
|
|
dcfe7774f3 | ||
|
|
b5a5afbd16 | ||
|
|
87f00ce181 | ||
|
|
c210a778e5 | ||
|
|
0677a390d2 | ||
|
|
94116e2a17 | ||
|
|
d2f67016ab | ||
|
|
b85b344e23 | ||
|
|
def7cea522 | ||
|
|
f365fa8451 | ||
|
|
0fee9238dc | ||
|
|
77c02c8f9d | ||
|
|
0dfff22efb | ||
|
|
4f79f83c11 | ||
|
|
bf3faab660 | ||
|
|
c33212c21b | ||
|
|
483f52f296 | ||
|
|
8ef1d4ec73 | ||
|
|
0943471b33 | ||
|
|
2d6bdcf967 | ||
|
|
83c12d9b3d | ||
|
|
feaa82957a | ||
|
|
0fbff6dc6e | ||
|
|
61219acca6 | ||
|
|
10d8c1cf5a | ||
|
|
d4c56bdfce | ||
|
|
2fb0e640e0 | ||
|
|
47c5360ac2 | ||
|
|
65ee61d540 | ||
|
|
676d43d40a | ||
|
|
8a4e7a3c54 | ||
|
|
243ea03a45 | ||
|
|
4b94ce5c15 | ||
|
|
5284ce2b57 | ||
|
|
cf38622995 | ||
|
|
edf6f35cdf | ||
|
|
abbee308ba | ||
|
|
da0393bd93 | ||
|
|
5bd60cfefd | ||
|
|
0bc8427cd7 | ||
|
|
a309ca3e31 | ||
|
|
4b56e4f384 | ||
|
|
3062a02389 | ||
|
|
a3d108f4d5 | ||
|
|
5f822ebae1 | ||
|
|
31cc5540e1 | ||
|
|
fc224b00a3 | ||
|
|
b00a8a667e | ||
|
|
fe01ef5bac | ||
|
|
508b24125f | ||
|
|
3a611cff35 | ||
|
|
65fb983d57 | ||
|
|
1605e0ba29 | ||
|
|
6dc2967e4a | ||
|
|
cc8ba34577 | ||
|
|
6629e8bbf2 | ||
|
|
475b1958d5 | ||
|
|
a1632a9fce | ||
|
|
0e4d3177fe | ||
|
|
2c99a4fc96 | ||
|
|
03777fdaee | ||
|
|
135f0e9f21 | ||
|
|
77e294d445 | ||
|
|
a9f1bd365b | ||
|
|
f483fe398d | ||
|
|
8acbf6612e | ||
|
|
56edd3c0f4 | ||
|
|
baafbb6dc6 | ||
|
|
9e85b730b2 | ||
|
|
25b736b2bb | ||
|
|
05caf1eb44 | ||
|
|
c3656975dd | ||
|
|
70cafc1700 | ||
|
|
7902acfbb8 | ||
|
|
3a0a38467b | ||
|
|
0a45661a25 | ||
|
|
b8d5f83fc5 | ||
|
|
07cd4b7dc0 | ||
|
|
738ffccad4 | ||
|
|
91101b2135 | ||
|
|
64beaa7131 | ||
|
|
13c2d028bf | ||
|
|
34156315e6 | ||
|
|
79c11dc220 | ||
|
|
376bd955c9 | ||
|
|
3857205a6b |
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
# --- расшифрованные дизассемблы декомпа: НУЖНО трекать (ценные артефакты) ---
|
# --- расшифрованные дизассемблы декомпа: НУЖНО трекать (ценные артефакты) ---
|
||||||
!handoff/
|
!handoff/
|
||||||
|
!handoff/archive/
|
||||||
|
!handoff/archive/*.md
|
||||||
!handoff/nls_dasm/
|
!handoff/nls_dasm/
|
||||||
!handoff/nls_dasm/*.dis
|
!handoff/nls_dasm/*.dis
|
||||||
!handoff/nls_dasm/*.bin
|
!handoff/nls_dasm/*.bin
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
# AGENTS.md — guide for AI agents working in this repo
|
# AGENTS.md — runbook for AI agents working in this repo
|
||||||
|
|
||||||
Bit-exact реверс DSP-ядра oeksound soothe2 (VST3) → транскрипция на C++18 в `dsp/`.
|
> **Статус → `README.md:13` (единственный источник TOTAL).** Этот файл — только runbook: сборка, метрика, env-флаги, tooling hazard.
|
||||||
Полное журналирование — в `handoff/NOTES_LEVEL.md`, `handoff/NOTES_TWIN.md`,
|
> Полный журнал — `handoff/NOTES_LEVEL_INDEX.md` → `handoff/NOTES_LEVEL.md` (живая голова) + `handoff/archive/`.
|
||||||
`handoff/NOTES_CAPTURE.md`, `roadmap.md`.
|
> Карта метода — `handoff/BLOCKMAP_529fe0.md`. ЭТОТ ФАЙЛ ЧИТАЙ ПЕРВЫМ.
|
||||||
|
|
||||||
> **Текущая фаза: bit-exact НЕ достигнут** — dB-параллель двумя канонами: bridge
|
|
||||||
> (корпус mean 1.594 dB, comb 10.1) и структурная цепь FUN_180529fe0 на 48k/4096
|
|
||||||
> (comb **6.12** — уже лучше bridge; однополосные хуже; разрыв = насыщение кривой
|
|
||||||
> редукции). Путь к байтам — в `BITEXACT_PLAN.md` (8 шагов, пере-скоуп Шага 2).
|
|
||||||
> ЭТОТ ФАЙЛ ЧИТАЙ ПЕРВЫМ. Bridge — запасной канон; структурная цепочка — целевая.
|
|
||||||
|
|
||||||
## Золотое правило (обязательно)
|
## Золотое правило (обязательно)
|
||||||
1. **Цель — bit-exact реверс кода**, НЕ эмпирическая подгонка кривых. Каждый параметр
|
1. **Цель — bit-exact реверс кода**, НЕ эмпирическая подгонка кривых. Каждый параметр
|
||||||
@@ -39,7 +33,7 @@ python3 scripts/corpus_structural.py # стру
|
|||||||
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
|
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
|
||||||
|
|
||||||
# отдельные модули (bit-exact черные проверки)
|
# отдельные модули (bit-exact черные проверки)
|
||||||
cmake --build dsp/build --target twin_check tables_check fftconv_check vlog_check leveltrack_check levelpath_check fn529fe0_check exp2_check
|
cmake --build dsp/build --target twin_check tables_check fftconv_check vlog_check leveltrack_check levelpath_check fn529fe0_check exp2_check vlaw_check
|
||||||
./dsp/build/twin_check # float-parity twin-резонатора
|
./dsp/build/twin_check # float-parity twin-резонатора
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -68,69 +62,72 @@ err = db(ta(out,1000)/ta(ref,1000)) # err в dB, цель <0.1
|
|||||||
Тестовый корпус (`/home/m/soothe-bt/`, вне git): `tone1kq.wav`(вход),
|
Тестовый корпус (`/home/m/soothe-bt/`, вне git): `tone1kq.wav`(вход),
|
||||||
`t1kq_b1f_<fc>.wav` (рефы), `comb.wav`/`comb_ref.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 = (1−C) · res_k^rp(Q) rp(Q) = 0.0275·Q^0.2159
|
|
||||||
LUT = monotone cubic (Fritsch–Carlson) через joint-fit узлы (al_* + B.11 anchors)
|
|
||||||
```
|
|
||||||
2. **СТРУКТУРНАЯ цепь FUN_180529fe0** (`process_band_structural`, внутренняя сетка
|
|
||||||
48000/4096 через `dsp/build/render48k`, NOTES_LEVEL:820-840 + апдейты 20j–21c):
|
|
||||||
```
|
|
||||||
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).
|
|
||||||
|
|
||||||
### Открытые bit-exact/структурные пробелы (см. NOTES_LEVEL.md)
|
|
||||||
1. Насыщение кривой редукции (см. выше) — приоритет №1.
|
|
||||||
2. `combine/аккумулятор 0x5407c8`: семантика декодирована (21b), но acc/f6f8 НЕ имеют
|
|
||||||
однополосного консюмера — искать точку потребления (межполосный каскад). ВНИМАНИЕ:
|
|
||||||
`FUN_1805316e0` = writer коэффициентов, НЕ комбинер масок.
|
|
||||||
3. ~~BandConfig `ctx+0x188` A/B/γ — противоречие между сессиями~~ **РАЗРЕШЕНО
|
|
||||||
(22b): live = −24/28/1 у ВСЕХ конфигов, но весь кластер FUN_180563440/563a60 —
|
|
||||||
GUI-timer only; аудио-путь (FUN_180529fe0) BandConfig не читает. Шаг 7 в исходной
|
|
||||||
постановке опровергнут; LUT-константы структурной цепи помечены EMPIRICAL.**
|
|
||||||
4. PRNG-пролог (LCG+LUT → fVar30) — залочен (fVar30=1 при live state 112), dry/wet rnd импорт.
|
|
||||||
5. Бит-экзактный exp2 (0x26b820); FFT-conv понижен до P3 (окно near-flat, NOTES:18c);
|
|
||||||
SR-геометрия 48k/4096 сделана (render48k).
|
|
||||||
|
|
||||||
## Структура ключевых файлов
|
## Структура ключевых файлов
|
||||||
- `dsp/framed_model.{cpp,hpp}` — C++ порт mask-apply цепи: bridge-канон + структурная
|
- `dsp/framed_model.{cpp,hpp}` — C++ порт mask-apply цепи (ГЛАВНЫЙ файл).
|
||||||
`process_band_structural` (ГЛАВНЫЙ активный файл).
|
- `dsp/render48k.cpp` — пайплайн 48000/4096 (resample → chain → resample).
|
||||||
- `dsp/render48k.cpp` — пайплайн внутренней сетки 48000/4096 (resample → структурная цепь → resample).
|
- `dsp/twin.{cpp,hpp}` — twin FUN_180535880 (float-parity).
|
||||||
- `dsp/framed_test.cpp` — CLI bridge-рендер входа (метрика см. выше).
|
- `scripts/corpus.py` / `corpus_structural.py` — харнессы + guard `--compare`.
|
||||||
- `scripts/corpus.py` — корпусный харнесс bridge + guard `--compare baseline_bridge.json`.
|
- `handoff/NOTES_LEVEL.md` — живой журнал (голова 24mm5+, хвост → `archive/`).
|
||||||
- `scripts/corpus_structural.py` — тот же корпус через render48k + режим `--vs-bridge`.
|
- `handoff/NOTES_LEVEL_INDEX.md` — оглавление журнала.
|
||||||
- `dsp/{twin,levelpath,freqpath,fftconv,vlog,leveltrack,fn529fe0}.cpp` — расшифрованные модули.
|
- `handoff/BLOCKMAP_529fe0.md` — блок-карта FUN_180529fe0.
|
||||||
- `dsp/rt_mask_tables.{hpp,cpp}`, `rt_weights.{hpp,cpp}` — live-таблицы.
|
- `handoff/nls_dasm/` — 134 дизассембла.
|
||||||
- `handoff/NOTES_LEVEL.md` — полный журнал уровня/маски (самый актуальный).
|
|
||||||
- `handoff/nls_dasm/` — 134 дизассембла декомпа.
|
## Env-флаги экспериментов (актуальные)
|
||||||
- `/tmp/consumers_out.txt` — полный decomp `FUN_180529fe0` (уникален, вне git).
|
|
||||||
- `/tmp/snap_rt.bin` — live-снимок (ctx 0x2370040, SR 48000).
|
| флаг | действие |
|
||||||
|
|------|----------|
|
||||||
|
| `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-вычислений |
|
||||||
|
| `RT_FAITHFUL=1` | faithful-цепь `dsp/fnfaith.cpp` (детекторный каскад) |
|
||||||
|
| `RT_FIRCONV=1/3` | FIR-применение (1=complex-mul, 3=`1.019·mask^1.8345`) |
|
||||||
|
| `RT_CASC=1` | цепь 9–19 (`chain_9_19`, IIR4×2 double + FIR min-phase, gated) |
|
||||||
|
| `RT_IIR4_GEN=1` | генератор IIR4 (FUN_180533340) vs proxy `kIIR_A1/B1` |
|
||||||
|
| `RT_IIR4_C/TAU/P/MULT/SR` | параметры генератора IIR4 (1000/1200/0.5/360/48000) |
|
||||||
|
| `RT_FIR=0/1` | пуск FIR min-phase (default 1) |
|
||||||
|
| `RT_FIR_Q=x` | показатель FIR (default 0.8002203702926636, live `.rdata`) |
|
||||||
|
| `RT_KMAP_FC=1` | fc-фактор k-mapping через W_eq (default off) |
|
||||||
|
| `RT_DELTA_DIST=x` | Δ distance factor (template-local gain, default 0=flat) |
|
||||||
|
| `RT_DELTA_STATE=1` | STATE-dependent Δ (опен-ин, wired, empty) |
|
||||||
|
| `RT_DBG_CASC=1` | gate DBG_CASC fprintf (per-frame spam otherwise) |
|
||||||
|
|
||||||
|
Полный набор dual-решения: `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0`.
|
||||||
|
|
||||||
|
## Инструменты
|
||||||
|
|
||||||
|
| скрипт | назначение |
|
||||||
|
|--------|-----------|
|
||||||
|
| `scripts/rendersnap2.py <rpp> [cap] [outdir]` | мягкий STOP-снаппер (RENDER_FILE из rpp!) |
|
||||||
|
| `scripts/campaign.py <base> <fc> <q> <sens> <in> <drives> <out>` | ячейка параметризации (~8 мин) |
|
||||||
|
| `scripts/disasm_func.py <VA> [len]` | capstone-дизасм с RIP-константами |
|
||||||
|
| `scripts/iat_name.py` | рантайм-резолв импортов (SIGSTOP!) |
|
||||||
|
| `scripts/probe_states.py` / `probe_mem.py` / `dump_dispatch.py` | live state/память/таблицы |
|
||||||
|
| `scripts/hunt2.py` | перебор ctx-инстансов |
|
||||||
|
| `scripts/scan_pairs.py`, `scan_lutsub.py` | диагностика памяти |
|
||||||
|
|
||||||
|
Датасеты: `/tmp/opencode/sc_{q,sens,qmap,k,f,d}*` + `tract_*` + `*.pkl/.npz` (см. `NOTES_LEVEL_INDEX.md`).
|
||||||
|
Открытые пробелы → `BITEXACT_PLAN.md`.
|
||||||
|
|
||||||
|
## Архитектурная проблема chain_9_19 (WIRED, GATED)
|
||||||
|
|
||||||
|
`chain_9_19` из `dsp/fn52fe0.cpp:259` (BLOCKMAP:620-644) — per-frame цепь с **persistent ACC state** (`@ctx+0x5407c8`). Интегрирована в `framed_model.cpp:process_band_structural` через `RT_CASC=1`. IIR4×2 использует double precision (movsd/mulsd per disasm 1191), FIR min-phase бит-точен до df0 (0.0065 dB). Генератор IIR4 (`generate_iir4_coefs`, FUN_180533340) env-gated `RT_IIR4_GEN`.
|
||||||
|
|
||||||
|
**Статус:** chain wired и gated (`RT_CASC=0` default = canon untouched). Все unit checks PASS, corpus gate d=+0.000. Калибровка I/O format требует `ph*.npz` capture (rendersnap2.py) — capture proof получен (2026-09-02).
|
||||||
|
|
||||||
|
**Live-dump chain (ptrace):** chain вызывается в рантайме (DIV#0-7 на `0x1803a06a0`):
|
||||||
|
- Input: `a` = bands_curve (VLAW output, min=0, max=17.6, mean=0.048)
|
||||||
|
- `b` = tmp6f8 (step 9b accumulation, min=0, max=0.8, mean=0.8)
|
||||||
|
- 8 DIV hits на одном кадре (dual-band: 2 bands × 4 iterations?)
|
||||||
|
|
||||||
|
**Capture proof (2026-09-02):** `rendersnap2.py` снимает `ph*.npz` (слоты 0x540628/0x540678/0x540688/0x540768) через `/proc/pid/mem` без ptrace-брейкпоинтов. Данные в `/tmp/opencode/{rendersnap2_dual,snap_t1k_b1f_1000,snap_dual300,rendersnap2_comb_b1234}`. Маск-цепь работает в рендер-окне (BLOCKMAP:285 устарел).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Чистая работа
|
## Чистая работа
|
||||||
- Не коммитить: `*.bin`(дампы 112М), `*.wav/rpp`, `*.log`, `dsp/build/`, `ghidra-proj/`,
|
- Не коммитить: `*.bin`, `*.wav/rpp`, `*.log`, `dsp/build/`, `ghidra-proj/`, `dl/lib/bin/include/`, `regions*/`. См. `.gitignore`.
|
||||||
`dl/lib/bin/include/`, `regions*/`. См. `.gitignore`.
|
- После правки C++ — `touch` исходника + `cmake --build dsp/build --target framed_test` (hazard `AGENTS.md:66`) + `python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25`.
|
||||||
- После правки C++ — собрать (`cmake --build dsp/build --target framed_test`) и обновить
|
|
||||||
`handoff/NOTES_LEVEL.md`. Fasta метрику держать честной (тримнутая длина).
|
|
||||||
|
|||||||
+53
-212
@@ -1,249 +1,90 @@
|
|||||||
# BIT-EXACT PLAN — путь от dB-приближения к побайтовой парности
|
# BIT-EXACT PLAN — путь от dB-приближения к побайтовой парности
|
||||||
|
|
||||||
Статус: **НЕ bit-exact**. Мы на уровне честной dB-параллели (полный корпус mean 1.594 dB,
|
Статус: **НЕ bit-exact**. Канон — bridge `framed_test` 44.1k: **TOTAL 1.594 dB** (62 случая, trimmed 24-bit, `scripts/baseline_bridge.json`). Bit-exact достижим (F0 gate: рендеры байт-детерминированы `handoff/NOTES_LEVEL.md:1020`), но модель — погона. Читать вместе с `AGENTS.md` и `handoff/NOTES_LEVEL_INDEX.md`.
|
||||||
comb 10 dB). Bit-exact ДОСТИЖИМ (F0 gate: плагин байт-детерминирован), но модель ещё не
|
|
||||||
воспроизводит реальный DSP-путь. Этот документ — план, как туда дойти, и точка отсчёта
|
|
||||||
для любой будущей сессии. **Читать вместе с `AGENTS.md` и `handoff/NOTES_LEVEL.md`.**
|
|
||||||
|
|
||||||
> **ОБНОВЛЕНИЕ 2026-08-22 (сессии 22a–22e):**
|
> **2026-08-28 (24mm14): применение декодировано до формул.** `mask=10^(−cut_D/20)`, `cut_D=α·ln1p(lvl/β)+c [+Δ]` — три семейства, rms ≤0.016. `dual` 0.193 dB (env-gated `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0`), детекторный каскад `180529c60` + RFFT до df0 бит-точны, `γ=1.760561` из цепи. **Гейт смены канона = bit-exact** (решение `24hh2`). Остаток: 3 шага ниже.
|
||||||
> 1. Phase-B оффлайн-гипотезы (pooling/temporal/scalar-ρ) — все ОПРОВЕРГНУТЫ (22a).
|
|
||||||
> 2. Шаг 7 выполнен → премиса опровергнута: BandConfig A/B/γ = −24/28/1 у ВСЕХ конфигов,
|
|
||||||
> но это GUI-timer кривые; аудио их не читает (22b). LUT-константы структурной цепи
|
|
||||||
> помечены EMPIRICAL.
|
|
||||||
> 3. Насыщение редукции НАЙДЕНО ЖИВЬЁМ и РЕШЕНО АЛГЕБРАИЧЕСКИ (22d/e):
|
|
||||||
> hot-тон упирается в ЖЁСТКИЙ пол gain=**−20.72 dB**, pin ≥24 dB драйва;
|
|
||||||
> `gain_floor = 20·log10(blend·ln10/20)` — совпадение 0.0055 dB!
|
|
||||||
> Константа ln10/20=0.11513 @0x1824c3cd4, аудио-юзеры FUN_180529c60/52baa0/bad0/bba0.
|
|
||||||
> 4. Инфраструктура: официальный параметр-мост (`setparam.lua`), XML `<PARAM>` в RPP =
|
|
||||||
> декоративная копия (не источник стейта); depth=±18dB/trim=±24dB; WAV bext/junk
|
|
||||||
> грабли подтверждены живьём.
|
|
||||||
> Новый приоритет №1 — Шаг 9 (ниже).
|
|
||||||
|
|
||||||
Цель фазы C (bit-exact): воспроизвести `FramedDetector` (= fn `FUN_180529fe0` mono-path)
|
Цель фазы C: воспроизвести `FramedDetector` (= `FUN_180529fe0` mono-path) до `verify_bit_exact.py` побайтового совпадения (`t1kq_*`, `dual_*`, `comb_*`).
|
||||||
настолько точно, что `verify_bit_exact.py` даёт побайтовое совпадение на рендерах
|
|
||||||
(`t1kq_*`, `dual_*`, `comb_*`). Ниже — конкретный порядок, что и зачем.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0. Текущее состояние (честная метрика, 24-bit, trimmed, 62 случая)
|
## 0. Текущее состояние — ЕДИНСТВЕННЫЙ ИСТОЧНИК `README.md:13`
|
||||||
|
|
||||||
| Группа | bridge mean|err| | bridge max | struct 48k mean|err| | struct max |
|
Честная trimmed метрика 62 случая (bridge vs structural 48k/4096 `corpus_structural.py`):
|
||||||
|--------|----------|-----|----------|---------|
|
|
||||||
| t1kq (fc-scan) | 0.226 | 0.500 | 0.768 | 1.013 |
|
|
||||||
| t1k (loud 0 dBFS) | 1.801 | 2.227 | 2.114 | 2.753 |
|
|
||||||
| al (level sweep) | 0.638 | 1.596 | 0.986 | 1.822 |
|
|
||||||
| res | 0.628 | 1.535 | **0.437** ✓ | 1.252 |
|
|
||||||
| dual (q-sweep) | 0.726 | 2.252 | 3.264 ✗ | 6.455 |
|
|
||||||
| comb (4-band) | **10.149** ✗ | 14.752 | **6.116** ✓ | 9.148 |
|
|
||||||
| **TOTAL** | **1.594** | 14.752 | 2.286 | 9.148 |
|
|
||||||
|
|
||||||
(struct = структурная цепь на внутренней сетке 48000/4096 через `dsp/build/render48k`,
|
| Группа | bridge mean | bridge max | structural mean | VLAW env-gated mean |
|
||||||
прогон `scripts/corpus_structural.py`, NOTES_LEVEL:21c. Bridge = `framed_test` 44.1k.)
|
|--------|-------------|------------|-----------------|---------------------|
|
||||||
|
| t1kq (fc-scan) | 0.226 | 0.500 | 0.768 | 0.426–0.852* |
|
||||||
|
| t1k (loud) | 1.801 | 2.227 | 2.114 | 0.577–0.930* |
|
||||||
|
| al (level) | 0.638 | 1.596 | 0.986 | 0.090–0.804* |
|
||||||
|
| res | 0.628 | 1.535 | **0.437** | 0.395* |
|
||||||
|
| dual (q-sweep) | 0.726 | 2.252 | 3.264 | **0.193** (max 0.438) ✓ |
|
||||||
|
| comb (4-band) | **10.149** | 14.752 | **6.116** | 2.678–4.335* |
|
||||||
|
| **TOTAL** | **1.594** | 14.752 | 2.286 | **0.870–1.931*** |
|
||||||
|
|
||||||
> **21d**: найден закон редукции — эффективное ослабление АФФИННО в dB(lvl):
|
`*` зависит от калибровки VLAW `α/β/c` (семейство dual vs fc-зависимая `AGENTS.md` больше не хранит таблицу — см. `README.md:13` и `handoff/NOTES_LEVEL.md:3919`).
|
||||||
> `x_exp2 = −0.6646 − 0.05877·dB` (маска ≈ 0.8·lvl^−0.354, γ≈decomp 0.344).
|
|
||||||
> На тонах al-свеепа линейность ±0.13 dB (42 dB диапазона!). Прекалибровка
|
|
||||||
> pre-IIR (X0=1.8/S=0.11) даёт TOTAL 2.633: t1kq/t1k/al/comb лучше, res/dual
|
|
||||||
> хуже. Grid-search семейства (X0,S,floor,CMAX) на валидированной траекторной
|
|
||||||
> модели: оптимум rms 0.75 dB, но НИ ОДНА точка семейства не закрывает тон+шум
|
|
||||||
> одновременно → канон не меняем (gate «без регресса групп»), ищем
|
|
||||||
> контент-зависимый механизм (детектор att/rel на флюктуациях / combine-
|
|
||||||
> консюмер). Инструментарий RT_DUMP_BIN/RT_DUMP_ALL оставлен opt-in.
|
|
||||||
|
|
||||||
**Корень проблемы**: активный канон — ЭМПИРИЧЕСКАЯ bridge-модель
|
Канон = bridge `1.594` (эмпирика `C=G·LUT(xv)+W·warp^A, gain=(1−C)·res^rp`). Structural `2.286` обгоняет bridge на `comb/res`, регрессирует на однополосных — форма кривой редукции неверна. VLAW закрывает `dual` (0.193) — применённый слой и каскад декодированы.
|
||||||
`C = G·LUT(xv) + W·warp(f)^A; gain = (1−C)·res^rp` (xv=log10(am/res)). Это ПОГОНА, не
|
|
||||||
транскрипция. Структурная цепочка FUN_180529fe0 перенесена в C++ на внутренней сетке
|
|
||||||
(коммиты 7bf5a4a..b2cb923): уже ОБГОНЯЕТ bridge на comb (−4 dB) и res, но регрессирует
|
|
||||||
на однополосных t1kq/t1k/al и проваливает dual@500 (константный −6.7 dB при всех q —
|
|
||||||
отсутствие НАСЫЩЕНИЯ редукции: реальная кривая упирается в C_max≈0.70, exp2(−lvl) не
|
|
||||||
ограничен). Свип γ×MULT подтвердил: текущий оптимум (0.344/4.2) лучший, слепая
|
|
||||||
подкрутка исчерпана — проблема в ФОРМЕ кривой.
|
|
||||||
|
|
||||||
> **КОРРЕКЦИЯ 22d/e**: «насыщение C_max≈0.70» старой модели = артефакт placeholder
|
|
||||||
> MULT=4.2 / Pchip cap 0.667, НЕ свойство плагина: живой плагин на ±24 dB trim
|
|
||||||
> редуцирует БЕЗ потолка (R 1.4→15.7 dB), а на горячем тоне (+6 dBFS) упирается в
|
|
||||||
> ЖЁСТКИЙ пол gain=−20.72 dB = **blend·ln10/20 точно** (Δ0.0055 dB). Механизм пола —
|
|
||||||
> в нетранскрибированном куске аудио-пути (след: FUN_180529c60, ln10/20-фактор),
|
|
||||||
> см. Шаг 9.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Порядок работ (обязательный порядок; каждый шаг валидируется отдельно)
|
## 1. Порядок работ (3 шага, обязательный порядок; каждый валидируется отдельно)
|
||||||
|
|
||||||
> **ВАЛИДАЦИЯ (обязательно, НЕ пропускать).** Чтобы не повторить регресс Phase B,
|
> **Валидация:** `cmake --build dsp/build --target framed_test` + `python3 scripts/corpus.py` + `python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25` (фейлит при регрессе группы >tol). Структурная цепь валидируется `scripts/corpus_structural.py --vs-bridge`. Каждый под-шаг коммитить отдельно.
|
||||||
> есть защитный харнесс `scripts/corpus.py` + зафиксированный bridge-базлайн
|
|
||||||
> `scripts/baseline_bridge.json` (62 случая, правильная 24-bit метрика):
|
|
||||||
> ```bash
|
|
||||||
> cmake --build dsp/build --target framed_test
|
|
||||||
> python3 scripts/corpus.py # полный корпус, текущая сборка
|
|
||||||
> python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25
|
|
||||||
> ```
|
|
||||||
> `--compare` фейлит (exit≠0), если любая группа регрессирует по mean|err| больше tol.
|
|
||||||
> Правило: структурная цепь (Шаг 1-2) должна НЕ регрессировать ниже bridge на
|
|
||||||
> однополосных (t1kq/t1k/al/res/dual) и ЖЕЛАТЕЛЬНО улучшать comb. Каждый под-шаг
|
|
||||||
> (IIR1 → blend → combine → warp → IIR3) коммитить отдельно и прогонять `--compare` —
|
|
||||||
> если конкретный под-шаг регрессирует, откатить именно его, а не всё сразу.
|
|
||||||
> Существующие `dsp/*_check.cpp` (twin/tables/leveltrack/levelpath/exp2/fftconv) —
|
|
||||||
> модульные чёрные проверки на бит-парность под-функций; тоже гонять: `cmake --build
|
|
||||||
> dsp/build` после правок.
|
|
||||||
|
|
||||||
|
### Шаг 1 — Каскадный симулятор шагов 9–19 FUN_180529fe0 (ПРИОРИТЕТ №1)
|
||||||
|
|
||||||
### Шаг 1 — Реализовать полную структурную mono-цепочку FUN_180529fe0 (КОРЕНЬ)
|
Dataflow декодирован `handoff/BLOCKMAP_529fe0.md:22v` + `handoff/NOTES_LEVEL.md:4400`:
|
||||||
Заменить bridge (эмпирическую погону) на точную транскрипцию. Реализовать в
|
|
||||||
`dsp/framed_model.cpp` (или отдельном `dsp/fn529fe0.cpp`) по NOTES_LEVEL:820-840:
|
|
||||||
```
|
```
|
||||||
1. scale: band_mask *= (fVar30/0x1a0)·0x540870·0x54088c (fVar30=1 из PRNG, locked)
|
bands[i] → LOG#1 (140980) → 52d650 (bidir #1) → vec698*= (1−[54087c]) → vec6f8+= [54087c]·0.8
|
||||||
2. IIR1: y[i]=A1[i]·acc+B1[i]·x[i] (kIIR_A1/B1, fast attack) -> f6f8
|
→ DIVIDE vec6f8/bands (1803a06a0) → vec6f8=bands−ACC (dc40) → fma ATT/REL (1fa0/1940, тройки re,im,coef)
|
||||||
3. copy f6f8 <- band
|
→ COPY (1b80) → mirror 9 + bigkernel 140b60 → EXP#1 (1409e0) + −1 → ×track (th2000) → ×warp (th2000)
|
||||||
4. IIR2: inline, kIIR_A2/B2 (slow release)
|
→ LOG#2 (140980) → IIR4×2 (bidir, log-домен) → EXP#2 → bands_final
|
||||||
5. mirror upper half = reversed lower (Hermitian)
|
→ FIR-секция: DESIGN 1802a24c0 (conv_float_a24c0.dis) → copy → RFFT pair th2180/th1a90 (buf548/598)
|
||||||
6. blend: f6f8 = 0x540698·(1−mix)+mix·0.8; mask = exp2(−level)·f6f8
|
→ FIR[1..n/2]*=2.0 → EXP 1803831c0 → WINfreq· → FIR[0]=1 → df0 track⊗FIR → mask
|
||||||
7. combine: acc[band] = 0x540678 − 0x5406f8; mirror;
|
|
||||||
+= 0x5406c8·upper; += 0x5406e8·lower; += 0x540678
|
|
||||||
8. warp: mask *= kBand768[band]; mask *= kWarp (TWO warps)
|
|
||||||
9. IIR3: inline TWICE, kIIR_A3/B3
|
|
||||||
10. dry/wet: mask = mask·(fVar30·0x540888)+(1−fVar30) (=identity сейчас)
|
|
||||||
11. FFT-conv (Шаг 4)
|
|
||||||
```
|
```
|
||||||
Критерий: на однополосных t1kq/t1k/al/res структурная цепочка должна НЕ регрессировать
|
Тела bigkernel'ов резолвлены статически `handoff/NOTES_LEVEL.md:3944` (`1803a06a0` divide, `180296c80` expf, `1803831c0` табличная кривая, `1803a06a0` etc.; `24mm2` таблица). RFFT до df0 бит-точна `24mm11`.
|
||||||
ниже bridge (т.е. mean ≤ 0.6-0.8). ВАЖНО: прошлый регресс (F2) был из-за неверного
|
|
||||||
домена (пробовали combine в bridge-финале). Здесь combine/exp2 — в ЕГО собственном
|
|
||||||
домене (reduction/exp2), как в декомпе.
|
|
||||||
|
|
||||||
### Шаг 2 — combine/аккумулятор 0x5407c8 (ПЕРЕ-СКОУП 2026-08-21)
|
**Делать:** оп-за-оп транскрипция `scripts/cascade_sim.py` (numpy-эквиваленты divide/expf с FMA-точностью) → валидация на `multi6` (6 нотчей один прогон, `handoff/NOTES_LEVEL.md:3809`) и `sc_*` дистанционной серии (rms сейчас 0.42 на угаданных формах). После структуры — C++ порт `dsp/fn529fe0.cpp` с точными полиномами.
|
||||||
Семантика декодирована на уровне thunk'ов (NOTES_LEVEL:21b, consumers_out.txt:833-885):
|
|
||||||
```
|
|
||||||
f6f8 = mask − acc (0x8d60, dst=3-й аргумент; acc НЕ перезаписывается)
|
|
||||||
mirror f6f8
|
|
||||||
f6f8_upper += kRTAtt·acc_upper ; f6f8_lower += kRTRel·acc_lower (0x3c40)
|
|
||||||
acc += mask (0x5a20, персистентный per-band аккумулятор)
|
|
||||||
```
|
|
||||||
**КРИТИЧЕСКОЕ**: в online однополосном пути обновлённые f6f8/acc НЕ имеют консюмера до
|
|
||||||
warp/IIR3/dry-wet (проверено исчерпывающим grep). Combine НЕ может влиять на single-band
|
|
||||||
вывод сам по себе. Ожидалось, что combine закроет comb — частично закрыл уже сам
|
|
||||||
структурный каркас (comb 6.1 vs bridge 10.1 без wiring). ДАЛЬНЕЙШИЙ ШАГ: найти точку
|
|
||||||
потребления acc/f6f8 (межполосный уровень или FFT-conv каскад) и только потом wire.
|
|
||||||
НЕ изобретать track→mask feedback (эмпирика, запрещено золотым правилом).
|
|
||||||
ВНИМАНИЕ: `FUN_1805316e0` — это WRITER КОЭФФИЦИЕНТОВ полос (17 case), НЕ масковый
|
|
||||||
комбинер (ошибка в старой редакции этого плана и в Шаге 8).
|
|
||||||
|
|
||||||
### Шаг 3 — Bit-exact exp2 (F5b)
|
Критерий: каскад воспроизводит `B=exp(scratch)` из `multi6` на всех 6 пиках ±0.05 dB (сейчас `rms 0.42`), затем `dual` 0.193 сохраняется без per-семейной калибровки.
|
||||||
Заменить `std::exp2`/`exp2d::exp2_dsp` на точную табличную реализацию `0x26b820`:
|
|
||||||
таблицы уже извлечены (`dsp/exp2_tables.{hpp,cpp}`, 8×16 irr + серия kExp2_big). Нужно
|
|
||||||
транскрибировать body 1:1 (Cody-Waite hi/lo, vfmadd213sd-полином, спец-ветви subnormal/
|
|
||||||
overflow). Пока не bit-exact — оставить `exp2d::exp2_dsp` (численно = std::exp2).
|
|
||||||
Критерий: `exp2_check` сравнивает протв захваченных пар точка-в-точку.
|
|
||||||
|
|
||||||
### Шаг 4 — FFT-conv 0x535a70 + FIR (ПРИОРИТЕТ ПОНИЖЕН 2026-08-21)
|
### Шаг 2 — k-маппинг фронтенда (twin/am)
|
||||||
FFT-conv сглаживает маску перед FIR (0x540658 window / freqaxis). Это последний этап
|
|
||||||
mono-цепи. `dsp/fftconv.cpp` есть, но нужна точная блоковая обработка (overlap-save как
|
|
||||||
в декомпе 0x52b550-0x52b8b5), не текущий stand-in. Критерий: маска-гейн после conv
|
|
||||||
совпадает по форме с реальным ref (сглаживание нотча, двусторонний хвост).
|
|
||||||
**ПОНИЖЕНО до P3**: NOTES_LEVEL:18c — окно 0x540658 near-flat (0.8→1.0 plateau при
|
|
||||||
N/2≥2048), эффект построения FIR на форму маски минимален при N=4096. Последовательность
|
|
||||||
уже размечена (fwd → kill mirror → fill xmm13/xmm9 → inv → complex op → fwd → window
|
|
||||||
copy → inv → FIR[0]=1.0); остались неизвестные скаляры xmm13/xmm9 и complex-op шага 4.
|
|
||||||
|
|
||||||
### Шаг 5 — Bit-exact DSP-FFT 0x140a70 (multi-week, P3)
|
`k = lvl_impl / lvl_ours` (`handoff/NOTES_LEVEL.md:3436`): `k(q≥2)=0.403` const, `k(sens)` экспонента (5.37 при s18, 22.0 при s24). При q=1.0 vs q=0.5 `k` идентичен — `q` НЕ влияет на закон (`24kk`), `sens` линейно через `lvl_raw` (`24kk`). Остаток — twin gain пол (`res` плагина ≥0.153 vs наш 0.0069) и амплитудная нормировка `am` (wsum/окно в `dsp/spectral.cpp` vs декомп `FUN_180535880` tail-calls).
|
||||||
Заменить `dsp/fft.cpp` (std::cos radix-2) на точный split-radix 2/4/8 по плану из
|
|
||||||
NOTES_LEVEL:1107-1116:
|
|
||||||
- twiddle: `DAT_182616800` sin-таблица, `sin(k·2π/1024)`, loader `FUN_180039b00` (stride 2^(10-m))
|
|
||||||
- butterfly: `FUN_18000bfc0/18000c5e0` + 0x8440 elementwise mul + `FUN_1800437c0` acc
|
|
||||||
- plan-gen: `FUN_18002f980` (рекурсия + per-log2 фактор-таблицы)
|
|
||||||
Это НЕ блокирует Шаги 1-4 (bridge/структурная цепь рендерят и с численным FFT), нужен
|
|
||||||
только для побайтовой парности FFT-conv-пути.
|
|
||||||
|
|
||||||
### Шаг 6 — Внутренняя геометрия 48000/4096 vs 44100/2048 — ✅ ПАЙПЛАЙН СДЕЛАН (2026-08-21)
|
**Делать:** декод `FUN_180535880` tail-calls + `FUN_180536300` caller (grid `NOTES_TWIN.md`) до точных `twin_coeff`/`am` формул, либо калибровка `k(q,sens,fc)` по `sc_*` + `tract_*` как таблицы с декомп-обоснованием.
|
||||||
Внутренний DSP SR=48000/N=4096 (freqaxis spacing 11.713 Hz). Хост рендера 44100/2048.
|
|
||||||
F4-тест показал, что простая resample_poly НЕ закрывает (даже хуже). Нужно: детектор
|
|
||||||
гнать на 48000/4096 (IIR-таблицы индексированы 0..2048 = N/2+1 при N=4096), затем
|
|
||||||
свести к 44100. Ожидается закрытие хвостов t1k_b1f/al (+2.2/−1.6) — именно уровневой
|
|
||||||
зависимости. Критерий: корректное выравнивание бина и окна между двумя сетками.
|
|
||||||
**Статус**: пайплайн `dsp/render48k.cpp` (resample 44.1→48 → SpectralProcessor(4096,1024,48000)
|
|
||||||
→ resample обратно) реализован и гоняет полный корпус (`scripts/corpus_structural.py`,
|
|
||||||
коммиты 7659eb0/6924e53). Выравнивание бина/окна валидировано smoke; уровневые хвосты
|
|
||||||
t1k/al НЕ закрыты самим по себе — см. §0 и Шаг 7. Известный артефакт: zero-pad последнего
|
|
||||||
BLK-блока даёт спад am в ~последних 0.06s (косметика, на метрику почти не влияет).
|
|
||||||
|
|
||||||
### Шаг 7 — BandConfig A/B/γ (level-path ctx+0x188) live-захват под конкретные конфиги
|
Критерий: cross-config `α/β/c` становятся вычисляемыми, а не фитовыми; `sens 6..24` перестаёт требовать per-групповой `α`.
|
||||||
> **СТАТУС 2026-08-22: ВЫПОЛНЕН → ПРЕМиса ОПРОВЕРГНУТА (NOTES_LEVEL 22b).** Захват по 7
|
|
||||||
> конфигам дал идентичные A=−24/B=28/γ=1, но весь кластер FUN_180563440/563a60 —
|
|
||||||
> GUI-timer only; аудио FUN_180529fe0 BandConfig не читает. Насыщение кривой редукции
|
|
||||||
> искать в теле аудио-функции (см. NOTES_LEVEL 22b, выводы).
|
|
||||||
Структурная LUT-кривая `FUN_180563a60` (A/B/γ). Снято для render_long (A=−24/B=28/γ=1)
|
|
||||||
и t1kq (то же), но для остальных тестов не захвачено. Метод автоматизирован
|
|
||||||
(NOTES_CAPTURE.md). Захватить для t1k_b1f / al / dual-конфигов → реальные A/B/γ → это
|
|
||||||
закрывает уровневую зависимость, которую bridge-LUT не может (F1 closure). Критерий:
|
|
||||||
mean кап-нагрузки al/t1k ≤ 0.3 dB.
|
|
||||||
|
|
||||||
### Шаг 8 — Стерео M8 + полный pipeline (P4/P5)
|
### Шаг 3 — Δ-правило вторых пиков (pre-combine)
|
||||||
Финальный рендер stereo (link/balance/LR-vs-MS) по M8 и межполосное суммирование.
|
|
||||||
Все текущие рендеры mono. Для bit-exact графа нужны стерео-рендеры как мишени.
|
|
||||||
ВНИМАНИЕ: `FUN_1805316e0` = writer коэффициентов полос, НЕ комбинер масок — точка
|
|
||||||
межполосного суммирования масок/acc ещё не локализована (см. пере-скоуп Шага 2).
|
|
||||||
Критерий: `verify_bit_exact.py` — побайтовое совпадение данных-чанка WAV.
|
|
||||||
|
|
||||||
### Шаг 9 — Механизм пола редукции blend·ln10/20 (НОВЫЙ ПРИОРИТЕТ №1, 2026-08-22)
|
Второй контент-пик: `cut = α·ln1p(g·lvl/β)+c`, `g@2000≈12.15` vs `g@fc=1.0` (`handoff/NOTES_LEVEL.md:3890`), но `g` меняется между прогонами (12.15 vs 1.85 при той же геометрии) — зависит от `STATE=[ctx+0x540788]` (спектральная память, `24ii2`). Смешение шаблонно-локальное (`24ll`: далёкий тон@4000 не влияет), ширина провалов константа `24k-2`.
|
||||||
Живой факт (22d): hot-тон упирается в ЖЁСТКИЙ пол gain=−20.72 dB = `20·log10(0.8·ln10/20)`
|
|
||||||
с точностью 0.0055 dB; колено между L=−6..0 (trim-шкала); холодный тон на ±24 trim
|
|
||||||
пола НЕ достигает. t-clamp в транскрибированной LUT НЕ даёт pin (grid MULT≤12/B≥18 —
|
|
||||||
NOTES 22e) ⇒ пол живёт в другом куске аудио-пути.
|
|
||||||
|
|
||||||
9a. Декодировать `FUN_180529c60` полностью: фактор
|
**Делать:** серия `campaign.py` с вариацией `STATE` (разный контент до основного тона) при фиксированной геометрии → формула `g(b)=G(геометрия, STATE)` + pre-combine шага `52a397` (`handoff/BLOCKMAP_529fe0.md:43`).
|
||||||
`expf((p87c·30 − 90)·ln10/20)` на band-буферы 0x540678[band] через
|
|
||||||
`FUN_1804d56b0` (f_529c60.dis:94-101) — что именно считает 1804d56b0 и куда
|
Критерий: `d1500` серия `3.43→4.23` монотонна без отрицательных `w(d)` (`24mm`).
|
||||||
идёт произведение (это vtbl-метод рядом с аудио-entry).
|
|
||||||
9b. Найти сайт клампа: grep ln10/20-константы (0x1824c3cd4) по остальным юзерам
|
|
||||||
(18052baa0/bad0/bba0) и по f529fe0.dis/f_52d650.dis на предмет floor/clamp маски.
|
|
||||||
9c. Проверка предсказаний модели пола: (i) floor_gain(mix) сдвигается как
|
|
||||||
20log10(blend′/0.8); (ii) пол частотно-зависит только через blend(freqaxis)
|
|
||||||
при mix<100; (iii) knee-позиция от sens/depth. Рендеры через setparam.lua.
|
|
||||||
9d. Порт клампа в `process_band_structural` → smoke → корпус с гейтом
|
|
||||||
(`corpus_structural.py --vs-bridge`; цель: dual@500 уходит с −6.7, res/dual/comb ≥ bridge).
|
|
||||||
Критерий: воспроизвести pin −20.72 dB в рендере модели + отсутствие регресса групп.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Что НЕ делать (подводные камни из дока)
|
## 2. Что НЕ делать
|
||||||
|
|
||||||
- **НЕ вводить эмпирию там, где есть декомп.** Каждый параметр — из декомп-адреса или
|
- **НЕ вводить эмпирию где есть декомп** (золотое правило `AGENTS.md:33`). `LCP` и `MULT` без источника — помечать `EMPIRICAL`.
|
||||||
live-таблицы, иначе пометить EMPIRICAL и в «осталось» (золотое правило AGENTS).
|
- **НЕ делать combine в bridge-final-gain домене** — F2 регресс 4.68/5.22 `handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md:1064`.
|
||||||
- **НЕ делать combine в bridge-final-gain домене** — F2 показал регресс 4.68/5.22.
|
- **НЕ читать WAV наивно** — только `render_parity.load` (bext/junk, 24-bit `handoff/NOTES_LEVEL.md:1462`).
|
||||||
Combine/exp2 живут в reduction/exp2-домене реальной цепочки.
|
- **НЕ править XML `<PARAM>` в RPP** — декоративная копия, стейт в бинарной части (`setparam.lua`/`patchparam.py`).
|
||||||
- **НЕ менять Pchip-LUT в bridge** — регрессирует весь корпус (F1, параметрич. хуже).
|
- **НЕ свёртка/OLA поверх маски** — опровергнуто `24j` (`A=1.019·V^1.8345` = произведение экспонент, не свёртка).
|
||||||
Bridge — только запасной вариант, пока структурная цепочка не пройдёт Шаг 1-2.
|
|
||||||
- **НЕ использовать битые 24-bit загрузчики** — метрика `render_parity.py:43` правильная
|
|
||||||
(x>=0x800000 => x−0x1000000). В тестовых скриптах использовать тот же код.
|
|
||||||
- **НЕ редактировать XML `<PARAM>` в RPP ради изменения звука** — это декоративная
|
|
||||||
UI-копия, плагин берёт стейт из бинарной части чанка (NOTES 22c). Только мост
|
|
||||||
`setparam.lua`.
|
|
||||||
- **НЕ читать WAV наивным readframes+reshape** — REAPER пишет bext/junk-чанки; только
|
|
||||||
каноничный `render_parity.load` (иначе фантомный шум/клиппинг, NOTES 22c).
|
|
||||||
|
|
||||||
## 3. Риски и время
|
## 3. Риски и время
|
||||||
|
|
||||||
- **Доминирующий риск**: дорогой структурный перенос (Шаг 1-2) может снова регрессировать
|
- Доминирующий риск — каскад 9–19: 5700 строк AVX-512+FMA (`1803831c0`), 10 ядер. Митигация: numpy-симулятор до C++ порта, валидация на `multi6` самосогласованно.
|
||||||
ниже bridge, как Phase B. Митигация: валидировать каждый под-шаг (IIR1 отдельно, blend
|
- Twin/am — multi-week (dispatch ядра 2/4/8, plan-gen `FUN_18002f980`). Не блокирует Шаг 1.
|
||||||
отдельно) на t1kq, не коммитить пока не ≥ bridge.
|
- Стерео M8 — отложен (все рендеры mono).
|
||||||
- **DSP-FFT (Шаг 5)** — multi-week само по себе. Но НЕ блокирует Шаги 1-4.
|
- Оценка: Шаг 1 — 1–2 недели, Шаг 2 — 1–2 недели, Шаг 3 — дни. До байтов — 1–2 месяца.
|
||||||
- **Стерео (Шаг 8)** — новая мишень-корпус, больше рендеров.
|
|
||||||
- **Оценка**: Шаги 1-4 (монопуть) — 1-2 недели. Шаг 5 — до 3 недель. Шаги 6-8 — 1-2 недели.
|
|
||||||
До полной байтовой парности — ориентировочно 1-2 месяца с аккуратным монофокусом.
|
|
||||||
|
|
||||||
## 4. Точка входа для следующей сессии
|
## 4. Точка входа
|
||||||
|
|
||||||
1. Прочитать `AGENTS.md`, этот `BITEXACT_PLAN.md`, `handoff/NOTES_LEVEL.md`
|
1. `AGENTS.md` → `handoff/NOTES_LEVEL_INDEX.md` (живая голова `24mm5+`) + `handoff/BLOCKMAP_529fe0.md`.
|
||||||
апдейты **20j / 21a-21e / 22a-22e** (цепочка, Phase-B негативы, Шаг 7 опровержение,
|
2. `touch dsp/framed_model.cpp && cmake --build dsp/build --target render48k framed_test` (hazard `AGENTS.md:66`).
|
||||||
параметр-мост, пол blend·ln10/20) и `dsp/rt_mask_tables.hpp` / `dsp/rt_weights.hpp`.
|
3. Бейзлайны: `python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25` (TOTAL 1.594); VLAW dual `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0` → 0.193.
|
||||||
2. Собрать: `cmake --build dsp/build --target render48k framed_test`.
|
4. Инструменты: `scripts/cascade_sim.py` (структурная фаза), `scripts/rendersnap2.py`, `scripts/disasm_func.py`, `scripts/iat_name.py`, `scripts/campaign.py`.
|
||||||
3. Бейзлайны: `python3 scripts/corpus.py` (bridge, 62 случая, §0) и
|
|
||||||
`python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json`.
|
|
||||||
4. ПРИОРИТЕТ №1 — **Шаг 9** (пол редукции):
|
|
||||||
a. 9a: полный декод FUN_180529c60 + FUN_1804d56b0 (f_529c60.dis готов).
|
|
||||||
b. 9b: grep 0x1824c3cd4 по аудио-функциям; hunt клампа маски.
|
|
||||||
c. 9c: предсказания пола — рендеры mix/sens через `setparam.lua` (мост готов,
|
|
||||||
XML-правки стейта НЕ работают — NOTES 22c).
|
|
||||||
d. 9d: порт + корпус с гейтом.
|
|
||||||
5. Инструменты сессии: `setparam.lua`/`dump_params.lua` (параметр-мост),
|
|
||||||
`scripts/rpp_setparam.py` (только формат-хирургия!), `RT_LUT_*` env в
|
|
||||||
framed_model.cpp (солвер констант), `scripts/step7_capture.py` (live BandConfig).
|
|
||||||
|
|||||||
@@ -10,69 +10,49 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Статус (P5, 2026-08-22)
|
## Статус (2026-09-02, chain919) — ЕДИНСТВЕННЫЙ ИСТОЧНИК TOTAL
|
||||||
|
|
||||||
**Цель — bit-exact реверс.** Декомпиляция DSP-ядра закрыта (~95%): twin-резонатор,
|
**Bridge (`framed_test` 44.1k): TOTAL 1.594 dB** (`scripts/baseline_bridge.json`, guard `--tol 0.25`). **Структурный `render48k` 48k/4096 L/R (`RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0`): TOTAL 2.689 dB** (dual 2.04, t1k 5.43, max `6.8`). Исторический канон `0.341` (24mm14, `f40f41e`) недостижим на текущем `HEAD` (L/R `e343b0a` + chain gated `RT_CASC=0`); требует перекалибровки `per-fc`/`EQ`. Guard: `python3 scripts/corpus_structural.py` + `python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25`. Chain 9–19 интегрирован (`RT_CASC=1` gated, IIR4 double + FIR min-phase) но некалиброван (`31.6` при включении, блокер — входной формат level vs cut). Следующий шаг — калибровка chain по live-dump/`ph*.npz` (BLOCKMAP:285). Детали — `handoff/NOTES_LEVEL_INDEX.md`, `.opencode/plans/chain919.md`.
|
||||||
генератор case8, level-path, mask-apply (FUN_180529fe0), FFT-conv — расшифрованы;
|
|
||||||
дизассемблы в `handoff/nls_dasm/` (~140).
|
|
||||||
|
|
||||||
**Активный канон — C++ `FramedDetector`** (`dsp/framed_model.cpp`): структурная цепь
|
**Цель — bit-exact** (гейт = все параметры до декомпа + корпус в шумовой пол). Декомп ~95%, `handoff/nls_dasm/` ~140 `.dis`.
|
||||||
|
|
||||||
|
**Применение декодировано**:
|
||||||
```
|
```
|
||||||
level=am*res*scale -> IIR1/IIR2 leaky -> mask=exp2(-level)*blend -> combine/acc
|
mask(b) = 10^(−cut_D(b)/20) ← вещественная, per-bin multiply
|
||||||
-> warp(mask*=0x540768, *=warp) -> IIR3x2 -> dry/wet -> FFT-conv
|
cut_D(b) = α·ln(1+lvl_raw EQ(b)/β)+c [+Δ content-aware, lvl EQ before detector]
|
||||||
|
lvl_raw = am/res·scale · W_eq(fc,q,sens) (W=10^(sens·H·0.3/12/20), H=1/√(1+(1.54q^1.33·A)²))
|
||||||
|
слой = STFT БЕЗ синтез-окна (RT_SYN=1), EQ до детектора (pipeline_ocr)
|
||||||
```
|
```
|
||||||
- **Level-tracker = би-направленный leaky-IIR** `y=A[i]·acc+B[i]·x` (B=1−A), live-таблицы
|
Калибровки: α/β/c контент-зависимы (dual `3.22/0.49/0.54 Δ6.92` rms 0.016), q не влияет (`24kk`), sens линейно, `fc`-геом через `W_eq` (`24mm14` `R bandpass`), `dual q1.0` misclass пофикшено `has_second_peak+maxlvl>2` (`dsp/framed_model.cpp:331`), `t1k_500` `4.5/0.35` + `comb 0.05/5.0`.
|
||||||
в `dsp/rt_mask_tables.{hpp,cpp}`, `dsp/rt_weights.{hpp,cpp}`.
|
|
||||||
- **Корпус (62 случая)**: bridge mean 1.594 dB; структурная цепь TOTAL 2.286,
|
|
||||||
но comb **6.12** и res **0.44** — лучше bridge.
|
|
||||||
|
|
||||||
### Главное за 2026-08-21…22 (сессии 21a–22i)
|
- **dual+EQ**: исторический `0.341` (канон `f40f41e`) сейчас `2.689` на L/R `e343b0a` — регресс из-за смены M/S→L/R + chain gated; per-fc `800/1200` и EQ требуют перекалибровки.
|
||||||
|
- **Буфер FIR@540668** — `exp(scratch)` напрямую (`24s`), мин.-фазовое `exp(s−iH(s))`.
|
||||||
|
- **Bigkernels** IAT: `1803a06a0 / 180296c80 / 180323f20 / 1802dc0e0` (`handoff/BLOCKMAP_529fe0.md:540`).
|
||||||
|
|
||||||
1. **«LUT-кривая» FUN_180563440/563a60 — GUI-only!** Весь кластер кривых питается от
|
### Главное за 2026-08-24…28 (24j–24mm14)
|
||||||
GUI-timer vtables; аудио-метод FUN_180529fe0 BandConfig не читает (22b). Live BandConfig
|
|
||||||
у всех конфигов одинаковый: A=−24/B=28/γ=1. LUT-константы в модели помечены EMPIRICAL.
|
1. **Применение = побиновный multiply** на вещественную маску; «×1.805» = `0.984×1.8345` (экспоненты стадий).
|
||||||
2. **Пол редукции найден живьём и решён алгебраически** (22d/e): hot-тон упирается в жёсткий
|
2. **Закон `α·ln1p(L/β)+c`** — три независимых калибровки; константы контент-зависимы.
|
||||||
пол gain=−20.72 dB = `20·log10(blend·ln10/20)` с точностью 0.0055 dB;
|
3. **Слой STFT без синтез-окна** (`RT_SYN=1`); `WIN_WINDOW` 0.5→0.8 за 2049 сэмплов.
|
||||||
`floor_dB(sens) ≈ −18.78 − (sens−6)/3` (якорь sens6 = ln10/20 ровно).
|
4. **GUI/аудио разделение**: `FUN_180563a60` — GUI-ветка; аудио-компрессия в шагах 9–19 `BLOCKMAP`.
|
||||||
3. **Разрыв локализован в детекторном фронте** (22g/i): модель теряет ×2.8 уровня на
|
5. **Dataflow 9–16**: `vec6f8=bands−ACC`, fma тройками `(re,im,coef)` ATT/REL, `th2000`=array-mul, шаг 12=COPY.
|
||||||
изолированных пиках (lvl_raw 3.01 → 1.06 после IIR1-разведения), реальный плагин
|
6. **Детекторный каскад `180529c60` (vt+0x28)**: `|z| → Haar [0.25,0.5,0.25]×2 → peak/sin → w=1/inner → blend 5407a8`.
|
||||||
доводит пост-IIR lvl до ~3.12. Не форма кривой — амплитудная цепочка am/res/scale.
|
7. **RFFT до df0 бит-точна** (`th1a90/th2180` + `buf548` scale 2^-12, `24mm9` 0.0065 dB).
|
||||||
4. **Инфраструктура**: официальный параметр-мост REAPER (`setparam.lua`/`dump_params.lua`),
|
|
||||||
XML `<PARAM>` в RPP = декоративная копия; live-capture BandConfig (`scripts/step7_capture.py`);
|
|
||||||
env-солверы констант (`RT_LUT_A/B/G/MULT`, `RT_LUT_OFF`, `RT_LVL_CAP`).
|
|
||||||
5. Оффлайн-гипотезы Phase B (pooling/temporal/scalar-ρ) — все опровергнуты (22a);
|
|
||||||
clipping-теория пола опровергнута (float-домен, 22g); mix = чистый dry/wet кроссфейд.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Канон сборки и рендера:
|
# Сборка и канонные команды (см. AGENTS.md — runbook):
|
||||||
cmake -S dsp -B dsp/build && cmake --build dsp/build --target framed_test render48k
|
cmake -S dsp -B dsp/build && cmake --build dsp/build --target framed_test render48k
|
||||||
./dsp/build/render48k /home/m/soothe-bt/tone1kq.wav /tmp/o48.wav 1000,0.99999785,12
|
./dsp/build/render48k /home/m/soothe-bt/tone1kq.wav /tmp/o48.wav 1000,0.99999785,12
|
||||||
|
python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25
|
||||||
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
|
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
|
||||||
```
|
```
|
||||||
Детальная метрика и история — в `AGENTS.md`, `BITEXACT_PLAN.md`, `handoff/NOTES_LEVEL.md`
|
|
||||||
(апдейты 20j…22i).
|
|
||||||
|
|
||||||
### Открытые bit-exact пробелы
|
### Открытые bit-exact пробелы (приоритет → `BITEXACT_PLAN.md`)
|
||||||
**Приоритет №1 — детекторный фронт**: амплитудная нормировка am, форма res_k, сила
|
1. **Каскадный симулятор шагов 9–19** — dataflow готов (`24hh/24ii`), тела bigkernel'ов известны, rms ~0.42 на угаданных формах → оп-за-оп транскрипция + `sc_*` датасеты.
|
||||||
IIR1/2 вдоль частоты (модель теряет уровень на пиках). Далее: PRNG-пролог (LCG→fVar30),
|
2. **k-маппинг фронтенда** (twin/am, `k=0.403 ∀q≥2`, `24x`).
|
||||||
FFT-conv сглаживание, бит-экзактный exp2, combine/acc консюмер, SR-mismatch
|
3. **Δ-правило вторых пиков** из pre-combine.
|
||||||
(внутренний DSP 48000/N=4096 против хоста). Полный список — `AGENTS.md`, `BITEXACT_PLAN.md`
|
|
||||||
(Шаг 9), `NOTES_LEVEL.md`.
|
|
||||||
|
|
||||||
> Исторический блок (поведенческая/численная модель B.1…B.15, `framed_render.py`,
|
> Исторический блок B.1…B.15 (`framed_render.py`, Pchip, `res_power`) — см. `handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md` и `roadmap.md`; не канон.
|
||||||
> Pchip LUT, res_power) — см. `roadmap.md`; самодостаточен как справочник, но не канон.
|
|
||||||
|
|
||||||
<details><summary>Было (B.15 — историческая численная модель)</summary>
|
|
||||||
|
|
||||||
`framed_render.py` (N=2048, hop=512, sqrt-Hann, twin-env tatt=11ms/trel=80ms):
|
|
||||||
`C(f_k)=g·LUT(log10(A_k/res_k)) + w·warp(f_k)^a`, `gain=(1−C)·res^(rp0·Q^drp)`.
|
|
||||||
- Честная (trimmed) метрика: Q-dep rp → mean 0.175 dB; B.15 joint free-knot LUT → dual 0.027 dB.
|
|
||||||
- Декомп DSP-ядра закрыт, `.dis` в `handoff/nls_dasm/`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 framed_render.py dual # (исторический канон B.15)
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -80,69 +60,52 @@ python3 framed_render.py dual # (исторический канон B.15)
|
|||||||
|
|
||||||
```
|
```
|
||||||
re-tools/
|
re-tools/
|
||||||
├── README.md ← вы здесь
|
├── README.md ← вы здесь (ЕДИНСТВЕННЫЙ источник TOTAL)
|
||||||
├── AGENTS.md ← гайд для агентов: сборка, метрика, bit-exact пробелы
|
├── AGENTS.md ← runbook: сборка, метрика, env-флаги, tooling hazard
|
||||||
├── roadmap.md ← журнал всех результатов/гипотез (B.1…B.15, P4)
|
├── BITEXACT_PLAN.md ← план к bit-exact (3 шага, критерии, риски)
|
||||||
├── framed_render.py ← историческая численная модель (B.15, не канон)
|
├── roadmap.md ← журнал B-фаз (B.1…B.15, свёрнут; детали → archive)
|
||||||
├── render_parity.py ← dB-parity харнесс (Goertzel steady-state замер)
|
|
||||||
├── model_lut.py, model_dual.py ← исторические модели B.10/B.11 (collapse-фиты)
|
|
||||||
├── model_fir.py ← bridge-модель B.12 (C=g·LUT+w·warp^a)
|
|
||||||
│
|
|
||||||
├── handoff/ ← журнал сессий
|
|
||||||
│ ├── SESSION_HANDOFF.md ← инвентарь декомпа, трансляция, Phase-5 план
|
|
||||||
│ ├── NOTES_TWIN.md ← twin-резонатор + caller + grid
|
|
||||||
│ ├── NOTES_LEVEL.md ← level-path, mask-цепь, live-таблицы (САМЫЙ АКТУАЛЬНЫЙ)
|
|
||||||
│ ├── NOTES_CAPTURE.md ← live-захват таблиц (registry heartbeat)
|
|
||||||
│ └── nls_dasm/ ← 134 дизассембла декомпа (f_563440, f_563a60, f529fe0, twin, fft…)
|
|
||||||
│
|
|
||||||
├── dsp/ ← реконструкция DSP-пайплайна на C++17
|
├── dsp/ ← реконструкция DSP-пайплайна на C++17
|
||||||
│ ├── framed_model{.cpp,.hpp} ← ГЛАВНЫЙ: mask-apply цепь (P4, активный канон)
|
│ ├── framed_model{.cpp,.hpp} ← ГЛАВНЫЙ: mask-apply цепь (канон)
|
||||||
│ ├── framed_test.cpp ← CLI рендер входа (N=2048, SR 44100) + метрика
|
│ ├── framed_test.cpp ← CLI bridge-рендер (N=2048, SR 44100)
|
||||||
│ ├── rt_mask_tables{.hpp,.cpp}, rt_weights{.hpp,.cpp} ← live-таблицы (IIR A/B, warp…)
|
│ ├── render48k.cpp ← структурная цепь 48k/4096 (resample → chain → resample)
|
||||||
│ ├── twin{.hpp,.cpp} ← бит-точный twin-резонатор (FUN_180535880)
|
│ ├── rt_mask_tables{.hpp,.cpp}, rt_weights{.hpp,.cpp} ← live-таблицы (IIR A/B, warp)
|
||||||
│ ├── levelpath.cpp/.hpp ← LUT-кривая FUN_180563440/563a60 + combine-ядра
|
│ ├── twin{.hpp,.cpp} ← twin FUN_180535880 (float-parity)
|
||||||
│ ├── freqpath.cpp/.hpp ← warp FUN_180530850 (0.87·x/(1+x/K))
|
│ ├── levelpath/freqpath/fftconv/vlog/leveltrack/fn529fe0 ← расшифрованные модули
|
||||||
│ ├── fftconv.cpp/.hpp ← FFT-conv (0x535a70)
|
│ ├── fft*.cpp, twiddle_*.cpp/hpp, phase_table.*, cody_waite.hpp
|
||||||
│ ├── spectral.cpp/.hpp ← WOLA/STFT-обработчик
|
|
||||||
│ ├── fft*.cpp, twiddle_*.cpp/hpp, phase_table.* ← FFT-планы/твилдлы
|
|
||||||
│ ├── ms.hpp ← encode/decode mid/side
|
|
||||||
│ ├── cody_waite.hpp ← быстрый sin/cos (FUN_1801de760/1e3f20)
|
|
||||||
│ ├── soothe_constants.hpp ← константы из дампа
|
|
||||||
│ ├── harness.cpp ← CLI: input.wav → output.wav (24-bit)
|
|
||||||
│ └── CMakeLists.txt
|
│ └── CMakeLists.txt
|
||||||
│
|
├── handoff/
|
||||||
├── *.java ← Ghidra-скрипты (analyzeHeadless, пост-скрипты)
|
│ ├── NOTES_LEVEL.md ← живой журнал (голова 24mm5+; хвост → archive/)
|
||||||
├── ghidra-proj/ ← Ghidra-проект (soothe2.gpr/.rep), вне git
|
│ ├── NOTES_LEVEL_INDEX.md ← оглавление журнала по датам/темам
|
||||||
├── soothe_mem.bin ← дамп памяти плагина, вне git
|
│ ├── BLOCKMAP_529fe0.md ← карта метода FUN_180529fe0 (актуальна)
|
||||||
├── rwin_{A0,A1,B0,C0}.npy, r_freqaxis.npy ← живые таблицы (48k) из runtime-снимков
|
│ ├── NOTES_TWIN.md ← twin краткая справка (детали → dsp/twin.cpp)
|
||||||
│
|
│ ├── NOTES_CAPTURE.md ← registry heartbeat, live-таблицы (сжато)
|
||||||
├── Измерение и фиты полосы: measure.py, probe.py, bandshape.py, fit_*.py,
|
│ ├── archive/ ← история: NOTES_LEVEL_2026-08-18_2026-08-23.md,
|
||||||
│ model_lut.py, notch.py (исторические, B-модели)
|
│ │ SESSION_HANDOFF_2026-08-18.md, summary_v4.md
|
||||||
├── Поведенческие симуляторы: sim.py, sim_v5.py, verify_sim.py (исторические)
|
│ └── nls_dasm/ ← 134 дизассембла (f_563440, f_563a60, f529fe0, twin, fft)
|
||||||
├── Инструменты REAPER-рендеров: sweep.py, run_sweep.py, tt_sweep.py,
|
├── scripts/
|
||||||
│ patchparam.py, addfx.lua
|
│ ├── corpus.py / corpus_structural.py ← харнессы (guard --compare)
|
||||||
├── Живая трассировка (yabridge-host + Frida): dump_soothe.py, rtall/rtscan/
|
│ ├── rendersnap2.py, campaign.py, disasm_func.py, iat_name.py ← инструменты 24j+
|
||||||
│ rtsig/rttbl/rtver/rtone/rtwin/rtdeep*.py, probe.py, procdump.py
|
│ └── lawfit22r.py, resalpha.py, cascade_sim.py, wine_*trace.py
|
||||||
├── Параметр-мост и live-capture: setparam.lua, dump_params.lua,
|
├── *.java ← Ghidra-скрипты (DumpFuns, ImportRtti …)
|
||||||
│ play.lua, scripts/step7_capture.py, scripts/rpp_setparam.py,
|
├── soothe_mem.bin ← дамп памяти (вне git, VA−0x180000000)
|
||||||
│ scripts/corpus{,_structural}.py, scripts/phaseA/B*.py
|
├── rwin_{A0,A1,B0,C0}.npy, r_freqaxis.npy ← живые таблицы (48k)
|
||||||
└── summary.md, notes_giant_fft.md
|
└── framed_render.py, model_*.py, sim.py ← исторические B-модели (не канон)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Документация
|
## Документация
|
||||||
|
|
||||||
| Документ | Содержание |
|
| Документ | Содержание |
|
||||||
|---|---|
|
|---|---|
|
||||||
| [AGENTS.md](AGENTS.md) | **Старт для агента**: сборка, метрика, bit-exact пробелы, структура |
|
| [AGENTS.md](AGENTS.md) | **Runbook**: сборка, метрика, env-флаги, tooling hazard |
|
||||||
| [BITEXACT_PLAN.md](BITEXACT_PLAN.md) | **Путь к bit-exact**: порядок работ (8 шагов), критерии, риски, точка входа |
|
| [BITEXACT_PLAN.md](BITEXACT_PLAN.md) | **План к bit-exact**: 3 шага, критерии, риски, точка входа |
|
||||||
| [roadmap.md](roadmap.md) | Журнал всех результатов/гипотез (B.1…B.15, P4), статус по фазам, риски |
|
| [handoff/NOTES_LEVEL_INDEX.md](handoff/NOTES_LEVEL_INDEX.md) | **Оглавление журнала** по датам/темам → `NOTES_LEVEL.md` / `archive/` |
|
||||||
| [handoff/SESSION_HANDOFF.md](handoff/SESSION_HANDOFF.md) | Инвентарь декомпа (§0), трансляция/ключевые адреса (§2), Phase-5 план (§6) |
|
| [handoff/NOTES_LEVEL.md](handoff/NOTES_LEVEL.md) | Живой журнал (голова 24mm5+; хвост → `archive/`) |
|
||||||
| [handoff/NOTES_LEVEL.md](handoff/NOTES_LEVEL.md) | **Level-path/mask-цепь, live-таблицы, bit-exact протокол** (самый актуальный) |
|
| [handoff/BLOCKMAP_529fe0.md](handoff/BLOCKMAP_529fe0.md) | Карта метода FUN_180529fe0 (актуальна) |
|
||||||
| [handoff/NOTES_TWIN.md](handoff/NOTES_TWIN.md) | Twin-резонатор (FUN_180535880/536f90), caller, grid/oversample |
|
| [handoff/nls_dasm/](handoff/nls_dasm/) | 134 дизассембла декомпа |
|
||||||
| [handoff/NOTES_CAPTURE.md](handoff/NOTES_CAPTURE.md) | Live-захват таблиц (registry heartbeat, SR 48000) |
|
| [roadmap.md](roadmap.md) | Журнал B-фаз (свёрнут, детали → archive) |
|
||||||
| [handoff/nls_dasm/](handoff/nls_dasm/) | 134 дизассембла (f_563440, f_563a60, f529fe0, twin, iface, fft) |
|
| [handoff/NOTES_TWIN.md](handoff/NOTES_TWIN.md) | Twin краткая справка (детали → `dsp/twin.cpp`) |
|
||||||
| [notes_giant_fft.md](notes_giant_fft.md) | FFT-планировщики/ядра/twiddle/Cody-Waite |
|
| [handoff/NOTES_CAPTURE.md](handoff/NOTES_CAPTURE.md) | Registry heartbeat, live-таблицы (сжато) |
|
||||||
| [summary.md](summary.md) | **Историческая** сводка поведенческой модели (v4, sim.py) |
|
| [handoff/archive/](handoff/archive/) | История: `NOTES_LEVEL_2026-08-18_2026-08-23.md`, `SESSION_HANDOFF_…`, `summary_v4.md` |
|
||||||
|
|
||||||
### Тестовый корпус `/home/m/soothe-bt/` (вне git)
|
### Тестовый корпус `/home/m/soothe-bt/` (вне git)
|
||||||
|
|
||||||
@@ -187,15 +150,8 @@ gain = max(1 - C, eps) * res_k**rp # rp = rp0 * Q**drp (res_power)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Чего не хватает / следующие шаги (bit-exact)
|
## Чего не хватает / следующие шаги (bit-exact) → `BITEXACT_PLAN.md`
|
||||||
|
|
||||||
Текущий фокус — **детекторный фронт** (Шаг 9 плана): амплитудная нормировка am,
|
1. **Каскадный симулятор шагов 9–19** (приоритет №1) — см. `BITEXACT_PLAN.md:1`.
|
||||||
форма res_k, сила IIR1/2 вдоль частоты — модель теряет ×2.8 уровня на изолированных
|
2. **k-маппинг фронтенда** twin/am (приоритет №2).
|
||||||
пиках относительно реального плагина (NOTES_LEVEL 22g/22i). Открытое:
|
3. **Δ-правило вторых пиков** (приоритет №3).
|
||||||
- **Детекторный фронт** (приоритет №1, Шаг 9) — см. выше.
|
|
||||||
- **PRNG-пролог** FUN_180529fe0 (LCG+LUT → fVar30) — per-frame рандомизация scale/dry-wet.
|
|
||||||
- **FFT-conv** (0x535a70) — сглаживание маски перед FIR (P3).
|
|
||||||
- **Бит-экзактный exp2** (0x26b820) вместо `std::exp2`.
|
|
||||||
- Точная обратная связь combine/аккумулятора `0x5407c8` (консюмер не найден).
|
|
||||||
- **SR-mismatch**: внутренний DSP 48000/N=4096 против хоста 44100/2048.
|
|
||||||
- Стерео-путь M8 (link/balance/LR-vs-MS), межполосное суммирование (Шаг 8).
|
|
||||||
@@ -25,9 +25,11 @@ add_library(soothe2_dsp SHARED
|
|||||||
exp2.cpp
|
exp2.cpp
|
||||||
leveltrack.cpp
|
leveltrack.cpp
|
||||||
framed_model.cpp
|
framed_model.cpp
|
||||||
|
fnfaith.cpp
|
||||||
fn529fe0.cpp
|
fn529fe0.cpp
|
||||||
rt_weights.cpp
|
rt_weights.cpp
|
||||||
rt_mask_tables.cpp
|
rt_mask_tables.cpp
|
||||||
|
log2_ln.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(soothe2_harness harness.cpp)
|
add_executable(soothe2_harness harness.cpp)
|
||||||
@@ -40,11 +42,13 @@ add_executable(vlog_check vlog_check.cpp)
|
|||||||
add_executable(leveltrack_check leveltrack_check.cpp)
|
add_executable(leveltrack_check leveltrack_check.cpp)
|
||||||
add_executable(levelpath_check levelpath_check.cpp)
|
add_executable(levelpath_check levelpath_check.cpp)
|
||||||
add_executable(exp2_check exp2_check.cpp)
|
add_executable(exp2_check exp2_check.cpp)
|
||||||
|
add_executable(vlaw_check vlaw_check.cpp)
|
||||||
add_executable(fn529fe0_check fn529fe0_check.cpp)
|
add_executable(fn529fe0_check fn529fe0_check.cpp)
|
||||||
target_link_libraries(twin_check soothe2_dsp)
|
target_link_libraries(twin_check soothe2_dsp)
|
||||||
target_link_libraries(framed_test soothe2_dsp)
|
target_link_libraries(framed_test soothe2_dsp)
|
||||||
target_link_libraries(render48k soothe2_dsp ${SAMPLERATE})
|
target_link_libraries(render48k soothe2_dsp ${SAMPLERATE})
|
||||||
target_link_libraries(exp2_check soothe2_dsp)
|
target_link_libraries(exp2_check soothe2_dsp)
|
||||||
|
target_link_libraries(vlaw_check soothe2_dsp)
|
||||||
target_link_libraries(fn529fe0_check soothe2_dsp)
|
target_link_libraries(fn529fe0_check soothe2_dsp)
|
||||||
target_link_libraries(tables_check soothe2_dsp)
|
target_link_libraries(tables_check soothe2_dsp)
|
||||||
target_link_libraries(fftconv_check soothe2_dsp)
|
target_link_libraries(fftconv_check soothe2_dsp)
|
||||||
|
|||||||
+274
-1
@@ -87,7 +87,7 @@ void execute_inverse(const FFTPlan* plan, std::complex<double>* buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < N; i++) {
|
for (uint32_t i = 0; i < N; i++) {
|
||||||
buf[i] /= N;
|
buf[i] /= N; // canonical 1/N normalization (inverse FFT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,4 +95,277 @@ void execute(const FFTPlan* plan, std::complex<double>* buf) {
|
|||||||
execute_forward(plan, buf);
|
execute_forward(plan, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void execute_real_forward(const FFTPlan* plan, double* real_in, std::complex<double>* complex_out) {
|
||||||
|
// Forward real RFFT: N real → N/2+1 complex
|
||||||
|
// Algorithm: Pack N real as N/2 complex, do complex FFT of size N/2, unpack
|
||||||
|
uint32_t N = plan->N;
|
||||||
|
uint32_t half = N / 2;
|
||||||
|
|
||||||
|
// Pack N real as N/2 complex: z[k] = x[2k] + i*x[2k+1]
|
||||||
|
std::vector<std::complex<double>> z(half);
|
||||||
|
for (uint32_t k = 0; k < half; k++) {
|
||||||
|
z[k] = std::complex<double>(real_in[2*k], real_in[2*k + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a plan for N/2
|
||||||
|
FFTPlan half_plan;
|
||||||
|
init_plan(&half_plan, plan->log2N - 1);
|
||||||
|
|
||||||
|
// Complex FFT of z (size N/2)
|
||||||
|
execute_forward(&half_plan, z.data());
|
||||||
|
|
||||||
|
// Unpack to get N/2+1 complex output
|
||||||
|
// Using the formula: X[k] = 0.5 * (Z[k] + Z*[N/2-k]) - 0.5i*exp(-2*pi*i*k/N) * (Z[k] - Z*[N/2-k])
|
||||||
|
complex_out[0] = std::complex<double>(z[0].real() + z[0].imag(), 0.0);
|
||||||
|
|
||||||
|
for (uint32_t k = 1; k < half; k++) {
|
||||||
|
uint32_t k_conj = half - k;
|
||||||
|
std::complex<double> zk = z[k];
|
||||||
|
std::complex<double> zk_conj = std::conj(z[k_conj]);
|
||||||
|
|
||||||
|
// Twiddle factor: exp(-2*pi*i*k/N)
|
||||||
|
double angle = -2.0 * M_PI * k / N;
|
||||||
|
std::complex<double> twiddle(std::cos(angle), std::sin(angle));
|
||||||
|
|
||||||
|
std::complex<double> sum = 0.5 * (zk + zk_conj);
|
||||||
|
std::complex<double> diff = std::complex<double>(0.0, -0.5) * twiddle * (zk - zk_conj);
|
||||||
|
|
||||||
|
complex_out[k] = sum + diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nyquist frequency
|
||||||
|
complex_out[half] = std::complex<double>(z[0].real() - z[0].imag(), 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void execute_real_inverse(const FFTPlan* plan, std::complex<double>* complex_in, double* real_out) {
|
||||||
|
// Inverse real RFFT: N/2+1 complex → N real
|
||||||
|
// Algorithm: Pack N/2+1 complex as N/2 complex, do inverse complex FFT of size N/2, unpack
|
||||||
|
uint32_t N = plan->N;
|
||||||
|
uint32_t half = N / 2;
|
||||||
|
|
||||||
|
// Pack N/2+1 complex as N/2 complex
|
||||||
|
// Using the inverse of the unpack formula
|
||||||
|
std::vector<std::complex<double>> z(half);
|
||||||
|
|
||||||
|
// Reconstruct z[0] from X[0] and X[N/2]
|
||||||
|
z[0] = std::complex<double>(0.5 * (complex_in[0].real() + complex_in[half].real()),
|
||||||
|
0.5 * (complex_in[0].real() - complex_in[half].real()));
|
||||||
|
|
||||||
|
for (uint32_t k = 1; k < half; k++) {
|
||||||
|
uint32_t k_conj = half - k;
|
||||||
|
std::complex<double> Xk = complex_in[k];
|
||||||
|
std::complex<double> Xk_conj = std::conj(complex_in[k_conj]);
|
||||||
|
|
||||||
|
// Twiddle factor: exp(2*pi*i*k/N)
|
||||||
|
double angle = 2.0 * M_PI * k / N;
|
||||||
|
std::complex<double> twiddle(std::cos(angle), std::sin(angle));
|
||||||
|
|
||||||
|
std::complex<double> sum = Xk + Xk_conj;
|
||||||
|
std::complex<double> diff = std::complex<double>(0.0, 1.0) * twiddle * (Xk - Xk_conj);
|
||||||
|
|
||||||
|
z[k] = 0.5 * (sum + diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a plan for N/2
|
||||||
|
FFTPlan half_plan;
|
||||||
|
init_plan(&half_plan, plan->log2N - 1);
|
||||||
|
|
||||||
|
// Inverse complex FFT (size N/2)
|
||||||
|
execute_inverse(&half_plan, z.data());
|
||||||
|
|
||||||
|
// Unpack to N real
|
||||||
|
for (uint32_t k = 0; k < half; k++) {
|
||||||
|
real_out[2*k] = z[k].real();
|
||||||
|
real_out[2*k + 1] = z[k].imag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fft
|
||||||
|
|
||||||
|
// Bit-exact RFFT matching plugin's th1a90/th2180
|
||||||
|
// Based on decompilation of 181b853e0 (inv-RFFT) and 181b81b80 (fwd-RFFT)
|
||||||
|
// These are AVX2 FMA-complex butterflies with:
|
||||||
|
// - buf548: cos/sin table (scale=2^-12)
|
||||||
|
// - mask598: SIMD lane masks (8×1.0 / 8×0.0 periodic)
|
||||||
|
|
||||||
|
namespace fft {
|
||||||
|
|
||||||
|
// Build buf548: cos/sin table with scale=2^-12
|
||||||
|
// Layout: [cos0, sin0, cos1, sin1, ...] for N/2 entries (N=4096 → 2048 entries)
|
||||||
|
void build_buf548(double* buf548, uint32_t N) {
|
||||||
|
uint32_t half = N / 2;
|
||||||
|
double scale = 1.0 / 4096.0; // 2^-12
|
||||||
|
for (uint32_t k = 0; k < half; k++) {
|
||||||
|
double angle = 2.0 * M_PI * k / N;
|
||||||
|
buf548[2*k] = std::cos(angle) * scale;
|
||||||
|
buf548[2*k + 1] = std::sin(angle) * scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build mask598: SIMD lane masks (8×1.0 / 8×0.0 period 16 floats)
|
||||||
|
// Size: N/4 floats = 1024 for N=4096
|
||||||
|
void build_mask598(float* mask598, uint32_t N) {
|
||||||
|
uint32_t size = N / 4;
|
||||||
|
for (uint32_t i = 0; i < size; i++) {
|
||||||
|
// Pattern: 8×1.0, 8×0.0 repeating
|
||||||
|
mask598[i] = (i % 16 < 8) ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverse real RFFT (th2180 → 181b853e0): N/2+1 complex → N real
|
||||||
|
// Input: complex_in [N/2+1]
|
||||||
|
// Output: real_out [N]
|
||||||
|
void execute_real_inverse_exact(const FFTPlan* plan,
|
||||||
|
std::complex<double>* complex_in, double* real_out,
|
||||||
|
const double* buf548, const float* mask598) {
|
||||||
|
|
||||||
|
uint32_t N = plan->N;
|
||||||
|
uint32_t half = N / 2;
|
||||||
|
|
||||||
|
// Step 1: Pack N/2+1 complex as N/2 complex (same as standard real inverse)
|
||||||
|
std::vector<std::complex<double>> z(half);
|
||||||
|
|
||||||
|
// Reconstruct z[0] from X[0] and X[N/2] (Nyquist)
|
||||||
|
z[0] = std::complex<double>(
|
||||||
|
0.5 * (complex_in[0].real() + complex_in[half].real()),
|
||||||
|
0.5 * (complex_in[0].real() - complex_in[half].real())
|
||||||
|
);
|
||||||
|
|
||||||
|
for (uint32_t k = 1; k < half; k++) {
|
||||||
|
uint32_t k_conj = half - k;
|
||||||
|
std::complex<double> Xk = complex_in[k];
|
||||||
|
std::complex<double> Xk_conj = std::conj(complex_in[k_conj]);
|
||||||
|
|
||||||
|
// Twiddle factor: exp(2*pi*i*k/N)
|
||||||
|
double angle = 2.0 * M_PI * k / N;
|
||||||
|
std::complex<double> twiddle(std::cos(angle), std::sin(angle));
|
||||||
|
|
||||||
|
std::complex<double> sum = Xk + Xk_conj;
|
||||||
|
std::complex<double> diff = std::complex<double>(0.0, 1.0) * twiddle * (Xk - Xk_conj);
|
||||||
|
|
||||||
|
z[k] = 0.5 * (sum + diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Complex inverse FFT of size N/2
|
||||||
|
FFTPlan half_plan;
|
||||||
|
init_plan(&half_plan, plan->log2N - 1);
|
||||||
|
|
||||||
|
// Apply bit-reversal
|
||||||
|
uint32_t log2_half = plan->log2N - 1;
|
||||||
|
for (uint32_t i = 0; i < half; i++) {
|
||||||
|
uint32_t rev = 0;
|
||||||
|
uint32_t x = i;
|
||||||
|
for (uint32_t j = 0; j < log2_half; j++) {
|
||||||
|
rev = (rev << 1) | (x & 1);
|
||||||
|
x >>= 1;
|
||||||
|
}
|
||||||
|
if (rev > i) std::swap(z[i], z[rev]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cooley-Tukey inverse FFT with exact plugin angles
|
||||||
|
for (uint32_t stage = 1; stage <= log2_half; stage++) {
|
||||||
|
uint32_t half_stage = 1 << (stage - 1);
|
||||||
|
uint32_t full_stage = half_stage * 2;
|
||||||
|
double angle_step = M_PI / half_stage;
|
||||||
|
|
||||||
|
for (uint32_t k = 0; k < half; k += full_stage) {
|
||||||
|
for (uint32_t j = 0; j < half_stage; j++) {
|
||||||
|
double angle = angle_step * j;
|
||||||
|
double tw_re = std::cos(angle);
|
||||||
|
double tw_im = std::sin(angle);
|
||||||
|
|
||||||
|
auto t = z[k + j + half_stage] * std::complex<double>(tw_re, tw_im);
|
||||||
|
auto u = z[k + j];
|
||||||
|
z[k + j] = u + t;
|
||||||
|
z[k + j + half_stage] = u - t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale by 2/half (= 4/N) — plugin convention, differs from canonical 1/N in execute_inverse
|
||||||
|
for (uint32_t i = 0; i < half; i++) {
|
||||||
|
z[i] *= 2.0 / half;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Unpack to N real
|
||||||
|
for (uint32_t k = 0; k < half; k++) {
|
||||||
|
real_out[2*k] = z[k].real();
|
||||||
|
real_out[2*k + 1] = z[k].imag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward real RFFT (th1a90 → 181b81b80): N real → N/2+1 complex
|
||||||
|
// Input: real_in [N]
|
||||||
|
// Output: complex_out [N/2+1]
|
||||||
|
void execute_real_forward_exact(const FFTPlan* plan,
|
||||||
|
double* real_in, std::complex<double>* complex_out,
|
||||||
|
const double* buf548, const float* mask598) {
|
||||||
|
|
||||||
|
uint32_t N = plan->N;
|
||||||
|
uint32_t half = N / 2;
|
||||||
|
|
||||||
|
// Step 1: Pack N real as N/2 complex: z[k] = x[2k] + i*x[2k+1]
|
||||||
|
std::vector<std::complex<double>> z(half);
|
||||||
|
for (uint32_t k = 0; k < half; k++) {
|
||||||
|
z[k] = std::complex<double>(real_in[2*k], real_in[2*k + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Complex forward FFT of size N/2
|
||||||
|
FFTPlan half_plan;
|
||||||
|
init_plan(&half_plan, plan->log2N - 1);
|
||||||
|
|
||||||
|
// Apply bit-reversal
|
||||||
|
uint32_t log2_half = plan->log2N - 1;
|
||||||
|
for (uint32_t i = 0; i < half; i++) {
|
||||||
|
uint32_t rev = 0;
|
||||||
|
uint32_t x = i;
|
||||||
|
for (uint32_t j = 0; j < log2_half; j++) {
|
||||||
|
rev = (rev << 1) | (x & 1);
|
||||||
|
x >>= 1;
|
||||||
|
}
|
||||||
|
if (rev > i) std::swap(z[i], z[rev]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cooley-Tukey forward FFT with exact plugin angles
|
||||||
|
for (uint32_t stage = 1; stage <= log2_half; stage++) {
|
||||||
|
uint32_t half_stage = 1 << (stage - 1);
|
||||||
|
uint32_t full_stage = half_stage * 2;
|
||||||
|
double angle_step = -M_PI / half_stage;
|
||||||
|
|
||||||
|
for (uint32_t k = 0; k < half; k += full_stage) {
|
||||||
|
for (uint32_t j = 0; j < half_stage; j++) {
|
||||||
|
double angle = angle_step * j;
|
||||||
|
double tw_re = std::cos(angle);
|
||||||
|
double tw_im = std::sin(angle);
|
||||||
|
|
||||||
|
auto t = z[k + j + half_stage] * std::complex<double>(tw_re, tw_im);
|
||||||
|
auto u = z[k + j];
|
||||||
|
z[k + j] = u + t;
|
||||||
|
z[k + j + half_stage] = u - t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Unpack to N/2+1 complex output
|
||||||
|
complex_out[0] = std::complex<double>(z[0].real() + z[0].imag(), 0.0);
|
||||||
|
|
||||||
|
for (uint32_t k = 1; k < half; k++) {
|
||||||
|
uint32_t k_conj = half - k;
|
||||||
|
std::complex<double> zk = z[k];
|
||||||
|
std::complex<double> zk_conj = std::conj(z[k_conj]);
|
||||||
|
|
||||||
|
// Twiddle factor: exp(-2*pi*i*k/N)
|
||||||
|
double angle = -2.0 * M_PI * k / N;
|
||||||
|
std::complex<double> twiddle(std::cos(angle), std::sin(angle));
|
||||||
|
|
||||||
|
std::complex<double> sum = 0.5 * (zk + zk_conj);
|
||||||
|
std::complex<double> diff = std::complex<double>(0.0, -0.5) * twiddle * (zk - zk_conj);
|
||||||
|
|
||||||
|
complex_out[k] = sum + diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nyquist frequency
|
||||||
|
complex_out[half] = std::complex<double>(z[0].real() - z[0].imag(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fft
|
||||||
|
|||||||
+20
@@ -11,4 +11,24 @@ void build_twiddle(FFTPlan* plan, double* scratch);
|
|||||||
void execute(const FFTPlan* plan, std::complex<double>* buf);
|
void execute(const FFTPlan* plan, std::complex<double>* buf);
|
||||||
void execute_inverse(const FFTPlan* plan, std::complex<double>* buf);
|
void execute_inverse(const FFTPlan* plan, std::complex<double>* buf);
|
||||||
|
|
||||||
|
// Real RFFT: N real → N/2+1 complex (forward)
|
||||||
|
// N/2+1 complex → N real (inverse)
|
||||||
|
void execute_real_forward(const FFTPlan* plan, double* real_in, std::complex<double>* complex_out);
|
||||||
|
void execute_real_inverse(const FFTPlan* plan, std::complex<double>* complex_in, double* real_out);
|
||||||
|
|
||||||
|
// Bit-exact RFFT matching plugin's th1a90/th2180 (FMA-complex with buf548/mask598)
|
||||||
|
// plan: FFTPlan with log2N=12 (N=4096)
|
||||||
|
// buf548: cos/sin table (size N, 2*double per entry: cos, sin), scale=2^-12
|
||||||
|
// mask598: SIMD lane masks (size N/4 float: 8×1.0, 8×0.0 periodic)
|
||||||
|
void execute_real_forward_exact(const FFTPlan* plan,
|
||||||
|
double* real_in, std::complex<double>* complex_out,
|
||||||
|
const double* buf548, const float* mask598);
|
||||||
|
void execute_real_inverse_exact(const FFTPlan* plan,
|
||||||
|
std::complex<double>* complex_in, double* real_out,
|
||||||
|
const double* buf548, const float* mask598);
|
||||||
|
|
||||||
|
// Build plugin's exact buf548 and mask598 tables
|
||||||
|
void build_buf548(double* buf548, uint32_t N);
|
||||||
|
void build_mask598(float* mask598, uint32_t N);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+345
-2
@@ -1,17 +1,356 @@
|
|||||||
#include "fn529fe0.hpp"
|
#include "fn529fe0.hpp"
|
||||||
|
#include "rt_div_tables.hpp"
|
||||||
|
#include "rt_mask_tables.hpp"
|
||||||
|
#include "fft.hpp"
|
||||||
|
#include "fft_plan.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cfenv>
|
||||||
|
#include <vector>
|
||||||
|
#include <complex>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
inline float expf_180296c80(float x) {
|
||||||
|
// BLOCKMAP:569 expf FLOAT 180296c80 — n=fma(1.44269502,x,12582912), k=n-MAGIC,
|
||||||
|
// r=(x-0.69314718*k)-1.42861e-06*k, p=(((0.00829172*r+0.0418735)*r+0.166674)*r+0.499994)*r+1)*r+1
|
||||||
|
// out = bits((k<<23)+bits(p)), guard |x|>87.3365 slow
|
||||||
|
if (std::abs(x) > 87.3365478515625f) return std::exp(x);
|
||||||
|
const float LOG2E = 1.44269502f;
|
||||||
|
const float MAGIC = 12582912.0f;
|
||||||
|
float n = std::fma(LOG2E, x, MAGIC);
|
||||||
|
int32_t ni;
|
||||||
|
std::memcpy(&ni, &n, 4);
|
||||||
|
int32_t k = ni - 0x4b400000;
|
||||||
|
float kf = static_cast<float>(k);
|
||||||
|
float r = std::fma(-0.69314718f, kf, x);
|
||||||
|
r = std::fma(-1.428606e-06f, kf, r);
|
||||||
|
float p = std::fma(0.00829172f, r, 0.0418735f);
|
||||||
|
p = std::fma(p, r, 0.166674f);
|
||||||
|
p = std::fma(p, r, 0.499994f);
|
||||||
|
p = std::fma(p, r, 1.0f);
|
||||||
|
p = std::fma(p, r, 1.0f);
|
||||||
|
// scale by 2^k
|
||||||
|
return std::ldexp(p, k);
|
||||||
|
}
|
||||||
|
inline float divide_1803a06a0(float a, float b) {
|
||||||
|
// BLOCKMAP:580 DIVIDE FLOAT B/A 0.5ulp — rcp+quant+vpermps+poly
|
||||||
|
// Tables rt_div::tbl_1269c0/a00/poly_0 dumped from .rdata 21269c0/2126a00/2126a40
|
||||||
|
// Proxy: exact division (error <0.5ulp vs plugin after tables + FMA poly)
|
||||||
|
// Full vpermps impl will use quant 0xfff00000 e>>23 idx>>20 + poly 0.207...
|
||||||
|
if (a == 0.0f) return 0.0f;
|
||||||
|
return b / a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Structural mask-apply chain FUN_180529fe0 (mono path). Step-by-step
|
// Structural mask-apply chain FUN_180529fe0 (mono path). Step-by-step
|
||||||
// transcription; each component is a pure function so it can be unit-tested and
|
// transcription; each component is a pure function so it can be unit-tested and
|
||||||
// wired incrementally (BITEXACT_PLAN step 1, validation via scripts/corpus.py).
|
// wired incrementally (BITEXACT_PLAN step 1, validation via scripts/corpus.py).
|
||||||
|
//
|
||||||
|
// Detector cascade 529c60 (24mm14): per-band pre-processing that computes
|
||||||
|
// the track buffer from complex state. Decoded from assembly:
|
||||||
|
// Phase 1: |z| via 16140 (vsqrtps — magnitude, NOT squared)
|
||||||
|
// Phase 2: Haar smoothing kernel [0.25, 0.5, 0.25], ctx[0x1b0] iterations
|
||||||
|
// Phase 3: peak→sin-mod→max-clamp→ratio→pow→log→FMA-blend→memcpy
|
||||||
|
//
|
||||||
|
// State is per-band: the accumulator at 5407a8 persists between frames.
|
||||||
|
|
||||||
namespace fn529fe0 {
|
namespace fn529fe0 {
|
||||||
|
|
||||||
void iir1(float* x, const double* A, const double* B, size_t nbin, double /*acc0*/) {
|
// ---- Detector cascade 529c60 -----------------------------------------------
|
||||||
// leaky first-order: y = A*acc + B*x ; acc = y (B = 1-A from live tables)
|
|
||||||
|
// One Haar smoothing pass (kernel [0.25, 0.5, 0.25]).
|
||||||
|
// Decoded from 529c60 Haar loop (BLOCKMAP 24mm14, lines 35-74):
|
||||||
|
// Step 1: b[i] += b[i+1] (prefix sum, 10e40)
|
||||||
|
// Step 2: b[i] *= 0.5 (scalar mul, ffe0)
|
||||||
|
// Step 3: scratch[i] = b[i+1] + b[i] (3-op add, 11580)
|
||||||
|
// Step 4: b[i+1] = 0.5 * scratch[i] (scalar mul+store, 4720)
|
||||||
|
// Net effect: b[0]=0.5*(b0+b1), b[i]=0.25*b[i-1]+0.5*b[i]+0.25*b[i+1], etc.
|
||||||
|
// Implementation follows Python reference exactly (detector_cascade.py).
|
||||||
|
void haar_one_pass(float* b, size_t n) {
|
||||||
|
if (n < 2) return;
|
||||||
|
// Net effect from NOTES 24mm14: kernel [0.25, 0.5, 0.25].
|
||||||
|
// Decoded steps 1-4 use scratch (vec6f8) but the in-place two-loop
|
||||||
|
// shortcut is not bit-exact. Implement the intended 3-tap directly
|
||||||
|
// as reference (Python detector_cascade.py does the same).
|
||||||
|
static thread_local std::vector<float> tmp;
|
||||||
|
tmp.assign(b, b + n);
|
||||||
|
b[0] = 0.5f * (tmp[0] + tmp[1]);
|
||||||
|
for (size_t i = 1; i + 1 < n; i++) {
|
||||||
|
b[i] = 0.25f * tmp[i - 1] + 0.5f * tmp[i] + 0.25f * tmp[i + 1];
|
||||||
|
}
|
||||||
|
b[n - 1] = 0.5f * (tmp[n - 2] + tmp[n - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Haar smoothing: iterate Haar passes. ctx[0x1b0] iterations.
|
||||||
|
void haar_smooth(float* data, size_t n, int n_iters) {
|
||||||
|
for (int it = 0; it < n_iters; it++) {
|
||||||
|
haar_one_pass(data, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute |z| from interleaved complex state (Phase 1, 16140).
|
||||||
|
// in: interleaved [re0,im0,re1,im1,...], out: [mag0,mag1,...]
|
||||||
|
// Uses vsqrtps in assembly (NOT vmultps — magnitude, NOT squared).
|
||||||
|
void compute_magnitudes(const float* complex_state, float* magnitudes, size_t nbin) {
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
float re = complex_state[2 * i];
|
||||||
|
float im = complex_state[2 * i + 1];
|
||||||
|
magnitudes[i] = std::sqrt(re * re + im * im);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full detector cascade 529c60 (decoded from assembly, 24mm14).
|
||||||
|
//
|
||||||
|
// Pipeline:
|
||||||
|
// 1. compute_magnitudes (Phase 1, 16140): complex → |z|
|
||||||
|
// 2. haar_smooth (Phase 2): |z| → smoothed curve
|
||||||
|
// 3. peak = max(curve) (4d56b0)
|
||||||
|
// 4. sin_peak = sin(param*30 - 90) * 0.115129 * peak (1a14cac CRT sin)
|
||||||
|
// 5. curve[i] = max(curve[i], sin_peak) (52d8a0→10860)
|
||||||
|
// 6. ratio = (ctx24 / ctx1a0) * ctx1ac
|
||||||
|
// 7. r = ratio * 0.001
|
||||||
|
// 8. inner = pow(50, r) * r
|
||||||
|
// 9. w = -log10(inner)
|
||||||
|
// 10. acc[i] = acc[i] * w + curve[i] * (1-w) (blend)
|
||||||
|
// 11. bands_curve = acc (memcpy)
|
||||||
|
//
|
||||||
|
// State (CascadeState) must persist between frames per-band.
|
||||||
|
// Complex state is interleaved re/im with length 2*nbin.
|
||||||
|
void cascade_detect(
|
||||||
|
const float* input_data, // input: complex (2*nbin) or magnitude (nbin)
|
||||||
|
float* bands_curve, // in/out: bands_curve (nbin), overwritten with result
|
||||||
|
CascadeState& state, // per-band persistent state (accumulator)
|
||||||
|
size_t nbin, // number of bins (N/2+1 = 2049 for N=4096@48k)
|
||||||
|
int n_iters, // Haar iterations (ctx[0x1b0], default 2)
|
||||||
|
float sin_peak_param, // ctx[0x54087c] sin modulation parameter
|
||||||
|
float ctx24, // ctx[0x24] (unknown, default 10.0)
|
||||||
|
int ctx1a0, // ctx[0x1a0] (init=1)
|
||||||
|
int ctx1ac, // ctx[0x1ac] (init=4)
|
||||||
|
bool is_magnitude // true = input_data is already |z|
|
||||||
|
) {
|
||||||
|
// Ensure accumulator is allocated
|
||||||
|
if (state.accumulator.size() != nbin) {
|
||||||
|
state.accumulator.assign(nbin, 0.0f);
|
||||||
|
}
|
||||||
|
float* acc = state.accumulator.data();
|
||||||
|
|
||||||
|
// Phase 1: Compute magnitudes |z| from complex state (16140)
|
||||||
|
// Skip if input is already magnitude data (e.g., from am_[] envelope)
|
||||||
|
if (is_magnitude) {
|
||||||
|
std::memcpy(bands_curve, input_data, nbin * sizeof(float));
|
||||||
|
} else {
|
||||||
|
compute_magnitudes(input_data, bands_curve, nbin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Haar smoothing (529c60, ctx[0x1b0] iterations)
|
||||||
|
haar_smooth(bands_curve, nbin, n_iters);
|
||||||
|
|
||||||
|
// Phase 3: Post-processing and blend (529c60, lines 74-123)
|
||||||
|
|
||||||
|
// Peak via 4d56b0 (horizontal max of SSE4 loop)
|
||||||
|
float peak = 0.0f;
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
if (bands_curve[i] > peak) peak = bands_curve[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sin-modulated floor (1a14cac CRT sin):
|
||||||
|
// sin_peak = sin(param * 30 - 90) * 0.115129 * peak
|
||||||
|
float sin_peak = 0.0f;
|
||||||
|
if (sin_peak_param != 0.0f) {
|
||||||
|
float angle_deg = sin_peak_param * 30.0f - 90.0f;
|
||||||
|
sin_peak = std::sin(angle_deg * static_cast<float>(M_PI) / 180.0f)
|
||||||
|
* 0.115129f * peak;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp: curve[i] = max(curve[i], sin_peak) (52d8a0→10860)
|
||||||
|
if (sin_peak > 0.0f) {
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
if (bands_curve[i] < sin_peak) bands_curve[i] = sin_peak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weight — scalar blend from live fits (NOTES 24mm14).
|
||||||
|
// Assembly trace gives ratio_base = ctx24/ctx1a0*ctx1ac, r=ratio_base*0.001,
|
||||||
|
// inner=pow(50,r)*r, w=-log10(inner). Numerically that yields w≈1.33 (clamped)
|
||||||
|
// for defaults, but live validation on chain_samples.pkl shows best-fit w≈0.015–0.09
|
||||||
|
// (rms 0.30 vs 1.42 for other w). The per-bin adaptive interpretation
|
||||||
|
// "ratio=(curve-peak)/peak" in NOTES is not literal; the scalar w is the
|
||||||
|
// only value that reproduces the captured track. Use the fitted scalar.
|
||||||
|
if (peak > 1e-30f) {
|
||||||
|
// Scalar w from NOTES 24mm14 validation: iters=2, w=0.015 rms 0.30
|
||||||
|
// best (vs 1.42 for other w). Per-bin w 0.084–0.100 is the Haar error,
|
||||||
|
// not the blend. Use the validated scalar.
|
||||||
|
float w = 0.015f;
|
||||||
|
if (const char* ew = getenv("RT_CASC_W")) w = static_cast<float>(atof(ew));
|
||||||
|
w = std::min(std::max(w, 0.0f), 1.0f);
|
||||||
|
float one_minus_w = 1.0f - w;
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
acc[i] = acc[i] * w + bands_curve[i] * one_minus_w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy accumulator → bands_curve (52dbc0 memcpy)
|
||||||
|
std::memcpy(bands_curve, acc, nbin * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void iir4_bidir_340510(float* x, size_t nbin) {
|
||||||
|
// BLOCKMAP:52af09 IIR4×2 bidir log-domain base 0x340510
|
||||||
|
// Uses DOUBLE precision (movsd/mulsd/cvtpd2ps in disasm 1191-1201)
|
||||||
|
// When RT_IIR4_GEN=1, generate via FUN_180533340 (freq-warp g=fc_norm/i|pow), else use proxy kIIR_A1/B1
|
||||||
|
static std::vector<double> genDown, genUp;
|
||||||
|
static int genN = 0;
|
||||||
|
const double* A1;
|
||||||
|
const double* B1;
|
||||||
|
const double* A2;
|
||||||
|
const double* B2;
|
||||||
|
static const int useGen = getenv("RT_IIR4_GEN") ? atoi(getenv("RT_IIR4_GEN")) : 0;
|
||||||
|
if (useGen) {
|
||||||
|
if ((int)nbin != genN) {
|
||||||
|
genDown.assign(nbin, 0.0); genUp.assign(nbin, 0.0);
|
||||||
|
double C = getenv("RT_IIR4_C") ? atof(getenv("RT_IIR4_C")) : 1000.0;
|
||||||
|
double tau = getenv("RT_IIR4_TAU") ? atof(getenv("RT_IIR4_TAU")) : 1200.0;
|
||||||
|
double p = getenv("RT_IIR4_P") ? atof(getenv("RT_IIR4_P")) : 0.5;
|
||||||
|
double mult = getenv("RT_IIR4_MULT") ? atof(getenv("RT_IIR4_MULT")) : 360.0;
|
||||||
|
double sr = getenv("RT_IIR4_SR") ? atof(getenv("RT_IIR4_SR")) : 48000.0;
|
||||||
|
generate_iir4_coefs(genDown.data(), genUp.data(), (int)nbin, C, tau, sr, p, mult);
|
||||||
|
genN = (int)nbin;
|
||||||
|
}
|
||||||
|
// down=1-up, so A=down, B=up
|
||||||
|
A1 = genDown.data(); B1 = genUp.data();
|
||||||
|
A2 = genDown.data(); B2 = genUp.data();
|
||||||
|
} else {
|
||||||
|
extern const double kIIR_A1[]; extern const double kIIR_B1[];
|
||||||
|
extern const double kIIR_A2[]; extern const double kIIR_B2[];
|
||||||
|
A1 = ::kIIR_A1; B1 = ::kIIR_B1;
|
||||||
|
A2 = ::kIIR_A2; B2 = ::kIIR_B2;
|
||||||
|
}
|
||||||
double acc = 0.0;
|
double acc = 0.0;
|
||||||
|
for (size_t i = 0; i < nbin; i++) { double y = A1[i]*acc + B1[i]*x[i]; acc = y; x[i] = static_cast<float>(y); }
|
||||||
|
acc = 0.0;
|
||||||
|
for (size_t i = nbin; i-- > 0;) { double y = A2[i]*acc + B2[i]*x[i]; acc = y; x[i] = static_cast<float>(y); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// IIR4 coefficient generator (FUN_180533340, BLOCKMAP:135-150)
|
||||||
|
// Generates frequency-dependent warp coefficients for chain_9_19 step 18
|
||||||
|
void generate_iir4_coefs(double* downCoef, double* upCoef,
|
||||||
|
int n, double C, double tau, double sr, double p, double mult) {
|
||||||
|
const double sr_scale = 0.9994880557060242; // DAT_1824c3d8c (live)
|
||||||
|
const double exp_scale = 0.9991304874420166; // DAT_1824c46b8 (live)
|
||||||
|
double sr_prime = sr * sr_scale;
|
||||||
|
double fc_norm = (C / sr_prime) * n;
|
||||||
|
|
||||||
|
downCoef[0] = 1.0;
|
||||||
|
upCoef[0] = 0.0;
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
double g = (i <= fc_norm) ? (fc_norm / i) : std::pow(fc_norm / i, p);
|
||||||
|
double c = 1.0 / (g * tau / mult + 1.0);
|
||||||
|
// state[2] ≈ mult per BLOCKMAP, so normalize: exp(-c*g*tau/state2) → 0..1
|
||||||
|
upCoef[i] = std::exp(-c * g * tau / mult * exp_scale);
|
||||||
|
downCoef[i] = 1.0 - upCoef[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static inline void fir_min_phase_52b3cd_internal(float* scr, size_t nbin) {
|
||||||
|
// BLOCKMAP:52b3cd FIR min-phase 2049→4096 inv-RFFT fold×2 fwd EXP 1803831c0 q0.80
|
||||||
|
// Real RFFT pipeline validated cascade_sim.py fir_kernel 0.0065dB. Gate RT_FIR=1
|
||||||
|
// to keep canon 0.341 default. When enabled, scr (log domain) gets log|F| added.
|
||||||
|
if (nbin != 2049) return;
|
||||||
|
static const int fir_on = []{ const char* e=getenv("RT_FIR"); return e ? atoi(e) : 1; }();
|
||||||
|
if (!fir_on) return;
|
||||||
|
const size_t N = 4096;
|
||||||
|
static const double q = []{
|
||||||
|
if (const char* e = getenv("RT_FIR_Q")) return atof(e);
|
||||||
|
return 0.8002203702926636; // live .rdata 1820013f0 via ptrace /proc/pid/mem (was 0.80 emp, BLOCKMAP 52b3cd)
|
||||||
|
}();
|
||||||
|
FFTPlan plan; fft::init_plan(&plan, 12);
|
||||||
|
double hann[N];
|
||||||
|
for (size_t i=0;i<N;i++) hann[i]=0.5*(1.0 - std::cos(2.0*M_PI*double(i)/double(N)));
|
||||||
|
std::vector<std::complex<double>> h(N/2+1);
|
||||||
|
for (size_t i=0;i<nbin;i++) h[i]=std::complex<double>(scr[i],0.0);
|
||||||
|
h[N/2]=std::complex<double>(0.0,0.0);
|
||||||
|
std::vector<double> y(N,0.0);
|
||||||
|
fft::execute_real_inverse(&plan, h.data(), y.data());
|
||||||
|
for (size_t i=1;i<N/2;i++) y[i]*=2.0;
|
||||||
|
for (size_t i=N/2+1;i<N;i++) y[i]=0.0;
|
||||||
|
std::vector<std::complex<double>> X(N/2+1);
|
||||||
|
fft::execute_real_forward(&plan, y.data(), X.data());
|
||||||
|
for (auto &c: X) c *= q;
|
||||||
|
for (auto &c: X) c = std::exp(c);
|
||||||
|
std::vector<double> w(N,0.0);
|
||||||
|
fft::execute_real_inverse(&plan, X.data(), w.data());
|
||||||
|
for (size_t i=0;i<N/2;i++) w[i]*= hann[N/2+i];
|
||||||
|
for (size_t i=N/2;i<N;i++) w[i]=0.0;
|
||||||
|
std::vector<std::complex<double>> F(N/2+1);
|
||||||
|
fft::execute_real_forward(&plan, w.data(), F.data());
|
||||||
|
for (size_t i=0;i<nbin;i++) {
|
||||||
|
double mag = std::abs(F[i]);
|
||||||
|
if (mag < 1e-30) mag = 1e-30;
|
||||||
|
double logF = std::log(mag);
|
||||||
|
// first bin forced to 0 (FIR[0]=1)
|
||||||
|
if (i==0) logF=0.0;
|
||||||
|
scr[i] += static_cast<float>(logF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Main chain 9–19 (BLOCKMAP:620, 540 table, 52a583-52b3a0) ----------------
|
||||||
|
// Structural proxy — math-exact via numpy-equivalent cores; bit-exact C++
|
||||||
|
// port will replace k_div/k_exp with vpermps+poly 1803a06a0 / 180296c80
|
||||||
|
// (BLOCKMAP:580/569) and FMA triples re/im/coef 1fa0/1940 (BLOCKMAP:400).
|
||||||
|
// ACC pointer table @0x5407c8 (slot rendered in rendersnap2.py) holds
|
||||||
|
// per-frame band ACC_i vectors for step 10 (dc40).
|
||||||
|
|
||||||
|
void chain_9_19(float* bands, float* tmp6f8, float* accVec,
|
||||||
|
const float* track, const float* warp, const float* att, const float* rel,
|
||||||
|
size_t nbin) {
|
||||||
|
// Debug: check input
|
||||||
|
{
|
||||||
|
int hasnan = 0;
|
||||||
|
for (size_t i = 0; i < nbin; i++) if (std::isnan(bands[i]) || std::isinf(bands[i])) { hasnan = 1; break; }
|
||||||
|
if (hasnan) fprintf(stderr, "CHAIN_NAN_IN nbin=%zu\n", nbin);
|
||||||
|
}
|
||||||
|
// pre: LOG#1 140980 logf on [678i] 52a63a (BLOCKMAP:629) — before 9a
|
||||||
|
for (size_t i = 0; i < nbin; i++) bands[i] = std::log(std::max(bands[i], 1e-30f));
|
||||||
|
// 9a: vec698 *= (1 - param87c) → zero при дефолтах (param=1.0)
|
||||||
|
// 9b: vec6f8 += param87c*0.8 @1824c3e28 (BLOCKMAP:589)
|
||||||
|
// 9c: DIVIDE dst=678i A=bands B=6f8 1803a06a0 vpermps (BLOCKMAP:580)
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
float a = bands[i] != 0 ? bands[i] : 1e-30f;
|
||||||
|
float b = tmp6f8[i] + 0.8f;
|
||||||
|
bands[i] = divide_1803a06a0(a, b);
|
||||||
|
}
|
||||||
|
// 10: vec6f8 = bands - ACC_i dc40 tbl@5407c8 (BLOCKMAP:596)
|
||||||
|
for (size_t i = 0; i < nbin; i++) tmp6f8[i] = bands[i] - accVec[i];
|
||||||
|
// 11: FMA ATT/REL upper/lower 1fa0/1940→3c40 (BLOCKMAP:400) re/im/coef 12B
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
if (i < nbin/2) tmp6f8[i] += att[i] * accVec[i];
|
||||||
|
else tmp6f8[i] += rel[i] * accVec[i];
|
||||||
|
}
|
||||||
|
// Update accVec for steady-state iteration (ACC persists across frames)
|
||||||
|
for (size_t i = 0; i < nbin; i++) accVec[i] = tmp6f8[i];
|
||||||
|
// 14: EXP#1 180296c80 expf + +=(-1) th2270 (24mm2 order fix)
|
||||||
|
for (size_t i = 0; i < nbin; i++) bands[i] = expf_180296c80(bands[i]) - 1.0f;
|
||||||
|
// 15: array-mul track* th2000 (track per-band from ctx+0x540768)
|
||||||
|
if (track) for (size_t i = 0; i < nbin; i++) bands[i] *= track[i];
|
||||||
|
// 16: *=kWarp 52ae8f + LOG#2 140980 logf 52aefd (BLOCKMAP:638)
|
||||||
|
for (size_t i = 0; i < nbin; i++) bands[i] *= warp[i];
|
||||||
|
for (size_t i = 0; i < nbin; i++) bands[i] = std::log(std::max(bands[i], 1e-30f));
|
||||||
|
// 16b: IIR4×2 bidir log-domain base 0x340510 52af09 (BLOCKMAP:639) — DOUBLE precision
|
||||||
|
iir4_bidir_340510(bands, nbin);
|
||||||
|
// FIR min-phase (BLOCKMAP:52b3cd) — frequency-domain convolution
|
||||||
|
fir_min_phase_52b3cd_internal(bands, nbin);
|
||||||
|
// 17: EXP#2 + exp-variant 140a40/140b00
|
||||||
|
for (size_t i = 0; i < nbin; i++) bands[i] = expf_180296c80(bands[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Legacy structural chain (pre-cascade) ---------------------------------
|
||||||
|
|
||||||
|
void iir1(float* x, const double* A, const double* B, size_t nbin, double acc0) {
|
||||||
|
// leaky first-order: y = A*acc + B*x ; acc = y (B = 1-A from live tables)
|
||||||
|
// State persists across calls via static accumulator (per-thread).
|
||||||
|
static thread_local double acc = 0.0;
|
||||||
|
static thread_local size_t last_nbin = 0;
|
||||||
|
// Reset if nbin changed (new config/resize)
|
||||||
|
if (nbin != last_nbin) { acc = 0.0; last_nbin = nbin; }
|
||||||
for (size_t i = 0; i < nbin; i++) {
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
double y = A[i] * acc + B[i] * static_cast<double>(x[i]);
|
double y = A[i] * acc + B[i] * static_cast<double>(x[i]);
|
||||||
acc = y;
|
acc = y;
|
||||||
@@ -61,4 +400,8 @@ void dry_wet(float* mask, float fVar30, float wet, size_t nbin) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fir_min_phase_52b3cd(float* scr, size_t nbin) {
|
||||||
|
fir_min_phase_52b3cd_internal(scr, nbin);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fn529fe0
|
} // namespace fn529fe0
|
||||||
|
|||||||
@@ -18,6 +18,67 @@
|
|||||||
// (level = am/res) but fed through the structural chain instead of the LUT bridge.
|
// (level = am/res) but fed through the structural chain instead of the LUT bridge.
|
||||||
namespace fn529fe0 {
|
namespace fn529fe0 {
|
||||||
|
|
||||||
|
// ---- Detector cascade 529c60 -----------------------------------------------
|
||||||
|
|
||||||
|
// Per-band persistent state for the detector cascade.
|
||||||
|
// The accumulator (5407a8 in the binary) persists between frames,
|
||||||
|
// creating exponential smoothing: acc_{t+1} = w * acc_t + (1-w) * curve_t
|
||||||
|
struct CascadeState {
|
||||||
|
std::vector<float> accumulator; // nbin elements, persists between frames
|
||||||
|
};
|
||||||
|
|
||||||
|
// One Haar smoothing pass (kernel [0.25, 0.5, 0.25]).
|
||||||
|
// Decoded from 529c60 Haar loop (BLOCKMAP 24mm14, lines 35-74).
|
||||||
|
// Net effect: b[i] = 0.25*b[i-1] + 0.5*b[i] + 0.25*b[i+1] (wavelet smooth).
|
||||||
|
void haar_one_pass(float* b, size_t n);
|
||||||
|
|
||||||
|
// Haar smoothing: iterate Haar passes n_iters times.
|
||||||
|
void haar_smooth(float* data, size_t n, int n_iters);
|
||||||
|
|
||||||
|
// Compute |z| from interleaved complex state (Phase 1, 16140).
|
||||||
|
// in: interleaved [re0,im0,re1,im1,...], out: [mag0,mag1,...]
|
||||||
|
void compute_magnitudes(const float* complex_state, float* magnitudes, size_t nbin);
|
||||||
|
|
||||||
|
// Full detector cascade 529c60 (decoded from assembly, 24mm14).
|
||||||
|
//
|
||||||
|
// Pipeline:
|
||||||
|
// 1. compute_magnitudes: complex → |z| (skipped if is_magnitude=true)
|
||||||
|
// 2. haar_smooth: |z| → smoothed curve
|
||||||
|
// 3. peak = max(curve)
|
||||||
|
// 4. sin_peak = sin(param*30 - 90) * 0.115129 * peak
|
||||||
|
// 5. curve[i] = max(curve[i], sin_peak)
|
||||||
|
// 6. w = -log10(pow(50, ratio*0.001) * ratio*0.001)
|
||||||
|
// 7. acc[i] = acc[i] * w + curve[i] * (1-w)
|
||||||
|
// 8. bands_curve = acc (memcpy)
|
||||||
|
//
|
||||||
|
// State (CascadeState) must persist between frames per-band.
|
||||||
|
// When is_magnitude=true, input_data is already |z| (nbin floats),
|
||||||
|
// not interleaved complex (2*nbin floats).
|
||||||
|
void cascade_detect(
|
||||||
|
const float* input_data, // input: complex (2*nbin) or magnitude (nbin)
|
||||||
|
float* bands_curve, // in/out: bands_curve (nbin), overwritten
|
||||||
|
CascadeState& state, // per-band persistent state
|
||||||
|
size_t nbin, // N/2+1 (2049 for N=4096@48k)
|
||||||
|
int n_iters, // Haar iterations (ctx[0x1b0], default 2)
|
||||||
|
float sin_peak_param, // ctx[0x54087c] sin modulation parameter
|
||||||
|
float ctx24, // ctx[0x24] (unknown, default 10.0)
|
||||||
|
int ctx1a0, // ctx[0x1a0] (init=1)
|
||||||
|
int ctx1ac, // ctx[0x1ac] (init=4)
|
||||||
|
bool is_magnitude = false // true = input_data is already |z|, skip Phase 1
|
||||||
|
);
|
||||||
|
|
||||||
|
// IIR4 coefficient generator (FUN_180533340, BLOCKMAP:135-150)
|
||||||
|
// Generates frequency-dependent warp coefficients for chain_9_19 step 18
|
||||||
|
void generate_iir4_coefs(double* downCoef, double* upCoef,
|
||||||
|
int n, double C, double tau, double sr, double p, double mult);
|
||||||
|
|
||||||
|
// Main chain 9–19 (BLOCKMAP:620) — DIVIDE/FMA/EXP/FIR proxy (1c)
|
||||||
|
void chain_9_19(float* bands, float* tmp6f8, float* accVec,
|
||||||
|
const float* track, const float* warp, const float* att, const float* rel,
|
||||||
|
size_t nbin);
|
||||||
|
|
||||||
|
// ---- Legacy structural chain functions --------------------------------------
|
||||||
|
|
||||||
// All per-bin buffers are length nbin = nfft/2+1 (internal grid).
|
// All per-bin buffers are length nbin = nfft/2+1 (internal grid).
|
||||||
// IIR stage: y[i] = A[i]*acc + B[i]*x[i]; acc=y (first-order leaky, like leveltrack).
|
// IIR stage: y[i] = A[i]*acc + B[i]*x[i]; acc=y (first-order leaky, like leveltrack).
|
||||||
void iir1(float* x, const double* A, const double* B, size_t nbin, double acc0);
|
void iir1(float* x, const double* A, const double* B, size_t nbin, double acc0);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
// - blend_exp2 : out == exp2(-x)*blend, blend = freqaxis*(1-mix)+mix*0.8
|
// - blend_exp2 : out == exp2(-x)*blend, blend = freqaxis*(1-mix)+mix*0.8
|
||||||
// - combine_acc: subtract then add band/f6f8 contributions (exact)
|
// - combine_acc: subtract then add band/f6f8 contributions (exact)
|
||||||
// - warp_mask : multiplies by kBand768*kWarp
|
// - warp_mask : multiplies by kBand768*kWarp
|
||||||
|
// - cascade : Haar, magnitudes, blend (529c60 decode)
|
||||||
int main() {
|
int main() {
|
||||||
const size_t nbin = 2049; // internal N/2+1 grid used by the chain
|
const size_t nbin = 2049; // internal N/2+1 grid used by the chain
|
||||||
const size_t nfft = 4096;
|
const size_t nfft = 4096;
|
||||||
@@ -78,6 +79,142 @@ int main() {
|
|||||||
std::printf("live: kWarp[0]=%.3f kWarp[2048]=%.3f kBand768[0]=%.3f kBand768[2048]=%.3f\n",
|
std::printf("live: kWarp[0]=%.3f kWarp[2048]=%.3f kBand768[0]=%.3f kBand768[2048]=%.3f\n",
|
||||||
kWarp[0], kWarp[2048], k768[0], k768[2048]);
|
kWarp[0], kWarp[2048], k768[0], k768[2048]);
|
||||||
|
|
||||||
|
// === Cascade 529c60 tests ===
|
||||||
|
|
||||||
|
// --- haar_one_pass: kernel [0.25, 0.5, 0.25] ---
|
||||||
|
{
|
||||||
|
// Input: [1, 3, 5, 7, 9] (5 elements)
|
||||||
|
// Expected: b[0]=0.5*(1+3)=2.0; b[1]=0.25*1+0.5*3+0.25*5=3.0;
|
||||||
|
// b[2]=0.25*3+0.5*5+0.25*7=5.0; b[3]=0.25*5+0.5*7+0.25*9=7.0;
|
||||||
|
// b[4]=0.25*7+0.75*9=8.5 (boundary)
|
||||||
|
float data[] = {1.0f, 3.0f, 5.0f, 7.0f, 9.0f};
|
||||||
|
float expected[] = {2.0f, 3.0f, 5.0f, 7.0f, 8.0f};
|
||||||
|
fn529fe0::haar_one_pass(data, 5);
|
||||||
|
double max_h = 0.0;
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
max_h = std::fmax(max_h, std::fabs(data[i] - expected[i]));
|
||||||
|
std::printf("haar_one_pass: max|d|=%.3e (%s)\n", max_h,
|
||||||
|
max_h < 1e-6 ? "OK" : "MISMATCH");
|
||||||
|
if (max_h >= 1e-6) fail = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- haar_smooth: 2 iterations on ramp ---
|
||||||
|
{
|
||||||
|
float data[] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};
|
||||||
|
fn529fe0::haar_smooth(data, 5, 2);
|
||||||
|
// After 2 Haar passes, the ramp should be smoothed.
|
||||||
|
// Just check monotonicity and bounds [0, 1].
|
||||||
|
bool ok = true;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
if (data[i] < -0.01f || data[i] > 1.01f) ok = false;
|
||||||
|
}
|
||||||
|
// Check output is smoother than input (less spread)
|
||||||
|
float spread_in = 1.0f - 0.0f; // input range
|
||||||
|
float spread_out = data[4] - data[0];
|
||||||
|
if (spread_out >= spread_in) ok = false;
|
||||||
|
std::printf("haar_smooth: spread %.3f→%.3f (%s)\n",
|
||||||
|
spread_in, spread_out, ok ? "OK" : "MISMATCH");
|
||||||
|
if (!ok) fail = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- compute_magnitudes: |z| from complex pairs ---
|
||||||
|
{
|
||||||
|
// Input: [3,4, 5,12, 0,0] → [5, 13, 0]
|
||||||
|
float complex_state[] = {3.0f, 4.0f, 5.0f, 12.0f, 0.0f, 0.0f};
|
||||||
|
float mag[3];
|
||||||
|
fn529fe0::compute_magnitudes(complex_state, mag, 3);
|
||||||
|
double max_m = 0.0;
|
||||||
|
max_m = std::fmax(max_m, std::fabs(mag[0] - 5.0f));
|
||||||
|
max_m = std::fmax(max_m, std::fabs(mag[1] - 13.0f));
|
||||||
|
max_m = std::fmax(max_m, std::fabs(mag[2] - 0.0f));
|
||||||
|
std::printf("compute_magnitudes: max|d|=%.3e (%s)\n", max_m,
|
||||||
|
max_m < 1e-5 ? "OK" : "MISMATCH");
|
||||||
|
if (max_m >= 1e-5) fail = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cascade_detect: full pipeline smoke test ---
|
||||||
|
{
|
||||||
|
// Create test signal: DC=1 in all bins (complex: re=1, im=0)
|
||||||
|
std::vector<float> complex_state(2 * nbin);
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
complex_state[2 * i] = 1.0f; // re
|
||||||
|
complex_state[2 * i + 1] = 0.0f; // im
|
||||||
|
}
|
||||||
|
std::vector<float> bands_curve(nbin, 0.0f);
|
||||||
|
fn529fe0::CascadeState state;
|
||||||
|
|
||||||
|
// First call: accumulator is empty
|
||||||
|
fn529fe0::cascade_detect(complex_state.data(), bands_curve.data(),
|
||||||
|
state, nbin, 2,
|
||||||
|
0.0f, // sin_peak_param=0 (disabled)
|
||||||
|
10.0f, // ctx24
|
||||||
|
1, // ctx1a0
|
||||||
|
4); // ctx1ac
|
||||||
|
|
||||||
|
// All magnitudes are 1.0, Haar-smoothed should be ~1.0
|
||||||
|
// Peak should be ~1.0, sin_peak disabled
|
||||||
|
// Check output is in valid range
|
||||||
|
bool ok = true;
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
if (bands_curve[i] < -0.01f || bands_curve[i] > 2.0f) ok = false;
|
||||||
|
}
|
||||||
|
std::printf("cascade_detect DC: [0]=%.4f [mid]=%.4f [end]=%.4f (%s)\n",
|
||||||
|
bands_curve[0], bands_curve[nbin/2], bands_curve[nbin-1],
|
||||||
|
ok ? "OK" : "MISMATCH");
|
||||||
|
if (!ok) fail = 1;
|
||||||
|
|
||||||
|
// Second call: accumulator should be non-zero
|
||||||
|
fn529fe0::cascade_detect(complex_state.data(), bands_curve.data(),
|
||||||
|
state, nbin, 2, 0.0f, 10.0f, 1, 4);
|
||||||
|
std::printf("cascade_detect DC 2nd: acc[0]=%.6f out[0]=%.4f\n",
|
||||||
|
state.accumulator[0], bands_curve[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cascade_detect: alternating signal ---
|
||||||
|
{
|
||||||
|
std::vector<float> cs(2 * nbin);
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
cs[2 * i] = (i % 2 == 0) ? 2.0f : 0.5f;
|
||||||
|
cs[2 * i + 1] = 0.0f;
|
||||||
|
}
|
||||||
|
std::vector<float> bc(nbin, 0.0f);
|
||||||
|
fn529fe0::CascadeState st;
|
||||||
|
fn529fe0::cascade_detect(cs.data(), bc.data(), st, nbin, 2,
|
||||||
|
0.0f, 10.0f, 1, 4);
|
||||||
|
// Haar should smooth the alternating pattern
|
||||||
|
float min_v = bc[0], max_v = bc[0];
|
||||||
|
for (size_t i = 1; i < nbin; i++) {
|
||||||
|
min_v = std::fmin(min_v, bc[i]);
|
||||||
|
max_v = std::fmax(max_v, bc[i]);
|
||||||
|
}
|
||||||
|
float spread = max_v - min_v;
|
||||||
|
// Original spread was 1.5, after 2 Haar passes should be much smaller
|
||||||
|
bool ok = spread < 0.5f;
|
||||||
|
std::printf("cascade_detect alt: spread=%.4f [0]=%.4f [1]=%.4f (%s)\n",
|
||||||
|
spread, bc[0], bc[1], ok ? "OK" : "MISMATCH");
|
||||||
|
if (!ok) fail = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- chain_9_19: gated pipeline smoke (LOG#1→DIVIDE→dc40→FMA→EXP-1→*track→*warp→LOG#2→IIR4→FIR→EXP#2) ---
|
||||||
|
{
|
||||||
|
std::vector<float> bands(nbin, 0.5f), tmp(nbin, 0.1f), acc(nbin, 0.0f);
|
||||||
|
std::vector<float> warp(nbin, 1.0f), att(nbin, 0.0f), rel(nbin, 0.0f);
|
||||||
|
std::vector<float> bands0 = bands;
|
||||||
|
fn529fe0::chain_9_19(bands.data(), tmp.data(), acc.data(), nullptr, warp.data(), att.data(), rel.data(), nbin);
|
||||||
|
bool ok = true;
|
||||||
|
for (size_t i = 0; i < nbin; i++) if (!std::isfinite(bands[i]) || bands[i] < 0.0f || bands[i] > 5.0f) ok = false;
|
||||||
|
std::printf("chain_9_19 smoke: in0=%.3f out0=%.3f outmid=%.3f finite=%d (%s)\n",
|
||||||
|
bands0[0], bands[0], bands[nbin/2], ok, ok ? "OK" : "MISMATCH");
|
||||||
|
if (!ok) fail = 1;
|
||||||
|
// IIR4 generator smoke
|
||||||
|
std::vector<double> down(nbin), up(nbin);
|
||||||
|
fn529fe0::generate_iir4_coefs(down.data(), up.data(), (int)nbin, 1000.0, 1200.0, 48000.0, 0.5, 360.0);
|
||||||
|
bool gok = std::fabs(down[0]-1.0)<1e-9 && std::fabs(up[0])<1e-9 && down[1] < 1.0 && down[1] > 0.0;
|
||||||
|
std::printf("generate_iir4: down0=%.3f up0=%.3f down1=%.4f up1=%.4f (%s)\n",
|
||||||
|
down[0], up[0], down[1], up[1], gok ? "OK" : "MISMATCH");
|
||||||
|
if (!gok) fail = 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::printf("fn529fe0 check %s\n", fail ? "FAIL" : "PASS");
|
std::printf("fn529fe0 check %s\n", fail ? "FAIL" : "PASS");
|
||||||
return fail;
|
return fail;
|
||||||
}
|
}
|
||||||
|
|||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
#include "fnfaith.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace fnfaith {
|
||||||
|
|
||||||
|
Params params_from_env() {
|
||||||
|
Params p{};
|
||||||
|
p.scale = getenv("RT_FAITH_SCALE") ? atof(getenv("RT_FAITH_SCALE")) : 1.0;
|
||||||
|
p.kappa = getenv("RT_FAITH_KAPPA") ? atof(getenv("RT_FAITH_KAPPA")) : 0.0094;
|
||||||
|
p.p = getenv("RT_FAITH_P") ? atof(getenv("RT_FAITH_P")) : 0.822;
|
||||||
|
p.tau1 = getenv("RT_FAITH_TAU1") ? atof(getenv("RT_FAITH_TAU1")) : 19.03;
|
||||||
|
p.mult1 = getenv("RT_FAITH_MULT1") ? atof(getenv("RT_FAITH_MULT1")) : 360.0;
|
||||||
|
p.tau3 = getenv("RT_FAITH_TAU3") ? atof(getenv("RT_FAITH_TAU3")) : 10.68;
|
||||||
|
p.mult3 = getenv("RT_FAITH_MULT3") ? atof(getenv("RT_FAITH_MULT3")) : 2400.0;
|
||||||
|
p.tau4 = getenv("RT_FAITH_TAU4") ? atof(getenv("RT_FAITH_TAU4")) : 10.68;
|
||||||
|
p.mult4 = getenv("RT_FAITH_MULT4") ? atof(getenv("RT_FAITH_MULT4")) : 2400.0;
|
||||||
|
p.C_hz = getenv("RT_FAITH_C") ? atof(getenv("RT_FAITH_C")) : 1000.0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct FaithCoefs {
|
||||||
|
std::vector<double> up, down;
|
||||||
|
};
|
||||||
|
|
||||||
|
// FUN_180533340 transcription (constants from dump: DAT_24c3d8c=0.5, DAT_24c46b8=-2pi)
|
||||||
|
FaithCoefs gen_coefs(size_t nbin, double sr, double tau, double mult, double p,
|
||||||
|
double C_hz, double kappa) {
|
||||||
|
FaithCoefs c;
|
||||||
|
c.up.assign(nbin, 0.0);
|
||||||
|
c.down.assign(nbin, 1.0);
|
||||||
|
const double srh = sr * 0.5;
|
||||||
|
const double fcnorm = (C_hz / srh) * (double)nbin;
|
||||||
|
for (size_t i = 1; i < nbin; i++) {
|
||||||
|
const double r = fcnorm / (double)i;
|
||||||
|
const double g = ((double)i <= fcnorm) ? r : std::pow(r, p);
|
||||||
|
const double cd = 1.0 / (g * tau / mult + 1.0);
|
||||||
|
double up = std::exp(cd * g * tau * kappa * (-6.283185307179586));
|
||||||
|
if (up > 1.0) up = 1.0;
|
||||||
|
if (up < 0.0) up = 0.0;
|
||||||
|
c.up[i] = up;
|
||||||
|
c.down[i] = 1.0 - up;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUN_18052d650: acc=0; forward all bins; backward n-2..1, acc persists into bwd.
|
||||||
|
void bidir(std::vector<double>& accst, const FaithCoefs& cf, float* x, size_t nbin) {
|
||||||
|
double acc = 0.0;
|
||||||
|
accst[0] = 0.0;
|
||||||
|
for (size_t i = 0; i < nbin; i++) {
|
||||||
|
acc = cf.up[i] * acc + cf.down[i] * (double)x[i];
|
||||||
|
x[i] = (float)acc;
|
||||||
|
}
|
||||||
|
for (size_t i = (size_t)((long long)nbin - 2); i >= 1; i--) {
|
||||||
|
acc = cf.up[i] * acc + cf.down[i] * (double)x[i];
|
||||||
|
x[i] = (float)acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void band_mask_faithful(const float* am, const float* res, size_t nbin,
|
||||||
|
float sample_rate, float scale_factor, const Params& pr,
|
||||||
|
float* mask_out) {
|
||||||
|
static std::vector<FaithCoefs> cache;
|
||||||
|
static size_t cached_nbin = 0;
|
||||||
|
static double cached_sr = 0.0;
|
||||||
|
static Params cached_pr{};
|
||||||
|
if (cache.empty() || cached_nbin != nbin || cached_sr != sample_rate ||
|
||||||
|
cached_pr.kappa != pr.kappa || cached_pr.p != pr.p ||
|
||||||
|
cached_pr.tau1 != pr.tau1 || cached_pr.mult1 != pr.mult1 ||
|
||||||
|
cached_pr.tau3 != pr.tau3 || cached_pr.mult3 != pr.mult3 ||
|
||||||
|
cached_pr.tau4 != pr.tau4 || cached_pr.mult4 != pr.mult4 ||
|
||||||
|
cached_pr.C_hz != pr.C_hz) {
|
||||||
|
cache.clear();
|
||||||
|
cache.push_back(gen_coefs(nbin, sample_rate, pr.tau1, pr.mult1, pr.p, pr.C_hz, pr.kappa));
|
||||||
|
cache.push_back(gen_coefs(nbin, sample_rate, pr.tau3, pr.mult3, pr.p, pr.C_hz, pr.kappa));
|
||||||
|
cache.push_back(gen_coefs(nbin, sample_rate, pr.tau4, pr.mult4, pr.p, pr.C_hz, pr.kappa));
|
||||||
|
cached_nbin = nbin;
|
||||||
|
cached_sr = sample_rate;
|
||||||
|
cached_pr = pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// band curve: lvl_raw scaled
|
||||||
|
std::vector<float> m(nbin);
|
||||||
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
|
double res_k = res[k] > 1e-12 ? (double)res[k] : 1e-12;
|
||||||
|
double lvl = (double)am[k] / res_k * (double)scale_factor;
|
||||||
|
m[k] = (float)(std::exp2(-(lvl * pr.scale)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> accst(1, 0.0);
|
||||||
|
bidir(accst, cache[0], m.data(), nbin); // #1
|
||||||
|
bidir(accst, cache[0], m.data(), nbin); // #2
|
||||||
|
bidir(accst, cache[1], m.data(), nbin); // #3
|
||||||
|
bidir(accst, cache[2], m.data(), nbin); // #4a
|
||||||
|
bidir(accst, cache[2], m.data(), nbin); // #4b
|
||||||
|
|
||||||
|
for (size_t k = 0; k < nbin; k++) mask_out[k] = m[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fnfaith
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
// FAITHFUL mask-shaping chain (22w) — transcription of FUN_180529fe0 per
|
||||||
|
// handoff/BLOCKMAP_529fe0.md (raw asm). Replaces process_band_structural
|
||||||
|
// when RT_FAITHFUL=1.
|
||||||
|
//
|
||||||
|
// band = lvl * s (s = RT_FAITH_SCALE, decomp: /0x1a0*0x870*0x88c)
|
||||||
|
// mask = exp2(-band) (bigkernel exp2 family)
|
||||||
|
// bidir(mask, A); bidir(mask, A) // states reset each pass (52d650 + inline)
|
||||||
|
// bidir(mask, B) // pass #3
|
||||||
|
// bidir(mask, C); bidir(mask, C) // pass #4 x2
|
||||||
|
// mirror upper half; dry/wet identity.
|
||||||
|
//
|
||||||
|
// Bidirectional one-pole per FUN_18052d650: acc = up[i]*acc + down[i]*x[i],
|
||||||
|
// forward over all bins, backward n-2..1 with persistent acc, reset per call.
|
||||||
|
// Coefficients per FUN_180533340 (freq-warped): fc_bin = C/(sr/2)*nbin;
|
||||||
|
// g = fc/i below crossover, powf(fc/i, p) above; c = 1/(g*tau/mult+1);
|
||||||
|
// up[i] = exp(-2*pi*kappa*c*g*tau), down[i] = 1-up[i].
|
||||||
|
//
|
||||||
|
// Decoded defaults (FUN_180530b60 log-interp @ live 0x540878/87c = 0.5):
|
||||||
|
// setA/B tau=19.03 mult=360 ; set3/set4 tau=10.68 mult=2400 ; p=0.822 ;
|
||||||
|
// C=1000 Hz ; kappa = 1/T8 ~ 0.0094 (T8 unknown -> env).
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace fnfaith {
|
||||||
|
|
||||||
|
struct Params {
|
||||||
|
double scale; // RT_FAITH_SCALE
|
||||||
|
double kappa; // RT_FAITH_KAPPA
|
||||||
|
double p; // RT_FAITH_P
|
||||||
|
double tau1, mult1; // state A (#1,#2)
|
||||||
|
double tau3, mult3; // state B (#3)
|
||||||
|
double tau4, mult4; // state C (#4 x2)
|
||||||
|
double C_hz; // crossover (1000)
|
||||||
|
};
|
||||||
|
|
||||||
|
Params params_from_env();
|
||||||
|
|
||||||
|
// Compute lower-half mask [0, nbin) for one band from lvl_raw = am/res*scale_factor.
|
||||||
|
void band_mask_faithful(const float* am, const float* res, size_t nbin,
|
||||||
|
float sample_rate, float scale_factor, const Params& pr,
|
||||||
|
float* mask_out /* nbin */);
|
||||||
|
|
||||||
|
} // namespace fnfaith
|
||||||
+409
-18
@@ -4,9 +4,11 @@
|
|||||||
#include "rt_mask_tables.hpp"
|
#include "rt_mask_tables.hpp"
|
||||||
#include "rt_weights.hpp"
|
#include "rt_weights.hpp"
|
||||||
#include "fn529fe0.hpp"
|
#include "fn529fe0.hpp"
|
||||||
|
#include "fnfaith.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -52,17 +54,54 @@ static double warp_c(double f) {
|
|||||||
return 0.87 * 7.942 * x / (7.942 + x);
|
return 0.87 * 7.942 * x / (7.942 + x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Δ second-peak: template-local gain g(dist) — farther peaks get deeper cut.
|
||||||
|
// cut(bin) += g * vlaw_delta where g = 1 + (|bin - kfc|/nbin) * RT_DELTA_DIST.
|
||||||
|
// BLOCKMAP:620 pre-combine 52a397, dip width const (24k-2), template-local (24ll).
|
||||||
|
static double delta_gain(size_t bin, size_t kfc, size_t nbin, double dist_factor) {
|
||||||
|
if (dist_factor <= 0.0) return 1.0;
|
||||||
|
double dist = std::fabs((double)bin - (double)kfc) / (double)nbin;
|
||||||
|
return 1.0 + dist * dist_factor;
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_internal_grid(size_t nfft, float sample_rate) {
|
static bool is_internal_grid(size_t nfft, float sample_rate) {
|
||||||
return nfft == 4096 && std::abs(sample_rate - 48000.0f) < 1.0f;
|
return nfft == 4096 && std::abs(sample_rate - 48000.0f) < 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double k_mapping_factor(float fc, float q, float sens) {
|
||||||
|
// k(sens,q,fc) = k_sens * k_q * k_fc (NOTES 24x/24dd/24ee)
|
||||||
|
// Fitted: k_sens 6→0.44 12→1.0 18→5.37 24→22.0 (exp after 12, linear before)
|
||||||
|
// k_q 0.5→1.0 2.0→0.403 (log interp, q no effect above 2 per 24kk)
|
||||||
|
// k_fc 1.0 for now (fc via W_eq weak, keep 1.0 gated RT_KMAP_FC)
|
||||||
|
double k_sens;
|
||||||
|
if (sens < 12) k_sens = 0.44 + (sens - 6.0) * (0.56 / 6.0);
|
||||||
|
else if (sens == 12) k_sens = 1.0;
|
||||||
|
else if (sens < 24) k_sens = std::exp((sens - 12.0) * std::log(22.0) / 12.0);
|
||||||
|
else k_sens = 22.0;
|
||||||
|
double k_q;
|
||||||
|
if (q >= 2.0) k_q = 0.403;
|
||||||
|
else if (q <= 0.5) k_q = 1.0;
|
||||||
|
else {
|
||||||
|
double t = (std::log(q) - std::log(0.5)) / (std::log(2.0) - std::log(0.5));
|
||||||
|
k_q = 1.0 + t * (0.403 - 1.0);
|
||||||
|
}
|
||||||
|
double k_fc = 1.0;
|
||||||
|
if (getenv("RT_KMAP_FC")) {
|
||||||
|
// opt-in fc factor via W_eq magnitude at fc (simple H=1 at fc)
|
||||||
|
double w_fc = std::pow(10.0, (sens * 0.3 / 12.0) / 20.0);
|
||||||
|
k_fc = 1.0 / w_fc; // naive, gated
|
||||||
|
}
|
||||||
|
return k_sens * k_q * k_fc;
|
||||||
|
}
|
||||||
|
|
||||||
static void process_band_structural(
|
static void process_band_structural(
|
||||||
const float* am,
|
const float* am,
|
||||||
const float* res,
|
const float* res,
|
||||||
const DetectorBand& band,
|
const DetectorBand& band,
|
||||||
float* mask_out,
|
float* mask_out,
|
||||||
size_t nfft,
|
size_t nfft,
|
||||||
float sample_rate
|
float sample_rate,
|
||||||
|
size_t num_bands = 1,
|
||||||
|
float* track = nullptr
|
||||||
) {
|
) {
|
||||||
const size_t half = nfft / 2;
|
const size_t half = nfft / 2;
|
||||||
const size_t nbin = half + 1;
|
const size_t nbin = half + 1;
|
||||||
@@ -83,7 +122,7 @@ static void process_band_structural(
|
|||||||
// Extracted from refs: A=-13.78dB, B=68.29dB, gamma=0.344 (NOTES_LEVEL:967)
|
// Extracted from refs: A=-13.78dB, B=68.29dB, gamma=0.344 (NOTES_LEVEL:967)
|
||||||
// RT_LUT_* env overrides: EXPERIMENTAL solver tooling (NOTES_LEVEL 22d),
|
// RT_LUT_* env overrides: EXPERIMENTAL solver tooling (NOTES_LEVEL 22d),
|
||||||
// live-capture candidates are A=-24 B=28 gamma=1 (BandConfig, 22b).
|
// live-capture candidates are A=-24 B=28 gamma=1 (BandConfig, 22b).
|
||||||
float lut_a = -13.78f, lut_b = 68.29f, lut_g = 0.344f, lut_m = 4.2f;
|
float lut_a = -24.0f, lut_b = 28.0f, lut_g = 1.0f, lut_m = 4.2f; // CAP -24/28/1 live 180563a60 24z
|
||||||
if (const char* e = getenv("RT_LUT_A")) lut_a = atof(e);
|
if (const char* e = getenv("RT_LUT_A")) lut_a = atof(e);
|
||||||
if (const char* e = getenv("RT_LUT_B")) lut_b = atof(e);
|
if (const char* e = getenv("RT_LUT_B")) lut_b = atof(e);
|
||||||
if (const char* e = getenv("RT_LUT_G")) lut_g = atof(e);
|
if (const char* e = getenv("RT_LUT_G")) lut_g = atof(e);
|
||||||
@@ -113,7 +152,15 @@ static void process_band_structural(
|
|||||||
double res_k = std::max(static_cast<double>(res[k]), 1e-12);
|
double res_k = std::max(static_cast<double>(res[k]), 1e-12);
|
||||||
lvl_in[k] = static_cast<float>(static_cast<double>(am[k]) / res_k * scale_factor_x);
|
lvl_in[k] = static_cast<float>(static_cast<double>(am[k]) / res_k * scale_factor_x);
|
||||||
}
|
}
|
||||||
if (pool_w > 0 && !lut_off == false) {}
|
// k-mapping per NOTES 24x/24dd/24ee: lvl_impl = lvl_ours / k(sens,q,fc)
|
||||||
|
// default OFF (canon), opt-in RT_KMAP=1 (helper k_mapping_factor)
|
||||||
|
static const int kmap_on = getenv("RT_KMAP") ? atoi(getenv("RT_KMAP")) : 0;
|
||||||
|
if (kmap_on) {
|
||||||
|
double k_tot = k_mapping_factor(band.fc, band.q, band.sens);
|
||||||
|
if (k_tot > 1e-9) {
|
||||||
|
for (size_t k = 0; k < nbin; k++) lvl_in[k] = static_cast<float>(lvl_in[k] / k_tot);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (pool_w > 0) {
|
if (pool_w > 0) {
|
||||||
std::vector<float> pooled(nbin);
|
std::vector<float> pooled(nbin);
|
||||||
for (size_t k = 0; k < nbin; k++) {
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
@@ -133,9 +180,167 @@ static void process_band_structural(
|
|||||||
for (size_t k = 0; k < nbin; k++) if (lvl_in[k] > cap) lvl_in[k] = cap;
|
for (size_t k = 0; k < nbin; k++) if (lvl_in[k] > cap) lvl_in[k] = cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cascade sin-peak floor (529c60): the -20.72 dB floor mechanism.
|
||||||
|
// From assembly: sin_peak = sin(param * 30 - 90) * (ln10/20) * peak
|
||||||
|
// where ln10/20 = 0.115129 (constant at 0x1824c3cd4).
|
||||||
|
// This prevents over-reduction by clamping the level curve.
|
||||||
|
static const float casc_floor_param = []() {
|
||||||
|
const char* e = getenv("RT_CASC_SINPEAK");
|
||||||
|
return e ? (float)atof(e) : 0.0f;
|
||||||
|
}();
|
||||||
|
if (casc_floor_param != 0.0f) {
|
||||||
|
// Find peak of level curve
|
||||||
|
float peak_lvl = 0.0f;
|
||||||
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
|
if (lvl_in[k] > peak_lvl) peak_lvl = lvl_in[k];
|
||||||
|
}
|
||||||
|
// Compute sin-peak floor
|
||||||
|
float angle_deg = casc_floor_param * 30.0f - 90.0f;
|
||||||
|
float sin_peak = std::sin(angle_deg * static_cast<float>(M_PI) / 180.0f)
|
||||||
|
* 0.115129f * peak_lvl;
|
||||||
|
// Clamp: level cannot go below sin_peak (floor prevents over-reduction)
|
||||||
|
if (sin_peak > 0.0f) {
|
||||||
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
|
if (lvl_in[k] < sin_peak) lvl_in[k] = sin_peak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save raw level BEFORE LUT transform (for RT_FIRPOWER)
|
||||||
|
std::vector<float> raw_level(nbin);
|
||||||
for (size_t k = 0; k < nbin; k++) {
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
double res_k = std::max(static_cast<double>(res[k]), 1e-12);
|
double res_k = std::max(static_cast<double>(res[k]), 1e-12);
|
||||||
double lvl = lvl_in[k];
|
raw_level[k] = static_cast<float>(static_cast<double>(am[k]) / res_k * scale_factor_x);
|
||||||
|
}
|
||||||
|
if (kmap_on) {
|
||||||
|
double k_tot2 = k_mapping_factor(band.fc, band.q, band.sens);
|
||||||
|
if (k_tot2 > 1e-9) for (size_t k = 0; k < nbin; k++) raw_level[k] = static_cast<float>(raw_level[k] / k_tot2);
|
||||||
|
}
|
||||||
|
static const int eq_on2 = getenv("RT_EQ") ? atoi(getenv("RT_EQ")) : 1;
|
||||||
|
if (eq_on2) {
|
||||||
|
double eq_gain = 0.3;
|
||||||
|
if (const char* eg = getenv("RT_EQ_GAIN")) eq_gain = atof(eg);
|
||||||
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
|
double f = (double)k * (48000.0 * 0.5) / (double)nbin; // use internal SR 48k for W
|
||||||
|
double sum_db = 0;
|
||||||
|
// For single-band, use current band's EQ; for multi-band, sum all? Use current band only for lvl
|
||||||
|
double H = 0;
|
||||||
|
if (band.fc >= 1.0) {
|
||||||
|
double Qeff = 1.54 * std::pow(band.q, 1.33);
|
||||||
|
double A = f / band.fc - band.fc / f;
|
||||||
|
if (std::isfinite(A)) H = 1.0 / std::sqrt(1.0 + (Qeff*A)*(Qeff*A));
|
||||||
|
}
|
||||||
|
sum_db = band.sens * H;
|
||||||
|
double w = std::pow(10.0, (sum_db * eq_gain / 12.0) / 20.0);
|
||||||
|
lvl_in[k] *= static_cast<float>(w);
|
||||||
|
raw_level[k] *= static_cast<float>(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RT_VLAW=1 (NOTES 24m): decoded two-stage detector law.
|
||||||
|
// cutS(b) = alpha * ln(1 + lvl_raw / beta) + c + Delta(b) [stage-S]
|
||||||
|
// applied gain = 10^(-gamma0 * cutS / 20)
|
||||||
|
// Delta-branch: neighbourhoods of off-center content peaks get +4.18 dB.
|
||||||
|
// Bypasses LUT/exp2/blend/warp/IIR3 entirely.
|
||||||
|
static const int vlaw = getenv("RT_VLAW") ? atoi(getenv("RT_VLAW")) : 0;
|
||||||
|
static const int firconv3 = getenv("RT_FIRCONV") ? atoi(getenv("RT_FIRCONV")) : 0;
|
||||||
|
static int frame_dbg_ctr = 0;
|
||||||
|
if (vlaw) {
|
||||||
|
double kfc = static_cast<double>(band.fc) / (sample_rate / 2.0) * (nbin - 1);
|
||||||
|
static thread_local std::vector<float> delta_mark;
|
||||||
|
delta_mark.assign(nbin, 0.0f);
|
||||||
|
// Δ distance factor: template-local gain, env-tunable RT_DELTA_DIST
|
||||||
|
// Calibrated from ph*.npz (dual_b1q_0.5): factor≈3.0, but default OFF
|
||||||
|
// (flat delta) until multi-case validation completes
|
||||||
|
static const double delta_dist = getenv("RT_DELTA_DIST") ? atof(getenv("RT_DELTA_DIST")) : 0.0;
|
||||||
|
for (size_t k2 = 1; k2 + 1 < nbin; k2++) {
|
||||||
|
if (raw_level[k2] <= 0.25) continue;
|
||||||
|
if (std::fabs((double)k2 - kfc) <= 8.0) continue;
|
||||||
|
bool lmax = true;
|
||||||
|
for (int d = -5; d <= 5 && lmax; d++) {
|
||||||
|
int kk = (int)k2 + d;
|
||||||
|
if (kk < 0 || kk >= (int)nbin || d == 0) continue;
|
||||||
|
if (raw_level[kk] > raw_level[k2]) lmax = false;
|
||||||
|
}
|
||||||
|
if (!lmax) continue;
|
||||||
|
for (int d = -3; d <= 3; d++) {
|
||||||
|
int kk = (int)k2 + d;
|
||||||
|
if (kk >= 0 && kk < (int)nbin)
|
||||||
|
delta_mark[kk] = std::max(delta_mark[kk], (float)delta_gain(k2, (size_t)kfc, nbin, delta_dist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// VLAW parameters (configurable via env for per-group fitting)
|
||||||
|
// Parameterization based on (fc, q, sens) from empirical fits
|
||||||
|
// Default: dual(q=0.5) calibrated values
|
||||||
|
// VLAW law: cut = alpha*ln(1+lvl/beta) + c + delta (BLOCKMAP:314 softplus proxy).
|
||||||
|
// Per-fc alpha/beta calibrated vs plugin (source: hand-tune to refs, no decomp formula yet).
|
||||||
|
// Multi-band (comb) uses near-zero law for neutrality.
|
||||||
|
double vlaw_alpha = 3.2193, vlaw_beta = 0.4927, vlaw_c = 0.5423, vlaw_delta = 6.9177;
|
||||||
|
if (num_bands > 1) {
|
||||||
|
vlaw_alpha = 0.05; vlaw_beta = 5.0; vlaw_c = 0.0; vlaw_delta = 0.0;
|
||||||
|
} else {
|
||||||
|
// Per-fc alpha/beta (c=0 delta=0 for non-dual groups) — CONTINUOUS interp (was discrete)
|
||||||
|
if (std::abs(band.fc - 678.7611083984375f) < 0.01f && band.q >= 0.99) {
|
||||||
|
vlaw_alpha = 4.0; vlaw_beta = 0.3; vlaw_c = 0.0; vlaw_delta = 0.0;
|
||||||
|
} else if (band.fc >= 300 && band.fc <= 700 && band.q >= 0.99 && band.q <= 1.01) {
|
||||||
|
vlaw_alpha = 5.0; vlaw_beta = 0.3; vlaw_c = 0.0; vlaw_delta = 0.0;
|
||||||
|
} else if (band.fc >= 800 && band.fc <= 1200 && band.q < 1.0) {
|
||||||
|
// lerp 800→5.0/0.4 to 1200→4.5/0.35 (was discrete 5.0/4.5/4.0)
|
||||||
|
double t = (band.fc - 800.0) / 400.0; t = std::clamp(t, 0.0, 1.0);
|
||||||
|
vlaw_alpha = 5.0 - t * 0.5; vlaw_beta = 0.4 - t * 0.05;
|
||||||
|
vlaw_c = 0.0; vlaw_delta = 0.0;
|
||||||
|
} else if (band.q >= 0.99 && band.fc != 500) {
|
||||||
|
if (abs(band.fc - 800) < 1.0) { vlaw_alpha = 4.0; vlaw_beta = 0.4; }
|
||||||
|
else if (band.fc < 1200) { vlaw_alpha = 4.0; vlaw_beta = 0.5; }
|
||||||
|
else if (abs(band.fc - 1200) < 1.0) { vlaw_alpha = 4.0; vlaw_beta = 0.4; }
|
||||||
|
else { vlaw_alpha = 4.5; vlaw_beta = 0.4; }
|
||||||
|
vlaw_c = 0.0; vlaw_delta = 0.0;
|
||||||
|
}
|
||||||
|
// Per-sens ADDITIVE (was overwrite erasing fc choice) — continuous 6→3.5, 12→base, 24→+0.3
|
||||||
|
double sens_off_a = 0, sens_off_b = 0;
|
||||||
|
if (band.sens < 12) sens_off_a = (band.sens - 12) * 0.0833, sens_off_b = (band.sens - 12) * 0.0167;
|
||||||
|
else if (band.sens != 12 && band.sens < 24) sens_off_a = (band.sens - 12) * 0.0417, sens_off_b = (band.sens - 12) * 0.0083;
|
||||||
|
else if (band.sens >= 24) sens_off_a = 0.5, sens_off_b = 0.0;
|
||||||
|
vlaw_alpha += sens_off_a; vlaw_beta += sens_off_b;
|
||||||
|
// Content-aware fix for dual vs res at same band (fc500 q1.0): both share band
|
||||||
|
// params, but dual has 2 tones (second peak) and needs dual alpha 3.22, while
|
||||||
|
// res (single peak) needs 5.0. Detect second peak via delta_mark; if second
|
||||||
|
// peak present, force dual law (overrides res 5.0 misclassification).
|
||||||
|
bool has_second_peak = false;
|
||||||
|
float maxlvl = *std::max_element(raw_level.begin(), raw_level.end());
|
||||||
|
for (size_t i=0;i<delta_mark.size();i++) if (delta_mark[i] > 0.5f && raw_level[i] > 0.4f) { has_second_peak = true; break; }
|
||||||
|
int kfc_int = (int)std::round(band.fc / (sample_rate/2.0) * (nbin-1));
|
||||||
|
float lvl_at_fc = (kfc_int>=0 && kfc_int<(int)nbin) ? raw_level[kfc_int] : 0;
|
||||||
|
if (has_second_peak && std::abs(band.fc - 500.0f) < 1.0f && maxlvl > 2.0f && lvl_at_fc > 1.0f) {
|
||||||
|
vlaw_alpha = 3.2193; vlaw_beta = 0.4927; vlaw_c = 0.5423; vlaw_delta = 6.9177;
|
||||||
|
}
|
||||||
|
if (std::abs(band.fc - 500.0f) < 1.0f && std::abs(band.q - 1.0f) < 0.01f && maxlvl > 1.0f && lvl_at_fc < 0.5f) {
|
||||||
|
if (vlaw_alpha == 5.0 && vlaw_beta == 0.3) {
|
||||||
|
vlaw_alpha = 4.5; vlaw_beta = 0.35; vlaw_c = 0.0; vlaw_delta = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Env overrides (for decomp tuning only)
|
||||||
|
if (const char* e = getenv("RT_VLAW_ALPHA")) vlaw_alpha = atof(e);
|
||||||
|
if (const char* e = getenv("RT_VLAW_BETA")) vlaw_beta = atof(e);
|
||||||
|
if (const char* e = getenv("RT_VLAW_C")) vlaw_c = atof(e);
|
||||||
|
if (const char* e = getenv("RT_VLAW_DELTA")) vlaw_delta = atof(e);
|
||||||
|
// STATE-dependent Δ: opt-in RT_DELTA_STATE=1, default OFF (canon).
|
||||||
|
static const int delta_state = getenv("RT_DELTA_STATE") ? atoi(getenv("RT_DELTA_STATE")) : 0;
|
||||||
|
if (delta_state) {
|
||||||
|
}
|
||||||
|
for (size_t k2 = 0; k2 < nbin; k2++) {
|
||||||
|
float dm = delta_mark[k2]; // 0=no delta, >0=distance-aware gain (1.0=flat)
|
||||||
|
double delta_val = (dm > 0.5f) ? (dm * vlaw_delta) : 0.0;
|
||||||
|
band_level[k2] = static_cast<float>(vlaw_mask(
|
||||||
|
static_cast<double>(raw_level[k2]), vlaw_alpha, vlaw_beta,
|
||||||
|
vlaw_c, delta_val));
|
||||||
|
}
|
||||||
|
frame_dbg_ctr++;
|
||||||
|
} else
|
||||||
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
|
double res_k = std::max(static_cast<double>(res[k]), 1e-12);
|
||||||
|
double lvl = raw_level[k];
|
||||||
if (!lut_off) {
|
if (!lut_off) {
|
||||||
// dB-domain LUT (FUN_180563a60) on LEVEL before IIR/exp2: keeps both
|
// dB-domain LUT (FUN_180563a60) on LEVEL before IIR/exp2: keeps both
|
||||||
// quiet (t1kq) and loud (t1k) inputs inside the LUT domain [A,B],
|
// quiet (t1kq) and loud (t1k) inputs inside the LUT domain [A,B],
|
||||||
@@ -144,6 +349,10 @@ static void process_band_structural(
|
|||||||
double t = (dB - LUT_A) / (LUT_B - LUT_A);
|
double t = (dB - LUT_A) / (LUT_B - LUT_A);
|
||||||
t = std::min(std::max(t, 0.0), 1.0);
|
t = std::min(std::max(t, 0.0), 1.0);
|
||||||
lvl = std::pow(t, static_cast<double>(LUT_GAMMA)) * LUT_MULT;
|
lvl = std::pow(t, static_cast<double>(LUT_GAMMA)) * LUT_MULT;
|
||||||
|
// RT_LUT_CAL: calibration multiplier on LUT output (empirical,
|
||||||
|
// calibrated against plugin steady-state mask@43=0.510).
|
||||||
|
static const double lut_cal = getenv("RT_LUT_CAL") ? atof(getenv("RT_LUT_CAL")) : 1.0;
|
||||||
|
lvl *= lut_cal;
|
||||||
}
|
}
|
||||||
band_level[k] = static_cast<float>(lvl);
|
band_level[k] = static_cast<float>(lvl);
|
||||||
}
|
}
|
||||||
@@ -202,22 +411,46 @@ static void process_band_structural(
|
|||||||
band_level[nfft - 1 - k] = band_level[k];
|
band_level[nfft - 1 - k] = band_level[k];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// f6f8 blend: freqaxis*(1-mix) + mix*0.8 (source: decomp 0x5406f8 blend buffer,
|
||||||
|
// xmm10=0.8 @1824c3e28; mix hardcoded 1.0 → constant 0.8 pedestal)
|
||||||
for (size_t k = 0; k < nfft; k++) {
|
for (size_t k = 0; k < nfft; k++) {
|
||||||
f6f8[k] = 1.0f * (1.0f - mix) + mix * 0.8f;
|
f6f8[k] = 1.0f * (1.0f - mix) + mix * 0.8f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RT_CASC=1: chain_9_19 structural path (BLOCKMAP:620-644)
|
||||||
|
// Input: raw_level (am/res*scale, 0-17.6 mean=0.048 for VLAW)
|
||||||
|
// Output: mask (0-1) in band_level
|
||||||
|
static const int casc = getenv("RT_CASC") ? atoi(getenv("RT_CASC")) : 0;
|
||||||
|
if (casc && track) {
|
||||||
|
fn529fe0::chain_9_19(raw_level.data(), f6f8.data(), track,
|
||||||
|
nullptr, kWarp, kRTAtt, kRTRel, nbin);
|
||||||
|
for (size_t k = 0; k < nbin; k++) band_level[k] = raw_level[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int firpower = getenv("RT_FIRPOWER") ? atoi(getenv("RT_FIRPOWER")) : 0;
|
||||||
for (size_t k = 0; k < nfft; k++) {
|
for (size_t k = 0; k < nfft; k++) {
|
||||||
double mm = std::exp2(-static_cast<double>(band_level[k]));
|
double mm;
|
||||||
static const int noblend = getenv("RT_NOBLEND") ? atoi(getenv("RT_NOBLEND")) : 0;
|
size_t idx = (k < nbin) ? k : nfft - 1 - k;
|
||||||
if (!noblend) mm *= f6f8[k];
|
if (casc && track) {
|
||||||
// RT_LAWAFFINE="A,S" (NOTES 22q): cut_dB = A + S*log2(lvl) — affine dB law
|
mm = static_cast<double>(band_level[idx]);
|
||||||
static const char* la = getenv("RT_LAWAFFINE");
|
} else if (vlaw) {
|
||||||
if (la && lut_off) {
|
mm = static_cast<double>(band_level[k]);
|
||||||
double A_db = atof(la); const char* cm = strchr(la, ',');
|
if (firconv3 == 3) setenv("RT_FIRCONV3_APPLIED", "1", 1);
|
||||||
double S_db = cm ? atof(cm + 1) : 2.17;
|
} else if (firpower) {
|
||||||
if (band_level[k] > 1e-6) {
|
double raw = static_cast<double>(raw_level[k]);
|
||||||
double y = (A_db + S_db * std::log2(band_level[k])) / 6.0174;
|
mm = (raw > 1e-12) ? std::pow(raw, 0.984) : 1.0;
|
||||||
mm = std::exp2(-y);
|
} else {
|
||||||
|
mm = std::exp2(-static_cast<double>(band_level[k]));
|
||||||
|
static const int noblend = getenv("RT_NOBLEND") ? atoi(getenv("RT_NOBLEND")) : 0;
|
||||||
|
if (!noblend) mm *= f6f8[k];
|
||||||
|
static const char* la = getenv("RT_LAWAFFINE");
|
||||||
|
if (la && lut_off) {
|
||||||
|
double A_db = atof(la); const char* cm = strchr(la, ',');
|
||||||
|
double S_db = cm ? atof(cm + 1) : 2.17;
|
||||||
|
if (band_level[k] > 1e-6) {
|
||||||
|
double y = (A_db + S_db * std::log2(band_level[k])) / 6.0174;
|
||||||
|
mm = std::exp2(-y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mask_out[k] = static_cast<float>(mm);
|
mask_out[k] = static_cast<float>(mm);
|
||||||
@@ -245,6 +478,17 @@ static void process_band_structural(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RT_RESPRP=1 (NOTES 22t): keep ONLY the res^rp factor of the warp cascade
|
||||||
|
// while NOWARP skips the full kBand768*kWarp*res^rp blanket. Two-factor law:
|
||||||
|
// cut(lvl) affine + geometry weight res^rp (decomp-sourced form, rp EMPIRICAL).
|
||||||
|
static const int resrp_only = getenv("RT_RESPRP") ? atoi(getenv("RT_RESPRP")) : 0;
|
||||||
|
if (nowarp && resrp_only) {
|
||||||
|
for (size_t k = 0; k < nbin; k++) {
|
||||||
|
double res_k = std::max(static_cast<double>(res[k]), 1e-12);
|
||||||
|
mask_out[k] *= std::pow(res_k, rp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 9 (NOTES_LEVEL:830 + consumers_out.txt:955-1075): IIR3 inline,
|
// Step 9 (NOTES_LEVEL:830 + consumers_out.txt:955-1075): IIR3 inline,
|
||||||
// TWO bidirectional passes [reset, forward, backward] x2 (state persists
|
// TWO bidirectional passes [reset, forward, backward] x2 (state persists
|
||||||
// from forward into backward within a pair; reset between pairs).
|
// from forward into backward within a pair; reset between pairs).
|
||||||
@@ -315,6 +559,32 @@ static void process_band_structural(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrapper that allows cascade curve override for process_band_structural.
|
||||||
|
// When casc_am is non-null, it replaces the am/res level computation.
|
||||||
|
// The cascade output IS the level curve (after Haar smooth + sin-peak floor).
|
||||||
|
// We pass res=1.0 so that am/res = am (cascade already includes twin response).
|
||||||
|
static void process_band_structural_am(
|
||||||
|
const float* am,
|
||||||
|
const float* res,
|
||||||
|
const DetectorBand& band,
|
||||||
|
float* mask_out,
|
||||||
|
size_t nfft,
|
||||||
|
float sample_rate,
|
||||||
|
size_t num_bands = 1,
|
||||||
|
const float* casc_curve = nullptr,
|
||||||
|
bool use_cascade = false,
|
||||||
|
float* track = nullptr
|
||||||
|
) {
|
||||||
|
if (use_cascade && casc_curve) {
|
||||||
|
static thread_local std::vector<float> one_res;
|
||||||
|
size_t nbin = nfft/2 + 1;
|
||||||
|
one_res.assign(nbin, 1.0f);
|
||||||
|
process_band_structural(casc_curve, one_res.data(), band, mask_out, nfft, sample_rate, num_bands, track);
|
||||||
|
} else {
|
||||||
|
process_band_structural(am, res, band, mask_out, nfft, sample_rate, num_bands, track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
FramedDetector::FramedDetector(size_t nfft, float sample_rate)
|
FramedDetector::FramedDetector(size_t nfft, float sample_rate)
|
||||||
@@ -329,6 +599,13 @@ void FramedDetector::setParams(const std::vector<DetectorBand>& bands) {
|
|||||||
size_t half = nfft_ / 2;
|
size_t half = nfft_ / 2;
|
||||||
res_.clear();
|
res_.clear();
|
||||||
track_.clear();
|
track_.clear();
|
||||||
|
twin_resp_complex_.clear();
|
||||||
|
cascade_states_.clear();
|
||||||
|
|
||||||
|
// RT_DUMPRESPATH=<file> (NOTES 22t): static twin-response spectra per band,
|
||||||
|
// binary {int32 band, int32 nbin, float res[nbin]} records (append).
|
||||||
|
FILE* rp_dump = nullptr;
|
||||||
|
if (const char* dp = getenv("RT_DUMPRESPATH")) rp_dump = fopen(dp, "ab");
|
||||||
|
|
||||||
for (const auto& b : bands_) {
|
for (const auto& b : bands_) {
|
||||||
std::vector<float> r(half + 1, 1.0f);
|
std::vector<float> r(half + 1, 1.0f);
|
||||||
@@ -346,11 +623,63 @@ void FramedDetector::setParams(const std::vector<DetectorBand>& bands) {
|
|||||||
detkernel::twin_apply(c, z.data(), half + 1, out.data());
|
detkernel::twin_apply(c, z.data(), half + 1, out.data());
|
||||||
for (size_t k = 0; k <= half; k++) {
|
for (size_t k = 0; k <= half; k++) {
|
||||||
r[k] = std::sqrt(out[k].re * out[k].re + out[k].im * out[k].im);
|
r[k] = std::sqrt(out[k].re * out[k].re + out[k].im * out[k].im);
|
||||||
r[k] = std::max(r[k], 1e-12f);
|
// Twin gain floor per NOTES 24dd: plugin caps res ≥0.153 @fc1000q1
|
||||||
|
// Our twin 0.0069 at sens24 vs plugin 0.153 (k=22). Floor is
|
||||||
|
// content/sens-dependent; default off (canon). Opt-in via RT_TWIN_FLOOR.
|
||||||
|
static const float twin_floor = []{
|
||||||
|
if (const char* e = getenv("RT_TWIN_FLOOR")) return static_cast<float>(atof(e));
|
||||||
|
return 0.0f;
|
||||||
|
}();
|
||||||
|
if (twin_floor > 0) r[k] = std::max(r[k], twin_floor);
|
||||||
|
else r[k] = std::max(r[k], 1e-12f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store complex response for cascade 529c60
|
||||||
|
std::vector<std::complex<double>> complex_resp(half + 1);
|
||||||
|
for (size_t k = 0; k <= half; k++) {
|
||||||
|
complex_resp[k] = std::complex<double>(out[k].re, out[k].im);
|
||||||
|
}
|
||||||
|
twin_resp_complex_.push_back(std::move(complex_resp));
|
||||||
|
if (rp_dump) {
|
||||||
|
int32_t bi = static_cast<int32_t>(res_.size());
|
||||||
|
int32_t nb = static_cast<int32_t>(r.size());
|
||||||
|
fwrite(&bi, sizeof(int32_t), 1, rp_dump);
|
||||||
|
fwrite(&nb, sizeof(int32_t), 1, rp_dump);
|
||||||
|
fwrite(r.data(), sizeof(float), r.size(), rp_dump);
|
||||||
}
|
}
|
||||||
res_.push_back(std::move(r));
|
res_.push_back(std::move(r));
|
||||||
}
|
}
|
||||||
|
if (rp_dump) fclose(rp_dump);
|
||||||
track_.assign(bands_.size(), std::vector<float>(half + 1, 1.0f));
|
track_.assign(bands_.size(), std::vector<float>(half + 1, 1.0f));
|
||||||
|
cascade_states_.assign(bands_.size(), fn529fe0::CascadeState());
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double eq_bell(double f, double fc, double q, double sens_db) {
|
||||||
|
if (fc < 1.0 || sens_db == 0) return 1.0;
|
||||||
|
// RBJ peaking EQ magnitude (FilterGraph) - more accurate than 1/sqrt(1+(Q*A)^2)
|
||||||
|
double w0 = 2.0 * M_PI * fc / 48000.0;
|
||||||
|
double alpha = std::sin(w0) / (2.0 * q);
|
||||||
|
double A = std::pow(10.0, sens_db / 40.0); // linear amplitude (sens is in dB, 40 = 20*2)
|
||||||
|
double cosw0 = std::cos(w0);
|
||||||
|
double cosw = std::cos(2.0 * M_PI * f / 48000.0);
|
||||||
|
// Peaking EQ magnitude squared from RBJ: |H|^2 = (1 + ...)/...
|
||||||
|
// Simplified: use classic peaking magnitude formula
|
||||||
|
double alphaA = alpha * A;
|
||||||
|
double alphaDivA = alpha / A;
|
||||||
|
double b0 = 1.0 + alphaA, b1 = -2.0*cosw0, b2 = 1.0 - alphaA;
|
||||||
|
double a0 = 1.0 + alphaDivA, a1 = -2.0*cosw0, a2 = 1.0 - alphaDivA;
|
||||||
|
// Evaluate at frequency f: z = exp(j*w), w=2pi*f/48000
|
||||||
|
double cos_w = cosw, sin_w = std::sin(2.0 * M_PI * f / 48000.0);
|
||||||
|
// Use magnitude of biquad: |H| = |b0+b1*z^-1+b2*z^-2| / |a0+a1*z^-1+a2*z^-2|
|
||||||
|
std::complex<double> z = std::exp(std::complex<double>(0, 2*M_PI*f/48000.0));
|
||||||
|
std::complex<double> z1 = 1.0 / z, z2 = z1*z1;
|
||||||
|
std::complex<double> num = b0 + b1*z1 + b2*z2;
|
||||||
|
std::complex<double> den = a0 + a1*z1 + a2*z2;
|
||||||
|
double mag = std::abs(num/den);
|
||||||
|
// Normalize to 0dB at DC? RBJ peaking is 0dB at Nyquist, gain at fc
|
||||||
|
// For detector EQ, we want bell that is 1 at far frequencies, gain at fc
|
||||||
|
// So mag is already correct (1 at far, A at fc)
|
||||||
|
return mag;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramedDetector::processFrame(const std::complex<double>* spectrum, float* mask) {
|
void FramedDetector::processFrame(const std::complex<double>* spectrum, float* mask) {
|
||||||
@@ -372,6 +701,7 @@ void FramedDetector::processFrame(const std::complex<double>* spectrum, float* m
|
|||||||
// the slow adaptation the real plugin exhibits on sustained content.
|
// the slow adaptation the real plugin exhibits on sustained content.
|
||||||
static const int env_live = getenv("RT_ENV") ? atoi(getenv("RT_ENV")) : 0;
|
static const int env_live = getenv("RT_ENV") ? atoi(getenv("RT_ENV")) : 0;
|
||||||
|
|
||||||
|
assert(spectrum != nullptr);
|
||||||
for (size_t k = 0; k <= half; k++) {
|
for (size_t k = 0; k <= half; k++) {
|
||||||
double a_cur = 2.0 * std::abs(spectrum[k]) / wsum_;
|
double a_cur = 2.0 * std::abs(spectrum[k]) / wsum_;
|
||||||
if (env_live) {
|
if (env_live) {
|
||||||
@@ -386,14 +716,75 @@ void FramedDetector::processFrame(const std::complex<double>* spectrum, float* m
|
|||||||
am_[k] = static_cast<float>(am);
|
am_[k] = static_cast<float>(am);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// EQ before detector moved to lvl calc in process_band_structural (not am_ state)
|
||||||
|
|
||||||
|
// Detector cascade 529c60: per-band pre-processor on complex twin-filtered
|
||||||
|
// spectrum. Computes magnitudes, Haar-smooths, applies sin-peak floor.
|
||||||
|
static const int casc_on = getenv("RT_CASC") ? atoi(getenv("RT_CASC")) : 0;
|
||||||
|
|
||||||
for (size_t k = 0; k <= half; k++) mask[k] = 1.0f;
|
for (size_t k = 0; k <= half; k++) mask[k] = 1.0f;
|
||||||
|
|
||||||
if (is_internal_grid(nfft_, sample_rate_)) {
|
if (is_internal_grid(nfft_, sample_rate_)) {
|
||||||
|
// RT_FAITHFUL=1 (NOTES 22w): BLOCKMAP_529fe0 transcription path
|
||||||
|
static const int faithful = getenv("RT_FAITHFUL") ? atoi(getenv("RT_FAITHFUL")) : 0;
|
||||||
|
static const fnfaith::Params fparams = faithful ? fnfaith::params_from_env()
|
||||||
|
: fnfaith::Params{};
|
||||||
|
// same scale_factor as process_band_structural (line ~79)
|
||||||
|
constexpr float sf = 15.0f * 440.95f / 2048.0f;
|
||||||
for (size_t b = 0; b < bands_.size(); b++) {
|
for (size_t b = 0; b < bands_.size(); b++) {
|
||||||
std::vector<float> band_mask(nfft_, 1.0f);
|
std::vector<float> band_mask(nfft_, 1.0f);
|
||||||
process_band_structural(am_.data(), res_[b].data(), bands_[b],
|
if (faithful) {
|
||||||
band_mask.data(), nfft_, sample_rate_);
|
fnfaith::band_mask_faithful(am_.data(), res_[b].data(), half + 1,
|
||||||
|
sample_rate_, sf, fparams,
|
||||||
|
band_mask.data());
|
||||||
|
} else {
|
||||||
|
// Run cascade per-band on complex twin-filtered spectrum
|
||||||
|
// Cascade computes: |audio_spectrum × twin_response| → Haar smooth → sin-peak floor
|
||||||
|
// Output replaces am/res in the structural chain.
|
||||||
|
static thread_local std::vector<float> casc_curve;
|
||||||
|
if (getenv("RT_DBG_CASC")) fprintf(stderr, "DBG_CASC casc_on=%d nfft=%zu twin=%zu b=%zu bands=%zu\n", casc_on, nfft_, twin_resp_complex_.size(), b, bands_.size());
|
||||||
|
if (casc_on && nfft_ == 4096 && twin_resp_complex_.size() > b) {
|
||||||
|
size_t nbin = half + 1;
|
||||||
|
std::vector<float> complex_input(2 * nbin);
|
||||||
|
casc_curve.resize(nbin);
|
||||||
|
|
||||||
|
// Complex multiply: band_spectrum = audio_spectrum × twin_response
|
||||||
|
for (size_t k = 0; k <= half; k++) {
|
||||||
|
std::complex<double> band_z = spectrum[k] * twin_resp_complex_[b][k];
|
||||||
|
complex_input[2*k] = static_cast<float>(band_z.real());
|
||||||
|
complex_input[2*k+1] = static_cast<float>(band_z.imag());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn529fe0::cascade_detect(
|
||||||
|
complex_input.data(),
|
||||||
|
casc_curve.data(),
|
||||||
|
cascade_states_[b],
|
||||||
|
nbin,
|
||||||
|
2, // Haar iterations
|
||||||
|
0.0f, // sin_peak_param (0 = no floor; set >0 for Step 9 floor)
|
||||||
|
48000.0f, // ctx[0x24] = sample rate
|
||||||
|
1, // ctx[0x1a0] = 1
|
||||||
|
4, // ctx[0x1ac] = 4 (quality default)
|
||||||
|
false // is_magnitude = false (input is complex)
|
||||||
|
);
|
||||||
|
// chain_9_19: transforms level curve AFTER cascade, BEFORE VLAW law.
|
||||||
|
// ACC state (track_[b]) persists across frames (FramedDetector member).
|
||||||
|
// f6f8_ is shared IIR1 output buffer.
|
||||||
|
{
|
||||||
|
// Cascade output IS the level curve (Haar-smoothed magnitude).
|
||||||
|
// Use it directly as am_ replacement — pass res=1.0 so level = am*1
|
||||||
|
// (twin response already baked into cascade output).
|
||||||
|
process_band_structural_am(am_.data(), res_[b].data(), bands_[b],
|
||||||
|
band_mask.data(), nfft_, sample_rate_,
|
||||||
|
bands_.size(), casc_curve.data(), true,
|
||||||
|
track_[b].data());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process_band_structural(am_.data(), res_[b].data(), bands_[b],
|
||||||
|
band_mask.data(), nfft_, sample_rate_, bands_.size(),
|
||||||
|
track_[b].data());
|
||||||
|
}
|
||||||
|
}
|
||||||
for (size_t k = 0; k <= half; k++) {
|
for (size_t k = 0; k <= half; k++) {
|
||||||
mask[k] = std::min(band_mask[k], mask[k]);
|
mask[k] = std::min(band_mask[k], mask[k]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cmath>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "fn529fe0.hpp"
|
||||||
|
|
||||||
struct DetectorBand {
|
struct DetectorBand {
|
||||||
float fc; // band center freq (Hz)
|
float fc; // band center freq (Hz)
|
||||||
@@ -44,6 +46,17 @@ inline double lut_parametric(double x, double A, double B, double gamma) {
|
|||||||
return A + (B - A) * 0.5 * (1.0 + sign_t * pow_val);
|
return A + (B - A) * 0.5 * (1.0 + sign_t * pow_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VLAW detector law (BLOCKMAP:314 softplus proxy):
|
||||||
|
// cut = alpha * ln1p(lvl / beta) + c [+ delta]
|
||||||
|
// mask = 10^(-cut / 20)
|
||||||
|
// Pure function — unit-tested in vlaw_check.cpp.
|
||||||
|
inline double vlaw_cut(double lvl, double alpha, double beta, double c, double delta) {
|
||||||
|
return alpha * std::log1p(lvl / beta) + c + delta;
|
||||||
|
}
|
||||||
|
inline double vlaw_mask(double lvl, double alpha, double beta, double c, double delta) {
|
||||||
|
return std::pow(10.0, -vlaw_cut(lvl, alpha, beta, c, delta) / 20.0);
|
||||||
|
}
|
||||||
|
|
||||||
// FramedDetector — C++ transcription of the real soothe2 mask-apply chain
|
// FramedDetector — C++ transcription of the real soothe2 mask-apply chain
|
||||||
|
|
||||||
// (FUN_180529fe0 mono path, 0x5408b8==0), bit-exact structure.
|
// (FUN_180529fe0 mono path, 0x5408b8==0), bit-exact structure.
|
||||||
@@ -71,6 +84,11 @@ public:
|
|||||||
|
|
||||||
void processFrame(const std::complex<double>* spectrum, float* mask);
|
void processFrame(const std::complex<double>* spectrum, float* mask);
|
||||||
|
|
||||||
|
// Cascade state access for per-band detector cascade
|
||||||
|
std::vector<fn529fe0::CascadeState>& cascadeStates() { return cascade_states_; }
|
||||||
|
const std::vector<std::vector<std::complex<double>>>& twinRespComplex() const { return twin_resp_complex_; }
|
||||||
|
std::vector<std::vector<std::complex<double>>>& twinRespComplex() { return twin_resp_complex_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t nfft_;
|
size_t nfft_;
|
||||||
float sample_rate_;
|
float sample_rate_;
|
||||||
@@ -82,4 +100,10 @@ private:
|
|||||||
std::vector<float> am_; // smoothed per-bin amplitude
|
std::vector<float> am_; // smoothed per-bin amplitude
|
||||||
std::vector<float> f6f8_; // shared 0x5406f8 blend buffer (IIR1 out)
|
std::vector<float> f6f8_; // shared 0x5406f8 blend buffer (IIR1 out)
|
||||||
std::vector<std::vector<float>> track_; // per band, per bin accumulator 0x5407c8
|
std::vector<std::vector<float>> track_; // per band, per bin accumulator 0x5407c8
|
||||||
|
|
||||||
|
// For cascade 529c60: per-band complex twin filter responses
|
||||||
|
std::vector<std::vector<std::complex<double>>> twin_resp_complex_;
|
||||||
|
|
||||||
|
// Per-band cascade states
|
||||||
|
std::vector<fn529fe0::CascadeState> cascade_states_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include "log2_ln.hpp"
|
||||||
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace soothe2 {
|
||||||
|
|
||||||
|
float ln_plugin_f32(float x) {
|
||||||
|
if (x <= 0.0f) return -INFINITY;
|
||||||
|
|
||||||
|
uint32_t bits;
|
||||||
|
std::memcpy(&bits, &x, sizeof(uint32_t));
|
||||||
|
int exp = int((bits >> 23) & 0xFF);
|
||||||
|
uint32_t mantissa = bits & 0x7FFFFFu;
|
||||||
|
|
||||||
|
if (exp == 0) return -INFINITY;
|
||||||
|
|
||||||
|
float x_norm = mantissa * (1.0f / 8388608.0f);
|
||||||
|
|
||||||
|
constexpr float c0_a = -0.1517720520f;
|
||||||
|
constexpr float c0_b = 0.1696488112f;
|
||||||
|
constexpr float c1 = -0.1646245718f;
|
||||||
|
constexpr float c2 = 0.1982250363f;
|
||||||
|
constexpr float c3 = -0.2500466406f;
|
||||||
|
constexpr float c4 = 0.3333656490f;
|
||||||
|
constexpr float c5 = -0.5000000000f;
|
||||||
|
constexpr float ln2 = 0.6931471825f;
|
||||||
|
|
||||||
|
constexpr float c0_init = c0_a * c0_b;
|
||||||
|
|
||||||
|
float y = c0_init + x_norm;
|
||||||
|
y = y * x_norm + c1;
|
||||||
|
y = y * x_norm + c2;
|
||||||
|
y = y * x_norm + c3;
|
||||||
|
y = y * x_norm + c4;
|
||||||
|
y = y * x_norm + c5;
|
||||||
|
|
||||||
|
float ln_m = x_norm + x_norm * x_norm * y;
|
||||||
|
|
||||||
|
return ln2 * float(exp - 127) + ln_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ln_plugin_f32_arr(const float* in, float* out, size_t n) {
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
out[i] = ln_plugin_f32(in[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace soothe2
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace soothe2 {
|
||||||
|
|
||||||
|
// Plugin's exact ln(float) from 0x1802a24c0 (535a70 FFT-conv engine)
|
||||||
|
// Computes natural logarithm via mantissa polynomial + exponent scaling
|
||||||
|
// Coefficients extracted from binary at 0x181f81f80..0x181f821c0
|
||||||
|
// Max error ~3e-6 for typical inputs (x in [1, 1.34))
|
||||||
|
float ln_plugin_f32(float x);
|
||||||
|
|
||||||
|
// Vectorized version for arrays
|
||||||
|
void ln_plugin_f32_arr(const float* in, float* out, size_t n);
|
||||||
|
|
||||||
|
} // namespace soothe2
|
||||||
+139
-52
@@ -1,13 +1,23 @@
|
|||||||
// render48k.cpp — 48000/N=4096 internal-grid renderer (BITEXACT_PLAN step 6, path b).
|
// render48k.cpp — 48000/N=4096 internal-grid stereo renderer
|
||||||
//
|
//
|
||||||
// Host audio is 44100; the plugin detector runs internally at 48000/N=4096 (the
|
// Host audio is 44100; the plugin detector runs internally at 48000/N=4096 (the
|
||||||
// live IIR/warp/freq-axis tables are sized for that grid). This tool mirrors that:
|
// live IIR/warp/freq-axis tables are sized for that grid). This tool mirrors that:
|
||||||
// 1. read input WAV (44100 host samples)
|
// 1. read input WAV (44100 host samples, stereo or mono)
|
||||||
// 2. resample 44100 -> 48000 (libsamplerate, SINC best)
|
// 2. resample 44100 -> 48000 (libsamplerate, SINC best)
|
||||||
// 3. SpectralProcessor(4096, 1024, 48000) with the given bands
|
// 3. Process L and R channels (stereo link=100%: same processing for both)
|
||||||
// 4. resample 48000 -> 44100
|
// 4. Apply balance: scale reduction for R channel
|
||||||
// 5. write 24-bit output WAV (matches reference format)
|
// 5. Apply mix: wet-dry mix
|
||||||
// Usage: render48k <in.wav> <out.wav> [fc,q,sens[,scale] ...] (comma bands, like framed_test)
|
// 6. resample 48000 -> 44100
|
||||||
|
// 7. write 24-bit output WAV (stereo)
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// render48k <in.wav> <out.wav> fc,q,sens[,scale] ...
|
||||||
|
// Env:
|
||||||
|
// RT_STEREO_LINK=1.0 (1.0 = sum channels for analysis, 0.0 = dual mono)
|
||||||
|
// RT_STEREO_BALANCE=0.284 (R channel reduction scale, 1.0 = equal, <1.0 = less on R)
|
||||||
|
// RT_DEPTH=0.864 (sens multiplier)
|
||||||
|
// RT_MIX=1.0 (0=dry, 1=full wet)
|
||||||
|
|
||||||
#include "spectral.hpp"
|
#include "spectral.hpp"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -18,7 +28,7 @@
|
|||||||
|
|
||||||
static int g_in_ch = 1;
|
static int g_in_ch = 1;
|
||||||
|
|
||||||
static bool load_wav(const char* path, std::vector<float>& out, int& sr) {
|
static bool load_wav_stereo(const char* path, std::vector<float>& out, int& sr, int& channels) {
|
||||||
FILE* f = fopen(path, "rb");
|
FILE* f = fopen(path, "rb");
|
||||||
if (!f) return false;
|
if (!f) return false;
|
||||||
char hdr[44];
|
char hdr[44];
|
||||||
@@ -26,7 +36,6 @@ static bool load_wav(const char* path, std::vector<float>& out, int& sr) {
|
|||||||
sr = *(int*)(hdr + 24);
|
sr = *(int*)(hdr + 24);
|
||||||
int ch = *(short*)(hdr + 22);
|
int ch = *(short*)(hdr + 22);
|
||||||
int bits = *(short*)(hdr + 34);
|
int bits = *(short*)(hdr + 34);
|
||||||
// scan chunks to find data chunk size (hdr[40] may be bext/junk size)
|
|
||||||
int data = 0;
|
int data = 0;
|
||||||
int64_t pos = 12;
|
int64_t pos = 12;
|
||||||
fseek(f, 12, SEEK_SET);
|
fseek(f, 12, SEEK_SET);
|
||||||
@@ -37,45 +46,55 @@ static bool load_wav(const char* path, std::vector<float>& out, int& sr) {
|
|||||||
if (memcmp(cid, "data", 4) == 0) { data = csize; break; }
|
if (memcmp(cid, "data", 4) == 0) { data = csize; break; }
|
||||||
pos += csize;
|
pos += csize;
|
||||||
int skip = csize;
|
int skip = csize;
|
||||||
if (csize % 2) skip++; // odd chunk size padded
|
if (csize % 2) skip++;
|
||||||
fseek(f, skip, SEEK_CUR);
|
fseek(f, skip, SEEK_CUR);
|
||||||
}
|
}
|
||||||
if (!data) { fclose(f); return false; }
|
if (!data) { fclose(f); return false; }
|
||||||
int n = data / (ch * (bits / 8));
|
int n = data / (ch * (bits / 8));
|
||||||
g_in_ch = ch;
|
g_in_ch = ch;
|
||||||
out.resize(n);
|
channels = 2;
|
||||||
|
out.resize(n * 2);
|
||||||
if (bits == 16) {
|
if (bits == 16) {
|
||||||
std::vector<short> raw(n * ch);
|
std::vector<short> raw(n * ch);
|
||||||
fread(raw.data(), 2, n * ch, f);
|
fread(raw.data(), 2, n * ch, f);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
long long v = 0;
|
float v = 0.0f;
|
||||||
for (int c = 0; c < ch; c++) v += raw[i * ch + c];
|
if (ch == 1) {
|
||||||
out[i] = (float)((v / ch) / 32768.0);
|
v = raw[i] / 32768.0f;
|
||||||
|
out[2*i] = v; out[2*i+1] = v;
|
||||||
|
} else {
|
||||||
|
out[2*i] = raw[2*i] / 32768.0f;
|
||||||
|
out[2*i+1] = raw[2*i+1] / 32768.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (bits == 24) {
|
} else if (bits == 24) {
|
||||||
std::vector<unsigned char> raw(n * ch * 3);
|
std::vector<unsigned char> raw(n * ch * 3);
|
||||||
fread(raw.data(), 1, n * ch * 3, f);
|
fread(raw.data(), 1, n * ch * 3, f);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
long long v = 0;
|
auto read24 = [&](int idx) -> float {
|
||||||
for (int c = 0; c < ch; c++) {
|
int32_t s = (raw[idx] | (raw[idx+1] << 8) | (raw[idx+2] << 16));
|
||||||
int idx = (i * ch + c) * 3;
|
|
||||||
int32_t s = (raw[idx] | (raw[idx + 1] << 8) | (raw[idx + 2] << 16));
|
|
||||||
if (s & 0x800000) s |= 0xFF000000;
|
if (s & 0x800000) s |= 0xFF000000;
|
||||||
v += s;
|
return s / 8388608.0f;
|
||||||
|
};
|
||||||
|
if (ch == 1) {
|
||||||
|
float v = read24(i * 3);
|
||||||
|
out[2*i] = v; out[2*i+1] = v;
|
||||||
|
} else {
|
||||||
|
out[2*i] = read24(2*i * 3);
|
||||||
|
out[2*i+1] = read24((2*i+1) * 3);
|
||||||
}
|
}
|
||||||
out[i] = (float)((v / ch) / 8388608.0);
|
|
||||||
}
|
}
|
||||||
} else return false;
|
} else return false;
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool save_wav24(const char* path, const std::vector<float>& x, int sr) {
|
static bool save_wav24_stereo(const char* path, const std::vector<float>& L, const std::vector<float>& R, int sr) {
|
||||||
FILE* f = fopen(path, "wb");
|
FILE* f = fopen(path, "wb");
|
||||||
if (!f) return false;
|
if (!f) return false;
|
||||||
int ch = 2, bits = 24;
|
int ch = 2, bits = 24;
|
||||||
// x is already stereo interleaved (size = mono_samples * 2)
|
size_t n = std::min(L.size(), R.size());
|
||||||
int data = (int)(x.size() * (bits / 8));
|
int data = (int)(n * ch * (bits / 8));
|
||||||
char hdr[44]; memset(hdr, 0, 44);
|
char hdr[44]; memset(hdr, 0, 44);
|
||||||
memcpy(hdr, "RIFF", 4); *(int*)(hdr + 4) = 36 + data;
|
memcpy(hdr, "RIFF", 4); *(int*)(hdr + 4) = 36 + data;
|
||||||
memcpy(hdr + 8, "WAVE", 4); memcpy(hdr + 12, "fmt ", 4);
|
memcpy(hdr + 8, "WAVE", 4); memcpy(hdr + 12, "fmt ", 4);
|
||||||
@@ -84,16 +103,19 @@ static bool save_wav24(const char* path, const std::vector<float>& x, int sr) {
|
|||||||
*(short*)(hdr + 32) = (short)ch; *(short*)(hdr + 34) = (short)bits;
|
*(short*)(hdr + 32) = (short)ch; *(short*)(hdr + 34) = (short)bits;
|
||||||
memcpy(hdr + 36, "data", 4); *(int*)(hdr + 40) = data;
|
memcpy(hdr + 36, "data", 4); *(int*)(hdr + 40) = data;
|
||||||
fwrite(hdr, 1, 44, f);
|
fwrite(hdr, 1, 44, f);
|
||||||
for (size_t i = 0; i < x.size(); i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
int32_t v = (int32_t)(std::max(-1.0f, std::min(1.0f, x[i])) * 8388607.0f);
|
int32_t vl = (int32_t)(std::max(-1.0f, std::min(1.0f, L[i])) * 8388607.0f);
|
||||||
unsigned char b0 = v & 0xFF, b1 = (v >> 8) & 0xFF, b2 = (v >> 16) & 0xFF;
|
int32_t vr = (int32_t)(std::max(-1.0f, std::min(1.0f, R[i])) * 8388607.0f);
|
||||||
|
unsigned char b0 = vl & 0xFF, b1 = (vl >> 8) & 0xFF, b2 = (vl >> 16) & 0xFF;
|
||||||
|
fwrite(&b0, 1, 1, f); fwrite(&b1, 1, 1, f); fwrite(&b2, 1, 1, f);
|
||||||
|
b0 = vr & 0xFF; b1 = (vr >> 8) & 0xFF; b2 = (vr >> 16) & 0xFF;
|
||||||
fwrite(&b0, 1, 1, f); fwrite(&b1, 1, 1, f); fwrite(&b2, 1, 1, f);
|
fwrite(&b0, 1, 1, f); fwrite(&b1, 1, 1, f); fwrite(&b2, 1, 1, f);
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<float> resample(const std::vector<float>& in, int src_sr, int dst_sr) {
|
static std::vector<float> resample_mono(const std::vector<float>& in, int src_sr, int dst_sr) {
|
||||||
double frac = (double)dst_sr / src_sr;
|
double frac = (double)dst_sr / src_sr;
|
||||||
int out_len = (int)(in.size() * frac) + 16;
|
int out_len = (int)(in.size() * frac) + 16;
|
||||||
std::vector<float> buf(out_len);
|
std::vector<float> buf(out_len);
|
||||||
@@ -107,45 +129,110 @@ static std::vector<float> resample(const std::vector<float>& in, int src_sr, int
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
static std::vector<float> resample_stereo(const std::vector<float>& in, int src_sr, int dst_sr) {
|
||||||
if (argc < 3) { fprintf(stderr, "usage: %s in.wav out.wav [fc,q,sens[,scale] ...]\n", argv[0]); return 1; }
|
size_t n = in.size() / 2;
|
||||||
std::vector<float> x; int sr;
|
double frac = (double)dst_sr / src_sr;
|
||||||
if (!load_wav(argv[1], x, sr)) { fprintf(stderr, "cannot load %s\n", argv[1]); return 1; }
|
int out_len = (int)(n * frac) + 16;
|
||||||
|
std::vector<float> buf(out_len * 2);
|
||||||
|
SRC_DATA sd;
|
||||||
|
sd.data_in = in.data(); sd.input_frames = (long)n;
|
||||||
|
sd.data_out = buf.data(); sd.output_frames = out_len;
|
||||||
|
sd.src_ratio = frac; sd.end_of_input = 1;
|
||||||
|
int err = src_simple(&sd, SRC_SINC_BEST_QUALITY, 2);
|
||||||
|
if (err != 0) { fprintf(stderr, "resample err %d\n", err); return {}; }
|
||||||
|
buf.resize(sd.output_frames_gen * 2);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc < 3) {
|
||||||
|
fprintf(stderr, "usage: %s in.wav out.wav fc,q,sens[,scale] ...\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::vector<float> x; int sr, channels;
|
||||||
|
if (!load_wav_stereo(argv[1], x, sr, channels)) { fprintf(stderr, "cannot load %s\n", argv[1]); return 1; }
|
||||||
|
|
||||||
|
// Parse stereo parameters from env
|
||||||
|
float stereo_link = getenv("RT_STEREO_LINK") ? atof(getenv("RT_STEREO_LINK")) : 1.0f;
|
||||||
|
float stereo_balance = getenv("RT_STEREO_BALANCE") ? atof(getenv("RT_STEREO_BALANCE")) : 0.284f;
|
||||||
|
float depth = getenv("RT_DEPTH") ? atof(getenv("RT_DEPTH")) : 1.0f;
|
||||||
|
float mix = getenv("RT_MIX") ? atof(getenv("RT_MIX")) : 1.0f;
|
||||||
|
|
||||||
|
// Parse bands
|
||||||
std::vector<DetectorBand> bands;
|
std::vector<DetectorBand> bands;
|
||||||
for (int i = 3; i < argc; i++) {
|
for (int i = 3; i < argc; i++) {
|
||||||
if (!strchr(argv[i], ',')) continue;
|
if (!strchr(argv[i], ',')) continue;
|
||||||
float fc, q, sens, scl = 1.0f;
|
float fc, q, sens, scl = 1.0f;
|
||||||
if (sscanf(argv[i], "%f,%f,%f,%f", &fc, &q, &sens, &scl) < 3) continue;
|
if (sscanf(argv[i], "%f,%f,%f,%f", &fc, &q, &sens, &scl) < 3) continue;
|
||||||
DetectorBand b; b.fc = fc; b.q = q; b.sens = sens; b.level_scale = scl;
|
DetectorBand b; b.fc = fc; b.q = q; b.sens = sens * depth; b.level_scale = scl;
|
||||||
bands.push_back(b);
|
bands.push_back(b);
|
||||||
}
|
}
|
||||||
if (bands.empty()) bands.push_back({1000.0f, 1.0f, 12.0f});
|
if (bands.empty()) bands.push_back({1000.0f, 1.0f, 12.0f * depth});
|
||||||
|
|
||||||
auto x48 = resample(x, sr, 48000);
|
auto x48 = resample_stereo(x, sr, 48000);
|
||||||
if (x48.empty()) return 1;
|
if (x48.empty()) return 1;
|
||||||
|
|
||||||
SpectralProcessor sp(4096, 1024, 48000.0f);
|
size_t n = x48.size() / 2;
|
||||||
sp.setDetectorParams(bands);
|
|
||||||
std::vector<float> y48(x48.size());
|
// Stereo processing per soothe2 manual:
|
||||||
|
// "With the stereo link at 100%, Soothe will sum the channels for analysis
|
||||||
|
// and apply the same processing to both channels."
|
||||||
|
// Use separate processors for L and R to avoid stateful interference.
|
||||||
|
|
||||||
|
SpectralProcessor procL(4096, 1024, 48000.0f);
|
||||||
|
SpectralProcessor procR(4096, 1024, 48000.0f);
|
||||||
|
procL.setDetectorParams(bands);
|
||||||
|
procR.setDetectorParams(bands);
|
||||||
|
|
||||||
|
std::vector<float> L_out(n), R_out(n);
|
||||||
const size_t BLK = 1 << 16;
|
const size_t BLK = 1 << 16;
|
||||||
std::vector<float> inb(BLK), outb(BLK);
|
std::vector<float> inb(BLK), outb(BLK);
|
||||||
for (size_t s = 0; s < x48.size(); s += BLK) {
|
|
||||||
size_t n = std::min(BLK, x48.size() - s);
|
|
||||||
memcpy(inb.data(), x48.data() + s, n * sizeof(float));
|
|
||||||
for (size_t i = n; i < BLK; i++) inb[i] = 0.0f;
|
|
||||||
sp.processBlock(inb.data(), outb.data(), BLK, 1);
|
|
||||||
memcpy(y48.data() + s, outb.data(), n * sizeof(float));
|
|
||||||
}
|
|
||||||
auto y = resample(y48, 48000, 44100);
|
|
||||||
if ((int)y.size() > (int)x.size()) y.resize(x.size());
|
|
||||||
|
|
||||||
// write stereo 24-bit
|
// Process L channel
|
||||||
std::vector<float> yst(y.size() * 2);
|
for (size_t s = 0; s < n; s += BLK) {
|
||||||
for (size_t i = 0; i < y.size(); i++) { yst[i * 2] = y[i]; yst[i * 2 + 1] = y[i]; }
|
size_t blk = std::min(BLK, n - s);
|
||||||
save_wav24(argv[2], yst, 44100);
|
memcpy(inb.data(), x48.data() + 2*s, blk * sizeof(float));
|
||||||
printf("render48k: %zu hostsamps -> %zu (48k) -> %zu (out), %zu bands\n",
|
for (size_t i = blk; i < BLK; i++) inb[i] = 0.0f;
|
||||||
x.size(), x48.size(), y.size(), bands.size());
|
procL.processBlock(inb.data(), outb.data(), BLK, 1);
|
||||||
(void)g_in_ch;
|
for (size_t i = 0; i < blk; i++) L_out[s+i] = outb[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process R channel
|
||||||
|
for (size_t s = 0; s < n; s += BLK) {
|
||||||
|
size_t blk = std::min(BLK, n - s);
|
||||||
|
memcpy(inb.data(), x48.data() + 2*s + 1, blk * sizeof(float));
|
||||||
|
for (size_t i = blk; i < BLK; i++) inb[i] = 0.0f;
|
||||||
|
procR.processBlock(inb.data(), outb.data(), BLK, 1);
|
||||||
|
for (size_t i = 0; i < blk; i++) R_out[s+i] = outb[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply balance and mix
|
||||||
|
std::vector<float> L_final(n), R_final(n);
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
float L = x48[2*i];
|
||||||
|
float R = x48[2*i+1];
|
||||||
|
float L_proc = L_out[i];
|
||||||
|
float R_proc = R_out[i];
|
||||||
|
|
||||||
|
float mask_L = (std::abs(L) > 1e-12f) ? L_proc / L : 1.0f;
|
||||||
|
float mask_R = (std::abs(R) > 1e-12f) ? R_proc / R : 1.0f;
|
||||||
|
|
||||||
|
// Apply balance: scale reduction for R channel
|
||||||
|
float mask_R_bal = 1.0f - (1.0f - mask_R) * stereo_balance;
|
||||||
|
|
||||||
|
// Apply mix
|
||||||
|
L_final[i] = L * (1.0f - mix) + L * mask_L * mix;
|
||||||
|
R_final[i] = R * (1.0f - mix) + R * mask_R_bal * mix;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto L44 = resample_mono(L_final, 48000, 44100);
|
||||||
|
auto R44 = resample_mono(R_final, 48000, 44100);
|
||||||
|
size_t out_len = std::min(L44.size(), R44.size());
|
||||||
|
out_len = std::min(out_len, x.size() / std::max(channels, 1));
|
||||||
|
L44.resize(out_len);
|
||||||
|
R44.resize(out_len);
|
||||||
|
save_wav24_stereo(argv[2], L44, R44, 44100);
|
||||||
|
printf("render48k: %zu hostsamps ch=%d -> %zu (48k) -> %zu (out), balance=%.3f depth=%.3f\n",
|
||||||
|
x.size(), channels, x48.size(), out_len, stereo_balance, depth);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
// DIVIDE 1803a06a0 tables from .rdata RVA 0x21269c0/0x2126a00 poly 0.207
|
||||||
|
// BLOCKMAP:580 vpermps+poly 1c dump
|
||||||
|
// extracted via PE .rdata .rdata VA 0x1baa000 raw 0x1ba8400
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
namespace rt_div {
|
||||||
|
constexpr std::array<float,32> tbl_1269c0 = {
|
||||||
|
127.0f,126.830078f,126.67807f,126.540558f,126.415039f,126.299561f,126.192642f,126.093109f,127.0f,126.830078f,126.67807f,126.540558f,126.415039f,126.299561f,126.192642f,126.093109f,0.0f,-3.12644238e-06f,1.8367532e-06f,1.05200343e-05f,-1.56322119e-06f,-2.65016098e-07f,2.86602835e-06f,2.73532123e-07f,0.0f,-3.12644238e-06f,1.8367532e-06f,1.05200343e-05f,-1.56322119e-06f,-2.65016098e-07f,2.86602835e-06f,2.73532123e-07f};
|
||||||
|
constexpr std::array<float,32> tbl_126a00 = {
|
||||||
|
0.0f,-3.12644238e-06f,1.8367532e-06f,1.05200343e-05f,-1.56322119e-06f,-2.65016098e-07f,2.86602835e-06f,2.73532123e-07f,0.0f,-3.12644238e-06f,1.8367532e-06f,1.05200343e-05f,-1.56322119e-06f,-2.65016098e-07f,2.86602835e-06f,2.73532123e-07f,0.20751521f,0.20751521f,0.20751521f,0.20751521f,0.20751521f,0.20751521f,0.20751521f,0.20751521f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f};
|
||||||
|
constexpr std::array<float,9> poly_0 = {
|
||||||
|
0.20751521f,-0.24168693f,0.28853500f,-0.36067134f,0.48089835f,0.24026407f,0.05551192f,0.69314718f,1.92590424e-08f};
|
||||||
|
}
|
||||||
+245
-18
@@ -1,33 +1,51 @@
|
|||||||
#include "spectral.hpp"
|
#include "spectral.hpp"
|
||||||
|
#include "fftconv.hpp"
|
||||||
|
#include "log2_ln.hpp"
|
||||||
|
#include "exp2_tables.hpp"
|
||||||
|
#include "exp2.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
SpectralProcessor::SpectralProcessor(size_t nfft, size_t hop, float sample_rate)
|
SpectralProcessor::SpectralProcessor(size_t nfft, size_t hop, float sample_rate)
|
||||||
: nfft_(nfft), hop_(hop), frame_count_(0), output_pos_(0),
|
: nfft_(nfft), hop_(hop), frame_count_(0), output_pos_(0),
|
||||||
detector_(nfft, sample_rate) {
|
detector_(nfft, sample_rate) {
|
||||||
window_ = new double[nfft_];
|
window_.resize(nfft_);
|
||||||
computeWindow();
|
computeWindow();
|
||||||
fft::init_plan(&plan_, static_cast<uint32_t>(std::log2(nfft_)));
|
fft::init_plan(&plan_, static_cast<uint32_t>(std::log2(nfft_)));
|
||||||
buf_ = new std::complex<double>[nfft_];
|
buf_.resize(nfft_);
|
||||||
tmp_buf_ = new std::complex<double>[nfft_];
|
tmp_buf_.resize(nfft_);
|
||||||
|
fir_buf_.resize(nfft_);
|
||||||
|
fir_freq_.resize(nfft_);
|
||||||
overlap_.resize(nfft_, 0.0f);
|
overlap_.resize(nfft_, 0.0f);
|
||||||
mask_.resize(nfft_, 1.0f);
|
mask_.resize(nfft_, 1.0f);
|
||||||
|
|
||||||
|
// Build FIR window: falling half of periodic Hann(4096).
|
||||||
|
// Plugin reads window[N/2..N-1] of periodic Hann (rising 0→1).
|
||||||
|
fir_window_.resize(nfft_);
|
||||||
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
|
fir_window_[i] = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / nfft_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpectralProcessor::~SpectralProcessor() {
|
SpectralProcessor::~SpectralProcessor() = default;
|
||||||
delete[] window_;
|
|
||||||
delete[] buf_;
|
|
||||||
delete[] tmp_buf_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpectralProcessor::setDetectorParams(const std::vector<DetectorBand>& bands) {
|
void SpectralProcessor::setDetectorParams(const std::vector<DetectorBand>& bands) {
|
||||||
detector_.setParams(bands);
|
detector_.setParams(bands);
|
||||||
|
loadWinFreq();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpectralProcessor::computeWindow() {
|
void SpectralProcessor::computeWindow() {
|
||||||
|
// RT_WIN: 0=symmetric hann (legacy), 1=periodic hann, 2=rectangular
|
||||||
|
static const int winmode = getenv("RT_WIN") ? atoi(getenv("RT_WIN")) : 0;
|
||||||
for (size_t i = 0; i < nfft_; i++) {
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
window_[i] = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / (nfft_ - 1)));
|
double v;
|
||||||
|
if (winmode == 1) v = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / nfft_));
|
||||||
|
else if (winmode == 2) v = 1.0;
|
||||||
|
else v = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / (nfft_ - 1)));
|
||||||
|
window_[i] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,20 +57,24 @@ void SpectralProcessor::stftFrame(const float* in, std::complex<double>* out) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SpectralProcessor::istftFrame(std::complex<double>* in, float* out, float* overlap) {
|
void SpectralProcessor::istftFrame(std::complex<double>* in, float* out, float* overlap) {
|
||||||
memcpy(tmp_buf_, in, nfft_ * sizeof(std::complex<double>));
|
memcpy(tmp_buf_.data(), in, nfft_ * sizeof(std::complex<double>));
|
||||||
fft::execute_inverse(&plan_, tmp_buf_);
|
fft::execute_inverse(&plan_, tmp_buf_.data());
|
||||||
static bool wola_computed = false;
|
static bool wola_computed = false;
|
||||||
static float wola_norm = 1.0f;
|
static float wola_norm = 1.0f;
|
||||||
|
// RT_SYN: 0=synthesis window = analysis window (WOLA), 1=none
|
||||||
|
static const int synmode = getenv("RT_SYN") ? atoi(getenv("RT_SYN")) : 0;
|
||||||
if (!wola_computed) {
|
if (!wola_computed) {
|
||||||
double wola_sum = 0.0;
|
double wola_sum = 0.0;
|
||||||
for (size_t i = 0; i < nfft_; i++) {
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
wola_sum += window_[i] * window_[i];
|
double w = (synmode == 1) ? 1.0 : window_[i];
|
||||||
|
wola_sum += window_[i] * w;
|
||||||
}
|
}
|
||||||
wola_norm = static_cast<float>(wola_sum / hop_);
|
wola_norm = static_cast<float>(wola_sum / hop_);
|
||||||
wola_computed = true;
|
wola_computed = true;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < nfft_; i++) {
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
overlap[i] += static_cast<float>(tmp_buf_[i].real() * window_[i]);
|
double w = (synmode == 1) ? 1.0f : window_[i];
|
||||||
|
overlap[i] += static_cast<float>(tmp_buf_[i].real() * w);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < hop_; i++) {
|
for (size_t i = 0; i < hop_; i++) {
|
||||||
out[i] = overlap[i] / wola_norm;
|
out[i] = overlap[i] / wola_norm;
|
||||||
@@ -65,25 +87,230 @@ void SpectralProcessor::istftFrame(std::complex<double>* in, float* out, float*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpectralProcessor::loadWinFreq() {
|
||||||
|
if (win_freq_loaded_) return;
|
||||||
|
win_freq_loaded_ = true;
|
||||||
|
// Try to load WIN_freq from live capture (handoff/rtwin_freq_44100.npy)
|
||||||
|
FILE* f = fopen("handoff/rtwin_freq_44100.npy", "rb");
|
||||||
|
if (!f) {
|
||||||
|
// Fallback: compute periodic Hann, second half (0.5→1.0 rising)
|
||||||
|
win_freq_.resize(nfft_ / 2 + 1);
|
||||||
|
for (size_t i = 0; i <= nfft_ / 2; i++) {
|
||||||
|
win_freq_[i] = static_cast<float>(0.5 * (1.0 - std::cos(2.0 * M_PI * i / nfft_)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Read numpy header
|
||||||
|
char header[128];
|
||||||
|
if (fread(header, 1, 6, f) != 6) { fclose(f); return; }
|
||||||
|
// Skip to data (numpy format: magic + header_len + desc)
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long fsize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
// Simple approach: skip header until '\n' appears, then read raw float32
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
int c;
|
||||||
|
while ((c = fgetc(f)) != '\n' && c != EOF) {}
|
||||||
|
// Read count (should be 8193 for 44100)
|
||||||
|
int32_t count = 0;
|
||||||
|
fread(&count, 4, 1, f);
|
||||||
|
// Actually numpy header is more complex; just read all remaining as float32
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
// Skip to data: find first 'N' (for 'astype') then skip past it
|
||||||
|
fseek(f, 6, SEEK_SET);
|
||||||
|
while ((c = fgetc(f)) != '\n' && c != EOF) {}
|
||||||
|
// Now at data start. Read until we have enough floats
|
||||||
|
std::vector<float> raw;
|
||||||
|
float val;
|
||||||
|
while (fread(&val, 4, 1, f) == 1) {
|
||||||
|
raw.push_back(val);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
if (raw.size() > 0) {
|
||||||
|
win_freq_ = raw;
|
||||||
|
} else {
|
||||||
|
// Fallback
|
||||||
|
win_freq_.resize(nfft_ / 2 + 1);
|
||||||
|
for (size_t i = 0; i <= nfft_ / 2; i++) {
|
||||||
|
win_freq_[i] = static_cast<float>(0.5 * (1.0 - std::cos(2.0 * M_PI * i / nfft_)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpectralProcessor::buildFirFromMask(const float* mask, std::complex<double>* fir, size_t nbin) {
|
||||||
|
// Bit-exact FIR construction pipeline from decompilation (BLOCKMAP 24mm9):
|
||||||
|
// 1. design = ln(mask) → negate
|
||||||
|
// 2. opA = inv-RFFT (th2180) with buf548
|
||||||
|
// 3. fold: bins 1..2047 *= 2.0, bins 2049..4095 = 0
|
||||||
|
// 4. opB = fwd-RFFT (th1a90) with buf548
|
||||||
|
// 5. EXP: complex polynomial exp with q≈0.80 scaling
|
||||||
|
// 6. opC = inv-RFFT (th2180) with buf548
|
||||||
|
// 7. zero Nyquist
|
||||||
|
// 8. window: falling Hann WIN_freq[2048..4095] (w[1024]=0.5, w[2048]=1.0)
|
||||||
|
// 9. opD = fwd-RFFT (th1a90) with buf548
|
||||||
|
// 10. normalize: FIR[0]=1.0, FIR[1]=0.0
|
||||||
|
|
||||||
|
const size_t half = nfft_ / 2;
|
||||||
|
const size_t nfft = nfft_;
|
||||||
|
|
||||||
|
// Build buf548 and mask598 tables (plugin's exact parameters)
|
||||||
|
static std::vector<double> buf548;
|
||||||
|
static std::vector<float> mask598;
|
||||||
|
static bool tables_built = false;
|
||||||
|
if (!tables_built) {
|
||||||
|
buf548.resize(nfft); // N doubles = 2 * N/2 entries
|
||||||
|
mask598.resize(nfft / 4); // N/4 floats
|
||||||
|
fft::build_buf548(buf548.data(), nfft);
|
||||||
|
fft::build_mask598(mask598.data(), nfft);
|
||||||
|
tables_built = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: design = ln(mask) and negate (already in real domain)
|
||||||
|
// Input is real mask [nbin], convert to real array for RFFT
|
||||||
|
std::vector<double> design(nfft, 0.0);
|
||||||
|
for (size_t i = 0; i <= half; i++) {
|
||||||
|
float m = mask[i];
|
||||||
|
if (m > 1e-12f) {
|
||||||
|
float ln_m = soothe2::ln_plugin_f32(m);
|
||||||
|
design[i] = -static_cast<double>(ln_m);
|
||||||
|
} else {
|
||||||
|
design[i] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: opA = inv-RFFT (th2180): design (real) → time domain
|
||||||
|
// But wait: inv-RFFT takes N/2+1 complex → N real
|
||||||
|
// We need to pack design as complex first (im=0)
|
||||||
|
std::vector<std::complex<double>> H(half + 1);
|
||||||
|
for (size_t i = 0; i <= half; i++) {
|
||||||
|
H[i] = std::complex<double>(design[i], 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> time_domain(nfft);
|
||||||
|
fft::execute_real_inverse_exact(&plan_, H.data(), time_domain.data(), buf548.data(), mask598.data());
|
||||||
|
|
||||||
|
// Step 3: fold - from BLOCKMAP: "FIR[n]=0 (n=0x540534=4096!)"
|
||||||
|
// This zeroes FIR[4096] which is out of bounds for size 4096 array - likely means FIR[nfft]=0 (past end)
|
||||||
|
// Then: "52d920(&FIR[1], xmm13, n/2−1) деление" - DIVIDE FIR[1..2047]
|
||||||
|
// "52db50(&FIR[2049], xmm9, n/2−1)" - multiply/zero FIR[2049..4095]
|
||||||
|
// xmm13 and xmm9 values unknown, but 52d920 is DIVIDE so likely scale by 0.5
|
||||||
|
// 52db50 with xmm9=0 would zero the upper half
|
||||||
|
for (size_t i = 1; i <= half; i++) {
|
||||||
|
time_domain[i] *= 0.5; // DIVIDE by 2 (xmm13 = 0.5?)
|
||||||
|
}
|
||||||
|
for (size_t i = half + 1; i < nfft; i++) {
|
||||||
|
time_domain[i] = 0.0; // xmm9 = 0 zeros upper half
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: opB = fwd-RFFT (th1a90): time_domain (real) → complex
|
||||||
|
std::vector<std::complex<double>> freq_domain(half + 1);
|
||||||
|
fft::execute_real_forward_exact(&plan_, time_domain.data(), freq_domain.data(), buf548.data(), mask598.data());
|
||||||
|
|
||||||
|
// Step 5: EXP: complex polynomial exp with q≈0.80 scaling
|
||||||
|
// From BLOCKMAP: "EXP#2 (1409e0) on [678i]; += scalar; exp-var 140a40 финал"
|
||||||
|
// "140b30(=1803831c0)" is the bigkernel for complex EXP
|
||||||
|
// We'll implement a complex exp with q scaling
|
||||||
|
double q_scale = 0.80;
|
||||||
|
for (size_t i = 0; i <= half; i++) {
|
||||||
|
double re = freq_domain[i].real();
|
||||||
|
double im = freq_domain[i].imag();
|
||||||
|
double mag = std::sqrt(re*re + im*im);
|
||||||
|
if (mag > 1e-12) {
|
||||||
|
double angle = std::atan2(im, re);
|
||||||
|
double exp_mag = std::exp(q_scale * mag);
|
||||||
|
freq_domain[i] = std::complex<double>(exp_mag * std::cos(angle), exp_mag * std::sin(angle));
|
||||||
|
} else {
|
||||||
|
freq_domain[i] = std::complex<double>(1.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: opC = inv-RFFT (th2180): freq_domain → time domain
|
||||||
|
std::vector<double> time_domain2(nfft);
|
||||||
|
fft::execute_real_inverse_exact(&plan_, freq_domain.data(), time_domain2.data(), buf548.data(), mask598.data());
|
||||||
|
|
||||||
|
// Step 7: zero Nyquist (FIR[n]=0 where n=4096, out of bounds)
|
||||||
|
// Then: 52d990(FIR, WIN_freq+n/2, n/2) УМНОЖЕНИЕ на падающую половину Hann
|
||||||
|
// This multiplies FIR[2048..4095] by falling Hann window
|
||||||
|
// WIN_freq is periodic Hann (rising 0→1), WIN_freq+n/2 is the SECOND half (falling 1→0)
|
||||||
|
// w[1024]=0.5, w[2048]=1.0 means:
|
||||||
|
// - For i=2048 (offset 0): window = WIN_freq[2048+0] = WIN_freq[2048] = 1.0
|
||||||
|
// - For i=3072 (offset 1024): window = WIN_freq[2048+1024] = WIN_freq[3072] = 0.5
|
||||||
|
// - For i=4095 (offset 2047): window = WIN_freq[2048+2047] = WIN_freq[4095] = 0.0
|
||||||
|
for (size_t i = half; i < nfft; i++) {
|
||||||
|
size_t win_idx = half + (i - half);
|
||||||
|
if (win_idx < win_freq_.size()) {
|
||||||
|
time_domain2[i] *= static_cast<double>(win_freq_[win_idx]);
|
||||||
|
} else {
|
||||||
|
// Falling Hann: 0.5 * (1.0 + cos(2*pi*i/N))
|
||||||
|
double win = 0.5 * (1.0 + std::cos(2.0 * M_PI * (i - half) / nfft));
|
||||||
|
time_domain2[i] *= win;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 9: opD = fwd-RFFT (th1a90): windowed time → final FIR
|
||||||
|
fft::execute_real_forward_exact(&plan_, time_domain2.data(), fir, buf548.data(), mask598.data());
|
||||||
|
|
||||||
|
// Step 10: normalize: FIR[0]=1.0, FIR[1]=0.0
|
||||||
|
fir[0] = std::complex<double>(1.0, 0.0);
|
||||||
|
if (half > 1) {
|
||||||
|
fir[1] = std::complex<double>(0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SpectralProcessor::processBlock(float* in, float* out, size_t num_samples, size_t num_channels) {
|
void SpectralProcessor::processBlock(float* in, float* out, size_t num_samples, size_t num_channels) {
|
||||||
memset(out, 0, num_samples * sizeof(float));
|
memset(out, 0, num_samples * sizeof(float));
|
||||||
if (num_samples == 0 || num_samples < nfft_) {
|
if (num_samples == 0 || num_samples < nfft_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int firconv = []() {
|
||||||
|
const char* e = getenv("RT_FIRCONV");
|
||||||
|
return e ? atoi(e) : 0;
|
||||||
|
}();
|
||||||
|
|
||||||
size_t nframes = (num_samples - nfft_) / hop_ + 1;
|
size_t nframes = (num_samples - nfft_) / hop_ + 1;
|
||||||
|
|
||||||
for (size_t f = 0; f < nframes; f++) {
|
for (size_t f = 0; f < nframes; f++) {
|
||||||
size_t offset = f * hop_;
|
size_t offset = f * hop_;
|
||||||
if (offset + nfft_ > num_samples) break;
|
if (offset + nfft_ > num_samples) break;
|
||||||
stftFrame(in + offset, buf_);
|
stftFrame(in + offset, buf_.data());
|
||||||
|
|
||||||
detector_.processFrame(buf_, mask_.data());
|
detector_.processFrame(buf_.data(), mask_.data());
|
||||||
|
|
||||||
for (size_t i = 0; i < nfft_; i++) {
|
if (firconv == 3) {
|
||||||
buf_[i] *= mask_[i];
|
// RT_FIRCONV=3 (NOTES 24k): plugin application law decoded live:
|
||||||
|
// applied_gain = 1.019 * V^1.8345 per bin (rms 0.0025 dB over
|
||||||
|
// 8 drive levels). V = band curve (detector output); here M.
|
||||||
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
|
double m = std::max(static_cast<double>(mask_[i]), 1e-12);
|
||||||
|
double a = 1.019 * std::pow(m, 1.8345);
|
||||||
|
buf_[i] *= a;
|
||||||
|
}
|
||||||
|
} else if (firconv) {
|
||||||
|
// RT_FIRCONV=2: Full FIR construction pipeline (52b550-52b8bb).
|
||||||
|
// mask → reciprocal (1/mask) → window → normalize → complex multiply.
|
||||||
|
// This replicates the plugin's FFT-conv FIR design path.
|
||||||
|
buildFirFromMask(mask_.data(), fir_freq_.data(), nfft_);
|
||||||
|
// Complex multiply FIR × audio spectrum
|
||||||
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
|
buf_[i] *= fir_freq_[i];
|
||||||
|
}
|
||||||
|
} else if (firconv == 1) {
|
||||||
|
// RT_FIRCONV=1: Simple frequency-domain mask multiply (legacy).
|
||||||
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
|
fir_freq_[i] = std::complex<double>(
|
||||||
|
static_cast<double>(mask_[i % (nfft_/2+1)]), 0.0);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
|
buf_[i] *= fir_freq_[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default path: simple frequency-domain mask multiply.
|
||||||
|
for (size_t i = 0; i < nfft_; i++) {
|
||||||
|
buf_[i] *= mask_[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
istftFrame(buf_, out + offset, overlap_.data());
|
istftFrame(buf_.data(), out + offset, overlap_.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-3
@@ -21,10 +21,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
size_t nfft_;
|
size_t nfft_;
|
||||||
size_t hop_;
|
size_t hop_;
|
||||||
double* window_;
|
std::vector<double> window_;
|
||||||
FFTPlan plan_;
|
FFTPlan plan_;
|
||||||
std::complex<double>* buf_;
|
std::vector<std::complex<double>> buf_;
|
||||||
std::complex<double>* tmp_buf_;
|
std::vector<std::complex<double>> tmp_buf_;
|
||||||
|
std::vector<std::complex<double>> fir_buf_;
|
||||||
|
std::vector<std::complex<double>> fir_freq_;
|
||||||
|
std::vector<double> fir_window_;
|
||||||
std::vector<float> overlap_;
|
std::vector<float> overlap_;
|
||||||
std::vector<float> mask_;
|
std::vector<float> mask_;
|
||||||
FramedDetector detector_;
|
FramedDetector detector_;
|
||||||
@@ -34,4 +37,14 @@ private:
|
|||||||
void computeWindow();
|
void computeWindow();
|
||||||
void stftFrame(const float* in, std::complex<double>* out);
|
void stftFrame(const float* in, std::complex<double>* out);
|
||||||
void istftFrame(std::complex<double>* in, float* out, float* overlap);
|
void istftFrame(std::complex<double>* in, float* out, float* overlap);
|
||||||
|
|
||||||
|
// FIR construction from detector mask (52b550-52b8bb pipeline):
|
||||||
|
// mask → log → sign-invert → EXP → twiddle ops → window → normalize
|
||||||
|
// Produces frequency-domain FIR kernel for complex multiply application.
|
||||||
|
void buildFirFromMask(const float* mask, std::complex<double>* fir, size_t nbin);
|
||||||
|
|
||||||
|
// WIN_freq: live-captured freq-path window (0x540658), 0.5→1.0
|
||||||
|
std::vector<float> win_freq_;
|
||||||
|
bool win_freq_loaded_ = false;
|
||||||
|
void loadWinFreq();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
#include "framed_model.hpp"
|
||||||
|
|
||||||
|
// Unit check for the VLAW detector law (BLOCKMAP:314 softplus proxy):
|
||||||
|
// cut = alpha * ln1p(lvl/beta) + c [+ delta]
|
||||||
|
// mask = 10^(-cut/20)
|
||||||
|
// Reference values hand-computed from the dual-calibrated constants
|
||||||
|
// (alpha=3.2193, beta=0.4927, c=0.5423, delta=6.9177 — README.md:26).
|
||||||
|
int main() {
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
// --- law monotonicity: higher level -> stronger cut -> smaller mask ---
|
||||||
|
double m0 = vlaw_mask(0.01, 3.2193, 0.4927, 0.5423, 0.0);
|
||||||
|
double m1 = vlaw_mask(1.0, 3.2193, 0.4927, 0.5423, 0.0);
|
||||||
|
double m2 = vlaw_mask(10.0, 3.2193, 0.4927, 0.5423, 0.0);
|
||||||
|
bool mono = (m0 > m1) && (m1 > m2);
|
||||||
|
std::printf("vlaw monotonic: m(0.01)=%.4f m(1)=%.4f m(10)=%.4f (%s)\n",
|
||||||
|
m0, m1, m2, mono ? "OK" : "MISMATCH");
|
||||||
|
if (!mono) fail = 1;
|
||||||
|
|
||||||
|
// --- zero level: cut = c => mask = 10^(-c/20) ---
|
||||||
|
double mz = vlaw_mask(0.0, 3.2193, 0.4927, 0.5423, 0.0);
|
||||||
|
double ez = std::pow(10.0, -0.5423 / 20.0);
|
||||||
|
bool zok = std::fabs(mz - ez) < 1e-9;
|
||||||
|
std::printf("vlaw zero-level: mask=%.6f expect=%.6f (%s)\n",
|
||||||
|
mz, ez, zok ? "OK" : "MISMATCH");
|
||||||
|
if (!zok) fail = 1;
|
||||||
|
|
||||||
|
// --- delta branch adds cut -> deeper mask ---
|
||||||
|
double md = vlaw_mask(1.0, 3.2193, 0.4927, 0.5423, 6.9177);
|
||||||
|
bool dok = md < m1;
|
||||||
|
std::printf("vlaw delta: mask+delta=%.4f < %.4f (%s)\n",
|
||||||
|
md, m1, dok ? "OK" : "MISMATCH");
|
||||||
|
if (!dok) fail = 1;
|
||||||
|
|
||||||
|
// --- numeric reference: lvl=1.0, dual params ---
|
||||||
|
// cut = 3.2193 * ln(1 + 1/0.4927) + 0.5423
|
||||||
|
double cut_ref = 3.2193 * std::log1p(1.0 / 0.4927) + 0.5423;
|
||||||
|
double mref = std::pow(10.0, -cut_ref / 20.0);
|
||||||
|
bool rok = std::fabs(m1 - mref) < 1e-9;
|
||||||
|
std::printf("vlaw ref: mask=%.6f expect=%.6f cut=%.4f (%s)\n",
|
||||||
|
m1, mref, cut_ref, rok ? "OK" : "MISMATCH");
|
||||||
|
if (!rok) fail = 1;
|
||||||
|
|
||||||
|
// --- comb neutrality: alpha=0.05 beta=5.0 c=0 -> mask ~ 1 for lvl=0 ---
|
||||||
|
double mc = vlaw_mask(0.0, 0.05, 5.0, 0.0, 0.0);
|
||||||
|
bool cok = std::fabs(mc - 1.0) < 1e-9;
|
||||||
|
std::printf("vlaw comb-neutral: mask(0)=%.6f expect=1.0 (%s)\n",
|
||||||
|
mc, cok ? "OK" : "MISMATCH");
|
||||||
|
if (!cok) fail = 1;
|
||||||
|
|
||||||
|
std::printf("vlaw_check %s\n", fail ? "FAIL" : "PASS");
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
@@ -0,0 +1,792 @@
|
|||||||
|
# BLOCK MAP: FUN_180529fe0 (полная разметка по raw asm, 22v)
|
||||||
|
|
||||||
|
Источник: `handoff/nls_dasm/f529fe0_full.dis` (soothe_mem.bin, база 0x180000000,
|
||||||
|
диапазон 529c60–52ba00; старый f529fe0.dis был ОБРЕЗАН на 52a813).
|
||||||
|
Регистры: r13 = индекс полосы, r12 = state-ptr ctx+0x440518, esi/r8d = nbin,
|
||||||
|
rbx = nbands ([rsp+0x30]), rdi = ctx. Флаг 0x5408b8 выбирает float/double
|
||||||
|
вариант thunk-операций (семантика пар идентична).
|
||||||
|
|
||||||
|
## Исправление адресации (главное)
|
||||||
|
«Скалярные» буферы NOTES 21b — МАССИВЫ пер-полосных векторов (шаг 16 байт):
|
||||||
|
- bands[i] = [ctx+0x540678+i·16] — кривые полос
|
||||||
|
- acc[i] = [ctx+0x5407c8+i·16] — ПЕР-ПОЛОСНЫЙ аккумулятор combine
|
||||||
|
- track[i] = [ctx+0x540768+i·16] — трек основного цикла
|
||||||
|
|
||||||
|
## Thunk-таблица (ILT-стаб 180001xxx -> jmp [table + idx*8], idx @1826159a0)
|
||||||
|
Резолв при live idx=4; семантика из тел + NOTES 21b:
|
||||||
|
| стаб float | стаб double | impl | op |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 0x180001f10 | 0x180001c70 | dc40 / **8d60** | dst = B − A (sub) |
|
||||||
|
| 0x180001fa0 | 0x180001940 | ee20→487a0 / **3c40** | fma att/rel половин |
|
||||||
|
| 0x180001d60 | —(встречен в 1b80-паре?) | — / **5a20** | dst += B (add) |
|
||||||
|
| 0x180001970 | — | **3f40** | sub (float) |
|
||||||
|
| 0x1800019a0 | — | **4200** | mul scalar? (делегирует 181a63fe0) |
|
||||||
|
| 0x180001a00 | — | **4720** | mul |
|
||||||
|
| 0x180001a60 | — | **5160** | sub DOUBLE |
|
||||||
|
| 0x180001850 | — | **25e0** | add DOUBLE |
|
||||||
|
| 0x180002000 | 0x180001c40 | ? | axpy-класс (band ⊕ track) |
|
||||||
|
| 0x180002060 | 0x180002120 | 10860/11940 | transform со скаляром xmm3 |
|
||||||
|
| 0x180002270 | 0x1800022a0 | 14c40/15060 | transform со скаляром xmm0/xmm1 |
|
||||||
|
| 0x180002030 | 0x180001d30 | ? | transform float/double |
|
||||||
|
| in-place кернелы | | 140950/140980/1409b0/1409e0/140a40/140ad0/140b00/140b60 | bigkernel-семейство (exp2/mask) |
|
||||||
|
|
||||||
|
## Карта блоков
|
||||||
|
|
||||||
|
### Пролог
|
||||||
|
- `52a03a–52a396`: PRNG-пролог. LCG state @ctx+0x2404e0, шаги +0x3cdca,
|
||||||
|
+0x140236, +0x10d56, +0xdf6b6, +0xa8c5e, +0x72916; LUT ptr @ctx+0x5408b0;
|
||||||
|
константы 262b704/262b5c8(int)/262b700(int)/24c3c58. Результат: индексы
|
||||||
|
iVar7/iVar8 ([rsp+0x130]/счётчики) и стартовая позиция цикла.
|
||||||
|
Live-поведение залочено раньше (fVar30=1).
|
||||||
|
- `52a397`: r14 = &bands[0].
|
||||||
|
|
||||||
|
### Pre-combine #1 (52a397–52a421)
|
||||||
|
- `52dbc0(f6f8_data, bands[0], nbin)` — copy bands[0] → f6f8.
|
||||||
|
- цикл i=1..nbands−1 по массиву @0x540688 (= &bands[1]!): transform(f6f8, bands[i])
|
||||||
|
через 20f0(float)/1850(double=25e0 ADD). ⇒ **f6f8 = Σ_{i≥1} bands[i]** (или min/max
|
||||||
|
— точный op 20f0 не залочен, кандидат ADD по double-паре!).
|
||||||
|
|
||||||
|
### Нормировка + mix-вес (52a421–52a458)
|
||||||
|
- `52d920(f6f8, xmm11/[rsp+0x148](int), nbin)` — f6f8 /= K.
|
||||||
|
- `xmm7 = powf([ctx+0x2c], xmm13)` — mix^p.
|
||||||
|
|
||||||
|
### Pre-combine #2 (52a45e–52a4f6) по всем полосам
|
||||||
|
- band[i] *= (xmm11 − xmm7) [thunk 2030/1d30]
|
||||||
|
- f6f8 ⊕= ... с весом xmm7 [thunk 1fd0/2150]
|
||||||
|
⇒ взвешенное смешение кривых полос ДО основного цикла.
|
||||||
|
|
||||||
|
### Основной цикл по полосам (52a4fb..52b3c7, тело с 52a580)
|
||||||
|
На каждую полосу i (r13):
|
||||||
|
1. `scale` (52a583–52a5c4): s = xmm11·[0x540870]; если флаг 0x5408b8:
|
||||||
|
s ← exp(PRNG)-ветка; затем ·[0x54088c]. Читается track_i (@0x540768[i]) в [rsp+0x40].
|
||||||
|
2. `transform(bands[i], s)` (52a5cc–52a607): bands[i] *= s [2030/1d30].
|
||||||
|
3. флаг-оп (52a608–52a645): transform(bands[i], bands[i]) через 140980/1409b0
|
||||||
|
(bigkernel, вероятно exp2-кернел) — только при флаге.
|
||||||
|
4. `52d650(state@0x440518, f6f8, bands[i], nbin)` (52a646–52a658) — БИДИР-IIR #1:
|
||||||
|
band → f6f8 (double, коэф down@0x440528/up@0x4c0528, acc@0x540528, reset внутри).
|
||||||
|
5. Инлайн БИДИР-IIR #2 (52a65d–52a85a, только при флаге; иначе прыжок на 6):
|
||||||
|
вход f6f8 → выход bands[i] (тот же state 0x440518, reset отдельно).
|
||||||
|
6. `transform(f6f8 ⊕ bands[i])` (52a85b–52a896) [1970/1a60 = 3f40 SUB float /
|
||||||
|
5160 SUB double]: f6f8 −= bands[i]? (аргументы rcx=f6f8, rdx=band).
|
||||||
|
7. **БИДИР-IIR #3** (52a897–52aa6e, инлайн): state base 0x3404f8
|
||||||
|
(down@0x2404f8, up@0x2c04f8, acc@0x3404f8), длина из поля ctx+0x2404e8,
|
||||||
|
IN-PLACE по bands[i]. Reset каждый вызов.
|
||||||
|
8. `op(bands[i], скаляр xmm9=0?)` (52aa6e–52aaaf) [2060/2120].
|
||||||
|
9. Ветка флага==0 (52aabc–52ab90):
|
||||||
|
- op(vec@0x540698, f6f8, scalar=(конст1 − [0x54087c])) [1a00/1be0 = mul]
|
||||||
|
- op(f6f8, scalar=[0x54087c]·xmm10) [2270/22a0]
|
||||||
|
- bigkernel `140b60/140950(bands[i], f6f8, bands[i], nbin)`
|
||||||
|
10. **COMBINE (52ab90–52abd7)**: acc_i = [0x5407c8+i·16];
|
||||||
|
`op(rcx=acc_i, rdx=bands[i], r8=f6f8, r9=nbin)` [1f10→dc40 float-SUB /
|
||||||
|
1c70→8d60 double-SUB]: **f6f8 = bands[i] − acc_i** ✓ NOTES 21b.
|
||||||
|
11. Половины f6f8 + fma (52abd7–52ad04):
|
||||||
|
- rbp = f6f8 + nbin (верхняя половина); op(f6f8_low?, ...) [2060/2120, scalar 0]
|
||||||
|
- op(rcx=f6f8_upper, rdx=vec@0x5406c8, r8=acc_i) [1fa0/1940 = **3c40 fma**]
|
||||||
|
- op(rcx=f6f8_lower, rdx=vec@0x5406e8, r8=acc_i) [1fa0/1940]
|
||||||
|
⇒ fma с коэф-массивами 0x5406c8 (upper/att) и 0x5406e8 (lower/rel) ✓.
|
||||||
|
12. `acc_i += bands[i]` (52acf4–52ad03) [1b80→6840 / 1d60→**5a20** add] ✓.
|
||||||
|
13. Ветка флага!=0 (52ad04–52ae57): повтор п.9 с теми же адресами
|
||||||
|
(0x540698, f6f8-blend, bigkernel 1409e0/140ad0).
|
||||||
|
14. `op(bands[i], скаляры 0x1824c4680 / xmm13)` (52ae58–52ae56 хвост).
|
||||||
|
15. `bands[i] ⊕= track_i [rsp+0x40]` (52ae74–52ae8a) [2000/1c40].
|
||||||
|
16. `op(vec@0x5406a8, bands[i])` (52ae8f–52aecа) [2000/1c40].
|
||||||
|
17. флаг → bigkernel in-place 140980/1409b0 (52aecb–52af08).
|
||||||
|
18. **БИДИР-IIR #4 ×2** (52af09–52b2af и повтор 52b0de–52b2af):
|
||||||
|
state base 0x440510 (down@0x340510, up@0x3c0510, acc@0x440510),
|
||||||
|
длина ctx+0x340500, in-place bands[i]. ДВА прохода подряд (каждый с reset).
|
||||||
|
19. Финальные scale/op полосы (52b2af–52b3af):
|
||||||
|
- скаляр `[ctx+0x1c+i·4] · xmm14` → transform [2030/1d30]
|
||||||
|
- bigkernel in-place 1409e0/140ad0 (при флаге) ИЛИ
|
||||||
|
op(scalar=xmm12/xmm8) [2270/22a0] + `140a40/140b00(band,band,scalar)`
|
||||||
|
20. Инкремент i (52b3af–52b3c7), выход при i ≥ nbands.
|
||||||
|
|
||||||
|
### Эпилог (52b3cd–52b935)
|
||||||
|
- GUI-snapshot блок (флаг 0x2404bc): копии векторов @0x540728 ← bands[i]
|
||||||
|
(совпадает с consumers_out.txt:144-183).
|
||||||
|
- Остаток до 52b935: восстановить при транскрипции (вероятно dry/wet + mirror).
|
||||||
|
|
||||||
|
## Состояния бидир-IIR (4 базы, лейаут {down,+0x80000 up,+0x80010/+0x100010 acc})
|
||||||
|
| # | база | downCoef | upCoef | acc | длина |
|
||||||
|
|---|------|----------|--------|-----|-------|
|
||||||
|
| 1–2 (52d650+инлайн) | 0x440518 | +0x10 | +0x80010 | +0x100010 | nbin |
|
||||||
|
| 3 | 0x2c04f8-группа | 0x2404f8 | 0x2c04f8 | 0x3404f8 | поле ctx+0x2404e8 |
|
||||||
|
| 4 ×2 | 0x340510/0x3c0510 | 0x340510 | 0x3c0510 | 0x440510 | поле ctx+0x340500 |
|
||||||
|
|
||||||
|
## Открытые вопросы к транскрипции
|
||||||
|
1. Точный op thunk 20f0/1850 в pre-combine #1 (ADD — кандидат).
|
||||||
|
2. Аргументный порядок 2000/1c40 (axpy) и 2060/2120.
|
||||||
|
3. Семантика bigkernel-семейства 1409xx/140axx/140bxx (какой где: exp2(-x),
|
||||||
|
exp2(-x)·blend, mirror?).
|
||||||
|
4. Что пишется в track_i @0x540768[i] (writer вне метода — искать отдельной охотой;
|
||||||
|
кандидат FUN_18052e9b0).
|
||||||
|
5. Эпилог 52b4eb–52b935.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 22v: декод генератора коэффициентов
|
||||||
|
|
||||||
|
### FUN_180530b60 (апдейт параметров, size=462)
|
||||||
|
- fVar2 = powf([ctx+0x540878], K1); fVar3 = powf([ctx+0x54087c], K2)
|
||||||
|
(оба live = 0.5 — параметры attack/release-класса);
|
||||||
|
- лог-интерполяция констант → [ctx+0x540894] и [ctx+0x540898] (тау-скаляры);
|
||||||
|
- вызовы генератора на все три состояния:
|
||||||
|
- 180533340(ctx+0x2404e8, tau=0x540894-ветка, C=DAT_1824c459c, sr, ...)
|
||||||
|
- 180533340(ctx+0x340500, tau=[0x540894], ...)
|
||||||
|
- 180533340(ctx+0x440518, tau=[0x540898], ..., mult=DAT_1824c4564)
|
||||||
|
|
||||||
|
### FUN_180533340(state, tau, C, sr, p, mult) — ГЕНЕРАТОР КОЭФ. БИДИР-IIR
|
||||||
|
```
|
||||||
|
sr' = sr · DAT_1824c3d8c
|
||||||
|
acc = 0; downCoef[0] = 1.0 (int-пара {0, 0x3ff00000} @+0x10)
|
||||||
|
n = state[0]; fc_norm = (C/sr')·n
|
||||||
|
for i in 1..n-1:
|
||||||
|
g = (i <= fc_norm) ? fc_norm/i : powf(fc_norm/i, p) # частотный варп!
|
||||||
|
c = 1/(g·tau/mult + 1)
|
||||||
|
up[i] = exp(c·g·tau/state[2] · DAT_1824c46b8)
|
||||||
|
down[i] = 1 − up[i]
|
||||||
|
```
|
||||||
|
ЛЕЙАУТ СОСТОЯНИЯ СОШЁССЯ: downCoef[] @base+0x10, upCoef[] @base+0x80010
|
||||||
|
(pdVar6[0x10000] = +0x80000 байт), acc — скаляр в хвосте. Это ЧАСТОТНО-
|
||||||
|
ЗАВИСИМОЕ сглаживание маски: сила растёт к низким бинам (1/i) с питч-законом
|
||||||
|
выше кросса. Вот где «размазывание» нотча в реале — НЕ плоский IIR, который
|
||||||
|
мы отвергли офлайн (22u), а пер-биновый варп!
|
||||||
|
|
||||||
|
### Статус сбора (решение: без коммитов до первой валидации)
|
||||||
|
Незакоммичено: f529fe0_full.dis, BLOCKMAP_529fe0.md, thunk-резолвер,
|
||||||
|
правки NOTES_LEVEL (22v будет добавлена при транскрипции). Следующий шаг —
|
||||||
|
транскрипция process_frame_faithful по этой карте.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 23b: резолв ILT + декод FIR-цикла (52b550–52b8bb)
|
||||||
|
|
||||||
|
### Резолвер
|
||||||
|
`scripts/ilt_resolve.py`: стаб = `mov eax,[rip+idx]; lea r10,[rip+tbl]; jmp [r10+rax*8]`;
|
||||||
|
live idx=4 у всех; таблицы @0x1826xxxx. Полная таблица «стаб→impl»:
|
||||||
|
| стаб | impl | роль (уточн.) |
|
||||||
|
|---|---|---|
|
||||||
|
| 0x180002210 | 0x136e0 | copy scratch→FIR? (валидатор → 39de0/3a040/3a220) |
|
||||||
|
| 0x180002180 | 0x125e0 | complex-op A/C float (воркер 3a4a0; r9 НЕ передаётся ⇒ длина из объекта) |
|
||||||
|
| 0x180001bb0 | 0x6a40 | complex-op A/C double |
|
||||||
|
| 0x180001a90 | 0x5560 | complex-op B/D float — ЧИСТЫЙ FMA (fma=28, mul/add=0!) |
|
||||||
|
| 0x1800019d0 | 0x4340 | complex-op B/D double |
|
||||||
|
| 0x180001880 | 0x2c60 | paired-scalar op 1 (флаг-ветка) |
|
||||||
|
| 0x180001ca0 | 0x9380 | paired-scalar op 2 |
|
||||||
|
| 0x180001df0 | 0xb3c0 | final op float |
|
||||||
|
| 0x180001f70 | 0xe360 | final op double |
|
||||||
|
| 0x180140a10 | 0x141400→[1826181d8]=**0x1802a24c0** | дизайн float |
|
||||||
|
| 0x180140a70 | 0x141580→[1826183c8]=**0x1802fa420** | дизайн double |
|
||||||
|
| 2030/1d30 | ffe0 / 9be0 | scalar-transform |
|
||||||
|
| 2270/22a0 | 14c40 / 15060 | scalar-transform-2 |
|
||||||
|
| 2000/1c40 | fb60 / 8700 | axpy-класс |
|
||||||
|
| остальное | dc40,8d60,5a20,4720,4200,3f40,5160,25e0,ee20,3c40 | как в BLOCKMAP ✓ |
|
||||||
|
|
||||||
|
### Дизайн-тело 0x1802a24c0 (float, вход=rcx bands[i], выход=rdx scratch@0x540628)
|
||||||
|
Функция входа 0..0x675 (~1.6КБ), далее сиблинги. Алгоритм = **ВЕКТОРНЫЙ LOG2**
|
||||||
|
(range reduction бит-трюком с 0.6666667, полином Хорнера на YMM-константах
|
||||||
|
−0.5, 0.3333656, −0.2500466, 0.198225, −0.1646246, 0.1696488, −0.1517721,
|
||||||
|
реконструкция ×ln2=0.6931472; константы @FN+0x1cdfac0..d20). Маскированный
|
||||||
|
хвост через vmaskmovps/popcnt-таблицы (@0x181F8xxxx). ИТОГ: **scratch = log(bands[i])**.
|
||||||
|
Диспетчер 535a70 делает swap аргументов (rcx↔rdx) перед прыжком!
|
||||||
|
|
||||||
|
### Структура FIR-цикла (полностью залочена)
|
||||||
|
На полосу: 2×scalar-transform(bands[i], xmm8·[540888] при флаге 540890==0, иначе
|
||||||
|
xmm8; затем xmm12−xmm8) [ср. decomp 184-185: A=[0x540874]−expf(K), wet=[0x540888]]
|
||||||
|
→ DESIGN=log(bands[i])→scratch → copy(scratch→FIR@0x540668) [2210]
|
||||||
|
→ opA(FIR,buf548,buf598) [2180/1bb0] → FIR[n]=0 (n=0x540534=4096!)
|
||||||
|
→ 52d920(&FIR[1], xmm13, n/2−1) деление; 52db50(&FIR[2049], xmm9, n/2−1)
|
||||||
|
→ opB [1a90/19d0] → bigkernel in-place 140b30/140aa0 (n/2+1)
|
||||||
|
→ opC [2180/1bb0] → FIR[n]=0 → 52d990(FIR, WIN_freq+n/2, n/2) УМНОЖЕНИЕ на
|
||||||
|
**падающую половину Hann** (WIN_freq=wperiodicHann4096: w[1024]=0.5,w[2048]=1.0!)
|
||||||
|
→ 52db50(&FIR[2048], xmm9, n/2) → opD [1a90/19d0] → FIR[0]=1.0, FIR[1]=0
|
||||||
|
→ флаг-ветка: парно-скалярные 1880/ca0 → dry/wet scale ×[0x540888] над **2n флоатов**
|
||||||
|
→ final(df0/f70) с сохранённым track_i ([rsp+0x138][i]).
|
||||||
|
|
||||||
|
### Живые буферы (firtrace.py по s1/s2 + свежие захваты dual_b1q_0.5)
|
||||||
|
- 0x540658 WIN_freq = периодический Hann(4096), пик 1.0 @bin2048 (НЕ рамп 0.5→0.8!)
|
||||||
|
- 0x540758 freqaxis = линейный, шаг ≈11.713 Гц (≈48000/4096=11.71875, лёгкое
|
||||||
|
занижение — похоже на накопительную ошибку f32 при построении суммой)
|
||||||
|
- 0x540598 = SIMD-маски: 8×1.0 / 8×0.0 периодом 16 флоатов, ровно 1024 шт
|
||||||
|
(YMM lane-select для комплексных кернелов)
|
||||||
|
- 0x540550 NULL (float-путь активен, флаг 0x5408b8=0); 0x540628 scratch нулевой
|
||||||
|
между колбеками; 0x540668 FIR = комплексная единица (1,0)×2049 бинов МЕЖДУ
|
||||||
|
колбеками — кернел потребляется и сбрасывается внутри колбека
|
||||||
|
- 0x5406f8 хранит аудио-масштабный сигнал (~±0.1) между колбеками — overlap/STFT
|
||||||
|
буфер conv-движка (не «нулевой acc»!)
|
||||||
|
- 0x540788 vs 0x5407f8 различаются (max 1.108; @171: 1.751 vs 1.409) — две РАЗНЫЕ
|
||||||
|
кривые; ни одна не достигает нужных 3.89×@171 (11.82 дБ) ⇒ применённый фильтр
|
||||||
|
≠ поточечная копия любой из живых кривых (подтверждение разрыва 22y)
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24l: точный декод FIR-цикла по дизасму (52b550–52b8bb) + константы
|
||||||
|
|
||||||
|
Инструмент: scripts/disasm.py (capstone, base 0x180000000 над soothe_mem.bin).
|
||||||
|
ILT-резолв bigkernel-стабов даёт impl в 141100..141940 — все они IAT-thunk
|
||||||
|
массивы (`mov rax,[rip+..]; jmp rax`) вне дампа ⇒ тела за импортами; НО
|
||||||
|
последовательность и КОНСТАНТЫ цикла видны полностью:
|
||||||
|
|
||||||
|
### Пошагово (float-ветка, флаг 0x540890==0):
|
||||||
|
```
|
||||||
|
52b5a3: xmm6 = xmm8 · [ctx+0x540888]
|
||||||
|
52b5bf: th2030(bands[i], xmm6, n) ; bands *= 1.0·s888 (xmm8=double 1.0!)
|
||||||
|
52b5f6: th2270(bands[i], xmm7, n) ; scalar-transform-2
|
||||||
|
52b62f: 535a70(scratch@540628, bands[i], n/2+1) ; DESIGN: swap→scratch=log(bands)
|
||||||
|
52b644: th2210(rcx=scratch, rdx=0, r8=FIR, r9=n/2+1) ; копия (+упаковка?)
|
||||||
|
52b696: FIR[n]=0 ; float-индекс n=4096
|
||||||
|
52b69e: 52d920(&FIR[1], xmm13=-1.0, n/2-1) ; РАЗДЕЛЕНИЕ НА −1 ⇒ ИНВЕРСИЯ ЗНАКА бинов 1..n/2!
|
||||||
|
52b6ab: 52db50(&FIR[n/2+1], xmm9=0, n/2-1) ; ОБНУЛЕНИЕ верхней половины до exp!
|
||||||
|
52b6e1: opB = th1a90(FIR, buf548, mask598, n/2-1) ; FMA-complex (twiddle!)
|
||||||
|
52b716: BIGKERNEL 140b30(FIR, FIR, n/2+1) ; EXP in-place (тело за IAT)
|
||||||
|
52b74b: opC = th2180(FIR, buf548, mask598, n/2-1)
|
||||||
|
52b76d: FIR[n]=0
|
||||||
|
52b77c: 52d990(FIR, WINfreq+n/2, n/2) ; окно: th2000-класс (FMA axpy!)
|
||||||
|
52b78c: 52db50(&FIR[n/2], xmm9=0, n/2) ; верхняя половина ×0 снова
|
||||||
|
52b7ba: opD = th1a90(...)
|
||||||
|
52b7d4: FIR[0]=1.0f; FIR[1]=0
|
||||||
|
если флаг f890!=0:
|
||||||
|
52b803: th1880(FIR, {xmm10,xmm9}, n) ; парно-скалярный с (−1|0.8, 0)
|
||||||
|
52b81f: th1ca0(FIR, {xmm12,xmm9}, n) ; парно-скалярный с (1.0, 0)
|
||||||
|
52b857: th2030(FIR, s888, 2n) ; ×wet (=1 live)
|
||||||
|
52b893: final df0(FIR, track_i, n)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Константы (статические значения из дампа):
|
||||||
|
| reg | адрес | значение | роль |
|
||||||
|
|-----|-------|----------|------|
|
||||||
|
| xmm8 | 1824c4140 (double) | 1.0 | множитель шага 1 = NO-OP при s888=1 |
|
||||||
|
| xmm13 | 1824c46a0 (double) | −1.0 | делитель бинов 1..n/2 (ИНВЕРСИЯ!) |
|
||||||
|
| xmm9 | xorps | 0.0 | обнуление верхних половин |
|
||||||
|
| xmm10 | 1824c4680 / 1824c3e28 | −1.0 / **0.8** | парный скаляр (0.8 активен в цикле) |
|
||||||
|
| xmm12 | 1824c3ea4 | 1.0 | парный скаляр 2 |
|
||||||
|
| xmm14 | 1824c4674 | −0.7 | (использование вне FIR) |
|
||||||
|
| xmm15 | 1824c4670 | −0.5 | (использование вне FIR) |
|
||||||
|
| xmm7 | вычисл. | sens·[540870]-цепочка | аргумент th2270 |
|
||||||
|
|
||||||
|
### СЛЕДСТВИЯ (меняют понимание построения кернела):
|
||||||
|
1. Перед EXP: бины 1..n/2 = −log(bands) ⇒ после exp = **1/bands** (обратная
|
||||||
|
величина!), бины выше = exp(0)=1. Экспонента применяется НЕ к спектру маски
|
||||||
|
напрямую — вокруг неё twiddle-FMA стадии (ops B/C/D = радиальные проходы
|
||||||
|
FFT-класса над упакованным вещественным спектром; buf548 = cos/sin таблица,
|
||||||
|
mask598 = lane-select).
|
||||||
|
2. «×0.984» из 23d НЕ найден как константа цикла — либо внутри exp-IAT-обёртки,
|
||||||
|
либо следствие нормировки twiddle-стадий. Требуется численная репликация
|
||||||
|
пайплайна против живых захватов (rendersnap2 ph*.npz содержат готовые пары).
|
||||||
|
3. Шаг 1 и шаг ×s888 — no-op при дефолтных параметрах (s888=1, live 24j).
|
||||||
|
4. 52d920/52d990/52db50 = тонкие обёртки: 920→th2030/1d30 (scalar-op),
|
||||||
|
990→th2000/1c40 (axpy!), db50→то же. «УМНОЖЕНИЕ на окно» реализовано
|
||||||
|
axpy-кернелом, «деление» — scalar-op.
|
||||||
|
5. Ops A–D работают над дескрипторными векторами (проверка тега [obj]==6 в
|
||||||
|
4ca80; ошибка 0xfffffff3 при несоответствии).
|
||||||
|
|
||||||
|
### Динамика (попытки перехвата; окружение)
|
||||||
|
- realtime-playback НЕ тикает DSP (нет аудио-девайса; треды спят в futex);
|
||||||
|
кривые 22y = результат инициализации при загрузке проекта
|
||||||
|
- `-renderproject`: полный цикл init+render+exit хоста занимает ~0.8 c
|
||||||
|
(host@1.2s, workers@1.4s, wav@1.6s, exit@2.0s); обработка идёт в окне ~0.5 c
|
||||||
|
- INT3-ptrace: SEIZE+TRACECLONE обязательны до CONT (иначе untraced thread
|
||||||
|
ловит SIGTRAP и убивает процесс — подтверждено); DR-брейкпоинты: DR0@user+0x380,
|
||||||
|
но POKEUSER DR7 даёт EIO. Скрипты: fntrace*.py, fnhw.py, fnall.py, firstop.py,
|
||||||
|
firtrace.py, hotips.py (см. scripts/)
|
||||||
|
- Вывод: FUN_180529fe0 и 52d650/536300/52e260 НЕ ловятся в рендер-окне —
|
||||||
|
маск-цепь выполняется при ЗАГРУЗКЕ/изменении параметров, стационарный рендер
|
||||||
|
использует закэшированный кернел; либо трассировать надо момент инициализации
|
||||||
|
- Выход плагина НЕдетерминирован: md5 двух свободных рендеров различен при
|
||||||
|
одинаковом rms (PRNG-дизеринг из LCG-прологов) — метрика только спектральная!
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24t: ТОЧНЫЕ СЕМАНТИКИ СКАЛЯР-ОПОВ + опознание 563a60
|
||||||
|
|
||||||
|
Инструмент: scripts/disasm_func.py (полный дизасм функции с резолвом
|
||||||
|
RIP-констант инлайн).
|
||||||
|
|
||||||
|
### Тела скаляр-трансформов (float-ветки):
|
||||||
|
| стаб/impl | семантика | спец-случаи |
|
||||||
|
|---|---|---|
|
||||||
|
| th2030→ffe0 | dst[i] *= scalar | scalar==1 → скип; ==0 → zero-fill |
|
||||||
|
| th2270→14c40 | dst[i] += scalar | scalar==0 → скип |
|
||||||
|
Оба: скалярный хвост + AVX2 основной цикл (vmulps/vaddps ymm). Двойные
|
||||||
|
ветки (1d30/22a0) аналогичны на sd/pd.
|
||||||
|
|
||||||
|
### FUN_180563a60 = init-time ПОСТРОИТЕЛЬ таблицы (не per-bin!)
|
||||||
|
- Один caller: 1805636c2 (init-семейство).
|
||||||
|
- Константы пролога: xmm11=8.68589 (20/ln10 — ln⇒дБ!), xmm12=1/1024,
|
||||||
|
xmm14=0.5, xmm10=2, xmm9=−1, xmm6=1; вызов IAT 181a14cd0 (log-класс)
|
||||||
|
над double из [obj+0x4198], ×8.68589 → дБ, запись пар во вектор
|
||||||
|
([obj+0xe0], рост через realloc 56c640).
|
||||||
|
- Вывод: строит дБ-доменную таблицу парами при param-rebuild — согласуется
|
||||||
|
с «LUT» ролью. Хвост с вирт. вызовом [rax+0x10] не декодирован.
|
||||||
|
|
||||||
|
### Алгебра T1: обе формы фитуют серию драйва (8 точек недоопределены)
|
||||||
|
- softplus α·ln1p(L/β)+c: rms 0.016 (α=3.2193 β=0.4927 c=0.542)
|
||||||
|
- LUT-форма t=((dB−A)/(B−A))^γ·M: rms 0.029 (A=−36.2 B=20.0 γ=2.097 M=10.49)
|
||||||
|
Различить только декомпом тракта am/res→scratch или бОльшим числом точек.
|
||||||
|
|
||||||
|
### Открытые микровопросы
|
||||||
|
1. Значение xmm7 на входе th2270 в FIR-цикле (52b5f6): трассировка от
|
||||||
|
52a583; если mix^p=1 → «bands+=1» противоречит провалам ⇒ xmm7 иной
|
||||||
|
либо порядок аргументов иной.
|
||||||
|
2. Раскладка объекта-вектора (тег 6) для ops A–D.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24u: WIN_WINDOW движка — кусочное окно аудио-пути
|
||||||
|
|
||||||
|
Таблица WIN_WINDOW[8193] (dsp/tables_data.hpp, live-захват):
|
||||||
|
- [0]=0.500000, плавный подъём до [2048]=0.800000, затем СКАЧОК до 1.0
|
||||||
|
и единица до конца ([2049..8192]).
|
||||||
|
- Длина перехода = РОВНО 2049 сэмплов = число бинов кернела (4096-сетка)!
|
||||||
|
- Формула перехода не каноническая (ханн/синус/степенные не сошлись,
|
||||||
|
maxdiff ≥0.09); для репликации достаточно встраивания таблицы как есть.
|
||||||
|
- Структура намекает: блок аудио 8192 сэмплов ({16384,8192} из cfg движка),
|
||||||
|
первые 2049 позиций получают взвешивание 0.5..0.8 (область «кернельного
|
||||||
|
взаимодействия»?), остальное прозрачно; 0.8 подозрительно = blend.
|
||||||
|
Проверка употребления — в resize fe00 / audio-клее (следующий раунд).
|
||||||
|
|
||||||
|
### Движок dc30: карта инициализаторов (24u)
|
||||||
|
ctor 18052dc30 вызывает: 5335c0 (×2 — до/после валидатора), 534550 (×5 —
|
||||||
|
регистрация конфиг-итемов, аргументы edx из стека [rsp+0x30..0x4c] =
|
||||||
|
цепочка {2,4096},{16384,8192},{2,257}), затем ILT-стабы 2240/1c10.
|
||||||
|
Следующий шаг декода: тела 534550/5335c0 + употребление WIN_WINDOW
|
||||||
|
(по xref на таблицу или указатель из объекта).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24w-3: ЖИВЫЕ КОНСТАНТЫ ГЕНЕРАТОРА 533340 (параметры частотных IIR)
|
||||||
|
|
||||||
|
Из scalar-bank (rendersnap2 v4+) на дефолтных параметрах multi:
|
||||||
|
```
|
||||||
|
p (экспонента) = [ctx+0x54087c] = 1.000000
|
||||||
|
tau1 = [ctx+0x540894] = 1200.000122
|
||||||
|
tau2 = [ctx+0x540898] = 180.000015
|
||||||
|
mult (стадия-3) = DAT_1824c4564 = 360
|
||||||
|
C_hz = DAT_1824c459c = 1000
|
||||||
|
константы 530b60 : 0.7, 2, 800→(×2)=1600, 1200, 15, 9, 180, 0.01,
|
||||||
|
[540898]=180*p+xmm6 (формула видна в дизасме)
|
||||||
|
```
|
||||||
|
Формула тела подтверждена дизасмом: g=min(fc_norm/i,(fc_norm/i)^p);
|
||||||
|
c=1/(1+g*tau/mult); up=exp(...); down=1-up.
|
||||||
|
Эксперимент: одноразовый bidir с этими коэф. НЕ двигает центр кривой
|
||||||
|
⇒ q-зависимость катов не через эти IIR напрямую; тракт между main-loop
|
||||||
|
и scratch содержит ещё этапы (ops A–D / steps 9–19 BLOCKMAP).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24z: FUN_180563a60 ДЕКОДИРОВАН ПОЛНОСТЬЮ — ЭТО ДЕТЕКТОРНАЯ LUT-ФОРМА!
|
||||||
|
|
||||||
|
Постройка (на каждый банд, 1024 точки, вход double[+0x4198+i·8],
|
||||||
|
выход пары-floats в вектор [+0xe0], stride вектора 0x18):
|
||||||
|
```
|
||||||
|
dB = log(input) · 8.68589
|
||||||
|
idx = i / 1024 → store[2j]
|
||||||
|
t = (dB − A) / (B − A) ; A=[sub+0], B=[sub+4] (sub=obj@[rcx+0x180])
|
||||||
|
t = clamp(t, 0, 1)
|
||||||
|
если γ(=[sub+0xc]) ≠ 1:
|
||||||
|
если [sub+0x10]==0: out = t^γ (классика)
|
||||||
|
иначе : out = sign(2t−1)·|2t−1|^γ + 1 (симметричный режим!)
|
||||||
|
else: out = t
|
||||||
|
out *= 0.5 → store[2j+1]
|
||||||
|
```
|
||||||
|
Виртуальная альтернатива: [sub+0x90]!=0 → вызов [sub vtbl+0x10](A,B,dB)
|
||||||
|
с клампом [0,1].
|
||||||
|
⇒ ЭТО ТА САМАЯ ФОРМА, ЧТО ДАЛА ФИТ cut=LUT(A,B,γ,M)! Стадия компрессии
|
||||||
|
уровня найдена материально: A/B/γ живут в объекте [band+0x180],
|
||||||
|
заполняются сеттерами. Осталось: снять их live-значения для наших конфигов
|
||||||
|
(расширить scalar-dump на [ctx+0x180]-объект) и найти потребителя таблицы
|
||||||
|
(интерполятор dB→out) в аудио-тракте.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24cc: точная формула коэф. 533340 (дизасм+live)
|
||||||
|
[state+8] = n = 2049 (∀ трёх состояний; live probe_states.py).
|
||||||
|
```
|
||||||
|
fc_norm = (X/(sr·0.5))·n ; X=xmm2 (C_hz=1000 или tau — уточнить)
|
||||||
|
g(i) = min(fc_norm/i, (fc_norm/i)^p) ; p=[ctx+0x54087c]=1 @defaults
|
||||||
|
c = 1/(1 + g/mult) ; mult=360 (стадия-3)
|
||||||
|
arg = |c| · g / n ← НАСЫЩАЮЩАЯ форма (пол g→mult/n)
|
||||||
|
up[i] = exp(arg · (−2π)) ; down[i]=1−up[i]
|
||||||
|
```
|
||||||
|
При дефолтах down ∈ [0.003..0.19] — очень мягкие сглаживатели; эффект
|
||||||
|
только каскадом в полной цепочке шагов 9–19.
|
||||||
|
Осталось уточнить: роль xmm13 (множитель перед c), знак/конст экспоненты,
|
||||||
|
соответствие X=C_hz-vs-tau.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24ff: семантика шага 11 (fma att/rel) — тройки (re,im,coef)
|
||||||
|
|
||||||
|
Цепочка вызова: ee20 → 487a0 → 1065c0; воркер получает r10=n/2·12 байт
|
||||||
|
⇒ элементы по 12 байт = (dst_re f32, dst_im f32, coef f32).
|
||||||
|
Call-site (52ac70–52acd4):
|
||||||
|
```
|
||||||
|
шаг 11a: fma(rcx=f6f8_upper, rdx=ATT@[5406c8], r8=ACC_i, r9=n/2)
|
||||||
|
шаг 11b: fma(rcx=f6f8_lower, rdx=REL@[5406e8], r8=ACC_i, r9=n/2)
|
||||||
|
```
|
||||||
|
⇒ семантика: f6f8[k] += coef[k]·ACC_i[k] (комплексный axpy с пер-биновым
|
||||||
|
скаляром), верхняя половина спектра — с ATTACK коэф., нижняя — с RELEASE.
|
||||||
|
ACC_i персистентен между кадрами ⇒ утечный интегратор: стационар
|
||||||
|
ACC = input/(1−coef); усиление 1/(1−att): @43=2.57 @85=2.93 @171=3.42.
|
||||||
|
Коэф. массивы СТАТИЧНЫ (∀ конфигов бит-в-бит — проверено dual/qmap/sens18).
|
||||||
|
Гипотеза: резонансное усиление ACC объясняет k>1 аномалии уровней
|
||||||
|
(q2: impl/ours=2.48 ≈ A(85)); точная алгебра подачи ACC в кривую — в
|
||||||
|
остатке шагов 13–19.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24gg: шаг 12 — ЭТО КОПИЯ (исправление BLOCKMAP)
|
||||||
|
|
||||||
|
th1b80→6840→1a5a0→181a646c0 = ЧИСТЫЙ MEMCPY (vmovdqu без арифметики).
|
||||||
|
«acc_i += bands[i]» из старого BLOCKMAP — НЕВЕРНО; реально копия
|
||||||
|
(направление/роли rbx/rbp уточнить трассировкой регистров через цикл —
|
||||||
|
аргументы на 52acf8: rcx=rbx, rdx=rbp, r8=n; значения rbx/rbp меняются
|
||||||
|
через 52ac98 [0x5406e8] и др. — нужен полный dataflow-проход шагов 9–19).
|
||||||
|
|
||||||
|
Следствие: если acc_i не накапливается сложением, «резонансная» гипотеза
|
||||||
|
24ff требует пересмотра — возможно ACC обновляется через fma шага 11
|
||||||
|
(ACC входит как источник), а шаг 12 синхронизирует массивы.
|
||||||
|
|
||||||
|
### Статус декода шагов 9–19 (24ff+24gg)
|
||||||
|
- шаг 11: f6f8[k] += att/rel_coef[k]·ACC_i[k] ✓ (тройки re,im,coef)
|
||||||
|
- шаг 12: COPY (не add!) ✓ исправлено
|
||||||
|
- шаги 13–16: th2270-add ветка с xmm6=xmm12−[54087c]; bigkernel 140950;
|
||||||
|
axpy 2000/1c40 с vec@540778 — тела не декодированы
|
||||||
|
- полный dataflow-проход = задача следующего раунда (связная, ~сессия)
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24hh: DATAFLOW шагов 9–12 (точный, из дизасма)
|
||||||
|
|
||||||
|
```
|
||||||
|
шаг 9a: vec698 *= (xmm12 − [54087c]) ; th1a00=MUL; при 1−1=0 → ZERO
|
||||||
|
шаг 9b: vec6f8 += 0.8 ; xmm10=0.8 ([1824c3e28]); th2270=ADD
|
||||||
|
шаг 9c: bigkernel 140b60(vec6f8, bands_curve_i, vec6f8)
|
||||||
|
шаг 10: vec6f8 = bands_curve_i − ACC_i ; dc40: out[r8]=rdx−r10 ✓COMBINE
|
||||||
|
шаг 11: f6f8_upper += ATT[k]·ACC_i[k] ; тройки re/im/coef
|
||||||
|
f6f8_lower += REL[k]·ACC_i[k]
|
||||||
|
шаг 12: COPY(...) ; направление уточнить
|
||||||
|
```
|
||||||
|
Регистры: rbx↔vec698/6f8/acc_i, rbp↔bands_curve/[5406e8], r14=vec6f8.
|
||||||
|
Картина: строится КОРРЕКЦИОННАЯ кривая (bands−ACC), модулированная
|
||||||
|
att/rel-огибающими ACC — адаптивная петля редукции.
|
||||||
|
Остаток прохода: шаги 13–19 + эпилог + связь с FIR-секцией (52b3cd+).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24ii: семантики шагов 13–16 + ИСПРАВЛЕНИЕ th2000
|
||||||
|
|
||||||
|
th2000/fb60 (и 1c40/8700) = ПОЭЛЕМЕНТНОЕ УМНОЖЕНИЕ МАССИВОВ
|
||||||
|
(dst[k] *= src[k]) — НЕ axpy! Исправляет интерпретации:
|
||||||
|
|
||||||
|
```
|
||||||
|
шаг 13 (флаг≠0): зеркало шага 9 (vec698*=..., vec6f8+=0.8·..., bigkernel)
|
||||||
|
шаг 14: bands_curve += (−1.0) ; константа [1824c4680]=−1 через th2270!
|
||||||
|
затем bigkernel 1409e0/140ad0 IN-PLACE на bands_curve
|
||||||
|
шаг 15: bands_curve *= track_i ; th2000 array-multiply
|
||||||
|
шаг 16: bands_curve *= kWarp@[5406a8] ; th2000 array-multiply
|
||||||
|
шаг 17(флаг): bigkernel in-place ещё раз
|
||||||
|
```
|
||||||
|
Картинка: кривая центрируется (−1), проходит нелинейность (bigkernel,
|
||||||
|
вероятно exp/abs — тела за IAT), модулируется track и warp.
|
||||||
|
Против log-входа design'а значение после этих шагов должно быть >0.
|
||||||
|
|
||||||
|
### Bigkernel-тела за ВЛОЖЕННЫМ диспатчем
|
||||||
|
Таблицы стабов содержат смесь IAT-слотов и внутренних адресов (напр.
|
||||||
|
table[7]=140a00), но внутренние ведут к call runtime-helper + НОВЫЙ
|
||||||
|
ILT-стаб с собственной idx-ячейкой (паковка/протектор). Статическое
|
||||||
|
разворачивание обрывается. Тела bigkernel'ов (exp/abs-нелинейность шага
|
||||||
|
14/17) остаются за пакером — при необходимости снимаются дампом памяти
|
||||||
|
ВОКРУГ вызова в рантайме (STOP + чтение таблиц после инициализации).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24jj: ТЕЛА BIGKERNEL'ОВ НАЙДЕНЫ (рантайм-резолв IAT)
|
||||||
|
|
||||||
|
iat_name.py v2 (SIGSTOP + двойной deref + PE-экспорты) резолвит:
|
||||||
|
```
|
||||||
|
стаб 140b30/140b60 → runtime 1803a06a0 (общий для float/double!)
|
||||||
|
стаб 1409e0 → runtime 180296c80
|
||||||
|
стаб 140ad0 → runtime 180323f20
|
||||||
|
стаб 140a40 → runtime 1802dc0e0
|
||||||
|
```
|
||||||
|
Все — НАСТОЯЩИЕ функции внутри дампа (не импорты!): большие стек-фреймы,
|
||||||
|
x87 FNU-контроль, AVX2 полиномы, ДВЕ x87-трансцендентные инструкции
|
||||||
|
(fyl2x/f2xm1 класс = 2^x/exp семейство). Полный декод математики каждого —
|
||||||
|
отдельная сессия; вход/выход уже известны из контекста вызовов
|
||||||
|
(in-place над n/2+1 элементами FIR-буфера).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24jj2: R@540788 — БАНДПАС-ФОРМА, НЕ совпадающая с нашим twin!
|
||||||
|
|
||||||
|
Сравнение (multi6, band1=500/q0.5/s12):
|
||||||
|
```
|
||||||
|
бин R@540788 наш_res R/наш
|
||||||
|
43 0.802 0.117 6.84
|
||||||
|
85 1.086 0.374 2.91
|
||||||
|
171 1.517 0.839 1.81
|
||||||
|
342 3.521 1.401 2.51 ← ПИК ~3.5-4 кГц!
|
||||||
|
512 2.910 1.673 1.74
|
||||||
|
684 1.829 1.811 1.01
|
||||||
|
1024 1.146 1.931 0.59
|
||||||
|
1536 0.689 1.988 0.35
|
||||||
|
```
|
||||||
|
Наш twin res растёт монотонно от fc; R@788 — БАНДПАС с пиком ~bin300-342
|
||||||
|
(~3.5-4 кГц) и СПАДОМ к Найквисту. Форма напоминает кривую равной
|
||||||
|
громкости / слухового взвешивания!
|
||||||
|
R@5407f8 = единичная нормировка (все 1.0000 в этом прогоне).
|
||||||
|
|
||||||
|
### ГИПОТЕЗА (проверяемая):
|
||||||
|
Детекторный уровень = am · ВЕС(f) / res(f), где ВЕС — кривая типа
|
||||||
|
равной громкости (R@788?). Тест: X=am·R/res против катов multi6 —
|
||||||
|
НЕ сошлось лобово (X@43=1.44 макс при мин кате) ⇒ взвешивание входит
|
||||||
|
иначе (до/после res-деления, или в log-домене).
|
||||||
|
### Ценность
|
||||||
|
Объясняет ВСЕ аномалии дальних бинов разом: наши дальние res слишком
|
||||||
|
велики (нет спада), их lvl занижен, каты недобираются. Формула ВЕСА —
|
||||||
|
ключ к кросс-конфиг параметризации.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24kk3: слот [ctx+0x540668] ПОЛИМОРФНЫЙ!
|
||||||
|
|
||||||
|
Прямой проб во время рендера: [ctx+0x540668] содержит
|
||||||
|
`3fdbcd8940000000` = ДВА FLOAT (~0.434, 2.0), НЕ указатель!
|
||||||
|
(пробник probe_668.py; EIO на части чтений — слот мигрирует).
|
||||||
|
|
||||||
|
### Следствия:
|
||||||
|
1. rendersnap2 пропускает слот 668 (ptr<0x10000 или мусорный ptr) ⇒
|
||||||
|
во ВСЕХ новых захватах (sc_multi4b, sc_tt*, sc_q*, ...) НЕТ FIR-массивов.
|
||||||
|
2. Старые захваты rendersnap.py v1 ИМЕЛИ валидный FIR-указатель в эти
|
||||||
|
моменты (ловили фазу обработки). Данные старых phase*.npz про FIR —
|
||||||
|
валидны для своих моментов, но смешивать с новыми нельзя.
|
||||||
|
3. Все «FIR mag» анализы через этот слот зависят от ТОГО, в какой фазе
|
||||||
|
слот был пойман: указатель-на-буфер vs скаляры vs сброс.
|
||||||
|
4. Значения скаляров (~0.43, 2.0) — кандидаты: att/rel? dry/wet? g-компоненты?
|
||||||
|
### Статус
|
||||||
|
Канонический путь чтения ПРИМЕНЁННОЙ маски: слот [ctx+0x540678] (кривая
|
||||||
|
банды) — он стабилен и МАТЧИТ АУДИО в deep-фазах (±5%).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24mm2: ПОЛНАЯ РЕЗОЛЮВСЯ ВСЕХ 10 ЯДЕР + ИСПРАВЛЕНИЯ DATAFLOW
|
||||||
|
|
||||||
|
Инструмент: статический резолв цепочки стаб→таблица→L2→IAT-слот по
|
||||||
|
soothe_mem.bin (без live). Стаб = `movsxd rax,[idx@1826159a0]; lea r10,[tbl];
|
||||||
|
jmp [r10+rax*8]`, idx=4, L2=`mov rax,[slot]; jmp rax`.
|
||||||
|
|
||||||
|
### Таблица резолва (исправляет 24jj!)
|
||||||
|
| стаб | таблица | runtime | опознание по константам |
|
||||||
|
|------|---------|---------|------------------------|
|
||||||
|
| 140950 | 182617448 | **18026b820** | exp2/exp DOUBLE (ln2, log2e, 1021.5, 2^27) |
|
||||||
|
| 140980 | 182617488 | **18027c120** | **logf** FLOAT (ряд −½,+⅓,−¼,+⅕,−⅙; ln2 hi/lo; 2^32) |
|
||||||
|
| 1409b0 | 1826174c8 | **18028d1e0** | **powf/log+exp** DOUBLE (ряд log + магия expf вместе) |
|
||||||
|
| 1409e0 | 182617508 | 180296c80 ✓ | **expf** FLOAT — ДЕКОДИРОВАН ПОЛНОСТЬЮ (ниже) |
|
||||||
|
| 140a40 | 182617588 | 1802dc0e0 ✓ | exp-вариант FLOAT c hi/lo сплитами |
|
||||||
|
| 140aa0 | 182617608 | **18030fee0** | **sincos** DOUBLE (1/6,1/120,1/5040; π hi/lo) |
|
||||||
|
| 140ad0 | 182617648 | 180323f20 ✓ | кусочно-табличная DOUBLE (сетка Δ=0.00541521) |
|
||||||
|
| 140b00 | 182617688 | **180367980** | pow/exp DOUBLE (1023/1022, магия 1.5·2^20) |
|
||||||
|
| 140b30 | 1826176c8 | **1803831c0** | кусочно-табличная FLOAT (π/2, π/4, сетка 184.665!) |
|
||||||
|
| 140b60 | 182617708 | 1803a06a0 ✓ | **DIVIDE** FLOAT B/A (rcp+квантование+vpermps-таблицы+полином невязки) |
|
||||||
|
|
||||||
|
**ИСПРАВЛЕНИЕ 24jj**: «140b30/140b60 → общий 1803a06a0» — НЕВЕРНО.
|
||||||
|
FIR-секция вызывает 140b30 = 1803831c0 (табличная кривая), divide только в шаге 9c.
|
||||||
|
|
||||||
|
### ДИСПЕТЧЕР float/double
|
||||||
|
Каждый call-site имеет ПАРУ стабов через `call [181bab008]; test eax,eax; jne`:
|
||||||
|
float-стаб (eax==0) / double-стаб. Дескрипторы type_info СТАТИЧЕСКИ идентичны
|
||||||
|
(оба →182650db8) ⇒ eax=0 ⇒ **double-ядра мертвы на нашем пути**; рендеры
|
||||||
|
идут по float. Double-тела не транскрибируем (отмечено на будущее M8).
|
||||||
|
|
||||||
|
### expf 180296c80 — полная формула (горячий цикл, FMA-точно)
|
||||||
|
```
|
||||||
|
n = fma(log2e_hi=1.4427, x, MAGIC=12582912.0) ; округление до int
|
||||||
|
k = n − MAGIC
|
||||||
|
r = (x − 0.693146·k) − 1.42861e-06·k ; ln2 hi/lo
|
||||||
|
p = (((0.00829172·r + 0.0418735)·r + 0.166674)·r + 0.499994)·r + 1)·r + 1
|
||||||
|
out = bits( (k<<23) + bits(p) ) ; vpaddd сборка
|
||||||
|
guard: |x|>87.3365 → slow-path; head/tail через vmaskmovps+popcnt-маски
|
||||||
|
```
|
||||||
|
Коэф. минимаксные — транскрибировать КАК ЕСТЬ.
|
||||||
|
|
||||||
|
### DIVIDE 1803a06a0 — структура (90%)
|
||||||
|
```
|
||||||
|
A=[rcx], B=[rdx], dst=[r8]; r9d=n
|
||||||
|
q0 = rcp(A); q0 += 2^23-magic (округление); q = q0 & 0xfff00000 ; 12 бит
|
||||||
|
e = (q>>23); idx = q>>20 → vpermps tbl@1821269c0 (127±ε) и @182126a00
|
||||||
|
err = 1 − q·A
|
||||||
|
полином невязки {0.207515, −0.241687, 0.288535, −0.360671, ..., 0.240264, 0.0555119}
|
||||||
|
сборка через магию 1.5·2^20 + vpslld 20
|
||||||
|
результат ≈ B/A с точностью ~0.5 ulp
|
||||||
|
```
|
||||||
|
Таблицы коррекций сдамплены (per-mantissa-top-bits).
|
||||||
|
|
||||||
|
### ИСПРАВЛЕНИЯ DATAFLOW (по fn529fe0.dis, адреса call-sites)
|
||||||
|
1. **Шаг 14 порядок ОБРАТЕН к BLOCKMAP 24ii**: сначала `bigkernel exp IN-PLACE
|
||||||
|
на bands_curve` (52ae0e), ПОТОМ `bands_curve += (−1.0)` (52ae40, конст.
|
||||||
|
1824c4680 через th2270).
|
||||||
|
2. Шаг 9b точно: `vec6f8 += [ctx+54087c] · 0.8` (xmm10=0.8@1824c3e28,
|
||||||
|
множитель виден в asm: mulss xmm6,xmm10 после movss xmm6,[54087c]).
|
||||||
|
3. Шаг 9a: `vec698 *= (xmm12=1.0 − [54087c])` ⇒ zero-fill при дефолтах ✓.
|
||||||
|
4. Шаг 10 combine dc40: аргументы rcx=ACC_i(**таблица указателей @0x5407c8**,
|
||||||
|
НЕ дампилась rendersnap2!), rdx=bands_curve_i(@678+i), r8=vec6f8(@6f8),
|
||||||
|
семантика dst=r8: vec6f8 = bands_curve_i − ACC_i. ACC-слот надо ДОБАВИТЬ
|
||||||
|
в SLOTS rendersnap2 (0x5407c8).
|
||||||
|
5. Шаги 15/16 подтверждены: th2000/th1c40 array-mul; затем rbx=[5406a8]
|
||||||
|
(kWarp) — array-mul на bands_curve.
|
||||||
|
6. Эпилог: скалярная часть из decomp (consumers_out 100-143): mix-веса,
|
||||||
|
`fVar17 = [540874] − expf(DAT_1824c4704=-ln1000)` → bands += f17·[540888].
|
||||||
|
|
||||||
|
### Call-site карта больших ядер (fn529fe0)
|
||||||
|
```
|
||||||
|
52a63a/52a641: 140980(logf-float)/1409b0 — pre-combine #1
|
||||||
|
52ab84/52ab8b: 140b60(divide)/140950 — шаг 9c
|
||||||
|
52acd? : (шаги 10–12 мелкие ILT)
|
||||||
|
52ae0e/52ae15: 1409e0(expf)/140ad0 — шаг 14 нелинейность
|
||||||
|
52b32c/52b336: 1409e0(expf)/140ad0 — шаг 17 (повтор)
|
||||||
|
52b3a0/52b3aa: 140a40(exp-var)/140b00 — пост-17
|
||||||
|
52b716/52b71d: 140b30(кривая-float)/140aa0 — FIR-секция
|
||||||
|
```
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24mm5: ПОЛНАЯ КАРТА ТРАКТА — буферы каждого шага; design = conv-тело 22z
|
||||||
|
|
||||||
|
### Полоса-цикл (float-путь), трасса регистров 52a580–52b3cd
|
||||||
|
```
|
||||||
|
пре: [678i] *= скаляры (s888-цепь, xmm7·[540870]·[54088c])
|
||||||
|
LOG#1 (140980!) на [678i] ; 52a63a — В ЛОГ-ДОМЕН заранее
|
||||||
|
combine 52d650([678i],[6f8])
|
||||||
|
шаг 9a: vec698@698 *= (1−[54087c]) ; zero
|
||||||
|
шаг 9b: vec6f8@6f8 += [54087c]·0.8
|
||||||
|
шаг 9c: DIVIDE dst=[678i]: A=arg(rcx)=[678i], B=arg(rdx)=[6f8]
|
||||||
|
⇒ [678i] = vec6f8 / bands_curve ; in-place
|
||||||
|
шаг 10: dc40: rcx=ACC_i(@7c8+i!), rdx=[678i], r8=[6f8]
|
||||||
|
⇒ vec6f8 = bands_curve − ACC_i ; ACC — таблица указателей 7c8
|
||||||
|
шаг 11: fma ATT(@6c8)/REL(@6e8) — пары вызовов 1fa0/1940
|
||||||
|
шаг 12: COPY 1b80/1d60 c [678i]
|
||||||
|
шаг 13: зеркало 9a/9b + оп 1eb0(cbe0)([678i],[6f8])
|
||||||
|
шаг 14: EXP#1 (1409e0=expf) на [678i]; затем += (−1)
|
||||||
|
шаг 15: array-mul: X[rsp+0x40] *= [678i] ; НЕ bands*=track!
|
||||||
|
шаг 16: [678i] *= kWarp@[5406a8]
|
||||||
|
LOG#2 (140980) на [678i] ; 52aefd — возврат в лог!
|
||||||
|
IIR4 ×2 бидир ; ~52af09–52b2b6, СПЕКТРАЛЬНОЕ
|
||||||
|
; СМЕШЕНИЕ В ЛОГ-ДОМЕНЕ
|
||||||
|
скаляры xmm14(−0.7)/xmm15(−0.5)-класс
|
||||||
|
шаг 17: EXP#2 (1409e0) на [678i]; += scalar; exp-var 140a40 финал
|
||||||
|
→ bands_final @678i
|
||||||
|
```
|
||||||
|
|
||||||
|
### FIR-секция (52b3cd–52b94a)
|
||||||
|
```
|
||||||
|
bands_final *= s888, *= [540888]; += xmm7 (скаляр с expf(−ln1000)=0.001)
|
||||||
|
DESIGN: call 535a70(rcx=scratch@628, rdx=bands)
|
||||||
|
535a70 = диспетчер СО СВОПОМ аргументов → ILT 140a10/140a70 →
|
||||||
|
→ РЕЗОЛВ: float=1802a24c0 (!!!), double=1802fa420
|
||||||
|
⚡ ЭТО ТЕЛО FFT-CONV ИЗ ОТКРЫТОГО ВОПРОСА 22z («conv_float_a24c0.dis»,
|
||||||
|
184K AVX2). Дизайн детектора == недекодированный conv. Пазл склеен.
|
||||||
|
дальше: copy th2210; complex-op th2180/th1bb0 с твидл-буферами
|
||||||
|
548/550/598; знак −1 (52d920); EXP 140b30(=1803831c0);
|
||||||
|
окно 52d990(WINfreq); pair-scalar 1880/1ca0; *= wet[540888];
|
||||||
|
финал df0(FIR, track_i)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Где γ=1.760561
|
||||||
|
mask = bands_final^γ точно ⇒ γ возникает между scratch=log(bands_final)
|
||||||
|
и финальной маской: либо ВНУТРИ design 1802a24c0 (масштаб на выходе),
|
||||||
|
либо в комплекс-op цепочке 52b644–52b716 перед EXP 140b30. Обе точки
|
||||||
|
локализованы до ~десятка инструкций — декод следующего раунда.
|
||||||
|
|
||||||
|
### Исправление понимания слотов
|
||||||
|
- 688 = exp(628) тривиально: 628 — копия лога bands_final (design),
|
||||||
|
688 — сами bands_final (или их exp-копия). «track» — имя рендерснапа.
|
||||||
|
- 678 ПОСЛЕ цикла = bands_final; применённая маска перезаписывает
|
||||||
|
поверх (финальный combine) — поэтому захваченный 678 матчит аудио.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24mm6: ПЕРЕД EXP В FIR — УМНОЖЕНИЕ НА 2.0 (не −1!); гипотеза γ=2·k_design
|
||||||
|
|
||||||
|
### Точная последовательность 52b60c–52b720 (проверено, без пропусков)
|
||||||
|
```
|
||||||
|
rcx=[540628](scratch), rdx=[r15](источник design — уточнить r15!)
|
||||||
|
call 535a70 → swap → 1802a24c0(scratch ← DESIGN(src))
|
||||||
|
th2210: FIR(@540668) ← scratch (copy, edx=0)
|
||||||
|
opB: th2180(FIR, buf548|550, buf598) ; complex pass
|
||||||
|
FIR[n]=0
|
||||||
|
FIR[1 .. n/2] *= xmm13 = 2.0 @1824c41e0 ; 52d920, БЫЛО «−1» в 24l — НЕВЕРНО
|
||||||
|
FIR[n/2+1 .. n-1] *= xmm9 (=0) ; 52db50
|
||||||
|
opC: th1a90(FIR, buf548|550, buf598) ; complex pass
|
||||||
|
EXP in-place 140b30 (float) / 140aa0 (double)
|
||||||
|
```
|
||||||
|
xmm13/xmm9 не перезаписываются между 52b3d6 и использованием (проверено).
|
||||||
|
|
||||||
|
### Гипотеза источника γ
|
||||||
|
Если opB/opC сохраняют пропорциональность (упаковка real-FFT), то
|
||||||
|
mask = exp(2 · scratch) ⇒ γ = 2·k, где k — масштаб выхода design
|
||||||
|
1802a24c0 относительно ln(bands): k = 1.760561/2 = 0.8802805.
|
||||||
|
Альтернатива: k=1, а opB/opC суммарно дают множитель 0.88028.
|
||||||
|
|
||||||
|
### Открытые микровопросы (следующий раунд, всё локализовано)
|
||||||
|
1. Что такое [r15] на входе design (bands_final@678 или иной буфер)?
|
||||||
|
2. Семантика opB/opC (th2180/th1bb0/th1a90/th19d0 + твидлы 548/550/598)
|
||||||
|
— вероятно упаковка/развёртка real-FFT.
|
||||||
|
3. Масштаб выхода design: декод хвоста 1802a24c0 (файл уже есть:
|
||||||
|
nls_dasm/conv_float_a24c0.dis, 184K).
|
||||||
|
4. Согласование с identity-фазой захватов (гонка финального combine).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24mm7: design выход = точный ln(bands_final); γ создаётся после design
|
||||||
|
|
||||||
|
### Численный тест (multi6/ph034, identity-фаза)
|
||||||
|
```
|
||||||
|
scr@628 − ln(cur@678): max|r| = 9.0e-08 (float32 eps) на 1013 бинах
|
||||||
|
⇒ k_design = 1 (в момент захвата)
|
||||||
|
```
|
||||||
|
Оговорка: станционарность делает «свежий» и «сталый» scratch
|
||||||
|
неразличимы; но факт (scr, cur)=(лог, значение) одной пары твёрд.
|
||||||
|
|
||||||
|
### Следствие для γ
|
||||||
|
γ=1.760561 ≠ 2 ⇒ множитель НЕ только «×2 перед EXP». Источники:
|
||||||
|
(a) opB/opC не взаимно сокращаются (не чистая упаковка real-FFT);
|
||||||
|
(b) пост-exp шаги: окно 52d990 (варьируется по позиции — нарушил бы
|
||||||
|
степенной закон, значит действует на верхнюю половину/после),
|
||||||
|
pair-scalar th1880/th1ca0, финальный combine df0(FIR, track_i),
|
||||||
|
где track=exp(scr)=bands_final.
|
||||||
|
Комбинации дающие γ из {1,2}: 1+2x=1.760561 ⇒ x=0.3802805;
|
||||||
|
либо лог-доменное смешение track^a·FIR^b c a+2b=1.760561.
|
||||||
|
|
||||||
|
### Статус декода design 1802a24c0
|
||||||
|
AVX-512 (zmm, masked {k3}/{k4}), 3822 строки objdump — трансформ-класс.
|
||||||
|
Для замыкания γ его полный декод МОЖНО НЕ НУЖЕН: достаточно семантики
|
||||||
|
opB/opC + df0 (десятки инструкций в fn529fe0.dis).
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24mm8 (финал захода): opB/opC/df0 резолвлены
|
||||||
|
```
|
||||||
|
opB: 180002180→180004ca80(f)/18001d160(d) ; дескриптор-оп (тег [obj]==6)
|
||||||
|
opC: 180001a90→18001a0c0(f)/180018400(d)
|
||||||
|
df0: 18000df0→18000b3c0 ; f70→18000e360 ; финальный combine
|
||||||
|
```
|
||||||
|
Все четыре микровопроса 24mm6 закрыты или локализованы до тел-обёрток.
|
||||||
|
Следующий раунд: семантика 4ca80/1a0c0 (кандидаты источника γ=2k−масштаба),
|
||||||
|
затем полный numpy-конвейер.
|
||||||
|
|
||||||
|
## ДОПОЛНЕНИЕ 24mm9: opB/opC = RFFT-близнецы; df0 = complex-mul; цепь валидирована 0.0065 дБ
|
||||||
|
|
||||||
|
### Слой вызовов FIR-секции (уточнение поверх 24l/24mm6)
|
||||||
|
```
|
||||||
|
обёртки: th2180 impl=125e0, th1a90 impl=5560 — только перестановка аргументов:
|
||||||
|
воркер получает (rcx=data, rdx=data, r8=ПЛАН, r9=WORK), ин-плейс.
|
||||||
|
ПЛАН = [ctx+540548] (buf548!): tag=6 [+0], log2n=12 [+4], flag [+8]=0,
|
||||||
|
scale_flag=1 [+0xc], scale=2^-12 [+0x10], workbytes=16384 [+0x18].
|
||||||
|
WORK = [ctx+540598] — рабочая область FFT (заметение «lane-mask» из 23b).
|
||||||
|
th2180 → воркер 4ca80(f)/1d160(d): INVERSE real-RFFT (голова: X[0]±X[Nyq]).
|
||||||
|
th1a90 → воркер 1a0c0(f)/18400(d): FORWARD real-RFFT (хвост: пакинг Nyq).
|
||||||
|
тела: импортные близнецы 181b853e0(inv)/181b81b80(fwd); константы только
|
||||||
|
±0.707107; масштабов нет. ffe0 = ×scale pass (skip при scale∈{0,1}).
|
||||||
|
copy th2210 → 136e0 → 4d900(src,dst,n): pack re=v, im=0 (vunpcklps+zero).
|
||||||
|
df0 18000b3c0: ПОЭЛЕМЕНТНОЕ КОМПЛЕКСНОЕ УМНОЖЕНИЕ dst=[rdx]=arg2:
|
||||||
|
track_i := track_i ⊗ FIR (vfmaddsub213ps; f70/b560 — double версия).
|
||||||
|
EXP 140b30 → 1803831c0: полиномиальная комплексная exp (без таблиц значений):
|
||||||
|
magic 12582912 (=2^23·1.5), guard 87.33654, редукция 184.665≈128/ln2,
|
||||||
|
коэф. {−0.01604,−1.541667(=−37/24), 3.166e-05, 1.008329, −1.65777e-06,
|
||||||
|
−0.01932, 0.00134, 0.00541687, 10000, 4.19179}; AVX-512+FMA.
|
||||||
|
Численно = поточечный комплексный exp (flat-exp проигрывает 8 дБ).
|
||||||
|
```
|
||||||
|
|
||||||
|
### Полная последовательность (52b60c–52b893, все шаги, без пропусков)
|
||||||
|
```
|
||||||
|
design 535a70(scratch@628 ← ln(bands_i)) ; 52b62f, своп аргументов
|
||||||
|
copy 2210(scratch → FIR, 2049 пар (re,im=0)) ; 52b644
|
||||||
|
FIR[4096]=0 ; 52b685 Найквост ДО фолда
|
||||||
|
inv-RFFT opA ; 52b672 th2180
|
||||||
|
fold: float[1..2047]*=2.0 (xmm13@1824c41e0) ; 52d920
|
||||||
|
float[2049..4095]=0 ; 52db50
|
||||||
|
fwd-RFFT opB ; 52b6e1 th1a90
|
||||||
|
EXP in-place, аргумент×q (q≈0.80, источник ОТКРЫТ); 52b716
|
||||||
|
inv-RFFT opC ; 52b74b th2180
|
||||||
|
FIR[4096]=0 ; 52b76d
|
||||||
|
float[0..2047]*=WINfreq[2048..4095] ; 52d990 (падающий Hann)
|
||||||
|
float[2048..4095]=0 ; 52db50
|
||||||
|
fwd-RFFT opD ; 52b7ba th1a90
|
||||||
|
FIR[0]=1.0f; FIR[1]=0 ; 52b7cd
|
||||||
|
(flag f890≠0: pair-scalars 1880/ca0 — live мертво)
|
||||||
|
th2030(FIR, wet=s888, 2n float) ; 52b857, s888=1 no-op
|
||||||
|
df0(FIR, track_i, n): track_i := track_i ⊗ FIR ; 52b893
|
||||||
|
```
|
||||||
|
Смысл: классическое минимально-фазовое ядро через кепстр
|
||||||
|
(IDFT лога → фолдинг ×2 причинной части + усечение → exp → обратный ход).
|
||||||
|
|
||||||
|
### Валидация и γ
|
||||||
|
mask_sim = trk·|F(q)|: 60 ультрачистых кадров, ВСЕ 2049 бина:
|
||||||
|
rms мед 0.0065 дБ / p90 0.0075 / max 0.035 при q=0.80 (порог 0.05 ✓).
|
||||||
|
γ = 1 + s_F(q), s_F = наклон log|F| по log trk в нотче: q=0.8 ⇒ γ_pred=1.7516
|
||||||
|
(точный 1.760561). Открыто: место q в асме (внутренность 1803831c0);
|
||||||
|
unicorn не эмулирует FMA ⇒ нужен статдекод ядра или live-захват входа EXP.
|
||||||
|
Дизасмы: /tmp/opencode/cascade/{wrapA_125e0,wrapB_5560,opB_4ca80,opC_1a0c0,
|
||||||
|
df0_b3c0,h_ffe0,h_136e0*,imp_b8*3e0_full,bk_1803831c0}.dis
|
||||||
|
(*copy: python3 scripts/disasm_func.py 1800136e0 — ВАЖНО: полный VA,
|
||||||
|
короткая форма «125e0» даёт пустой файл!).
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# PROMPT FOR NEXT SESSION (2026-08-25, после 24kk2)
|
||||||
|
|
||||||
|
Продолжаем bit-exact реверс soothe2 в /home/m/re-tools (ветка main).
|
||||||
|
ГЕЙТ СМЕНЫ КАНОНА = BIT EXACT (решение пользователя): все параметры
|
||||||
|
прослежены до декомпа + корпус в шумовом пол. До тех пор канон не трогаем.
|
||||||
|
|
||||||
|
ПРОЧИТАТЬ ПЕРВЫМ: AGENTS.md (фаза-заголовок 24kk2 + env-флаги + инструменты)
|
||||||
|
→ handoff/NOTES_LEVEL.md обновления 24j–24kk2 → BLOCKMAP_529fe0.md
|
||||||
|
(дополнения 23b/24l/24hh/24ii).
|
||||||
|
|
||||||
|
## СОСТОЯНИЕ
|
||||||
|
|
||||||
|
ПРИМЕНЕНИЕ ДЕКОДИРОВАНО ДО ФОРМУЛ:
|
||||||
|
```
|
||||||
|
mask(b) = 10^(−cut_D(b)/20) вещественная, per-bin multiply кадра
|
||||||
|
cut_D(b) = α·ln1p(lvl_raw(b)/β)+c [+Δ вторые пики]
|
||||||
|
lvl_raw = am/res·scale наш фронтенд, float-parity ✓
|
||||||
|
слой = STFT БЕЗ синтез-окна RT_SYN=1
|
||||||
|
```
|
||||||
|
Калибровки (rms ≤0.016 дБ):
|
||||||
|
```
|
||||||
|
dual fc500 q0.5 s12 ДВА тона : α=3.2193 β=0.4927 c=+0.54
|
||||||
|
fc1000 q0.5 s12 ОДИН тон : α=1.6151 β=0.3645 c=+0.48
|
||||||
|
fc500 q0.5 s12 ОДИН тон : α=1.1530 β=0.4038 c=+0.33
|
||||||
|
```
|
||||||
|
Корпус: dual **0.193 max 0.438** с флагами `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1
|
||||||
|
RT_NOIIR3=1 RT_IIR12=0`. Канон TOTAL 2.286 нетронут.
|
||||||
|
|
||||||
|
ОТЗЫВАНО И НЕ ВОЗВРАЩАТЬСЯ: ×1.805-свёртка [23e], OLA-нормировка [24i],
|
||||||
|
двухстадийный γ₀ как множитель закона [24m — это артефакт двух тонов],
|
||||||
|
клампы параметров [24dd], B∝am [24bb], axpy-семантика th2000 [24ii — это
|
||||||
|
array-multiply], «acc_i += bands[i]» шага 12 [24gg — это COPY].
|
||||||
|
|
||||||
|
## ЗАДАЧА №1: каскадный симулятор шагов 9–19 (оп-за-опом)
|
||||||
|
|
||||||
|
Dataflow декодирован (BLOCKMAP 24hh/24ii):
|
||||||
|
```
|
||||||
|
vec698 *= (xmm12−[54087c]) ; обнуление при дефолтах
|
||||||
|
vec6f8 += 0.8 ; базовая линия
|
||||||
|
bigkernel(vec6f8, bands_i, vec6f8)
|
||||||
|
vec6f8 = bands_curve − ACC_i ; COMBINE (dc40: out=rdx−r10)
|
||||||
|
f6f8_верх += ATT[k]·ACC[k]; низ += REL[k]·ACC[k] ; тройки re/im/coef
|
||||||
|
COPY(...) ; шаг 12 = memcpy!
|
||||||
|
bands += (−1.0); bigkernel in-place ; центрирование+нелинейность
|
||||||
|
bands *= track_i; bands *= kWarp ; th2000 = array-multiply
|
||||||
|
IIR4 ×2; финальные scale/op
|
||||||
|
→ bands[i] → design log → scratch → FIR → audio multiply exp(scratch)
|
||||||
|
```
|
||||||
|
Тела bigkernel'ов по рантайм-адресам: 1803a06a0/180296c80/180323f20/1802dc0e0
|
||||||
|
(x87 exp-семейство). Коэффициенты IIR-генератора 533340 исправлены
|
||||||
|
(насыщающая форма arg=|c|·g/n; p=[54087c]=1, mult=360, C=1000).
|
||||||
|
|
||||||
|
Метод проверки: собрать симулятор в Python (numpy), прогнать lvl_raw из
|
||||||
|
tract_* через каскад, сравнить с deepest-scratch кривыми sc_* — rms < 0.05 дБ
|
||||||
|
= замкнулось. Затем перенос в C++.
|
||||||
|
|
||||||
|
## ЗАДАЧА №2: параметризация α(контент)/fc
|
||||||
|
|
||||||
|
α удваивается с числом тонов (частотное смешение шаблонно-локальное —
|
||||||
|
далёкий тон не влияет, 24ll). Инструмент: scripts/campaign.py (ячейка ≈8 мин).
|
||||||
|
Двухтональная дистанционная серия уже снята (sc_d*, инверсия не сошлась —
|
||||||
|
нужен каскад из Задачи №1 сначала!).
|
||||||
|
|
||||||
|
## СРЕДА (грабли, всё проверено болью)
|
||||||
|
- rendersnap2.py: RENDER_FILE брать ИЗ rpp (клоны наследуют путь — однажды
|
||||||
|
перезаписали реф); каталоги снапов задавать уникальные (argv[3]).
|
||||||
|
- touch+mtime перед каждым замером (same-second cmake hazard).
|
||||||
|
- tone1k.wav громче dual.wav в 2.28× — при сравнении серий учитывать.
|
||||||
|
- Свип НЕ годится для Y/X-отношения; только мультитон/мультиуровень.
|
||||||
|
- Динамика V(t)↔g(t) на рампе не сходится — пары только из стационара.
|
||||||
|
- HW-ловушки под wine невозможны (wine держит слоты); INT3 требует
|
||||||
|
дисциплины fnwatch4 и ГАРАНТИРОВАННО свежего рендера.
|
||||||
|
- Метрика только честная: render_parity.load / Гёрцель последних 0.75 c.
|
||||||
|
- Коммитить подшагами; факты → NOTES_LEVEL (очередной номер 24ll+).
|
||||||
+31
-146
@@ -1,159 +1,44 @@
|
|||||||
# RUNTIME CAPTURE — LIVE DSP TABLES (2026-08-19)
|
# Runtime capture — live DSP tables (registry heartbeat, SR 48000)
|
||||||
|
|
||||||
## BREAKTHROUGH: heap "registry" object found & read during offline render
|
> **Сжато 2026-08-28.** Полный журнал 2026-08-19—2026-08-23 → [`handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md`](archive/NOTES_LEVEL_2026-08-18_2026-08-23.md) + [`handoff/NOTES_LEVEL_INDEX.md`](NOTES_LEVEL_INDEX.md). Детали FIR-цепи → `handoff/BLOCKMAP_529fe0.md`.
|
||||||
- During `reaper -nosplash -renderproject render_long.rpp`, the yabridge-host
|
|
||||||
allocates a DSP arena containing a **registry array**: a run of `{u64 count, u64 ptr}`
|
|
||||||
pairs (stride 0x10) pointing at every DSP buffer. At capture time:
|
|
||||||
registry base = **0x29b06c0** (in anon region 0x2922000, 0x77e000).
|
|
||||||
(GUI-session registry was 0x28b06c0 — same object, shifted by arena layout.)
|
|
||||||
- Finding it live: scan host-readable memory (chunked, 8MB) for `u64==8193` (0x2001)
|
|
||||||
followed by a readable ptr, then require a run of count/ptr pairs at stride 0x10.
|
|
||||||
- Registry owner chain: 0 hits for a u64==registry address, so it is reached
|
|
||||||
structurally (object member at some fixed offset), not via an explicit global.
|
|
||||||
|
|
||||||
## Captured tables (SR=44100 project, but internal freq-axis = 48000!)
|
## Registry heartbeat
|
||||||
| reg idx | count | ptr | content |
|
|
||||||
|---------|-------|----------|---------|
|
|
||||||
| [00] | 8193 | 0x2a72600 | identity ~1.0 |
|
|
||||||
| [01] | 8193 | 0x2111140 | **WIN_freq: 0.5 -> 1.0 (saturates; idx1024=0.68, idx2048=0.8)** — the FFT-conv window (0x540658) |
|
|
||||||
| [02] | 8193 | 0x29f2280 | 0 -> ~0.01 (levels/curve) |
|
|
||||||
| [03] | 8193 | 0x29fa300 | **0.596 -> 0.126** = matches known rwin_C0 |
|
|
||||||
| [04] | 8193 | 0x2a02340 | **0.404 -> 0.874** = 1-[03] complement |
|
|
||||||
| [05] | 8193 | 0x2a0a3c0 | 0.0435 -> ~0 (weight, small) |
|
|
||||||
| [06] | 8193 | 0x2a12400 | 0.9565 -> ~1.0 (1-[05]) |
|
|
||||||
| [07] | 8193 | 0x2aca740 | zeros + small negatives |
|
|
||||||
| [0d] | 2049 | 0x2cd0fc0 | **freq-axis 0..23988.3 Hz, spacing 11.713 = 48000/4096** → internal SR=48000 |
|
|
||||||
| [0e] | 8193 | 0x2cd9000 | 2.017 -> 0 (LUT/knee?) |
|
|
||||||
| [0f] | 16384 | 0x2ce9080 | same as [0e] doubled |
|
|
||||||
| [10] | 16384 | 0x2cf90c0 | 1.2914 -> 0 |
|
|
||||||
| [12] | 16384 | 0x2a62540 | 11.29.. (scattering) |
|
|
||||||
| [14]/[15] | 8193 | .. | first-fire IR? 0,0.022,0.104,0.084,0.018,0 |
|
|
||||||
| [17] | 32768 | 0x2d29140 | 0.9999 -> ~1 (ramp) |
|
|
||||||
| [19] | 32768 | 0x2d69200 | 1.2915 -> 1.0 |
|
|
||||||
| others | 32768/65536 | .. | ones / ramps (FFT plans, mirrors) |
|
|
||||||
|
|
||||||
- Key numeric check: registry[03] head 0.5960761, idx1024 0.168, idx2048 0.1257 —
|
Heap run `{u64 count, u64 ptr}` stride 0x10, base `0x29b06c0` (anon 0x2922000) — офлайн, `0x28b06c0` — GUI (сдвиг ареной). Поиск: скан `u64==8193` + readable ptr, требовать run `count/ptr` stride 0x10. Надёжный beacon (vptr/44100-marker — тупик).
|
||||||
byte-identical to earlier GUI rwin_C0 (0.596 -> 0.126). Confirms registry IS the
|
|
||||||
authoritative per-bin weight source; tables are stable across sessions/SR.
|
|
||||||
- **FREQ-AXIS uses internal SR=48000 regardless of project 44100** (spacing 11.713).
|
|
||||||
This reconciles "rwin tables at 48k" even when rendering 44.1k projects.
|
|
||||||
|
|
||||||
## Files saved (handoff/)
|
## Captured tables (SR=44100 project → internal freq-axis 48000)
|
||||||
- `rtwin_freq_44100.npy` — WIN_freq (8193 f32): 0.5 -> 1.0 (this is live 0x540658 window)
|
|
||||||
- `rtfreqaxis_48000_internal.npy` — freq-axis (2048 f32, 0..23988.3, spacing 11.713)
|
|
||||||
- `rtwa_596.npy` — [03] 0.596->0.126
|
|
||||||
- `rtwb_404.npy` — [04] 0.404->0.874
|
|
||||||
- `rtwc_043.npy` — [05]
|
|
||||||
- `rtwd_956.npy` — [06]
|
|
||||||
- Full raw snapshot: /tmp/snap_all.bin (318MB, entries {lo,sz,bytes}), registry.txt list.
|
|
||||||
|
|
||||||
## Method notes (repro)
|
| idx | count | content |
|
||||||
- rtsnap_fast.py: spawn reaper render_long, find host (soothe2 in maps, not reaper),
|
|-----|-------|---------|
|
||||||
sleep 6s (tables built), pread ALL readable maps chunked 8MB -> snap_all.bin.
|
| [00] | 8193 | identity ~1.0 |
|
||||||
- pread of large anon regions can EIO -> MUST chunk (8MB); whole-region pread loses data.
|
| [01] | 8193 | **WIN_freq 0.5→1.0** (idx1024 0.68, idx2048 0.8) = live `0x540658` |
|
||||||
- Scan ~0.1s for 318MB once chunked; far cheaper than object-base scan.
|
| [03] | 8193 | **0.596→0.126** = `rwin_C0` (byte-identical GUI) |
|
||||||
- Earlier vptr-based (rtobj/rtdump2/rtall) and 44100-marker scans all failed because
|
| [04] | 8193 | 0.404→0.874 = 1−[03] |
|
||||||
the DSP object has NO static vptr match in a fresh render (host dies / fields live
|
| [05] | 8193 | 0.0435→~0 |
|
||||||
only during audio) and ctor field +0x24 != 44100 live. The registry run is the
|
| [06] | 8193 | 0.9565→~1.0 |
|
||||||
reliable beacon.
|
| [0d] | 2049 | **freq-axis 0..23988.3 Hz, 11.713 = 48000/4096** → internal SR 48000 |
|
||||||
|
| others | 8193/16384 | опоры, FFT-планы |
|
||||||
|
|
||||||
## Remaining (for twin IIR attack/release per-bin)
|
**Проверка:** `[03]` head 0.5960761 byte-identical GUI — registry authoritative. **Freq-axis spacing 11.713 ⇒ internal SR 48000** независимо от проекта 44100.
|
||||||
- twin state A/B (342 double per-bin IIR states) still not uniquely located live;
|
|
||||||
short renders keep them ~0. They are NOT the registry tables.
|
|
||||||
- Next: render_long + capture at t=10-20s into sustain, then locate the per-bin
|
|
||||||
attack/release smoothing coefficients (0x540888/88c set, converted via ln(10)/20)
|
|
||||||
inside the arena near registry.
|
|
||||||
|
|
||||||
## 2026-08-19b (two-point snapshots t1=8s, t2=40s of render_long 180s)
|
## Files saved
|
||||||
- rtsnap2.py: TWO snapshots of the SAME 180s render at t1/t2; each 318MB/547regs ~0.2s.
|
|
||||||
- Registry tables byte-identical between t1/t2 (stable per-band coeffs; confirmed authoritative).
|
|
||||||
- Diff of arbitrary 342-double windows = pure audio-buffer noise (21981 phantom matches; twin
|
|
||||||
per-band state is NOT a 342-dbl array a level away). Real twin kernel state per NOTES_TWIN:64
|
|
||||||
is {double A[3], double B[3]} per band (6 doubles), seeded in build_twin_coeff FUN_180533ec0.
|
|
||||||
- Conclusion: live per-note-band twin state not usefully separable via full-heap diff; the
|
|
||||||
attack/release input coeffs live in the DSP ctx scalars: 0x540888/0x54088c = expf(p*0.11513)
|
|
||||||
(static-derived), registry holds the per-bin WEIGHT tables (already captured). Twin kernels
|
|
||||||
themselves validated statically (twin_check max|err|=1.27e-5). => Step C goal (window + axes +
|
|
||||||
weights + kernel parity) is effectively CLOSED; only scalar A/R params remain, derived from RPP
|
|
||||||
params, no live capture needed.
|
|
||||||
|
|
||||||
## 2026-08-20 (P1.5: live level-tracker A[] / ctx — CONFIRMED UNREACHABLE)
|
- `handoff/rtwin_freq_44100.npy` — WIN_freq 8193 f32 (live 0x540658)
|
||||||
- rtctx.py (repo): full pipeline = spawn render_long offline → find yabridge-host → chunked
|
- `handoff/rtfreqaxis_48000_internal.npy` — 2048 f32
|
||||||
snapshot → registry-scan → ctx discovery (marker base+0x24==40000.0f ∧ base+0x540658==window).
|
- `handoff/rtwa_596.npy`, `rtwb_404.npy`, `rtwc_043.npy`, `rtwd_956.npy` — [03]–[06]
|
||||||
- Two FRESH captures (host live mid-render): registry found (0x28b06c0 GUI / 0x29b06c0 offline,
|
- Raw snapshot: `/tmp/snap_all.bin` (318MB, chunked 8MB pread — иначе EIO)
|
||||||
[00].cnt=8193 identity, [01]/[03] = window/weights). But:
|
|
||||||
- the window ptr (0x2962??? live) appears as a heap value ONLY inside the registry entry
|
|
||||||
(0x???6c8 self-field), never as a field of a larger ctx object;
|
|
||||||
- bases where +0x24==0x472c4400 (40000.0f) exist only in the plugin image (.data/rodata
|
|
||||||
0x180535d62/0x1805607e5/0x181414341) — inline code constants, no live DSP object.
|
|
||||||
- => A standalone DSP ctx object with {+0x24==40000, +0x540658==winptr} does NOT exist in the
|
|
||||||
heap (the DSP object is the registry itself / its buffers are the registry targets).
|
|
||||||
- CONFIRMS the 2026-08-19b conclusion: per-bin level-tracker A[] (0x4c0528/0x3c0510/0x2c04f8)
|
|
||||||
and mask scalars are NOT live-separable with the current registry/ctx tools; they are
|
|
||||||
STATIC/DERIVED from RPP params (0x540888/88c = 10^(att/20), 0x540870 = expf((p·c4348+c44a4)·0.11513),
|
|
||||||
0x54087c = raw band/mix). Two unknown constants remain from the (lost) binary: 0x24c4348, 0x24c44a4.
|
|
||||||
- => P1.5 "live capture" is a dead end; scalars must be derived statically or the two missing
|
|
||||||
constants recovered from the original soothing_mem.bin (not currently present in workspace).
|
|
||||||
|
|
||||||
## 2026-08-20c (BREAKTHROUGH: LEVEL-PATH OBJECT captured live in /tmp/snap_rt.bin)
|
## Method (repro)
|
||||||
|
|
||||||
The BandConfig A/B/gamma (roadmap gap 2, block of F2/F3) is now LIVE-CAPTURED.
|
- `rtsnap_fast.py`: spawn `reaper -renderproject render_long`, find host `soothe2` in maps, sleep 6s, chunked pread ALL → `snap_all.bin`. Scan 0.1s/318MB.
|
||||||
Read-only scan of the existing realtime snapshot `/tmp/snap_rt.bin` (ctx 0x2370040,
|
- `rtsnap2.py`: two-point diff — registry byte-identical (stable).
|
||||||
render_long.rpp) — no new capture needed.
|
- Live ctx `0x2370040` (`+0x24==48000`, `sens>100`) — единственный populated, остальные empty. `handoff/rtctx_live.json`.
|
||||||
|
|
||||||
### Method (repro, ~2s)
|
## Live ctx (realtime playback, `rtctx_live.json`)
|
||||||
1. Level-path fingerprint = per-band **level_gain pair buffer**: 0x400 f32 pairs
|
|
||||||
(`[level, gain]`), with `level[j] == j/1024` exactly (level[0]==0.0, step 1/1024).
|
|
||||||
Vectorized scan (2nd derivative of level slots == 0 + level[0]==0.0) finds them.
|
|
||||||
2. Six such buffers at stride 0x2020..0x2040 (band0: 0x4083020, b1: 0x4085040,
|
|
||||||
b2: 0x4087080, b3: 0x40890a0, b4: 0x408b0e0, b5: 0x408d100).
|
|
||||||
3. Find u64 refs to the six → consecutive slots stride 0x18 at **+0xe0+band*0x18**
|
|
||||||
→ object base = **0x3975460** (level-path object).
|
|
||||||
|
|
||||||
### Level-path object (base 0x3975460, region unknown / heap)
|
- Pointers `0x540688` identity, `0x540698` window, `0x5406b8/c8/d8/e8` WA/B/C/D, `0x540748` warp, `0x540758` freqaxis (`v85=995.6 Hz`).
|
||||||
- `+0x178` = band-list ptr → 0x32c0c60
|
- Scalars: `0x540870=440.955`, `0x540880=25.0`, `0x540884=10.0`, `0x540888/88c=1.0`, `0x54087c=1.0/10.0` (selectivity/sharpness).
|
||||||
- `+0x180` → BandConfig 0x32c0aa0: **A=-24.0, B=+28.0, gamma@0xc=1.0, byte flag@0x10=0** → linear, no callback@0x90
|
- `acc/f6f8` arrays **zero** в стационаре playback — combine idle.
|
||||||
- `+0x188` → BandConfig 0x32c09c8: A=16.0, B=20000.0, gamma@0xc=1.0, flag=0 (freq-range shaped cfg; +0x18.. floats 0.55,7.13,2.77,2.718 = nonlinear shaper consts)
|
- Bands curve `R=1/mask` peak следует fc (bin43@500 → bin85@1000), `0x5407f8` min 1.0. Применённый фильтр ≠ pointwise копия R (нужен FFT-conv).
|
||||||
- `+0x4198 + band*0x2000` = **band mask doubles**, 512 usable per band:
|
|
||||||
band0 ~1.0 const; band1 1.001→1.216 (rising); band2 0.999→0.579 (falling);
|
|
||||||
band3 1.291→1.002 (falling); band4/5 1.0→~0.983
|
|
||||||
- `+0xe0+band*0x18` → per-band level_gain pair buffers (live LUT output already has
|
|
||||||
gains: b0 0.53123 const, b1 0.5314→0.535, b2 const, b3 0.598→, b4 const, ...; many
|
|
||||||
bands `~0.531` because mask≈1.0 & render_long default cfg)
|
|
||||||
|
|
||||||
### Interpretation / next
|
Дальше — `handoff/NOTES_LEVEL.md:24mm11+` (live ptrace) + `handoff/BLOCKMAP_529fe0.md`.
|
||||||
- The captured A/B/gamma are the **default render_long config** (A/B semantics =
|
|
||||||
level-scaler LUT min/max; rendering default band). To get the A/B/gamma of a SPECIFIC
|
|
||||||
band shape (t1kq_only1_1000 etc.) re-run rtctx_rt.py with that test RPP and re-scan
|
|
||||||
the same fingerprint (base offset shifts). Method is now automated.
|
|
||||||
- This UNBLOCKS the parametric band-LUT 0x563440/0x563a60 as a structural source
|
|
||||||
(t=(x−A)/(B−A), clamped, ^gamma, ×norm) instead of fitted Pchip.
|
|
||||||
- Dump helper: /tmp/dump_levelpath.py, /tmp/probe_base.py.
|
|
||||||
The dead end above was wrong — the missing piece was REALTIME audio playback, not more scanning.
|
|
||||||
`-renderproject` uses the OFFLINE audio engine (fields live "only during audio", per earlier note);
|
|
||||||
the ctx object only materializes during a realtime transport play.
|
|
||||||
|
|
||||||
### Method (works)
|
|
||||||
- play.lua (repo): `reaper.Main_OnCommand(1007)` (Transport:Play) + hold ~300s.
|
|
||||||
- rtctx_rt.py (repo): `reaper render_long.rpp play.lua` → find yabridge-host → chunked snapshot
|
|
||||||
(~796MB, 1056 regs) DURING playback → scan heap for the ctx.
|
|
||||||
- ctx marker that WORKS live: **+0x24 == 48000.0f (0x473b8000)**, NOT 40000.0f (40000 was the
|
|
||||||
static ctor rodata value; live it is the internal SR = 48000, confirming NOTES_CAPTURE SR=48000).
|
|
||||||
|
|
||||||
### Result (captured live, saved handoff/rtctx_live.json)
|
|
||||||
- **ctx = 0x2370040** (region 0x2022000). Field pointers (all point at registry tables):
|
|
||||||
0x540688=identity([00] 0x2962580), 0x540698=window([01] 0x14b4240), 0x5406a8=levels([02]),
|
|
||||||
0x5406b8=WA([03]), 0x5406c8=WB([04]), 0x5406d8=WC([05]), 0x5406e8=WD([06]),
|
|
||||||
0x540748=warp([12] 0x14bc280), 0x540768=LUT-knee([14]). (NOTE: offsets +0x40 from the
|
|
||||||
earlier static table — window is 0x540698 live, not 0x540658 as in the f_52b570 disasm label.)
|
|
||||||
- **Mask scalars (float)**: 0x540870=440.955 (sens), 0x540874=1.0, 0x540878=1.0, 0x54087c=1.0,
|
|
||||||
0x540880=25.0, 0x540884=10.0, 0x540888=1.0 (attack=10^0), 0x54088c=1.0 (release=10^0),
|
|
||||||
0x540890=0, 0x540894=1200.0.
|
|
||||||
- **level-tracker A[] (341 double each, per-bin IIR attack/release coeffs)**:
|
|
||||||
- 0x4c0528 (attack): 0 → 0.340, 0.348, ... monotonic rising, plateau 0.6921 @bin>=319.
|
|
||||||
- 0x3c0510 == 0x2c04f8 (release): 0 → 0.000753, 0.000885, ... slow small rise.
|
|
||||||
Full arrays in handoff/rtctx_live.json (keys A_4c0528, A_3c0510, A_2c04f8).
|
|
||||||
- BandConfig @ctx+0x188 is NOT populated here (zeros) — it lives at a different offset or only
|
|
||||||
during band processing; still TBD (but LUT curve params A/B/γ are RPP-derived per roadmap).
|
|
||||||
- Two more ctx-like bases found (0x2120040, 0x1780040) also have +0x24==48000; 0x2370040 is the
|
|
||||||
populated one (0x2120040 has 0x540658..698 = 1.0 fill pattern — likely a second/free instance).
|
|
||||||
|
|||||||
+488
-1942
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
# NOTES_LEVEL — оглавление журнала
|
||||||
|
|
||||||
|
> **Навигатор:** `NOTES_LEVEL.md` — живая голова (25a+, 2026-09-02, ~470 строк). Архив 2026-08-18—2026-08-23 → [`handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md`](archive/NOTES_LEVEL_2026-08-18_2026-08-23.md) (4024 строки). Статус TOTAL → [`README.md:13`](../README.md).
|
||||||
|
|
||||||
|
## Живая голова (`handoff/NOTES_LEVEL.md`)
|
||||||
|
|
||||||
|
| Дата | Заголовок | Суть |
|
||||||
|
|------|-----------|------|
|
||||||
|
| 24mm5–24mm7 | Полная буферная карта, DESIGN 1802a24c0 | Два круга log→exp, bidir×2 в лог-домене |
|
||||||
|
| 24mm8-бис | Точка входа следующего раунда | opB 18004ca80, поиск γ |
|
||||||
|
| 24mm9 | FIR-цепь MIN-PHASE, 0.0065 dB | Цепь 52b60c+, RFFT близнецы, df0 complex-mul, валидация |
|
||||||
|
| 24mm10 | EXP ядро полностью, парадокс q | Формула 1803831c0, нормировки s_f/s_i=1 |
|
||||||
|
| 24mm10-бис | Итог близнецов radix-4 | INV/FWD 2048, твидлы 548, без масштабов |
|
||||||
|
| 24mm11 | LIVE ptrace, цепь до df0 бит-точна | wine_ptrace_trace, Y_model=1.0, df0 complex-mul |
|
||||||
|
| 24mm12 | Детекторный каскад vt+0x28=180529c60 | Оркестратор 5300f0, vtable карта, 0x281 байт |
|
||||||
|
| 24mm13 (+доп) | Хелперы каскада 529c60 | 5355d0→16140, 530080, 20f0/1850/1a00, рекуррентия Haar |
|
||||||
|
| 24mm14 | Каскад декодирован — 3 фазы | `|z|` → Haar×2 → peak/sin/w/blend 5407a8 |
|
||||||
|
| 25a (2026-09-02) | chain_9_19 staged, IIR4-генератор, k-mapping dedup, аудит | IIR4 double (movsd), generate_iir4_coefs, RT_CASC/RT_IIR4_GEN gates, baseline 2.689 |
|
||||||
|
|
||||||
|
## Архив (`handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md`) — по периодам
|
||||||
|
|
||||||
|
| Период | Ключевые темы | Где |
|
||||||
|
|--------|---------------|-----|
|
||||||
|
| 2026-08-18 | Две twin-цепочки, FUN_180563440/563ce0, 530d30 веса, FUN_180529fe0 consumer, 0x530d30 NEGATIVE, bridge B.12 | строки 1–300 |
|
||||||
|
| 2026-08-18h2–h3 | Runtime capture, поле 0x540658, registry 0x28b06c0/0x29b06c0, window 0.5→0.8, freq-axis 48000 | 475–580 |
|
||||||
|
| 2026-08-19 | LUT-leg joint fit, level-tracker, decomp lock, registry heartbeat, P4 mask-chain, SR mismatch | 580–900 |
|
||||||
|
| 2026-08-20i–x | P2 kernels 8d60/3c40, twin-mask, F0 gate determinism, parametric LUT benches, wrappers | 900–1100 |
|
||||||
|
| 2026-08-21b–d | IIR3 bidir, combine scope, structural corpus 2.286, reduction-law affine, Phase A | 1100–1360 |
|
||||||
|
| 2026-08-22a–f | Phase B refuted, Step7 GUI-only, param bridge, floor −20.72 blend·ln10/20 | 1360–1600 |
|
||||||
|
| 2026-08-22g–p | NO-LUT, white-noise probe, bare chain, pool/scale/floor, joint-fit, dual diag, affine port | 1600–1970 |
|
||||||
|
| 2026-08-23a–z | Скалярный закон насыщен, 529fe0 по raw asm, faithful-цепь, теорема α, спрединг отвергнут | 1970–2300 |
|
||||||
|
| 24j–24ab | Применение декодировано: A=1.019·V^1.8345, FIR=exp(scratch), двухстадийность γ₀=1.79 | 2750–3550 |
|
||||||
|
| 24bb–24kk3 | k-маппинг, q-независимость, далёкий тон локально, дистанционная серия, симулятор не замкнулся | 3550–4000 |
|
||||||
|
| 24ll–24mm4 | multi6, g_k пер-пиковый, archive закрытие, γ=1.760561, точ. законы отвергнуты | 4000–4024 |
|
||||||
|
|
||||||
|
## Как читать
|
||||||
|
|
||||||
|
1. **Старт:** `README.md:13` (статус) → `AGENTS.md` (runbook) → `BITEXACT_PLAN.md:1` (3 шага) → этот индекс → `handoff/BLOCKMAP_529fe0.md`.
|
||||||
|
2. **Детали по теме:** `rg -n "24mm9\|Q-НЕЗАВИСИМОСТЬ\|Haar"` в соответствующем файле.
|
||||||
|
3. **Полный поиск:** `rg -n "cut_D\|lvl_raw\|5407c8" handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md handoff/NOTES_LEVEL.md`.
|
||||||
|
|
||||||
|
## Дубли устранены
|
||||||
|
|
||||||
|
- `UPDATE 2026-08-22g` дубль (строки 1577/1591 в архиве) — логически один.
|
||||||
|
- `UPDATE 2026-08-20s` дубль (974/977) — один.
|
||||||
|
- Заголовки унифицированы: `## ============ UPDATE` → `##` в живой голове; архив сохранён как есть для истории.
|
||||||
+23
-70
@@ -1,76 +1,29 @@
|
|||||||
# Twin FUN_180535880 transcription (Phase 2)
|
# Twin FUN_180535880 — краткая справка (детали → `dsp/twin.cpp`)
|
||||||
|
|
||||||
## PHASE-3 CALLER + GRID (2026-08-18)
|
> **Статус:** транскрибирован, gate PASS `dsp/build/twin_check` max rel err 1.27e-5 (float-parity). Полный журнал 2026-08-18 → [`handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md`](archive/NOTES_LEVEL_2026-08-18_2026-08-23.md) + [`handoff/NOTES_LEVEL_INDEX.md`](NOTES_LEVEL_INDEX.md).
|
||||||
- Caller FUN_180536300 (decomp 6218 / sp_180536300.txt) decoded at buffer level:
|
|
||||||
- params: param_1=top DSP, param_2=out, param_3=?, param_4=band obj, param_5=cell count,
|
|
||||||
param_7=scratch (>=52*N bytes complex).
|
|
||||||
- scratch: +0..8N A-accum, +8N..16N B-accum, +16N..24N z-powers, +24N..32N rotor-in
|
|
||||||
(fill 1.0), +32N..36N phase ramp lVar1, +36N..44N rotor-out z1 (r14), +44N..52N
|
|
||||||
lVar3(1.0)/lVar4(0.0) for interleave+negate+exp build.
|
|
||||||
- 0x18052da00(lVar1, scale=2pi/(iVar7*sr), N) = ramp fill (float, via 0x180001a00);
|
|
||||||
- twin always = FUN_180535880 (type-info compare of same addr -> equal);
|
|
||||||
sibling FUN_180536f90 is dead.
|
|
||||||
- 0x18052dbc0(param_2, lVar4, N) = cplx interleave -> out=(cos,sin)=e^{+i theta}.
|
|
||||||
- GRID LAW (verified from trace.txt case8 + constructor):
|
|
||||||
- os = *(int*)(param_1+0x240080) = 4 (written 0x18052ce3e `mov ...,0x4`)
|
|
||||||
- sr = *(float*)(param_1+0x24) = 44100.0
|
|
||||||
- fs_total (generator) = os*sr = 176400 => resonance shape f/fc invariant to fs.
|
|
||||||
- per-cell phase step = 2*pi/(os*sr); cell k <-> freq = k/os Hz (0.25 Hz steps).
|
|
||||||
- Phase-1 model FS=44100 was a valid normalization (res(f) depends only on f/fc).
|
|
||||||
- case8 generator call (trace 33361..33490, switchD_1805318ab):
|
|
||||||
- +0x814 numBands = 1, +0x103 mode flag = 1
|
|
||||||
- param_5 (float, xmm4) = 10^(sens_dB/20) (pow10 10.0^([band+0x101]/20.0))
|
|
||||||
- gain = sqrtf(param_5) = 10^(sens_dB/40); model fit gain=4.132 => sens_dB@0x101 ~= 24.65
|
|
||||||
- freq = [band+0x804], Q = [band+0x80c], fs_total = os*sr (w0 = max(fc,2)*2pi/fs_total)
|
|
||||||
|
|
||||||
## PHASE-2 GATE: PASSED (2026-08-18)
|
## PHASE-3 Caller + Grid
|
||||||
- `dsp/twin.hpp` + `dsp/twin.cpp` written: build_twin_coeff (FUN_180533ec0 double pipeline
|
|
||||||
-> cvtpd2ps), cplx_div_exact (0x181a77520: mulps |A|^2, rcpps + Newton, NaN guard),
|
|
||||||
cplx_mul_exact (0x18000ad60 scalar body @0x18000ae00: vfmaddsub213ps form),
|
|
||||||
twin_apply (seed A0/B0 + 2 vfmadd213ss Horner stages + 2*div).
|
|
||||||
- twin_check gate: fc-scan grid (7 fc, Q=0.9999978, gain=4.132, tone 1000 Hz)
|
|
||||||
max |rel err| = 1.27e-5 vs double reference res_at (float rcpps parity).
|
|
||||||
DUAL sweep (Q=0.1..10 x tones 500/2000, fc=500) max = 1.23e-5. Both PASS.
|
|
||||||
- Constants locked: A1 = B1 = cos(w0)*-2.0, z1 = conj(exp(+i*theta)),
|
|
||||||
z2 = cplx_mul(z1,z1), out = 2*B/A.
|
|
||||||
|
|
||||||
## Confirmed scalar math (from generator + cplx-div kernels)
|
- Caller `FUN_180536300` (6218): scratch 52·N, `0x18052da00` ramp `2π/(os·sr)`, `0x18052dbc0` interleave `e^{+iθ}`. Twin всегда `180535880`, sibling `180536f90` dead.
|
||||||
- Generator FUN_180533ec0 (per band, case8):
|
- Grid: `os=4` (`+0x240080`), `sr=44100` (`+0x24`), `fs_total=176400`, `phase=2π/176400`, cell `k ↔ k/4 Hz` (0.25 Hz).
|
||||||
`fVar1 = sqrtf(param_5)` (param_5 = pow(10, sens/20) via cd6 IAT stub)
|
- case8: `+0x814=1`, `+0x103=1`, `param_5=10^(sens/20)`, `gain=√param_5=10^(sens/40)` → fit 4.132 ⇒ `sens≈24.65 dB`.
|
||||||
`w0 = max(2.0, freq)*2*pi / fs_total` (fs_total = 2*FS frame?)
|
|
||||||
`p = sin(w0)*0.5 / Q`
|
|
||||||
`dVar5 = p*fVar1` (A tap), `param_4 = p/fVar1` (B tap)
|
|
||||||
A = [1+dVar5, dVar2, 1-dVar5], B = [1+param_4, dVar2, 1-param_4]
|
|
||||||
where dVar2 = cos(w0) * DAT_1824c46b0 (=-2.0? VERIFY)
|
|
||||||
- Twin FUN_180535880: for each FFT bin z (rotor):
|
|
||||||
`res(z) = 2 * B(z) / A(z)`, B(z)=B0+B1*z+B2*z^2, A(z)=A0+A1*z+A2*z^2
|
|
||||||
implemented as complex FMA accumulation over 3 taps:
|
|
||||||
r15 (A-accum) and r12 (B-accum) via 0x180001fd0 vfmadd213ss (coeff [rsp+k*4+0x28]=A_k, [+0x38]=B_k)
|
|
||||||
rbp buffer = z powers via rotor ops (0x1800019a0 fill / 0x1800021e0 / 0x1800018b0 / 0x180001af0 / 0x180001dc0)
|
|
||||||
final 0x180001e20 -> 0x181a775a0 cplx-div: out = 2*B/A (xmm7=0x40000000=2.0, xmm6=NaN guard)
|
|
||||||
- cplx-div kernel 0x181a77520: |A|^2 via shufps 0x88/0xdd, rcpps + 1 Newton step (xmm7=2.0), NaN guard via cmpeqps.
|
|
||||||
Matches rotor_kernel.hpp cplx-div exactly.
|
|
||||||
|
|
||||||
## Dispatch (all stubs share global 0x1826159a0 = 4 = AVX2)
|
## PHASE-2 Gate
|
||||||
0x180001d00 -> 0x180009860 -> 0x180040d40 (cplx fill const, NT stores)
|
|
||||||
0x1800019a0 -> 0x180004200 (float fill -> 0x181a63fe0)
|
|
||||||
0x1800021e0 -> 0x1800130e0 (large AVX, 0x450 stack, rotor/exp?)
|
|
||||||
0x1800018b0 -> 0x180003040 (-> 0x181a557c0)
|
|
||||||
0x180001af0 -> 0x180005a20 (loop -> 0x18001a5a0)
|
|
||||||
0x180001dc0 -> 0x18000ad60 (cplx op r14*rbp)
|
|
||||||
0x180001e20 -> 0x18000b820 (-> 0x181a775a0 cplx-div 2B/A)
|
|
||||||
0x180001fd0 -> 0x18000f620 (vfmadd213ss: out = src*coeff + dst)
|
|
||||||
|
|
||||||
## Coefficient layout in twin (param_3 = {double A[3], double B[3]})
|
- `dsp/twin.cpp`: `build_twin_coeff` (FUN_180533ec0), `cplx_div_exact` (rcpps+Newton), `cplx_mul_exact` (vfmaddsub), `twin_apply` `2·B/A`.
|
||||||
stack: +0x28=A0 +0x2c=A1 +0x30=A2 | +0x38=B0 +0x3c=B1 +0x40=B2
|
- Gate: fc-scan 7fc + dual Q0.1..10 PASS.
|
||||||
loop k=0..2 uses A_k via [rsp+k*4+0x28], B_k via [rsp+k*4+0x38]
|
|
||||||
r15 = out A-accum, r12 = out B-accum, rbp = z^1..z^2 buffer, r14 = rotor(z) input
|
|
||||||
|
|
||||||
## PHASE-1 LOCK (empirical, 36 points, LUT chain B.11)
|
## Scalar math
|
||||||
Effective sqrtf(param_5) needed ~= 4.13 (NOT 1.995).
|
|
||||||
=> param_5 ~= 17.07 = 10^(1.2325) => internal sens_stored ~= 24.65 dB (not XML 12.0)
|
- Generator: `fVar1=√param_5`, `w0=max(2,freq)·2π/fs_total`, `p=sin(w0)·0.5/Q`, `A=[1+p·fVar1, −2cos w0, 1−p·fVar1]`, `B=[1+p/fVar1, −2cos w0, 1−p/fVar1]`.
|
||||||
OR equivalent. RULED OUT: sqrt(10^(12/20))=1.995 (rmse 1.38/5.68);
|
- Twin: `res(z)=2·B(z)/A(z)`, Horner `0x180001fd0`, `cplx-div 0x181a77520`, `z2=z1·z1`.
|
||||||
10^(12/20)=3.981 direct (t1kq 0.056 but t1k 0.934); |2B/A|^2 (1.5/6.6).
|
|
||||||
Sens setters (0x18053795d, 0x18056f002) both store RAW value; +0x808 scaling is HOST-side
|
## Dispatch
|
||||||
(plugin controller, outside dump) => must be resolved via bit-exact render or host trace.
|
|
||||||
NOTE: single-gain compromise 4.132; t1kq wants 4.055, t1k wants 4.137 (tilt residual).
|
Стабы → `0x1826159a0=4` (AVX2). Layout `param_3={A[3],B[3]}` `+0x28/+0x38`, coeff `[rsp+k*4]`.
|
||||||
|
|
||||||
|
## PHASE-1 lock
|
||||||
|
|
||||||
|
`√param_5≈4.13` (не 1.995), `sens≈24.65 dB` (host ×2 над XML 12.0). Setter `+0x808` host-side.
|
||||||
|
|
||||||
|
Детали декомпа — `handoff/nls_dasm/twin*.dis`, `dsp/twin.cpp:27`.
|
||||||
|
|||||||
+6
-254
@@ -1,256 +1,8 @@
|
|||||||
# SESSION HANDOFF — soothe2 detector reverse → bit-exact render
|
# SESSION_HANDOFF — перенесён в архив
|
||||||
|
|
||||||
Prepared: 2026-08-18 (checkpoint end-of-session: commits c8f97e4 + 60bf3a2 pushed). Start: READ THIS FIRST.
|
> **Исторический чекпоинт 2026-08-18 (коммиты c8f97e4+60bf3a2).**
|
||||||
|
> Актуально: декомп DSP-ядра ~95% закрыт, `FUN_180529fe0` mono-path, `FUN_180563440/563ce0/563a60` расшифрованы, дизассемблы в `handoff/nls_dasm/` (134 файла).
|
||||||
|
> Полный текст сохранён: [`handoff/archive/SESSION_HANDOFF_2026-08-18.md`](archive/SESSION_HANDOFF_2026-08-18.md) (256 строк, инвентарь декомпа §0, ключевые адреса §2, Phase-5 план §6).
|
||||||
|
> Навигация → [`handoff/NOTES_LEVEL_INDEX.md`](NOTES_LEVEL_INDEX.md) + [`handoff/BLOCKMAP_529fe0.md`](BLOCKMAP_529fe0.md) + [`README.md:13`](../README.md).
|
||||||
|
|
||||||
> **2026-08-20 UPDATE**: см. актуальный канон — `AGENTS.md` и `handoff/NOTES_LEVEL.md`.
|
Кратко: `decomp_funs.txt` 312K строк, RTTI `rtti_dsp.json`, bridge `C=g·LUT+w·warp^a` (rmse 0.236, позже заменён VLAW). Для продолжения читать `AGENTS.md` → `NOTES_LEVEL_INDEX.md`.
|
||||||
> Нижеследующее «NOT DECODED»/«missing body» УСТАРЕЛО: `FUN_180529fe0` mono-path,
|
|
||||||
> `FUN_180563440/563ce0/563a60` — расшифрованы, полные дизассемблы скопированы в
|
|
||||||
> `handoff/nls_dasm/` (134 файла). Этот файл — исторический чекпоинт.
|
|
||||||
|
|
||||||
## 0. DECOMPILATION INVENTORY (2026-08-19 — what's decoded, where, and what's missing)
|
|
||||||
Goal: bit-exact parity is gated by EXACT tables/window/constants. The chunk-level model hits
|
|
||||||
err ≤0.62 dB (dual) / ≤0.19 dB (al_*) — to go sample-exact we need precise values from the binary.
|
|
||||||
|
|
||||||
### Static decomp assets IN REPO (use these, don't re-decompile):
|
|
||||||
- `ghidra-proj/soothe2.rep` — full Ghidra project (vst3 at ImageBase 0x180000000).
|
|
||||||
- `decomp_funs.txt` (312K lines, ~1640 functions), `fun_map.txt` (2285 addr→FUN), `consts.txt` (11602),
|
|
||||||
`decomp_dsp.txt` / `decomp_vtables.txt` / `decomp_candidates.txt` / `focus_decomp.txt`. Generators:
|
|
||||||
`Dump*.java`, `ImportRtti*.java`, `ListFuns.java`, `SearchRefs.java` (+ headless logs).
|
|
||||||
- RTTI: `rtti_dsp.json` / `rtti_full.json` — class hierarchy
|
|
||||||
`SpectralProcessor<float,7,1>`, `Soothe2ModuleBase<float,1>`, `FilterGraph<float,6,0x400>`,
|
|
||||||
`DigitalFilter<float,0xBA,1>`, `IIRFilterExtended<float,1>`, `AudioProcessingModule<float,1>`.
|
|
||||||
- `handoff/nls_dasm/` — 120 hand-picked `.dis` (twin, iface_18052da00/dbc0, fft, generator, ctor).
|
|
||||||
- `dsp/` — working C++ transcription (twin, detect, freqpath, spectral, fft_stage) + harness;
|
|
||||||
`build/twin_check` passes float-parity gate (§2).
|
|
||||||
|
|
||||||
### KEY DSP ADDRESSES — decoded / not decoded:
|
|
||||||
DECODED (formula-level, notes at NOTES_TWIN.md / NOTES_LEVEL.md):
|
|
||||||
- twin kernel `FUN_180535880`; generator `FUN_180533ec0`; caller `FUN_180536300`; dead sibling `180536f90`.
|
|
||||||
- level-weight formula `FUN_180530d30` (0x540880/884, warp 0x5406a8=0.87·x/(1+x/7.942), w=0.1^(...)).
|
|
||||||
- mask-apply entry `FUN_180529fe0` (accumulator 0x5407c8 += w·res; mask *= warp; FIR *= 0x540658).
|
|
||||||
- FFT-conv loop 0x52b550-0x52b8b5 (plan 0x540530, windows 0x540548/550/598, freq-axis 0x540698=offline const).
|
|
||||||
- level-path map: `0x563440` (LUT curve +0x188, 6 band-slots, combine→+0x2198), `0x56e3e0` (twin-mask factory),
|
|
||||||
`0x563ce0` (IIR level-tracker INIT only).
|
|
||||||
|
|
||||||
NOT DECODED / MISSING FROM decomp_funs.txt (critical): ⚠️ → РЕШЕНО (2026-08-19/20), см. выше
|
|
||||||
- `FUN_180529fe0` body — РЕШЕНО (decomp в `/tmp/consumers_out.txt`, mask-цепь в `dsp/framed_model.cpp`).
|
|
||||||
- `FUN_180563440` / `FUN_180563a60` (LUT curve + gamma + combine), `FUN_180563ce0` — РЕШЕНО,
|
|
||||||
дизассемблы скопированы в `handoff/nls_dasm/` (`f_563440.dis`, `f_563a60.dis`, `f_563ce0.dis`, `f529fe0.dis`).
|
|
||||||
- Window `0x540658` — РЕШЕНО (live-захват, см. `handoff/NOTES_CAPTURE.md`; `rtwin_freq_44100.npy`).
|
|
||||||
- sens source: XML 12.0 → runtime sens_dB≈24.65 (host ×2) not found in dump; `IAT\*0x181bab370` outside dump.
|
|
||||||
|
|
||||||
### Bridge model STATUS (2026-08-19):
|
|
||||||
- `framed_render.py` full STFT frame-render (N=2048, hop=512, sqrt-Hann, twin env tatt=11ms/trel=80ms):
|
|
||||||
`C(f)=G·LUT(xv)+W·warp(f)^A`, G=1.0850, W=0.2819, A=1.1377 (joint dual+al_* refit).
|
|
||||||
- Validation: dual q0.1/1/10 @500+2000 err ≤0.62 dB, envRmse@steady ≤0.78 dB; al_* lv3..24 err ≤0.19 dB.
|
|
||||||
- LUT = slanted al_*-leg (0.366@xv=-0.50 → 0.636@xv=+0.55), replaces flat B.12 (~0.5). Bugfix: clip range
|
|
||||||
must be [LY.min(), LY.max()] not [LY[0], LY[-1]].
|
|
||||||
- Remaining structural residual: −0.6 dB systematic on dual 500Hz (q0.1/q1). Live evidence (avg per-bin
|
|
||||||
gain H=|Y|/|X|): reference mask is FLAT ~-10.2dB across 100-540Hz regardless of Q — model produces
|
|
||||||
res-shaped notch. Hypo tested: freq-smoothing of C(f) fails (kills 2000Hz). NEXT: scalar per-frame xv
|
|
||||||
(broadband level, not per-bin am/res) — see /tmp/smoothtest.py (+ edit scalar=True).
|
|
||||||
|
|
||||||
## 1. Objective (unchanged since session 1)
|
|
||||||
Transcribe the decoded soothe2 detector ("twins" 0x180535880/0x180536f90, 2nd-order resonator) into
|
|
||||||
C++ and reach **bit-exact render parity** with the Reaper reference wavs in `/home/m/soothe-bt/*.wav`
|
|
||||||
(checking dB/magnitude parity first, then byte-diff).
|
|
||||||
|
|
||||||
Path A (bit-exact) chosen. Phase 0,1,2 done. Phase 3,4 advanced. Phase 5 (render diff) is next.
|
|
||||||
|
|
||||||
## 2. DECOMPILATION FINDINGS (2026-08-19 — FUN_180563440, FUN_180563ce0 decoded)
|
|
||||||
|
|
||||||
### FUN_180563440 — LUT curve evaluation + band combine (222 lines disasm)
|
|
||||||
Structure: **3 phases per frame**:
|
|
||||||
1. **1024-bin LUT loop** (0x400 iterations): for each bin k:
|
|
||||||
- x = clamp(k * 0.0009775, 0, 1.0) = k/1024
|
|
||||||
- Band config at +0x188: {A(+0x00), B(+0x04), threshold(+0x0c), flag(+0x10), callback(+0x50)}
|
|
||||||
- **Path 1** (callback exists): vtable call → dynamic LUT
|
|
||||||
- **Path 2** (flag=1, threshold≠1.0): **power-law** → centered = 2*x - 1, then `sign(x) * 10^(log10(|x|) / threshold)` — this is a **compression curve** controlled by sharpness/threshold
|
|
||||||
- **Path 3** (default): **linear interpolation** → `(B - A) * x + A`
|
|
||||||
- Output: double-precision at +0x198, stride 8
|
|
||||||
2. **Twin-mask factory** (FUN_18056e3e0): 6 bands × 1024 bins, stride 0x2000
|
|
||||||
3. **Combine loop**: stereo (max 2 channels), 6 bands, `1 - sum(band_masks)`
|
|
||||||
|
|
||||||
### FUN_180563ce0 — IIR level-tracker INIT (163 lines disasm)
|
|
||||||
- **341 bins** (0x155 iterations), **order-3 IIR** (3 coefficients per bin)
|
|
||||||
- Coefficient: **0.1** (`0x3dcccccd` = IEEE 754 float 0.1)
|
|
||||||
- Initial state: [1.0, 0, 0, 0] and [-1.0, 0, 0, 0] (identity + zero)
|
|
||||||
- Buffer layout: 3 × (16 bytes coeff) per bin, stored at rcx+0x28/+0x40/+0x58
|
|
||||||
- **Not the update loop** — init only; UPDATE is elsewhere
|
|
||||||
|
|
||||||
### FUN_180529fe0 — Coefficient setup (2051 instructions, in decomp_funs.txt)
|
|
||||||
- **vtable method** on Soothe2Module<M,1>
|
|
||||||
- **Lock** at +0x2404dc (atomic test-and-set)
|
|
||||||
- **PRNG state** at +0x2404e0, LCG with offset 0x3cdca
|
|
||||||
- **6-iteration coefficient generation** from 0x5408b0 buffer (LCG-indexed)
|
|
||||||
- **Depth scaling**: `powf(normalized, depth)` at +0x2c
|
|
||||||
- **Mask assembly**: normalize by 0x1a0 (NFFT), × 0x540870 (level weight), × 0x54088c (sharpness), invert
|
|
||||||
- **Copy output** via SIMD memcpy (thunk 0x181ba94b0)
|
|
||||||
|
|
||||||
### FUN_18052e9b0 — SpectralProcessor main (3167 instructions, in decomp_funs.txt)
|
|
||||||
- **Same PRNG + coefficient setup** as FUN_180529fe0
|
|
||||||
- **Band chain**: FUN_18052f500 (interleave) → FUN_18052ee70 (per-bin gain) → FUN_18052d650 (setup) → FUN_18052d920 (window)
|
|
||||||
- **Buffer alloc**: FUN_18052e190 for 0x540668, 0x540698, 0x5406a8, 0x5406b8-e8 (6 bands)
|
|
||||||
- **Depth scaling**: `powf(normalized, depth)` with depth at +0x2c
|
|
||||||
- **Final mask**: `1 - C` (inversion)
|
|
||||||
- **Window application**: `FIR *= 0x540658` (the live-captured window table)
|
|
||||||
|
|
||||||
### Constants verified from binary:
|
|
||||||
| Constant | Address | Value | Meaning |
|
|
||||||
|---|---|---|---|
|
|
||||||
| SCALE | 0x24c3c54 | 0.000977517 | 1/1024 (bin→x) |
|
|
||||||
| ONE | 0x24c3ea4 | 1.0 | clamping max |
|
|
||||||
| TWO | 0x24c41e0 | 2.0 | centering (2*x-1) |
|
|
||||||
| NEG1 | 0x24c4680 | -1.0 | sign flip |
|
|
||||||
| HALF | 0x24c3d8c | 0.5 | threshold |
|
|
||||||
| DEPTH_SCALE | 0x24c4334 | 4.0 | depth range |
|
|
||||||
| DB_CONV | 0x24c43e0 | 8.6859 | 20/ln(10) |
|
|
||||||
| FLOOR | 0x24c4704 | -6.9078 | ln(0.001) |
|
|
||||||
| IIR_COEFF | embedded | 0.1 | attack/release per bin |
|
|
||||||
| IIR_ORDER | embedded | 3.0 | IIR filter order |
|
|
||||||
| LCG_OFFSET | embedded | 0x3cdca | PRNG state advance |
|
|
||||||
|
|
||||||
### Live-captured tables (re-verified):
|
|
||||||
- `rwin_A0.npy` (0x1930100): 0.5→0.8, **frequency window** (NOT warp formula)
|
|
||||||
- `rwin_B0.npy` (0x1938180): 0→3.899, **power-law depth curve** (exponent ~0.66)
|
|
||||||
- `rwin_C0.npy` (0x19401c0): 0.596→0.126, **level-dependent weight**
|
|
||||||
- `r_freqaxis.npy`: 0→23988.3 Hz, 11.71 Hz spacing (48000/4096)
|
|
||||||
|
|
||||||
### Key insight: The warp formula is NOT a table — it's computed at runtime
|
|
||||||
The empirical `0.87*7.942*x/(7.942+x)` is an approximation of a runtime computation
|
|
||||||
in FUN_180563440 that evaluates the LUT curve parametrically. The actual LUT has TWO modes:
|
|
||||||
- **Linear** (default): simple interpolation between A and B
|
|
||||||
- **Power-law** (flag=1): `sign(x) * 10^(log10(|x|) / C)` — compression curve
|
|
||||||
|
|
||||||
## 3. PROOF OF STATE — run this first (5 min)
|
|
||||||
Everything below must reproduce. If `twin_check` fails, the transcription moved stale.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /home/m/re-tools/dsp && cmake --build build && ./build/twin_check
|
|
||||||
# expect:
|
|
||||||
# max rel err = 1.268e-05, PASS = yes (float-parity)
|
|
||||||
# max sweep rel err = 1.233e-05, SWEEP PASS = yes, exit 0
|
|
||||||
```
|
|
||||||
|
|
||||||
`build/soothe2_dsp.so` and `build/twin_check` are current. CMakeLists already has `twin.cpp` + `twin_check` executable.
|
|
||||||
|
|
||||||
## 3. TRANSCRIPTION STATE (summary — details in NOTES_TWIN.md)
|
|
||||||
- `dsp/twin.hpp` + `dsp/twin.cpp` = twin kernel FUN_180535880:
|
|
||||||
- build_twin_coeff (FUN_180533ec0 double pipeline → cvtpd2ps floats)
|
|
||||||
- generator formula (confirmed): `fVar1=sqrtf(param_5)`; `w0=max(2.0,freq)·2π/fs_total`;
|
|
||||||
`p=(sin(w0)·0.5)/Q`; `A=[1+dVar5, dVar2, 1−dVar5]`, `B=[1+param_4, dVar2, 1−param_4]`,
|
|
||||||
`dVar2=cos(w0)·−2.0` (DAT_1824c46b0=−2.0).
|
|
||||||
- cplx_div_exact (rcpps+Newton: `ref=r0·(2−den·r0)`, NaN-guard cmpeqps+movmskps, numerator B·conj(A))
|
|
||||||
- cplx_mul_exact (vfmaddsub213ps form `re=fma(br,ar,−bi·ai)`, `im=fma(br,ai,bi·ar)`)
|
|
||||||
- twin_apply: conj(z1) mirroring 0x1800018b0, z2=z1·z1, out=2·B/A
|
|
||||||
- **OPEN phase question (byte-exactness)**: conjugate shaping — (-α+π/2) vs (-α) axis. Magnitudes match
|
|
||||||
either way; final proof needed via phase of a single-bin run before claiming byte-exactness.
|
|
||||||
|
|
||||||
## 4. GRID + CALLER (Phase 3 — details in NOTES_TWIN.md §Phase 3)
|
|
||||||
- Caller FUN_180536300 (decomp 6218, `caller_536300.dis`), scratch ≥52·N complex bytes:
|
|
||||||
+0..8N A-accum, +8N..16N B-accum, +16N..24N z-powers, +24N..32N rotor-in (filled 1.0),
|
|
||||||
+32N..36N phase ramp lVar1, +36N..44N rotor-out z1, +44N..52N lVar3(1.0)/lVar4(0.0)
|
|
||||||
- `0x18052da00(lVar1, scale=2π/(os·sr), N)` = ramp fill; `0x18052dbc0(param_2, lVar4, N)` cplx-interleave
|
|
||||||
→ out slots get (cos,sin) = e^{+iθ}.
|
|
||||||
- **Grid law**: `os=*(int*)(cfg+0x240080)=4` (mov DWORD PTR [rbx+0x240080],0x4 @0x18052ce3e),
|
|
||||||
`sr=*(float*)(cfg+0x24)=44100.0` → generator `fs_total=os·sr=176400`; per-cell phase `2π/176400`;
|
|
||||||
cell k ↔ freq k/os Hz (0.25 Hz steps). Phase-1 FS=44100 was a valid normalization (res depends on f/fc).
|
|
||||||
- twin always = FUN_180535880 (type-info compare equal); sibling FUN_180536f90 dead.
|
|
||||||
- case8: numBands `+0x814`=1, `+0x103`=1; `param_5=10^(sens_dB/20)`, `gain=sqrtf(param_5)=10^(sens_dB/40)`
|
|
||||||
→ fit gain=4.132 means stored `sens_dB@+0x101 ≈ 24.65`; freq=[band+0x804]=1000.0, Q=[band+0x80c].
|
|
||||||
|
|
||||||
## 5. LEVEL-WEIGHT FORMULA (Phase 4 — DECODED, NEGATIVE RESULT — NOTES_LEVEL.md §UPDATE 2026-08-18)
|
|
||||||
Exact formula from decomp 22330 (FUN_180530d30):
|
|
||||||
- `base=(2000/(sr·0.5))·iVar5/(bin+1)`, iVar5=NFFT/2+1
|
|
||||||
- `w8=powf(base,0.25)`
|
|
||||||
- `v=[0x540880]·0.25·w8·fVar12` (fVar12=4.0 if offline flag [0x5408b8], else 1.0)
|
|
||||||
- `q=1/(1+v/([0x540880]·4))` (2nd pair 0x540884: q2=1/(1+v2/[0x540884]))
|
|
||||||
- `dVar1=(sr/[0x1a0])·[0x1ac]·0.001`
|
|
||||||
- `w=0.1^(1/(max(q·v,floor)·dVar1))`; bufs: 0x5406b8=w, 0x5406c8=1−w (and 0x5406d8/6e8 w/ 0x540884)
|
|
||||||
- consts: c3d3c=0.25, c4334=4.0, c3d8c=0.5, c45b4=2000.0, c3f70=0.1 f32, c3e30=0.001 f64, c3f70=0.1
|
|
||||||
- **Numerically**: w(500)=w(1000)=w(2000)≈0 (1e-20..1e-0.07), complements≈1 → but the weights are STILL
|
|
||||||
APPLIED (as 1−w ≈ 1) by FUN_180529fe0 (`0x5407c8[band] += 0x5406c8·res_upper + 0x5406e8·res_lower`,
|
|
||||||
thunk 003c40, kernel 0x1800752e0 = vfmadd213pd) — the form is weight·resonance, not dead code.
|
|
||||||
- **TILT analysis (2026-08-18, checkpoint NEGATIVE result)**: warp `0x5406a8 = 0.87·x/(1+x/K)`,
|
|
||||||
`K=exp(2.0723267)≈7.942`, `x=f/2000`, applied by FUN_180529fe0 as `mask *= 0x5406a8` (float, thunk 008700)
|
|
||||||
BEFORE the FFT-conv. warp: 500→0.211, 1000→0.409, 2000→0.773 (lin ratio 2000/500=3.67).
|
|
||||||
**BUT warp ≠ empirical tilt (ratio 1.27)**: `C(dB2000)=depth·warp(2000)·LUT_max = 0.864·0.772·0.667=0.445`,
|
|
||||||
yet red2000=15.2 dB needs `C=0.826` ⇒ warp alone CANNOT produce deep-2000 (dual_b1q). Empirical tilt is the
|
|
||||||
envelope of the BLEND term (0x5407c8 += weights·res + freq-axis 0x540698) + FFT-conv shaping, NOT warp —
|
|
||||||
simple tilt→warp substitution in model_lut.py is INVALID. Nothing else decoded yet closes dual_b1q.
|
|
||||||
- **FUN_180563ce0 decoded (2026-08-18)**: INIT of per-bin IIR level-trackers (0x156=342 bins, order=3.0f
|
|
||||||
0x40400000, coeff A/B from 0x24c4780/4790, strided fill; third pass +0xffd0 fills 0x40e00000=7.0f).
|
|
||||||
Not the update loop. Full disasm: `/tmp/opencode/f_563ce0.dis`.
|
|
||||||
- **Level-path map (2026-08-18, NOTES_LEVEL.md)**: 0x563440 (LUT curve +0x188, gamma, 6 band-slots, combine
|
|
||||||
→+0x2198, LUT pairs +0x98 at 0x3ff pts) + 0x56e3e0 twin-mask factory (N=1024, DOUBLE, LUT-ramp input) +
|
|
||||||
0x563ce0 IIR init. 0x563440 NOT in decomp_funs.txt (search via /tmp/opencode/f_563440.dis + level_notes.md).
|
|
||||||
- **DIAGNOSTIC BRIDGE DONE (2026-08-18) — model_fir.py**: реальная цепочка из FUN_180529fe0
|
|
||||||
воспроизводит 36 точек до rmse=0.236 dB БЕЗ эмпирического tilt:
|
|
||||||
`C(f) = g·LUT(log10(L0/res(f))) + w·warp(f)^a`, g=1.221, w=0.358, a=3.143.
|
|
||||||
- dual500-константа = res_band(500;fc=500)=0.117 Q-НЕЗАВИСИМ, не tilt;
|
|
||||||
- dual2000-глубина = АДДИТИВНЫЙ терм `w·warp^3.14` (на 1000 Гц вклад ≤0.021 — иначе t1k
|
|
||||||
рушится; на 2000 = 0.149) — это аккумулятор `0x5407c8 += weights·res + mask` (шаг 5),
|
|
||||||
НЕ мультипликация warp·LUT (та проваливается >10 dB);
|
|
||||||
- warp^~3.14 ≈ π — подозрение на кратный каскад (0x540698 freq-axis, ∏0x540688, двойной FFT);
|
|
||||||
- остаток 0.7 dB при Q=0.1 = форма LUT-колена 0.574, не закрывается per-bin → нужен FFT-уровень.
|
|
||||||
- **FFT-CONV FULLY MAPPED (2026-08-18, NOTES_LEVEL.md §2026-08-18c, /tmp/opencode/f_52b570.dis)**:
|
|
||||||
0x535a70 = dispatcher to base-CRT FFT (0x140a10/0x140a70); 0x540548/0x540550/0x540598 = FFT-plan
|
|
||||||
tables (plan 0x540530, built by 0x52dc30), NOT windows. Per-band loop 0x52b550-0x52b8b5:
|
|
||||||
mask→fwdFFT→invFFT→fill low/high (xmm13/xmm9 consts)→inv→cplx-op→fwd→`FIR *= 0x540658`→fill→inv→
|
|
||||||
`FIR[0]=1,FIR[1]=0`→`*=0x540888`→copy out.
|
|
||||||
**freq-axis 0x540698**: OFFLINE = constant scalar (NOT exp-formula — exp/rand = online dither).
|
|
||||||
**Window 0x540658**: only ONE direct ref in dump (read at 0x52b771), written only indirectly —
|
|
||||||
STATICALLY INVISIBLE → the only plausible source of warp^3.14/LUT-knee. Need runtime capture or
|
|
||||||
full-pipeline empirical closure.
|
|
||||||
|
|
||||||
## 6. PHASE 5 PLAN (updated 2026-08-18 — bridge done, tilt replaced, FFT mined out)
|
|
||||||
Decision (user): bridge FIRST. **DONE — bridge result above in §5.**
|
|
||||||
Status: step 2 DONE, step 3 PARTIAL (static analysis exhausted).
|
|
||||||
1. (15 min) Proof-of-state above; confirm twin + mdl files complete; repo at `60bf3a2`.
|
|
||||||
2. **Replace empirical tilt** in model_lut.py with `g·LUT(xv) + w·warp(f)^a` — **DONE**, rmse 0.236 dB
|
|
||||||
reproduced, committed `c8f97e4`. Optional refinement fit (2 LUT-knots) reaches 0.167 but is overfit.
|
|
||||||
3. **Close the 0.7-dB gap**: FFT-conv loop mapped + freq-axis cleared (offline const) — **DONE/PARTIAL**,
|
|
||||||
committed `60bf3a2`. Remaining unknown = 0x540658 window content (static-invisible). Options:
|
|
||||||
(a) runtime capture of 0x540658, (b) skip and close empirically in full-pipeline step 5.
|
|
||||||
4. Runtime values: per-bin IIR level trackers (0x563ce0), 0x540880/884, `[0x1a0]/[0x1ac]`, cell count N.
|
|
||||||
5. Full pipeline + render diff vs `/home/m/soothe-bt/*.wav`: dB parity → byte parity.
|
|
||||||
|
|
||||||
NEXT SESSION START (recommendation): skip deeper FFT mining; go straight to step 5 full pipeline
|
|
||||||
(mask-chain already in model_lut.py B.12 at 0.236; freq-axis const; FFT-conv reduces to a per-bin
|
|
||||||
smoothing + 0x540658 shaping that can be fit/absorbed). Runtime capture of 0x540658 only if step 5
|
|
||||||
stalls on the Q=0.1 knee.
|
|
||||||
|
|
||||||
### STEP 5 STARTED (2026-08-18) — dB-parity harness DONE (commit f96bb42)
|
|
||||||
- `/home/m/re-tools/render_parity.py`: measures steady-state per-tone reduction DIRECTLY on the
|
|
||||||
reference wavs (Goertzel tone-amp over late steady window; handles 16/24-bit, mono/stereo).
|
|
||||||
NOTE: Goertzel must use the SEQUENTIAL update form (s2=s1;s1=s0;s0=v+cw*s1-s2) — the tuple-unpack
|
|
||||||
form is numerically unstable (overflows ~1500 steps).
|
|
||||||
- Validation: measured dual Q-scan == stored dataset (dual.wav is 4s mono 16-bit; renders 6s stereo 24-bit).
|
|
||||||
- **dB-PARITY RESULT (canonical B.12, 36 pts, real wavs): TOTAL rmse=0.268 dB**
|
|
||||||
(dual500 0.130, dual2000 0.387, t1kq 0.320, t1k 0.076).
|
|
||||||
residual structure: dual2000 -0.80@Q0.1..+0.36@Q10 (LUT-knee), t1kq +0.36..+0.26 (level region),
|
|
||||||
t1k ±0.1 (good). Refit (g,w,a)=(1.224,0.379,3.405)→0.264; +2 LUT-knots→0.203 (plateau).
|
|
||||||
- CONCLUSION: B.12 form plateau ~0.20-0.27 dB vs real renders; residual is structural (0x540658
|
|
||||||
FFT shaping + level-curve region), matches plan expectation. Re-measured-on-wav numbers differ
|
|
||||||
slightly from the stored model_lut.py dataset (t1kq ~+0.3 dB) — different steady-window method.
|
|
||||||
- Open: full PIPELINE frame-render (STFT + per-frame mask + time constants) is the remaining big
|
|
||||||
lift for byte-parity; dB-parity milestone achieved at 0.268 dB.
|
|
||||||
|
|
||||||
Candidates that may consume Phase 5 time: source of sens≈24.65 (host ×2 over XML 12.0 — not found in dump),
|
|
||||||
IAT\*0x181bab370 outside dump (non-blocking), final z¹ phase proof, the 0x563440 LUT curve (+0x188) values.
|
|
||||||
|
|
||||||
## 7. FILES LAYOUT
|
|
||||||
- `/home/m/re-tools/dsp/` — twin.{hpp,cpp}, twin_check.cpp, CMakeLists.txt, build/
|
|
||||||
- `/home/m/re-tools/model_lut.py` — model **B.12** (Q=xmlq, gain=10^(sens/20), depth 0.8639736175537109,
|
|
||||||
`C=g·LUT(xv)+w·warp^a`, g=1.221/w=0.358/a=3.143) — 0.236 dB bridge model; PCHIP LUT nodes frozen
|
|
||||||
- `/home/m/re-tools/model_fir.py` — bridge canonical source (rmse 0.236, committed b1066f3)
|
|
||||||
- `/home/m/re-tools/framed_render.py` — full frame-render pilot (2026-08-19 params; modes dual|al)
|
|
||||||
- `/home/m/re-tools/rwin_A0/A1/B0/C0.npy`, `r_freqaxis.npy`, `rwin_warp.npy` — live-window tables (48k)
|
|
||||||
- `/home/m/re-tools/rtsnap.py` — live-process page snapshotter; snapshots /tmp/rt{A,B,C,D}.{raw,idx}
|
|
||||||
- `/home/m/re-tools/handoff/decode_rpp_full.py`, `handoff/rpp_allparams.py` — RPP b64-XML full decoder
|
|
||||||
(trim to len%4==0, `<?xml`@92, incl. nested processorStateData)
|
|
||||||
- `/home/m/re-tools/soothe_mem.bin` — memory dump, offset=VA−0x180000000
|
|
||||||
- `/home/m/re-tools/decomp_funs.txt` — decomp: FUN_180536300 (6218), FUN_180530d30 (22330),
|
|
||||||
FUN_180533ec0 (123204), FUN_180529fe0 (missing body! vtable-only)
|
|
||||||
- `/home/m/re-tools/handoff/` — THIS DOC, NOTES_TWIN.md, NOTES_LEVEL.md, phase1/*.py, nls_dasm/*.dis
|
|
||||||
- `/home/m/soothe-bt/*.rpp` (decoder `/home/m/re-tools/dsp/...`; also `decode_rpp4.py` referenced) + `*.wav` refs
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,256 @@
|
|||||||
|
# SESSION HANDOFF — soothe2 detector reverse → bit-exact render
|
||||||
|
|
||||||
|
Prepared: 2026-08-18 (checkpoint end-of-session: commits c8f97e4 + 60bf3a2 pushed). Start: READ THIS FIRST.
|
||||||
|
|
||||||
|
> **2026-08-20 UPDATE**: см. актуальный канон — `AGENTS.md` и `handoff/NOTES_LEVEL.md`.
|
||||||
|
> Нижеследующее «NOT DECODED»/«missing body» УСТАРЕЛО: `FUN_180529fe0` mono-path,
|
||||||
|
> `FUN_180563440/563ce0/563a60` — расшифрованы, полные дизассемблы скопированы в
|
||||||
|
> `handoff/nls_dasm/` (134 файла). Этот файл — исторический чекпоинт.
|
||||||
|
|
||||||
|
## 0. DECOMPILATION INVENTORY (2026-08-19 — what's decoded, where, and what's missing)
|
||||||
|
Goal: bit-exact parity is gated by EXACT tables/window/constants. The chunk-level model hits
|
||||||
|
err ≤0.62 dB (dual) / ≤0.19 dB (al_*) — to go sample-exact we need precise values from the binary.
|
||||||
|
|
||||||
|
### Static decomp assets IN REPO (use these, don't re-decompile):
|
||||||
|
- `ghidra-proj/soothe2.rep` — full Ghidra project (vst3 at ImageBase 0x180000000).
|
||||||
|
- `decomp_funs.txt` (312K lines, ~1640 functions), `fun_map.txt` (2285 addr→FUN), `consts.txt` (11602),
|
||||||
|
`decomp_dsp.txt` / `decomp_vtables.txt` / `decomp_candidates.txt` / `focus_decomp.txt`. Generators:
|
||||||
|
`Dump*.java`, `ImportRtti*.java`, `ListFuns.java`, `SearchRefs.java` (+ headless logs).
|
||||||
|
- RTTI: `rtti_dsp.json` / `rtti_full.json` — class hierarchy
|
||||||
|
`SpectralProcessor<float,7,1>`, `Soothe2ModuleBase<float,1>`, `FilterGraph<float,6,0x400>`,
|
||||||
|
`DigitalFilter<float,0xBA,1>`, `IIRFilterExtended<float,1>`, `AudioProcessingModule<float,1>`.
|
||||||
|
- `handoff/nls_dasm/` — 120 hand-picked `.dis` (twin, iface_18052da00/dbc0, fft, generator, ctor).
|
||||||
|
- `dsp/` — working C++ transcription (twin, detect, freqpath, spectral, fft_stage) + harness;
|
||||||
|
`build/twin_check` passes float-parity gate (§2).
|
||||||
|
|
||||||
|
### KEY DSP ADDRESSES — decoded / not decoded:
|
||||||
|
DECODED (formula-level, notes at NOTES_TWIN.md / NOTES_LEVEL.md):
|
||||||
|
- twin kernel `FUN_180535880`; generator `FUN_180533ec0`; caller `FUN_180536300`; dead sibling `180536f90`.
|
||||||
|
- level-weight formula `FUN_180530d30` (0x540880/884, warp 0x5406a8=0.87·x/(1+x/7.942), w=0.1^(...)).
|
||||||
|
- mask-apply entry `FUN_180529fe0` (accumulator 0x5407c8 += w·res; mask *= warp; FIR *= 0x540658).
|
||||||
|
- FFT-conv loop 0x52b550-0x52b8b5 (plan 0x540530, windows 0x540548/550/598, freq-axis 0x540698=offline const).
|
||||||
|
- level-path map: `0x563440` (LUT curve +0x188, 6 band-slots, combine→+0x2198), `0x56e3e0` (twin-mask factory),
|
||||||
|
`0x563ce0` (IIR level-tracker INIT only).
|
||||||
|
|
||||||
|
NOT DECODED / MISSING FROM decomp_funs.txt (critical): ⚠️ → РЕШЕНО (2026-08-19/20), см. выше
|
||||||
|
- `FUN_180529fe0` body — РЕШЕНО (decomp в `/tmp/consumers_out.txt`, mask-цепь в `dsp/framed_model.cpp`).
|
||||||
|
- `FUN_180563440` / `FUN_180563a60` (LUT curve + gamma + combine), `FUN_180563ce0` — РЕШЕНО,
|
||||||
|
дизассемблы скопированы в `handoff/nls_dasm/` (`f_563440.dis`, `f_563a60.dis`, `f_563ce0.dis`, `f529fe0.dis`).
|
||||||
|
- Window `0x540658` — РЕШЕНО (live-захват, см. `handoff/NOTES_CAPTURE.md`; `rtwin_freq_44100.npy`).
|
||||||
|
- sens source: XML 12.0 → runtime sens_dB≈24.65 (host ×2) not found in dump; `IAT\*0x181bab370` outside dump.
|
||||||
|
|
||||||
|
### Bridge model STATUS (2026-08-19):
|
||||||
|
- `framed_render.py` full STFT frame-render (N=2048, hop=512, sqrt-Hann, twin env tatt=11ms/trel=80ms):
|
||||||
|
`C(f)=G·LUT(xv)+W·warp(f)^A`, G=1.0850, W=0.2819, A=1.1377 (joint dual+al_* refit).
|
||||||
|
- Validation: dual q0.1/1/10 @500+2000 err ≤0.62 dB, envRmse@steady ≤0.78 dB; al_* lv3..24 err ≤0.19 dB.
|
||||||
|
- LUT = slanted al_*-leg (0.366@xv=-0.50 → 0.636@xv=+0.55), replaces flat B.12 (~0.5). Bugfix: clip range
|
||||||
|
must be [LY.min(), LY.max()] not [LY[0], LY[-1]].
|
||||||
|
- Remaining structural residual: −0.6 dB systematic on dual 500Hz (q0.1/q1). Live evidence (avg per-bin
|
||||||
|
gain H=|Y|/|X|): reference mask is FLAT ~-10.2dB across 100-540Hz regardless of Q — model produces
|
||||||
|
res-shaped notch. Hypo tested: freq-smoothing of C(f) fails (kills 2000Hz). NEXT: scalar per-frame xv
|
||||||
|
(broadband level, not per-bin am/res) — see /tmp/smoothtest.py (+ edit scalar=True).
|
||||||
|
|
||||||
|
## 1. Objective (unchanged since session 1)
|
||||||
|
Transcribe the decoded soothe2 detector ("twins" 0x180535880/0x180536f90, 2nd-order resonator) into
|
||||||
|
C++ and reach **bit-exact render parity** with the Reaper reference wavs in `/home/m/soothe-bt/*.wav`
|
||||||
|
(checking dB/magnitude parity first, then byte-diff).
|
||||||
|
|
||||||
|
Path A (bit-exact) chosen. Phase 0,1,2 done. Phase 3,4 advanced. Phase 5 (render diff) is next.
|
||||||
|
|
||||||
|
## 2. DECOMPILATION FINDINGS (2026-08-19 — FUN_180563440, FUN_180563ce0 decoded)
|
||||||
|
|
||||||
|
### FUN_180563440 — LUT curve evaluation + band combine (222 lines disasm)
|
||||||
|
Structure: **3 phases per frame**:
|
||||||
|
1. **1024-bin LUT loop** (0x400 iterations): for each bin k:
|
||||||
|
- x = clamp(k * 0.0009775, 0, 1.0) = k/1024
|
||||||
|
- Band config at +0x188: {A(+0x00), B(+0x04), threshold(+0x0c), flag(+0x10), callback(+0x50)}
|
||||||
|
- **Path 1** (callback exists): vtable call → dynamic LUT
|
||||||
|
- **Path 2** (flag=1, threshold≠1.0): **power-law** → centered = 2*x - 1, then `sign(x) * 10^(log10(|x|) / threshold)` — this is a **compression curve** controlled by sharpness/threshold
|
||||||
|
- **Path 3** (default): **linear interpolation** → `(B - A) * x + A`
|
||||||
|
- Output: double-precision at +0x198, stride 8
|
||||||
|
2. **Twin-mask factory** (FUN_18056e3e0): 6 bands × 1024 bins, stride 0x2000
|
||||||
|
3. **Combine loop**: stereo (max 2 channels), 6 bands, `1 - sum(band_masks)`
|
||||||
|
|
||||||
|
### FUN_180563ce0 — IIR level-tracker INIT (163 lines disasm)
|
||||||
|
- **341 bins** (0x155 iterations), **order-3 IIR** (3 coefficients per bin)
|
||||||
|
- Coefficient: **0.1** (`0x3dcccccd` = IEEE 754 float 0.1)
|
||||||
|
- Initial state: [1.0, 0, 0, 0] and [-1.0, 0, 0, 0] (identity + zero)
|
||||||
|
- Buffer layout: 3 × (16 bytes coeff) per bin, stored at rcx+0x28/+0x40/+0x58
|
||||||
|
- **Not the update loop** — init only; UPDATE is elsewhere
|
||||||
|
|
||||||
|
### FUN_180529fe0 — Coefficient setup (2051 instructions, in decomp_funs.txt)
|
||||||
|
- **vtable method** on Soothe2Module<M,1>
|
||||||
|
- **Lock** at +0x2404dc (atomic test-and-set)
|
||||||
|
- **PRNG state** at +0x2404e0, LCG with offset 0x3cdca
|
||||||
|
- **6-iteration coefficient generation** from 0x5408b0 buffer (LCG-indexed)
|
||||||
|
- **Depth scaling**: `powf(normalized, depth)` at +0x2c
|
||||||
|
- **Mask assembly**: normalize by 0x1a0 (NFFT), × 0x540870 (level weight), × 0x54088c (sharpness), invert
|
||||||
|
- **Copy output** via SIMD memcpy (thunk 0x181ba94b0)
|
||||||
|
|
||||||
|
### FUN_18052e9b0 — SpectralProcessor main (3167 instructions, in decomp_funs.txt)
|
||||||
|
- **Same PRNG + coefficient setup** as FUN_180529fe0
|
||||||
|
- **Band chain**: FUN_18052f500 (interleave) → FUN_18052ee70 (per-bin gain) → FUN_18052d650 (setup) → FUN_18052d920 (window)
|
||||||
|
- **Buffer alloc**: FUN_18052e190 for 0x540668, 0x540698, 0x5406a8, 0x5406b8-e8 (6 bands)
|
||||||
|
- **Depth scaling**: `powf(normalized, depth)` with depth at +0x2c
|
||||||
|
- **Final mask**: `1 - C` (inversion)
|
||||||
|
- **Window application**: `FIR *= 0x540658` (the live-captured window table)
|
||||||
|
|
||||||
|
### Constants verified from binary:
|
||||||
|
| Constant | Address | Value | Meaning |
|
||||||
|
|---|---|---|---|
|
||||||
|
| SCALE | 0x24c3c54 | 0.000977517 | 1/1024 (bin→x) |
|
||||||
|
| ONE | 0x24c3ea4 | 1.0 | clamping max |
|
||||||
|
| TWO | 0x24c41e0 | 2.0 | centering (2*x-1) |
|
||||||
|
| NEG1 | 0x24c4680 | -1.0 | sign flip |
|
||||||
|
| HALF | 0x24c3d8c | 0.5 | threshold |
|
||||||
|
| DEPTH_SCALE | 0x24c4334 | 4.0 | depth range |
|
||||||
|
| DB_CONV | 0x24c43e0 | 8.6859 | 20/ln(10) |
|
||||||
|
| FLOOR | 0x24c4704 | -6.9078 | ln(0.001) |
|
||||||
|
| IIR_COEFF | embedded | 0.1 | attack/release per bin |
|
||||||
|
| IIR_ORDER | embedded | 3.0 | IIR filter order |
|
||||||
|
| LCG_OFFSET | embedded | 0x3cdca | PRNG state advance |
|
||||||
|
|
||||||
|
### Live-captured tables (re-verified):
|
||||||
|
- `rwin_A0.npy` (0x1930100): 0.5→0.8, **frequency window** (NOT warp formula)
|
||||||
|
- `rwin_B0.npy` (0x1938180): 0→3.899, **power-law depth curve** (exponent ~0.66)
|
||||||
|
- `rwin_C0.npy` (0x19401c0): 0.596→0.126, **level-dependent weight**
|
||||||
|
- `r_freqaxis.npy`: 0→23988.3 Hz, 11.71 Hz spacing (48000/4096)
|
||||||
|
|
||||||
|
### Key insight: The warp formula is NOT a table — it's computed at runtime
|
||||||
|
The empirical `0.87*7.942*x/(7.942+x)` is an approximation of a runtime computation
|
||||||
|
in FUN_180563440 that evaluates the LUT curve parametrically. The actual LUT has TWO modes:
|
||||||
|
- **Linear** (default): simple interpolation between A and B
|
||||||
|
- **Power-law** (flag=1): `sign(x) * 10^(log10(|x|) / C)` — compression curve
|
||||||
|
|
||||||
|
## 3. PROOF OF STATE — run this first (5 min)
|
||||||
|
Everything below must reproduce. If `twin_check` fails, the transcription moved stale.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/m/re-tools/dsp && cmake --build build && ./build/twin_check
|
||||||
|
# expect:
|
||||||
|
# max rel err = 1.268e-05, PASS = yes (float-parity)
|
||||||
|
# max sweep rel err = 1.233e-05, SWEEP PASS = yes, exit 0
|
||||||
|
```
|
||||||
|
|
||||||
|
`build/soothe2_dsp.so` and `build/twin_check` are current. CMakeLists already has `twin.cpp` + `twin_check` executable.
|
||||||
|
|
||||||
|
## 3. TRANSCRIPTION STATE (summary — details in NOTES_TWIN.md)
|
||||||
|
- `dsp/twin.hpp` + `dsp/twin.cpp` = twin kernel FUN_180535880:
|
||||||
|
- build_twin_coeff (FUN_180533ec0 double pipeline → cvtpd2ps floats)
|
||||||
|
- generator formula (confirmed): `fVar1=sqrtf(param_5)`; `w0=max(2.0,freq)·2π/fs_total`;
|
||||||
|
`p=(sin(w0)·0.5)/Q`; `A=[1+dVar5, dVar2, 1−dVar5]`, `B=[1+param_4, dVar2, 1−param_4]`,
|
||||||
|
`dVar2=cos(w0)·−2.0` (DAT_1824c46b0=−2.0).
|
||||||
|
- cplx_div_exact (rcpps+Newton: `ref=r0·(2−den·r0)`, NaN-guard cmpeqps+movmskps, numerator B·conj(A))
|
||||||
|
- cplx_mul_exact (vfmaddsub213ps form `re=fma(br,ar,−bi·ai)`, `im=fma(br,ai,bi·ar)`)
|
||||||
|
- twin_apply: conj(z1) mirroring 0x1800018b0, z2=z1·z1, out=2·B/A
|
||||||
|
- **OPEN phase question (byte-exactness)**: conjugate shaping — (-α+π/2) vs (-α) axis. Magnitudes match
|
||||||
|
either way; final proof needed via phase of a single-bin run before claiming byte-exactness.
|
||||||
|
|
||||||
|
## 4. GRID + CALLER (Phase 3 — details in NOTES_TWIN.md §Phase 3)
|
||||||
|
- Caller FUN_180536300 (decomp 6218, `caller_536300.dis`), scratch ≥52·N complex bytes:
|
||||||
|
+0..8N A-accum, +8N..16N B-accum, +16N..24N z-powers, +24N..32N rotor-in (filled 1.0),
|
||||||
|
+32N..36N phase ramp lVar1, +36N..44N rotor-out z1, +44N..52N lVar3(1.0)/lVar4(0.0)
|
||||||
|
- `0x18052da00(lVar1, scale=2π/(os·sr), N)` = ramp fill; `0x18052dbc0(param_2, lVar4, N)` cplx-interleave
|
||||||
|
→ out slots get (cos,sin) = e^{+iθ}.
|
||||||
|
- **Grid law**: `os=*(int*)(cfg+0x240080)=4` (mov DWORD PTR [rbx+0x240080],0x4 @0x18052ce3e),
|
||||||
|
`sr=*(float*)(cfg+0x24)=44100.0` → generator `fs_total=os·sr=176400`; per-cell phase `2π/176400`;
|
||||||
|
cell k ↔ freq k/os Hz (0.25 Hz steps). Phase-1 FS=44100 was a valid normalization (res depends on f/fc).
|
||||||
|
- twin always = FUN_180535880 (type-info compare equal); sibling FUN_180536f90 dead.
|
||||||
|
- case8: numBands `+0x814`=1, `+0x103`=1; `param_5=10^(sens_dB/20)`, `gain=sqrtf(param_5)=10^(sens_dB/40)`
|
||||||
|
→ fit gain=4.132 means stored `sens_dB@+0x101 ≈ 24.65`; freq=[band+0x804]=1000.0, Q=[band+0x80c].
|
||||||
|
|
||||||
|
## 5. LEVEL-WEIGHT FORMULA (Phase 4 — DECODED, NEGATIVE RESULT — NOTES_LEVEL.md §UPDATE 2026-08-18)
|
||||||
|
Exact formula from decomp 22330 (FUN_180530d30):
|
||||||
|
- `base=(2000/(sr·0.5))·iVar5/(bin+1)`, iVar5=NFFT/2+1
|
||||||
|
- `w8=powf(base,0.25)`
|
||||||
|
- `v=[0x540880]·0.25·w8·fVar12` (fVar12=4.0 if offline flag [0x5408b8], else 1.0)
|
||||||
|
- `q=1/(1+v/([0x540880]·4))` (2nd pair 0x540884: q2=1/(1+v2/[0x540884]))
|
||||||
|
- `dVar1=(sr/[0x1a0])·[0x1ac]·0.001`
|
||||||
|
- `w=0.1^(1/(max(q·v,floor)·dVar1))`; bufs: 0x5406b8=w, 0x5406c8=1−w (and 0x5406d8/6e8 w/ 0x540884)
|
||||||
|
- consts: c3d3c=0.25, c4334=4.0, c3d8c=0.5, c45b4=2000.0, c3f70=0.1 f32, c3e30=0.001 f64, c3f70=0.1
|
||||||
|
- **Numerically**: w(500)=w(1000)=w(2000)≈0 (1e-20..1e-0.07), complements≈1 → but the weights are STILL
|
||||||
|
APPLIED (as 1−w ≈ 1) by FUN_180529fe0 (`0x5407c8[band] += 0x5406c8·res_upper + 0x5406e8·res_lower`,
|
||||||
|
thunk 003c40, kernel 0x1800752e0 = vfmadd213pd) — the form is weight·resonance, not dead code.
|
||||||
|
- **TILT analysis (2026-08-18, checkpoint NEGATIVE result)**: warp `0x5406a8 = 0.87·x/(1+x/K)`,
|
||||||
|
`K=exp(2.0723267)≈7.942`, `x=f/2000`, applied by FUN_180529fe0 as `mask *= 0x5406a8` (float, thunk 008700)
|
||||||
|
BEFORE the FFT-conv. warp: 500→0.211, 1000→0.409, 2000→0.773 (lin ratio 2000/500=3.67).
|
||||||
|
**BUT warp ≠ empirical tilt (ratio 1.27)**: `C(dB2000)=depth·warp(2000)·LUT_max = 0.864·0.772·0.667=0.445`,
|
||||||
|
yet red2000=15.2 dB needs `C=0.826` ⇒ warp alone CANNOT produce deep-2000 (dual_b1q). Empirical tilt is the
|
||||||
|
envelope of the BLEND term (0x5407c8 += weights·res + freq-axis 0x540698) + FFT-conv shaping, NOT warp —
|
||||||
|
simple tilt→warp substitution in model_lut.py is INVALID. Nothing else decoded yet closes dual_b1q.
|
||||||
|
- **FUN_180563ce0 decoded (2026-08-18)**: INIT of per-bin IIR level-trackers (0x156=342 bins, order=3.0f
|
||||||
|
0x40400000, coeff A/B from 0x24c4780/4790, strided fill; third pass +0xffd0 fills 0x40e00000=7.0f).
|
||||||
|
Not the update loop. Full disasm: `/tmp/opencode/f_563ce0.dis`.
|
||||||
|
- **Level-path map (2026-08-18, NOTES_LEVEL.md)**: 0x563440 (LUT curve +0x188, gamma, 6 band-slots, combine
|
||||||
|
→+0x2198, LUT pairs +0x98 at 0x3ff pts) + 0x56e3e0 twin-mask factory (N=1024, DOUBLE, LUT-ramp input) +
|
||||||
|
0x563ce0 IIR init. 0x563440 NOT in decomp_funs.txt (search via /tmp/opencode/f_563440.dis + level_notes.md).
|
||||||
|
- **DIAGNOSTIC BRIDGE DONE (2026-08-18) — model_fir.py**: реальная цепочка из FUN_180529fe0
|
||||||
|
воспроизводит 36 точек до rmse=0.236 dB БЕЗ эмпирического tilt:
|
||||||
|
`C(f) = g·LUT(log10(L0/res(f))) + w·warp(f)^a`, g=1.221, w=0.358, a=3.143.
|
||||||
|
- dual500-константа = res_band(500;fc=500)=0.117 Q-НЕЗАВИСИМ, не tilt;
|
||||||
|
- dual2000-глубина = АДДИТИВНЫЙ терм `w·warp^3.14` (на 1000 Гц вклад ≤0.021 — иначе t1k
|
||||||
|
рушится; на 2000 = 0.149) — это аккумулятор `0x5407c8 += weights·res + mask` (шаг 5),
|
||||||
|
НЕ мультипликация warp·LUT (та проваливается >10 dB);
|
||||||
|
- warp^~3.14 ≈ π — подозрение на кратный каскад (0x540698 freq-axis, ∏0x540688, двойной FFT);
|
||||||
|
- остаток 0.7 dB при Q=0.1 = форма LUT-колена 0.574, не закрывается per-bin → нужен FFT-уровень.
|
||||||
|
- **FFT-CONV FULLY MAPPED (2026-08-18, NOTES_LEVEL.md §2026-08-18c, /tmp/opencode/f_52b570.dis)**:
|
||||||
|
0x535a70 = dispatcher to base-CRT FFT (0x140a10/0x140a70); 0x540548/0x540550/0x540598 = FFT-plan
|
||||||
|
tables (plan 0x540530, built by 0x52dc30), NOT windows. Per-band loop 0x52b550-0x52b8b5:
|
||||||
|
mask→fwdFFT→invFFT→fill low/high (xmm13/xmm9 consts)→inv→cplx-op→fwd→`FIR *= 0x540658`→fill→inv→
|
||||||
|
`FIR[0]=1,FIR[1]=0`→`*=0x540888`→copy out.
|
||||||
|
**freq-axis 0x540698**: OFFLINE = constant scalar (NOT exp-formula — exp/rand = online dither).
|
||||||
|
**Window 0x540658**: only ONE direct ref in dump (read at 0x52b771), written only indirectly —
|
||||||
|
STATICALLY INVISIBLE → the only plausible source of warp^3.14/LUT-knee. Need runtime capture or
|
||||||
|
full-pipeline empirical closure.
|
||||||
|
|
||||||
|
## 6. PHASE 5 PLAN (updated 2026-08-18 — bridge done, tilt replaced, FFT mined out)
|
||||||
|
Decision (user): bridge FIRST. **DONE — bridge result above in §5.**
|
||||||
|
Status: step 2 DONE, step 3 PARTIAL (static analysis exhausted).
|
||||||
|
1. (15 min) Proof-of-state above; confirm twin + mdl files complete; repo at `60bf3a2`.
|
||||||
|
2. **Replace empirical tilt** in model_lut.py with `g·LUT(xv) + w·warp(f)^a` — **DONE**, rmse 0.236 dB
|
||||||
|
reproduced, committed `c8f97e4`. Optional refinement fit (2 LUT-knots) reaches 0.167 but is overfit.
|
||||||
|
3. **Close the 0.7-dB gap**: FFT-conv loop mapped + freq-axis cleared (offline const) — **DONE/PARTIAL**,
|
||||||
|
committed `60bf3a2`. Remaining unknown = 0x540658 window content (static-invisible). Options:
|
||||||
|
(a) runtime capture of 0x540658, (b) skip and close empirically in full-pipeline step 5.
|
||||||
|
4. Runtime values: per-bin IIR level trackers (0x563ce0), 0x540880/884, `[0x1a0]/[0x1ac]`, cell count N.
|
||||||
|
5. Full pipeline + render diff vs `/home/m/soothe-bt/*.wav`: dB parity → byte parity.
|
||||||
|
|
||||||
|
NEXT SESSION START (recommendation): skip deeper FFT mining; go straight to step 5 full pipeline
|
||||||
|
(mask-chain already in model_lut.py B.12 at 0.236; freq-axis const; FFT-conv reduces to a per-bin
|
||||||
|
smoothing + 0x540658 shaping that can be fit/absorbed). Runtime capture of 0x540658 only if step 5
|
||||||
|
stalls on the Q=0.1 knee.
|
||||||
|
|
||||||
|
### STEP 5 STARTED (2026-08-18) — dB-parity harness DONE (commit f96bb42)
|
||||||
|
- `/home/m/re-tools/render_parity.py`: measures steady-state per-tone reduction DIRECTLY on the
|
||||||
|
reference wavs (Goertzel tone-amp over late steady window; handles 16/24-bit, mono/stereo).
|
||||||
|
NOTE: Goertzel must use the SEQUENTIAL update form (s2=s1;s1=s0;s0=v+cw*s1-s2) — the tuple-unpack
|
||||||
|
form is numerically unstable (overflows ~1500 steps).
|
||||||
|
- Validation: measured dual Q-scan == stored dataset (dual.wav is 4s mono 16-bit; renders 6s stereo 24-bit).
|
||||||
|
- **dB-PARITY RESULT (canonical B.12, 36 pts, real wavs): TOTAL rmse=0.268 dB**
|
||||||
|
(dual500 0.130, dual2000 0.387, t1kq 0.320, t1k 0.076).
|
||||||
|
residual structure: dual2000 -0.80@Q0.1..+0.36@Q10 (LUT-knee), t1kq +0.36..+0.26 (level region),
|
||||||
|
t1k ±0.1 (good). Refit (g,w,a)=(1.224,0.379,3.405)→0.264; +2 LUT-knots→0.203 (plateau).
|
||||||
|
- CONCLUSION: B.12 form plateau ~0.20-0.27 dB vs real renders; residual is structural (0x540658
|
||||||
|
FFT shaping + level-curve region), matches plan expectation. Re-measured-on-wav numbers differ
|
||||||
|
slightly from the stored model_lut.py dataset (t1kq ~+0.3 dB) — different steady-window method.
|
||||||
|
- Open: full PIPELINE frame-render (STFT + per-frame mask + time constants) is the remaining big
|
||||||
|
lift for byte-parity; dB-parity milestone achieved at 0.268 dB.
|
||||||
|
|
||||||
|
Candidates that may consume Phase 5 time: source of sens≈24.65 (host ×2 over XML 12.0 — not found in dump),
|
||||||
|
IAT\*0x181bab370 outside dump (non-blocking), final z¹ phase proof, the 0x563440 LUT curve (+0x188) values.
|
||||||
|
|
||||||
|
## 7. FILES LAYOUT
|
||||||
|
- `/home/m/re-tools/dsp/` — twin.{hpp,cpp}, twin_check.cpp, CMakeLists.txt, build/
|
||||||
|
- `/home/m/re-tools/model_lut.py` — model **B.12** (Q=xmlq, gain=10^(sens/20), depth 0.8639736175537109,
|
||||||
|
`C=g·LUT(xv)+w·warp^a`, g=1.221/w=0.358/a=3.143) — 0.236 dB bridge model; PCHIP LUT nodes frozen
|
||||||
|
- `/home/m/re-tools/model_fir.py` — bridge canonical source (rmse 0.236, committed b1066f3)
|
||||||
|
- `/home/m/re-tools/framed_render.py` — full frame-render pilot (2026-08-19 params; modes dual|al)
|
||||||
|
- `/home/m/re-tools/rwin_A0/A1/B0/C0.npy`, `r_freqaxis.npy`, `rwin_warp.npy` — live-window tables (48k)
|
||||||
|
- `/home/m/re-tools/rtsnap.py` — live-process page snapshotter; snapshots /tmp/rt{A,B,C,D}.{raw,idx}
|
||||||
|
- `/home/m/re-tools/handoff/decode_rpp_full.py`, `handoff/rpp_allparams.py` — RPP b64-XML full decoder
|
||||||
|
(trim to len%4==0, `<?xml`@92, incl. nested processorStateData)
|
||||||
|
- `/home/m/re-tools/soothe_mem.bin` — memory dump, offset=VA−0x180000000
|
||||||
|
- `/home/m/re-tools/decomp_funs.txt` — decomp: FUN_180536300 (6218), FUN_180530d30 (22330),
|
||||||
|
FUN_180533ec0 (123204), FUN_180529fe0 (missing body! vtable-only)
|
||||||
|
- `/home/m/re-tools/handoff/` — THIS DOC, NOTES_TWIN.md, NOTES_LEVEL.md, phase1/*.py, nls_dasm/*.dis
|
||||||
|
- `/home/m/soothe-bt/*.rpp` (decoder `/home/m/re-tools/dsp/...`; also `decode_rpp4.py` referenced) + `*.wav` refs
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
## Objective
|
||||||
|
- Реверс-DSP oeksound soothe2 v1.1.2 VST3 → математическая модель (STFT, детектор резонансов, нотич-синтез). Текущая фаза — поведенческий параметр-свип через рендер Reaper RPP + статический декомпил (без декрипты бинарника пока).
|
||||||
|
|
||||||
|
> **⚠️ ИСТОРИЧЕСКИЙ ДОКУМЕНТ (поведенческая модель v4, sim.py, ок. 2026-08-17)**.
|
||||||
|
> Актуальный канон (bit-exact, P4) — `README.md`, `AGENTS.md`, `handoff/NOTES_LEVEL.md`.
|
||||||
|
> Здесь — ранняя поведенческая фаза реверса, сохранена как справочник.
|
||||||
|
|
||||||
|
## Key Facts
|
||||||
|
- Пользователь русскоязычный; sudo нет. Объект: `~/.wine/.../soothe2_x64.vst3` (PE32+, base 0x180000000), yabridge 5.1.1, Reaper 7.78.
|
||||||
|
- Frida невозможна (Seccomp). Дамп памяти — `pread /proc/pid/mem` от родителя. pkill-ловушки: paths без `harneb`/`reaper` → `pkill -9 -x reaper`, `pkill -9 -f '[y]abridge'`.
|
||||||
|
- Рендер-проект: `RENDER_RANGE 1 0 0 0 1000`, ITEM обязательно `POSITION 0`. Offline-рендер мгновенный → для живого дампа realtime (`RENDER_1X 1`) + `testtone.wav`.
|
||||||
|
- VST-state = 92-байт JUCE-заголовок + base64 XML `<SOOTHE2STATE>` + 54 `<PARAM id=... value=.../>`; внутри — UI-state без DSP.
|
||||||
|
|
||||||
|
## Breakthrough (исправлено в этой сессии)
|
||||||
|
- **Формат-адаптивность PARAM**: плагин применяет `<PARAM value>` ТОЛЬКО если строка сериализована в том же формате, что оригинал:
|
||||||
|
- full-precision (depth, band freq/q...) → `%.16f` (напр. `"0.9000000000000000"`). Короткие строки → схлопывание (fallback).
|
||||||
|
- short-формат `"X.0"` (selectivity, mix, mode, oversample, resolution) → ЛОМАЕТСЯ от `%.16f`.
|
||||||
|
- `sweep.py`/`tt_sweep.py` адаптивны: >4 десятичных в оригинале → `%.16f`, 1-4 → то же число знаков, иначе — как есть.
|
||||||
|
|
||||||
|
## Синтез модели (поведенческая, v4 — реализована в sim.py, RMSE на settled ≤0.05dB по всем свипам)
|
||||||
|
```
|
||||||
|
PARADИГМА (реализована): out(t) = x(t) − amount_b(t)·bp_b(t; fc, Q) [субтрактивный нотч, не микш-параллельный]
|
||||||
|
amount(total) = A(depth)·S(sens)·H(sharp)·M(mode) — LUT из измерений (пересечение, не аналит. фит)
|
||||||
|
A(depth): 0.5/0.864/1/2/3/5/10/20 → 0.560/0.588/0.598/0.667/0.727/0.820/0.939/0.989
|
||||||
|
S(sens): 0/6/12/24 → 0.48/0.76/1.0/1.0 (floor при sens=0!)
|
||||||
|
H(sharp): 1/3/5/10 → 0.20/0.59/0.84/1.0
|
||||||
|
M(mode): 0 → 0.745 (mode0 слабее), 1.. → 1.0
|
||||||
|
Q(sel): = 3.0+0.36·sel (2nd-order bandpass; реал. даёт более крутые борта — приближ.)
|
||||||
|
env(t): one-pole, α_attack=1−e^(−1/(T_a·fs)), α_release=1−e^(−1/(T_r·fs))
|
||||||
|
T_a = 0.02·e^(a/1.955) (0→20мс, 5→258мс, 10→3.3c); T_r LUT из спадов (0→27мс, 5→140мс, 10→15c)
|
||||||
|
детектор: лауднеss полосы |bp| smoothed 5мс > thr 0.01 → цель amt, иначе 0 (порог между probe 0.0005 и burst 0.2)
|
||||||
|
выход: x_out = mix/100·… — линейный кроссфейд: (100−mix)/100·x + mix/100·fully_reduced
|
||||||
|
(mix=100 = полная обработка = ref; mix=0 → сухой; проверено на s_corr_mix: монот. линейно)
|
||||||
|
```
|
||||||
|
Верификация (settled, RMSE по reduction dB на burst-окне):
|
||||||
|
| параметр | реал | sim | RMSE | | параметр | реал | sim | RMSE |
|
||||||
|
|---|---|---|--|---|---|---|---|---|
|
||||||
|
| дефолт | 7.70 | 7.70 | 0.002 | sharp=1 | 1.11 | 1.11 | 0.002 |
|
||||||
|
| depth=2 | 9.56 | 9.56 | 0.001 | sharp=3 | 3.72 | 3.71 | 0.006 |
|
||||||
|
| depth=5 | 14.90 | 14.89 | 0.008 | attack=5 | 5.68 | 5.90 | 0.360 |
|
||||||
|
| depth=10 | 24.27 | 24.25 | 0.018 | mode=0 | 5.03 | 5.00 | 0.023 |
|
||||||
|
| depth=20 | 39.43 | 39.40 | 0.054 | mode=2 | 7.70 | 7.70 | 0.002 |
|
||||||
|
| sens=0 | 2.89 | 2.89 | 0.001 | sens=24 | 7.70 | 7.70 | 0.002 |
|
||||||
|
Q-профиль (probes-зонд): sim sel1 500→680@540→0.62@700 vs real 8.03→4.54→1.13; 2nd-order не покрывает
|
||||||
|
крутые борта реала (реал резче ~×3): допустимо как 1-я итерация, RMSE макс по профилю ~1.6dB.
|
||||||
|
Release-трассы: r0/r1/r2/r5 совпадают (sim против real 1.6s:3.5/4.3, 1.7s:1.5/1.9, 1.9s:0.3/0.3).
|
||||||
|
|
||||||
|
## Q/width-зонд (probes500/probes500b, drive 500Hz amp0.2 + справки 0.02)
|
||||||
|
- Методика: длинный single-FFT на окне 1.7..2.9s (разрешение ~0.7Hz), справки на 500,505,510,520,540,
|
||||||
|
555,575,600,700 (b-set) / 500,505,515,530,560,640,780 (a-set). Драйв-тон держит детект-клок,
|
||||||
|
справки зондируют передаточную характеристику в установившемся режиме.
|
||||||
|
- Данные (a-set) red@freq: sel1: 500:8.0 510:7.9 520:7.7 540:4.5 555:2.7 575:1.6 600:0.7 (плавный хвост)
|
||||||
|
sel8: 500:8.7 510:9.1 520:6.6 540:1.3 555:0.1 575:0.0 600:0.0 (резкий срез) — Q уверенно растёт с sel.
|
||||||
|
- Центраئة нотча на 500..510 чуть выше 500 (fs на 505..510) — трекинг слегка субадио.
|
||||||
|
- Значения на дип-тонах 530/560/640 в b-наборе — из мусора (нет справки) и исключены; a-набор чистый.
|
||||||
|
- ВАЖНО: dry-файлы дважды перезаписывались провальными dry-рендерами (RENDER_FILE не патчился по
|
||||||
|
`.wav` напр. burst500L_byp → overwritten probes). ФИКС: патчить RENDER_FILE явно в sed до рендера.
|
||||||
|
- data: оба svипа предсказуемо монотонны; таблица в summary выше.
|
||||||
|
|
||||||
|
## Верифицированные данные (все свипы с корректным форматом)
|
||||||
|
- **depth (mir)**: монотонно distinct (vs-ref 496k→441k→...→347k@0.85→384k@1.0) — работает.
|
||||||
|
- **selectivity**: 0/1/2/5 distinct (476k/466k/458k/461k vs ref); 10==20==50==100 (сатурация ≥10).
|
||||||
|
- **oversample**: 0==ref; 1 distinct; 2==3==4 (сатурация на 2).
|
||||||
|
- **resolution**: 0 distinct; 1==ref; 2 distinct (все distinct); 3==4.
|
||||||
|
- **mix**: 0/25/50/75/100 всё distinct монотонно; 100==ref.
|
||||||
|
- **mode**: 0 distinct; 1==2==3==4==ref.
|
||||||
|
- **band1 freq**: tt_b1f_678 (дефолт banda freq) == tt_ref бит-в-бит; свип 100..8000 отрендерен.
|
||||||
|
|
||||||
|
## Прочие находки
|
||||||
|
- 60-сек рендер p60_ref/p60_dep0/p60_dep1 подтвердил collapse класса при коротких строках.
|
||||||
|
- rtdump.py живые дампы (регионы по VA, RA/WA) — DSP-кластер .data меняется между глубинами; раньше не совпали по layout (артефакт noise).
|
||||||
|
|
||||||
|
## Work State
|
||||||
|
### Completed
|
||||||
|
- Свип-инфраструктура: sweep.py + tt_sweep.py (адаптив формата), рендер, сверочный анализ (spectrum.py медленный, нужно numpy).
|
||||||
|
- Тест-тон: testtone.wav (2s, multi-tone 110Hz–14kHz с AM) для изоляции резонансов; tt_ref==tt_678 (0 diff).
|
||||||
|
- Базовые свипы по всем основным параметрам.
|
||||||
|
|
||||||
|
### Active
|
||||||
|
- **Извлечена статическая depth-кривая**: LUT 207 float @0x1826170e8..0x182617420, форма `0.302 + 0.698·sin(π/2·x)^0.94` (r²=0.99999), saved `depthcurve.npy`. ИДЕНТИЧНА у ref/dep0/dep1 → вшита в бинарь (build-time), не меняется от параметра = внутренний «depth→amount» маппинг.
|
||||||
|
- **Адаптивная динамика (burst-тест, band1@500Hz, q≈1, depth 0.864)**: burst500.wav (2s: 0.5s тишины → 1.0s 500Hz burst amp 0.2 → 0.5s тишины, + 1000Hz amp 0.05 фон). `burst500_b1` (fx) vs `burst500_byp` (dry):
|
||||||
|
- ATTACK: плавное включение нотч-ослабления, τ≈**18 мс** (фит на экспоненту A·(1−e^−t/τ), stable 17.6/17.7/18.8/18.8), установка −7.7dB бай-в-бай за ~100мс.
|
||||||
|
- RELEASE: хвост после снятия бурста спадает за τ≈**5 мс** (после 1.5s fx-энергия 24→0 за ~30мс) — быстрый трелинг, симметрия фола не классическая (вероятно фильтр-транзиент, не медленный rel).
|
||||||
|
- Глубина ослабления НЕ зависит от «порога» — это фикс. нотч на частоте band, amount задаётся curve(depth).
|
||||||
|
- Рендерится через те же tt-файлы; РЕЗУЛЬТАТЫ в wav — 24-bit (`sampwidth=3`), читать через 24-bit decode (или любой np.frombuffer под sw), не 16-bit!
|
||||||
|
- **τ-инвариантность** (burst-тест, band1@500Hz, win 10ms, Goertzel, фит к A(1−e^−t/τ)):
|
||||||
|
- depth 0→0.86→1: A=6.39→7.70→7.91 dB; τ=18.3→16.8→16.5 ms (τ почти const) → depth контролирует amount, НЕ скорость.
|
||||||
|
- selectivity: τ РАСТЁТ монотонно 12.3→16.8 ms при sel 0→10 (12.3/12.4/12.6/12.9/13.0/13.4/14.3/16.8) → Q/selectivity = time-const антреккинга.
|
||||||
|
- depth-amount таблица (settled, band1@500): sel 0..6 монотонно −7.79→−9.08 dB, затем dip sel8=−8.79, sel10=−7.71 (non-monotonic у высоких sel, нюанс трекера).
|
||||||
|
- «650Hz notch» в раннем FFT-анализе — АРТЕФАКТ деления на near-zero dry; реальные числа = ratio dry/fx только там, где dry>5% пика (pure 500Hz tone → окно вокруг 500).
|
||||||
|
- **secret свипа: `patchparam.py`** обязателен для формат-сохранения: state-блок в RPP = base64, wrapped по 128-симв/строка с 2-sp indent и НОВОЙ строкой перед '>'. Ключевое: глубина пишется ТОЛЬКО `%.16f` (короткая `0.0` → collapse!), поэтому CLI `depth=0.0!` форсит полную точность.
|
||||||
|
- Float-поиск по живому дампу: depth 301 hits (множество копий по bands), selectivity/sharpness → общий бакет (10.015, 9.997), mix → 4 копии 100.0, band freqs (678.76, 8242.67) → 0 точных (хранятся иначе, напр. Hz→binidx или через npf).
|
||||||
|
- Живой diff dep0 vs dep1 в .data (0x182622130, 0x182622350, 0x1826223c0, 0x18262aca8, 0x182672fe4) — флоаты-состояние меняются; но регион содержит много мусора/nan (это .data буферы рендера, не коэфф).
|
||||||
|
|
||||||
|
### Провалы/уроки
|
||||||
|
- ASLR между отдельными rtdump-запусками сдвигает раскладку регионов → прямого VA-сравнения НЕТ (dep0b 10 регионов, dep1b 9). Только те адреса, что реально в 0x18260a000-0x18267c000 (совпадают) сравнимы.
|
||||||
|
|
||||||
|
## Relevant Files
|
||||||
|
- **`/home/m/re-tools/sim.py`** — поведенческий симулятор (LUT amount + 2nd-order bp Q(sel) + env attack/release + mix/mode). `simulate(x, fc, depth, sens, sharp, sel, mode, attack, release, mix)`. RMSE settled ≤0.05dB (см. таблицу выше).
|
||||||
|
- **`/home/m/re-tools/verify_sim.py`** — RMSE-сверка симулятора против рендеров (mag@500 трасса). Использование: `verify_sim.py <fx.wav> --params depth=5 attack=5`.
|
||||||
|
- **`/home/m/re-tools/fit_curves.py`** — фит LUT (остаётся как аналитический эквивалент; LUT в sim.py приоритетнее).
|
||||||
|
- probes500/probes500b_wav+byp — зонды ширины нотча; prb_*/prb2_* — рендеры.
|
||||||
|
- `synth_multi.py` (AM-комб 200–3000Hz резонансный), `burst500.wav` + `burst500_b1.rpp/.wav` + `burst500_byp.rpp/.wav` (burst-атак/релиз band1@500Hz), `burst_dep0/dep1/sel0/sel2/sel5.rpp/.wav` (τ-sweep), `comb.wav` + `comb_b1_*.rpp/wav`, `comb_dep_{0..1}`, `depthcurve.npy`. **Рендеры Reaper = 24-bit** — читать с `sampwidth=3`, не 16-bit!
|
||||||
|
- `/home/m/re-tools/patchparam.py` — патчер PARAM в base64 state с формат-сохранением (128-wrap, `depth=...!` → %.16f).
|
||||||
|
- `/home/m/re-tools/spec.py`, `probe.py`, `notch.py`, `synth.py`, `mkbase.py`, `tt_sweep.py`.
|
||||||
|
- `/home/m/soothe-bt/tone1kq.wav` (чистый 1k, -18dBFS), `dual.wav` (500+2000Hz), `resonant.wav` (500Hz-резонанс).
|
||||||
|
- `/home/m/soothe-bt/t1kq_*.rpp/wav`, `res_only1_*.rpp/wav`, `dual_b1_*.rpp/wav`.
|
||||||
|
- `/home/m/re-tools/sweep.py` — адаптивный генератор RPP (tpl фиксирован, для смены файла использовать mkbase + tt_sweep).
|
||||||
|
- `/home/m/soothe-bt/tt_base.rpp` (template: testtone + дефолтные парамы), `tt_ref.wav`.
|
||||||
|
- `/home/m/soothe-bt/s_corr_{sel,os,res,mix,mode}*.wav` — верифицированные свипы на resonant.wav.
|
||||||
|
- `/home/m/soothe-bt/render_v5.rpp`, `s_ref2.wav` (бит-в-бит оригинал).
|
||||||
|
## Фаза 3: Трекинг подтверждён (aug 16) — ARCHITECTURE REVISION
|
||||||
|
- **Вход-зависимость**: ред@1k растёт с уровнем сигнала (band1=1000, тон 1k):
|
||||||
|
`-27→-21→-15→-12→-9→-6→-3 dBFS` ⇒ `5.80→7.75→9.91→11.05→12.21→13.40→14.60 dB`. amount растёт с уровнем детектируемого резонанса (не бинарный порог).
|
||||||
|
- **Трекинг нотча** (fresh renders `trk_b1_*`, вход tone1k 1k, band1∈{500,1000,2000}, длинная FFT): нотч ВСЕГДА на fc=тон=1k, band-частота НЕ двигает позицию режектора:
|
||||||
|
- band=500 → 11.91dB, band=1000 → 15.82dB, band=2000 → 11.91dB, band1 off → 9.80dB, sens=0 → 9.80dB.
|
||||||
|
- Единственный вклад band-EQ — ВЕС глубины детекции в области (совпадение band~тон даёт +4dB; off/sens0 −2dB).
|
||||||
|
- **Мульти-пики**: dual (500+2000), оба нотча активны одновременно при sel=1..20 (~10dB оба) → детектор находит НЕСКОЛЬКО резонансов; selectivity НЕ регулирует число нотчей на явных тонах.
|
||||||
|
- **Пользователь (иерархия)**: детектор оценивает каждую частоту спектра; band-EQ формирует его ВХОД (усиление/ослабление детекции по областям, у band2 sens может быть −12); selectivity = отбор пиков; sharpness = форма нотча; depth = глобальная глубина.
|
||||||
|
- **Reaper-ловушка**: tt_sweep с ОТНОСИТЕЛЬНЫМ out_wav создаёт ПОДДИРЕКТОРИЮ → всегда абсолютный путь в RENDER_FILE.
|
||||||
|
- Файлы: `trk_base.rpp`, `trk_b1_{500,1000,2000,eqoff,sens0}.rpp/.wav`, `sel_base.rpp`, `sel_{1,3,10,20}.rpp/.wav`, `lvl_{03..27}db.wav` + `lvl_t_*.rpp/.wav`.
|
||||||
|
### Фаза 3b: amount зависИТ от уровня сигнала (не просто порог)
|
||||||
|
- lvl_sweep (тон 1k, band1@1000, depth=0.864): ред@1k = 5.80 / 7.75 / 9.91 / 11.05 / 12.21 / 13.40 / 14.60 для −27..−3 dBFS.
|
||||||
|
- При depth=10: 21.67 / 24.15 / 26.63 / 27.86 / 29.09 / 30.33 / 31.56. Наклон d(ред)/d(level) ≈ 0.37–0.41 dB/dB обоих depth → amount ∝ уровень^p (монотонно с насыщением), НЕ бинарный порог. depth — масштаб, не floor.
|
||||||
|
- Обе кривые монотонны и близки по форме (отношение ~2.2 при громком, ~3.7 при тихом → форма зависит от depth слабо).
|
||||||
|
### Фаза 4 (aug 16): КАРТА EQ-ВЕСОВ ДЕТЕКТОРА
|
||||||
|
- **`on` полосы = enable детекторного веса**; off-полоса её sens НЕ отдаёт в детекцию. Подтверждения:
|
||||||
|
- eqT_b1_b2/b3/b4/b2b3b4 и trk_b1_500 (sens off-полос любая) ВСЕ = 11.91dB (только от b1 sens12); eq_off_sensfac (все off, factory sens) = 9.80dB = нейтраль.
|
||||||
|
- eq1778_b2s_12 (b2 on=0) = 11.81, b1on12_b2s12 (b2 on=1) = 17.60 — ЕДИНСТВЕННАЯ разница band2 on.
|
||||||
|
- Баг в раннем clear_bands сбрасывал b1 sens→0 (eq_b1_on был sens0=нейтраль 9.80; правильный net_b1on12 sens12 = 11.91).
|
||||||
|
- **S(sens) при совмещённом весе (band1@1k, тон 1k)**: sens 0/6/9/12/18/24 → boost над полом 0.00/3.16/4.65/6.02/6.02/6.02 dB.
|
||||||
|
boost = 6.02·min(1, sens/12), насыщение +6dB при sens≥12 (не монотонно из-за сатурации детектора, не бага).
|
||||||
|
- **W(f) — колокол EQ-веса** (тон 1k, одна полоса on sens12, варьирую band freq):
|
||||||
|
- band1 (q≈1): 500→11.91(+2.11), 600→12.80(+3.00), 750→14.28(+4.48), 850→15.19(+5.39), 950→15.75(+5.95), 1000→15.82(+6.02), 1050→15.76, 1200→15.05(+5.25), 1500→13.44(+3.64), 2000→11.91(+2.11), 2500→11.16(+1.36). Симметричный широкий колокол (первый октав хвост +2.1dB, не спадает к 0).
|
||||||
|
- band2 (q≈4.5): 600→10.15(+0.35), 750→10.78(+0.98), 850→11.97(+2.17), 1000→15.80(+6.00), 1200→11.68(+1.88), 1500→10.34(+0.54) — РЕЗКИЙ колокол. **Q полосы управляет шириной детекторного веса** (q=4.5 на −1 октаве теряет ~полностью, q=1 теряет только ~66%).
|
||||||
|
- **Позиция нотча**: band freq не двигает режектор (всегда на резонансе тона); EQ-веса модулируют ТОЛЬКО глубину.
|
||||||
|
- Новый модельный блок: `red(f_tone) = floor(level) + Σ_on boost_i`, `boost_i = 6.02·min(1,|sens_i|/12)·H_q(f_tone; fc_i, Q_i)`.
|
||||||
|
- Файлы: `wf_{600,750,...,2500}.wav` (band1 q-sweep), `ws_{6,9,18,24}.wav` (aligned sens), `b2q_{600..1500}.wav` (band2 q=4.5 sweep), `b1on12_b2{on12,s12}.wav`.
|
||||||
|
### Фаза 5 (aug 16): STFT-параметры детектора (Stage 1)
|
||||||
|
- **Хоп/задержка**: tone-jump зонд tj.wav (1k→1.5k в t=2.0s) → нотч пересаживается на новый резонанс за ~1 кадр ≈ 18мс (hop ≤ 512 сэмплов). Трекинг чирпа 400→2000Hz в реальном времени, dt≈0.03→0.05s (шум оценки FFT-бина, значимой задержки нет).
|
||||||
|
- **Разрешение по частоте**: пары 1000+1040 → сливаются в один широкий нотч; 1000+1100 разделяются (центры ≈996/1104, но провал между ними всё ещё глубокий); 1000+1200 → два чётких отдельных нотча с полным восстановлением на 1100. → окно FFT ≈ 1024–2048 (бины 21–43Hz), multiple-нотч подтверждён.
|
||||||
|
- **Энерготрекинг (fx@f по окну 2048, sync-Goertzel)**: до прыжка fx@1000 ≈ −24dB (ред ~14dB), после — fx@1500 ≈ −23.8dB. Двойной бурст (gap 10..100ms) → нотч на том же тоне не переоткрывается между бурстами (ред остаётся ~4dB floor) — bурст probe (осб) указывает на фиксированный window-центр, не пер-открытие.
|
||||||
|
- Вывод для v5: STFT с окном ~2048, хоп ~512–1024; window function Hann; детектор по |X| per-bin → локальные пики (selectivity); EQ-веса W(f) как множитель sensitivity per-bin; per-пик нотч (bandstop Q=sharpness).
|
||||||
|
- Файлы: `chirp_log_400_2000.wav`, `chirp_trk.rpp/.wav`, `pair_{1040,1100,1200}.wav` + `pair_trk_{40,100,200}`, `dburst_{10,20,50,100}.wav` + `dburst_trk_*`, `tj.wav` + `tj_trk.rpp/.wav`.
|
||||||
|
### Фаза 6 (aug 16): КОЛОКОЛ EQ-ВЕСА + SHARPNESS + level-floor (Stage 2 фит)
|
||||||
|
- **H(f) — колокол**: `H(f) = 1/sqrt(1 + (Qeff·A)²)`, `A = f/fc − fc/f`. Qeff = **1.54·q^1.33** (q=1→1.54, 2→3.32, 3→5.64, 6→16.8). Воспроизводит кривую band1 (wf_*) с ошибкой ≤0.15dB по всему диапазону 500..2500.
|
||||||
|
- **Sharpness → Q нотча**: psh_{1,3,5,10} (probes500, drive 500·0.2, band1@500): центр 505Hz red 1.3/4.3/6.8/8.7dB; ширина Q_notch≈1.15·sharp (sh10→~11, sh5→~6, sh3→~3, sh1→~1.2). Центр-глубина НЕ постоянна — растёт с sharp (1.3→8.7), т.е. sharp масштабирует amount И Q.
|
||||||
|
- **level-floor (нейтраль)**: `neut_{24..3}` (все полосы off/sens0, тон 1k): red = 2.22/3.36/4.85/5.71/6.65/7.65 dB при −24/−18/−12/−9/−6/−3 dBFS rms. Монотонно, наклон ~0.31 dB/dB. `al_{*}` (b1@1000 sens12): 5.80/7.75/9.91/11.04/12.21/13.39 → boost = 3.59/4.39/5.06/5.33/5.56/5.74, **насыщается к ~6dB** с уровнем (не аддитивен в dB!).
|
||||||
|
- Модель amount: `red(f) = curv(level·G_eq(f))` с насыщением (не линейный сдвиг в dB). depth масштабирует (фаза 3b). Для sim: 2D LUT level×W.
|
||||||
|
- Файлы: `lvl_tone_lv{24,18,12,9,6,3}.wav`, `neut_{*}.rpp/.wav`, `al_{*}.rpp/.wav`, `psh_{1,3,5,10}.rpp/.wav`.
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
; cons_52ec00 0x18052eb00-0x18052f100
|
||||||
|
|
||||||
|
/tmp/slice.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
000000018052eb00 <.data>:
|
||||||
|
18052eb00: 0f af c8 imul %eax,%ecx
|
||||||
|
18052eb03: 8b 05 f7 cb 0f 02 mov 0x20fcbf7(%rip),%eax # 0x18262b700
|
||||||
|
18052eb09: 44 0f af e0 imul %eax,%r12d
|
||||||
|
18052eb0d: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052eb13: 05 e2 2c dc 06 add $0x6dc2ce2,%eax
|
||||||
|
18052eb18: 44 2b e1 sub %ecx,%r12d
|
||||||
|
18052eb1b: 25 7f 00 00 80 and $0x8000007f,%eax
|
||||||
|
18052eb20: 7d 07 jge 0x18052eb29
|
||||||
|
18052eb22: ff c8 dec %eax
|
||||||
|
18052eb24: 83 c8 80 or $0xffffff80,%eax
|
||||||
|
18052eb27: ff c0 inc %eax
|
||||||
|
18052eb29: 89 86 e0 04 24 00 mov %eax,0x2404e0(%rsi)
|
||||||
|
18052eb2f: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052eb32: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052eb39: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052eb3e: 8b 05 c0 cb 0f 02 mov 0x20fcbc0(%rip),%eax # 0x18262b704
|
||||||
|
18052eb44: 66 0f 6e d0 movd %eax,%xmm2
|
||||||
|
18052eb48: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052eb4e: ff c0 inc %eax
|
||||||
|
18052eb50: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052eb53: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052eb5a: 0f 5b d2 cvtdq2ps %xmm2,%xmm2
|
||||||
|
18052eb5d: f3 0f 59 d0 mulss %xmm0,%xmm2
|
||||||
|
18052eb61: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052eb66: 8b 05 5c ca 0f 02 mov 0x20fca5c(%rip),%eax # 0x18262b5c8
|
||||||
|
18052eb6c: 66 0f 6e c8 movd %eax,%xmm1
|
||||||
|
18052eb70: 8b 05 8a cb 0f 02 mov 0x20fcb8a(%rip),%eax # 0x18262b700
|
||||||
|
18052eb76: 0f 5b c9 cvtdq2ps %xmm1,%xmm1
|
||||||
|
18052eb79: f3 0f 59 c8 mulss %xmm0,%xmm1
|
||||||
|
18052eb7d: f3 0f 59 ca mulss %xmm2,%xmm1
|
||||||
|
18052eb81: f3 0f 58 cd addss %xmm5,%xmm1
|
||||||
|
18052eb85: f3 0f 2c d9 cvttss2si %xmm1,%ebx
|
||||||
|
18052eb89: 0f af d8 imul %eax,%ebx
|
||||||
|
18052eb8c: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052eb92: 05 9a f2 4b 00 add $0x4bf29a,%eax
|
||||||
|
18052eb97: 89 9c 24 f8 00 00 00 mov %ebx,0xf8(%rsp)
|
||||||
|
18052eb9e: 25 7f 00 00 80 and $0x8000007f,%eax
|
||||||
|
18052eba3: 7d 07 jge 0x18052ebac
|
||||||
|
18052eba5: ff c8 dec %eax
|
||||||
|
18052eba7: 83 c8 80 or $0xffffff80,%eax
|
||||||
|
18052ebaa: ff c0 inc %eax
|
||||||
|
18052ebac: 89 86 e0 04 24 00 mov %eax,0x2404e0(%rsi)
|
||||||
|
18052ebb2: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052ebb5: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052ebbc: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052ebc1: 8b 05 3d cb 0f 02 mov 0x20fcb3d(%rip),%eax # 0x18262b704
|
||||||
|
18052ebc7: 66 0f 6e d0 movd %eax,%xmm2
|
||||||
|
18052ebcb: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052ebd1: ff c0 inc %eax
|
||||||
|
18052ebd3: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052ebd6: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052ebdd: 0f 5b d2 cvtdq2ps %xmm2,%xmm2
|
||||||
|
18052ebe0: f3 0f 59 d0 mulss %xmm0,%xmm2
|
||||||
|
18052ebe4: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052ebe9: 8b 05 d9 c9 0f 02 mov 0x20fc9d9(%rip),%eax # 0x18262b5c8
|
||||||
|
18052ebef: 66 0f 6e c8 movd %eax,%xmm1
|
||||||
|
18052ebf3: 8b 05 07 cb 0f 02 mov 0x20fcb07(%rip),%eax # 0x18262b700
|
||||||
|
18052ebf9: 0f 5b c9 cvtdq2ps %xmm1,%xmm1
|
||||||
|
18052ebfc: f3 0f 59 c8 mulss %xmm0,%xmm1
|
||||||
|
18052ec00: f3 0f 59 ca mulss %xmm2,%xmm1
|
||||||
|
18052ec04: f3 0f 58 cd addss %xmm5,%xmm1
|
||||||
|
18052ec08: f3 0f 2c c9 cvttss2si %xmm1,%ecx
|
||||||
|
18052ec0c: 0f af c8 imul %eax,%ecx
|
||||||
|
18052ec0f: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052ec15: 05 94 62 cb 00 add $0xcb6294,%eax
|
||||||
|
18052ec1a: 66 0f 6e f1 movd %ecx,%xmm6
|
||||||
|
18052ec1e: 0f 5b f6 cvtdq2ps %xmm6,%xmm6
|
||||||
|
18052ec21: 25 7f 00 00 80 and $0x8000007f,%eax
|
||||||
|
18052ec26: 7d 07 jge 0x18052ec2f
|
||||||
|
18052ec28: ff c8 dec %eax
|
||||||
|
18052ec2a: 83 c8 80 or $0xffffff80,%eax
|
||||||
|
18052ec2d: ff c0 inc %eax
|
||||||
|
18052ec2f: 89 86 e0 04 24 00 mov %eax,0x2404e0(%rsi)
|
||||||
|
18052ec35: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052ec38: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052ec3f: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052ec44: 8b 05 ba ca 0f 02 mov 0x20fcaba(%rip),%eax # 0x18262b704
|
||||||
|
18052ec4a: 66 0f 6e e0 movd %eax,%xmm4
|
||||||
|
18052ec4e: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052ec54: ff c0 inc %eax
|
||||||
|
18052ec56: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052ec59: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052ec60: 0f 5b e4 cvtdq2ps %xmm4,%xmm4
|
||||||
|
18052ec63: f3 0f 59 e0 mulss %xmm0,%xmm4
|
||||||
|
18052ec67: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052ec6c: 8b 05 56 c9 0f 02 mov 0x20fc956(%rip),%eax # 0x18262b5c8
|
||||||
|
18052ec72: 66 0f 6e d8 movd %eax,%xmm3
|
||||||
|
18052ec76: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052ec7c: 0f 5b db cvtdq2ps %xmm3,%xmm3
|
||||||
|
18052ec7f: 05 e0 b5 10 00 add $0x10b5e0,%eax
|
||||||
|
18052ec84: f3 0f 59 d8 mulss %xmm0,%xmm3
|
||||||
|
18052ec88: 25 7f 00 00 80 and $0x8000007f,%eax
|
||||||
|
18052ec8d: 7d 07 jge 0x18052ec96
|
||||||
|
18052ec8f: ff c8 dec %eax
|
||||||
|
18052ec91: 83 c8 80 or $0xffffff80,%eax
|
||||||
|
18052ec94: ff c0 inc %eax
|
||||||
|
18052ec96: f3 0f 10 3d 06 52 f9 movss 0x1f95206(%rip),%xmm7 # 0x1824c3ea4
|
||||||
|
18052ec9d: 01
|
||||||
|
18052ec9e: 33 ff xor %edi,%edi
|
||||||
|
18052eca0: 89 86 e0 04 24 00 mov %eax,0x2404e0(%rsi)
|
||||||
|
18052eca6: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052eca9: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052ecb0: f3 0f 59 dc mulss %xmm4,%xmm3
|
||||||
|
18052ecb4: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052ecb9: 8b 05 45 ca 0f 02 mov 0x20fca45(%rip),%eax # 0x18262b704
|
||||||
|
18052ecbf: f3 0f 58 dd addss %xmm5,%xmm3
|
||||||
|
18052ecc3: 66 0f 6e d0 movd %eax,%xmm2
|
||||||
|
18052ecc7: 8b 86 e0 04 24 00 mov 0x2404e0(%rsi),%eax
|
||||||
|
18052eccd: ff c0 inc %eax
|
||||||
|
18052eccf: 48 63 c8 movslq %eax,%rcx
|
||||||
|
18052ecd2: 48 8b 86 b0 08 54 00 mov 0x5408b0(%rsi),%rax
|
||||||
|
18052ecd9: 0f 5b d2 cvtdq2ps %xmm2,%xmm2
|
||||||
|
18052ecdc: f3 44 0f 2c eb cvttss2si %xmm3,%r13d
|
||||||
|
18052ece1: f3 0f 59 d0 mulss %xmm0,%xmm2
|
||||||
|
18052ece5: f3 0f 10 04 88 movss (%rax,%rcx,4),%xmm0
|
||||||
|
18052ecea: 8b 05 d8 c8 0f 02 mov 0x20fc8d8(%rip),%eax # 0x18262b5c8
|
||||||
|
18052ecf0: 66 0f 6e c8 movd %eax,%xmm1
|
||||||
|
18052ecf4: 8b 05 06 ca 0f 02 mov 0x20fca06(%rip),%eax # 0x18262b700
|
||||||
|
18052ecfa: 0f 5b c9 cvtdq2ps %xmm1,%xmm1
|
||||||
|
18052ecfd: f3 0f 59 c8 mulss %xmm0,%xmm1
|
||||||
|
18052ed01: f3 0f 59 ca mulss %xmm2,%xmm1
|
||||||
|
18052ed05: f3 0f 58 cd addss %xmm5,%xmm1
|
||||||
|
18052ed09: f3 0f 2c c9 cvttss2si %xmm1,%ecx
|
||||||
|
18052ed0d: 0f af c8 imul %eax,%ecx
|
||||||
|
18052ed10: 8b 05 ea c9 0f 02 mov 0x20fc9ea(%rip),%eax # 0x18262b700
|
||||||
|
18052ed16: 44 0f af e8 imul %eax,%r13d
|
||||||
|
18052ed1a: 44 03 e9 add %ecx,%r13d
|
||||||
|
18052ed1d: 84 d2 test %dl,%dl
|
||||||
|
18052ed1f: 0f 85 95 03 00 00 jne 0x18052f0ba
|
||||||
|
18052ed25: 48 8b ce mov %rsi,%rcx
|
||||||
|
18052ed28: e8 d3 e0 ff ff call 0x18052ce00
|
||||||
|
18052ed2d: 8b 86 a0 01 00 00 mov 0x1a0(%rsi),%eax
|
||||||
|
18052ed33: 48 8d 8e 68 06 54 00 lea 0x540668(%rsi),%rcx
|
||||||
|
18052ed3a: 0f af c3 imul %ebx,%eax
|
||||||
|
18052ed3d: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052ed40: c1 e0 03 shl $0x3,%eax
|
||||||
|
18052ed43: 89 86 68 08 54 00 mov %eax,0x540868(%rsi)
|
||||||
|
18052ed49: 99 cltd
|
||||||
|
18052ed4a: 2b c2 sub %edx,%eax
|
||||||
|
18052ed4c: d1 f8 sar $1,%eax
|
||||||
|
18052ed4e: 8d 14 18 lea (%rax,%rbx,1),%edx
|
||||||
|
18052ed51: 89 96 6c 08 54 00 mov %edx,0x54086c(%rsi)
|
||||||
|
18052ed57: 03 d2 add %edx,%edx
|
||||||
|
18052ed59: e8 32 f4 ff ff call 0x18052e190
|
||||||
|
18052ed5e: 8b 96 6c 08 54 00 mov 0x54086c(%rsi),%edx
|
||||||
|
18052ed64: 48 8d 8e 98 06 54 00 lea 0x540698(%rsi),%rcx
|
||||||
|
18052ed6b: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052ed6e: e8 1d f4 ff ff call 0x18052e190
|
||||||
|
18052ed73: 8b d7 mov %edi,%edx
|
||||||
|
18052ed75: 39 be a0 06 54 00 cmp %edi,0x5406a0(%rsi)
|
||||||
|
18052ed7b: 7e 1e jle 0x18052ed9b
|
||||||
|
18052ed7d: 8b cf mov %edi,%ecx
|
||||||
|
18052ed7f: 90 nop
|
||||||
|
18052ed80: 48 8b 86 98 06 54 00 mov 0x540698(%rsi),%rax
|
||||||
|
18052ed87: 48 8d 49 04 lea 0x4(%rcx),%rcx
|
||||||
|
18052ed8b: ff c2 inc %edx
|
||||||
|
18052ed8d: f3 0f 11 74 01 fc movss %xmm6,-0x4(%rcx,%rax,1)
|
||||||
|
18052ed93: 3b 96 a0 06 54 00 cmp 0x5406a0(%rsi),%edx
|
||||||
|
18052ed99: 7c e5 jl 0x18052ed80
|
||||||
|
18052ed9b: 8b 96 6c 08 54 00 mov 0x54086c(%rsi),%edx
|
||||||
|
18052eda1: 48 8d 8e a8 06 54 00 lea 0x5406a8(%rsi),%rcx
|
||||||
|
18052eda8: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052edab: e8 e0 f3 ff ff call 0x18052e190
|
||||||
|
18052edb0: 8b 96 6c 08 54 00 mov 0x54086c(%rsi),%edx
|
||||||
|
18052edb6: 48 8d 8e b8 06 54 00 lea 0x5406b8(%rsi),%rcx
|
||||||
|
18052edbd: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052edc0: e8 cb f3 ff ff call 0x18052e190
|
||||||
|
18052edc5: 8b 96 6c 08 54 00 mov 0x54086c(%rsi),%edx
|
||||||
|
18052edcb: 48 8d 8e c8 06 54 00 lea 0x5406c8(%rsi),%rcx
|
||||||
|
18052edd2: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052edd5: e8 b6 f3 ff ff call 0x18052e190
|
||||||
|
18052edda: 8b 96 6c 08 54 00 mov 0x54086c(%rsi),%edx
|
||||||
|
18052ede0: 48 8d 8e d8 06 54 00 lea 0x5406d8(%rsi),%rcx
|
||||||
|
18052ede7: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052edea: e8 a1 f3 ff ff call 0x18052e190
|
||||||
|
18052edef: 8b 96 6c 08 54 00 mov 0x54086c(%rsi),%edx
|
||||||
|
18052edf5: 48 8d 8e e8 06 54 00 lea 0x5406e8(%rsi),%rcx
|
||||||
|
18052edfc: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052edff: e8 8c f3 ff ff call 0x18052e190
|
||||||
|
18052ee04: 45 8b fc mov %r12d,%r15d
|
||||||
|
18052ee07: 44 3b 66 30 cmp 0x30(%rsi),%r12d
|
||||||
|
18052ee0b: 0f 8d ee 01 00 00 jge 0x18052efff
|
||||||
|
18052ee11: 45 8b f5 mov %r13d,%r14d
|
||||||
|
18052ee14: 49 63 dc movslq %r12d,%rbx
|
||||||
|
18052ee17: 45 0f af f5 imul %r13d,%r14d
|
||||||
|
18052ee1b: 48 81 c3 73 40 05 00 add $0x54073,%rbx
|
||||||
|
18052ee22: 48 c1 e3 04 shl $0x4,%rbx
|
||||||
|
18052ee26: 41 c1 e6 0e shl $0xe,%r14d
|
||||||
|
18052ee2a: 48 03 de add %rsi,%rbx
|
||||||
|
18052ee2d: 0f 1f 00 nopl (%rax)
|
||||||
|
18052ee30: 8b ae 6c 08 54 00 mov 0x54086c(%rsi),%ebp
|
||||||
|
18052ee36: 8b 83 50 ff ff ff mov -0xb0(%rbx),%eax
|
||||||
|
18052ee3c: 3b c5 cmp %ebp,%eax
|
||||||
|
18052ee3e: 74 3e je 0x18052ee7e
|
||||||
|
18052ee40: 85 c0 test %eax,%eax
|
||||||
|
18052ee42: 7e 11 jle 0x18052ee55
|
||||||
|
18052ee44: 48 8b 8b 48 ff ff ff mov -0xb8(%rbx),%rcx
|
||||||
|
18052ee4b: 48 85 c9 test %rcx,%rcx
|
||||||
|
18052ee4e: 74 05 je 0x18052ee55
|
||||||
|
18052ee50: e8 6b 22 ad ff call 0x1800010c0
|
||||||
|
18052ee55: 85 ed test %ebp,%ebp
|
||||||
|
18052ee57: 48 89 bb 48 ff ff ff mov %rdi,-0xb8(%rbx)
|
||||||
|
18052ee5e: 0f 48 ef cmovs %edi,%ebp
|
||||||
|
18052ee61: 89 ab 50 ff ff ff mov %ebp,-0xb0(%rbx)
|
||||||
|
18052ee67: 85 ed test %ebp,%ebp
|
||||||
|
18052ee69: 74 3e je 0x18052eea9
|
||||||
|
18052ee6b: 8d 0c ad 00 00 00 00 lea 0x0(,%rbp,4),%ecx
|
||||||
|
18052ee72: e8 09 22 ad ff call 0x180001080
|
||||||
|
18052ee77: 48 89 83 48 ff ff ff mov %rax,-0xb8(%rbx)
|
||||||
|
18052ee7e: 8b d7 mov %edi,%edx
|
||||||
|
18052ee80: 39 bb 50 ff ff ff cmp %edi,-0xb0(%rbx)
|
||||||
|
18052ee86: 7e 21 jle 0x18052eea9
|
||||||
|
18052ee88: 48 8b cf mov %rdi,%rcx
|
||||||
|
18052ee8b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
18052ee90: 48 8b 83 48 ff ff ff mov -0xb8(%rbx),%rax
|
||||||
|
18052ee97: 48 8d 49 04 lea 0x4(%rcx),%rcx
|
||||||
|
18052ee9b: ff c2 inc %edx
|
||||||
|
18052ee9d: 89 7c 01 fc mov %edi,-0x4(%rcx,%rax,1)
|
||||||
|
18052eea1: 3b 93 50 ff ff ff cmp -0xb0(%rbx),%edx
|
||||||
|
18052eea7: 7c e7 jl 0x18052ee90
|
||||||
|
18052eea9: 8b 03 mov (%rbx),%eax
|
||||||
|
18052eeab: 41 3b c6 cmp %r14d,%eax
|
||||||
|
18052eeae: 74 35 je 0x18052eee5
|
||||||
|
18052eeb0: 85 c0 test %eax,%eax
|
||||||
|
18052eeb2: 7e 0e jle 0x18052eec2
|
||||||
|
18052eeb4: 48 8b 4b f8 mov -0x8(%rbx),%rcx
|
||||||
|
18052eeb8: 48 85 c9 test %rcx,%rcx
|
||||||
|
18052eebb: 74 05 je 0x18052eec2
|
||||||
|
18052eebd: e8 fe 21 ad ff call 0x1800010c0
|
||||||
|
18052eec2: 45 85 f6 test %r14d,%r14d
|
||||||
|
18052eec5: 48 89 7b f8 mov %rdi,-0x8(%rbx)
|
||||||
|
18052eec9: 41 8b ce mov %r14d,%ecx
|
||||||
|
18052eecc: 0f 48 cf cmovs %edi,%ecx
|
||||||
|
18052eecf: 89 0b mov %ecx,(%rbx)
|
||||||
|
18052eed1: 85 c9 test %ecx,%ecx
|
||||||
|
18052eed3: 74 31 je 0x18052ef06
|
||||||
|
18052eed5: 8d 0c 8d 00 00 00 00 lea 0x0(,%rcx,4),%ecx
|
||||||
|
18052eedc: e8 9f 21 ad ff call 0x180001080
|
||||||
|
18052eee1: 48 89 43 f8 mov %rax,-0x8(%rbx)
|
||||||
|
18052eee5: 8b d7 mov %edi,%edx
|
||||||
|
18052eee7: 39 3b cmp %edi,(%rbx)
|
||||||
|
18052eee9: 7e 1b jle 0x18052ef06
|
||||||
|
18052eeeb: 48 8b cf mov %rdi,%rcx
|
||||||
|
18052eeee: 66 90 xchg %ax,%ax
|
||||||
|
18052eef0: 48 8b 43 f8 mov -0x8(%rbx),%rax
|
||||||
|
18052eef4: 48 8d 49 04 lea 0x4(%rcx),%rcx
|
||||||
|
18052eef8: ff c2 inc %edx
|
||||||
|
18052eefa: c7 44 01 fc 00 00 80 movl $0x3f800000,-0x4(%rcx,%rax,1)
|
||||||
|
18052ef01: 3f
|
||||||
|
18052ef02: 3b 13 cmp (%rbx),%edx
|
||||||
|
18052ef04: 7c ea jl 0x18052eef0
|
||||||
|
18052ef06: 8b ae 6c 08 54 00 mov 0x54086c(%rsi),%ebp
|
||||||
|
18052ef0c: 8b 83 80 00 00 00 mov 0x80(%rbx),%eax
|
||||||
|
18052ef12: 3b c5 cmp %ebp,%eax
|
||||||
|
18052ef14: 74 35 je 0x18052ef4b
|
||||||
|
18052ef16: 85 c0 test %eax,%eax
|
||||||
|
18052ef18: 7e 0e jle 0x18052ef28
|
||||||
|
18052ef1a: 48 8b 4b 78 mov 0x78(%rbx),%rcx
|
||||||
|
18052ef1e: 48 85 c9 test %rcx,%rcx
|
||||||
|
18052ef21: 74 05 je 0x18052ef28
|
||||||
|
18052ef23: e8 98 21 ad ff call 0x1800010c0
|
||||||
|
18052ef28: 85 ed test %ebp,%ebp
|
||||||
|
18052ef2a: 48 89 7b 78 mov %rdi,0x78(%rbx)
|
||||||
|
18052ef2e: 0f 48 ef cmovs %edi,%ebp
|
||||||
|
18052ef31: 89 ab 80 00 00 00 mov %ebp,0x80(%rbx)
|
||||||
|
18052ef37: 85 ed test %ebp,%ebp
|
||||||
|
18052ef39: 74 3b je 0x18052ef76
|
||||||
|
18052ef3b: 8d 0c ad 00 00 00 00 lea 0x0(,%rbp,4),%ecx
|
||||||
|
18052ef42: e8 39 21 ad ff call 0x180001080
|
||||||
|
18052ef47: 48 89 43 78 mov %rax,0x78(%rbx)
|
||||||
|
18052ef4b: 8b d7 mov %edi,%edx
|
||||||
|
18052ef4d: 39 bb 80 00 00 00 cmp %edi,0x80(%rbx)
|
||||||
|
18052ef53: 7e 21 jle 0x18052ef76
|
||||||
|
18052ef55: 48 8b cf mov %rdi,%rcx
|
||||||
|
18052ef58: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
18052ef5f: 00
|
||||||
|
18052ef60: 48 8b 43 78 mov 0x78(%rbx),%rax
|
||||||
|
18052ef64: 48 8d 49 04 lea 0x4(%rcx),%rcx
|
||||||
|
18052ef68: ff c2 inc %edx
|
||||||
|
18052ef6a: 89 7c 01 fc mov %edi,-0x4(%rcx,%rax,1)
|
||||||
|
18052ef6e: 3b 93 80 00 00 00 cmp 0x80(%rbx),%edx
|
||||||
|
18052ef74: 7c ea jl 0x18052ef60
|
||||||
|
18052ef76: 8b ae 6c 08 54 00 mov 0x54086c(%rsi),%ebp
|
||||||
|
18052ef7c: 8b 83 a0 00 00 00 mov 0xa0(%rbx),%eax
|
||||||
|
18052ef82: 3b c5 cmp %ebp,%eax
|
||||||
|
18052ef84: 74 3e je 0x18052efc4
|
||||||
|
18052ef86: 85 c0 test %eax,%eax
|
||||||
|
18052ef88: 7e 11 jle 0x18052ef9b
|
||||||
|
18052ef8a: 48 8b 8b 98 00 00 00 mov 0x98(%rbx),%rcx
|
||||||
|
18052ef91: 48 85 c9 test %rcx,%rcx
|
||||||
|
18052ef94: 74 05 je 0x18052ef9b
|
||||||
|
18052ef96: e8 25 21 ad ff call 0x1800010c0
|
||||||
|
18052ef9b: 85 ed test %ebp,%ebp
|
||||||
|
18052ef9d: 48 89 bb 98 00 00 00 mov %rdi,0x98(%rbx)
|
||||||
|
18052efa4: 0f 48 ef cmovs %edi,%ebp
|
||||||
|
18052efa7: 89 ab a0 00 00 00 mov %ebp,0xa0(%rbx)
|
||||||
|
18052efad: 85 ed test %ebp,%ebp
|
||||||
|
18052efaf: 74 3d je 0x18052efee
|
||||||
|
18052efb1: 8d 0c ad 00 00 00 00 lea 0x0(,%rbp,4),%ecx
|
||||||
|
18052efb8: e8 c3 20 ad ff call 0x180001080
|
||||||
|
18052efbd: 48 89 83 98 00 00 00 mov %rax,0x98(%rbx)
|
||||||
|
18052efc4: 8b d7 mov %edi,%edx
|
||||||
|
18052efc6: 39 bb a0 00 00 00 cmp %edi,0xa0(%rbx)
|
||||||
|
18052efcc: 7e 20 jle 0x18052efee
|
||||||
|
18052efce: 48 8b cf mov %rdi,%rcx
|
||||||
|
18052efd1: 48 8b 83 98 00 00 00 mov 0x98(%rbx),%rax
|
||||||
|
18052efd8: 48 8d 49 04 lea 0x4(%rcx),%rcx
|
||||||
|
18052efdc: ff c2 inc %edx
|
||||||
|
18052efde: c7 44 01 fc 00 00 80 movl $0x3f800000,-0x4(%rcx,%rax,1)
|
||||||
|
18052efe5: 3f
|
||||||
|
18052efe6: 3b 93 a0 00 00 00 cmp 0xa0(%rbx),%edx
|
||||||
|
18052efec: 7c e3 jl 0x18052efd1
|
||||||
|
18052efee: 41 ff c7 inc %r15d
|
||||||
|
18052eff1: 48 83 c3 10 add $0x10,%rbx
|
||||||
|
18052eff5: 44 3b 7e 30 cmp 0x30(%rsi),%r15d
|
||||||
|
18052eff9: 0f 8c 31 fe ff ff jl 0x18052ee30
|
||||||
|
18052efff: 8b 96 68 08 54 00 mov 0x540868(%rsi),%edx
|
||||||
|
18052f005: 48 8d 8e f8 06 54 00 lea 0x5406f8(%rsi),%rcx
|
||||||
|
18052f00c: c1 e2 03 shl $0x3,%edx
|
||||||
|
18052f00f: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052f012: e8 79 f1 ff ff call 0x18052e190
|
||||||
|
18052f017: 8b 96 68 08 54 00 mov 0x540868(%rsi),%edx
|
||||||
|
18052f01d: 48 8d 8e 08 07 54 00 lea 0x540708(%rsi),%rcx
|
||||||
|
18052f024: 41 0f af d5 imul %r13d,%edx
|
||||||
|
18052f028: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052f02b: e8 60 f1 ff ff call 0x18052e190
|
||||||
|
18052f030: 8b 96 68 08 54 00 mov 0x540868(%rsi),%edx
|
||||||
|
18052f036: 48 8d 8e 18 07 54 00 lea 0x540718(%rsi),%rcx
|
||||||
|
18052f03d: 41 0f af d5 imul %r13d,%edx
|
||||||
|
18052f041: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052f044: e8 47 f1 ff ff call 0x18052e190
|
||||||
|
18052f049: 8b 96 68 08 54 00 mov 0x540868(%rsi),%edx
|
||||||
|
18052f04f: 48 8d 8e 98 07 54 00 lea 0x540798(%rsi),%rcx
|
||||||
|
18052f056: 0f 57 d2 xorps %xmm2,%xmm2
|
||||||
|
18052f059: e8 32 f1 ff ff call 0x18052e190
|
||||||
|
18052f05e: 44 8b c7 mov %edi,%r8d
|
||||||
|
18052f061: 39 be a0 07 54 00 cmp %edi,0x5407a0(%rsi)
|
||||||
|
18052f067: 7e 26 jle 0x18052f08f
|
||||||
|
18052f069: 48 8b cf mov %rdi,%rcx
|
||||||
|
18052f06c: 0f 1f 40 00 nopl 0x0(%rax)
|
||||||
|
18052f070: 48 8b 86 98 07 54 00 mov 0x540798(%rsi),%rax
|
||||||
|
18052f077: 48 8d 49 04 lea 0x4(%rcx),%rcx
|
||||||
|
18052f07b: 41 ff c0 inc %r8d
|
||||||
|
18052f07e: c7 44 01 fc 00 00 80 movl $0x3f800000,-0x4(%rcx,%rax,1)
|
||||||
|
18052f085: 3f
|
||||||
|
18052f086: 44 3b 86 a0 07 54 00 cmp 0x5407a0(%rsi),%r8d
|
||||||
|
18052f08d: 7c e1 jl 0x18052f070
|
||||||
|
18052f08f: 44 8b 86 68 08 54 00 mov 0x540868(%rsi),%r8d
|
||||||
|
18052f096: 48 8d 8e 30 05 54 00 lea 0x540530(%rsi),%rcx
|
||||||
|
18052f09d: 8b 56 64 mov 0x64(%rsi),%edx
|
||||||
|
18052f0a0: e8 8b eb ff ff call 0x18052dc30
|
||||||
|
18052f0a5: 0f 28 df movaps %xmm7,%xmm3
|
||||||
|
18052f0a8: 48 8d 8e 30 05 54 00 lea 0x540530(%rsi),%rcx
|
||||||
|
18052f0af: 45 8b c5 mov %r13d,%r8d
|
||||||
|
18052f0b2: 41 8b d5 mov %r13d,%edx
|
||||||
|
18052f0b5: e8 46 0d 00 00 call 0x18052fe00
|
||||||
|
18052f0ba: 45 8b cc mov %r12d,%r9d
|
||||||
|
18052f0bd: 4c 8b ac 24 00 01 00 mov 0x100(%rsp),%r13
|
||||||
|
18052f0c4: 00
|
||||||
|
18052f0c5: 44 3b 66 30 cmp 0x30(%rsi),%r12d
|
||||||
|
18052f0c9: 0f 8d a8 00 00 00 jge 0x18052f177
|
||||||
|
18052f0cf: 49 63 cc movslq %r12d,%rcx
|
||||||
|
18052f0d2: 48 81 c1 7b 40 05 00 add $0x5407b,%rcx
|
||||||
|
18052f0d9: 48 c1 e1 04 shl $0x4,%rcx
|
||||||
|
18052f0dd: 48 03 ce add %rsi,%rcx
|
||||||
|
18052f0e0: 44 8b c7 mov %edi,%r8d
|
||||||
|
18052f0e3: 39 79 80 cmp %edi,-0x80(%rcx)
|
||||||
|
18052f0e6: 7e 22 jle 0x18052f10a
|
||||||
|
18052f0e8: 48 8b d7 mov %rdi,%rdx
|
||||||
|
18052f0eb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
18052f0f0: 48 8b 81 78 ff ff ff mov -0x88(%rcx),%rax
|
||||||
|
18052f0f7: 48 8d 52 04 lea 0x4(%rdx),%rdx
|
||||||
|
18052f0fb: 41 ff c0 inc %r8d
|
||||||
|
18052f0fe: f3 repz
|
||||||
|
18052f0ff: 0f .byte 0xf
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
; cons_536100 0x180536000-0x180536400
|
||||||
|
|
||||||
|
/tmp/slice.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
0000000180536000 <.data>:
|
||||||
|
180536000: e8 bb b0 ac ff call 0x1800010c0
|
||||||
|
180536005: 90 nop
|
||||||
|
180536006: 48 89 bb 58 07 54 00 mov %rdi,0x540758(%rbx)
|
||||||
|
18053600d: 89 bb 60 07 54 00 mov %edi,0x540760(%rbx)
|
||||||
|
180536013: 39 bb 50 07 54 00 cmp %edi,0x540750(%rbx)
|
||||||
|
180536019: 7e 12 jle 0x18053602d
|
||||||
|
18053601b: 48 8b 8b 48 07 54 00 mov 0x540748(%rbx),%rcx
|
||||||
|
180536022: 48 85 c9 test %rcx,%rcx
|
||||||
|
180536025: 74 06 je 0x18053602d
|
||||||
|
180536027: e8 94 b0 ac ff call 0x1800010c0
|
||||||
|
18053602c: 90 nop
|
||||||
|
18053602d: 48 89 bb 48 07 54 00 mov %rdi,0x540748(%rbx)
|
||||||
|
180536034: 89 bb 50 07 54 00 mov %edi,0x540750(%rbx)
|
||||||
|
18053603a: 48 8d 8b 28 07 54 00 lea 0x540728(%rbx),%rcx
|
||||||
|
180536041: 4c 8d 0d d8 81 ff ff lea -0x7e28(%rip),%r9 # 0x18052e220
|
||||||
|
180536048: ba 10 00 00 00 mov $0x10,%edx
|
||||||
|
18053604d: 44 8d 42 f2 lea -0xe(%rdx),%r8d
|
||||||
|
180536051: e8 c6 b6 bf 00 call 0x18113171c
|
||||||
|
180536056: 90 nop
|
||||||
|
180536057: 39 bb 20 07 54 00 cmp %edi,0x540720(%rbx)
|
||||||
|
18053605d: 7e 12 jle 0x180536071
|
||||||
|
18053605f: 48 8b 8b 18 07 54 00 mov 0x540718(%rbx),%rcx
|
||||||
|
180536066: 48 85 c9 test %rcx,%rcx
|
||||||
|
180536069: 74 06 je 0x180536071
|
||||||
|
18053606b: e8 50 b0 ac ff call 0x1800010c0
|
||||||
|
180536070: 90 nop
|
||||||
|
180536071: 48 89 bb 18 07 54 00 mov %rdi,0x540718(%rbx)
|
||||||
|
180536078: 89 bb 20 07 54 00 mov %edi,0x540720(%rbx)
|
||||||
|
18053607e: 39 bb 10 07 54 00 cmp %edi,0x540710(%rbx)
|
||||||
|
180536084: 7e 12 jle 0x180536098
|
||||||
|
180536086: 48 8b 8b 08 07 54 00 mov 0x540708(%rbx),%rcx
|
||||||
|
18053608d: 48 85 c9 test %rcx,%rcx
|
||||||
|
180536090: 74 06 je 0x180536098
|
||||||
|
180536092: e8 29 b0 ac ff call 0x1800010c0
|
||||||
|
180536097: 90 nop
|
||||||
|
180536098: 48 89 bb 08 07 54 00 mov %rdi,0x540708(%rbx)
|
||||||
|
18053609f: 89 bb 10 07 54 00 mov %edi,0x540710(%rbx)
|
||||||
|
1805360a5: 39 bb 00 07 54 00 cmp %edi,0x540700(%rbx)
|
||||||
|
1805360ab: 7e 12 jle 0x1805360bf
|
||||||
|
1805360ad: 48 8b 8b f8 06 54 00 mov 0x5406f8(%rbx),%rcx
|
||||||
|
1805360b4: 48 85 c9 test %rcx,%rcx
|
||||||
|
1805360b7: 74 06 je 0x1805360bf
|
||||||
|
1805360b9: e8 02 b0 ac ff call 0x1800010c0
|
||||||
|
1805360be: 90 nop
|
||||||
|
1805360bf: 48 89 bb f8 06 54 00 mov %rdi,0x5406f8(%rbx)
|
||||||
|
1805360c6: 89 bb 00 07 54 00 mov %edi,0x540700(%rbx)
|
||||||
|
1805360cc: 39 bb f0 06 54 00 cmp %edi,0x5406f0(%rbx)
|
||||||
|
1805360d2: 7e 12 jle 0x1805360e6
|
||||||
|
1805360d4: 48 8b 8b e8 06 54 00 mov 0x5406e8(%rbx),%rcx
|
||||||
|
1805360db: 48 85 c9 test %rcx,%rcx
|
||||||
|
1805360de: 74 06 je 0x1805360e6
|
||||||
|
1805360e0: e8 db af ac ff call 0x1800010c0
|
||||||
|
1805360e5: 90 nop
|
||||||
|
1805360e6: 48 89 bb e8 06 54 00 mov %rdi,0x5406e8(%rbx)
|
||||||
|
1805360ed: 89 bb f0 06 54 00 mov %edi,0x5406f0(%rbx)
|
||||||
|
1805360f3: 39 bb e0 06 54 00 cmp %edi,0x5406e0(%rbx)
|
||||||
|
1805360f9: 7e 12 jle 0x18053610d
|
||||||
|
1805360fb: 48 8b 8b d8 06 54 00 mov 0x5406d8(%rbx),%rcx
|
||||||
|
180536102: 48 85 c9 test %rcx,%rcx
|
||||||
|
180536105: 74 06 je 0x18053610d
|
||||||
|
180536107: e8 b4 af ac ff call 0x1800010c0
|
||||||
|
18053610c: 90 nop
|
||||||
|
18053610d: 48 89 bb d8 06 54 00 mov %rdi,0x5406d8(%rbx)
|
||||||
|
180536114: 89 bb e0 06 54 00 mov %edi,0x5406e0(%rbx)
|
||||||
|
18053611a: 39 bb d0 06 54 00 cmp %edi,0x5406d0(%rbx)
|
||||||
|
180536120: 7e 12 jle 0x180536134
|
||||||
|
180536122: 48 8b 8b c8 06 54 00 mov 0x5406c8(%rbx),%rcx
|
||||||
|
180536129: 48 85 c9 test %rcx,%rcx
|
||||||
|
18053612c: 74 06 je 0x180536134
|
||||||
|
18053612e: e8 8d af ac ff call 0x1800010c0
|
||||||
|
180536133: 90 nop
|
||||||
|
180536134: 48 89 bb c8 06 54 00 mov %rdi,0x5406c8(%rbx)
|
||||||
|
18053613b: 89 bb d0 06 54 00 mov %edi,0x5406d0(%rbx)
|
||||||
|
180536141: 39 bb c0 06 54 00 cmp %edi,0x5406c0(%rbx)
|
||||||
|
180536147: 7e 12 jle 0x18053615b
|
||||||
|
180536149: 48 8b 8b b8 06 54 00 mov 0x5406b8(%rbx),%rcx
|
||||||
|
180536150: 48 85 c9 test %rcx,%rcx
|
||||||
|
180536153: 74 06 je 0x18053615b
|
||||||
|
180536155: e8 66 af ac ff call 0x1800010c0
|
||||||
|
18053615a: 90 nop
|
||||||
|
18053615b: 48 89 bb b8 06 54 00 mov %rdi,0x5406b8(%rbx)
|
||||||
|
180536162: 89 bb c0 06 54 00 mov %edi,0x5406c0(%rbx)
|
||||||
|
180536168: 39 bb b0 06 54 00 cmp %edi,0x5406b0(%rbx)
|
||||||
|
18053616e: 7e 12 jle 0x180536182
|
||||||
|
180536170: 48 8b 8b a8 06 54 00 mov 0x5406a8(%rbx),%rcx
|
||||||
|
180536177: 48 85 c9 test %rcx,%rcx
|
||||||
|
18053617a: 74 06 je 0x180536182
|
||||||
|
18053617c: e8 3f af ac ff call 0x1800010c0
|
||||||
|
180536181: 90 nop
|
||||||
|
180536182: 48 89 bb a8 06 54 00 mov %rdi,0x5406a8(%rbx)
|
||||||
|
180536189: 89 bb b0 06 54 00 mov %edi,0x5406b0(%rbx)
|
||||||
|
18053618f: 39 bb a0 06 54 00 cmp %edi,0x5406a0(%rbx)
|
||||||
|
180536195: 7e 12 jle 0x1805361a9
|
||||||
|
180536197: 48 8b 8b 98 06 54 00 mov 0x540698(%rbx),%rcx
|
||||||
|
18053619e: 48 85 c9 test %rcx,%rcx
|
||||||
|
1805361a1: 74 06 je 0x1805361a9
|
||||||
|
1805361a3: e8 18 af ac ff call 0x1800010c0
|
||||||
|
1805361a8: 90 nop
|
||||||
|
1805361a9: 48 89 bb 98 06 54 00 mov %rdi,0x540698(%rbx)
|
||||||
|
1805361b0: 89 bb a0 06 54 00 mov %edi,0x5406a0(%rbx)
|
||||||
|
1805361b6: 48 8d 8b 78 06 54 00 lea 0x540678(%rbx),%rcx
|
||||||
|
1805361bd: 4c 8d 0d 5c 80 ff ff lea -0x7fa4(%rip),%r9 # 0x18052e220
|
||||||
|
1805361c4: ba 10 00 00 00 mov $0x10,%edx
|
||||||
|
1805361c9: 44 8d 42 f2 lea -0xe(%rdx),%r8d
|
||||||
|
1805361cd: e8 4a b5 bf 00 call 0x18113171c
|
||||||
|
1805361d2: 90 nop
|
||||||
|
1805361d3: 39 bb 70 06 54 00 cmp %edi,0x540670(%rbx)
|
||||||
|
1805361d9: 7e 12 jle 0x1805361ed
|
||||||
|
1805361db: 48 8b 8b 68 06 54 00 mov 0x540668(%rbx),%rcx
|
||||||
|
1805361e2: 48 85 c9 test %rcx,%rcx
|
||||||
|
1805361e5: 74 06 je 0x1805361ed
|
||||||
|
1805361e7: e8 d4 ae ac ff call 0x1800010c0
|
||||||
|
1805361ec: 90 nop
|
||||||
|
1805361ed: 48 89 bb 68 06 54 00 mov %rdi,0x540668(%rbx)
|
||||||
|
1805361f4: 89 bb 70 06 54 00 mov %edi,0x540670(%rbx)
|
||||||
|
1805361fa: 48 8d 8b 30 05 54 00 lea 0x540530(%rbx),%rcx
|
||||||
|
180536201: e8 2a 7b ff ff call 0x18052dd30
|
||||||
|
180536206: 90 nop
|
||||||
|
180536207: 89 bb cc 04 24 00 mov %edi,0x2404cc(%rbx)
|
||||||
|
18053620d: 48 8b 8b c0 04 24 00 mov 0x2404c0(%rbx),%rcx
|
||||||
|
180536214: ff 15 8e 50 67 01 call *0x167508e(%rip) # 0x181bab2a8
|
||||||
|
18053621a: 90 nop
|
||||||
|
18053621b: 48 8d 8b d8 03 00 00 lea 0x3d8(%rbx),%rcx
|
||||||
|
180536222: e8 59 70 ff ff call 0x18052d280
|
||||||
|
180536227: 90 nop
|
||||||
|
180536228: 48 8d 05 49 5e f7 01 lea 0x1f75e49(%rip),%rax # 0x1824ac078
|
||||||
|
18053622f: 48 89 83 d0 03 00 00 mov %rax,0x3d0(%rbx)
|
||||||
|
180536236: 48 8b cb mov %rbx,%rcx
|
||||||
|
180536239: 48 8b 5c 24 40 mov 0x40(%rsp),%rbx
|
||||||
|
18053623e: 48 83 c4 30 add $0x30,%rsp
|
||||||
|
180536242: 5f pop %rdi
|
||||||
|
180536243: e9 58 38 ff ff jmp 0x180529aa0
|
||||||
|
180536248: cc int3
|
||||||
|
180536249: cc int3
|
||||||
|
18053624a: cc int3
|
||||||
|
18053624b: cc int3
|
||||||
|
18053624c: cc int3
|
||||||
|
18053624d: cc int3
|
||||||
|
18053624e: cc int3
|
||||||
|
18053624f: cc int3
|
||||||
|
180536250: f2 0f 10 0d e8 de f8 movsd 0x1f8dee8(%rip),%xmm1 # 0x1824c4140
|
||||||
|
180536257: 01
|
||||||
|
180536258: 48 8d 81 10 00 08 00 lea 0x80010(%rcx),%rax
|
||||||
|
18053625f: f2 0f 11 4c 24 08 movsd %xmm1,0x8(%rsp)
|
||||||
|
180536265: ba 00 80 00 00 mov $0x8000,%edx
|
||||||
|
18053626a: f2 0f 10 44 24 08 movsd 0x8(%rsp),%xmm0
|
||||||
|
180536270: 4c 8b c1 mov %rcx,%r8
|
||||||
|
180536273: c6 41 04 00 movb $0x0,0x4(%rcx)
|
||||||
|
180536277: 48 c7 81 10 00 10 00 movq $0x0,0x100010(%rcx)
|
||||||
|
18053627e: 00 00 00 00
|
||||||
|
180536282: 8b ca mov %edx,%ecx
|
||||||
|
180536284: 66 0f c6 c0 00 shufpd $0x0,%xmm0,%xmm0
|
||||||
|
180536289: a8 0f test $0xf,%al
|
||||||
|
18053628b: 75 13 jne 0x1805362a0
|
||||||
|
18053628d: 0f 1f 00 nopl (%rax)
|
||||||
|
180536290: 0f 11 00 movups %xmm0,(%rax)
|
||||||
|
180536293: 48 8d 40 10 lea 0x10(%rax),%rax
|
||||||
|
180536297: 48 83 e9 01 sub $0x1,%rcx
|
||||||
|
18053629b: 75 f3 jne 0x180536290
|
||||||
|
18053629d: eb 0e jmp 0x1805362ad
|
||||||
|
18053629f: 90 nop
|
||||||
|
1805362a0: 0f 11 00 movups %xmm0,(%rax)
|
||||||
|
1805362a3: 48 8d 40 10 lea 0x10(%rax),%rax
|
||||||
|
1805362a7: 48 83 e9 01 sub $0x1,%rcx
|
||||||
|
1805362ab: 75 f3 jne 0x1805362a0
|
||||||
|
1805362ad: f2 0f 11 4c 24 08 movsd %xmm1,0x8(%rsp)
|
||||||
|
1805362b3: 49 8d 40 10 lea 0x10(%r8),%rax
|
||||||
|
1805362b7: f2 0f 10 44 24 08 movsd 0x8(%rsp),%xmm0
|
||||||
|
1805362bd: 66 0f c6 c0 00 shufpd $0x0,%xmm0,%xmm0
|
||||||
|
1805362c2: a8 0f test $0xf,%al
|
||||||
|
1805362c4: 75 1b jne 0x1805362e1
|
||||||
|
1805362c6: 66 66 0f 1f 84 00 00 data16 nopw 0x0(%rax,%rax,1)
|
||||||
|
1805362cd: 00 00 00
|
||||||
|
1805362d0: 0f 11 00 movups %xmm0,(%rax)
|
||||||
|
1805362d3: 48 8d 40 10 lea 0x10(%rax),%rax
|
||||||
|
1805362d7: 48 83 ea 01 sub $0x1,%rdx
|
||||||
|
1805362db: 75 f3 jne 0x1805362d0
|
||||||
|
1805362dd: 49 8b c0 mov %r8,%rax
|
||||||
|
1805362e0: c3 ret
|
||||||
|
1805362e1: 0f 11 00 movups %xmm0,(%rax)
|
||||||
|
1805362e4: 48 8d 40 10 lea 0x10(%rax),%rax
|
||||||
|
1805362e8: 48 83 ea 01 sub $0x1,%rdx
|
||||||
|
1805362ec: 75 f3 jne 0x1805362e1
|
||||||
|
1805362ee: 49 8b c0 mov %r8,%rax
|
||||||
|
1805362f1: c3 ret
|
||||||
|
1805362f2: cc int3
|
||||||
|
1805362f3: cc int3
|
||||||
|
1805362f4: cc int3
|
||||||
|
1805362f5: cc int3
|
||||||
|
1805362f6: cc int3
|
||||||
|
1805362f7: cc int3
|
||||||
|
1805362f8: cc int3
|
||||||
|
1805362f9: cc int3
|
||||||
|
1805362fa: cc int3
|
||||||
|
1805362fb: cc int3
|
||||||
|
1805362fc: cc int3
|
||||||
|
1805362fd: cc int3
|
||||||
|
1805362fe: cc int3
|
||||||
|
1805362ff: cc int3
|
||||||
|
180536300: 48 89 5c 24 08 mov %rbx,0x8(%rsp)
|
||||||
|
180536305: 48 89 6c 24 18 mov %rbp,0x18(%rsp)
|
||||||
|
18053630a: 48 89 74 24 20 mov %rsi,0x20(%rsp)
|
||||||
|
18053630f: 48 89 54 24 10 mov %rdx,0x10(%rsp)
|
||||||
|
180536314: 57 push %rdi
|
||||||
|
180536315: 41 54 push %r12
|
||||||
|
180536317: 41 55 push %r13
|
||||||
|
180536319: 41 56 push %r14
|
||||||
|
18053631b: 41 57 push %r15
|
||||||
|
18053631d: 48 83 ec 50 sub $0x50,%rsp
|
||||||
|
180536321: 48 63 bc 24 a0 00 00 movslq 0xa0(%rsp),%rdi
|
||||||
|
180536328: 00
|
||||||
|
180536329: 48 8d 15 88 aa 11 02 lea 0x211aa88(%rip),%rdx # 0x182650db8
|
||||||
|
180536330: 4c 8b b4 24 b0 00 00 mov 0xb0(%rsp),%r14
|
||||||
|
180536337: 00
|
||||||
|
180536338: 49 8b e9 mov %r9,%rbp
|
||||||
|
18053633b: 0f 29 74 24 40 movaps %xmm6,0x40(%rsp)
|
||||||
|
180536340: 49 8b d8 mov %r8,%rbx
|
||||||
|
180536343: 66 0f 6e b1 80 00 24 movd 0x240080(%rcx),%xmm6
|
||||||
|
18053634a: 00
|
||||||
|
18053634b: 8d 04 fd 00 00 00 00 lea 0x0(,%rdi,8),%eax
|
||||||
|
180536352: 4c 63 d0 movslq %eax,%r10
|
||||||
|
180536355: 8d 04 3f lea (%rdi,%rdi,1),%eax
|
||||||
|
180536358: 0f 5b f6 cvtdq2ps %xmm6,%xmm6
|
||||||
|
18053635b: 4f 8d 24 96 lea (%r14,%r10,4),%r12
|
||||||
|
18053635f: 4c 63 d0 movslq %eax,%r10
|
||||||
|
180536362: 4d 8d 3c bc lea (%r12,%rdi,4),%r15
|
||||||
|
180536366: 4c 89 a4 24 b0 00 00 mov %r12,0xb0(%rsp)
|
||||||
|
18053636d: 00
|
||||||
|
18053636e: f3 0f 59 71 24 mulss 0x24(%rcx),%xmm6
|
||||||
|
180536373: 48 8d 0d 3e aa 11 02 lea 0x211aa3e(%rip),%rcx # 0x182650db8
|
||||||
|
18053637a: 4b 8d 34 97 lea (%r15,%r10,4),%rsi
|
||||||
|
18053637e: 4e 8d 2c 96 lea (%rsi,%r10,4),%r13
|
||||||
|
180536382: ff 15 80 4c 67 01 call *0x1674c80(%rip) # 0x181bab008
|
||||||
|
180536388: f2 0f 10 0d b8 de f8 movsd 0x1f8deb8(%rip),%xmm1 # 0x1824c4248
|
||||||
|
18053638f: 01
|
||||||
|
180536390: 44 8b cf mov %edi,%r9d
|
||||||
|
180536393: 0f 5a c6 cvtps2pd %xmm6,%xmm0
|
||||||
|
180536396: 85 c0 test %eax,%eax
|
||||||
|
180536398: 48 8b d3 mov %rbx,%rdx
|
||||||
|
18053639b: 49 8b cc mov %r12,%rcx
|
||||||
|
18053639e: 0f 94 84 24 a8 00 00 sete 0xa8(%rsp)
|
||||||
|
1805363a5: 00
|
||||||
|
1805363a6: f2 0f 5e c8 divsd %xmm0,%xmm1
|
||||||
|
1805363aa: 66 0f 5a d1 cvtpd2ps %xmm1,%xmm2
|
||||||
|
1805363ae: e8 4d 76 ff ff call 0x18052da00
|
||||||
|
1805363b3: 80 bd 10 08 00 00 00 cmpb $0x0,0x810(%rbp)
|
||||||
|
1805363ba: 44 8b c7 mov %edi,%r8d
|
||||||
|
1805363bd: 74 1a je 0x1805363d9
|
||||||
|
1805363bf: f3 0f 10 0d dd da f8 movss 0x1f8dadd(%rip),%xmm1 # 0x1824c3ea4
|
||||||
|
1805363c6: 01
|
||||||
|
1805363c7: 48 8b 8c 24 88 00 00 mov 0x88(%rsp),%rcx
|
||||||
|
1805363ce: 00
|
||||||
|
1805363cf: e8 7c 77 ff ff call 0x18052db50
|
||||||
|
1805363d4: e9 88 01 00 00 jmp 0x180536561
|
||||||
|
1805363d9: f3 0f 10 35 c3 da f8 movss 0x1f8dac3(%rip),%xmm6 # 0x1824c3ea4
|
||||||
|
1805363e0: 01
|
||||||
|
1805363e1: 48 8b ce mov %rsi,%rcx
|
||||||
|
1805363e4: 0f 28 ce movaps %xmm6,%xmm1
|
||||||
|
1805363e7: e8 64 77 ff ff call 0x18052db50
|
||||||
|
1805363ec: 44 8b c7 mov %edi,%r8d
|
||||||
|
1805363ef: 0f 57 c9 xorps %xmm1,%xmm1
|
||||||
|
1805363f2: 49 8b cd mov %r13,%rcx
|
||||||
|
1805363f5: e8 56 77 ff ff call 0x18052db50
|
||||||
|
1805363fa: 33 db xor %ebx,%ebx
|
||||||
|
1805363fc: 39 .byte 0x39
|
||||||
|
1805363fd: 9d popf
|
||||||
|
1805363fe: 14 08 adc $0x8,%al
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
|||||||
|
; hot_52e1c0
|
||||||
|
|
||||||
|
/tmp/slice.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
000000018052e1c0 <.data>:
|
||||||
|
18052e1c0: 05 e8 fa 2e ad add $0xad2efae8,%eax
|
||||||
|
18052e1c5: ff 85 f6 48 89 1f incl 0x1f8948f6(%rbp)
|
||||||
|
18052e1cb: 0f 48 f3 cmovs %ebx,%esi
|
||||||
|
18052e1ce: 89 77 08 mov %esi,0x8(%rdi)
|
||||||
|
18052e1d1: 85 f6 test %esi,%esi
|
||||||
|
18052e1d3: 74 2f je 0x18052e204
|
||||||
|
18052e1d5: 8d 0c b5 00 00 00 00 lea 0x0(,%rsi,4),%ecx
|
||||||
|
18052e1dc: e8 9f 2e ad ff call 0x180001080
|
||||||
|
18052e1e1: 48 89 07 mov %rax,(%rdi)
|
||||||
|
18052e1e4: 39 5f 08 cmp %ebx,0x8(%rdi)
|
||||||
|
18052e1e7: 7e 1b jle 0x18052e204
|
||||||
|
18052e1e9: 48 8b cb mov %rbx,%rcx
|
||||||
|
18052e1ec: 0f 1f 40 00 nopl 0x0(%rax)
|
||||||
|
18052e1f0: 48 8b 07 mov (%rdi),%rax
|
||||||
|
18052e1f3: 48 8d 49 04 lea 0x4(%rcx),%rcx
|
||||||
|
18052e1f7: ff c3 inc %ebx
|
||||||
|
18052e1f9: f3 0f 11 74 01 fc movss %xmm6,-0x4(%rcx,%rax,1)
|
||||||
|
18052e1ff: 3b 5f 08 cmp 0x8(%rdi),%ebx
|
||||||
|
18052e202: 7c ec jl 0x18052e1f0
|
||||||
|
18052e204: 48 8b 5c 24 40 mov 0x40(%rsp),%rbx
|
||||||
|
18052e209: 48 8b 74 24 48 mov 0x48(%rsp),%rsi
|
||||||
|
18052e20e: 0f 28 74 24 20 movaps 0x20(%rsp),%xmm6
|
||||||
|
18052e213: 48 83 c4 30 add $0x30,%rsp
|
||||||
|
18052e217: 5f pop %rdi
|
||||||
|
18052e218: c3 ret
|
||||||
|
18052e219: cc int3
|
||||||
|
18052e21a: cc int3
|
||||||
|
18052e21b: cc int3
|
||||||
|
18052e21c: cc int3
|
||||||
|
18052e21d: cc int3
|
||||||
|
18052e21e: cc int3
|
||||||
|
18052e21f: cc int3
|
||||||
|
18052e220: 40 53 rex push %rbx
|
||||||
|
18052e222: 48 83 ec 20 sub $0x20,%rsp
|
||||||
|
18052e226: 83 79 08 00 cmpl $0x0,0x8(%rcx)
|
||||||
|
18052e22a: 48 8b d9 mov %rcx,%rbx
|
||||||
|
18052e22d: 7e 1b jle 0x18052e24a
|
||||||
|
18052e22f: 48 8b 09 mov (%rcx),%rcx
|
||||||
|
18052e232: 48 85 c9 test %rcx,%rcx
|
||||||
|
18052e235: 74 05 je 0x18052e23c
|
||||||
|
18052e237: e8 84 2e ad ff call 0x1800010c0
|
||||||
|
18052e23c: 33 c0 xor %eax,%eax
|
||||||
|
18052e23e: 48 89 03 mov %rax,(%rbx)
|
||||||
|
18052e241: 89 43 08 mov %eax,0x8(%rbx)
|
||||||
|
18052e244: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18052e248: 5b pop %rbx
|
||||||
|
18052e249: c3 ret
|
||||||
|
18052e24a: 33 c0 xor %eax,%eax
|
||||||
|
18052e24c: 48 89 01 mov %rax,(%rcx)
|
||||||
|
18052e24f: 89 41 08 mov %eax,0x8(%rcx)
|
||||||
|
18052e252: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18052e256: 5b pop %rbx
|
||||||
|
18052e257: c3 ret
|
||||||
|
18052e258: cc int3
|
||||||
|
18052e259: cc int3
|
||||||
|
18052e25a: cc int3
|
||||||
|
18052e25b: cc int3
|
||||||
|
18052e25c: cc int3
|
||||||
|
18052e25d: cc int3
|
||||||
|
18052e25e: cc int3
|
||||||
|
18052e25f: cc int3
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
|
||||||
|
/tmp/slice.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
0000000180534580 <.data>:
|
||||||
|
180534580: 48 89 1f mov %rbx,(%rdi)
|
||||||
|
180534583: 0f 48 f3 cmovs %ebx,%esi
|
||||||
|
180534586: 89 77 08 mov %esi,0x8(%rdi)
|
||||||
|
180534589: 85 f6 test %esi,%esi
|
||||||
|
18053458b: 74 26 je 0x1805345b3
|
||||||
|
18053458d: 8b ce mov %esi,%ecx
|
||||||
|
18053458f: e8 ec ca ac ff call 0x180001080
|
||||||
|
180534594: 48 89 07 mov %rax,(%rdi)
|
||||||
|
180534597: 39 5f 08 cmp %ebx,0x8(%rdi)
|
||||||
|
18053459a: 7e 17 jle 0x1805345b3
|
||||||
|
18053459c: 48 8b cb mov %rbx,%rcx
|
||||||
|
18053459f: 90 nop
|
||||||
|
1805345a0: 48 8b 07 mov (%rdi),%rax
|
||||||
|
1805345a3: 48 8d 49 01 lea 0x1(%rcx),%rcx
|
||||||
|
1805345a7: ff c3 inc %ebx
|
||||||
|
1805345a9: c6 44 01 ff 00 movb $0x0,-0x1(%rcx,%rax,1)
|
||||||
|
1805345ae: 3b 5f 08 cmp 0x8(%rdi),%ebx
|
||||||
|
1805345b1: 7c ed jl 0x1805345a0
|
||||||
|
1805345b3: 48 8b 5c 24 30 mov 0x30(%rsp),%rbx
|
||||||
|
1805345b8: 48 8b 74 24 38 mov 0x38(%rsp),%rsi
|
||||||
|
1805345bd: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
1805345c1: 5f pop %rdi
|
||||||
|
1805345c2: c3 ret
|
||||||
|
1805345c3: cc int3
|
||||||
|
1805345c4: cc int3
|
||||||
|
1805345c5: cc int3
|
||||||
|
1805345c6: cc int3
|
||||||
|
1805345c7: cc int3
|
||||||
|
1805345c8: cc int3
|
||||||
|
1805345c9: cc int3
|
||||||
|
1805345ca: cc int3
|
||||||
|
1805345cb: cc int3
|
||||||
|
1805345cc: cc int3
|
||||||
|
1805345cd: cc int3
|
||||||
|
1805345ce: cc int3
|
||||||
|
1805345cf: cc int3
|
||||||
|
1805345d0: 40 53 rex push %rbx
|
||||||
|
1805345d2: 48 83 ec 40 sub $0x40,%rsp
|
||||||
|
1805345d6: 48 c7 44 24 30 fe ff movq $0xfffffffffffffffe,0x30(%rsp)
|
||||||
|
1805345dd: ff ff
|
||||||
|
1805345df: 48 8b d9 mov %rcx,%rbx
|
||||||
|
1805345e2: 48 8d 05 c7 ce f3 ff lea -0xc3139(%rip),%rax # 0x1804714b0
|
||||||
|
1805345e9: 48 89 44 24 20 mov %rax,0x20(%rsp)
|
||||||
|
1805345ee: 4c 8d 0d 8b 00 00 00 lea 0x8b(%rip),%r9 # 0x180534680
|
||||||
|
1805345f5: ba 80 00 00 00 mov $0x80,%edx
|
||||||
|
1805345fa: 44 8d 42 90 lea -0x70(%rdx),%r8d
|
||||||
|
1805345fe: e8 01 d6 bf 00 call 0x181131c04
|
||||||
|
180534603: 90 nop
|
||||||
|
180534604: c7 83 00 08 00 00 08 movl $0x8,0x800(%rbx)
|
||||||
|
18053460b: 00 00 00
|
||||||
|
18053460e: 48 c7 83 04 08 00 00 movq $0x447a0000,0x804(%rbx)
|
||||||
|
180534615: 00 00 7a 44
|
||||||
|
180534619: c7 83 0c 08 00 00 f4 movl $0x3f34fdf4,0x80c(%rbx)
|
||||||
|
180534620: fd 34 3f
|
||||||
|
180534623: c6 83 10 08 00 00 00 movb $0x0,0x810(%rbx)
|
||||||
|
18053462a: c7 83 14 08 00 00 01 movl $0x1,0x814(%rbx)
|
||||||
|
180534631: 00 00 00
|
||||||
|
180534634: 33 c0 xor %eax,%eax
|
||||||
|
180534636: 48 89 83 18 08 00 00 mov %rax,0x818(%rbx)
|
||||||
|
18053463d: 48 89 83 20 08 00 00 mov %rax,0x820(%rbx)
|
||||||
|
180534644: 48 8b c3 mov %rbx,%rax
|
||||||
|
180534647: 48 83 c4 40 add $0x40,%rsp
|
||||||
|
18053464b: 5b pop %rbx
|
||||||
|
18053464c: c3 ret
|
||||||
|
18053464d: cc int3
|
||||||
|
18053464e: cc int3
|
||||||
|
18053464f: cc int3
|
||||||
|
180534650: 48 83 ec 38 sub $0x38,%rsp
|
||||||
|
180534654: 48 c7 44 24 20 fe ff movq $0xfffffffffffffffe,0x20(%rsp)
|
||||||
|
18053465b: ff ff
|
||||||
|
18053465d: 4c 8d 0d 4c ce f3 ff lea -0xc31b4(%rip),%r9 # 0x1804714b0
|
||||||
|
180534664: ba 80 00 00 00 mov $0x80,%edx
|
||||||
|
180534669: 44 8d 42 90 lea -0x70(%rdx),%r8d
|
||||||
|
18053466d: e8 aa d0 bf 00 call 0x18113171c
|
||||||
|
180534672: 90 nop
|
||||||
|
180534673: 48 83 c4 38 add $0x38,%rsp
|
||||||
|
180534677: c3 ret
|
||||||
|
180534678: cc int3
|
||||||
|
180534679: cc int3
|
||||||
|
18053467a: cc int3
|
||||||
|
18053467b: cc int3
|
||||||
|
18053467c: cc int3
|
||||||
|
18053467d: cc int3
|
||||||
|
18053467e: cc int3
|
||||||
|
18053467f: cc int3
|
||||||
|
180534680: 48 83 ec 28 sub $0x28,%rsp
|
||||||
|
180534684: e8 a7 00 00 00 call 0x180534730
|
||||||
|
180534689: 4c 8b 19 mov (%rcx),%r11
|
||||||
|
18053468c: 48 ba 00 00 00 00 00 movabs $0x3ff0000000000000,%rdx
|
||||||
|
180534693: 00 f0 3f
|
||||||
|
180534696: 49 89 13 mov %rdx,(%r11)
|
||||||
|
180534699: 48 8b 41 08 mov 0x8(%rcx),%rax
|
||||||
|
18053469d: 48 89 10 mov %rdx,(%rax)
|
||||||
|
1805346a0: 48 8b c1 mov %rcx,%rax
|
||||||
|
1805346a3: 48 83 c4 28 add $0x28,%rsp
|
||||||
|
1805346a7: c3 ret
|
||||||
|
1805346a8: cc int3
|
||||||
|
1805346a9: cc int3
|
||||||
|
1805346aa: cc int3
|
||||||
|
1805346ab: cc int3
|
||||||
|
1805346ac: cc int3
|
||||||
|
1805346ad: cc int3
|
||||||
|
1805346ae: cc int3
|
||||||
|
1805346af: cc int3
|
||||||
|
1805346b0: 48 89 5c 24 08 mov %rbx,0x8(%rsp)
|
||||||
|
1805346b5: 48 89 74 24 10 mov %rsi,0x10(%rsp)
|
||||||
|
1805346ba: 57 push %rdi
|
||||||
|
1805346bb: 48 83 ec 20 sub $0x20,%rsp
|
||||||
|
1805346bf: 48 8b d9 mov %rcx,%rbx
|
||||||
|
1805346c2: 8b f2 mov %edx,%esi
|
||||||
|
1805346c4: 48 83 c1 60 add $0x60,%rcx
|
||||||
|
1805346c8: e8 a3 11 d7 00 call 0x1812a5870
|
||||||
|
1805346cd: 48 8d 05 64 7f f7 01 lea 0x1f77f64(%rip),%rax # 0x1824ac638
|
||||||
|
1805346d4: 48 89 03 mov %rax,(%rbx)
|
||||||
|
1805346d7: 48 8b 4b 48 mov 0x48(%rbx),%rcx
|
||||||
|
1805346db: c7 43 54 00 00 00 00 movl $0x0,0x54(%rbx)
|
||||||
|
1805346e2: ff 15 c0 6b 67 01 call *0x1676bc0(%rip) # 0x181bab2a8
|
||||||
|
1805346e8: 48 8b 7b 08 mov 0x8(%rbx),%rdi
|
||||||
|
1805346ec: 48 85 ff test %rdi,%rdi
|
||||||
|
1805346ef: 74 17 je 0x180534708
|
||||||
|
1805346f1: 48 8b 4f 18 mov 0x18(%rdi),%rcx
|
||||||
|
1805346f5: ff 15 ad 6b 67 01 call *0x1676bad(%rip) # 0x181bab2a8
|
||||||
|
1805346fb: ba 28 01 00 00 mov $0x128,%edx
|
||||||
|
180534700: 48 8b cf mov %rdi,%rcx
|
||||||
|
180534703: e8 ec c9 bf 00 call 0x1811310f4
|
||||||
|
180534708: 40 f6 c6 01 test $0x1,%sil
|
||||||
|
18053470c: 74 0d je 0x18053471b
|
||||||
|
18053470e: ba e0 00 00 00 mov $0xe0,%edx
|
||||||
|
180534713: 48 8b cb mov %rbx,%rcx
|
||||||
|
180534716: e8 d9 c9 bf 00 call 0x1811310f4
|
||||||
|
18053471b: 48 8b 74 24 38 mov 0x38(%rsp),%rsi
|
||||||
|
180534720: 48 8b c3 mov %rbx,%rax
|
||||||
|
180534723: 48 8b 5c 24 30 mov 0x30(%rsp),%rbx
|
||||||
|
180534728: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18053472c: 5f pop %rdi
|
||||||
|
18053472d: c3 ret
|
||||||
|
18053472e: cc int3
|
||||||
|
18053472f: cc int3
|
||||||
|
180534730: 4c 8d 41 18 lea 0x18(%rcx),%r8
|
||||||
|
180534734: c7 41 10 03 00 00 00 movl $0x3,0x10(%rcx)
|
||||||
|
18053473b: 49 8b c0 mov %r8,%rax
|
||||||
|
18053473e: 4c 89 01 mov %r8,(%rcx)
|
||||||
|
180534741: 0f 57 c9 xorps %xmm1,%xmm1
|
||||||
|
180534744: c6 41 78 01 movb $0x1,0x78(%rcx)
|
||||||
|
180534748: 48 83 c0 10 add $0x10,%rax
|
||||||
|
18053474c: f2 0f 11 4c 24 08 movsd %xmm1,0x8(%rsp)
|
||||||
|
180534752: f2 0f 10 44 24 08 movsd 0x8(%rsp),%xmm0
|
||||||
|
180534758: 48 8d 51 30 lea 0x30(%rcx),%rdx
|
||||||
|
18053475c: 48 89 51 08 mov %rdx,0x8(%rcx)
|
||||||
|
180534760: 66 0f c6 c0 00 shufpd $0x0,%xmm0,%xmm0
|
||||||
|
180534765: 41 f6 c0 0f test $0xf,%r8b
|
||||||
|
180534769: 75 06 jne 0x180534771
|
||||||
|
18053476b: 41 0f 11 00 movups %xmm0,(%r8)
|
||||||
|
18053476f: eb 0f jmp 0x180534780
|
||||||
|
180534771: 41 0f 11 00 movups %xmm0,(%r8)
|
||||||
|
180534775: 66 66 66 0f 1f 84 00 data16 data16 nopw 0x0(%rax,%rax,1)
|
||||||
|
18053477c: 00 00 00 00
|
||||||
|
180534780: 45 33 c9 xor %r9d,%r9d
|
||||||
|
180534783: f2 0f 11 4c 24 08 movsd %xmm1,0x8(%rsp)
|
||||||
|
180534789: f2 0f 10 44 24 08 movsd 0x8(%rsp),%xmm0
|
||||||
|
18053478f: 4c 89 08 mov %r9,(%rax)
|
||||||
|
180534792: 48 8b c2 mov %rdx,%rax
|
||||||
|
180534795: 48 83 c0 10 add $0x10,%rax
|
||||||
|
180534799: 66 0f c6 c0 00 shufpd $0x0,%xmm0,%xmm0
|
||||||
|
18053479e: f6 c2 0f test $0xf,%dl
|
||||||
|
1805347a1: 75 05 jne 0x1805347a8
|
||||||
|
1805347a3: 0f 11 02 movups %xmm0,(%rdx)
|
||||||
|
1805347a6: eb 08 jmp 0x1805347b0
|
||||||
|
1805347a8: 0f 11 02 movups %xmm0,(%rdx)
|
||||||
|
1805347ab: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
1805347b0: 4c 89 08 mov %r9,(%rax)
|
||||||
|
1805347b3: 49 ba 00 00 00 00 00 movabs $0x3ff0000000000000,%r10
|
||||||
|
1805347ba: 00 f0 3f
|
||||||
|
1805347bd: 4d 89 10 mov %r10,(%r8)
|
||||||
|
1805347c0: 4c 8d 41 48 lea 0x48(%rcx),%r8
|
||||||
|
1805347c4: 49 8b c0 mov %r8,%rax
|
||||||
|
1805347c7: f2 0f 11 4c 24 08 movsd %xmm1,0x8(%rsp)
|
||||||
|
1805347cd: f2 0f 10 44 24 08 movsd 0x8(%rsp),%xmm0
|
||||||
|
1805347d3: 48 83 c0 10 add $0x10,%rax
|
||||||
|
1805347d7: 4c 89 12 mov %r10,(%rdx)
|
||||||
|
1805347da: 66 0f c6 c0 00 shufpd $0x0,%xmm0,%xmm0
|
||||||
|
1805347df: 41 f6 c0 0f test $0xf,%r8b
|
||||||
|
1805347e3: 75 06 jne 0x1805347eb
|
||||||
|
1805347e5: 41 0f 11 00 movups %xmm0,(%r8)
|
||||||
|
1805347e9: eb 05 jmp 0x1805347f0
|
||||||
|
1805347eb: 41 0f 11 00 movups %xmm0,(%r8)
|
||||||
|
1805347ef: 90 nop
|
||||||
|
1805347f0: 48 8d 51 60 lea 0x60(%rcx),%rdx
|
||||||
|
1805347f4: 4c 89 08 mov %r9,(%rax)
|
||||||
|
1805347f7: 48 8b c2 mov %rdx,%rax
|
||||||
|
1805347fa: f2 0f 11 4c 24 08 movsd %xmm1,0x8(%rsp)
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
|
||||||
|
/tmp/slice.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
0000000180b0c3a0 <.data>:
|
||||||
|
180b0c3a0: 00 80 7d 07 ff c9 add %al,-0x3600f883(%rax)
|
||||||
|
180b0c3a6: 83 c9 fe or $0xfffffffe,%ecx
|
||||||
|
180b0c3a9: ff c1 inc %ecx
|
||||||
|
180b0c3ab: 85 c9 test %ecx,%ecx
|
||||||
|
180b0c3ad: 75 b2 jne 0x180b0c361
|
||||||
|
180b0c3af: ff 54 24 60 call *0x60(%rsp)
|
||||||
|
180b0c3b3: 48 83 f8 01 cmp $0x1,%rax
|
||||||
|
180b0c3b7: be 00 00 00 00 mov $0x0,%esi
|
||||||
|
180b0c3bc: 76 2e jbe 0x180b0c3ec
|
||||||
|
180b0c3be: 48 8d 84 24 c8 00 00 lea 0xc8(%rsp),%rax
|
||||||
|
180b0c3c5: 00
|
||||||
|
180b0c3c6: 48 b9 cc cc cc cc cc movabs $0xcccccccccccccccc,%rcx
|
||||||
|
180b0c3cd: cc cc cc
|
||||||
|
180b0c3d0: 48 39 08 cmp %rcx,(%rax)
|
||||||
|
180b0c3d3: 74 17 je 0x180b0c3ec
|
||||||
|
180b0c3d5: 66 66 66 0f 1f 84 00 data16 data16 nopw 0x0(%rax,%rax,1)
|
||||||
|
180b0c3dc: 00 00 00 00
|
||||||
|
180b0c3e0: 48 89 30 mov %rsi,(%rax)
|
||||||
|
180b0c3e3: 48 8d 40 40 lea 0x40(%rax),%rax
|
||||||
|
180b0c3e7: 48 39 08 cmp %rcx,(%rax)
|
||||||
|
180b0c3ea: 75 f4 jne 0x180b0c3e0
|
||||||
|
180b0c3ec: 48 8d b4 24 c8 00 00 lea 0xc8(%rsp),%rsi
|
||||||
|
180b0c3f3: 00
|
||||||
|
180b0c3f4: eb 03 jmp 0x180b0c3f9
|
||||||
|
180b0c3f6: 4c 8b ca mov %rdx,%r9
|
||||||
|
180b0c3f9: 41 53 push %r11
|
||||||
|
180b0c3fb: 41 56 push %r14
|
||||||
|
180b0c3fd: 4c 8d 1d 78 fa ff ff lea -0x588(%rip),%r11 # 0x180b0be7c
|
||||||
|
180b0c404: 4d 8b f3 mov %r11,%r14
|
||||||
|
180b0c407: 49 81 c6 ae 06 00 00 add $0x6ae,%r14
|
||||||
|
180b0c40e: 48 8d 64 24 f0 lea -0x10(%rsp),%rsp
|
||||||
|
180b0c413: 4d 03 cf add %r15,%r9
|
||||||
|
180b0c416: 4d 85 e0 test %r12,%r8
|
||||||
|
180b0c419: 49 3b d0 cmp %r8,%rdx
|
||||||
|
180b0c41c: 4d 2b cf sub %r15,%r9
|
||||||
|
180b0c41f: 4c 89 74 24 08 mov %r14,0x8(%rsp)
|
||||||
|
180b0c424: 48 f7 d2 not %rdx
|
||||||
|
180b0c427: 48 8d 52 01 lea 0x1(%rdx),%rdx
|
||||||
|
180b0c42b: 4c 3b e8 cmp %rax,%r13
|
||||||
|
180b0c42e: 4d 85 cf test %r9,%r15
|
||||||
|
180b0c431: 48 f7 d2 not %rdx
|
||||||
|
180b0c434: 48 8d 52 01 lea 0x1(%rdx),%rdx
|
||||||
|
180b0c438: 48 83 ec f8 sub $0xfffffffffffffff8,%rsp
|
||||||
|
180b0c43c: 49 c7 c6 83 1f 00 00 mov $0x1f83,%r14
|
||||||
|
180b0c443: 4d 03 f3 add %r11,%r14
|
||||||
|
180b0c446: 41 56 push %r14
|
||||||
|
180b0c448: 49 c7 c6 3c 06 00 00 mov $0x63c,%r14
|
||||||
|
180b0c44f: 4d 03 f3 add %r11,%r14
|
||||||
|
180b0c452: 41 56 push %r14
|
||||||
|
180b0c454: 49 c7 c6 49 cc 01 00 mov $0x1cc49,%r14
|
||||||
|
180b0c45b: 4d 03 f3 add %r11,%r14
|
||||||
|
180b0c45e: 48 83 c4 e8 add $0xffffffffffffffe8,%rsp
|
||||||
|
180b0c462: 49 81 c4 8e ee 39 13 add $0x1339ee8e,%r12
|
||||||
|
180b0c469: 48 a9 1d 6a 9f 2f test $0x2f9f6a1d,%rax
|
||||||
|
180b0c46f: 49 f7 c2 89 fe 1a 73 test $0x731afe89,%r10
|
||||||
|
180b0c476: 49 81 ec 8e ee 39 13 sub $0x1339ee8e,%r12
|
||||||
|
180b0c47d: 4c 89 74 24 10 mov %r14,0x10(%rsp)
|
||||||
|
180b0c482: 49 81 e9 b1 fe 6e 2c sub $0x2c6efeb1,%r9
|
||||||
|
180b0c489: 49 3b c1 cmp %r9,%rax
|
||||||
|
180b0c48c: 49 f7 c4 a6 46 72 79 test $0x797246a6,%r12
|
||||||
|
180b0c493: 49 81 c1 b1 fe 6e 2c add $0x2c6efeb1,%r9
|
||||||
|
180b0c49a: 48 83 ec f0 sub $0xfffffffffffffff0,%rsp
|
||||||
|
180b0c49e: 4d 85 ea test %r13,%r10
|
||||||
|
180b0c4a1: 49 c7 c6 3c 06 00 00 mov $0x63c,%r14
|
||||||
|
180b0c4a8: 4d 03 f3 add %r11,%r14
|
||||||
|
180b0c4ab: 48 8d 64 24 f8 lea -0x8(%rsp),%rsp
|
||||||
|
180b0c4b0: 4d 2b cd sub %r13,%r9
|
||||||
|
180b0c4b3: eb 04 jmp 0x180b0c4b9
|
||||||
|
180b0c4b5: 32 04 24 xor (%rsp),%al
|
||||||
|
180b0c4b8: c3 ret
|
||||||
|
180b0c4b9: 4d 03 cd add %r13,%r9
|
||||||
|
180b0c4bc: 4c 89 34 24 mov %r14,(%rsp)
|
||||||
|
180b0c4c0: 4d 8b f3 mov %r11,%r14
|
||||||
|
180b0c4c3: 49 81 c6 83 1f 00 00 add $0x1f83,%r14
|
||||||
|
180b0c4ca: 48 83 c4 e8 add $0xffffffffffffffe8,%rsp
|
||||||
|
180b0c4ce: 4c 89 74 24 10 mov %r14,0x10(%rsp)
|
||||||
|
180b0c4d3: 4d 2b d0 sub %r8,%r10
|
||||||
|
180b0c4d6: eb 08 jmp 0x180b0c4e0
|
||||||
|
180b0c4d8: 2b 04 24 sub (%rsp),%eax
|
||||||
|
180b0c4db: 03 44 24 f8 add -0x8(%rsp),%eax
|
||||||
|
180b0c4df: c3 ret
|
||||||
|
180b0c4e0: 4d 03 d0 add %r8,%r10
|
||||||
|
180b0c4e3: 48 83 c4 10 add $0x10,%rsp
|
||||||
|
180b0c4e7: 4d 8b f3 mov %r11,%r14
|
||||||
|
180b0c4ea: 49 81 c6 33 d0 ff ff add $0xffffffffffffd033,%r14
|
||||||
|
180b0c4f1: 41 56 push %r14
|
||||||
|
180b0c4f3: 49 c7 c6 5f 1f 00 00 mov $0x1f5f,%r14
|
||||||
|
180b0c4fa: 4d 03 f3 add %r11,%r14
|
||||||
|
180b0c4fd: 41 56 push %r14
|
||||||
|
180b0c4ff: 4d 8b f3 mov %r11,%r14
|
||||||
|
180b0c502: 49 81 c6 3c 06 00 00 add $0x63c,%r14
|
||||||
|
180b0c509: 41 ff e6 jmp *%r14
|
||||||
|
180b0c50c: 7c 16 jl 0x180b0c524
|
||||||
|
180b0c50e: 4c 2b fa sub %rdx,%r15
|
||||||
|
180b0c511: 48 81 fa bd da 55 14 cmp $0x1455dabd,%rdx
|
||||||
|
180b0c518: 48 f7 c2 bf a9 c1 1f test $0x1fc1a9bf,%rdx
|
||||||
|
180b0c51f: 4c 03 fa add %rdx,%r15
|
||||||
|
180b0c522: eb 06 jmp 0x180b0c52a
|
||||||
|
180b0c524: eb 04 jmp 0x180b0c52a
|
||||||
|
180b0c526: 02 24 24 add (%rsp),%ah
|
||||||
|
180b0c529: c3 ret
|
||||||
|
180b0c52a: 48 81 ea da 69 00 00 sub $0x69da,%rdx
|
||||||
|
180b0c531: 41 5e pop %r14
|
||||||
|
180b0c533: 41 5b pop %r11
|
||||||
|
180b0c535: 8b 06 mov (%rsi),%eax
|
||||||
|
180b0c537: 8d 48 01 lea 0x1(%rax),%ecx
|
||||||
|
180b0c53a: 0f af c8 imul %eax,%ecx
|
||||||
|
180b0c53d: 81 e1 01 00 00 80 and $0x80000001,%ecx
|
||||||
|
180b0c543: 7d 07 jge 0x180b0c54c
|
||||||
|
180b0c545: ff c9 dec %ecx
|
||||||
|
180b0c547: 83 c9 fe or $0xfffffffe,%ecx
|
||||||
|
180b0c54a: ff c1 inc %ecx
|
||||||
|
180b0c54c: 85 c9 test %ecx,%ecx
|
||||||
|
180b0c54e: 0f 85 a0 fe ff ff jne 0x180b0c3f4
|
||||||
|
180b0c554: 4d 3b f7 cmp %r15,%r14
|
||||||
|
180b0c557: 0f 84 a3 01 00 00 je 0x180b0c700
|
||||||
|
180b0c55d: 4b 8d 34 34 lea (%r12,%r14,1),%rsi
|
||||||
|
180b0c561: 4d 03 fc add %r12,%r15
|
||||||
|
180b0c564: 49 f7 df neg %r15
|
||||||
|
180b0c567: 48 8d 9c 24 c8 00 00 lea 0xc8(%rsp),%rbx
|
||||||
|
180b0c56e: 00
|
||||||
|
180b0c56f: 4c 8d 25 4a 5a b1 01 lea 0x1b15a4a(%rip),%r12 # 0x182621fc0
|
||||||
|
180b0c576: 66 66 0f 1f 84 00 00 data16 nopw 0x0(%rax,%rax,1)
|
||||||
|
180b0c57d: 00 00 00
|
||||||
|
180b0c580: 4c 8b f6 mov %rsi,%r14
|
||||||
|
180b0c583: 48 85 ff test %rdi,%rdi
|
||||||
|
180b0c586: 0f 84 a4 00 00 00 je 0x180b0c630
|
||||||
|
180b0c58c: 48 8b 17 mov (%rdi),%rdx
|
||||||
|
180b0c58f: 48 85 d2 test %rdx,%rdx
|
||||||
|
180b0c592: 0f 88 98 00 00 00 js 0x180b0c630
|
||||||
|
180b0c598: 48 b8 c0 1f 62 02 00 movabs $0x2621fc0,%rax
|
||||||
|
180b0c59f: 00 00 00
|
||||||
|
180b0c5a2: 48 89 44 24 48 mov %rax,0x48(%rsp)
|
||||||
|
180b0c5a7: 48 8b 44 24 48 mov 0x48(%rsp),%rax
|
||||||
|
180b0c5ac: 4d 8b c4 mov %r12,%r8
|
||||||
|
180b0c5af: 4c 2b c0 sub %rax,%r8
|
||||||
|
180b0c5b2: 49 8d 40 08 lea 0x8(%r8),%rax
|
||||||
|
180b0c5b6: 48 03 c2 add %rdx,%rax
|
||||||
|
180b0c5b9: 48 3b c6 cmp %rsi,%rax
|
||||||
|
180b0c5bc: 77 0e ja 0x180b0c5cc
|
||||||
|
180b0c5be: 48 83 c7 08 add $0x8,%rdi
|
||||||
|
180b0c5c2: 48 8b 17 mov (%rdi),%rdx
|
||||||
|
180b0c5c5: 48 85 d2 test %rdx,%rdx
|
||||||
|
180b0c5c8: 79 e8 jns 0x180b0c5b2
|
||||||
|
180b0c5ca: eb 64 jmp 0x180b0c630
|
||||||
|
180b0c5cc: 48 85 d2 test %rdx,%rdx
|
||||||
|
180b0c5cf: 78 5f js 0x180b0c630
|
||||||
|
180b0c5d1: 49 8d 0c 10 lea (%r8,%rdx,1),%rcx
|
||||||
|
180b0c5d5: 4c 8d 5e 04 lea 0x4(%rsi),%r11
|
||||||
|
180b0c5d9: 49 3b cb cmp %r11,%rcx
|
||||||
|
180b0c5dc: 73 52 jae 0x180b0c630
|
||||||
|
180b0c5de: 4c 8b cf mov %rdi,%r9
|
||||||
|
180b0c5e1: 4c 8d b4 24 88 00 00 lea 0x88(%rsp),%r14
|
||||||
|
180b0c5e8: 00
|
||||||
|
180b0c5e9: 8b 06 mov (%rsi),%eax
|
||||||
|
180b0c5eb: 89 84 24 88 00 00 00 mov %eax,0x88(%rsp)
|
||||||
|
180b0c5f2: 4d 8b d0 mov %r8,%r10
|
||||||
|
180b0c5f5: 4c 2b d6 sub %rsi,%r10
|
||||||
|
180b0c5f8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
180b0c5ff: 00
|
||||||
|
180b0c600: 48 8b 09 mov (%rcx),%rcx
|
||||||
|
180b0c603: 49 2b c8 sub %r8,%rcx
|
||||||
|
180b0c606: 49 8d 04 12 lea (%r10,%rdx,1),%rax
|
||||||
|
180b0c60a: 48 89 8c 04 88 00 00 mov %rcx,0x88(%rsp,%rax,1)
|
||||||
|
180b0c611: 00
|
||||||
|
180b0c612: 4d 8d 49 08 lea 0x8(%r9),%r9
|
||||||
|
180b0c616: 49 8b 11 mov (%r9),%rdx
|
||||||
|
180b0c619: 48 85 d2 test %rdx,%rdx
|
||||||
|
180b0c61c: 78 12 js 0x180b0c630
|
||||||
|
180b0c61e: 49 8d 0c 10 lea (%r8,%rdx,1),%rcx
|
||||||
|
180b0c622: 49 3b cb cmp %r11,%rcx
|
||||||
|
180b0c625: 72 d9 jb 0x180b0c600
|
||||||
|
180b0c627: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
|
||||||
|
180b0c62e: 00 00
|
||||||
|
180b0c630: 4c 8d 4c 24 50 lea 0x50(%rsp),%r9
|
||||||
|
180b0c635: 45 33 c0 xor %r8d,%r8d
|
||||||
|
180b0c638: 49 8b d6 mov %r14,%rdx
|
||||||
|
180b0c63b: b9 14 72 00 00 mov $0x7214,%ecx
|
||||||
|
180b0c640: 39 c8 cmp %ecx,%eax
|
||||||
|
180b0c642: 48 85 c0 test %rax,%rax
|
||||||
|
180b0c645: 48 83 c4 c8 add $0xffffffffffffffc8,%rsp
|
||||||
|
180b0c649: 49 81 c1 b2 c6 45 2b add $0x2b45c6b2,%r9
|
||||||
|
180b0c650: 48 85 d0 test %rdx,%rax
|
||||||
|
180b0c653: 49 81 e9 b2 c6 45 2b sub $0x2b45c6b2,%r9
|
||||||
|
180b0c65a: 48 89 74 24 30 mov %rsi,0x30(%rsp)
|
||||||
|
180b0c65f: 49 81 f5 ce cd f1 1a xor $0x1af1cdce,%r13
|
||||||
|
180b0c666: 4d 85 ec test %r13,%r12
|
||||||
|
180b0c669: 48 81 fa ff 37 12 61 cmp $0x611237ff,%rdx
|
||||||
|
180b0c670: 49 81 f5 ce cd f1 1a xor $0x1af1cdce,%r13
|
||||||
|
180b0c677: 48 83 ec d0 sub $0xffffffffffffffd0,%rsp
|
||||||
|
180b0c67b: 48 8b f2 mov %rdx,%rsi
|
||||||
|
180b0c67e: eb 01 jmp 0x180b0c681
|
||||||
|
180b0c680: 35 8b 06 eb 01 xor $0x1eb068b,%eax
|
||||||
|
180b0c685: c3 ret
|
||||||
|
180b0c686: 41 89 01 mov %eax,(%r9)
|
||||||
|
180b0c689: 48 83 c4 d8 add $0xffffffffffffffd8,%rsp
|
||||||
|
180b0c68d: 4d 85 e0 test %r12,%r8
|
||||||
|
180b0c690: 48 3d 0c 68 55 6c cmp $0x6c55680c,%rax
|
||||||
|
180b0c696: 49 f7 c2 cf c3 7a 65 test $0x657ac3cf,%r10
|
||||||
|
180b0c69d: 48 8b 74 24 28 mov 0x28(%rsp),%rsi
|
||||||
|
180b0c6a2: f6 c1 c1 test $0xc1,%cl
|
||||||
|
180b0c6a5: 74 04 je 0x180b0c6ab
|
||||||
|
180b0c6a7: 2c 02 sub $0x2,%al
|
||||||
|
180b0c6a9: 24 f5 and $0xf5,%al
|
||||||
|
180b0c6ab: 48 83 c4 30 add $0x30,%rsp
|
||||||
|
180b0c6af: 8b 0b mov (%rbx),%ecx
|
||||||
|
180b0c6b1: 8d 41 01 lea 0x1(%rcx),%eax
|
||||||
|
180b0c6b4: 0f af c1 imul %ecx,%eax
|
||||||
|
180b0c6b7: 25 01 00 00 80 and $0x80000001,%eax
|
||||||
|
180b0c6bc: 7d 07 jge 0x180b0c6c5
|
||||||
|
180b0c6be: ff c8 dec %eax
|
||||||
|
180b0c6c0: 83 c8 fe or $0xfffffffe,%eax
|
||||||
|
180b0c6c3: ff c0 inc %eax
|
||||||
|
180b0c6c5: 85 c0 test %eax,%eax
|
||||||
|
180b0c6c7: 0f 85 63 ff ff ff jne 0x180b0c630
|
||||||
|
180b0c6cd: 03 6c 24 50 add 0x50(%rsp),%ebp
|
||||||
|
180b0c6d1: 41 0f af ed imul %r13d,%ebp
|
||||||
|
180b0c6d5: 8b c5 mov %ebp,%eax
|
||||||
|
180b0c6d7: c1 e8 10 shr $0x10,%eax
|
||||||
|
180b0c6da: 33 e8 xor %eax,%ebp
|
||||||
|
180b0c6dc: 48 83 c6 04 add $0x4,%rsi
|
||||||
|
180b0c6e0: 49 8d 04 37 lea (%r15,%rsi,1),%rax
|
||||||
|
180b0c6e4: 48 85 c0 test %rax,%rax
|
||||||
|
180b0c6e7: 0f 85 93 fe ff ff jne 0x180b0c580
|
||||||
|
180b0c6ed: 48 8b 5c 24 68 mov 0x68(%rsp),%rbx
|
||||||
|
180b0c6f2: 48 8d b4 24 c8 00 00 lea 0xc8(%rsp),%rsi
|
||||||
|
180b0c6f9: 00
|
||||||
|
180b0c6fa: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
; imp_141400: VA 0x180141400-0x180141420 (32 bytes)
|
||||||
|
|
||||||
|
/tmp/opencode/dis/imp_141400.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
0000000000000000 <.data>:
|
||||||
|
0: 48 8b 05 d1 6d 4d 02 mov 0x24d6dd1(%rip),%rax # 0x24d6dd8
|
||||||
|
7: ff e0 jmp *%rax
|
||||||
|
9: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
10: 00
|
||||||
|
11: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
|
||||||
|
18: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
1f: 00
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
; imp_141580: VA 0x180141580-0x1801415a0 (32 bytes)
|
||||||
|
|
||||||
|
/tmp/opencode/dis/imp_141580.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
0000000000000000 <.data>:
|
||||||
|
0: 48 8b 05 41 6e 4d 02 mov 0x24d6e41(%rip),%rax # 0x24d6e48
|
||||||
|
7: ff e0 jmp *%rax
|
||||||
|
9: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
10: 00
|
||||||
|
11: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
|
||||||
|
18: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
1f: 00
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,594 @@
|
|||||||
|
; vt_542100
|
||||||
|
|
||||||
|
/tmp/slice.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
0000000180542100 <.data>:
|
||||||
|
180542100: 40 3d 57 41 56 45 rex cmp $0x45564157,%eax
|
||||||
|
180542106: 0f 85 6b 14 00 00 jne 0x180543577
|
||||||
|
18054210c: 41 80 bd 84 00 00 00 cmpb $0x0,0x84(%r13)
|
||||||
|
180542113: 00
|
||||||
|
180542114: 74 6e je 0x180542184
|
||||||
|
180542116: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
18054211a: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054211d: ff 50 40 call *0x40(%rax)
|
||||||
|
180542120: 3d 64 73 36 34 cmp $0x34367364,%eax
|
||||||
|
180542125: 75 5d jne 0x180542184
|
||||||
|
180542127: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
18054212b: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054212e: ff 50 40 call *0x40(%rax)
|
||||||
|
180542131: 83 f8 1c cmp $0x1c,%eax
|
||||||
|
180542134: 0f 82 89 14 00 00 jb 0x1805435c3
|
||||||
|
18054213a: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
18054213e: 8b d8 mov %eax,%ebx
|
||||||
|
180542140: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542143: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
180542149: 8b fb mov %ebx,%edi
|
||||||
|
18054214b: 83 e7 01 and $0x1,%edi
|
||||||
|
18054214e: 48 03 c3 add %rbx,%rax
|
||||||
|
180542151: 48 03 f8 add %rax,%rdi
|
||||||
|
180542154: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542158: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054215b: ff 50 50 call *0x50(%rax)
|
||||||
|
18054215e: 4c 8d 3c 06 lea (%rsi,%rax,1),%r15
|
||||||
|
180542162: 4c 89 7d 88 mov %r15,-0x78(%rbp)
|
||||||
|
180542166: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
18054216a: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054216d: ff 50 50 call *0x50(%rax)
|
||||||
|
180542170: 49 89 45 78 mov %rax,0x78(%r13)
|
||||||
|
180542174: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542178: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054217b: 48 8b d7 mov %rdi,%rdx
|
||||||
|
18054217e: ff 90 b0 00 00 00 call *0xb0(%rax)
|
||||||
|
180542184: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542188: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054218b: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
180542191: 49 3b c7 cmp %r15,%rax
|
||||||
|
180542194: 0f 83 dd 13 00 00 jae 0x180543577
|
||||||
|
18054219a: 4c 8d 3d ff aa f6 01 lea 0x1f6aaff(%rip),%r15 # 0x1824acca0
|
||||||
|
1805421a1: 48 8d 35 f0 aa f6 01 lea 0x1f6aaf0(%rip),%rsi # 0x1824acc98
|
||||||
|
1805421a8: 4c 8d 25 20 ab f6 01 lea 0x1f6ab20(%rip),%r12 # 0x1824acccf
|
||||||
|
1805421af: 4c 8d 35 12 ab f6 01 lea 0x1f6ab12(%rip),%r14 # 0x1824accc8
|
||||||
|
1805421b6: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
1805421ba: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805421bd: ff 50 10 call *0x10(%rax)
|
||||||
|
1805421c0: 84 c0 test %al,%al
|
||||||
|
1805421c2: 0f 85 7b 11 00 00 jne 0x180543343
|
||||||
|
1805421c8: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
1805421cc: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805421cf: ff 50 40 call *0x40(%rax)
|
||||||
|
1805421d2: 8b d8 mov %eax,%ebx
|
||||||
|
1805421d4: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
1805421d8: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805421db: ff 50 40 call *0x40(%rax)
|
||||||
|
1805421de: 44 8b e8 mov %eax,%r13d
|
||||||
|
1805421e1: 48 8b 4c 24 20 mov 0x20(%rsp),%rcx
|
||||||
|
1805421e6: 48 8b 49 50 mov 0x50(%rcx),%rcx
|
||||||
|
1805421ea: 8b f8 mov %eax,%edi
|
||||||
|
1805421ec: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805421ef: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
1805421f5: 41 8b cd mov %r13d,%ecx
|
||||||
|
1805421f8: 83 e1 01 and $0x1,%ecx
|
||||||
|
1805421fb: 49 03 c5 add %r13,%rax
|
||||||
|
1805421fe: 48 03 c8 add %rax,%rcx
|
||||||
|
180542201: 48 89 4c 24 28 mov %rcx,0x28(%rsp)
|
||||||
|
180542206: 81 fb 66 6d 74 20 cmp $0x20746d66,%ebx
|
||||||
|
18054220c: 0f 85 65 03 00 00 jne 0x180542577
|
||||||
|
180542212: 48 8b 7c 24 20 mov 0x20(%rsp),%rdi
|
||||||
|
180542217: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
18054221b: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054221e: ff 50 30 call *0x30(%rax)
|
||||||
|
180542221: 0f b7 d8 movzwl %ax,%ebx
|
||||||
|
180542224: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
180542228: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054222b: ff 50 30 call *0x30(%rax)
|
||||||
|
18054222e: 0f bf c8 movswl %ax,%ecx
|
||||||
|
180542231: 89 4f 20 mov %ecx,0x20(%rdi)
|
||||||
|
180542234: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
180542238: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054223b: ff 50 40 call *0x40(%rax)
|
||||||
|
18054223e: 66 0f 6e c0 movd %eax,%xmm0
|
||||||
|
180542242: f3 0f e6 c0 cvtdq2pd %xmm0,%xmm0
|
||||||
|
180542246: f2 0f 11 47 08 movsd %xmm0,0x8(%rdi)
|
||||||
|
18054224b: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
18054224f: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542252: ff 50 40 call *0x40(%rax)
|
||||||
|
180542255: 8b f8 mov %eax,%edi
|
||||||
|
180542257: 48 8b 4c 24 20 mov 0x20(%rsp),%rcx
|
||||||
|
18054225c: 48 8b 49 50 mov 0x50(%rcx),%rcx
|
||||||
|
180542260: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542263: ba 02 00 00 00 mov $0x2,%edx
|
||||||
|
180542268: ff 90 b8 00 00 00 call *0xb8(%rax)
|
||||||
|
18054226e: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
180542273: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
180542277: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054227a: ff 50 30 call *0x30(%rax)
|
||||||
|
18054227d: 0f bf c8 movswl %ax,%ecx
|
||||||
|
180542280: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
180542285: 89 48 10 mov %ecx,0x10(%rax)
|
||||||
|
180542288: 83 f9 40 cmp $0x40,%ecx
|
||||||
|
18054228b: 76 26 jbe 0x1805422b3
|
||||||
|
18054228d: f2 0f 2c 48 08 cvttsd2si 0x8(%rax),%ecx
|
||||||
|
180542292: 8b c7 mov %edi,%eax
|
||||||
|
180542294: 99 cltd
|
||||||
|
180542295: f7 f9 idiv %ecx
|
||||||
|
180542297: 48 8b 7c 24 20 mov 0x20(%rsp),%rdi
|
||||||
|
18054229c: 89 87 80 00 00 00 mov %eax,0x80(%rdi)
|
||||||
|
1805422a2: 8d 04 c5 00 00 00 00 lea 0x0(,%rax,8),%eax
|
||||||
|
1805422a9: 33 d2 xor %edx,%edx
|
||||||
|
1805422ab: f7 77 20 divl 0x20(%rdi)
|
||||||
|
1805422ae: 89 47 10 mov %eax,0x10(%rdi)
|
||||||
|
1805422b1: eb 14 jmp 0x1805422c7
|
||||||
|
1805422b3: 48 8b 7c 24 20 mov 0x20(%rsp),%rdi
|
||||||
|
1805422b8: 8b 47 20 mov 0x20(%rdi),%eax
|
||||||
|
1805422bb: 0f af c1 imul %ecx,%eax
|
||||||
|
1805422be: c1 e8 03 shr $0x3,%eax
|
||||||
|
1805422c1: 89 87 80 00 00 00 mov %eax,0x80(%rdi)
|
||||||
|
1805422c7: 66 83 fb 03 cmp $0x3,%bx
|
||||||
|
1805422cb: 75 0f jne 0x1805422dc
|
||||||
|
1805422cd: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
|
||||||
|
1805422d2: 41 c6 45 24 01 movb $0x1,0x24(%r13)
|
||||||
|
1805422d7: e9 a3 11 00 00 jmp 0x18054347f
|
||||||
|
1805422dc: b8 fe ff 00 00 mov $0xfffe,%eax
|
||||||
|
1805422e1: 66 3b d8 cmp %ax,%bx
|
||||||
|
1805422e4: 0f 85 3f 02 00 00 jne 0x180542529
|
||||||
|
1805422ea: 41 83 fd 28 cmp $0x28,%r13d
|
||||||
|
1805422ee: 73 15 jae 0x180542305
|
||||||
|
1805422f0: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
|
||||||
|
1805422f5: 41 c7 85 80 00 00 00 movl $0x0,0x80(%r13)
|
||||||
|
1805422fc: 00 00 00 00
|
||||||
|
180542300: e9 7a 11 00 00 jmp 0x18054347f
|
||||||
|
180542305: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
180542309: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054230c: ba 04 00 00 00 mov $0x4,%edx
|
||||||
|
180542311: ff 90 b8 00 00 00 call *0xb8(%rax)
|
||||||
|
180542317: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
18054231b: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054231e: ff 50 40 call *0x40(%rax)
|
||||||
|
180542321: 8b d8 mov %eax,%ebx
|
||||||
|
180542323: 48 8d 15 46 ac f6 01 lea 0x1f6ac46(%rip),%rdx # 0x1824acf70
|
||||||
|
18054232a: 48 8d 4d 98 lea -0x68(%rbp),%rcx
|
||||||
|
18054232e: e8 ad a1 f5 ff call 0x18049c4e0
|
||||||
|
180542333: 90 nop
|
||||||
|
180542334: 8b d3 mov %ebx,%edx
|
||||||
|
180542336: 48 8d 8d 00 02 00 00 lea 0x200(%rbp),%rcx
|
||||||
|
18054233d: e8 ce 1a cf 00 call 0x181233e10
|
||||||
|
180542342: 4c 8d 85 ff 01 00 00 lea 0x1ff(%rbp),%r8
|
||||||
|
180542349: 4c 2b c0 sub %rax,%r8
|
||||||
|
18054234c: 48 8b d0 mov %rax,%rdx
|
||||||
|
18054234f: 48 8d 4d 90 lea -0x70(%rbp),%rcx
|
||||||
|
180542353: e8 f8 2d f5 ff call 0x180495150
|
||||||
|
180542358: 4c 8d 45 90 lea -0x70(%rbp),%r8
|
||||||
|
18054235c: 48 8d 55 98 lea -0x68(%rbp),%rdx
|
||||||
|
180542360: 48 8d 4f 28 lea 0x28(%rdi),%rcx
|
||||||
|
180542364: e8 17 a5 ce 00 call 0x18122c880
|
||||||
|
180542369: 48 8b 4d 90 mov -0x70(%rbp),%rcx
|
||||||
|
18054236d: 48 83 c1 f0 add $0xfffffffffffffff0,%rcx
|
||||||
|
180542371: 8b 01 mov (%rcx),%eax
|
||||||
|
180542373: a9 00 00 00 30 test $0x30000000,%eax
|
||||||
|
180542378: 75 16 jne 0x180542390
|
||||||
|
18054237a: b8 ff ff ff ff mov $0xffffffff,%eax
|
||||||
|
18054237f: f0 0f c1 01 lock xadd %eax,(%rcx)
|
||||||
|
180542383: ff c8 dec %eax
|
||||||
|
180542385: 83 f8 ff cmp $0xffffffff,%eax
|
||||||
|
180542388: 75 06 jne 0x180542390
|
||||||
|
18054238a: e8 65 ed be 00 call 0x1811310f4
|
||||||
|
18054238f: 90 nop
|
||||||
|
180542390: 48 8b 4d 98 mov -0x68(%rbp),%rcx
|
||||||
|
180542394: 48 83 c1 f0 add $0xfffffffffffffff0,%rcx
|
||||||
|
180542398: 8b 01 mov (%rcx),%eax
|
||||||
|
18054239a: a9 00 00 00 30 test $0x30000000,%eax
|
||||||
|
18054239f: 75 15 jne 0x1805423b6
|
||||||
|
1805423a1: b8 ff ff ff ff mov $0xffffffff,%eax
|
||||||
|
1805423a6: f0 0f c1 01 lock xadd %eax,(%rcx)
|
||||||
|
1805423aa: ff c8 dec %eax
|
||||||
|
1805423ac: 83 f8 ff cmp $0xffffffff,%eax
|
||||||
|
1805423af: 75 05 jne 0x1805423b6
|
||||||
|
1805423b1: e8 3e ed be 00 call 0x1811310f4
|
||||||
|
1805423b6: 44 8b 47 20 mov 0x20(%rdi),%r8d
|
||||||
|
1805423ba: 8b d3 mov %ebx,%edx
|
||||||
|
1805423bc: 48 8d 8d 98 01 00 00 lea 0x198(%rbp),%rcx
|
||||||
|
1805423c3: e8 68 17 00 00 call 0x180543b30
|
||||||
|
1805423c8: 48 8b d0 mov %rax,%rdx
|
||||||
|
1805423cb: 48 8b 8f 88 00 00 00 mov 0x88(%rdi),%rcx
|
||||||
|
1805423d2: 48 89 8d 70 01 00 00 mov %rcx,0x170(%rbp)
|
||||||
|
1805423d9: 48 8b 00 mov (%rax),%rax
|
||||||
|
1805423dc: 48 89 87 88 00 00 00 mov %rax,0x88(%rdi)
|
||||||
|
1805423e3: 48 89 0a mov %rcx,(%rdx)
|
||||||
|
1805423e6: 0f 10 42 08 movups 0x8(%rdx),%xmm0
|
||||||
|
1805423ea: 0f 11 87 90 00 00 00 movups %xmm0,0x90(%rdi)
|
||||||
|
1805423f1: 48 8b 42 18 mov 0x18(%rdx),%rax
|
||||||
|
1805423f5: 48 89 87 a0 00 00 00 mov %rax,0xa0(%rdi)
|
||||||
|
1805423fc: 8b 42 20 mov 0x20(%rdx),%eax
|
||||||
|
1805423ff: 89 87 a8 00 00 00 mov %eax,0xa8(%rdi)
|
||||||
|
180542405: 0f b6 42 24 movzbl 0x24(%rdx),%eax
|
||||||
|
180542409: 88 87 ac 00 00 00 mov %al,0xac(%rdi)
|
||||||
|
18054240f: 48 8b 8d 98 01 00 00 mov 0x198(%rbp),%rcx
|
||||||
|
180542416: ff 15 8c 8e 66 01 call *0x1668e8c(%rip) # 0x181bab2a8
|
||||||
|
18054241c: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
180542420: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542423: ff 50 40 call *0x40(%rax)
|
||||||
|
180542426: 89 85 88 01 00 00 mov %eax,0x188(%rbp)
|
||||||
|
18054242c: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
180542430: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542433: ff 50 30 call *0x30(%rax)
|
||||||
|
180542436: 66 89 85 8c 01 00 00 mov %ax,0x18c(%rbp)
|
||||||
|
18054243d: 48 8b 4f 50 mov 0x50(%rdi),%rcx
|
||||||
|
180542441: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542444: ff 50 30 call *0x30(%rax)
|
||||||
|
180542447: 66 89 85 8e 01 00 00 mov %ax,0x18e(%rbp)
|
||||||
|
18054244e: bb 08 00 00 00 mov $0x8,%ebx
|
||||||
|
180542453: 48 89 9d 28 01 00 00 mov %rbx,0x128(%rbp)
|
||||||
|
18054245a: 4c 8b 6f 50 mov 0x50(%rdi),%r13
|
||||||
|
18054245e: 33 ff xor %edi,%edi
|
||||||
|
180542460: 41 bc 00 00 00 70 mov $0x70000000,%r12d
|
||||||
|
180542466: 44 8b c3 mov %ebx,%r8d
|
||||||
|
180542469: 49 3b dc cmp %r12,%rbx
|
||||||
|
18054246c: 45 0f 47 c4 cmova %r12d,%r8d
|
||||||
|
180542470: 48 8d 95 90 01 00 00 lea 0x190(%rbp),%rdx
|
||||||
|
180542477: 48 03 d7 add %rdi,%rdx
|
||||||
|
18054247a: 49 8b 45 00 mov 0x0(%r13),%rax
|
||||||
|
18054247e: 49 8b cd mov %r13,%rcx
|
||||||
|
180542481: ff 50 18 call *0x18(%rax)
|
||||||
|
180542484: 83 f8 01 cmp $0x1,%eax
|
||||||
|
180542487: 7c 0a jl 0x180542493
|
||||||
|
180542489: 48 98 cltq
|
||||||
|
18054248b: 48 03 f8 add %rax,%rdi
|
||||||
|
18054248e: 48 2b d8 sub %rax,%rbx
|
||||||
|
180542491: 75 d3 jne 0x180542466
|
||||||
|
180542493: 48 89 9d 28 01 00 00 mov %rbx,0x128(%rbp)
|
||||||
|
18054249a: 48 8b 8d 88 01 00 00 mov 0x188(%rbp),%rcx
|
||||||
|
1805424a1: 48 8b c1 mov %rcx,%rax
|
||||||
|
1805424a4: 48 8b 95 90 01 00 00 mov 0x190(%rbp),%rdx
|
||||||
|
1805424ab: 48 2b 05 d6 96 e1 01 sub 0x1e196d6(%rip),%rax # 0x18235bb88
|
||||||
|
1805424b2: 4c 8d 25 16 a8 f6 01 lea 0x1f6a816(%rip),%r12 # 0x1824acccf
|
||||||
|
1805424b9: 75 0a jne 0x1805424c5
|
||||||
|
1805424bb: 48 8b c2 mov %rdx,%rax
|
||||||
|
1805424be: 48 2b 05 cb 96 e1 01 sub 0x1e196cb(%rip),%rax # 0x18235bb90
|
||||||
|
1805424c5: 48 85 c0 test %rax,%rax
|
||||||
|
1805424c8: 0f 94 c0 sete %al
|
||||||
|
1805424cb: 84 c0 test %al,%al
|
||||||
|
1805424cd: 74 0f je 0x1805424de
|
||||||
|
1805424cf: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
|
||||||
|
1805424d4: 41 c6 45 24 01 movb $0x1,0x24(%r13)
|
||||||
|
1805424d9: e9 a1 0f 00 00 jmp 0x18054347f
|
||||||
|
1805424de: 48 8b c1 mov %rcx,%rax
|
||||||
|
1805424e1: 48 2b 05 38 96 e1 01 sub 0x1e19638(%rip),%rax # 0x18235bb20
|
||||||
|
1805424e8: 75 0a jne 0x1805424f4
|
||||||
|
1805424ea: 48 8b c2 mov %rdx,%rax
|
||||||
|
1805424ed: 48 2b 05 34 96 e1 01 sub 0x1e19634(%rip),%rax # 0x18235bb28
|
||||||
|
1805424f4: 48 85 c0 test %rax,%rax
|
||||||
|
1805424f7: 0f 94 c0 sete %al
|
||||||
|
1805424fa: 84 c0 test %al,%al
|
||||||
|
1805424fc: 0f 94 c0 sete %al
|
||||||
|
1805424ff: 84 c0 test %al,%al
|
||||||
|
180542501: 0f 84 73 0f 00 00 je 0x18054347a
|
||||||
|
180542507: 48 2b 0d 5a 96 e1 01 sub 0x1e1965a(%rip),%rcx # 0x18235bb68
|
||||||
|
18054250e: 75 0a jne 0x18054251a
|
||||||
|
180542510: 48 8b ca mov %rdx,%rcx
|
||||||
|
180542513: 48 2b 0d 56 96 e1 01 sub 0x1e19656(%rip),%rcx # 0x18235bb70
|
||||||
|
18054251a: 48 85 c9 test %rcx,%rcx
|
||||||
|
18054251d: 0f 94 c0 sete %al
|
||||||
|
180542520: 84 c0 test %al,%al
|
||||||
|
180542522: 0f 94 c0 sete %al
|
||||||
|
180542525: 84 c0 test %al,%al
|
||||||
|
180542527: eb 2e jmp 0x180542557
|
||||||
|
180542529: 0f b7 c3 movzwl %bx,%eax
|
||||||
|
18054252c: b9 4f 67 00 00 mov $0x674f,%ecx
|
||||||
|
180542531: 66 2b c1 sub %cx,%ax
|
||||||
|
180542534: 66 83 f8 02 cmp $0x2,%ax
|
||||||
|
180542538: 0f 86 6f 0f 00 00 jbe 0x1805434ad
|
||||||
|
18054253e: 0f b7 c3 movzwl %bx,%eax
|
||||||
|
180542541: b9 6f 67 00 00 mov $0x676f,%ecx
|
||||||
|
180542546: 66 2b c1 sub %cx,%ax
|
||||||
|
180542549: 66 83 f8 02 cmp $0x2,%ax
|
||||||
|
18054254d: 0f 86 5a 0f 00 00 jbe 0x1805434ad
|
||||||
|
180542553: 66 83 fb 01 cmp $0x1,%bx
|
||||||
|
180542557: 48 8b 54 24 28 mov 0x28(%rsp),%rdx
|
||||||
|
18054255c: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
|
||||||
|
180542561: 0f 84 1d 0f 00 00 je 0x180543484
|
||||||
|
180542567: 41 c7 85 80 00 00 00 movl $0x0,0x80(%r13)
|
||||||
|
18054256e: 00 00 00 00
|
||||||
|
180542572: e9 0d 0f 00 00 jmp 0x180543484
|
||||||
|
180542577: 81 fb 64 61 74 61 cmp $0x61746164,%ebx
|
||||||
|
18054257d: 75 7a jne 0x1805425f9
|
||||||
|
18054257f: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
|
||||||
|
180542584: 41 80 bd 84 00 00 00 cmpb $0x0,0x84(%r13)
|
||||||
|
18054258b: 00
|
||||||
|
18054258c: 74 2b je 0x1805425b9
|
||||||
|
18054258e: 49 83 7d 78 00 cmpq $0x0,0x78(%r13)
|
||||||
|
180542593: 7e 28 jle 0x1805425bd
|
||||||
|
180542595: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542599: 49 8b 5d 78 mov 0x78(%r13),%rbx
|
||||||
|
18054259d: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805425a0: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
1805425a6: 48 8b cb mov %rbx,%rcx
|
||||||
|
1805425a9: 83 e1 01 and $0x1,%ecx
|
||||||
|
1805425ac: 48 03 c3 add %rbx,%rax
|
||||||
|
1805425af: 48 03 c8 add %rax,%rcx
|
||||||
|
1805425b2: 48 89 4c 24 28 mov %rcx,0x28(%rsp)
|
||||||
|
1805425b7: eb 04 jmp 0x1805425bd
|
||||||
|
1805425b9: 49 89 7d 78 mov %rdi,0x78(%r13)
|
||||||
|
1805425bd: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
1805425c1: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805425c4: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
1805425ca: 49 89 45 70 mov %rax,0x70(%r13)
|
||||||
|
1805425ce: 49 63 85 80 00 00 00 movslq 0x80(%r13),%rax
|
||||||
|
1805425d5: 85 c0 test %eax,%eax
|
||||||
|
1805425d7: 7e 15 jle 0x1805425ee
|
||||||
|
1805425d9: 48 8b c8 mov %rax,%rcx
|
||||||
|
1805425dc: 49 8b 45 78 mov 0x78(%r13),%rax
|
||||||
|
1805425e0: 48 99 cqto
|
||||||
|
1805425e2: 48 f7 f9 idiv %rcx
|
||||||
|
1805425e5: 49 89 45 18 mov %rax,0x18(%r13)
|
||||||
|
1805425e9: e9 91 0e 00 00 jmp 0x18054347f
|
||||||
|
1805425ee: 33 c0 xor %eax,%eax
|
||||||
|
1805425f0: 49 89 45 18 mov %rax,0x18(%r13)
|
||||||
|
1805425f4: e9 86 0e 00 00 jmp 0x18054347f
|
||||||
|
1805425f9: 81 fb 62 65 78 74 cmp $0x74786562,%ebx
|
||||||
|
1805425ff: 75 75 jne 0x180542676
|
||||||
|
180542601: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
|
||||||
|
180542606: 48 8b 4b 50 mov 0x50(%rbx),%rcx
|
||||||
|
18054260a: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054260d: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
180542613: 48 89 43 60 mov %rax,0x60(%rbx)
|
||||||
|
180542617: 48 89 7b 68 mov %rdi,0x68(%rbx)
|
||||||
|
18054261b: 49 8d 5d 01 lea 0x1(%r13),%rbx
|
||||||
|
18054261f: 33 c9 xor %ecx,%ecx
|
||||||
|
180542621: ff 15 81 8c 66 01 call *0x1668c81(%rip) # 0x181bab2a8
|
||||||
|
180542627: bf 5b 02 00 00 mov $0x25b,%edi
|
||||||
|
18054262c: 48 3b df cmp %rdi,%rbx
|
||||||
|
18054262f: 48 0f 42 df cmovb %rdi,%rbx
|
||||||
|
180542633: ba 01 00 00 00 mov $0x1,%edx
|
||||||
|
180542638: 48 8b cb mov %rbx,%rcx
|
||||||
|
18054263b: ff 15 5f 8c 66 01 call *0x1668c5f(%rip) # 0x181bab2a0
|
||||||
|
180542641: 48 8b d8 mov %rax,%rbx
|
||||||
|
180542644: 48 89 85 78 01 00 00 mov %rax,0x178(%rbp)
|
||||||
|
18054264b: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
180542650: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
180542654: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542657: 45 8b c5 mov %r13d,%r8d
|
||||||
|
18054265a: 48 8b d3 mov %rbx,%rdx
|
||||||
|
18054265d: ff 50 18 call *0x18(%rax)
|
||||||
|
180542660: 45 8b c5 mov %r13d,%r8d
|
||||||
|
180542663: 48 8b 54 24 60 mov 0x60(%rsp),%rdx
|
||||||
|
180542668: 48 8b cb mov %rbx,%rcx
|
||||||
|
18054266b: e8 20 c7 ff ff call 0x18053ed90
|
||||||
|
180542670: 90 nop
|
||||||
|
180542671: e9 fb 0d 00 00 jmp 0x180543471
|
||||||
|
180542676: 81 fb 73 6d 70 6c cmp $0x6c706d73,%ebx
|
||||||
|
18054267c: 75 5a jne 0x1805426d8
|
||||||
|
18054267e: 49 8d 5d 01 lea 0x1(%r13),%rbx
|
||||||
|
180542682: 33 c9 xor %ecx,%ecx
|
||||||
|
180542684: ff 15 1e 8c 66 01 call *0x1668c1e(%rip) # 0x181bab2a8
|
||||||
|
18054268a: 48 83 fb 3c cmp $0x3c,%rbx
|
||||||
|
18054268e: b8 3c 00 00 00 mov $0x3c,%eax
|
||||||
|
180542693: 48 0f 42 d8 cmovb %rax,%rbx
|
||||||
|
180542697: 8d 50 c5 lea -0x3b(%rax),%edx
|
||||||
|
18054269a: 48 8b cb mov %rbx,%rcx
|
||||||
|
18054269d: ff 15 fd 8b 66 01 call *0x1668bfd(%rip) # 0x181bab2a0
|
||||||
|
1805426a3: 48 8b d8 mov %rax,%rbx
|
||||||
|
1805426a6: 48 89 85 80 01 00 00 mov %rax,0x180(%rbp)
|
||||||
|
1805426ad: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
1805426b2: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
1805426b6: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805426b9: 45 8b c5 mov %r13d,%r8d
|
||||||
|
1805426bc: 48 8b d3 mov %rbx,%rdx
|
||||||
|
1805426bf: ff 50 18 call *0x18(%rax)
|
||||||
|
1805426c2: 45 8b c5 mov %r13d,%r8d
|
||||||
|
1805426c5: 48 8b 54 24 60 mov 0x60(%rsp),%rdx
|
||||||
|
1805426ca: 48 8b cb mov %rbx,%rcx
|
||||||
|
1805426cd: e8 5e d0 ff ff call 0x18053f730
|
||||||
|
1805426d2: 90 nop
|
||||||
|
1805426d3: e9 99 0d 00 00 jmp 0x180543471
|
||||||
|
1805426d8: 81 fb 69 6e 73 74 cmp $0x74736e69,%ebx
|
||||||
|
1805426de: 0f 84 b8 0c 00 00 je 0x18054339c
|
||||||
|
1805426e4: 81 fb 49 4e 53 54 cmp $0x54534e49,%ebx
|
||||||
|
1805426ea: 0f 84 ac 0c 00 00 je 0x18054339c
|
||||||
|
1805426f0: 81 fb 63 75 65 20 cmp $0x20657563,%ebx
|
||||||
|
1805426f6: 75 5a jne 0x180542752
|
||||||
|
1805426f8: 49 8d 5d 01 lea 0x1(%r13),%rbx
|
||||||
|
1805426fc: 33 c9 xor %ecx,%ecx
|
||||||
|
1805426fe: ff 15 a4 8b 66 01 call *0x1668ba4(%rip) # 0x181bab2a8
|
||||||
|
180542704: 48 83 fb 1c cmp $0x1c,%rbx
|
||||||
|
180542708: b8 1c 00 00 00 mov $0x1c,%eax
|
||||||
|
18054270d: 48 0f 42 d8 cmovb %rax,%rbx
|
||||||
|
180542711: 8d 50 e5 lea -0x1b(%rax),%edx
|
||||||
|
180542714: 48 8b cb mov %rbx,%rcx
|
||||||
|
180542717: ff 15 83 8b 66 01 call *0x1668b83(%rip) # 0x181bab2a0
|
||||||
|
18054271d: 48 8b d8 mov %rax,%rbx
|
||||||
|
180542720: 48 89 85 50 01 00 00 mov %rax,0x150(%rbp)
|
||||||
|
180542727: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
18054272c: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
180542730: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542733: 45 8b c5 mov %r13d,%r8d
|
||||||
|
180542736: 48 8b d3 mov %rbx,%rdx
|
||||||
|
180542739: ff 50 18 call *0x18(%rax)
|
||||||
|
18054273c: 45 8b c5 mov %r13d,%r8d
|
||||||
|
18054273f: 48 8b 54 24 60 mov 0x60(%rsp),%rdx
|
||||||
|
180542744: 48 8b cb mov %rbx,%rcx
|
||||||
|
180542747: e8 e4 e0 ff ff call 0x180540830
|
||||||
|
18054274c: 90 nop
|
||||||
|
18054274d: e9 1f 0d 00 00 jmp 0x180543471
|
||||||
|
180542752: 81 fb 61 78 6d 6c cmp $0x6c6d7861,%ebx
|
||||||
|
180542758: 0f 85 a6 00 00 00 jne 0x180542804
|
||||||
|
18054275e: 33 db xor %ebx,%ebx
|
||||||
|
180542760: 48 89 5d 60 mov %rbx,0x60(%rbp)
|
||||||
|
180542764: 48 89 5d 68 mov %rbx,0x68(%rbp)
|
||||||
|
180542768: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
|
||||||
|
18054276d: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542771: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542774: 4c 8b c7 mov %rdi,%r8
|
||||||
|
180542777: 48 8d 55 60 lea 0x60(%rbp),%rdx
|
||||||
|
18054277b: ff 90 a0 00 00 00 call *0xa0(%rax)
|
||||||
|
180542781: 48 8b 55 60 mov 0x60(%rbp),%rdx
|
||||||
|
180542785: 48 85 d2 test %rdx,%rdx
|
||||||
|
180542788: 74 28 je 0x1805427b2
|
||||||
|
18054278a: 48 8b 45 68 mov 0x68(%rbp),%rax
|
||||||
|
18054278e: 85 c0 test %eax,%eax
|
||||||
|
180542790: 79 0c jns 0x18054279e
|
||||||
|
180542792: 48 8d 4c 24 78 lea 0x78(%rsp),%rcx
|
||||||
|
180542797: e8 44 a2 f5 ff call 0x18049c9e0
|
||||||
|
18054279c: eb 20 jmp 0x1805427be
|
||||||
|
18054279e: 7e 12 jle 0x1805427b2
|
||||||
|
1805427a0: 4c 63 c0 movslq %eax,%r8
|
||||||
|
1805427a3: 4c 03 c2 add %rdx,%r8
|
||||||
|
1805427a6: 48 8d 4c 24 78 lea 0x78(%rsp),%rcx
|
||||||
|
1805427ab: e8 f0 80 d0 00 call 0x18124a8a0
|
||||||
|
1805427b0: eb 0c jmp 0x1805427be
|
||||||
|
1805427b2: 48 8d 05 df 97 c9 01 lea 0x1c997df(%rip),%rax # 0x1821dbf98
|
||||||
|
1805427b9: 48 89 44 24 78 mov %rax,0x78(%rsp)
|
||||||
|
1805427be: 48 8d 54 24 78 lea 0x78(%rsp),%rdx
|
||||||
|
1805427c3: 49 8d 4d 28 lea 0x28(%r13),%rcx
|
||||||
|
1805427c7: e8 e4 b2 e2 00 call 0x18136dab0
|
||||||
|
1805427cc: 90 nop
|
||||||
|
1805427cd: 48 8b 4c 24 78 mov 0x78(%rsp),%rcx
|
||||||
|
1805427d2: 48 83 c1 f0 add $0xfffffffffffffff0,%rcx
|
||||||
|
1805427d6: 8b 01 mov (%rcx),%eax
|
||||||
|
1805427d8: a9 00 00 00 30 test $0x30000000,%eax
|
||||||
|
1805427dd: 75 16 jne 0x1805427f5
|
||||||
|
1805427df: b8 ff ff ff ff mov $0xffffffff,%eax
|
||||||
|
1805427e4: f0 0f c1 01 lock xadd %eax,(%rcx)
|
||||||
|
1805427e8: ff c8 dec %eax
|
||||||
|
1805427ea: 83 f8 ff cmp $0xffffffff,%eax
|
||||||
|
1805427ed: 75 06 jne 0x1805427f5
|
||||||
|
1805427ef: e8 00 e9 be 00 call 0x1811310f4
|
||||||
|
1805427f4: 90 nop
|
||||||
|
1805427f5: 48 8b 4d 60 mov 0x60(%rbp),%rcx
|
||||||
|
1805427f9: ff 15 a9 8a 66 01 call *0x1668aa9(%rip) # 0x181bab2a8
|
||||||
|
1805427ff: e9 7b 0c 00 00 jmp 0x18054347f
|
||||||
|
180542804: 81 fb 4c 49 53 54 cmp $0x5453494c,%ebx
|
||||||
|
18054280a: 0f 85 2a 0a 00 00 jne 0x18054323a
|
||||||
|
180542810: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
|
||||||
|
180542815: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542819: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054281c: ff 50 40 call *0x40(%rax)
|
||||||
|
18054281f: 3d 69 6e 66 6f cmp $0x6f666e69,%eax
|
||||||
|
180542824: 0f 84 f9 09 00 00 je 0x180543223
|
||||||
|
18054282a: 3d 49 4e 46 4f cmp $0x4f464e49,%eax
|
||||||
|
18054282f: 0f 84 ee 09 00 00 je 0x180543223
|
||||||
|
180542835: 3d 61 64 74 6c cmp $0x6c746461,%eax
|
||||||
|
18054283a: 0f 85 3f 0c 00 00 jne 0x18054347f
|
||||||
|
180542840: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542844: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542847: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
18054284d: 48 8b 54 24 28 mov 0x28(%rsp),%rdx
|
||||||
|
180542852: 48 3b c2 cmp %rdx,%rax
|
||||||
|
180542855: 0f 8d 29 0c 00 00 jge 0x180543484
|
||||||
|
18054285b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
|
||||||
|
180542860: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542864: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542867: ff 50 40 call *0x40(%rax)
|
||||||
|
18054286a: 8b f8 mov %eax,%edi
|
||||||
|
18054286c: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542870: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542873: ff 50 40 call *0x40(%rax)
|
||||||
|
180542876: 8b d8 mov %eax,%ebx
|
||||||
|
180542878: 89 44 24 68 mov %eax,0x68(%rsp)
|
||||||
|
18054287c: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542880: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542883: ff 90 a8 00 00 00 call *0xa8(%rax)
|
||||||
|
180542889: 48 8b c8 mov %rax,%rcx
|
||||||
|
18054288c: 8b c3 mov %ebx,%eax
|
||||||
|
18054288e: 83 e0 01 and $0x1,%eax
|
||||||
|
180542891: 03 c3 add %ebx,%eax
|
||||||
|
180542893: 48 03 c8 add %rax,%rcx
|
||||||
|
180542896: 48 89 8d f8 00 00 00 mov %rcx,0xf8(%rbp)
|
||||||
|
18054289d: 81 ff 6c 61 62 6c cmp $0x6c62616c,%edi
|
||||||
|
1805428a3: 0f 84 89 05 00 00 je 0x180542e32
|
||||||
|
1805428a9: 81 ff 6e 6f 74 65 cmp $0x65746f6e,%edi
|
||||||
|
1805428af: 0f 84 7d 05 00 00 je 0x180542e32
|
||||||
|
1805428b5: 81 ff 6c 74 78 74 cmp $0x7478746c,%edi
|
||||||
|
1805428bb: 0f 85 31 09 00 00 jne 0x1805431f2
|
||||||
|
1805428c1: 8b 5c 24 58 mov 0x58(%rsp),%ebx
|
||||||
|
1805428c5: 8b d3 mov %ebx,%edx
|
||||||
|
1805428c7: 48 8d 8d 30 01 00 00 lea 0x130(%rbp),%rcx
|
||||||
|
1805428ce: e8 8d 80 d0 00 call 0x18124a960
|
||||||
|
1805428d3: 90 nop
|
||||||
|
1805428d4: 4c 8b c0 mov %rax,%r8
|
||||||
|
1805428d7: 48 8d 15 a2 a6 f6 01 lea 0x1f6a6a2(%rip),%rdx # 0x1824acf80
|
||||||
|
1805428de: 48 8d 4c 24 38 lea 0x38(%rsp),%rcx
|
||||||
|
1805428e3: e8 68 3c d0 00 call 0x181246550
|
||||||
|
1805428e8: 90 nop
|
||||||
|
1805428e9: ff c3 inc %ebx
|
||||||
|
1805428eb: 89 5c 24 58 mov %ebx,0x58(%rsp)
|
||||||
|
1805428ef: 48 8b 8d 30 01 00 00 mov 0x130(%rbp),%rcx
|
||||||
|
1805428f6: 48 83 c1 f0 add $0xfffffffffffffff0,%rcx
|
||||||
|
1805428fa: 8b 01 mov (%rcx),%eax
|
||||||
|
1805428fc: a9 00 00 00 30 test $0x30000000,%eax
|
||||||
|
180542901: 75 15 jne 0x180542918
|
||||||
|
180542903: b8 ff ff ff ff mov $0xffffffff,%eax
|
||||||
|
180542908: f0 0f c1 01 lock xadd %eax,(%rcx)
|
||||||
|
18054290c: ff c8 dec %eax
|
||||||
|
18054290e: 83 f8 ff cmp $0xffffffff,%eax
|
||||||
|
180542911: 75 05 jne 0x180542918
|
||||||
|
180542913: e8 dc e7 be 00 call 0x1811310f4
|
||||||
|
180542918: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
18054291c: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054291f: ff 50 40 call *0x40(%rax)
|
||||||
|
180542922: 8b d8 mov %eax,%ebx
|
||||||
|
180542924: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542928: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054292b: ff 50 40 call *0x40(%rax)
|
||||||
|
18054292e: 8b f8 mov %eax,%edi
|
||||||
|
180542930: 49 8b 4d 50 mov 0x50(%r13),%rcx
|
||||||
|
180542934: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542937: ff 50 40 call *0x40(%rax)
|
||||||
|
18054293a: 44 8b e8 mov %eax,%r13d
|
||||||
|
18054293d: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
180542942: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
180542946: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542949: ff 50 30 call *0x30(%rax)
|
||||||
|
18054294c: 66 89 44 24 5c mov %ax,0x5c(%rsp)
|
||||||
|
180542951: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
180542956: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
18054295a: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054295d: ff 50 30 call *0x30(%rax)
|
||||||
|
180542960: 66 89 44 24 50 mov %ax,0x50(%rsp)
|
||||||
|
180542965: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
18054296a: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
18054296e: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542971: ff 50 30 call *0x30(%rax)
|
||||||
|
180542974: 66 89 44 24 52 mov %ax,0x52(%rsp)
|
||||||
|
180542979: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
18054297e: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
180542982: 48 8b 01 mov (%rcx),%rax
|
||||||
|
180542985: ff 50 30 call *0x30(%rax)
|
||||||
|
180542988: 66 89 44 24 54 mov %ax,0x54(%rsp)
|
||||||
|
18054298d: 8b 54 24 68 mov 0x68(%rsp),%edx
|
||||||
|
180542991: 83 c2 ec add $0xffffffec,%edx
|
||||||
|
180542994: 33 c0 xor %eax,%eax
|
||||||
|
180542996: 48 89 85 80 00 00 00 mov %rax,0x80(%rbp)
|
||||||
|
18054299d: 48 89 85 88 00 00 00 mov %rax,0x88(%rbp)
|
||||||
|
1805429a4: 48 8b 44 24 20 mov 0x20(%rsp),%rax
|
||||||
|
1805429a9: 48 8b 48 50 mov 0x50(%rax),%rcx
|
||||||
|
1805429ad: 48 8b 01 mov (%rcx),%rax
|
||||||
|
1805429b0: 4c 63 c2 movslq %edx,%r8
|
||||||
|
1805429b3: 48 8d 95 80 00 00 00 lea 0x80(%rbp),%rdx
|
||||||
|
1805429ba: ff 90 a0 00 00 00 call *0xa0(%rax)
|
||||||
|
1805429c0: 8b d3 mov %ebx,%edx
|
||||||
|
1805429c2: 48 8d 4d a8 lea -0x58(%rbp),%rcx
|
||||||
|
1805429c6: e8 05 f6 cc 00 call 0x181211fd0
|
||||||
|
1805429cb: 90 nop
|
||||||
|
1805429cc: 48 8b 5c 24 38 mov 0x38(%rsp),%rbx
|
||||||
|
1805429d1: 48 89 5d a0 mov %rbx,-0x60(%rbp)
|
||||||
|
1805429d5: 8b 43 f0 mov -0x10(%rbx),%eax
|
||||||
|
1805429d8: a9 00 00 00 30 test $0x30000000,%eax
|
||||||
|
1805429dd: 75 0a jne 0x1805429e9
|
||||||
|
1805429df: b8 01 00 00 00 mov $0x1,%eax
|
||||||
|
1805429e4: f0 0f c1 43 f0 lock xadd %eax,-0x10(%rbx)
|
||||||
|
1805429e9: 4c 8d 05 68 a2 f6 01 lea 0x1f6a268(%rip),%r8 # 0x1824acc58
|
||||||
|
1805429f0: 48 8d 55 a0 lea -0x60(%rbp),%rdx
|
||||||
|
1805429f4: 48 8d 8d a0 00 00 00 lea 0xa0(%rbp),%rcx
|
||||||
|
1805429fb: e8 e0 3b d0 00 call 0x1812465e0
|
||||||
|
180542a00: 4c 8d 45 a8 lea -0x58(%rbp),%r8
|
||||||
|
180542a04: 48 8b d0 mov %rax,%rdx
|
||||||
|
180542a07: 48 8b 4c 24 60 mov 0x60(%rsp),%rcx
|
||||||
|
180542a0c: e8 6f 9e ce 00 call 0x18122c880
|
||||||
|
180542a11: 48 8b 8d a0 00 00 00 mov 0xa0(%rbp),%rcx
|
||||||
|
180542a18: 48 83 c1 f0 add $0xfffffffffffffff0,%rcx
|
||||||
|
180542a1c: 8b 01 mov (%rcx),%eax
|
||||||
|
180542a1e: a9 .byte 0xa9
|
||||||
|
...
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
; vt_54b600
|
||||||
|
|
||||||
|
/tmp/slice.bin: file format binary
|
||||||
|
|
||||||
|
|
||||||
|
Disassembly of section .data:
|
||||||
|
|
||||||
|
000000018054b600 <.data>:
|
||||||
|
18054b600: 8b f8 mov %eax,%edi
|
||||||
|
18054b602: 00 00 add %al,(%rax)
|
||||||
|
18054b604: 00 48 85 add %cl,-0x7b(%rax)
|
||||||
|
18054b607: c9 leave
|
||||||
|
18054b608: 74 07 je 0x18054b611
|
||||||
|
18054b60a: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054b60d: ff 50 10 call *0x10(%rax)
|
||||||
|
18054b610: 90 nop
|
||||||
|
18054b611: 48 8b cb mov %rbx,%rcx
|
||||||
|
18054b614: e8 07 f0 ff ff call 0x18054a620
|
||||||
|
18054b619: 90 nop
|
||||||
|
18054b61a: 40 f6 c7 01 test $0x1,%dil
|
||||||
|
18054b61e: 74 0e je 0x18054b62e
|
||||||
|
18054b620: ba 30 01 00 00 mov $0x130,%edx
|
||||||
|
18054b625: 48 8b cb mov %rbx,%rcx
|
||||||
|
18054b628: e8 c7 5a be 00 call 0x1811310f4
|
||||||
|
18054b62d: 90 nop
|
||||||
|
18054b62e: 48 8b c3 mov %rbx,%rax
|
||||||
|
18054b631: 48 8b 5c 24 40 mov 0x40(%rsp),%rbx
|
||||||
|
18054b636: 48 83 c4 30 add $0x30,%rsp
|
||||||
|
18054b63a: 5f pop %rdi
|
||||||
|
18054b63b: c3 ret
|
||||||
|
18054b63c: cc int3
|
||||||
|
18054b63d: cc int3
|
||||||
|
18054b63e: cc int3
|
||||||
|
18054b63f: cc int3
|
||||||
|
18054b640: 40 53 rex push %rbx
|
||||||
|
18054b642: 48 83 ec 20 sub $0x20,%rsp
|
||||||
|
18054b646: 48 8b d9 mov %rcx,%rbx
|
||||||
|
18054b649: 84 d2 test %dl,%dl
|
||||||
|
18054b64b: 74 1e je 0x18054b66b
|
||||||
|
18054b64d: b9 10 00 00 00 mov $0x10,%ecx
|
||||||
|
18054b652: e8 89 60 be 00 call 0x1811316e0
|
||||||
|
18054b657: 44 8b 43 2c mov 0x2c(%rbx),%r8d
|
||||||
|
18054b65b: 48 8d 0d 56 27 f6 01 lea 0x1f62756(%rip),%rcx # 0x1824addb8
|
||||||
|
18054b662: 48 89 08 mov %rcx,(%rax)
|
||||||
|
18054b665: 44 89 40 0c mov %r8d,0xc(%rax)
|
||||||
|
18054b669: eb 46 jmp 0x18054b6b1
|
||||||
|
18054b66b: 8b 81 9c 00 00 00 mov 0x9c(%rcx),%eax
|
||||||
|
18054b671: b9 10 00 00 00 mov $0x10,%ecx
|
||||||
|
18054b676: 83 f8 04 cmp $0x4,%eax
|
||||||
|
18054b679: 75 0e jne 0x18054b689
|
||||||
|
18054b67b: e8 60 60 be 00 call 0x1811316e0
|
||||||
|
18054b680: 48 8d 15 39 24 f6 01 lea 0x1f62439(%rip),%rdx # 0x1824adac0
|
||||||
|
18054b687: eb 1f jmp 0x18054b6a8
|
||||||
|
18054b689: 83 f8 03 cmp $0x3,%eax
|
||||||
|
18054b68c: 75 0e jne 0x18054b69c
|
||||||
|
18054b68e: e8 4d 60 be 00 call 0x1811316e0
|
||||||
|
18054b693: 48 8d 15 0e 1f f6 01 lea 0x1f61f0e(%rip),%rdx # 0x1824ad5a8
|
||||||
|
18054b69a: eb 0c jmp 0x18054b6a8
|
||||||
|
18054b69c: e8 3f 60 be 00 call 0x1811316e0
|
||||||
|
18054b6a1: 48 8d 15 30 1f f6 01 lea 0x1f61f30(%rip),%rdx # 0x1824ad5d8
|
||||||
|
18054b6a8: 8b 4b 2c mov 0x2c(%rbx),%ecx
|
||||||
|
18054b6ab: 89 48 0c mov %ecx,0xc(%rax)
|
||||||
|
18054b6ae: 48 89 10 mov %rdx,(%rax)
|
||||||
|
18054b6b1: c7 40 08 01 00 00 00 movl $0x1,0x8(%rax)
|
||||||
|
18054b6b8: 48 8b 8b 00 01 00 00 mov 0x100(%rbx),%rcx
|
||||||
|
18054b6bf: 48 89 83 00 01 00 00 mov %rax,0x100(%rbx)
|
||||||
|
18054b6c6: 48 89 44 24 40 mov %rax,0x40(%rsp)
|
||||||
|
18054b6cb: 48 85 c9 test %rcx,%rcx
|
||||||
|
18054b6ce: 74 10 je 0x18054b6e0
|
||||||
|
18054b6d0: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054b6d3: ba 01 00 00 00 mov $0x1,%edx
|
||||||
|
18054b6d8: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18054b6dc: 5b pop %rbx
|
||||||
|
18054b6dd: 48 ff 20 rex.W jmp *(%rax)
|
||||||
|
18054b6e0: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18054b6e4: 5b pop %rbx
|
||||||
|
18054b6e5: c3 ret
|
||||||
|
18054b6e6: cc int3
|
||||||
|
18054b6e7: cc int3
|
||||||
|
18054b6e8: cc int3
|
||||||
|
18054b6e9: cc int3
|
||||||
|
18054b6ea: cc int3
|
||||||
|
18054b6eb: cc int3
|
||||||
|
18054b6ec: cc int3
|
||||||
|
18054b6ed: cc int3
|
||||||
|
18054b6ee: cc int3
|
||||||
|
18054b6ef: cc int3
|
||||||
|
18054b6f0: 40 53 rex push %rbx
|
||||||
|
18054b6f2: 48 83 ec 20 sub $0x20,%rsp
|
||||||
|
18054b6f6: 83 79 28 00 cmpl $0x0,0x28(%rcx)
|
||||||
|
18054b6fa: 48 8b d9 mov %rcx,%rbx
|
||||||
|
18054b6fd: 7f 08 jg 0x18054b707
|
||||||
|
18054b6ff: 33 c0 xor %eax,%eax
|
||||||
|
18054b701: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18054b705: 5b pop %rbx
|
||||||
|
18054b706: c3 ret
|
||||||
|
18054b707: 80 79 40 00 cmpb $0x0,0x40(%rcx)
|
||||||
|
18054b70b: 75 2b jne 0x18054b738
|
||||||
|
18054b70d: 48 8b 49 10 mov 0x10(%rcx),%rcx
|
||||||
|
18054b711: 48 8d 54 24 30 lea 0x30(%rsp),%rdx
|
||||||
|
18054b716: c7 44 24 30 00 00 00 movl $0x0,0x30(%rsp)
|
||||||
|
18054b71d: 00
|
||||||
|
18054b71e: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054b721: ff 50 30 call *0x30(%rax)
|
||||||
|
18054b724: 85 c0 test %eax,%eax
|
||||||
|
18054b726: 78 10 js 0x18054b738
|
||||||
|
18054b728: 8b 83 98 00 00 00 mov 0x98(%rbx),%eax
|
||||||
|
18054b72e: 2b 44 24 30 sub 0x30(%rsp),%eax
|
||||||
|
18054b732: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18054b736: 5b pop %rbx
|
||||||
|
18054b737: c3 ret
|
||||||
|
18054b738: 8b 83 98 00 00 00 mov 0x98(%rbx),%eax
|
||||||
|
18054b73e: 48 83 c4 20 add $0x20,%rsp
|
||||||
|
18054b742: 5b pop %rbx
|
||||||
|
18054b743: c3 ret
|
||||||
|
18054b744: cc int3
|
||||||
|
18054b745: cc int3
|
||||||
|
18054b746: cc int3
|
||||||
|
18054b747: cc int3
|
||||||
|
18054b748: cc int3
|
||||||
|
18054b749: cc int3
|
||||||
|
18054b74a: cc int3
|
||||||
|
18054b74b: cc int3
|
||||||
|
18054b74c: cc int3
|
||||||
|
18054b74d: cc int3
|
||||||
|
18054b74e: cc int3
|
||||||
|
18054b74f: cc int3
|
||||||
|
18054b750: 48 8b c4 mov %rsp,%rax
|
||||||
|
18054b753: 48 89 50 10 mov %rdx,0x10(%rax)
|
||||||
|
18054b757: 55 push %rbp
|
||||||
|
18054b758: 57 push %rdi
|
||||||
|
18054b759: 48 83 ec 68 sub $0x68,%rsp
|
||||||
|
18054b75d: 83 79 28 00 cmpl $0x0,0x28(%rcx)
|
||||||
|
18054b761: 41 8b e9 mov %r9d,%ebp
|
||||||
|
18054b764: 48 8b f9 mov %rcx,%rdi
|
||||||
|
18054b767: 0f 8e b6 01 00 00 jle 0x18054b923
|
||||||
|
18054b76d: 48 89 70 e8 mov %rsi,-0x18(%rax)
|
||||||
|
18054b771: 33 f6 xor %esi,%esi
|
||||||
|
18054b773: 4c 89 60 e0 mov %r12,-0x20(%rax)
|
||||||
|
18054b777: 44 8b e6 mov %esi,%r12d
|
||||||
|
18054b77a: 4c 89 68 d8 mov %r13,-0x28(%rax)
|
||||||
|
18054b77e: 4d 63 e8 movslq %r8d,%r13
|
||||||
|
18054b781: 45 85 c9 test %r9d,%r9d
|
||||||
|
18054b784: 0f 8e 8a 01 00 00 jle 0x18054b914
|
||||||
|
18054b78a: 48 89 58 18 mov %rbx,0x18(%rax)
|
||||||
|
18054b78e: 4c 89 70 d0 mov %r14,-0x30(%rax)
|
||||||
|
18054b792: 4c 89 78 c8 mov %r15,-0x38(%rax)
|
||||||
|
18054b796: 4c 8b bc 24 a0 00 00 mov 0xa0(%rsp),%r15
|
||||||
|
18054b79d: 00
|
||||||
|
18054b79e: 66 90 xchg %ax,%ax
|
||||||
|
18054b7a0: 80 7f 40 00 cmpb $0x0,0x40(%rdi)
|
||||||
|
18054b7a4: 75 1d jne 0x18054b7c3
|
||||||
|
18054b7a6: 4d 85 ff test %r15,%r15
|
||||||
|
18054b7a9: 74 18 je 0x18054b7c3
|
||||||
|
18054b7ab: 49 8b 4f 58 mov 0x58(%r15),%rcx
|
||||||
|
18054b7af: 33 d2 xor %edx,%edx
|
||||||
|
18054b7b1: ff 15 69 ed 65 01 call *0x165ed69(%rip) # 0x181baa520
|
||||||
|
18054b7b7: 85 c0 test %eax,%eax
|
||||||
|
18054b7b9: 75 08 jne 0x18054b7c3
|
||||||
|
18054b7bb: 49 8b cf mov %r15,%rcx
|
||||||
|
18054b7be: e8 7d fc ff ff call 0x18054b440
|
||||||
|
18054b7c3: 83 7f 28 00 cmpl $0x0,0x28(%rdi)
|
||||||
|
18054b7c7: 7f 04 jg 0x18054b7cd
|
||||||
|
18054b7c9: 8b de mov %esi,%ebx
|
||||||
|
18054b7cb: eb 38 jmp 0x18054b805
|
||||||
|
18054b7cd: 80 7f 40 00 cmpb $0x0,0x40(%rdi)
|
||||||
|
18054b7d1: 75 2c jne 0x18054b7ff
|
||||||
|
18054b7d3: 48 8b 4f 10 mov 0x10(%rdi),%rcx
|
||||||
|
18054b7d7: 48 8d 94 24 80 00 00 lea 0x80(%rsp),%rdx
|
||||||
|
18054b7de: 00
|
||||||
|
18054b7df: 89 b4 24 80 00 00 00 mov %esi,0x80(%rsp)
|
||||||
|
18054b7e6: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054b7e9: ff 50 30 call *0x30(%rax)
|
||||||
|
18054b7ec: 85 c0 test %eax,%eax
|
||||||
|
18054b7ee: 78 0f js 0x18054b7ff
|
||||||
|
18054b7f0: 8b 9f 98 00 00 00 mov 0x98(%rdi),%ebx
|
||||||
|
18054b7f6: 2b 9c 24 80 00 00 00 sub 0x80(%rsp),%ebx
|
||||||
|
18054b7fd: eb 06 jmp 0x18054b805
|
||||||
|
18054b7ff: 8b 9f 98 00 00 00 mov 0x98(%rdi),%ebx
|
||||||
|
18054b805: 3b eb cmp %ebx,%ebp
|
||||||
|
18054b807: 0f 4c dd cmovl %ebp,%ebx
|
||||||
|
18054b80a: 85 db test %ebx,%ebx
|
||||||
|
18054b80c: 75 32 jne 0x18054b840
|
||||||
|
18054b80e: 48 8b 84 24 a8 00 00 mov 0xa8(%rsp),%rax
|
||||||
|
18054b815: 00
|
||||||
|
18054b816: 8b 80 b8 01 00 00 mov 0x1b8(%rax),%eax
|
||||||
|
18054b81c: 85 c0 test %eax,%eax
|
||||||
|
18054b81e: 0f 85 de 00 00 00 jne 0x18054b902
|
||||||
|
18054b824: 48 8b 4f 58 mov 0x58(%rdi),%rcx
|
||||||
|
18054b828: ba e8 03 00 00 mov $0x3e8,%edx
|
||||||
|
18054b82d: ff 15 ed ec 65 01 call *0x165eced(%rip) # 0x181baa520
|
||||||
|
18054b833: 85 c0 test %eax,%eax
|
||||||
|
18054b835: 0f 85 c7 00 00 00 jne 0x18054b902
|
||||||
|
18054b83b: e9 ba 00 00 00 jmp 0x18054b8fa
|
||||||
|
18054b840: 80 7f 40 00 cmpb $0x0,0x40(%rdi)
|
||||||
|
18054b844: 74 1a je 0x18054b860
|
||||||
|
18054b846: 48 8b 4f 58 mov 0x58(%rdi),%rcx
|
||||||
|
18054b84a: ba e8 03 00 00 mov $0x3e8,%edx
|
||||||
|
18054b84f: ff 15 cb ec 65 01 call *0x165eccb(%rip) # 0x181baa520
|
||||||
|
18054b855: 3d 02 01 00 00 cmp $0x102,%eax
|
||||||
|
18054b85a: 0f 84 a2 00 00 00 je 0x18054b902
|
||||||
|
18054b860: 48 8b 8f f8 00 00 00 mov 0xf8(%rdi),%rcx
|
||||||
|
18054b867: 4c 8d 44 24 30 lea 0x30(%rsp),%r8
|
||||||
|
18054b86c: 48 89 74 24 30 mov %rsi,0x30(%rsp)
|
||||||
|
18054b871: 8b d3 mov %ebx,%edx
|
||||||
|
18054b873: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054b876: ff 50 18 call *0x18(%rax)
|
||||||
|
18054b879: 85 c0 test %eax,%eax
|
||||||
|
18054b87b: 78 78 js 0x18054b8f5
|
||||||
|
18054b87d: 4d 85 ed test %r13,%r13
|
||||||
|
18054b880: 7e 5f jle 0x18054b8e1
|
||||||
|
18054b882: 4c 8b bc 24 88 00 00 mov 0x88(%rsp),%r15
|
||||||
|
18054b889: 00
|
||||||
|
18054b88a: 49 63 c4 movslq %r12d,%rax
|
||||||
|
18054b88d: 4c 8d 34 85 00 00 00 lea 0x0(,%rax,4),%r14
|
||||||
|
18054b894: 00
|
||||||
|
18054b895: 66 66 66 0f 1f 84 00 data16 data16 nopw 0x0(%rax,%rax,1)
|
||||||
|
18054b89c: 00 00 00 00
|
||||||
|
18054b8a0: 48 8b 8f 00 01 00 00 mov 0x100(%rdi),%rcx
|
||||||
|
18054b8a7: 48 8b 87 88 00 00 00 mov 0x88(%rdi),%rax
|
||||||
|
18054b8ae: 4d 8b 0c f7 mov (%r15,%rsi,8),%r9
|
||||||
|
18054b8b2: 48 8b 54 24 30 mov 0x30(%rsp),%rdx
|
||||||
|
18054b8b7: 4d 03 ce add %r14,%r9
|
||||||
|
18054b8ba: 4c 8b 11 mov (%rcx),%r10
|
||||||
|
18054b8bd: 44 8b 04 b0 mov (%rax,%rsi,4),%r8d
|
||||||
|
18054b8c1: 89 5c 24 28 mov %ebx,0x28(%rsp)
|
||||||
|
18054b8c5: c7 44 24 20 00 00 00 movl $0x0,0x20(%rsp)
|
||||||
|
18054b8cc: 00
|
||||||
|
18054b8cd: 41 ff 52 08 call *0x8(%r10)
|
||||||
|
18054b8d1: 48 ff c6 inc %rsi
|
||||||
|
18054b8d4: 49 3b f5 cmp %r13,%rsi
|
||||||
|
18054b8d7: 7c c7 jl 0x18054b8a0
|
||||||
|
18054b8d9: 4c 8b bc 24 a0 00 00 mov 0xa0(%rsp),%r15
|
||||||
|
18054b8e0: 00
|
||||||
|
18054b8e1: 48 8b 8f f8 00 00 00 mov 0xf8(%rdi),%rcx
|
||||||
|
18054b8e8: 45 33 c0 xor %r8d,%r8d
|
||||||
|
18054b8eb: 8b d3 mov %ebx,%edx
|
||||||
|
18054b8ed: 48 8b 01 mov (%rcx),%rax
|
||||||
|
18054b8f0: ff 50 20 call *0x20(%rax)
|
||||||
|
18054b8f3: 33 f6 xor %esi,%esi
|
||||||
|
18054b8f5: 2b eb sub %ebx,%ebp
|
||||||
|
18054b8f7: 44 03 e3 add %ebx,%r12d
|
||||||
|
18054b8fa: 85 ed test %ebp,%ebp
|
||||||
|
18054b8fc: 0f .byte 0xf
|
||||||
|
18054b8fd: 8f (bad)
|
||||||
|
18054b8fe: 9e sahf
|
||||||
|
18054b8ff: fe .byte 0xfe
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
|||||||
|
-- play_loop.lua : realtime playback with repeat ON, held alive for keep_secs.
|
||||||
|
local keep_secs = 240
|
||||||
|
|
||||||
|
if reaper.GetSetRepeat(0) == 0 then
|
||||||
|
reaper.Main_OnCommand(1068, 0) -- Transport: Repeat ON
|
||||||
|
end
|
||||||
|
reaper.Main_OnCommand(1007, 0) -- Transport: Play
|
||||||
|
|
||||||
|
local t0 = reaper.time_precise()
|
||||||
|
local function keepalive()
|
||||||
|
if reaper.time_precise() - t0 < keep_secs then
|
||||||
|
reaper.defer(keepalive)
|
||||||
|
else
|
||||||
|
reaper.OnStopButton()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
reaper.defer(keepalive)
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
# prd.md — Project Requirements Document
|
||||||
|
|
||||||
|
## 1. Project Overview
|
||||||
|
|
||||||
|
**Name**: `soothe2-re` — bit-exact reverse engineering of **oeksound soothe2** (VST3, Windows x64)
|
||||||
|
|
||||||
|
**Goal**: Reproduce the plugin's DSP core (level detector, mask computation, filter application) in C++17, byte-for-byte identical to the native binary.
|
||||||
|
|
||||||
|
**Status**: ~95% decompiled. Bridge **1.594 dB** stable; structural **2.689 dB** (L/R `e343b0a`, `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0`, `RT_CASC=0` gated). Historical best `0.341` (24mm14) not reachable on current HEAD. Target: **<0.05 dB** (bit-exact gate, requires chain calibration).
|
||||||
|
|
||||||
|
**Golden rule**: Every parameter must have a source (decomp address / live table). Empirical fits must be flagged `EMPIRICAL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
re-tools/
|
||||||
|
├── prd.md ← THIS FILE (project overview)
|
||||||
|
├── README.md ← TOTAL metric source (single source of truth)
|
||||||
|
├── AGENTS.md ← runbook: build, env flags, tooling hazard
|
||||||
|
├── BITEXACT_PLAN.md ← path to byte-exact (3 steps, criteria)
|
||||||
|
├── roadmap.md ← historical B-phase log (archived)
|
||||||
|
│
|
||||||
|
├── dsp/ ← C++17 DSP reconstruction (THE CANON)
|
||||||
|
│ ├── framed_model.{cpp,hpp} ← MAIN: mask-apply chain (empirical bridge)
|
||||||
|
│ ├── render48k.cpp ← 48k/4096 structural pipeline (resample→chain→resample)
|
||||||
|
│ ├── spectral.{cpp,hpp} ← STFT/ISTFT processor + FIR builder
|
||||||
|
│ ├── fn529fe0.{cpp,hpp} ← structural chain 9–19 (FUN_180529fe0)
|
||||||
|
│ ├── fnfaith.{cpp,hpp} ← faithful detector cascade (BLOCKMAP transcription)
|
||||||
|
│ ├── fft{,_plan,_stage}.cpp/hpp ← FFT engine (canonical + RFFT bit-exact)
|
||||||
|
│ ├── twin.{cpp,hpp} ← twin resonator (FUN_180535880, float-parity)
|
||||||
|
│ ├── rt_mask_tables.{hpp,.cpp} ← live IIR A/B coefficients (385K)
|
||||||
|
│ ├── rt_weights.{hpp,.cpp} ← live kWarp/kBand768 tables (44K)
|
||||||
|
│ ├── fftconv.{cpp,hpp} ← FFT convolution FIR application
|
||||||
|
│ ├── vlog.{cpp,hpp} ← fast log2 approximation
|
||||||
|
│ ├── exp2.{cpp,_tables.cpp,hpp} ← fast exp2 approximation
|
||||||
|
│ ├── levelpath.{cpp,hpp} ← level-curve path (xv = log10(am/res))
|
||||||
|
│ ├── freqpath.{cpp,hpp} ← frequency-axis warp/freq-domain helpers
|
||||||
|
│ ├── leveltrack.{cpp,hpp,hpp} ← envelope follower (attack/release tables)
|
||||||
|
│ ├── log2_ln.{cpp,hpp} ← log/ln utilities
|
||||||
|
│ ├── filter.{cpp,hpp} ← detection kernel (legacy)
|
||||||
|
│ ├── detect.{cpp,hpp} ← detector front-end (legacy)
|
||||||
|
│ ├── phase_table.{cpp,hpp} ← phase table for FFT
|
||||||
|
│ ├── ms.hpp ← mid-side helpers
|
||||||
|
│ ├── params.hpp ← parameter struct
|
||||||
|
│ ├── rotor_kernel.hpp ← rotor transform kernel
|
||||||
|
│ ├── rt_div_tables.hpp ← division tables
|
||||||
|
│ ├── soothe_constants.hpp ← decoded constants
|
||||||
|
│ ├── cody_waite.hpp ← Cody-Waite argument reduction
|
||||||
|
│ ├── twiddle_{builder,loader}.cpp/hpp ← FFT twiddle factors
|
||||||
|
│ ├── dsp_ctx.hpp ← DSP context layout
|
||||||
|
│ ├── framed_test.cpp ← CLI bridge renderer (44.1k)
|
||||||
|
│ ├── harness.cpp ← legacy harness
|
||||||
|
│ ├── *_check.cpp ← bit-exact unit test targets
|
||||||
|
│ └── CMakeLists.txt
|
||||||
|
│
|
||||||
|
├── scripts/
|
||||||
|
│ ├── corpus.py ← bridge regression harness (62 cases, --compare)
|
||||||
|
│ ├── corpus_structural.py ← structural chain harness (--vs-bridge)
|
||||||
|
│ ├── rendersnap2.py ← RENDER_FILE stopper (reads from .rpp)
|
||||||
|
│ ├── campaign.py ← parameter sweep cell (~8 min)
|
||||||
|
│ ├── cascade_sim.py ← numpy step 9–19 simulator
|
||||||
|
│ ├── disasm_func.py ← capstone disasm with RIP constants
|
||||||
|
│ ├── iat_name.py ← runtime import resolution
|
||||||
|
│ ├── wine_{chain,stage,ptrace}_trace.py ← ptrace-based live trace
|
||||||
|
│ ├── dump_dispatch.py ← table/state dumper
|
||||||
|
│ ├── probe_{states,mem}.py ← live state/memory probes
|
||||||
|
│ ├── hunt2.py ← ctx-instance hunter
|
||||||
|
│ ├── scan_{pairs,lutsub}.py ← memory diagnostics
|
||||||
|
│ ├── fit_vlaw_{params,by_group}.py ← VLAW law calibration
|
||||||
|
│ ├── lawfit22r.py ← law fitting (VLAW α/β/c)
|
||||||
|
│ ├── mk_{dist,far,multi6}.py ← RPP generators
|
||||||
|
│ └── render_parity.py ← model-vs-render comparison
|
||||||
|
│
|
||||||
|
├── handoff/
|
||||||
|
│ ├── NOTES_LEVEL.md ← live journal (head: 24mm5+)
|
||||||
|
│ ├── NOTES_LEVEL_INDEX.md ← journal index by date/topic
|
||||||
|
│ ├── BLOCKMAP_529fe0.md ← FUN_180529fe0 method map (53K)
|
||||||
|
│ ├── NOTES_TWIN.md ← twin reference
|
||||||
|
│ ├── NOTES_CAPTURE.md ← live-capture protocol
|
||||||
|
│ ├── NEXT_PROMPT.md ← entry point for new sessions
|
||||||
|
│ ├── SESSION_HANDOFF.md ← handoff template
|
||||||
|
│ ├── archive/ ← historical NOTES_LEVEL_*.md, SESSION_HANDOFF_*.md
|
||||||
|
│ ├── nls_dasm/ ← 183 disassembly files (.dis, .bin)
|
||||||
|
│ ├── phase1/ ← phase-1 outputs
|
||||||
|
│ ├── rt*.npy ← captured runtime tables (48k/44.1k)
|
||||||
|
│ └── *.py ← emit/extract/joint scripts
|
||||||
|
│
|
||||||
|
├── *.java ← Ghidra scripts (DumpFuns, ImportRtti…)
|
||||||
|
├── *.{bin,npz,npy,json,txt} ← datasets, dumps, LUTs (mostly outside git)
|
||||||
|
└── soothe-bt/ ← test corpus (~600 renders, outside git)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Build System
|
||||||
|
|
||||||
|
**Generator**: CMake 3.10+, C++17, GCC/Clang with `-O3 -march=native`.
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
# Key targets
|
||||||
|
soothe2_dsp # shared library (all dsp/*.cpp)
|
||||||
|
framed_test # CLI bridge renderer (44.1k)
|
||||||
|
render48k # structural renderer (48k/48000)
|
||||||
|
twin_check # float-parity unit test
|
||||||
|
tables_check # live-table verification
|
||||||
|
fftconv_check # FIR convolution check
|
||||||
|
vlog_check # fast log2 check
|
||||||
|
leveltrack_check # envelope follower check
|
||||||
|
levelpath_check # level-curve path check
|
||||||
|
exp2_check # fast exp2 check
|
||||||
|
fn529fe0_check # structural chain check
|
||||||
|
soothe2_harness # legacy harness
|
||||||
|
```
|
||||||
|
|
||||||
|
**External deps**: `libsamplerate` (render48k only), `pthread`.
|
||||||
|
|
||||||
|
**Tooling hazard**: CMake skips rebuild when source modified within same second. Protocol: `touch` source before build + verify binary mtime.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. DSP Architecture
|
||||||
|
|
||||||
|
### 4.1 Signal Flow (current canon: structural `render48k`)
|
||||||
|
|
||||||
|
```
|
||||||
|
Host 44.1k → resample → 48k → [per-channel processing] → resample → 44.1k Host
|
||||||
|
↓
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ FramedDetector │
|
||||||
|
│ (per 4096 block) │
|
||||||
|
│ │
|
||||||
|
│ am[] ← envelope │
|
||||||
|
│ res[] ← twin resp │
|
||||||
|
│ ↓ │
|
||||||
|
│ lvl_raw = am/res·sf │
|
||||||
|
│ ↓ │
|
||||||
|
│ [RT_VLAW=1]: │
|
||||||
|
│ cut = α·ln1p(L/β) │
|
||||||
|
│ +c [+Δ] │
|
||||||
|
│ mask = 10^(-cut/20)│
|
||||||
|
│ ↓ │
|
||||||
|
│ warp: mask *= │
|
||||||
|
│ kBand·kWarp·res^rp│
|
||||||
|
│ ↓ │
|
||||||
|
│ IIR3 ×2 (bidir) │
|
||||||
|
│ ↓ │
|
||||||
|
│ mask_out → multiply │
|
||||||
|
│ spectrum[k] *= mask │
|
||||||
|
└─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Dual Render Path
|
||||||
|
|
||||||
|
| Path | File | Grid | Use |
|
||||||
|
|------|------|------|-----|
|
||||||
|
| Bridge | `framed_model.cpp` | 44.1k/2048 | Legacy, TOTAL 1.594 (canon) |
|
||||||
|
| Structural | `render48k.cpp` | 48k/4096 L/R | `2.689` (`RT_CASC=0` gated, hist. `0.341`) |
|
||||||
|
|
||||||
|
**Dual-solution env set**: `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0` (chain `RT_CASC=1` adds IIR4 double + FIR min-phase, gated)
|
||||||
|
|
||||||
|
### 4.3 Key Modules
|
||||||
|
|
||||||
|
| Module | Responsibility |
|
||||||
|
|--------|---------------|
|
||||||
|
| `FramedDetector` | Per-band, per-frame mask computation. Holds `am_`, `res_`, `track_` state. |
|
||||||
|
| `SpectralProcessor` | STFT/ISTFT, OLA overlap-add, FIR application modes. |
|
||||||
|
| `fn529fe0` | Structural chain 9–19 (steps 9–19 of FUN_180529fe0): scale→IIR1→IIR2→blend→combine→warp→IIR3→dry/wet. |
|
||||||
|
| `fnfaith` | Faithful detector cascade transcription (BLOCKMAP). |
|
||||||
|
| `twin` | Twin resonator `|2B/A|` — frequency response per band. |
|
||||||
|
| `rt_mask_tables` | Live IIR A/B coefficients (kIIR_A1/A2/A3, kIIR_B1/B2/B3, kRTAtt, kRTRel). |
|
||||||
|
| `rt_weights` | Live warp weights (kBand768, kWarp). |
|
||||||
|
| `fft` | Bit-exact RFFT (th1a90/th2180) with `buf548`/`mask598` tables. |
|
||||||
|
| `fftconv` | FFT-based convolution for FIR application modes. |
|
||||||
|
| `vlog`/`exp2` | Fast polynomial approximations matching plugin精度. |
|
||||||
|
| `leveltrack` | Envelope follower with per-bin attack/release tables. |
|
||||||
|
|
||||||
|
### 4.4 Detector Cascade (529c60)
|
||||||
|
|
||||||
|
```
|
||||||
|
complex_spectrum × twin_response → |z|
|
||||||
|
→ Haar smooth [0.25,0.5,0.25] × n_iters
|
||||||
|
→ peak = max(curve)
|
||||||
|
→ sin_peak = sin(param·30 − 90) · 0.115129 · peak
|
||||||
|
→ curve = max(curve, sin_peak)
|
||||||
|
→ w = -log10(pow(50, ratio·0.001) · ratio·0.001)
|
||||||
|
→ acc = acc·w + curve·(1-w)
|
||||||
|
→ bands_curve = acc
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Environment Flags (experiment control)
|
||||||
|
|
||||||
|
| Flag | Effect |
|
||||||
|
|------|--------|
|
||||||
|
| `RT_VLAW=1` | Two-stage law: `mask = 10^(−(α·ln1p(lvl/β)+c)/20)` |
|
||||||
|
| `RT_SYN=1` | STFT without synthesis window (plugin's actual layer) |
|
||||||
|
| `RT_WIN=0/1/2` | Analysis window: sym-Hann / periodic / rect |
|
||||||
|
| `RT_NOWARP=1` | Skip warp modulation |
|
||||||
|
| `RT_NOIIR3=1` | Skip IIR3 ×2 |
|
||||||
|
| `RT_IIR12=0` | Skip freq-domain IIR1/2 (critical with VLAW) |
|
||||||
|
| `RT_DUMP_BIN=<f>` | Dump tract binary (frame `RT_DUMP_FRAME`) |
|
||||||
|
| `RT_VDBG=1` | Print VLAW computations to stderr |
|
||||||
|
| `RT_FAITHFUL=1` | Use faithful chain (`fnfaith.cpp`) |
|
||||||
|
| `RT_FIRCONV=1/3` | FIR application mode (1=complex-mul, 3=`1.019·mask^1.8345`) |
|
||||||
|
| `RT_ENV=live` | Live envelope from kRTAtt/kRTRel tables |
|
||||||
|
| `RT_KMAP=1` | k-mapping correction (twin/am scaling) |
|
||||||
|
| `RT_EQ=1` | Pre-detector EQ bell |
|
||||||
|
| `RT_LUT_OFF=1` | Skip LUT transform |
|
||||||
|
| `RT_LUT_A/B/G/M` | LUT parameters (A=-24, B=28, gamma=1, mult=4.2) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Test Corpus & Metrics
|
||||||
|
|
||||||
|
**Location**: `/home/m/soothe-bt/` (~600 renders, outside git).
|
||||||
|
|
||||||
|
**Key sets**:
|
||||||
|
|
||||||
|
| Prefix | Content |
|
||||||
|
|--------|---------|
|
||||||
|
| `tone1kq_*` | Single tone, fc-scan, quiet (-18 dBFS) |
|
||||||
|
| `tone1k_*` | Single tone, fc-scan, loud (0 dBFS) |
|
||||||
|
| `dual_b1q_*` | Two tones (500+2000), q 0.1…10 |
|
||||||
|
| `al_*` | Level sweep (fc=1000) |
|
||||||
|
| `comb_*` | 4-band multiband |
|
||||||
|
| `burst500_b1` | Primary reference (burst 500 Hz) |
|
||||||
|
|
||||||
|
**Metric** (Goertzel steady-state):
|
||||||
|
```python
|
||||||
|
err_dB = 20·log1₀( ta(out,1000) / ta(ref,1000) )
|
||||||
|
# trimmed to last 75% of input, matched to plugin render length
|
||||||
|
```
|
||||||
|
|
||||||
|
**Regression guards**:
|
||||||
|
```bash
|
||||||
|
python3 scripts/corpus.py # bridge + guard
|
||||||
|
python3 scripts/corpus_structural.py # structural chain
|
||||||
|
python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25
|
||||||
|
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Current Status (2026-09-02, chain919)
|
||||||
|
|
||||||
|
| Group | Bridge | Structural `RT_CASC=0` (L/R) | Structural `RT_CASC=1` |
|
||||||
|
|-------|--------|-------------------------------|------------------------|
|
||||||
|
| t1kq (fc-scan) | 0.226 | 0.356 | 0.717 |
|
||||||
|
| t1k (loud) | 1.801 | 5.437 | 5.631 |
|
||||||
|
| al (level) | 0.638 | 2.613 | 5.036 |
|
||||||
|
| res | 0.628 | 1.256 | 23.031 |
|
||||||
|
| dual (q-sweep) | 0.726 | 2.042 | 66.642 |
|
||||||
|
| comb (4-band) | 10.149 | 6.004 | 29.172 |
|
||||||
|
| **TOTAL** | **1.594** | **2.689** | **31.653** |
|
||||||
|
|
||||||
|
Historical best structural `0.341` (24mm14, `f40f41e` dual 0.193) not reachable on current HEAD (M/S→L/R + chain).
|
||||||
|
|
||||||
|
**Decoded**:
|
||||||
|
- Layer: STFT without synthesis window, per-bin mask multiply
|
||||||
|
- Law: `mask = 10^(−(α·ln1p(lvl/β)+c)/20)` with content-aware Δ
|
||||||
|
- FIR: `exp(0.984·ln(raw))` + Hann + normalize → bit-exact RFFT to df0
|
||||||
|
- Chain 9–19: IIR4 double + FIR min-phase integrated, `generate_iir4_coefs()` (FUN_180533340) added, gated `RT_CASC`
|
||||||
|
|
||||||
|
**Open gaps**:
|
||||||
|
1. **Chain 9–19** (priority #1): Integrated but uncalibrated (input format level vs cut, blocker `ph*.npz`/live-dump). Target: <0.05.
|
||||||
|
2. **k-mapping** (priority #2): twin/am scaling `k(q≥2)=0.403`.
|
||||||
|
3. **Δ second-peak rule** (priority #3): Content-dependent gain, requires live-dump.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Reference Documents
|
||||||
|
|
||||||
|
| Doc | Content |
|
||||||
|
|-----|---------|
|
||||||
|
| `README.md` | TOTAL metric, reproduction steps |
|
||||||
|
| `AGENTS.md` | Build commands, env flags, hazard, corpus format |
|
||||||
|
| `BITEXACT_PLAN.md` | 3-step plan to byte-exact |
|
||||||
|
| `handoff/BLOCKMAP_529fe0.md` | Method map for FUN_180529fe0 |
|
||||||
|
| `handoff/NOTES_LEVEL.md` | Live working journal |
|
||||||
|
| `handoff/NOTES_LEVEL_INDEX.md` | Journal index |
|
||||||
|
| `handoff/NOTES_TWIN.md` | Twin resonator reference |
|
||||||
|
| `handoff/NOTES_CAPTURE.md` | Live-capture protocol |
|
||||||
|
| `handoff/nls_dasm/` | 183 disassembly files |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Reproduction & Development Protocol
|
||||||
|
|
||||||
|
1. Install soothe2 VST3 (Windows) under yabridge → `dump_soothe.py` → `soothe_mem.bin`
|
||||||
|
2. Ghidra headless: `analyzeHeadless <proj> soothe_x64 -process soothe_mem.bin -noanalysis -postScript <X>.java`
|
||||||
|
3. Renders: `sweep.py` → `.rpp`, `reaper -renderproject` → `.wav`
|
||||||
|
4. Analysis: `render_parity.py` / `corpus.py`
|
||||||
|
5. After C++ edit: `touch` source → `cmake --build dsp/build --target framed_test` → run corpus guards
|
||||||
+38
-180
@@ -1,206 +1,64 @@
|
|||||||
# Roadmap: bit-exact реверс DSP-ядра oeksound soothe2 (v1.1.2)
|
# Roadmap: bit-exact реверс DSP-ядра oeksound soothe2 (v1.1.2)
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
Воспроизвести DSP-путь soothe2 в виде **собираемого standalone C++** с **побайтово
|
Воспроизвести DSP-путь soothe2 в виде **собираемого standalone C++** с **побайтово идентичным** выходом (bit-exact).
|
||||||
идентичным** выходом (bit-exact) на верификационных свипах, с пониманием логики
|
|
||||||
и подписанными функциями.
|
|
||||||
|
|
||||||
## Результат
|
## Результат
|
||||||
`re-tools/dsp/` — `*.cpp/*.h` (классы DSP, подписаны), `CMakeLists.txt`, `harness.cpp`
|
`re-tools/dsp/` — `*.cpp/*.h` (классы DSP), `CMakeLists.txt`, `harness.cpp` (WAV16 → рендер → WAV24), `dsp_notes.md` (карта функция↔смысл), `verify_bit_exact.py`.
|
||||||
(WAV16 → рендер → WAV24), `dsp_notes.md` (карта функция↔смысл, константы, LUT),
|
|
||||||
`verify_bit_exact.py` (прогон всех свипов, побайтовое сравнение).
|
|
||||||
|
|
||||||
## Текущий статус (2026-08-21, P4)
|
## Статус — см. `README.md:13` (единственный источник TOTAL)
|
||||||
|
|
||||||
> **2026-08-21: bit-exact НЕ достигнут** — честная dB-параллель. Два канона:
|
> **Итог 2026-08-28 (24mm14):** применение декодировано до формул `mask=10^(−cut_D/20)`, `cut_D=α·ln1p(lvl/β)+c` (rms ≤0.016, 3 семейства), слой STFT без синтез-окна, dual-корпус 0.193 dB (канон 3.264). Гейт = bit-exact. Остаток: 3 шага `BITEXACT_PLAN.md:1` (каскад 9–19, k-маппинг, Δ-правило). Архив B-фаз ниже свёрнут.
|
||||||
> bridge (эмпирическая погона, корпус mean 1.594 dB, comb 10.1) и СТРУКТУРНАЯ цепь
|
|
||||||
> FUN_180529fe0 на внутренней сетке 48000/4096 (`dsp/build/render48k`, корпус mean
|
|
||||||
> 2.286 dB, comb **6.12** — уже лучше bridge на мультиполосе, res тоже лучше; хуже на
|
|
||||||
> однополосных). Разрыв формы кривой редукции: реальная насыщается (C_max≈0.70),
|
|
||||||
> exp2(−lvl) нет → Шаг 7 захват A/B/γ или статический hunt clamp'а в level-пути.
|
|
||||||
> Путь к байтам — `BITEXACT_PLAN.md` (8 шагов, пере-скоуп Шага 2: acc не имеет
|
|
||||||
> однополосного консюмера; FUN_1805316e0 = writer коэффициентов, НЕ комбинер).
|
|
||||||
> Полный журнал — `handoff/NOTES_LEVEL.md` (апдейты 20j–21d).
|
|
||||||
|
|
||||||
> **21d**: закон редукции найден — АФФИННО в dB (маска≈0.8·lvl^−0.354, γ≈0.344
|
<details><summary>История B-фаз (B.1…P4.3) — свёрнуто, детали → handoff/archive/ + git log</summary>
|
||||||
> decomp); на тонах линейность ±0.13 dB. Но scalar-семейство не закрывает тон+шум
|
|
||||||
> одновременно (res-тенция) → канон не меняем; следующий рычаг — контент-зависимость
|
|
||||||
> (att/rel на флюктуациях, combine-консюмер). Инструментарий RT_DUMP_BIN/RT_DUMP_ALL.
|
|
||||||
|
|
||||||
- **Статический декомп DSP-ядра — закрыт (~95%)**: twin-резонатор, генератор case8,
|
- **Статический декомп ~95%**: twin, generator case8, level-path (0x529fe0/0x563440/0x563a60), mask-apply, IIR-трекеры, FFT-conv (0x535a70), render-loop FUN_18052e260 — `.dis` в `handoff/nls_dasm/`.
|
||||||
level-path (0x529fe0/0x563440/0x563a60), mask-apply (FUN_180529fe0 mono-path), IIR-трекеры,
|
- **Структурная цепь** `dsp/framed_model.cpp::process_band_structural` (48k/4096): `scale→LUT(level)→IIR1→IIR2→mirror→blend/exp2→combine→dual-warp→IIR3×2→dry/wet`; live-таблицы `rt_mask_tables`. Корпус: comb 6.12 (bridge 10.15), res 0.44.
|
||||||
FFT-conv (0x535a70), main render-loop (FUN_18052e260) декодированы; `.dis` в `handoff/nls_dasm/`.
|
- **Численная модель B.10…B.15**: `C=g·LUT(xv)+w·warp^a`, `gain=(1−C)·res^rp`, rp=0.0275·Q^0.2159 (mean 0.175), Pchip LUT (dual 0.027). Канон — `framed_render.py` (исторический).
|
||||||
- **Структурная цепь в C++** (`dsp/framed_model.cpp::process_band_structural`, сетка 48k/4096):
|
- **Фазы P1–P5**: twin float-parity 1.27e-5, FFT-conv, leveltrack, LUT, mask-apply — в основном готово; осталось каскад 9–19 + twin/am + Δ.
|
||||||
`scale→LUT(level-domain)→IIR1→IIR2→mirror→blend/exp2→combine(acc-update)→dual-warp→IIR3 bidirectional×2→dry/wet`;
|
|
||||||
live-таблицы в `dsp/rt_mask_tables.{hpp,cpp}`, `rt_weights.{hpp,cpp}`. Прогон:
|
|
||||||
`scripts/corpus_structural.py`. IIR3 = [fwd+bwd]×2 по декомпу (коммит 1a616e1).
|
|
||||||
- **Результаты структурной цепи (62 кейса)**: comb 6.12 (bridge 10.15), res 0.44 (0.63),
|
|
||||||
t1kq 0.77 / t1k 2.11 / al 0.99 / dual 3.26 (хуже bridge). Детали: NOTES_LEVEL:21c.
|
|
||||||
- **Честная тримнутая метрика** (длина выхода = длина входа) — см. AGENTS.md.
|
|
||||||
- **Цель bit-exact (Phase C) — в работе**: twin-транскрипция в `dsp/` готова и проходит
|
|
||||||
- **Бит-экзактная C++ транскрипция (Phase C)**: twin (float-parity 1.27e-5, tables_check ALL OK),
|
|
||||||
FFT-conv (P1.2/P1.3 fftconv.cpp), маска-канон (P1.4/P2), level-tracker (P2, leveltrack.cpp),
|
|
||||||
LUT (P2.5 levelpath.cpp), mask-apply цепь (P4 framed_model.cpp) — в основном готово.
|
|
||||||
- **ВАЖНО про метрику**: ранние записи "mean=0.160/0.144" считались на выходе НЕтримнутой
|
|
||||||
длины (`nfr*HOP+N`) и были артефактом; честный базлайн для scalar `rp=0.0169` = **0.280**.
|
|
||||||
Все числа в этом файле — на ТРИМНУТОЙ метрике.
|
|
||||||
|
|
||||||
## Историческая численная модель (B-фазы; канон теперь — C++ FramedDetector)
|
### Ключевые находки (итог, свёрнуто)
|
||||||
|
|
||||||
```python
|
- Маска аддитивная `C=g·LUT+w·warp^a` (не мультипликация).
|
||||||
xv = log10(A_k / res_k) # A_k = 2|X_k|/wsum (twin-env), res = |2B/A| case8
|
- `res_power` `gain=(1−C)·res^rp` — закрывает 500Hz residual.
|
||||||
C = G * LUT(xv) + W * warp(f_k)**A # additive mask (НЕ мультипликация warp·LUT)
|
- `0x530d30` дважды NEGATIVE — тилт не оттуда.
|
||||||
gain = max(1 - C, eps) * res_k**rp # rp = rp0 * Q**drp (res_power)
|
- LUT — гамма-монотонная `FUN_180563440` (Pchip немонотонна).
|
||||||
```
|
- Константы: SCALE=1/1024, DB_CONV=8.6859, IIR α=0.1, LCG 0x3cdca.
|
||||||
|
|
||||||
- LUT — Pchip; warp — `0.87·7.942·x/(7.942+x)`, x=f/2000 (аппроксимация runtime-вычисления FUN_180563440);
|
</details>
|
||||||
- Q-dep rp: `rp0=0.0275, drp=0.2159` (mean 0.175); B.15 joint-free-knot LUT: G/W/A/rp0/drp =
|
|
||||||
`0.9752/0.3394/1.0222/0.0254/0.2231` (dual mean 0.027);
|
|
||||||
- Запуск (историч.): `python3 framed_render.py dual`.
|
|
||||||
|
|
||||||
## Статус по фазам
|
## Контракт из manual (`soothe2_ManualFAQ.pdf`)
|
||||||
|
|
||||||
- **Фаза 0** — сделано (OCR стр.30 manual → `pipeline_ocr.txt`, топология ниже).
|
| # | Факт | Следствие |
|
||||||
- **Фаза A (статический RE)** — закрыта ~90%. Карта «что декодировано → где детали»:
|
|
||||||
|
|
||||||
| Компонент | Функция | Детали |
|
|
||||||
|---|---|---|
|
|
||||||
| Twin-резонатор | FUN_180535880/536f90 | `handoff/NOTES_TWIN.md`, `dsp/twin.*` |
|
|
||||||
| Генератор case8 | FUN_180533ec0 (RBJ bell, middle=−2cos w0) | `handoff/SESSION_HANDOFF.md §4` |
|
|
||||||
| Writer коэффициентов | FUN_1805316e0 (17 case, mode→тип полосы) | `NOTES_LEVEL.md`, B.7 |
|
|
||||||
| Level-weight | FUN_180530d30 (численно НЕ даёт tilt) | `NOTES_LEVEL.md`, B.9/B.14 |
|
|
||||||
| LUT-кривая | FUN_180563440 (linear/power-law, 1024 бин) | `handoff/nls_dasm/f_563440.dis`, B.13 |
|
|
||||||
| IIR level-трекеры | FUN_180563ce0 (init: 341 бин, α=0.1) | `handoff/nls_dasm/f_563ce0.dis`, B.13 |
|
|
||||||
| Mask-применитель | FUN_180529fe0 (PRNG, depth, mask 1−C) | `handoff/nls_dasm/f529fe0.dis` |
|
|
||||||
| FFT / twiddle / Cody-Waite | FUN_180008140 / FUN_180039b00 / 1801de760 | `notes_giant_fft.md`, `dsp/fft*` |
|
|
||||||
|
|
||||||
- **Фаза B (численная модель)** — пройдена до B.15: от B.10 (rmse 0.109 dB) до full frame-рендера
|
|
||||||
Q-dep rp (0.175) и joint free-knot LUT (dual 0.027). Канон — `framed_render.py`.
|
|
||||||
- **Фаза C (C++ bit-exact)** — in progress: `dsp/twin.*` готов и проходит float-parity (1.27e-5);
|
|
||||||
`dsp/{tables_data, fftconv, leveltrack}.cpp` + `dsp_ctx.hpp` готовы (P1-P2 структурно);
|
|
||||||
`verify_bit_exact.py` даёт базлайн (все DIFF — ожидаемо до подключения живой маски).
|
|
||||||
Оставшийся код по фазам:
|
|
||||||
|
|
||||||
| Фаза | Компонент | Статус |
|
|
||||||
|---|---|---|
|
|
||||||
| P1.5 | live-коэффициенты level-tracker A[] + скаляры RPP | ✅ DONE (rtctx_live.json, leveltrack_data.hpp) |
|
|
||||||
| P2 | mask-accumulator combine kernels (8d60/5a20/3c40) | ✅ DONE (levelpath.cpp combine_sub/acc_add/acc_fma) |
|
|
||||||
| P2.5 | twin-mask factory (56e3e0) + band LUT apply (563a60) | ✅ DONE (levelpath.cpp) |
|
|
||||||
| P3 | бит-экзактная base-CRT FFT | ⚠️ partial: архитектура размечена, vlog(ln) транскрибирован; butterfly+shuffle+plan-gen осталось |
|
|
||||||
| P4 | render-loop 52e260 + связка mask→FFT-conv + full pipeline | ✅ **существенно продвинут (2026-08-21)**: mono-path FUN_180529fe0 декодирован и транскрибирован на внутренней сетке 48k/4096 (`render48k`, LUT в level-домене, IIR3 bidirectional по декомпу); полный структурный корпус: comb **6.12** (bridge 10.15), res 0.44; однополосные хуже bridge — нужен шаг насыщения кривой редукции. Combine-семантика декодирована до thunk'ов; acc не имеет однополосного консюмера (пере-скоуп). Остаток: точка потребления acc/f6f8, PRNG-пролог, бит-экзактный exp2, FFT-conv (P3), SR-хвосты |
|
|
||||||
| P5 | стерео M8 (link/balance/LR-vs-MS) + multiband combine 5316e0 | TODO (в скоупе, не начат) |
|
|
||||||
|
|
||||||
Обновлённая оценка (2026-08-21): структурно ~80% готово — mask-apply цепь FUN_180529fe0
|
|
||||||
декодирована (scale→LUT→IIR1/2→exp2→combine→warp→IIR3-bidi→dry/wet→FFT-conv) и перенесена
|
|
||||||
в C++ на внутренней сетке, live-таблицы (IIR A/B, warp, band768, PRNG LUT) извлечены,
|
|
||||||
полный корпус гоняется. Осталось: (а) полная бит-экзактная FFT (butterfly+shuffle+plan-gen
|
|
||||||
— недели), (б) насыщение кривой редукции (Шаг 7 захват A/B/γ или clamp-hunt в level-пути),
|
|
||||||
(в) точка потребления combine acc/f6f8, (г) PRNG-пролог + FFT-conv + бит-экзактный exp2,
|
|
||||||
(д) стерео M8 (P5).
|
|
||||||
|
|
||||||
## Ключевые находки (итог)
|
|
||||||
|
|
||||||
- **Маска аддитивная**: `C = g·LUT(xv) + w·warp^a`, НЕ мультипликация warp·LUT (та проваливается >10 dB).
|
|
||||||
warp-terм ≈ `warp^3.14·w` — кратный каскад freq-axis ∏0x540688/FFT-conv.
|
|
||||||
- **res_power**: `gain = (1−C)·res^rp` — закрывает 500Hz-residual для q0.1/q1 (envRmse 0.64→0.05 dB).
|
|
||||||
- **0x530d30 дважды NEGATIVE**: спектральные веса численно ≈0 (10⁻⁴⁰…10⁻²⁰⁰) → тилт 2000-vs-500
|
|
||||||
из них НЕ идёт (подтверждено при N=1024 и N=2048).
|
|
||||||
- **Форма LUT**: Pchip-эмпирика была немонотонна (провал при xv≈−0.5); настоящая кривая
|
|
||||||
FUN_180563440 — гамма/монотонная; при замене на монотонную (free-knot или структурную)
|
|
||||||
q1@2000 закрывается до 0.000.
|
|
||||||
- **Константы из дампа**: SCALE=1/1024, 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, LCG_OFFSET=0x3cdca.
|
|
||||||
|
|
||||||
## Контракт из manual (`soothe2_ManualFAQ.pdf`, v1.0.0)
|
|
||||||
|
|
||||||
| # | Факт | Следствие для DSP |
|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| M1 | Soft/Hard: режим меняет всё; depth 3.0 в soft ≠ hard (стр.7) | mode = глобальный препроцессор детектора |
|
| M1 | Soft/Hard меняет всё | mode = препроцессор детектора |
|
||||||
| M2 | Depth — референтный dB (−18..18), реальная глубина до 60dB (стр.8) | вход depthcurve-LUT; clip 60dB |
|
| M2 | Depth ±18, до 60 dB | depthcurve-LUT, clip 60 dB |
|
||||||
| M3 | Sharpness: выше = глубже и уже (стр.8) | Q_notch растёт с sharp |
|
| M5 | Attack/Release частотно-зависимы | Env — частотный масштаб |
|
||||||
| M4 | Selectivity: 0 = «everything must go» (стр.9) | отбор/порог пиков, число нотчей НЕ лимитирует |
|
| M8 | Stereo link/balance/L-R vs M/S | детектор на sum |
|
||||||
| M5 | Attack/Release: реальные времена частотно-зависимы (стр.11) | Env НЕ один one-pole; нужен частотный масштаб |
|
| M10 | Банды: 2 cut + 4 general | `FilterGraph<float,6,0x400>` |
|
||||||
| M6 | Oversample = расчёт reduction-фильтра в высшем разрешении (стр.11) | интерполяция детекторной сетки → фильтра |
|
|
||||||
| M7 | Resolution = частота обновления детекции/фильтра (стр.11) | период пересчёта коэффициентов |
|
|
||||||
| M8 | Stereo: link 100% = сумма каналов; 0% = dual mono; balance; 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, сумма band-кривых = белая (стр.17) | `red(f)=base+ΣW_i·H_i` |
|
|
||||||
| M12 | Sidechain; Input trim — скрытый предусилитель анализа (стр.26) | отдельный вход/гейн анализа |
|
|
||||||
|
|
||||||
## Топология пайплайна (Приложение A, стр.30 — `pipeline_ocr.txt`)
|
## Топология пайплайна (Приложение A, стр.30)
|
||||||
|
|
||||||
```
|
```
|
||||||
main inputs ──┐
|
main/sidechain → input trim → analysis (depth/sharpness/selectivity; EQ per-band)
|
||||||
sidechain ────┤ (sidechain enabled?)
|
→ stereo apply + balance → ms encode? → processing (M/S) → mix → bypass → output
|
||||||
▼
|
|
||||||
input trim ──► analysis (детекция; depth/sharpness/selectivity; EQ из per-band balances)
|
|
||||||
│
|
|
||||||
stereo apply + balance (channel link + global balance)
|
|
||||||
│
|
|
||||||
ms encode? ──► processing (нотч-синтез в M/S) ──► ms decode?
|
|
||||||
│
|
|
||||||
mix ──► bypass? ──► output
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- mid/side ручка: обработка в M/S-области — стерео-путь подтверждён диаграммой.
|
## Журнал вех (свёрнуто)
|
||||||
- GUI/visualisation — побочная ветвь, в DSP-ядро не входит (кроме параметров).
|
|
||||||
|
|
||||||
## Журнал вех (детали — в handoff/ и git log)
|
| Веха | Дата | Суть |
|
||||||
|
|
||||||
| Веха | Дата | Суть / ключевые числа |
|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| B.1 | 08-17 | C++ каркас `dsp/*` (twin, spectral, filter, detect, ms, fft, cody, twiddle, harness) |
|
| B.1–B.6 | 08-17 | C++ каркас, twin, dispatch, band-структура |
|
||||||
| B.2 | 08-17 | `libsoothe2_dsp.so` + harness WAV16→WAV24; фиксы WAV-алиасинга / in-place |
|
| B.10–B.15 | 08-18…19 | Модели C/LUT, bridge `C=g·LUT+w·warp^a` (0.236), res_power, dB-parity 0.268 |
|
||||||
| B.3 | 08-17 | TODO: twiddle-loader FUN_18014ec20 (Dekker+Cody-Waite), bit-reverse 18003b6c0, транскрипция fft.cpp |
|
| P4.1–P4.3 | 08-20 | Декомп FUN_180529fe0, live-таблицы, dual-warp |
|
||||||
| B.4 | 08-17 | burst500: corr 0.995, SNR 19.8 dB, diff −0.065 dB; детектор = уровнезависимый floor |
|
|
||||||
| B.5 | 08-17 | FUN_180535880/536f90 — реальный SSE-код в дампе; .vst3 совпадает с дампом в `.text` |
|
|
||||||
| B.6 | 08-17 | Dispatch-модель исправлена (у каждого стаба своя таблица); полный twin-алгоритм; артефакты `det_*.dis` |
|
|
||||||
| B.7 | 08-18 | band-структура (+0x800 mode/0x804 freq/0x808 sens/0x80c Q), writer 5316e0 (17 case); XML mode=1 → case8 (RBJ bell) |
|
|
||||||
| B.8 | 08-18 | Коррекция middle=−2cos(w0); `mask(f)=|2B/A|(fc,Q,gain)`; freq-path 530850 + warp, level-path 56e3e0 |
|
|
||||||
| B.9 | 08-18 | Level-path замкнут (потребитель 529fe0, аккумулятор 0x5407c8); формула 530d30; warp-ось sigmoid; конструктор 535ae0 (0x24=40000) |
|
|
||||||
| B.10 | 08-18 | Единая модель `C=depth·tilt·D0·(L0/res)^p`; парадокс dual_b1q решён (tilt-наклон); rmse 0.109 dB (31 pt); `model_dual.py` |
|
|
||||||
| B.11 | 08-18 | LUT-кривая извлечена (36 pt на одну кривую); PCHIP 10 узлов rmse 0.0718 dB; колено x≈0.58; `model_lut.py` |
|
|
||||||
| B.12 | 08-18 | Bridge-модель `C=g·LUT(xv)+w·warp^a` (a≈π, аддитивно), rmse 0.236; FFT-conv размаплен; freq-axis offline const; `model_fir.py` |
|
|
||||||
| Ph.5 | 08-18 | `render_parity.py`; dB-parity 36 pt rmse 0.268 dB (рефит 0.203); остаток структурный (0x540658) |
|
|
||||||
| B.13 | 08-19 | res_power `gain=(1−C)·res^rp` (500Hz resid решён); декомп 563440/563ce0/529fe0; константы из дампа |
|
|
||||||
| B.14 | 08-19 | **Артефакт-коррекция** (честная тримнутая метрика; базлайн scalar=0.280); Q-dep rp → mean 0.175; 530d30 повторно NEGATIVE (N=2048) |
|
|
||||||
| B.15 | 08-19 | Диагноз LUT-немонотонности → joint free-knot LUT (8 узлов, dual+al_*) → **dual mean≈0.027 dB**; остаток al_* lv24 dC=+0.10 |
|
|
||||||
| P4.1 | 08-20 | Полный декомп mono-path FUN_180529fe0; real mask-цепь в C++ (IIR leaky + acc + live LUT); t1kq err 0.5 dB; `framed_test` |
|
|
||||||
| P4.2 | 08-20 | Live-таблицы извлечены (`rt_mask_tables` IIR A/B, warp, band768, PRNG LUT); bigkernel 0x26b820=exp2 семантика залочена (`exp2(-mask)·blend`) |
|
|
||||||
| P4.3 | 08-20 | Dual-warp шаг 6 транскрибирован (mask·=band768, ·=warp) → comb 500/3000 починены; t1kq −0.43 dB; comb ≤6.4 dB |
|
|
||||||
|
|
||||||
## Риски
|
Полный журнал — `handoff/NOTES_LEVEL_INDEX.md` + `git log`.
|
||||||
|
|
||||||
- **FFT bit-exact**: DSP-STFT FFT найден и транскрибирован; главный нерешённый риск — порядок
|
|
||||||
double-операций в dispatch-ядрах при побайтовом сравнении.
|
|
||||||
- **Хост-зависимость**: точный размер буфера/фрейма (рендеры при разных RENDER_RANGE должны
|
|
||||||
давать одинаковые байты).
|
|
||||||
- **Stereo**: все текущие свипы mono; для bit-exact графа M8 нужны стерео-рендеры (link/balance/ms).
|
|
||||||
- **Честная метрика**: тримнутая длина выхода обязательна (артефакт B.14); fit-цели проверять на
|
|
||||||
`render_trim`/`framed_render.synthe`.
|
|
||||||
|
|
||||||
## Открытые вопросы
|
|
||||||
|
|
||||||
1. **Форма LUT в зоне xv<−0.3**: joint-fit даёт dual≤0.07, но al_* lv24 dC=+0.10 — нужна структурная
|
|
||||||
кривая FUN_180563440 (linear/power-law) вместо Pchip-эмпирики.
|
|
||||||
2. **warp-терм**: эмпирическая формула — аппроксимация runtime-вычисления FUN_180563440;
|
|
||||||
для bit-exact заменить на реальный расчёт.
|
|
||||||
3. **Стерео-верификация (M8)** — приоритет mono-путь или сразу стерео-граф.
|
|
||||||
4. **0x540658 window** — ✅ **ЗАКРЫТ 2026-08-19**: live-захват через registry heartbeat
|
|
||||||
(`{u64 count, u64 ptr}` run at psy. 0x29b06c0) во время offline-рендера. Окно = registry[01]
|
|
||||||
(8193 f32, 0.5→1.0, сатурация), freq-axis = registry[0d] (0..23988 Hz, spacing 11.713 ⇒
|
|
||||||
**внутренний SR=48000**), веса [03][04][05][06] (WA/WB/WC/WD). Сохранено: `handoff/rtwin_freq_44100.npy`,
|
|
||||||
`rtfreqaxis_48000_internal.npy`, `rtwa/rtwb/rtwc/rtwd_*.npy`. Инфра: `rtsnap_fast.py`,
|
|
||||||
`rtsnap2.py` (двухточечный diff подтвердил стабильность таблиц), `findctx.py`. Полное описание:
|
|
||||||
`handoff/NOTES_CAPTURE.md`. vptr/44100-marker объект-скан (rtobj) — тупик (производная vtable
|
|
||||||
не адресуется статически; регистр-бикон — рабочий путь).
|
|
||||||
|
|
||||||
## Где лежат детали
|
## Где лежат детали
|
||||||
|
|
||||||
- `handoff/SESSION_HANDOFF.md` — инвентарь декомпа (§0), трансляция/ключевые адреса (§2), Phase-5 план (§6).
|
- `README.md:13` — актуальный статус (TOTAL).
|
||||||
- `handoff/NOTES_TWIN.md` — twin-резонатор, caller, grid/oversample.
|
- `handoff/NOTES_LEVEL.md` (живая голова) + `handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md` — журнал.
|
||||||
- `handoff/NOTES_LEVEL.md` — level-path, LUT-нога, res_power протокол, спектральные веса.
|
- `handoff/BLOCKMAP_529fe0.md` — карта метода 9–19.
|
||||||
- `handoff/nls_dasm/` — 120+ дизассемблей (f_563440, f_563ce0, f529fe0, twin, iface, fft).
|
- `handoff/NOTES_TWIN.md` — twin краткая справка.
|
||||||
- `notes_giant_fft.md` — FFT-планировщики/ядра/twiddle/Cody-Waite.
|
- `handoff/NOTES_CAPTURE.md` — registry heartbeat.
|
||||||
- `git log` — полная журнальная хронология с номерами коммитов.
|
- `notes_giant_fft.md` — FFT/twiddle.
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""amseries.py — amplitude sweep via rendersnap: scratch vs input level.
|
||||||
|
Gives ka = d(scr)/d(ln am) of the log-domain law."""
|
||||||
|
import glob
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
CASES = [('t_-24', -24.0), ('t_-12', -12.0), ('t_0', 0.0), ('t_24', 24.0)]
|
||||||
|
|
||||||
|
|
||||||
|
def steady(tag):
|
||||||
|
fs = sorted(glob.glob('/tmp/opencode/rendersnap/phase*.npz'))
|
||||||
|
for f in fs[::-1]:
|
||||||
|
z = np.load(f)
|
||||||
|
sc = z['0x540628']
|
||||||
|
if sc[85] < -1e-4:
|
||||||
|
return float(sc[85]), float(sc[85])
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
out = []
|
||||||
|
for tag, db in CASES:
|
||||||
|
rpp = f'/tmp/opencode/{tag}.rpp'
|
||||||
|
subprocess.run(['timeout', '100', 'python3',
|
||||||
|
'/home/m/re-tools/scripts/rendersnap.py', rpp],
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
st = steady(tag)
|
||||||
|
if st:
|
||||||
|
out.append((db, st))
|
||||||
|
print(tag, db, 'scr43=%.4f scr171=%.4f' % st, flush=True)
|
||||||
|
else:
|
||||||
|
print(tag, 'NO STEADY', flush=True)
|
||||||
|
if len(out) >= 2:
|
||||||
|
(d1, s1), (d2, s2) = out[0], out[-1]
|
||||||
|
ka = (s2[0] - s1[0]) / ((d2 - d1) * np.log(10) / 20)
|
||||||
|
ka171 = (s2[1] - s1[1]) / ((d2 - d1) * np.log(10) / 20)
|
||||||
|
print('ka(d scr/d ln am) @85: %.4f' % ka)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"al_12": 0.6589760322215087,
|
||||||
|
"al_18": 0.8869285064658652,
|
||||||
|
"al_24": 0.9872335108976188,
|
||||||
|
"al_3": 0.21467895021449704,
|
||||||
|
"al_6": 0.36768989474153474,
|
||||||
|
"al_9": 0.5175959713969748,
|
||||||
|
"comb_1000": -0.8390262089726862,
|
||||||
|
"comb_1500": -0.8112870378396803,
|
||||||
|
"comb_2000": -0.809711515922245,
|
||||||
|
"comb_3000": -0.8085898984121008,
|
||||||
|
"comb_500": -0.8092538987382586,
|
||||||
|
"dual_0.1_2000": -0.08995281533909788,
|
||||||
|
"dual_0.1_500": -0.05320412461191143,
|
||||||
|
"dual_0.2_2000": 0.3019781559654401,
|
||||||
|
"dual_0.2_500": -0.053358134264814706,
|
||||||
|
"dual_0.3_2000": 0.2902583798578576,
|
||||||
|
"dual_0.3_500": -0.05348688556617939,
|
||||||
|
"dual_0.5_2000": 0.19650867858105603,
|
||||||
|
"dual_0.5_500": -0.05387091733344582,
|
||||||
|
"dual_0.7_2000": 0.18642830232473012,
|
||||||
|
"dual_0.7_500": -0.05441615588199915,
|
||||||
|
"dual_1.0_2000": 0.23254813837808486,
|
||||||
|
"dual_1.0_500": -0.05551608061654063,
|
||||||
|
"dual_1.5_2000": 0.3084791235429092,
|
||||||
|
"dual_1.5_500": -0.057043442345604364,
|
||||||
|
"dual_10.0_2000": 0.4377055060111567,
|
||||||
|
"dual_10.0_500": 0.20966855072081148,
|
||||||
|
"dual_2.0_2000": 0.3539423473152209,
|
||||||
|
"dual_2.0_500": -0.0574902922094877,
|
||||||
|
"dual_3.0_2000": 0.3970249956345811,
|
||||||
|
"dual_3.0_500": -0.05247116979493978,
|
||||||
|
"dual_5.0_2000": 0.42379656083975153,
|
||||||
|
"dual_5.0_500": -0.010273028233688034,
|
||||||
|
"res_300": 0.740945052805549,
|
||||||
|
"res_400": 0.3420632609886434,
|
||||||
|
"res_450": 0.14758272030066333,
|
||||||
|
"res_475": 0.051083439669293566,
|
||||||
|
"res_490": -0.007634204659142415,
|
||||||
|
"res_500": -0.046625742175670996,
|
||||||
|
"res_510": -0.08701341292655208,
|
||||||
|
"res_525": -0.14710395674869248,
|
||||||
|
"res_550": -0.2500832704002971,
|
||||||
|
"res_600": -0.47125422339018486,
|
||||||
|
"res_700": -1.0367358917741667,
|
||||||
|
"t1k_1000": -0.5485503441283766,
|
||||||
|
"t1k_1050": -0.3167571986018916,
|
||||||
|
"t1k_1100": 0.17418996613796175,
|
||||||
|
"t1k_1200": 0.16460873061812337,
|
||||||
|
"t1k_1500": -0.2608862087302641,
|
||||||
|
"t1k_2000": 0.4691205643000097,
|
||||||
|
"t1k_500": -0.10662852895220419,
|
||||||
|
"t1k_678.7611083984375": 0.027561491468636564,
|
||||||
|
"t1k_800": 0.4822946693646578,
|
||||||
|
"t1k_900": 0.30705968131126005,
|
||||||
|
"t1k_950": -0.28077425246942284,
|
||||||
|
"t1kq_1000": -0.21168039641542824,
|
||||||
|
"t1kq_1050": -0.0011503254358071785,
|
||||||
|
"t1kq_1100": 0.4344054852348732,
|
||||||
|
"t1kq_1200": -0.11613738787651275,
|
||||||
|
"t1kq_800": -0.05292751629888988,
|
||||||
|
"t1kq_900": 0.54236470368085,
|
||||||
|
"t1kq_950": 0.026290891468426707
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"al_12": 0.6589760322215087,
|
||||||
|
"al_18": 0.8869285064658652,
|
||||||
|
"al_24": 0.9872335108976188,
|
||||||
|
"al_3": 0.21467895021449704,
|
||||||
|
"al_6": 0.36768989474153474,
|
||||||
|
"al_9": 0.5175959713969748,
|
||||||
|
"comb_1000": -0.8390262089726862,
|
||||||
|
"comb_1500": -0.8112870378396803,
|
||||||
|
"comb_2000": -0.809711515922245,
|
||||||
|
"comb_3000": -0.8085898984121008,
|
||||||
|
"comb_500": -0.8092538987382586,
|
||||||
|
"dual_0.1_2000": -0.08995281533909788,
|
||||||
|
"dual_0.1_500": -0.05320412461191143,
|
||||||
|
"dual_0.2_2000": 0.3019781559654401,
|
||||||
|
"dual_0.2_500": -0.053358134264814706,
|
||||||
|
"dual_0.3_2000": 0.2902583798578576,
|
||||||
|
"dual_0.3_500": -0.05348688556617939,
|
||||||
|
"dual_0.5_2000": 0.19650867858105603,
|
||||||
|
"dual_0.5_500": -0.05387091733344582,
|
||||||
|
"dual_0.7_2000": 0.18642830232473012,
|
||||||
|
"dual_0.7_500": -0.05441615588199915,
|
||||||
|
"dual_1.0_2000": 0.23254813837808486,
|
||||||
|
"dual_1.0_500": -0.05551608061654063,
|
||||||
|
"dual_1.5_2000": 0.3084791235429092,
|
||||||
|
"dual_1.5_500": -0.057043442345604364,
|
||||||
|
"dual_10.0_2000": 0.4377055060111567,
|
||||||
|
"dual_10.0_500": 0.20966855072081148,
|
||||||
|
"dual_2.0_2000": 0.3539423473152209,
|
||||||
|
"dual_2.0_500": -0.0574902922094877,
|
||||||
|
"dual_3.0_2000": 0.3970249956345811,
|
||||||
|
"dual_3.0_500": -0.05247116979493978,
|
||||||
|
"dual_5.0_2000": 0.42379656083975153,
|
||||||
|
"dual_5.0_500": -0.010273028233688034,
|
||||||
|
"res_300": 0.740945052805549,
|
||||||
|
"res_400": 0.3420632609886434,
|
||||||
|
"res_450": 0.14758272030066333,
|
||||||
|
"res_475": 0.051083439669293566,
|
||||||
|
"res_490": -0.007634204659142415,
|
||||||
|
"res_500": -0.046625742175670996,
|
||||||
|
"res_510": -0.08701341292655208,
|
||||||
|
"res_525": -0.14710395674869248,
|
||||||
|
"res_550": -0.2500832704002971,
|
||||||
|
"res_600": -0.47125422339018486,
|
||||||
|
"res_700": -1.0367358917741667,
|
||||||
|
"t1k_1000": -0.5485503441283766,
|
||||||
|
"t1k_1050": -0.3167571986018916,
|
||||||
|
"t1k_1100": 0.17418996613796175,
|
||||||
|
"t1k_1200": 0.16460873061812337,
|
||||||
|
"t1k_1500": -0.2608862087302641,
|
||||||
|
"t1k_2000": 0.4691205643000097,
|
||||||
|
"t1k_500": -0.10662852895220419,
|
||||||
|
"t1k_678.7611083984375": 0.027561491468636564,
|
||||||
|
"t1k_800": 0.4822946693646578,
|
||||||
|
"t1k_900": 0.30705968131126005,
|
||||||
|
"t1k_950": -0.28077425246942284,
|
||||||
|
"t1kq_1000": -0.21168039641542824,
|
||||||
|
"t1kq_1050": -0.0011503254358071785,
|
||||||
|
"t1kq_1100": 0.4344054852348732,
|
||||||
|
"t1kq_1200": 1.1724811230541312,
|
||||||
|
"t1kq_800": 0.6775178072048177,
|
||||||
|
"t1kq_900": 0.54236470368085,
|
||||||
|
"t1kq_950": 0.026290891468426707
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""campaign.py — one config-family cell: build rpp variants, clean-render refs,
|
||||||
|
capture deepest scratch, extract tracts, fit softplus law.
|
||||||
|
Usage: campaign.py <base_rpp> <band_freq> <band_q> <sens> <in_prefix> <n_drives> <out_prefix>
|
||||||
|
Inputs must exist as <in_prefix><drive>_in.wav for drive in list.
|
||||||
|
"""
|
||||||
|
import subprocess, os, re, sys, time, glob
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0,'/home/m/re-tools/scripts')
|
||||||
|
|
||||||
|
def sh(cmd):
|
||||||
|
return subprocess.run(cmd,shell=True,capture_output=True,text=True).stdout
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
import glob as g
|
||||||
|
for p in g.glob('/proc/[0-9]*'):
|
||||||
|
pid=int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd=open('/proc/%d/cmdline'%pid,'rb').read().replace(b'\0',b' ').decode('utf8','replace')
|
||||||
|
maps=open('/proc/%d/maps'%pid).read()
|
||||||
|
except Exception: continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd: return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
def main():
|
||||||
|
base,freq,q,sens,pref,drives,outp=sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4],sys.argv[5],sys.argv[6].split(','),sys.argv[7]
|
||||||
|
# build rpps
|
||||||
|
for a in drives:
|
||||||
|
src=open(base).read()
|
||||||
|
src=src.replace('FILE "/home/m/soothe-bt/tone1k.wav"',f'FILE "{pref}{a}_in.wav"')
|
||||||
|
src=re.sub(r'RENDER_FILE "[^"]*"',f'RENDER_FILE "{outp}/{a}_ref.wav"',src)
|
||||||
|
tmp=f'{outp}/{a}_tmp.rpp'
|
||||||
|
open(tmp,'w').write(src)
|
||||||
|
out=sh(f'cd /home/m/re-tools && python3 patchparam.py {tmp} {outp}/{a}.rpp '
|
||||||
|
f'"band1 freq"={freq} "band1 q"={q}')
|
||||||
|
if 'patched' not in out and 'warning' not in out:
|
||||||
|
print(f'drive {a}: patch issue: {out[:100]}')
|
||||||
|
sh("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1")
|
||||||
|
# render refs cleanly
|
||||||
|
for a in drives:
|
||||||
|
wav=f'{outp}/{a}_ref.wav'
|
||||||
|
if os.path.exists(wav): os.remove(wav)
|
||||||
|
pr=subprocess.Popen(['/usr/bin/reaper','-nosplash','-ignoreerrors','-renderproject',f'{outp}/{a}.rpp'],
|
||||||
|
stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
|
||||||
|
t0=time.time()
|
||||||
|
while time.time()-t0<30 and pr.poll() is None:
|
||||||
|
time.sleep(0.2)
|
||||||
|
for _ in range(60):
|
||||||
|
if pr.poll() is not None: break
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(f'drive {a}: ref rc={pr.poll()} size={os.path.getsize(wav) if os.path.exists(wav) else 0}',flush=True)
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""cascade_sim.py — структурный симулятор тракта маски soothe2 (float-путь).
|
||||||
|
|
||||||
|
Цель (24mm3): воспроизвести scr(f)=дизайн-сигнал детектора; применённая
|
||||||
|
маска = exp(γ·scr), γ=1.760561 (точно), trk@688=exp(scr@628) бит-в-бит.
|
||||||
|
|
||||||
|
Структура канонической цепи (BLOCKMAP 24hh/24ii + 24mm2):
|
||||||
|
шаг 9a: vec698 *= (1 − [54087c]) ; zero при дефолтах
|
||||||
|
шаг 9b: vec6f8 += [54087c]·0.8 ; xmm10=0.8 @1824c3e28
|
||||||
|
шаг 9c: bands_curve_i /= ... divide-ядро ; dst=678i, A/B уточняются
|
||||||
|
шаг 10: vec6f8 = bands_curve_i − ACC_i ; dc40, ACC @таблицы 0x5407c8
|
||||||
|
шаг 11: fma att/rel (тройки re/im/coef) ; коэф @6c8/6e8
|
||||||
|
шаг 12: COPY ; memcpy
|
||||||
|
шаг 13: зеркало 9
|
||||||
|
шаг 14: expf(bands_curve); bands_curve += (−1) ; ПОРЯДОК исправлен 24mm2
|
||||||
|
шаг 15: bands_curve *= track_i ; th2000 array-mul
|
||||||
|
шаг 16: bands_curve *= kWarp@[5406a8]
|
||||||
|
шаг 17: expf ещё раз ; call-site 52b32c
|
||||||
|
пост-17: exp-вариант(140a40) + pow?(140b00)
|
||||||
|
FIR-секция: кривая-float(140b30→1803831c0) + sincos-twiddle(140aa0)
|
||||||
|
|
||||||
|
ЯДРА (структурная фаза — математически точные numpy-эквиваленты;
|
||||||
|
канонический C++ порт = инструкци-точная транскрипция, см. BLOCKMAP 24mm2):
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
GAMMA = 1.760561 # 24mm3: показатель степени, rms фита 0 на чистых кадрах
|
||||||
|
N = 2049 # число бинов полной сетки
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- ядра ----
|
||||||
|
def k_exp(x):
|
||||||
|
"""expf-ядро 180296c80. Структурная фаза: np.exp.
|
||||||
|
Каноническая формула (для C++ порта, FMA-точно):
|
||||||
|
n = fma(log2e_hi=1.4427f, x, 12582912.0f); k = n - MAGIC
|
||||||
|
r = (x - 0.693146f*k) - 1.42861e-06f*k
|
||||||
|
p = (((0.00829172f*r+0.0418735f)*r+0.166674f)*r+0.499994f)*r+1)*r+1
|
||||||
|
out = bits((k<<23) + bits(p)); guard |x|>87.3365 -> slow path
|
||||||
|
"""
|
||||||
|
return np.exp(x)
|
||||||
|
|
||||||
|
|
||||||
|
def k_exp_exact(x):
|
||||||
|
"""Bit-exact expf 180296c80 (BLOCKMAP:569) — float32 FMA poly, guard 87.3365.
|
||||||
|
Python структурный прокси: np.exp (float64) — точный C++ порт в dsp/exp2.cpp
|
||||||
|
через fmaf+бит-манипуляции (1824c...), ошибка <0.5 ulp vs плагин."""
|
||||||
|
return np.exp(np.asarray(x, dtype=np.float64)).astype(np.float32)
|
||||||
|
|
||||||
|
|
||||||
|
def k_div(a, b):
|
||||||
|
"""divide-ядро 1803a06a0: dst = B/A (~0.5 ulp, rcp+таблицы+полином).
|
||||||
|
Структурная фаза: точное деление."""
|
||||||
|
return b / a
|
||||||
|
|
||||||
|
|
||||||
|
def k_div_exact(a, b):
|
||||||
|
"""Bit-exact DIVIDE 1803a06a0 (BLOCKMAP:580) — rcp+quant+vpermps+poly.
|
||||||
|
Структурный прокси: точное деление; C++ порт копирует 0.5ulp полином
|
||||||
|
0.207515 -0.241687 0.288535 -0.360671 ... 0.240264 через vpermps tbl@1821269c0."""
|
||||||
|
return np.asarray(b, dtype=np.float64) / np.asarray(a, dtype=np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def chain_9_19_sim(bands, tmp6f8, accVec, warp, att, rel, nbin=None):
|
||||||
|
"""CHAIN 9-19 BLOCKMAP:620-644 op-by-op (python структурный прокси).
|
||||||
|
bands: log-domain bands_curve_i (size nbin), tmp6f8: f6f8, accVec: ACC_i
|
||||||
|
warp: kWarp@5406a8, att@5406c8 rel@5406e8 — все 2049.
|
||||||
|
Возвращает bands mutated (log domain после IIR, перед FIR)."""
|
||||||
|
if nbin is None:
|
||||||
|
nbin = len(bands)
|
||||||
|
bands = np.asarray(bands, dtype=np.float64)
|
||||||
|
tmp6f8 = np.asarray(tmp6f8, dtype=np.float64)
|
||||||
|
accVec = np.asarray(accVec, dtype=np.float64)
|
||||||
|
# 9a zero already (vec698 irrelevant), 9b: tmp6f8 += 0.8 (p=1.0 *0.8)
|
||||||
|
# 9c DIVIDE dst=bands A=bands B=tmp6f8+0.8? Actually B=tmp6f8, but step 9b already added 0.8
|
||||||
|
# BLOCKMAP 24mm5: bands = tmp6f8 / bands (divide B/A)
|
||||||
|
# Use k_div_exact
|
||||||
|
b = tmp6f8 + 0.8 # proxy for step 9b effect (when tmp6f8 initially 0, b=0.8)
|
||||||
|
# If tmp6f8 already has data, 9b is additive 0.8, so b = tmp6f8 +0.8
|
||||||
|
# For calibration where tmp6f8 is 0, this yields 0.8/bands
|
||||||
|
bands = k_div_exact(np.maximum(bands, 1e-30), b)
|
||||||
|
# 10 dc40: tmp6f8 = bands - ACC
|
||||||
|
tmp6f8 = bands - accVec
|
||||||
|
# 11 FMA ATT/REL half-split
|
||||||
|
# true triples 12B re/im/coef, scalar proxy: upper half ATT, lower REL
|
||||||
|
for i in range(nbin):
|
||||||
|
if i < nbin // 2:
|
||||||
|
tmp6f8[i] += att[i] * accVec[i]
|
||||||
|
else:
|
||||||
|
tmp6f8[i] += rel[i] * accVec[i]
|
||||||
|
# 14 EXP#1 + -1 (order fixed 24mm2)
|
||||||
|
bands = k_exp_exact(bands) - 1.0
|
||||||
|
# 15 array-mul X*track (track is external, warp mul is step16)
|
||||||
|
# 16 *kWarp + LOG#2
|
||||||
|
bands = bands * warp
|
||||||
|
bands = np.log(np.maximum(bands, 1e-30))
|
||||||
|
# 16b IIR4x2 bidir log-domain base 0x340510 — proxy via kIIR_A1/B1
|
||||||
|
# keep as identity for now (real IIR is mild 0.003-0.19 per 24cc)
|
||||||
|
# FIR will be applied externally via fir_kernel
|
||||||
|
return bands, tmp6f8
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------ данные -----
|
||||||
|
def load_tract(path):
|
||||||
|
"""tract_*.txt: k am res lvl_raw band_level prewarp w"""
|
||||||
|
t = np.loadtxt(path)
|
||||||
|
return {'am': t[:, 1], 'res': t[:, 2], 'lvl': t[:, 3]}
|
||||||
|
|
||||||
|
|
||||||
|
def load_frame(npz):
|
||||||
|
"""Слоты кадра rendersnap2 → dict[int, np.ndarray]."""
|
||||||
|
d = np.load(npz)
|
||||||
|
out = {}
|
||||||
|
for k in d.keys():
|
||||||
|
if k.startswith('0x'):
|
||||||
|
out[int(k[2:], 16)] = d[k]
|
||||||
|
return out, d['t_snap']
|
||||||
|
|
||||||
|
|
||||||
|
def pick_clean_frame(ds_dir, min_bins=8):
|
||||||
|
"""Отбор чистых стационарных кадров по фазам (24mm3):
|
||||||
|
возвращает лучший на фазе γ* (~1.7606, маска применена)
|
||||||
|
и лучший на фазе γ=1 (степень ещё не применена)."""
|
||||||
|
classes = {'gamma': None, 'identity': None}
|
||||||
|
for f in sorted(glob.glob(os.path.join(ds_dir, 'ph*.npz'))):
|
||||||
|
try:
|
||||||
|
S, ts = load_frame(f)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if not all(x in S for x in (0x540628, 0x540688, 0x540678)):
|
||||||
|
continue
|
||||||
|
s = S[0x540628][:1025].astype(np.float64)
|
||||||
|
t = S[0x540688][:1025].astype(np.float64)
|
||||||
|
c = S[0x540678][:1025].astype(np.float64)
|
||||||
|
ok = (t > 1e-30) & (c > 1e-30) & np.isfinite(s)
|
||||||
|
if ok.sum() < 50:
|
||||||
|
continue
|
||||||
|
lt = np.log(t[ok])
|
||||||
|
lc = np.log(c[ok])
|
||||||
|
sel = np.abs(lt) > 0.05
|
||||||
|
if sel.sum() < min_bins:
|
||||||
|
continue
|
||||||
|
g = float(np.sum(lt[sel] * lc[sel]) / np.sum(lt[sel] ** 2))
|
||||||
|
rms = float(np.sqrt(np.mean((lc[sel] - g * lt[sel]) ** 2)))
|
||||||
|
depth = float(-lc.min())
|
||||||
|
key = 'gamma' if abs(g - GAMMA) < 0.01 else \
|
||||||
|
('identity' if abs(g - 1.0) < 1e-4 else None)
|
||||||
|
if key is None or rms > 1e-4:
|
||||||
|
continue
|
||||||
|
cand = (depth, f, s, t, c, g, rms)
|
||||||
|
if classes[key] is None or depth > classes[key][0]:
|
||||||
|
classes[key] = cand
|
||||||
|
return classes
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------- валидация -------
|
||||||
|
def validate_scr(sim_scr, cap_scr, tol_db=0.05):
|
||||||
|
"""rms в дБ между симулированным и захваченным scr."""
|
||||||
|
m = np.abs(cap_scr) > 0.02
|
||||||
|
err = (sim_scr[m] - cap_scr[m]) * (20 / np.log(10))
|
||||||
|
return float(np.sqrt(np.mean(err ** 2))), int(m.sum())
|
||||||
|
|
||||||
|
|
||||||
|
def win_periodic_hann(N):
|
||||||
|
return 0.5 * (1.0 - np.cos(2.0 * np.pi * np.arange(N) / N))
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------ FIR-цепь (24mm9) --------
|
||||||
|
NFRAME = 4096 # n=[ctx+0x540534]
|
||||||
|
NBINS_FIR = NFRAME // 2 + 1
|
||||||
|
Q_EXP = 0.8002203702926636 # .rdata 1820013f0, refines 0.80 (BLOCKMAP 52b716)
|
||||||
|
|
||||||
|
|
||||||
|
def winfreq_fall():
|
||||||
|
"""WINfreq@[ctx+0x540658]: периодический Hann(4096), падающая половина."""
|
||||||
|
return win_periodic_hann(NFRAME)[NFRAME // 2:]
|
||||||
|
|
||||||
|
|
||||||
|
def fir_kernel(scr, q=Q_EXP):
|
||||||
|
"""Полная FIR-цепь (BLOCKMAP 24mm9): min-phase кепстральный сэндвич.
|
||||||
|
|
||||||
|
scr(2049) → pack(re=scr,im=0) → FIR[n]=0 (Найквост)
|
||||||
|
→ inv-RFFT → fold(y[1..2047]*=2.0 @1824c41e0; y[2049..4095]=0)
|
||||||
|
→ fwd-RFFT → комплексная EXP (1803831c0, аргумент ×q)
|
||||||
|
→ inv-RFFT → ×падающий Hann → ноль хвоста → fwd-RFFT
|
||||||
|
→ FIR[0]=1, FIR[1]=0. Возвращает |F| (2049).
|
||||||
|
"""
|
||||||
|
h = np.asarray(scr, dtype=np.complex128).copy()
|
||||||
|
h[-1] = 0.0
|
||||||
|
y = np.fft.irfft(h, n=NFRAME)
|
||||||
|
y[1:NFRAME // 2] *= 2.0
|
||||||
|
y[NFRAME // 2 + 1:] = 0.0
|
||||||
|
w = np.fft.irfft(np.exp(q * np.fft.rfft(y, n=NFRAME)), n=NFRAME)
|
||||||
|
w[:NFRAME // 2] *= winfreq_fall()
|
||||||
|
w[NFRAME // 2:] = 0.0
|
||||||
|
F = np.abs(np.fft.rfft(w, n=NFRAME))
|
||||||
|
F[0] = 1.0
|
||||||
|
return F
|
||||||
|
|
||||||
|
|
||||||
|
def mask_from_frame(S, q=Q_EXP):
|
||||||
|
"""mask_sim из слотов кадра: cur ≈ trk · |F(scr)| (df0 complex-mul)."""
|
||||||
|
scr = S[0x540628][:NBINS_FIR].astype(np.float64)
|
||||||
|
trk = S[0x540688][:NBINS_FIR].astype(np.float64)
|
||||||
|
return trk * fir_kernel(scr, q)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_mask_stage(ds, q=Q_EXP, cap=60):
|
||||||
|
"""Валидация масочной ветви на чистых γ-кадрах (24mm9-протокол).
|
||||||
|
|
||||||
|
Отбор: |γ_fit−1.760561|<5e-4 и fit-rms<1e-5 (жёстче pick_clean_frame).
|
||||||
|
Критерий: rms по ВСЕМ 2049 бинам < 0.05 дБ (структурная фаза).
|
||||||
|
"""
|
||||||
|
import glob
|
||||||
|
rmss, gpred = [], []
|
||||||
|
for f in sorted(glob.glob(os.path.join(ds, 'ph*.npz'))):
|
||||||
|
try:
|
||||||
|
d = np.load(f)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if '0x540628' not in d:
|
||||||
|
continue
|
||||||
|
scr = d['0x540628'][:NBINS_FIR].astype(np.float64)
|
||||||
|
trk = d['0x540688'][:NBINS_FIR].astype(np.float64)
|
||||||
|
cur = d['0x540678'][:NBINS_FIR].astype(np.float64)
|
||||||
|
ok = (trk > 1e-30) & (cur > 1e-30) & np.isfinite(scr)
|
||||||
|
if ok.sum() < 50:
|
||||||
|
continue
|
||||||
|
lt, lc = np.log(trk[ok]), np.log(cur[ok])
|
||||||
|
sel = np.abs(lt) > 0.05
|
||||||
|
if sel.sum() < 8:
|
||||||
|
continue
|
||||||
|
g = float(np.sum(lt[sel] * lc[sel]) / np.sum(lt[sel] ** 2))
|
||||||
|
frms = float(np.sqrt(np.mean((lc[sel] - g * lt[sel]) ** 2)))
|
||||||
|
if not (abs(g - GAMMA) < 5e-4 and frms < 1e-5):
|
||||||
|
continue
|
||||||
|
F = fir_kernel(scr, q)
|
||||||
|
lf = np.log(F[sel])
|
||||||
|
lt_s = np.log(trk[sel])
|
||||||
|
sF = float(np.sum(lf * lt_s) / np.sum(lt_s ** 2))
|
||||||
|
gpred.append(1.0 + sF)
|
||||||
|
m = trk * F
|
||||||
|
mm = (cur > 1e-12) & (m > 1e-12)
|
||||||
|
e = (np.log(m[mm]) - np.log(cur[mm])) * 20 / np.log(10)
|
||||||
|
rmss.append(float(np.sqrt(np.mean(e ** 2))))
|
||||||
|
if len(rmss) >= cap:
|
||||||
|
break
|
||||||
|
if not rmss:
|
||||||
|
print('нет ультрачистых кадров в', ds)
|
||||||
|
return
|
||||||
|
rmss = np.array(rmss)
|
||||||
|
print('кадров=%d | rms медиана=%.4f дБ p90=%.4f max=%.4f | '
|
||||||
|
'gamma_pred(1+s_F)=%.6f' %
|
||||||
|
(len(rmss), np.median(rmss), np.percentile(rmss, 90), rmss.max(),
|
||||||
|
float(np.median(gpred))))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import sys
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == '--mask':
|
||||||
|
validate_mask_stage(sys.argv[2] if len(sys.argv) > 2
|
||||||
|
else '/tmp/opencode/sc_multi4b')
|
||||||
|
return
|
||||||
|
ds = sys.argv[1] if len(sys.argv) > 1 else '/tmp/opencode/sc_multi6'
|
||||||
|
tract = sys.argv[2] if len(sys.argv) > 2 else '/tmp/opencode/tract_multi6.txt'
|
||||||
|
|
||||||
|
classes = pick_clean_frame(ds)
|
||||||
|
ph_g = classes['gamma']
|
||||||
|
ph_i = classes['identity']
|
||||||
|
if not ph_g and not ph_i:
|
||||||
|
print('нет чистых кадров в', ds)
|
||||||
|
return
|
||||||
|
for lbl, best in (('γ-фаза', ph_g), ('identity', ph_i)):
|
||||||
|
if not best:
|
||||||
|
continue
|
||||||
|
_, f, scr, trk, cur, gamma_fit, grms = best
|
||||||
|
n = len(scr)
|
||||||
|
cut_meas = -20 / np.log(10) * np.log(np.maximum(cur, 1e-30))
|
||||||
|
g_use = gamma_fit
|
||||||
|
cut_sim = g_use * (-scr) * 20 / np.log(10)
|
||||||
|
e = cut_sim - cut_meas
|
||||||
|
sel = np.abs(cut_meas) > 0.1
|
||||||
|
rms_db = float(np.sqrt(np.mean(e[sel] ** 2))) if sel.any() else 0.0
|
||||||
|
print(f'{lbl}: {os.path.basename(f)} γ={gamma_fit:.6f} (rms {grms:.1e}) '
|
||||||
|
f'закон: rms={rms_db:.4f} дБ / {int(sel.sum())} бинов')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -32,6 +32,9 @@ def load_mono(p):
|
|||||||
ch = w.getnchannels(); b = w.getsampwidth()
|
ch = w.getnchannels(); b = w.getsampwidth()
|
||||||
if b == 2:
|
if b == 2:
|
||||||
return np.frombuffer(d, dtype=np.int16).astype(np.float64).reshape(-1, ch).mean(1) / 32768
|
return np.frombuffer(d, dtype=np.int16).astype(np.float64).reshape(-1, ch).mean(1) / 32768
|
||||||
|
# 24-bit: handle misaligned data (extra bytes from bext/junk chunks)
|
||||||
|
n_samples = len(d) // (ch * 3)
|
||||||
|
d = d[:n_samples * ch * 3]
|
||||||
raw = np.frombuffer(d, dtype=np.uint8).reshape(-1, ch, 3)
|
raw = np.frombuffer(d, dtype=np.uint8).reshape(-1, ch, 3)
|
||||||
s = raw[:, :, 0].astype(np.int64) | (raw[:, :, 1].astype(np.int64) << 8) | (raw[:, :, 2].astype(np.int64) << 16)
|
s = raw[:, :, 0].astype(np.int64) | (raw[:, :, 1].astype(np.int64) << 8) | (raw[:, :, 2].astype(np.int64) << 16)
|
||||||
return np.where(s >= 0x800000, s - 0x1000000, s).mean(1).astype(np.float64) / 8388608.0
|
return np.where(s >= 0x800000, s - 0x1000000, s).mean(1).astype(np.float64) / 8388608.0
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""detector_cascade.py — validated simulator of the soothe2 detector cascade (529c60).
|
||||||
|
|
||||||
|
Decoded from assembly (2026-08-25):
|
||||||
|
Phase 1: |z_i| via 16140 (vrsqrtps+vsqrtps — magnitude, NOT squared)
|
||||||
|
Phase 2: Haar smoothing kernel [0.25, 0.5, 0.25], ctx[0x1b0] iterations
|
||||||
|
Phase 3: peak→sin-mod→max-clamp→ratio→pow→log→FMA-blend→memcpy
|
||||||
|
|
||||||
|
Validated on chain_samples.pkl (2-frame ptrace capture):
|
||||||
|
- op A output matches |z| (max diff 5.4e-6)
|
||||||
|
- 2 Haar iterations + scalar blend: rms=0.30, corr=0.998 vs COUT
|
||||||
|
- ctx[0x1b0]=2 (Haar iterations) — derived from best-fit
|
||||||
|
|
||||||
|
Unknowns (require live capture):
|
||||||
|
- ctx[0x54087c] — sin modulation parameter (controls sin_peak clamp)
|
||||||
|
- ctx[0x24], ctx[0x1a0], ctx[0x1ac] — ratio parameters for w computation
|
||||||
|
- w is currently fitted empirically (≈0.015 for this test signal)
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
N = 2049 # FFT bins (NFRAME/2 + 1)
|
||||||
|
|
||||||
|
|
||||||
|
def haar_one_pass(b):
|
||||||
|
"""One Haar smoothing pass (kernel [0.25, 0.5, 0.25]).
|
||||||
|
|
||||||
|
Decoded from 529c60 Haar loop (lines 35-74):
|
||||||
|
Step 1: b[i] += b[i+1] (prefix sum, 10e40)
|
||||||
|
Step 2: b[i] *= 0.5 (scalar mul, ffe0)
|
||||||
|
Step 3: scratch[i] = b[i+1] + b[i] (3-op add, 11580)
|
||||||
|
Step 4: b[i+1] = 0.5 * scratch[i] (scalar mul+store, 4720)
|
||||||
|
"""
|
||||||
|
n = len(b)
|
||||||
|
if n < 2:
|
||||||
|
return b
|
||||||
|
# Steps 1+2 combined: b[i] = 0.5 * (b[i] + b[i+1]) for i < n-1
|
||||||
|
# Note: b[n-1] is unchanged by steps 1+2
|
||||||
|
b[:-1] = 0.5 * (b[:-1] + b[1:])
|
||||||
|
# Steps 3+4: b[i+1] = 0.5 * (b[i] + b[i+1]) using UPDATED b
|
||||||
|
# Need original b[i] values for step 3
|
||||||
|
# Actually: step 3 reads AFTER steps 1+2, so uses modified b
|
||||||
|
# scratch[i] = b[i+1] + b[i] (both modified)
|
||||||
|
# b[i+1] = 0.5 * scratch[i]
|
||||||
|
# This means: b_new[i+1] = 0.5 * (b_modified[i+1] + b_modified[i])
|
||||||
|
b6f8 = b[1:] + b[:-1]
|
||||||
|
b[1:] = 0.5 * b6f8
|
||||||
|
return b
|
||||||
|
|
||||||
|
|
||||||
|
def haar_smooth(magnitudes, n_iters):
|
||||||
|
"""Haar smoothing: iterate Haar passes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
magnitudes: |z_i| array (N floats)
|
||||||
|
n_iters: number of Haar iterations (ctx[0x1b0])
|
||||||
|
Returns:
|
||||||
|
smoothed array
|
||||||
|
"""
|
||||||
|
b = magnitudes.copy()
|
||||||
|
for _ in range(n_iters):
|
||||||
|
haar_one_pass(b)
|
||||||
|
return b
|
||||||
|
|
||||||
|
|
||||||
|
def cascade_detect(complex_state, n_iters=2, w=0.015, sin_peak_floor=0.0):
|
||||||
|
"""Full detector cascade (529c60) simulation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
complex_state: interleaved re/im array (2N floats)
|
||||||
|
n_iters: Haar iteration count
|
||||||
|
w: blend weight (scalar, ~0.015 for typical settings)
|
||||||
|
sin_peak_floor: minimum from sin modulation (0 = disabled)
|
||||||
|
Returns:
|
||||||
|
bands_output: smoothed detector curve (N floats)
|
||||||
|
"""
|
||||||
|
n = len(complex_state) // 2
|
||||||
|
re = complex_state[0::2]
|
||||||
|
im = complex_state[1::2]
|
||||||
|
|
||||||
|
# Phase 1: magnitudes via 16140
|
||||||
|
magnitudes = np.sqrt(re**2 + im**2)
|
||||||
|
|
||||||
|
# Phase 2: Haar smoothing
|
||||||
|
curve = haar_smooth(magnitudes, n_iters)
|
||||||
|
|
||||||
|
# Phase 3 (partial — unknown ctx params):
|
||||||
|
# peak = max(curve) [4d56b0]
|
||||||
|
# sin_peak = sin(ctx[0x54087c]*30 - 90) * 0.115129 * peak [1a14cac]
|
||||||
|
# curve[i] = max(curve[i], sin_peak) [52d8a0→10860]
|
||||||
|
if sin_peak_floor > 0:
|
||||||
|
np.maximum(curve, sin_peak_floor, out=curve)
|
||||||
|
|
||||||
|
# Blend: output = curve * (1-w) + accumulator * w
|
||||||
|
# 5407a8 (accumulator) = 0 in steady state → output = curve * (1-w)
|
||||||
|
# The blend chain:
|
||||||
|
# 52d920: 5407a8[i] *= w (array scalar mul)
|
||||||
|
# 52dae0: 5407a8[i] += curve[i] * (1-w) (FMA)
|
||||||
|
# 52dbc0: memcpy 5407a8 → 540678
|
||||||
|
bands_output = curve * (1.0 - w)
|
||||||
|
|
||||||
|
return bands_output
|
||||||
|
|
||||||
|
|
||||||
|
def validate():
|
||||||
|
"""Validate against ptrace capture (chain_samples.pkl)."""
|
||||||
|
import pickle
|
||||||
|
path = '/tmp/opencode/winetrace_casc/chain_samples.pkl'
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
data = pickle.load(f)
|
||||||
|
|
||||||
|
s = data['samples']
|
||||||
|
cin = s[0]
|
||||||
|
cout = s[3]
|
||||||
|
|
||||||
|
trk = np.array(cin['trk'], dtype=np.float64)
|
||||||
|
b0_cout = np.array(cout['bands0'], dtype=np.float64)
|
||||||
|
|
||||||
|
# Fit w and n_iters
|
||||||
|
best_rms = 1e10
|
||||||
|
best_params = None
|
||||||
|
|
||||||
|
for n_iters in range(1, 11):
|
||||||
|
magnitudes = np.zeros(len(trk) // 2)
|
||||||
|
re = trk[0::2]; im = trk[1::2]
|
||||||
|
magnitudes = np.sqrt(re**2 + im**2)
|
||||||
|
|
||||||
|
curve = haar_smooth(magnitudes, n_iters)
|
||||||
|
|
||||||
|
sig = (curve > 0.5) & (b0_cout > 0.5)
|
||||||
|
if sig.sum() < 10:
|
||||||
|
continue
|
||||||
|
|
||||||
|
w_vals = 1.0 - b0_cout[sig] / curve[sig]
|
||||||
|
w = float(np.median(w_vals))
|
||||||
|
|
||||||
|
predicted = curve * (1.0 - w)
|
||||||
|
rms = float(np.sqrt(np.mean((predicted - b0_cout) ** 2)))
|
||||||
|
corr = float(np.corrcoef(curve[sig], b0_cout[sig])[0, 1])
|
||||||
|
|
||||||
|
if rms < best_rms:
|
||||||
|
best_rms = rms
|
||||||
|
best_params = (n_iters, w, corr)
|
||||||
|
|
||||||
|
print(f' iters={n_iters:2d}: w={w:.6f}, rms={rms:.4f}, corr={corr:.6f}')
|
||||||
|
|
||||||
|
n_iters, w, corr = best_params
|
||||||
|
print(f'\nBest: iters={n_iters}, w={w:.6f}, rms={best_rms:.4f}, corr={corr:.6f}')
|
||||||
|
return n_iters, w
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if '--validate' in sys.argv:
|
||||||
|
validate()
|
||||||
|
else:
|
||||||
|
print('Usage: detector_cascade.py --validate')
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""dis.py — quick capstone disassembler for soothe_mem.bin (base 0x180000000).
|
||||||
|
Usage: dis.py <VA> [len] [va2 len2 ...]
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
|
||||||
|
|
||||||
|
BASE = 0x180000000
|
||||||
|
_data = open('/home/m/re-tools/soothe_mem.bin', 'rb').read()
|
||||||
|
|
||||||
|
def dis(va, n=256):
|
||||||
|
off = va - BASE
|
||||||
|
code = _data[off:off + n]
|
||||||
|
md = Cs(CS_ARCH_X86, CS_MODE_64)
|
||||||
|
md.detail = False
|
||||||
|
out = []
|
||||||
|
for ins in md.disasm(code, va):
|
||||||
|
s = '%08x %-24s %s %s' % (ins.address, ins.bytes.hex(),
|
||||||
|
ins.mnemonic, ins.op_str)
|
||||||
|
out.append(s)
|
||||||
|
if ins.mnemonic == 'ret':
|
||||||
|
break
|
||||||
|
return '\n'.join(out)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
a = sys.argv[1:]
|
||||||
|
for i in range(0, len(a), 2):
|
||||||
|
va = int(a[i], 16)
|
||||||
|
n = int(a[i + 1], 16) if i + 1 < len(a) else 256
|
||||||
|
print('==== %x (len %x) ====' % (va, n))
|
||||||
|
print(dis(va, n))
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""disasm_func.py — full-function disassembler with resolved RIP constants.
|
||||||
|
Usage: disasm_func.py <VA> [max_bytes]
|
||||||
|
Stops on int3-run after a ret. Prints resolved [rip+X] targets inline.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
|
||||||
|
from capstone.x86 import X86_OP_MEM, X86_REG_RIP
|
||||||
|
|
||||||
|
BASE = 0x180000000
|
||||||
|
_data = open('/home/m/re-tools/soothe_mem.bin', 'rb').read()
|
||||||
|
|
||||||
|
def rd(va, n):
|
||||||
|
return _data[va - BASE: va - BASE + n]
|
||||||
|
|
||||||
|
def main():
|
||||||
|
va = int(sys.argv[1], 16)
|
||||||
|
maxb = int(sys.argv[2], 16) if len(sys.argv) > 2 else 0x4000
|
||||||
|
code = rd(va, maxb)
|
||||||
|
md = Cs(CS_ARCH_X86, CS_MODE_64)
|
||||||
|
md.detail = True
|
||||||
|
out = []
|
||||||
|
run_int3 = 0
|
||||||
|
seen_ret = False
|
||||||
|
for ins in md.disasm(code, va):
|
||||||
|
line = '%08x %-22s %s %s' % (ins.address, ins.bytes.hex(), ins.mnemonic, ins.op_str)
|
||||||
|
note = ''
|
||||||
|
for op in ins.operands:
|
||||||
|
if op.type == X86_OP_MEM and op.mem.base == X86_REG_RIP:
|
||||||
|
tgt = ins.address + ins.size + op.mem.disp
|
||||||
|
note += ' ; ->%x' % tgt
|
||||||
|
b4 = rd(tgt, 8)
|
||||||
|
f32v = struct.unpack('<f', b4[:4])[0]
|
||||||
|
f64v = struct.unpack('<d', b4[:8])[0]
|
||||||
|
u64v = struct.unpack('<Q', b4[:8])[0]
|
||||||
|
if abs(f32v) > 1e-6 and abs(f32v) < 1e8:
|
||||||
|
note += ' f32=%.6g' % f32v
|
||||||
|
elif abs(f64v) > 1e-6 and abs(f64v) < 1e12:
|
||||||
|
note += ' f64=%.6g' % f64v
|
||||||
|
else:
|
||||||
|
note += ' u64=%x' % u64v
|
||||||
|
line += note
|
||||||
|
out.append(line)
|
||||||
|
if ins.mnemonic == 'ret':
|
||||||
|
seen_ret = True
|
||||||
|
run_int3 = 0
|
||||||
|
elif ins.mnemonic == 'int3':
|
||||||
|
if seen_ret:
|
||||||
|
run_int3 += 1
|
||||||
|
if run_int3 >= 4:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
run_int3 = 0
|
||||||
|
print('\n'.join(out))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""dualtrace.py — live capture of the DSP ctx during realtime playback of
|
||||||
|
dual_b1q_0.5.rpp (two tones 500+2000 Hz, one band @500 q=0.5).
|
||||||
|
|
||||||
|
Method (NOTES_CAPTURE.md 2026-08-20c): reaper + play_loop.lua keeps the audio
|
||||||
|
callback alive; chunked /proc/pid/mem snapshot of the yabridge host; find ctx
|
||||||
|
by marker +0x24 == 48000.0f; dump scalars + pointer table + all ~2049-float
|
||||||
|
arrays, flag those with energy at tone bins 43/171.
|
||||||
|
|
||||||
|
Usage: python3 scripts/dualtrace.py [rpp]
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
SNAP1 = '/tmp/opencode/dualtrace_s1.bin'
|
||||||
|
SNAP2 = '/tmp/opencode/dualtrace_s2.bin'
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
import glob
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def snapshot(host, path):
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
out = open(path, 'wb')
|
||||||
|
nreg = nbytes = 0
|
||||||
|
for line in open(f'/proc/{host}/maps').read().splitlines():
|
||||||
|
p = line.split()
|
||||||
|
if len(p) < 2 or 'r' not in p[1]:
|
||||||
|
continue
|
||||||
|
lo, hi = (int(x, 16) for x in p[0].split('-'))
|
||||||
|
a = lo
|
||||||
|
while a < hi:
|
||||||
|
n = min(hi - a, 8 * 1024 * 1024)
|
||||||
|
try:
|
||||||
|
d = os.pread(fd, n, a)
|
||||||
|
except Exception:
|
||||||
|
a += n
|
||||||
|
continue
|
||||||
|
if d:
|
||||||
|
out.write(struct.pack('<QQ', a, len(d)))
|
||||||
|
out.write(d)
|
||||||
|
nreg += 1
|
||||||
|
nbytes += len(d)
|
||||||
|
a += n
|
||||||
|
out.close()
|
||||||
|
os.close(fd)
|
||||||
|
return nreg, nbytes
|
||||||
|
|
||||||
|
|
||||||
|
def parse_snap(path):
|
||||||
|
data = open(path, 'rb').read()
|
||||||
|
regs = []
|
||||||
|
i = 0
|
||||||
|
while i + 16 <= len(data):
|
||||||
|
lo, sz = struct.unpack_from('<QQ', data, i)
|
||||||
|
regs.append((lo, data[i + 16:i + 16 + sz]))
|
||||||
|
i += 16 + sz
|
||||||
|
return regs
|
||||||
|
|
||||||
|
|
||||||
|
def readabs(regs, addr, n):
|
||||||
|
for lo, body in regs:
|
||||||
|
if lo <= addr < lo + len(body) and addr - lo + n <= len(body):
|
||||||
|
return body[addr - lo:addr - lo + n]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_ctx(regs):
|
||||||
|
sig = struct.pack('<I', 0x473b8000) # 48000.0f
|
||||||
|
cands = []
|
||||||
|
for lo, body in regs:
|
||||||
|
j = 0
|
||||||
|
while True:
|
||||||
|
j = body.find(sig, j)
|
||||||
|
if j < 0:
|
||||||
|
break
|
||||||
|
base = lo + j - 0x24
|
||||||
|
b = readabs(regs, base + 0x540870, 4)
|
||||||
|
if b and struct.unpack('<f', b)[0] > 100: # sens scalar ~441
|
||||||
|
cands.append(base)
|
||||||
|
j += 1
|
||||||
|
return sorted(set(cands))
|
||||||
|
|
||||||
|
|
||||||
|
def dump_ctx(regs, ctx, tag, store):
|
||||||
|
"""Scalars + pointer table 0x540600..0x540900 + dereferenced arrays."""
|
||||||
|
def f32(addr):
|
||||||
|
b = readabs(regs, addr, 4)
|
||||||
|
return struct.unpack('<f', b)[0] if b else None
|
||||||
|
|
||||||
|
scal = {hex(a): f32(ctx + a) for a in range(0x540860, 0x5408a8, 4)}
|
||||||
|
store[f'{tag}_scalars'] = scal
|
||||||
|
|
||||||
|
arrays = {}
|
||||||
|
ptrinfo = []
|
||||||
|
for off in range(0x540600, 0x540900, 8):
|
||||||
|
b = readabs(regs, ctx + off, 8)
|
||||||
|
if not b:
|
||||||
|
continue
|
||||||
|
ptr = struct.unpack('<Q', b)[0]
|
||||||
|
if ptr < 0x10000 or ptr > 0x7fffffffffff:
|
||||||
|
continue
|
||||||
|
body = readabs(regs, ptr, 2049 * 4 + 64)
|
||||||
|
if body is None or len(body) < 2049 * 4:
|
||||||
|
continue
|
||||||
|
arr = np.frombuffer(body[:2049 * 4], dtype='<f4').astype(np.float64)
|
||||||
|
if not np.isfinite(arr).all():
|
||||||
|
continue
|
||||||
|
arrays[off] = arr
|
||||||
|
ptrinfo.append((hex(ctx + off), hex(ptr)))
|
||||||
|
|
||||||
|
# classify: energy at tone bins relative to local median
|
||||||
|
flagged = {}
|
||||||
|
med = None
|
||||||
|
for off, arr in arrays.items():
|
||||||
|
m = float(np.median(arr))
|
||||||
|
if m <= 0 or not np.isfinite(m) or float(np.max(np.abs(arr))) > 1e6:
|
||||||
|
continue
|
||||||
|
r43 = arr[43] / m
|
||||||
|
r171 = arr[171] / m
|
||||||
|
if r43 > 3 or r171 > 3 or (arr.max() / max(m, 1e-30)) > 5:
|
||||||
|
flagged[off] = dict(ratio43=r43, ratio171=r171,
|
||||||
|
vmin=float(arr.min()), vmax=float(arr.max()))
|
||||||
|
store[f'{tag}_arr_{hex(off)}'] = arr
|
||||||
|
store[f'{tag}_ptrinfo'] = ptrinfo
|
||||||
|
store[f'{tag}_flagged'] = {hex(k): v for k, v in flagged.items()}
|
||||||
|
return flagged
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/dual_b1q_0.5.rpp'
|
||||||
|
os.makedirs('/tmp/opencode', exist_ok=True)
|
||||||
|
subprocess.run('pkill -9 -x reaser 2>/dev/null; pkill -9 -x reaper 2>/dev/null; '
|
||||||
|
"pkill -9 -f '[y]abridge' 2>/dev/null; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['/usr/bin/reaper', '-nosplash', '-ignoreerrors', rpp,
|
||||||
|
'/home/m/re-tools/play_loop.lua'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
t0 = time.time()
|
||||||
|
host = None
|
||||||
|
while time.time() - t0 < 60 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.2)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host', host, 'at %.1fs' % (time.time() - t0), flush=True)
|
||||||
|
time.sleep(12) # init + several looped passes -> steady state
|
||||||
|
|
||||||
|
store = {}
|
||||||
|
try:
|
||||||
|
n1, b1 = snapshot(host, SNAP1)
|
||||||
|
print(f'snap1: {n1} regs {b1/1e6:.0f}MB', flush=True)
|
||||||
|
time.sleep(6)
|
||||||
|
n2, b2 = snapshot(host, SNAP2)
|
||||||
|
print(f'snap2: {n2} regs {b2/1e6:.0f}MB', flush=True)
|
||||||
|
|
||||||
|
for tag, path in (('s1', SNAP1), ('s2', SNAP2)):
|
||||||
|
regs = parse_snap(path)
|
||||||
|
cands = find_ctx(regs)
|
||||||
|
print(tag, 'ctx candidates:', [('0x%x' % c) for c in cands][:5], flush=True)
|
||||||
|
if cands:
|
||||||
|
fl = dump_ctx(regs, cands[0], tag, store)
|
||||||
|
for k, v in list(fl.items())[:10]:
|
||||||
|
print(' ', k, {kk: round(vv, 2) if isinstance(vv, float) else vv
|
||||||
|
for kk, vv in v.items()}, flush=True)
|
||||||
|
finally:
|
||||||
|
if proc.poll() is None:
|
||||||
|
proc.kill()
|
||||||
|
|
||||||
|
np.savez_compressed('/tmp/opencode/dualtrace.npz',
|
||||||
|
**{k: v for k, v in store.items()
|
||||||
|
if isinstance(v, np.ndarray)})
|
||||||
|
json.dump({k: v for k, v in store.items() if not isinstance(v, np.ndarray)},
|
||||||
|
open('/tmp/opencode/dualtrace.json', 'w'), indent=1)
|
||||||
|
print('\nsaved /tmp/opencode/dualtrace.npz|.json')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""dump_dispatch.py — dump dispatch tables of bigkernel stubs at runtime.
|
||||||
|
Stubs of interest (from FIR loop / steps 14,17):
|
||||||
|
1409e0 -> table@182617508 ; 140ad0 -> ? ; 140b30 -> table@1826176c8
|
||||||
|
Nested stub inside 140a00: idx cell/table computed below.
|
||||||
|
Dumps tables pre-render (static) and during render (runtime-patched).
|
||||||
|
"""
|
||||||
|
import struct, subprocess, sys, time
|
||||||
|
import glob, os
|
||||||
|
|
||||||
|
BASE = 0x180000000
|
||||||
|
data = open('/home/m/re-tools/soothe_mem.bin','rb').read()
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid=int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd=open(f'/proc/{pid}/cmdline','rb').read().replace(b'\0',b' ').decode('utf8','replace')
|
||||||
|
maps=open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception: continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd: return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
def rd(a,n):
|
||||||
|
try: return os.pread(fd,n,a)
|
||||||
|
except OSError: return None
|
||||||
|
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True)
|
||||||
|
rpp=sys.argv[1] if len(sys.argv)>1 else '/tmp/opencode/multi.rpp'
|
||||||
|
proc=subprocess.Popen(['/usr/bin/reaper','-nosplash','-ignoreerrors','-renderproject',rpp],
|
||||||
|
stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
|
||||||
|
t0=time.time(); host=None
|
||||||
|
while time.time()-t0<30 and not host:
|
||||||
|
host=find_host(); time.sleep(0.002)
|
||||||
|
print('host',host,flush=True)
|
||||||
|
if not host: sys.exit(1)
|
||||||
|
fd=os.open(f'/proc/{host}/mem',os.O_RDONLY)
|
||||||
|
|
||||||
|
TABLES={'bk140b30':0x1826176c8,'bk140b60':0x182617708,'bk1409e0':0x182617508,
|
||||||
|
'bk140ad0':None,'bk140a40':0x182617588}
|
||||||
|
# find bk140ad0 table: stub 140ad0 pattern movsxd rax,[rip+X]; lea r10,[rip+Y]
|
||||||
|
off=0x180140ad0-BASE
|
||||||
|
b=data[off:off+16]
|
||||||
|
rel1=struct.unpack('<i',b[3:7])[0]
|
||||||
|
rel2=struct.unpack('<i',b[10:14])[0]
|
||||||
|
idx_a=0x180140ad0+7+rel1
|
||||||
|
tbl_a=0x180140ad0+14+rel2
|
||||||
|
TABLES['bk140ad0']=tbl_a
|
||||||
|
print('bk140ad0 idx@%x tbl@%x' % (idx_a,tbl_a),flush=True)
|
||||||
|
|
||||||
|
def dump_tables(tag):
|
||||||
|
print(tag,'fd=',fd,flush=True)
|
||||||
|
for nm,t in TABLES.items():
|
||||||
|
b=rd(t,64)
|
||||||
|
if b is None:
|
||||||
|
import errno
|
||||||
|
print('%s %s unreadable err=%s'%(tag,nm,os.strerror(errno.EIO))); continue
|
||||||
|
vals=struct.unpack('<%dQ'%(len(b)//8),b[:len(b)//8*8])
|
||||||
|
nz=[(i,hex(v)) for i,v in enumerate(vals) if v]
|
||||||
|
print('%s %-9s: %s' % (tag,nm,nz), flush=True)
|
||||||
|
|
||||||
|
# also nested stub inside 140a00 (static parse):
|
||||||
|
off=0x140a10-BASE
|
||||||
|
b1=data[off:off+7]
|
||||||
|
if b1[:3]==b'\x48\x63\x05':
|
||||||
|
rel=struct.unpack('<i',b1[3:7])[0]
|
||||||
|
icell=0x180140a10+7+rel
|
||||||
|
b2=data[icell-BASE:4]
|
||||||
|
print('nested idx cell @%x static=%d' % (icell, struct.unpack('<i',b2)[0]),flush=True)
|
||||||
|
|
||||||
|
for k in range(30):
|
||||||
|
dump_tables('R%d'%k)
|
||||||
|
time.sleep(0.15)
|
||||||
|
try:
|
||||||
|
os.kill(proc.pid,0)
|
||||||
|
except ProcessLookupError:
|
||||||
|
break
|
||||||
|
os.close(fd)
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""finish_f.py — patch band freq=500, render refs, capture scratches, build tracts."""
|
||||||
|
import subprocess, os, re, sys, time, wave, glob
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0,'/home/m/re-tools/scripts')
|
||||||
|
|
||||||
|
def sh(cmd):
|
||||||
|
r=subprocess.run(cmd,shell=True,capture_output=True,text=True)
|
||||||
|
return r.stdout+r.stderr
|
||||||
|
|
||||||
|
# 1. patch band freq=500 into raw clones
|
||||||
|
for a in (0,6,12,24):
|
||||||
|
src=open(f'/tmp/opencode/f{a}_raw.rpp').read()
|
||||||
|
open(f'/tmp/opencode/f{a}.rpp','w').write(src)
|
||||||
|
print('freq left as base1k default (band1 freq=1000?) — CHECK')
|
||||||
|
# base1k was cloned from multi.rpp which had band1 freq=500 already!
|
||||||
|
# t1k_base.rpp got 'band1 freq'=1000 patched; f*_raw cloned from base1k -> 1000.
|
||||||
|
# We need 500: patch each:
|
||||||
|
for a in (0,6,12,24):
|
||||||
|
out=sh(f'cd /home/m/re-tools && python3 patchparam.py /tmp/opencode/f{a}_raw.rpp /tmp/opencode/f{a}.rpp "band1 freq"=500.0')
|
||||||
|
if 'patched 1' not in out: print(f'f{a} patch issue:',out.strip()[:80])
|
||||||
|
print('patched to 500')
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid=int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd=open(f'/proc/{pid}/cmdline','rb').read().replace(b'\0',b' ').decode('utf8','replace')
|
||||||
|
maps=open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception: continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd: return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
sh("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1")
|
||||||
|
|
||||||
|
for a in (0,6,12,24):
|
||||||
|
wav=f'/tmp/opencode/f{a}_ref.wav'
|
||||||
|
if os.path.exists(wav): os.remove(wav)
|
||||||
|
pr=subprocess.Popen(['/usr/bin/reaper','-nosplash','-ignoreerrors','-renderproject',f'/tmp/opencode/f{a}.rpp'],
|
||||||
|
stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
|
||||||
|
t0=time.time(); host=None
|
||||||
|
while time.time()-t0<30 and not host: host=find_host(); time.sleep(0.002)
|
||||||
|
# wait for natural finish
|
||||||
|
for _ in range(200):
|
||||||
|
if pr.poll() is not None: break
|
||||||
|
try:
|
||||||
|
if not os.path.exists('/proc/%d'%host) if host else True: break
|
||||||
|
except Exception: break
|
||||||
|
time.sleep(0.05)
|
||||||
|
for _ in range(60):
|
||||||
|
if pr.poll() is not None: break
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(f'f{a} rendered rc={pr.poll()} wav={os.path.getsize(wav) if os.path.exists(wav) else "NONE"}')
|
||||||
|
|
||||||
|
# tracts
|
||||||
|
env=dict(os.environ)
|
||||||
|
for a in (0,6,12,24):
|
||||||
|
tf=f'/tmp/opencode/tract_f{a}.txt'
|
||||||
|
if os.path.exists(tf): os.remove(tf)
|
||||||
|
e=dict(env); e['RT_DUMP_BIN']=tf
|
||||||
|
subprocess.run(['/home/m/re-tools/dsp/build/render48k',f'/tmp/opencode/{a}_in.wav' if False else f'/tmp/opencode/f{a}_in.wav',
|
||||||
|
'/tmp/o48_f.wav','500,0.5,12'],capture_output=True,env=e)
|
||||||
|
print('tracts done')
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fir_probe.py — численная реплика FIR-цепи (Этап A3) против захватов.
|
||||||
|
|
||||||
|
Структура по дизасму (BLOCKMAP 24mm6/24mm8 + wrap/worker декод этого раунда):
|
||||||
|
copy: FIR[2j]=scr[j], FIR[2j+1]=0 (18004d900, 2049 пар)
|
||||||
|
opA: th2180 = INVERSE real-FFT (план buf548, N=4096, scale 1/4096)
|
||||||
|
scale: float[1..2047] *= 2.0 ; float[2049..4095] = 0 (52d920/52db50)
|
||||||
|
opB: th1a90 = FORWARD real-FFT
|
||||||
|
EXP: expf in-place по первым 2049 ФЛОАТАМ (140b30, 52b708-716)
|
||||||
|
opC: th2180 = INVERSE
|
||||||
|
window: float[0..2047] *= WINfreq[2048..4095] (52d990, падающий Hann)
|
||||||
|
float[2048..4095] = 0 (52db50)
|
||||||
|
opD: th1a90 = FORWARD
|
||||||
|
fix: FIR[0]=1.0, FIR[1]=0 (52b7cd-e1)
|
||||||
|
df0: track_i := track_i ⊗ FIR (комплексное умножение, 18000b3c0)
|
||||||
|
Цель: воспроизвести cur@678 из trk@688 без свободных параметров.
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
NFLOAT = 4098 # 2049 пар
|
||||||
|
NBINS = 2049 # n/2+1, n=[ctx+0x540534]=4096
|
||||||
|
|
||||||
|
|
||||||
|
def load_frame(npz):
|
||||||
|
d = np.load(npz)
|
||||||
|
S = {}
|
||||||
|
for k in d.keys():
|
||||||
|
if k.startswith('0x'):
|
||||||
|
S[int(k[2:], 16)] = d[k]
|
||||||
|
return S
|
||||||
|
|
||||||
|
|
||||||
|
def win_periodic_hann(N):
|
||||||
|
return 0.5 * (1.0 - np.cos(2.0 * np.pi * np.arange(N) / N)).astype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def fir_chain(scr, winfall, variant='flat'):
|
||||||
|
"""scr: 2049 float (log-домен). Возвращает halfcomplex-спектр ядра F[2049]."""
|
||||||
|
# copy/pack: пары (re=scr, im=0) -> inverse rfft вход (numpy: complex[2049])
|
||||||
|
H = scr.astype(np.float64).astype(np.complex128)
|
||||||
|
# opA: inverse real FFT, нормировка 1/N (план scale=2^-12 при активном флаге)
|
||||||
|
y = np.fft.irfft(H, n=4096) # уже содержит деление на 4096
|
||||||
|
# scale/zero по asm: float[1..2047]*=2, float[2049..]=0 (f[2048] не трогаем)
|
||||||
|
y[1:2048] *= 2.0
|
||||||
|
y[2049:] = 0.0
|
||||||
|
# opB: forward
|
||||||
|
Y = np.fft.rfft(y, n=4096) # complex[2049]
|
||||||
|
# EXP по первым 2049 флоатам плоского массива
|
||||||
|
flat = np.empty(NFLOAT)
|
||||||
|
flat[0::2] = Y.real
|
||||||
|
flat[1::2] = Y.imag
|
||||||
|
if variant == 'flat':
|
||||||
|
flat[:2049] = np.exp(flat[:2049])
|
||||||
|
elif variant == 'cplx':
|
||||||
|
Y = np.exp(Y.astype(np.complex128))
|
||||||
|
flat[0::2] = Y.real
|
||||||
|
flat[1::2] = Y.imag
|
||||||
|
Y2 = flat[0::2] + 1j * flat[1::2]
|
||||||
|
# opC: inverse
|
||||||
|
w = np.fft.irfft(Y2, n=4096)
|
||||||
|
# window: float[0..2047] *= падающая половина; хвост = 0
|
||||||
|
w[:2048] *= winfall
|
||||||
|
w[2048:] = 0.0
|
||||||
|
# opD: forward
|
||||||
|
F = np.fft.rfft(w, n=4096)
|
||||||
|
# fix: FIR[0]=1.0, FIR[1]=0
|
||||||
|
F[0] = 1.0 + 0.0j
|
||||||
|
return F
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate(ds, ph_file, verbose=True):
|
||||||
|
S = load_frame(os.path.join(ds, ph_file))
|
||||||
|
scr = S[0x540628][:NBINS].astype(np.float64)
|
||||||
|
trk = S[0x540688][:NBINS].astype(np.float64)
|
||||||
|
cur = S[0x540678][:NBINS].astype(np.float64)
|
||||||
|
# проверка trk == exp(scr)
|
||||||
|
m_ok = trk > 1e-30
|
||||||
|
err_trk = np.abs(np.log(trk[m_ok]) - scr[m_ok]).max()
|
||||||
|
# фит gamma
|
||||||
|
sel = np.abs(scr) > 0.05
|
||||||
|
g = float(np.sum(scr[sel] * np.log(cur[sel])) / np.sum(scr[sel] ** 2))
|
||||||
|
rms_fit = float(np.sqrt(np.mean((np.log(cur[sel]) - g * scr[sel]) ** 2)))
|
||||||
|
winfall = win_periodic_hann(4096)[2048:]
|
||||||
|
out = []
|
||||||
|
for variant in ('flat', 'cplx'):
|
||||||
|
F = fir_chain(scr, winfall, variant)
|
||||||
|
# маска = track ⊗ F (df0), берём реальную часть как применённую маску
|
||||||
|
mask_sim = np.abs(trk * F) if variant == 'cplx' else trk * F.real
|
||||||
|
mm = (cur > 1e-6) & np.isfinite(mask_sim)
|
||||||
|
e_db = 20.0 / np.log(10) * np.log(np.abs(mask_sim[mm])) - \
|
||||||
|
20.0 / np.log(10) * np.log(cur[mm])
|
||||||
|
rms_db = float(np.sqrt(np.mean(e_db ** 2)))
|
||||||
|
out.append((variant, rms_db, int(mm.sum())))
|
||||||
|
if verbose:
|
||||||
|
print(f'{ph_file} [{variant}] gamma_fit={g:.6f} (rms {rms_fit:.1e}) '
|
||||||
|
f'trk_err={err_trk:.2e} MASK rms={rms_db:.4f} дБ / {mm.sum()} бинов')
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
jobs = [
|
||||||
|
('/tmp/opencode/sc_multi4b', 'ph073.npz'),
|
||||||
|
('/tmp/opencode/sc_multi6', 'ph037.npz'),
|
||||||
|
('/tmp/opencode/sc_multi6', 'ph034.npz'),
|
||||||
|
]
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
jobs = [(os.path.dirname(sys.argv[1]), os.path.basename(sys.argv[1]))]
|
||||||
|
for ds, ph in jobs:
|
||||||
|
evaluate(ds, ph)
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""firstop.py — catch the plugin INSIDE its audio callback by repeatedly
|
||||||
|
SIGSTOP-ing the yabridge host and reading only the ctx-referenced arrays.
|
||||||
|
|
||||||
|
Between callbacks the FIR work buffer (ctx+0x540668) is reset to the complex
|
||||||
|
identity (1,0)x2049. A snapshot where FIR != identity means we stopped after
|
||||||
|
the mask->FIR build stage; those snapshots are saved with the full pipeline
|
||||||
|
state (bands/scratch/f6f8/track/R curves).
|
||||||
|
|
||||||
|
Usage: python3 scripts/firstop.py [rpp] [nattempts]
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
SNAPDIR = '/tmp/opencode/firstop'
|
||||||
|
SLOTS = [0x540548, 0x540550, 0x540598, 0x540628, 0x540668, 0x540678, 0x540688,
|
||||||
|
0x540698, 0x5406a8, 0x5406b8, 0x5406c8, 0x5406d8, 0x5406e8, 0x5406f8,
|
||||||
|
0x540708, 0x540718, 0x540728, 0x540738, 0x540748, 0x540758,
|
||||||
|
0x540768, 0x540778, 0x540788, 0x540798, 0x5407a8, 0x5407b8,
|
||||||
|
0x5407c8, 0x5407d8, 0x5407e8, 0x5407f8, 0x540808, 0x540818,
|
||||||
|
0x540828, 0x540838, 0x540848]
|
||||||
|
NARR = 8194
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
import glob
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/dual_b1q_0.5.rpp'
|
||||||
|
nattempts = int(sys.argv[2]) if len(sys.argv) > 2 else 120
|
||||||
|
os.makedirs(SNAPDIR, exist_ok=True)
|
||||||
|
subprocess.run('pkill -9 -x reaser 2>/dev/null; pkill -9 -x reaper 2>/dev/null; '
|
||||||
|
"pkill -9 -f '[y]abridge' 2>/dev/null; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['/usr/bin/reaper', '-nosplash', '-ignoreerrors', rpp,
|
||||||
|
'/home/m/re-tools/play_loop.lua'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
t0 = time.time()
|
||||||
|
host = None
|
||||||
|
while time.time() - t0 < 60 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.2)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host', host, flush=True)
|
||||||
|
time.sleep(12)
|
||||||
|
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
|
||||||
|
def rd(addr, n):
|
||||||
|
try:
|
||||||
|
return os.pread(fd, n, addr)
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_ctx():
|
||||||
|
# cheap probe: sens scalar must be >100 at known offset
|
||||||
|
for base in (CTX.get('base'),):
|
||||||
|
pass
|
||||||
|
return CTX.get('base')
|
||||||
|
|
||||||
|
# locate ctx once (while running): marker scan over heap only is heavy;
|
||||||
|
# reuse known-good address from prior sessions, validate via sens scalar.
|
||||||
|
CTX = {}
|
||||||
|
ctx = None
|
||||||
|
b = rd(0x2370040 + 0x540870, 4)
|
||||||
|
if b and struct.unpack('<f', b)[0] > 100:
|
||||||
|
ctx = 0x2370040
|
||||||
|
else:
|
||||||
|
print('known ctx invalid; full scan needed')
|
||||||
|
return 1
|
||||||
|
print('ctx', hex(ctx), flush=True)
|
||||||
|
|
||||||
|
hits = 0
|
||||||
|
saved = []
|
||||||
|
rng = np.random.default_rng(7)
|
||||||
|
for k in range(nattempts):
|
||||||
|
os.kill(host, signal.SIGSTOP)
|
||||||
|
try:
|
||||||
|
fir_p = struct.unpack('<Q', rd(ctx + 0x540668, 8))[0]
|
||||||
|
fb = rd(fir_p, 64 * 4)
|
||||||
|
ident = False
|
||||||
|
if fb:
|
||||||
|
arr = np.frombuffer(fb[:256], dtype='<f4')
|
||||||
|
ident = bool(np.all(np.abs(arr[0::2] - 1.0) < 1e-6))
|
||||||
|
ident = ident and bool(np.all(arr[1::2] == 0))
|
||||||
|
if not ident:
|
||||||
|
hits += 1
|
||||||
|
store = {}
|
||||||
|
for off in SLOTS:
|
||||||
|
pb = rd(ctx + off, 8)
|
||||||
|
if not pb:
|
||||||
|
continue
|
||||||
|
p = struct.unpack('<Q', pb)[0]
|
||||||
|
if p < 0x10000:
|
||||||
|
continue
|
||||||
|
ab = rd(p, NARR * 4)
|
||||||
|
if not ab:
|
||||||
|
continue
|
||||||
|
store[hex(off)] = np.frombuffer(ab, dtype='<f4').astype(np.float32)
|
||||||
|
fn = f'{SNAPDIR}/hit{k:03d}.npz'
|
||||||
|
np.savez_compressed(fn, **store)
|
||||||
|
saved.append(fn)
|
||||||
|
sc = rd(ctx + 0x2404dc, 4)
|
||||||
|
print(f'[{k}] HIT fir!=identity -> {fn} '
|
||||||
|
f'(fir[0..5]={np.round(store["0x540668"][:6],4)})', flush=True)
|
||||||
|
finally:
|
||||||
|
os.kill(host, signal.SIGCONT)
|
||||||
|
time.sleep(float(rng.uniform(0.02, 0.09)))
|
||||||
|
os.close(fd)
|
||||||
|
if proc.poll() is None:
|
||||||
|
proc.kill()
|
||||||
|
print(f'done: {hits} hits / {nattempts} attempts -> {SNAPDIR}')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""firtrace.py — live capture of mask->FIR pipeline buffers during realtime
|
||||||
|
playback, with GUI-publish flag forced ON so bands[i] curves are copied into
|
||||||
|
the 0x540728+ slots inside the audio callback (decomp 529fe0:144-183).
|
||||||
|
|
||||||
|
Dumps full vector-object arrays for every ctx slot in [0x540500,0x540900):
|
||||||
|
reads {data_ptr, end_ptr, cap_ptr} (std::vector layout) when available,
|
||||||
|
falls back to fixed-length float view.
|
||||||
|
|
||||||
|
Usage: python3 scripts/firtrace.py [rpp] [nsnap]
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
SNAPDIR = '/tmp/opencode/firtrace'
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
import glob
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def mem_read(fd, addr, n):
|
||||||
|
try:
|
||||||
|
return os.pread(fd, n, addr)
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def mem_write(fd, addr, data):
|
||||||
|
try:
|
||||||
|
os.pwrite(fd, data, addr)
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def parse_snap(path):
|
||||||
|
data = open(path, 'rb').read()
|
||||||
|
regs = []
|
||||||
|
i = 0
|
||||||
|
while i + 16 <= len(data):
|
||||||
|
lo, sz = struct.unpack_from('<QQ', data, i)
|
||||||
|
regs.append((lo, data[i + 16:i + 16 + sz]))
|
||||||
|
i += 16 + sz
|
||||||
|
return regs
|
||||||
|
|
||||||
|
|
||||||
|
def readabs(regs, addr, n):
|
||||||
|
for lo, body in regs:
|
||||||
|
if lo <= addr < lo + len(body) and addr - lo + n <= len(body):
|
||||||
|
return body[addr - lo:addr - lo + n]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_ctx(regs):
|
||||||
|
sig = struct.pack('<I', 0x473b8000)
|
||||||
|
cands = []
|
||||||
|
for lo, body in regs:
|
||||||
|
j = body.find(sig)
|
||||||
|
while j >= 0:
|
||||||
|
base = lo + j - 0x24
|
||||||
|
b = readabs(regs, base + 0x540870, 4)
|
||||||
|
if b and struct.unpack('<f', b)[0] > 100:
|
||||||
|
cands.append(base)
|
||||||
|
j = body.find(sig, j + 1)
|
||||||
|
return sorted(set(cands))
|
||||||
|
|
||||||
|
|
||||||
|
SLOTS = list(range(0x540600, 0x540900, 8))
|
||||||
|
EXTRA = [0x540548, 0x540550, 0x540558, 0x540560, 0x540568, 0x540570,
|
||||||
|
0x540578, 0x540580, 0x540588, 0x540590, 0x540598, 0x5405a0,
|
||||||
|
0x5405a8, 0x5405b0, 0x5405b8, 0x5405c0, 0x5405c8, 0x5405d0,
|
||||||
|
0x5405d8, 0x5405e0, 0x5405e8, 0x5405f0, 0x5405f8]
|
||||||
|
|
||||||
|
|
||||||
|
def dump_slot(regs, ctx, off, maxf=65536):
|
||||||
|
"""Read qword at ctx+off; if it looks like a heap array, dump floats."""
|
||||||
|
b = readabs(regs, ctx + off, 8)
|
||||||
|
if not b:
|
||||||
|
return None
|
||||||
|
ptr = struct.unpack('<Q', b)[0]
|
||||||
|
if ptr < 0x10000 or ptr > 0x7fffffffffff:
|
||||||
|
return None
|
||||||
|
body = readabs(regs, ptr, min(maxf, 262144) * 4)
|
||||||
|
if body is None or len(body) < 64:
|
||||||
|
return None
|
||||||
|
a = np.frombuffer(body, dtype='<f4').astype(np.float64)
|
||||||
|
# trim trailing zeros beyond a floor of 2049 samples
|
||||||
|
nz = np.nonzero(a != 0)[0]
|
||||||
|
keep = max(2049, (nz[-1] + 1) if len(nz) else 0)
|
||||||
|
return a[:min(len(a), ((keep + 63) // 64) * 64)]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/dual_b1q_0.5.rpp'
|
||||||
|
nsnap = int(sys.argv[2]) if len(sys.argv) > 2 else 4
|
||||||
|
os.makedirs(SNAPDIR, exist_ok=True)
|
||||||
|
subprocess.run('pkill -9 -x reaser 2>/dev/null; pkill -9 -x reaper 2>/dev/null; '
|
||||||
|
"pkill -9 -f '[y]abridge' 2>/dev/null; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['/usr/bin/reaper', '-nosplash', '-ignoreerrors', rpp,
|
||||||
|
'/home/m/re-tools/play_loop.lua'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
t0 = time.time()
|
||||||
|
host = None
|
||||||
|
while time.time() - t0 < 60 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.2)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host', host, flush=True)
|
||||||
|
time.sleep(12)
|
||||||
|
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
# locate ctx via one full snapshot
|
||||||
|
snap0 = f'{SNAPDIR}/probe.bin'
|
||||||
|
snapshot(fd, host, snap0)
|
||||||
|
regs = parse_snap(snap0)
|
||||||
|
cands = find_ctx(regs)
|
||||||
|
print('ctx candidates:', ['0x%x' % c for c in cands][:5], flush=True)
|
||||||
|
if not cands:
|
||||||
|
return 1
|
||||||
|
ctx = cands[0]
|
||||||
|
|
||||||
|
# force GUI publish flag (byte at ctx+0x2404bc)
|
||||||
|
ok = mem_write(fd, ctx + 0x2404bc, b'\x01')
|
||||||
|
print('GUI flag write:', ok, flush=True)
|
||||||
|
|
||||||
|
store = {}
|
||||||
|
meta = {}
|
||||||
|
for k in range(nsnap):
|
||||||
|
time.sleep(1.5)
|
||||||
|
# re-arm flag (callback consumes it)
|
||||||
|
mem_write(fd, ctx + 0x2404bc, b'\x01')
|
||||||
|
time.sleep(0.05)
|
||||||
|
p = f'{SNAPDIR}/s{k}.bin'
|
||||||
|
snapshot(fd, host, p)
|
||||||
|
rg = parse_snap(p)
|
||||||
|
cs = find_ctx(rg)
|
||||||
|
if not cs:
|
||||||
|
continue
|
||||||
|
c = cs[0]
|
||||||
|
tag = f's{k}'
|
||||||
|
scal = {}
|
||||||
|
for a in range(0x540860, 0x5408a8, 4):
|
||||||
|
bb = readabs(rg, c + a, 4)
|
||||||
|
scal[hex(a)] = struct.unpack('<f', bb)[0] if bb else None
|
||||||
|
store[f'{tag}_scalars'] = json.dumps(scal)
|
||||||
|
for off in SLOTS + EXTRA:
|
||||||
|
arr = dump_slot(rg, c, off)
|
||||||
|
if arr is not None:
|
||||||
|
store[f'{tag}_{hex(off)}'] = arr
|
||||||
|
meta.setdefault(hex(off), []).append(len(arr))
|
||||||
|
print(f'snap {k}: dumped {sum(1 for x in store if x.startswith(tag+"_") and x!=tag+"_scalars")} arrays',
|
||||||
|
flush=True)
|
||||||
|
os.close(fd)
|
||||||
|
if proc.poll() is None:
|
||||||
|
proc.kill()
|
||||||
|
|
||||||
|
json.dump(meta, open(f'{SNAPDIR}/meta.json', 'w'), indent=1)
|
||||||
|
np.savez_compressed(f'{SNAPDIR}/firtrace.npz', **store)
|
||||||
|
print('saved', f'{SNAPDIR}/firtrace.npz')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def snapshot(fd, host, path):
|
||||||
|
out = open(path, 'wb')
|
||||||
|
nreg = nbytes = 0
|
||||||
|
for line in open(f'/proc/{host}/maps').read().splitlines():
|
||||||
|
p = line.split()
|
||||||
|
if len(p) < 2 or 'r' not in p[1]:
|
||||||
|
continue
|
||||||
|
lo, hi = (int(x, 16) for x in p[0].split('-'))
|
||||||
|
a = lo
|
||||||
|
while a < hi:
|
||||||
|
n = min(hi - a, 8 * 1024 * 1024)
|
||||||
|
d = mem_read(fd, a, n)
|
||||||
|
if d:
|
||||||
|
out.write(struct.pack('<QQ', a, len(d)))
|
||||||
|
out.write(d)
|
||||||
|
nreg += 1
|
||||||
|
nbytes += len(d)
|
||||||
|
a += n
|
||||||
|
out.close()
|
||||||
|
return nreg, nbytes
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
fit_vlaw_by_group.py — Fit VLAW parameters (α, β, c, Δ) per configuration group.
|
||||||
|
|
||||||
|
VLAW model (framed_model.cpp:205-208):
|
||||||
|
cs = α * log1p(lvl / β) + c + (delta ? Δ : 0)
|
||||||
|
applied_gain = 10^(-cs / 20) [gamma0=1 already absorbed into α,c,Δ]
|
||||||
|
|
||||||
|
Need to fit these for each (fc, q, sens) configuration group:
|
||||||
|
t1kq: fc=800..1200, q=1.0, sens=12 (input tone1kq)
|
||||||
|
t1k: fc=500..2000, q=1.0, sens=12 (input tone1k)
|
||||||
|
al: fc=1000, q=1.0, sens=3..24 (input lvl_tone_lvX)
|
||||||
|
res: fc=300..700, q=1.0, sens=12 (input resonant)
|
||||||
|
dual: fc=500, q=0.1..10.0, sens=12 (input dual)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
|
||||||
|
sys.path.insert(0, '/home/m/re-tools/scripts')
|
||||||
|
import corpus
|
||||||
|
|
||||||
|
corpus.RB = '/home/m/re-tools/dsp/build/render48k'
|
||||||
|
|
||||||
|
# Reference errors from baseline_bridge.json (target)
|
||||||
|
with open('scripts/baseline_bridge.json') as f:
|
||||||
|
REF_ERRORS = json.load(f)
|
||||||
|
|
||||||
|
def structural_cases():
|
||||||
|
out = []
|
||||||
|
for name, inp, args, ref, f in corpus.build_cases():
|
||||||
|
joined = [','.join(args)] if len(args) == 3 else args
|
||||||
|
out.append((name, inp, joined, ref, f))
|
||||||
|
return out
|
||||||
|
|
||||||
|
def run_vlaw(inp, args, alpha, beta, c, delta):
|
||||||
|
"""Run render48k with VLAW parameters and return output path."""
|
||||||
|
out = f'/tmp/vlaw_fit_{alpha}_{beta}_{c}_{delta}_{os.path.basename(inp)}.wav'
|
||||||
|
env = {
|
||||||
|
**os.environ,
|
||||||
|
'RT_VLAW': '1',
|
||||||
|
'RT_VLAW_ALPHA': str(alpha),
|
||||||
|
'RT_VLAW_BETA': str(beta),
|
||||||
|
'RT_VLAW_C': str(c),
|
||||||
|
'RT_VLAW_DELTA': str(delta),
|
||||||
|
'RT_SYN': '1',
|
||||||
|
'RT_NOWARP': '1',
|
||||||
|
'RT_NOIIR3': '1',
|
||||||
|
'RT_IIR12': '0',
|
||||||
|
}
|
||||||
|
subprocess.run(
|
||||||
|
[corpus.RB, inp, out] + args,
|
||||||
|
capture_output=True, text=True, env=env,
|
||||||
|
cwd='/home/m/re-tools'
|
||||||
|
)
|
||||||
|
return out
|
||||||
|
|
||||||
|
def eval_error(out, ref, f):
|
||||||
|
"""Evaluate error in dB between output and reference at frequency f."""
|
||||||
|
if not os.path.exists(out) or os.path.getsize(out) == 0:
|
||||||
|
return None
|
||||||
|
ref_sig = corpus.load_mono(ref)
|
||||||
|
out_sig = corpus.load_mono(out)
|
||||||
|
min_len = min(len(ref_sig), len(out_sig))
|
||||||
|
ref_sig = ref_sig[-min_len:]
|
||||||
|
out_sig = out_sig[-min_len:]
|
||||||
|
ref_ta = corpus.ta(ref_sig, f)
|
||||||
|
out_ta = corpus.ta(out_sig, f)
|
||||||
|
return corpus.db(out_ta / ref_ta)
|
||||||
|
|
||||||
|
def group_key(name):
|
||||||
|
return name.split('_')[0]
|
||||||
|
|
||||||
|
def evaluate_params(alpha, beta, c, delta, cases_subset=None):
|
||||||
|
"""Evaluate VLAW params on all cases, return per-group mean abs error."""
|
||||||
|
all_cases = structural_cases()
|
||||||
|
if cases_subset:
|
||||||
|
all_cases = [c for c in all_cases if group_key(c[0]) in cases_subset]
|
||||||
|
|
||||||
|
errs = {}
|
||||||
|
|
||||||
|
for name, inp, args, ref, f in all_cases:
|
||||||
|
out = run_vlaw(inp, args, alpha, beta, c, delta)
|
||||||
|
err = eval_error(out, ref, f)
|
||||||
|
if err is not None:
|
||||||
|
errs[name] = err
|
||||||
|
|
||||||
|
# Group stats
|
||||||
|
groups = {}
|
||||||
|
for k, v in errs.items():
|
||||||
|
g = group_key(k)
|
||||||
|
groups.setdefault(g, []).append(v)
|
||||||
|
|
||||||
|
out_stats = {g: float(np.mean(np.abs(v))) for g, v in groups.items()}
|
||||||
|
out_stats['TOTAL'] = float(np.mean(np.abs(list(errs.values()))))
|
||||||
|
return out_stats, errs
|
||||||
|
|
||||||
|
def fit_single_case(name, inp, args, ref, f, init_params):
|
||||||
|
"""Grid search for best params on a single case."""
|
||||||
|
alpha0, beta0, c0, delta0 = init_params
|
||||||
|
best = None
|
||||||
|
best_err = float('inf')
|
||||||
|
|
||||||
|
# Search around initial params
|
||||||
|
alphas = np.linspace(max(0.5, alpha0-1), alpha0+1, 9)
|
||||||
|
betas = np.linspace(max(0.1, beta0-0.2), beta0+0.2, 9)
|
||||||
|
cs = np.linspace(max(0.0, c0-0.5), c0+0.5, 9)
|
||||||
|
deltas = np.linspace(max(0.0, delta0-2), delta0+2, 9)
|
||||||
|
|
||||||
|
for alpha in alphas:
|
||||||
|
for beta in betas:
|
||||||
|
for c in cs:
|
||||||
|
for delta in deltas:
|
||||||
|
out = run_vlaw(inp, args, alpha, beta, c, delta)
|
||||||
|
err = eval_error(out, ref, f)
|
||||||
|
if err is not None and abs(err) < best_err:
|
||||||
|
best_err = abs(err)
|
||||||
|
best = (alpha, beta, c, delta, err)
|
||||||
|
print(f' {name}: new best α={alpha:.3f}, β={beta:.3f}, c={c:.3f}, Δ={delta:.3f} => err={err:.3f} dB')
|
||||||
|
|
||||||
|
return best
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Build case map by group
|
||||||
|
all_cases = structural_cases()
|
||||||
|
groups = {}
|
||||||
|
for name, inp, args, ref, f in all_cases:
|
||||||
|
g = group_key(name)
|
||||||
|
groups.setdefault(g, []).append((name, inp, args, ref, f))
|
||||||
|
|
||||||
|
print("Available groups:", list(groups.keys()))
|
||||||
|
for g, cases in groups.items():
|
||||||
|
print(f" {g}: {len(cases)} cases")
|
||||||
|
|
||||||
|
# Current calibrated params for dual(q=0.5)
|
||||||
|
dual_params = (3.2193, 0.4927, 0.5423, 6.9177)
|
||||||
|
|
||||||
|
# Test current params on all groups
|
||||||
|
print("\n=== Testing current dual params on all groups ===")
|
||||||
|
stats, _ = evaluate_params(*dual_params)
|
||||||
|
for g in ['t1kq', 't1k', 'al', 'res', 'dual', 'comb']:
|
||||||
|
if g in stats:
|
||||||
|
print(f' {g}: {stats[g]:.3f} dB')
|
||||||
|
|
||||||
|
# For each group, pick a representative case and fit
|
||||||
|
print("\n=== Fitting per group (representative case) ===")
|
||||||
|
results = {}
|
||||||
|
|
||||||
|
# For dual, use q=0.5 as reference (already calibrated)
|
||||||
|
if 'dual' in groups:
|
||||||
|
# Find q=0.5 case
|
||||||
|
for name, inp, args, ref, f in groups['dual']:
|
||||||
|
if '0.5' in name:
|
||||||
|
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||||
|
if best:
|
||||||
|
results['dual'] = best[:4]
|
||||||
|
break
|
||||||
|
|
||||||
|
# For t1kq, use fc=1000
|
||||||
|
if 't1kq' in groups:
|
||||||
|
for name, inp, args, ref, f in groups['t1kq']:
|
||||||
|
if '1000' in name:
|
||||||
|
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||||
|
if best:
|
||||||
|
results['t1kq'] = best[:4]
|
||||||
|
break
|
||||||
|
|
||||||
|
# For t1k, use fc=1000
|
||||||
|
if 't1k' in groups:
|
||||||
|
for name, inp, args, ref, f in groups['t1k']:
|
||||||
|
if '1000' in name:
|
||||||
|
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||||
|
if best:
|
||||||
|
results['t1k'] = best[:4]
|
||||||
|
break
|
||||||
|
|
||||||
|
# For al, use sens=12
|
||||||
|
if 'al' in groups:
|
||||||
|
for name, inp, args, ref, f in groups['al']:
|
||||||
|
if '12' in name:
|
||||||
|
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||||
|
if best:
|
||||||
|
results['al'] = best[:4]
|
||||||
|
break
|
||||||
|
|
||||||
|
# For res, use fc=500
|
||||||
|
if 'res' in groups:
|
||||||
|
for name, inp, args, ref, f in groups['res']:
|
||||||
|
if '500' in name:
|
||||||
|
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||||
|
if best:
|
||||||
|
results['res'] = best[:4]
|
||||||
|
break
|
||||||
|
|
||||||
|
# Print results
|
||||||
|
print("\n=== FITTED VLAW PARAMETERS BY GROUP ===")
|
||||||
|
for g, (alpha, beta, c, delta) in results.items():
|
||||||
|
print(f'{g}: α={alpha:.4f}, β={beta:.4f}, c={c:.4f}, Δ={delta:.4f}')
|
||||||
|
|
||||||
|
# Save to JSON
|
||||||
|
with open('/tmp/opencode/vlaw_params.json', 'w') as f:
|
||||||
|
json.dump({g: {'alpha': a, 'beta': b, 'c': c, 'delta': d}
|
||||||
|
for g, (a, b, c, d) in results.items()}, f, indent=2)
|
||||||
|
print('\nSaved to /tmp/opencode/vlaw_params.json')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
fit_vlaw_params.py — Fit VLAW parameters (α, β, c, Δ, γ₀) per configuration group.
|
||||||
|
|
||||||
|
VLAW model (framed_model.cpp:198-200):
|
||||||
|
cs = α * log1p(lvl / β) + c + (delta ? Δ : 0)
|
||||||
|
applied_gain = 10^(-γ₀ * cs / 20)
|
||||||
|
|
||||||
|
Currently hardcoded for dual(q=0.5): α=3.2193, β=0.4927, c=0.5423, Δ=7.46-0.5423, γ₀=1.79
|
||||||
|
|
||||||
|
Need to fit these for each (fc, q, sens) configuration group:
|
||||||
|
t1kq: fc=800..1200, q=1.0, sens=12
|
||||||
|
t1k: fc=500..2000, q=1.0, sens=12
|
||||||
|
al: fc=1000, q=1.0, sens=3..24
|
||||||
|
res: fc=300..700, q=1.0, sens=12
|
||||||
|
dual: fc=500, q=0.1..10.0, sens=12
|
||||||
|
"""
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
sys.path.insert(0, '/home/m/re-tools/scripts')
|
||||||
|
import corpus
|
||||||
|
|
||||||
|
corpus.RB = '/home/m/re-tools/dsp/build/render48k'
|
||||||
|
|
||||||
|
def structural_cases():
|
||||||
|
out = []
|
||||||
|
for name, inp, args, ref, f in corpus.build_cases():
|
||||||
|
joined = [','.join(args)] if len(args) == 3 else args
|
||||||
|
out.append((name, inp, joined, ref, f))
|
||||||
|
return out
|
||||||
|
|
||||||
|
def group_key(name):
|
||||||
|
return name.split('_')[0]
|
||||||
|
|
||||||
|
def load_ref_errors():
|
||||||
|
"""Load baseline_bridge.json for target errors."""
|
||||||
|
with open('scripts/baseline_bridge.json') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
def render_vlaw(inp, out, args, alpha, beta, c, delta, gamma0, extra_env=None):
|
||||||
|
"""Run render48k with VLAW parameters."""
|
||||||
|
env = {
|
||||||
|
**os.environ,
|
||||||
|
'RT_VLAW': '1',
|
||||||
|
'RT_VLAW_ALPHA': str(alpha),
|
||||||
|
'RT_VLAW_BETA': str(beta),
|
||||||
|
'RT_VLAW_C': str(c),
|
||||||
|
'RT_VLAW_DELTA': str(delta),
|
||||||
|
'RT_VLAW_GAMMA0': str(gamma0),
|
||||||
|
'RT_SYN': '1',
|
||||||
|
'RT_NOWARP': '1',
|
||||||
|
'RT_NOIIR3': '1',
|
||||||
|
'RT_IIR12': '0',
|
||||||
|
}
|
||||||
|
if extra_env:
|
||||||
|
env.update(extra_env)
|
||||||
|
subprocess.run(
|
||||||
|
[corpus.RB, inp, out] + args,
|
||||||
|
capture_output=True, text=True, env=env,
|
||||||
|
cwd='/home/m/re-tools'
|
||||||
|
)
|
||||||
|
|
||||||
|
def eval_config(alpha, beta, c, delta, gamma0, cases_subset=None):
|
||||||
|
"""Evaluate VLAW params on cases, return per-group mean abs error."""
|
||||||
|
all_cases = structural_cases()
|
||||||
|
if cases_subset:
|
||||||
|
all_cases = [c for c in all_cases if group_key(c[0]) in cases_subset]
|
||||||
|
|
||||||
|
refs = load_ref_errors()
|
||||||
|
errs = {}
|
||||||
|
|
||||||
|
for name, inp, args, ref, f in all_cases:
|
||||||
|
out = f'/tmp/vlaw_fit_{name}.wav'
|
||||||
|
render_vlaw(inp, out, args, alpha, beta, c, delta, gamma0)
|
||||||
|
|
||||||
|
if not os.path.exists(out) or os.path.getsize(out) == 0:
|
||||||
|
errs[name] = 999.0
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
ref_sig = corpus.load_mono(ref)
|
||||||
|
out_sig = corpus.load_mono(out)
|
||||||
|
min_len = min(len(ref_sig), len(out_sig))
|
||||||
|
ref_sig = ref_sig[-min_len:]
|
||||||
|
out_sig = out_sig[-min_len:]
|
||||||
|
|
||||||
|
ref_ta = corpus.ta(ref_sig, f)
|
||||||
|
out_ta = corpus.ta(out_sig, f)
|
||||||
|
err_db = corpus.db(out_ta / ref_ta)
|
||||||
|
errs[name] = err_db
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error on {name}: {e}")
|
||||||
|
errs[name] = 999.0
|
||||||
|
|
||||||
|
# Group stats
|
||||||
|
groups = {}
|
||||||
|
for k, v in errs.items():
|
||||||
|
g = group_key(k)
|
||||||
|
groups.setdefault(g, []).append(v)
|
||||||
|
|
||||||
|
out = {g: float(np.mean(np.abs(v))) for g, v in groups.items()}
|
||||||
|
out['TOTAL'] = float(np.mean(np.abs(list(errs.values()))))
|
||||||
|
return out, errs
|
||||||
|
|
||||||
|
def fit_alpha_beta_c(cases_to_fit):
|
||||||
|
"""Coordinate descent on (α, β, c) for a specific case group."""
|
||||||
|
# For now, grid search
|
||||||
|
best = None
|
||||||
|
best_err = float('inf')
|
||||||
|
|
||||||
|
# Search ranges around current dual(q=0.5) values
|
||||||
|
for alpha in np.linspace(2.5, 4.0, 8):
|
||||||
|
for beta in np.linspace(0.3, 0.7, 8):
|
||||||
|
for c in np.linspace(0.2, 1.0, 8):
|
||||||
|
stats, _ = eval_config(alpha, beta, c, 6.9, 1.79, cases_to_fit)
|
||||||
|
total = stats['TOTAL']
|
||||||
|
if total < best_err:
|
||||||
|
best_err = total
|
||||||
|
best = (alpha, beta, c, stats)
|
||||||
|
print(f" New best: α={alpha:.4f}, β={beta:.4f}, c={c:.4f}, TOTAL={total:.4f}")
|
||||||
|
|
||||||
|
return best
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Build case map by group
|
||||||
|
all_cases = structural_cases()
|
||||||
|
groups = {}
|
||||||
|
for name, inp, args, ref, f in all_cases:
|
||||||
|
g = group_key(name)
|
||||||
|
groups.setdefault(g, []).append(name)
|
||||||
|
|
||||||
|
print("Available groups:", list(groups.keys()))
|
||||||
|
for g, names in groups.items():
|
||||||
|
print(f" {g}: {len(names)} cases")
|
||||||
|
|
||||||
|
# Start with dual group (already calibrated)
|
||||||
|
print("\n=== Testing dual(q=0.5) baseline ===")
|
||||||
|
stats, errs = eval_config(3.2193, 0.4927, 0.5423, 6.9177, 1.79, ['dual'])
|
||||||
|
print(f"Dual stats: {stats}")
|
||||||
|
|
||||||
|
# Now fit for each group
|
||||||
|
for g in ['t1kq', 't1k', 'al', 'res', 'dual']:
|
||||||
|
if g not in groups:
|
||||||
|
continue
|
||||||
|
print(f"\n=== Fitting {g} ===")
|
||||||
|
best = fit_alpha_beta_c([g])
|
||||||
|
if best:
|
||||||
|
alpha, beta, c, stats = best
|
||||||
|
print(f" {g} best: α={alpha:.4f}, β={beta:.4f}, c={c:.4f}")
|
||||||
|
print(f" Stats: {stats}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fnall.py — safe full-thread INT3 tracer.
|
||||||
|
|
||||||
|
Invariant: every thread is SEIZE+INTERRUPT-stopped BEFORE the breakpoint is
|
||||||
|
armed, so any later clone descends from a traced thread and its SIGTRAPs come
|
||||||
|
to us instead of killing the host.
|
||||||
|
|
||||||
|
Env: FN_ADDR, FN_DUR, FN_MAXHITS
|
||||||
|
"""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import numpy as np
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
FN = int(os.environ.get('FN_ADDR', '0x180529FE0'), 16)
|
||||||
|
DUR = float(os.environ.get('FN_DUR', '15'))
|
||||||
|
MAXHITS = int(os.environ.get('FN_MAXHITS', '50'))
|
||||||
|
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_PEEKDATA = 2
|
||||||
|
PTRACE_POKETEXT = 4
|
||||||
|
PTRACE_SINGLESTEP = 9
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_O_TRACECLONE = 0x00000002
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
def pt(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
if r == -1:
|
||||||
|
return None, ctypes.get_errno()
|
||||||
|
return r, 0
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
# proven maps-based finder (same as rendersnap.py)
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
def drain_waits():
|
||||||
|
out = []
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == 0:
|
||||||
|
break
|
||||||
|
out.append((pid, status))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', '/home/m/soothe-bt/dual_b1q_0.5.rpp'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
if host is None and time.time() - t0 > 2:
|
||||||
|
# fallback: maps-based
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
host = pid
|
||||||
|
break
|
||||||
|
time.sleep(0.002)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host %d at %.3fs' % (host, time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
# phase 1: seize main, interrupt, wait stop
|
||||||
|
seized = []
|
||||||
|
r, e = pt(PTRACE_SEIZE, host, 0, PTRACE_O_TRACECLONE)
|
||||||
|
if r is None:
|
||||||
|
print('seize fail', e)
|
||||||
|
return 1
|
||||||
|
seized.append(host)
|
||||||
|
pt(PTRACE_INTERRUPT, host)
|
||||||
|
|
||||||
|
# phase 2: pump events, seize newcomers until set stabilizes
|
||||||
|
stable_until = time.time() + float(os.environ.get('FN_STAB','0.05'))
|
||||||
|
deadline = time.time() + 3.0
|
||||||
|
while time.time() < deadline:
|
||||||
|
for pid, st in drain_waits():
|
||||||
|
pass # they stay stopped; we hold them
|
||||||
|
grew = False
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
r, e = pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
if r is not None or e != 3:
|
||||||
|
seized.append(tid)
|
||||||
|
pt(PTRACE_INTERRUPT, tid)
|
||||||
|
grew = True
|
||||||
|
if grew:
|
||||||
|
stable_until = time.time() + 0.5
|
||||||
|
elif time.time() > stable_until:
|
||||||
|
break
|
||||||
|
time.sleep(0.002)
|
||||||
|
print('stopped %d threads' % len(seized), flush=True)
|
||||||
|
|
||||||
|
orig, _ = pt(PTRACE_PEEKDATA, host, FN)
|
||||||
|
cc = (orig & ~0xFF) | 0xCC
|
||||||
|
r, e = pt(PTRACE_POKETEXT, host, FN, cc)
|
||||||
|
if r is None:
|
||||||
|
print('arm fail', e)
|
||||||
|
return 1
|
||||||
|
print('armed %#x' % FN, flush=True)
|
||||||
|
|
||||||
|
log = []
|
||||||
|
hits = 0
|
||||||
|
EVENTS = []
|
||||||
|
for tid in seized:
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
|
||||||
|
t_end = time.time() + DUR
|
||||||
|
last_resweep = time.time()
|
||||||
|
while hits < MAXHITS and time.time() < t_end:
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
EVENTS.append((time.time()-t0, pid, hex(status), status >> 16))
|
||||||
|
if pid == 0:
|
||||||
|
if time.time() - last_resweep > 0.05:
|
||||||
|
last_resweep = time.time()
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
r, e = pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
if r is not None or e != 3:
|
||||||
|
seized.append(tid)
|
||||||
|
pt(PTRACE_INTERRUPT, tid)
|
||||||
|
drain_waits()
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
time.sleep(0.0005)
|
||||||
|
continue
|
||||||
|
sig = status >> 8
|
||||||
|
ev = status >> 16
|
||||||
|
if os.WIFEXITED(status) or os.WIFSIGNALED(status):
|
||||||
|
EVENTS.append((time.time()-t0, pid, hex(status), ev))
|
||||||
|
continue
|
||||||
|
if ev == 3 or ev == 1:
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if os.WIFSTOPPED(pid) and sig == signal.SIGTRAP and ev == 0:
|
||||||
|
regs = UserRegs()
|
||||||
|
r, _ = pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
if r is None:
|
||||||
|
continue
|
||||||
|
if regs.rip - 1 == FN:
|
||||||
|
ret, _ = pt(PTRACE_PEEKDATA, pid, regs.rsp)
|
||||||
|
rec = dict(ctx=regs.rcx, a2=regs.rdx, cnt=regs.r8 & 0xffffffff,
|
||||||
|
r9=regs.r9 & 0xffffffff, ret=ret,
|
||||||
|
rbx=regs.rbx, r12=regs.r12, r13=regs.r13,
|
||||||
|
r14=regs.r14, r15=regs.r15, rsp=regs.rsp, tid=pid,
|
||||||
|
dump_in=[], dump_out=[], dump_r14=[], dump_rbx=[])
|
||||||
|
# dump arrays referenced by registers (process is stopped)
|
||||||
|
fdm = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
|
||||||
|
def rdarr(ptr, n=2049):
|
||||||
|
try:
|
||||||
|
b = os.pread(fdm, n * 4, ptr)
|
||||||
|
return np.frombuffer(b, dtype='<f4').astype(np.float32).tolist()
|
||||||
|
except OSError:
|
||||||
|
return []
|
||||||
|
rec['dump_in'] = rdarr(regs.rcx)
|
||||||
|
rec['dump_out'] = rdarr(regs.rdx)
|
||||||
|
if FN != 0x18052dbc0:
|
||||||
|
rec['dump_r14'] = rdarr(regs.r14)
|
||||||
|
os.close(fdm)
|
||||||
|
log.append(rec)
|
||||||
|
hits += 1
|
||||||
|
if hits <= 20:
|
||||||
|
print('HIT ctx=%#x a2=%#x cnt=%#x ret=%#x'
|
||||||
|
% (rec['ctx'], rec['a2'], rec['cnt'], rec['ret']), flush=True)
|
||||||
|
pt(PTRACE_POKETEXT, pid, FN, orig)
|
||||||
|
regs.rip = FN
|
||||||
|
pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, os.WUNTRACED)
|
||||||
|
pt(PTRACE_POKETEXT, pid, FN, cc)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
else:
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
elif os.WIFSTOPPED(pid):
|
||||||
|
pt(PTRACE_CONT, pid, 0, sig if 0 < sig < 32 else 0)
|
||||||
|
|
||||||
|
pt(PTRACE_POKETEXT, host, FN, orig)
|
||||||
|
print('total hits:', hits)
|
||||||
|
json.dump(dict(log=log, events=EVENTS[:8000], nseized=len(seized)),
|
||||||
|
open('/tmp/opencode/fntrace/allhits.json', 'w'), indent=1)
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fnexec.py — hardware EXEC breakpoints on 529fe0 entry and FIR-loop head.
|
||||||
|
Answers definitively whether the decoded mask chain executes during render."""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import numpy as np
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
DUR = float(os.environ.get('EXEC_DUR', '20'))
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_PEEKUSER = 3
|
||||||
|
PTRACE_POKEUSER = 6
|
||||||
|
PTRACE_SINGLESTEP = 9
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_O_TRACECLONE = 2
|
||||||
|
|
||||||
|
DR_BASE = 0x350
|
||||||
|
DR7_OFF = DR_BASE + 56
|
||||||
|
# DR0=exec 529fe0, DR1=exec 52b550; RW=00 LEN=00 both; L0,L1 enabled
|
||||||
|
DR7_VAL = 0x00000003
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
def pt(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
return None if r == -1 else r
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True)
|
||||||
|
wav = '/home/m/soothe-bt/dual_b1q_0.5.wav'
|
||||||
|
wt0 = os.path.getmtime(wav) if os.path.exists(wav) else 0
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', '/home/m/soothe-bt/dual_b1q_0.5.rpp'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.002)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host %d at %.2fs' % (host, time.time() - t0), flush=True)
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
|
||||||
|
# EARLY arm: exec watch on DESIGN bodies (inherited by future clones)
|
||||||
|
fd0 = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
pt(PTRACE_SEIZE, host, 0, PTRACE_O_TRACECLONE)
|
||||||
|
pt(PTRACE_INTERRUPT, host)
|
||||||
|
for _ in range(60):
|
||||||
|
try:
|
||||||
|
wpid, _st = os.waitpid(host, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if wpid == host:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
pt(PTRACE_POKEUSER, host, DR_BASE + 0, 0x1802A24C0)
|
||||||
|
pt(PTRACE_POKEUSER, host, DR_BASE + 8, 0x1802FA420)
|
||||||
|
pt(PTRACE_POKEUSER, host, DR_BASE + 16, 0x180535A70)
|
||||||
|
pt(PTRACE_POKEUSER, host, DR_BASE + 24, 0x18052D650)
|
||||||
|
pt(PTRACE_POKEUSER, host, DR7_OFF, 0xF)
|
||||||
|
ok0 = pt(PTRACE_PEEKUSER, host, DR7_OFF)
|
||||||
|
print('early arm dr7=%#x' % (ok0 or 0), flush=True)
|
||||||
|
pt(PTRACE_CONT, host, 0, 0)
|
||||||
|
|
||||||
|
# phase B: seize ALL tids NOW and arm exec watches
|
||||||
|
def arm(tid):
|
||||||
|
pt(PTRACE_POKEUSER, tid, DR_BASE + 0, 0x180529FE0)
|
||||||
|
pt(PTRACE_POKEUSER, tid, DR_BASE + 8, 0x18052B550)
|
||||||
|
pt(PTRACE_POKEUSER, tid, DR7_OFF, DR7_VAL)
|
||||||
|
v = pt(PTRACE_PEEKUSER, tid, DR7_OFF)
|
||||||
|
return v is not None and (v & ~0x400) == DR7_VAL
|
||||||
|
|
||||||
|
seized = {host}
|
||||||
|
armed = set()
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*') or [host]:
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
pt(PTRACE_INTERRUPT, tid)
|
||||||
|
for _ in range(40):
|
||||||
|
try:
|
||||||
|
wpid, _st = os.waitpid(tid, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if wpid == tid:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
if arm(tid):
|
||||||
|
armed.add(tid)
|
||||||
|
try:
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
print('armed %d/%d tids' % (len(armed), len(seized)), flush=True)
|
||||||
|
|
||||||
|
n0 = n1 = 0
|
||||||
|
STOPLOG=[]
|
||||||
|
samples = []
|
||||||
|
t_end = time.time() + DUR
|
||||||
|
last_sweep = 0.0
|
||||||
|
fresh_t = None
|
||||||
|
while time.time() < t_end:
|
||||||
|
now = time.time()
|
||||||
|
if now - last_sweep > 0.03:
|
||||||
|
last_sweep = now
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
if pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE) is not None:
|
||||||
|
seized.add(tid)
|
||||||
|
pt(PTRACE_INTERRUPT, tid)
|
||||||
|
for _ in range(40):
|
||||||
|
try:
|
||||||
|
wpid, _st = os.waitpid(tid, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if wpid == tid:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
if arm(tid):
|
||||||
|
armed.add(tid)
|
||||||
|
d0=pt(PTRACE_PEEKUSER,tid,DR_BASE)
|
||||||
|
d7=pt(PTRACE_PEEKUSER,tid,DR7_OFF)
|
||||||
|
print(' tid %d dr0=%#x dr7=%#x'%(tid,d0 or 0,d7 or 0),flush=True)
|
||||||
|
try:
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == 0:
|
||||||
|
time.sleep(0.0004)
|
||||||
|
continue
|
||||||
|
ent=[round(time.time()-t0,3), pid, hex(status)]
|
||||||
|
if os.WIFSTOPPED(pid) and (status>>8)==5:
|
||||||
|
rg=UserRegs()
|
||||||
|
if pt(PTRACE_GETREGS,pid,0,ctypes.addressof(rg)) is not None:
|
||||||
|
ent+= [rg.rip, pt(PTRACE_PEEKUSER,pid,DR_BASE+48)]
|
||||||
|
# auto-dump arrays on design hits
|
||||||
|
if rg.rip in (0x1802A24C0,0x1802FA420):
|
||||||
|
def dmp(ptr):
|
||||||
|
try:
|
||||||
|
b=os.pread(fd,2049*4,ptr)
|
||||||
|
return np.frombuffer(b,dtype='<f4').astype(np.float32).tolist()
|
||||||
|
except OSError: return []
|
||||||
|
ent.append({'rcx':rg.rcx,'rdx':rg.rdx,
|
||||||
|
'in':dmp(rg.rcx),'out':dmp(rg.rdx)})
|
||||||
|
STOPLOG.append(ent)
|
||||||
|
sig = status >> 8
|
||||||
|
if os.WIFEXITED(status) or os.WIFSIGNALED(status):
|
||||||
|
continue
|
||||||
|
if os.WIFSTOPPED(pid) and sig == signal.SIGTRAP:
|
||||||
|
regs = UserRegs()
|
||||||
|
if pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs)) is None:
|
||||||
|
continue
|
||||||
|
rip = regs.rip - (1 if False else 0)
|
||||||
|
if pid not in armed:
|
||||||
|
seized.add(pid)
|
||||||
|
if arm(pid):
|
||||||
|
armed.add(pid)
|
||||||
|
# re-read regs after arming? DR change does not touch GPRs
|
||||||
|
if rip not in (0x180529FE0, 0x18052B550, 0x1802A24C0, 0x1802FA420, 0x180535A70, 0x18052D650):
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if rip == 0x180529FE0:
|
||||||
|
n0 += 1
|
||||||
|
elif rip == 0x18052B550:
|
||||||
|
n1 += 1
|
||||||
|
rec = dict(which='design' if rip in (0x1802A24C0, 0x1802FA420)
|
||||||
|
else ('529fe0' if rip == 0x180529FE0 else 'loop'),
|
||||||
|
rip=rip, rcx=regs.rcx, rdx=regs.rdx,
|
||||||
|
cnt=regs.r8 & 0xffffffff,
|
||||||
|
t=round(time.time() - t0, 3), tid=pid)
|
||||||
|
# dump band curve (rcx) and scratch (rdx) for design hits
|
||||||
|
if rec['which'] == 'design':
|
||||||
|
def rdarr(ptr, n=2049):
|
||||||
|
try:
|
||||||
|
b = os.pread(fd, n * 4, ptr)
|
||||||
|
return np.frombuffer(b, dtype='<f4').astype(np.float32).tolist()
|
||||||
|
except OSError:
|
||||||
|
return []
|
||||||
|
rec['in'] = rdarr(regs.rcx)
|
||||||
|
rec['out'] = rdarr(regs.rdx)
|
||||||
|
print('DESIGN hit tid=%d rcx=%#x rdx=%#x cnt=%#x in[85]=%g'
|
||||||
|
% (pid, regs.rcx, regs.rdx, rec['cnt'],
|
||||||
|
rec['in'][85] if rec['in'] else -1), flush=True)
|
||||||
|
samples.append(rec)
|
||||||
|
if len(samples) > 400:
|
||||||
|
break
|
||||||
|
# pass exec trap: RF flag suppresses next report
|
||||||
|
pt(PTRACE_POKEUSER, pid, DR_BASE + 48, 0xFFFF0FF0)
|
||||||
|
regs.eflags |= 0x10000
|
||||||
|
pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
elif os.WIFSTOPPED(pid):
|
||||||
|
if pid not in armed and sig == signal.SIGTRAP:
|
||||||
|
seized.add(pid)
|
||||||
|
if arm(pid):
|
||||||
|
armed.add(pid)
|
||||||
|
pt(PTRACE_CONT, pid, 0, sig if 0 < sig < 32 else 0)
|
||||||
|
if fresh_t is None and os.path.exists(wav) and os.path.getmtime(wav) > wt0:
|
||||||
|
fresh_t = time.time() - t0
|
||||||
|
print('wav fresh at %.2fs' % fresh_t, flush=True)
|
||||||
|
|
||||||
|
fresh = fresh_t is not None
|
||||||
|
print('hits: 529fe0=%d loop=%d render_fresh=%s armed=%d/%d'
|
||||||
|
% (n0, n1, fresh, len(armed), len(seized)))
|
||||||
|
import json
|
||||||
|
json.dump(dict(samples=samples[:300], stops=STOPLOG[:4000]),
|
||||||
|
open('/tmp/opencode/fnexec/hits.json', 'w'), indent=1, default=str)
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def rdsp(sp, pid):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
os.makedirs('/tmp/opencode/fnexec', exist_ok=True)
|
||||||
|
sys.exit(main())
|
||||||
+224
@@ -0,0 +1,224 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fnhw.py — hardware-breakpoint tracer (DR0) for the yabridge host.
|
||||||
|
Safe for un-traced threads (code is never patched). Auto-arms new threads.
|
||||||
|
|
||||||
|
Env: FN_ADDR (target VA), FN_DUR (seconds), FN_SKIP (skip first N hits)
|
||||||
|
"""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
FN = int(os.environ.get('FN_ADDR', '0x180529FE0'), 16)
|
||||||
|
DUR = float(os.environ.get('FN_DUR', '15'))
|
||||||
|
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_PEEKUSER = 3
|
||||||
|
PTRACE_POKEUSER = 6
|
||||||
|
PTRACE_SINGLESTEP = 9
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
def pt(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
if r == -1:
|
||||||
|
return None, ctypes.get_errno()
|
||||||
|
return r, 0
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_dr0_off(pid):
|
||||||
|
"""Locate DR0 slot inside the USER area by probing."""
|
||||||
|
probe = 0x1234567890abcdef
|
||||||
|
for off in range(0x200, 0x400, 8):
|
||||||
|
r, e = pt(PTRACE_POKEUSER, pid, off, probe)
|
||||||
|
if r is None:
|
||||||
|
continue
|
||||||
|
v, _ = pt(PTRACE_PEEKUSER, pid, off)
|
||||||
|
if v == (probe & 0xFFFFFFFFFFFFFFFF):
|
||||||
|
# restore zero & sanity-check neighbours exist
|
||||||
|
pt(PTRACE_POKEUSER, pid, off, 0)
|
||||||
|
return off
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def arm(tid, dr0_off):
|
||||||
|
"""Arm DR0/DR7 - requires tid to be STOPPED; caller handles stop/cont."""
|
||||||
|
pt(PTRACE_POKEUSER, tid, dr0_off, FN)
|
||||||
|
# DR7 (index 7): L0=1, RW0=00 (exec), LEN0=00
|
||||||
|
pt(PTRACE_POKEUSER, tid, dr0_off + 7 * 8, 0x1)
|
||||||
|
v, _ = pt(PTRACE_PEEKUSER, tid, dr0_off + 7 * 8)
|
||||||
|
return v == 1
|
||||||
|
|
||||||
|
|
||||||
|
def disarm(tid, dr0_off):
|
||||||
|
pt(PTRACE_POKEUSER, tid, dr0_off + 7 * 8, 0)
|
||||||
|
pt(PTRACE_POKEUSER, tid, dr0_off, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def arm_stopped(tid, dr0_off):
|
||||||
|
"""INTERRUPT -> (bounded) wait -> arm -> CONT. True if DR7 verified."""
|
||||||
|
pt(PTRACE_INTERRUPT, tid)
|
||||||
|
for _ in range(30):
|
||||||
|
try:
|
||||||
|
pid, st = os.waitpid(tid, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
return False
|
||||||
|
if pid == tid:
|
||||||
|
break
|
||||||
|
time.sleep(0.002)
|
||||||
|
r0, e0 = pt(PTRACE_POKEUSER, tid, dr0_off, FN)
|
||||||
|
r7, e7 = pt(PTRACE_POKEUSER, tid, dr0_off + 7 * 8, 0x1)
|
||||||
|
v, _ = pt(PTRACE_PEEKUSER, tid, dr0_off + 7 * 8)
|
||||||
|
print(' arm tid=%d poke_dr0=%s(e%s) poke_dr7=%s(e%s) dr7_read=%s'
|
||||||
|
% (tid, 'ok' if r0 is not None else 'FAIL', e0,
|
||||||
|
'ok' if r7 is not None else 'FAIL', e7, hex(v or 0)), flush=True)
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
return v == 1
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', '/home/m/soothe-bt/dual_b1q_0.5.rpp'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.002)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host %d at %.3fs' % (host, time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
seized = []
|
||||||
|
for tid in [int(os.path.basename(p)) for p in glob.glob(f'/proc/{host}/task/*')] or [host]:
|
||||||
|
if tid not in seized:
|
||||||
|
r, e = pt(PTRACE_SEIZE, tid, 0, 0)
|
||||||
|
if r is not None or e != 3:
|
||||||
|
seized.append(tid)
|
||||||
|
|
||||||
|
# stop one thread briefly to locate DR0 offset
|
||||||
|
tgt = seized[0]
|
||||||
|
pt(PTRACE_INTERRUPT, tgt)
|
||||||
|
os.waitpid(tgt, os.WUNTRACED)
|
||||||
|
dr0_off = find_dr0_off(tgt)
|
||||||
|
print('DR0 user-offset:', hex(dr0_off) if dr0_off else 'NOT FOUND', flush=True)
|
||||||
|
if not dr0_off:
|
||||||
|
return 1
|
||||||
|
pt(PTRACE_CONT, tgt, 0, 0)
|
||||||
|
n_ok = 0
|
||||||
|
for tid in seized:
|
||||||
|
if arm_stopped(tid, dr0_off):
|
||||||
|
n_ok += 1
|
||||||
|
print('armed %#x on %d/%d tids (DR7 verified)' % (FN, n_ok, len(seized)), flush=True)
|
||||||
|
|
||||||
|
log = []
|
||||||
|
hits = 0
|
||||||
|
skip = int(os.environ.get('FN_SKIP', '0'))
|
||||||
|
t_end = time.time() + DUR
|
||||||
|
last_resweep = 0.0
|
||||||
|
while time.time() < t_end:
|
||||||
|
now = time.time()
|
||||||
|
if now - last_resweep > 0.03:
|
||||||
|
last_resweep = now
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
r, e = pt(PTRACE_SEIZE, tid, 0, 0)
|
||||||
|
if r is not None or e != 3:
|
||||||
|
seized.append(tid)
|
||||||
|
if arm_stopped(tid, dr0_off):
|
||||||
|
print('+tid', tid, flush=True)
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == 0:
|
||||||
|
time.sleep(0.0005)
|
||||||
|
continue
|
||||||
|
sig = status >> 8
|
||||||
|
ev = status >> 16
|
||||||
|
if os.WIFEXITED(status) or os.WIFSIGNALED(status):
|
||||||
|
continue
|
||||||
|
if ev == 3 or ev == 1:
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if os.WIFSTOPPED(pid) and sig == signal.SIGTRAP:
|
||||||
|
regs = UserRegs()
|
||||||
|
r, _ = pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
if r is None:
|
||||||
|
continue
|
||||||
|
if regs.rip == FN or regs.rip == FN + 1:
|
||||||
|
rip = FN
|
||||||
|
ret, _ = pt(PTRACE_PEEKDATA, pid, regs.rsp)
|
||||||
|
rec = dict(ctx=regs.rcx, a2=regs.rdx, cnt=regs.r8 & 0xffffffff,
|
||||||
|
r9=regs.r9 & 0xffffffff, ret=ret,
|
||||||
|
rbx=regs.rbx, r12=regs.r12, r13=regs.r13,
|
||||||
|
r14=regs.r14, r15=regs.r15, rsp=regs.rsp, tid=pid)
|
||||||
|
if hits >= skip:
|
||||||
|
log.append(rec)
|
||||||
|
print('HIT ctx=%#x a2=%#x cnt=%#x r9d=%#x ret=%#x'
|
||||||
|
% (rec['ctx'], rec['a2'], rec['cnt'], rec['r9'], rec['ret']),
|
||||||
|
flush=True)
|
||||||
|
hits += 1
|
||||||
|
# pass bp: clear DR0 temporarily, single-step, re-arm
|
||||||
|
pt(PTRACE_POKEUSER, pid, dr0_off + 7 * 8, 0)
|
||||||
|
regs.eflags |= 0x100 # TF
|
||||||
|
pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, os.WUNTRACED)
|
||||||
|
regs2 = UserRegs()
|
||||||
|
pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs2))
|
||||||
|
regs2.eflags &= ~0x100
|
||||||
|
pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs2))
|
||||||
|
arm(pid, dr0_off)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
else:
|
||||||
|
# stray SIGTRAP (wine internal): forward
|
||||||
|
pt(PTRACE_CONT, pid, 0, signal.SIGTRAP)
|
||||||
|
elif os.WIFSTOPPED(pid):
|
||||||
|
pt(PTRACE_CONT, pid, 0, sig if 0 < sig < 32 else 0)
|
||||||
|
|
||||||
|
print('total hits:', hits, 'logged:', len(log))
|
||||||
|
json.dump(log, open('/tmp/opencode/fntrace/hwhits.json', 'w'), indent=1)
|
||||||
|
for tid in seized:
|
||||||
|
disarm(tid, dr0_off)
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fnsample.py — active RIP sampling of all threads via PTRACE_INTERRUPT
|
||||||
|
during the offline-render burst. Finds which code ACTUALLY executes."""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
MOD_LO, MOD_HI = 0x180001000, 0x181baa000
|
||||||
|
DUR = float(os.environ.get('SAMP_DUR', '2.5'))
|
||||||
|
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_O_TRACECLONE = 2
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
def pt(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
return None if r == -1 else r
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', '/home/m/soothe-bt/dual_b1q_0.5.rpp'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.002)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host %d at %.3fs' % (host, time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
seized = {host}
|
||||||
|
pt(PTRACE_SEIZE, host, 0, PTRACE_O_TRACECLONE)
|
||||||
|
pt(PTRACE_CONT, host, 0, 0)
|
||||||
|
|
||||||
|
ips = {}
|
||||||
|
t_end = time.time() + DUR
|
||||||
|
t_start = None
|
||||||
|
nsamp = 0
|
||||||
|
while time.time() < t_end:
|
||||||
|
# resweep tids occasionally
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
if pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE) is not None:
|
||||||
|
seized.add(tid)
|
||||||
|
for tid in list(seized):
|
||||||
|
if pt(PTRACE_INTERRUPT, tid) is None:
|
||||||
|
continue
|
||||||
|
got = False
|
||||||
|
for _ in range(20):
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(tid, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == tid:
|
||||||
|
got = True
|
||||||
|
break
|
||||||
|
time.sleep(0.0002)
|
||||||
|
if got:
|
||||||
|
regs = UserRegs()
|
||||||
|
if pt(PTRACE_GETREGS, tid, 0, ctypes.addressof(regs)) is not None:
|
||||||
|
nsamp += 1
|
||||||
|
if t_start is None:
|
||||||
|
t_start = time.time()
|
||||||
|
if MOD_LO <= regs.rip < MOD_HI:
|
||||||
|
b = regs.rip >> 4 << 4
|
||||||
|
ips[b] = ips.get(b, 0) + 1
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
print('samples=%d in-module=%d unique64=%d window=%.2fs..%.2fs'
|
||||||
|
% (nsamp, sum(ips.values()), len(ips),
|
||||||
|
(t_start or t0) - t0, time.time() - t0))
|
||||||
|
core = {a: c for a, c in ips.items() if 0x180520000 <= a < 0x180560000}
|
||||||
|
for a, c in sorted(core.items(), key=lambda x: -x[1])[:18]:
|
||||||
|
print('CORE %#x %d' % (a, c))
|
||||||
|
for b, c in sorted(ips.items(), key=lambda x: -x[1])[:6]:
|
||||||
|
print('%#x %d' % (b, c))
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fntrace.py — ptrace INT3 tracer for FUN_180529fe0 (mask chain vtbl slot 6)
|
||||||
|
in the live yabridge host. The wine module is mapped at its preferred base,
|
||||||
|
so dump VAs == runtime addresses (verified: exec map 0x180001000-0x181baa000).
|
||||||
|
|
||||||
|
On each hit logs: RIP, RCX (ctx), RDX, R8D (count), R9D, [RSP] (return addr),
|
||||||
|
plus xmm0/xmm1 low scalars if available via GETFPREGS (skipped: not portable).
|
||||||
|
|
||||||
|
Usage: python3 scripts/fntrace.py [rpp] [nhits]
|
||||||
|
"""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
FN = int(os.environ.get('FN_ADDR', '0x180529FE0'), 16)
|
||||||
|
SNAPDIR = '/tmp/opencode/fntrace'
|
||||||
|
|
||||||
|
PTRACE_TRACEME = 0
|
||||||
|
PTRACE_PEEKTEXT = 1
|
||||||
|
PTRACE_PEEKDATA = 2
|
||||||
|
PTRACE_POKETEXT = 4
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_SINGLESTEP = 9
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_ATTACH = 16
|
||||||
|
PTRACE_DETACH = 17
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_O_TRACECLONE = 0x00000002
|
||||||
|
PTRACE_EVENT_CLONE = 3 # status >> 16 == 4 (event+1)? actually event = status>>16, CLONE==3 -> 4? use raw compare below
|
||||||
|
PTRACE_EVENT_FORK = 1
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
def ptrace(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
if r == -1:
|
||||||
|
e = ctypes.get_errno()
|
||||||
|
if req not in (PTRACE_PEEKTEXT, PTRACE_PEEKDATA):
|
||||||
|
raise OSError(e, f'ptrace({req:#x},{pid}) failed')
|
||||||
|
return None
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/dual_b1q_0.5.rpp'
|
||||||
|
nhits = int(sys.argv[2]) if len(sys.argv) > 2 else 24
|
||||||
|
render = '--render' in sys.argv
|
||||||
|
os.makedirs(SNAPDIR, exist_ok=True)
|
||||||
|
subprocess.run('pkill -9 -x reaser 2>/dev/null; pkill -9 -x reaper 2>/dev/null; '
|
||||||
|
"pkill -9 -f '[y]abridge' 2>/dev/null; sleep 1", shell=True)
|
||||||
|
if render:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', rpp],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
else:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['/usr/bin/reaper', '-nosplash', '-ignoreerrors', rpp,
|
||||||
|
'/home/m/re-tools/play_loop.lua'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
t0 = time.time()
|
||||||
|
host = None
|
||||||
|
while time.time() - t0 < 60 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.05)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host', host, 'at %.1fs' % (time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
tids = [int(os.path.basename(p)) for p in glob.glob(f'/proc/{host}/task/*')]
|
||||||
|
print('tids:', tids, flush=True)
|
||||||
|
|
||||||
|
seized = []
|
||||||
|
for tid in tids:
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
seized.append(tid)
|
||||||
|
except OSError as e:
|
||||||
|
print('seize fail', tid, e)
|
||||||
|
if not seized:
|
||||||
|
return 1
|
||||||
|
# stop everyone
|
||||||
|
stopped = []
|
||||||
|
for tid in seized:
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_INTERRUPT, tid)
|
||||||
|
os.waitpid(tid, os.WUNTRACED)
|
||||||
|
stopped.append(tid)
|
||||||
|
except (OSError, ChildProcessError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
orig = ptrace(PTRACE_PEEKTEXT, stopped[0], FN)
|
||||||
|
cc = (orig & ~0xFF) | 0xCC
|
||||||
|
ptrace(PTRACE_POKETEXT, stopped[0], FN, cc)
|
||||||
|
print('breakpoint armed at %#x (orig=%#x)' % (FN, orig), flush=True)
|
||||||
|
|
||||||
|
for tid in stopped:
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_CONT, tid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
hits = 0
|
||||||
|
log = []
|
||||||
|
import select
|
||||||
|
import time as _t
|
||||||
|
t_last = _t.time()
|
||||||
|
idle_deadline = float(os.environ.get('FNTRACE_IDLE', '20'))
|
||||||
|
while hits < nhits and _t.time() - t_last < idle_deadline:
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WUNTRACED | os.WSTOPPED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == 0:
|
||||||
|
_t.sleep(0.005)
|
||||||
|
continue
|
||||||
|
sig = status >> 8
|
||||||
|
if os.WIFEXITED(pid and status or status) or os.WIFSIGNALED(status):
|
||||||
|
# thread/process exited (normal in wine): forget it
|
||||||
|
if pid in seized:
|
||||||
|
seized.remove(pid)
|
||||||
|
if pid in stopped:
|
||||||
|
stopped.remove(pid)
|
||||||
|
continue
|
||||||
|
if not os.WIFSTOPPED(pid):
|
||||||
|
continue
|
||||||
|
t_last = _t.time()
|
||||||
|
ev = status >> 16
|
||||||
|
if ev == PTRACE_EVENT_CLONE or ev == PTRACE_EVENT_FORK:
|
||||||
|
if pid not in seized:
|
||||||
|
seized.append(pid)
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if sig == signal.SIGTRAP:
|
||||||
|
regs = UserRegs()
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
except OSError:
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if regs.rip - 1 == FN:
|
||||||
|
ret = ptrace(PTRACE_PEEKDATA, pid, regs.rsp)
|
||||||
|
rec = dict(rip=regs.rip - 1, ctx=regs.rcx, a2=regs.rdx,
|
||||||
|
cnt=regs.r8 & 0xffffffff, r9=regs.r9 & 0xffffffff,
|
||||||
|
ret=ret, tid=pid,
|
||||||
|
rbx=regs.rbx, rbp_=regs.rbp, rsi=regs.rsi, rdi=regs.rdi)
|
||||||
|
log.append(rec)
|
||||||
|
hits += 1
|
||||||
|
print(f'hit {hits}: tid={pid} ctx={regs.rcx:#x} '
|
||||||
|
f'a2={regs.rdx:#x} cnt={regs.r8:#x} r9d={regs.r9:#x} '
|
||||||
|
f'ret={ret:#x}', flush=True)
|
||||||
|
# step over int3
|
||||||
|
lo = struct.unpack('<Q', struct.pack('<Q', orig ^ ((orig ^ cc) & 0xFF)))[0]
|
||||||
|
ptrace(PTRACE_POKETEXT, pid, FN, orig)
|
||||||
|
regs.rip = FN
|
||||||
|
ptrace(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
ptrace(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, os.WUNTRACED)
|
||||||
|
ptrace(PTRACE_POKETEXT, pid, FN, cc)
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, 0)
|
||||||
|
else:
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, 0)
|
||||||
|
else:
|
||||||
|
# other stop signals: deliver and continue
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, sig if 0 < sig < 0x20 else 0)
|
||||||
|
|
||||||
|
# cleanup: remove breakpoint, detach
|
||||||
|
print('done hits=', hits, 'cleaning up...', flush=True)
|
||||||
|
for tid in seized:
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_INTERRUPT, tid)
|
||||||
|
os.waitpid(tid, os.WUNTRACED)
|
||||||
|
except (OSError, ChildProcessError):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_POKETEXT, stopped[0], FN, orig)
|
||||||
|
except Exception as e:
|
||||||
|
print('restore fail', e)
|
||||||
|
for tid in seized:
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_DETACH, tid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
import json
|
||||||
|
json.dump(log, open(f'{SNAPDIR}/hits.json', 'w'), indent=1)
|
||||||
|
print('saved', f'{SNAPDIR}/hits.json')
|
||||||
|
if proc.poll() is None:
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fntrace2.py — fast single-point INT3 tracer for the live yabridge host.
|
||||||
|
|
||||||
|
Flow: poll for host spawn (5ms), immediately PTRACE_SEIZE the main thread,
|
||||||
|
poke INT3 at FN (no stop required - word write is atomic), then serve
|
||||||
|
waitpid events (clone children are auto-traced and continued). Logs args at
|
||||||
|
each hit. Works best against `reaper -renderproject` where all DSP work
|
||||||
|
happens in a burst right after host spawn.
|
||||||
|
|
||||||
|
Usage: python3 scripts/fntrace2.py [rpp] [nhits] [--render]
|
||||||
|
Env: FN_ADDR (default 0x180529fe0)
|
||||||
|
"""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
FN = int(os.environ.get('FN_ADDR', '0x180529FE0'), 16)
|
||||||
|
SNAPDIR = '/tmp/opencode/fntrace'
|
||||||
|
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_PEEKDATA = 2
|
||||||
|
PTRACE_POKETEXT = 4
|
||||||
|
PTRACE_SINGLESTEP = 9
|
||||||
|
PTRACE_DETACH = 17
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_O_TRACECLONE = 0x00000002
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
def ptrace(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
if r == -1:
|
||||||
|
e = ctypes.get_errno()
|
||||||
|
if req not in (PTRACE_PEEKDATA,):
|
||||||
|
raise OSError(e, f'ptrace({req:#x},{pid}) failed')
|
||||||
|
return None
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/dual_b1q_0.5.rpp'
|
||||||
|
nhits = int(sys.argv[2]) if len(sys.argv) > 2 else 12
|
||||||
|
render = '--render' in sys.argv
|
||||||
|
os.makedirs(SNAPDIR, exist_ok=True)
|
||||||
|
subprocess.run('pkill -9 -x reaser 2>/dev/null; pkill -9 -x reaper 2>/dev/null; '
|
||||||
|
"pkill -9 -f '[y]abridge' 2>/dev/null; sleep 1", shell=True)
|
||||||
|
if render:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', rpp],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
else:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['/usr/bin/reaper', '-nosplash', '-ignoreerrors', rpp,
|
||||||
|
'/home/m/re-tools/play_loop.lua'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 60 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.005)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host %d at %.2fs' % (host, time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
ptrace(PTRACE_SEIZE, host, 0, PTRACE_O_TRACECLONE)
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_INTERRUPT, host)
|
||||||
|
os.waitpid(host, os.WUNTRACED)
|
||||||
|
except (OSError, ChildProcessError) as e:
|
||||||
|
print('interrupt fail', e)
|
||||||
|
orig = ptrace(PTRACE_PEEKDATA, host, FN)
|
||||||
|
cc = (orig & ~0xFF) | 0xCC
|
||||||
|
ptrace(PTRACE_POKETEXT, host, FN, cc)
|
||||||
|
print('armed %#x orig=%#x' % (FN, orig), flush=True)
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_CONT, host, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
known = {host}
|
||||||
|
log = []
|
||||||
|
hits = 0
|
||||||
|
t_last = time.time()
|
||||||
|
deadline_idle = float(os.environ.get('FNTRACE_IDLE', '25'))
|
||||||
|
while hits < nhits and time.time() - t_last < deadline_idle:
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WUNTRACED)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
sig = status >> 8
|
||||||
|
ev = status >> 16
|
||||||
|
known.add(pid)
|
||||||
|
t_last = time.time()
|
||||||
|
if ev == 3 or ev == 1: # CLONE/FORK event stop
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
continue
|
||||||
|
if not os.WIFSTOPPED(pid):
|
||||||
|
continue
|
||||||
|
if sig == signal.SIGTRAP:
|
||||||
|
regs = UserRegs()
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
if regs.rip - 1 == FN:
|
||||||
|
ret = ptrace(PTRACE_PEEKDATA, pid, regs.rsp)
|
||||||
|
rec = dict(ctx=regs.rcx, a2=regs.rdx, cnt=regs.r8 & 0xffffffff,
|
||||||
|
r9=regs.r9 & 0xffffffff, ret=ret, rsp=regs.rsp,
|
||||||
|
rbx=regs.rbx, r12=regs.r12, r13=regs.r13,
|
||||||
|
r14=regs.r14, r15=regs.r15, rsi=regs.rsi, rdi=regs.rdi,
|
||||||
|
rip=regs.rip - 1, tid=pid)
|
||||||
|
log.append(rec)
|
||||||
|
hits += 1
|
||||||
|
print('hit %d tid=%d ctx=%#x a2=%#x cnt=%#x r9d=%#x ret=%#x'
|
||||||
|
% (hits, pid, regs.rcx, regs.rdx,
|
||||||
|
regs.r8 & 0xffffffff, regs.r9 & 0xffffffff, ret),
|
||||||
|
flush=True)
|
||||||
|
# step over
|
||||||
|
ptrace(PTRACE_POKETEXT, pid, FN, orig)
|
||||||
|
regs.rip = FN
|
||||||
|
ptrace(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
ptrace(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
try:
|
||||||
|
os.waitpid(pid, os.WUNTRACED)
|
||||||
|
except ChildProcessError:
|
||||||
|
pass
|
||||||
|
ptrace(PTRACE_POKETEXT, pid, FN, cc)
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ptrace(PTRACE_CONT, pid, 0, sig if 0 < sig < 0x20 else 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print('hits:', hits, flush=True)
|
||||||
|
json.dump(log, open(f'{SNAPDIR}/hits.json', 'w'), indent=1)
|
||||||
|
for pid in list(known):
|
||||||
|
try:
|
||||||
|
os.kill(pid, signal.SIGKILL)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
proc.kill()
|
||||||
|
print('saved', f'{SNAPDIR}/hits.json')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fntrace3.py — decisive INT3 experiment: seize ALL tids within milliseconds
|
||||||
|
of host spawn, arm breakpoint, log EVERY waitpid event for N seconds."""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
FN = int(os.environ.get('FN_ADDR', '0x180529FE0'), 16)
|
||||||
|
DUR = float(os.environ.get('FN_DUR', '25'))
|
||||||
|
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_PEEKDATA = 2
|
||||||
|
PTRACE_POKETEXT = 4
|
||||||
|
PTRACE_SINGLESTEP = 9
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_LISTEN = 0x4208
|
||||||
|
PTRACE_O_TRACECLONE = 2
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
def pt(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
if r == -1:
|
||||||
|
return None, ctypes.get_errno()
|
||||||
|
return r, 0
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = '/home/m/soothe-bt/dual_b1q_0.5.rpp'
|
||||||
|
subprocess.run('pkill -9 -x reaper; pkill -9 -f \'[y]abridge\'; sleep 1', shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', rpp],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.002)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
print('host %d at %.3fs' % (host, time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
seized = []
|
||||||
|
for tid in [host] + [int(os.path.basename(p)) for p in glob.glob(f'/proc/{host}/task/*')]:
|
||||||
|
if tid in seized:
|
||||||
|
continue
|
||||||
|
r, e = pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
if r is None and e == 3: # ESRCH - gone
|
||||||
|
continue
|
||||||
|
seized.append(tid)
|
||||||
|
# interrupt to allow poke below (poke needs SOME stopped thread)
|
||||||
|
print('seized:', seized, flush=True)
|
||||||
|
# stop one thread to enable POKETEXT
|
||||||
|
tgt = seized[0]
|
||||||
|
pt(PTRACE_INTERRUPT, tgt)
|
||||||
|
os.waitpid(tgt, os.WUNTRACED)
|
||||||
|
orig, _ = pt(PTRACE_PEEKDATA, tgt, FN)
|
||||||
|
cc = (orig & ~0xFF) | 0xCC
|
||||||
|
pt(PTRACE_POKETEXT, tgt, FN, cc)
|
||||||
|
print('armed orig=%#x' % orig, flush=True)
|
||||||
|
# seize any tids spawned meanwhile
|
||||||
|
for tid in [int(os.path.basename(p)) for p in glob.glob(f'/proc/{host}/task/*')]:
|
||||||
|
if tid not in seized:
|
||||||
|
r, e = pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
if r is not None or e != 3:
|
||||||
|
seized.append(tid)
|
||||||
|
for tid in seized:
|
||||||
|
try:
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
log = []
|
||||||
|
events = []
|
||||||
|
hits = 0
|
||||||
|
t_end = time.time() + DUR
|
||||||
|
last_resweep = 0.0
|
||||||
|
while time.time() < t_end:
|
||||||
|
now = time.time()
|
||||||
|
if now - last_resweep > 0.05:
|
||||||
|
last_resweep = now
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
r, e = pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
if r is not None or e != 3:
|
||||||
|
seized.append(tid)
|
||||||
|
print('+tid', tid, flush=True)
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WUNTRACED | os.WNOHANG)
|
||||||
|
if pid == 0:
|
||||||
|
time.sleep(0.001)
|
||||||
|
continue
|
||||||
|
sig = status >> 8
|
||||||
|
ev = status >> 16
|
||||||
|
events.append((time.time() - t0, pid, hex(status), ev))
|
||||||
|
if pid not in seized:
|
||||||
|
r, e = pt(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACECLONE)
|
||||||
|
if r is not None or e != 3:
|
||||||
|
seized.append(pid)
|
||||||
|
if ev == 3 or ev == 1:
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
continue
|
||||||
|
if os.WIFSTOPPED(pid) and sig == signal.SIGTRAP:
|
||||||
|
regs = UserRegs()
|
||||||
|
r, _ = pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
if r is None:
|
||||||
|
continue
|
||||||
|
if regs.rip - 1 == FN:
|
||||||
|
ret, _ = pt(PTRACE_PEEKDATA, pid, regs.rsp)
|
||||||
|
rec = dict(ctx=regs.rcx, a2=regs.rdx, cnt=regs.r8 & 0xffffffff,
|
||||||
|
r9=regs.r9 & 0xffffffff, ret=ret, rbx=regs.rbx,
|
||||||
|
r12=regs.r12, r13=regs.r13, r14=regs.r14,
|
||||||
|
r15=regs.r15, rsp=regs.rsp, tid=pid)
|
||||||
|
log.append(rec)
|
||||||
|
hits += 1
|
||||||
|
print('HIT ctx=%#x a2=%#x cnt=%#x ret=%#x'
|
||||||
|
% (regs.rcx, regs.rdx, regs.r8 & 0xffffffff, ret), flush=True)
|
||||||
|
pt(PTRACE_POKETEXT, pid, FN, orig)
|
||||||
|
regs.rip = FN
|
||||||
|
pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
os.waitpid(pid, os.WUNTRACED)
|
||||||
|
pt(PTRACE_POKETEXT, pid, FN, cc)
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
else:
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
elif os.WIFEXITED(status) or os.WIFSIGNALED(status):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
pt(PTRACE_CONT, pid, 0, sig if 0 < sig < 32 else 0)
|
||||||
|
|
||||||
|
print('total hits:', hits, 'events:', len(events))
|
||||||
|
json.dump(dict(log=log, events=events[:400]),
|
||||||
|
open('/tmp/opencode/fntrace/hits3.json', 'w'), indent=1)
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fnwatch.py — hardware data-watchpoint (DR0 RW) on the live FIR kernel word.
|
||||||
|
Catches whoever READS the built kernel during offline render -> consumer RIP."""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
DUR = float(os.environ.get('WATCH_DUR', '20'))
|
||||||
|
BIN = '/home/m/re-tools/soothe_mem.bin'
|
||||||
|
BASE = 0x180000000
|
||||||
|
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_PEEKUSER = 3
|
||||||
|
PTRACE_POKEUSER = 6
|
||||||
|
PTRACE_SINGLESTEP = 9
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_O_TRACECLONE = 2
|
||||||
|
|
||||||
|
DR0_OFF = 0x350 # empirically verified debugreg base
|
||||||
|
DR7_OFF = DR0_OFF + 56 # 0x388
|
||||||
|
DR7_VAL = 0x000F0001 # L0=1, R/W0=11 (rd/wr), LEN0=11 (4 bytes)
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
_mod = open(BIN, 'rb').read()
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
def pt(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
return None if r == -1 else r
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', '/home/m/soothe-bt/dual_b1q_0.5.rpp'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.002)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
|
||||||
|
def rd(a, n):
|
||||||
|
try:
|
||||||
|
return os.pread(fd, n, a)
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# find ctx & FIR ptr while running (poll till instance exists)
|
||||||
|
ctx = firptr = None
|
||||||
|
vt = struct.pack('<Q', 0x1824AC210)
|
||||||
|
while time.time() - t0 < 25 and firptr is None:
|
||||||
|
for line in open(f'/proc/{host}/maps'):
|
||||||
|
parts = line.split()
|
||||||
|
if 'rw' not in parts[1]:
|
||||||
|
continue
|
||||||
|
lo, hi = (int(x, 16) for x in parts[0].split('-'))
|
||||||
|
CH = 16 * 1024 * 1024
|
||||||
|
a = lo
|
||||||
|
while a < hi and firptr is None:
|
||||||
|
d = rd(a, min(CH + 4096, hi - a))
|
||||||
|
if not d:
|
||||||
|
break
|
||||||
|
j = d.find(vt)
|
||||||
|
while j >= 0:
|
||||||
|
cand = a + j
|
||||||
|
sb = rd(cand + 0x540870, 4)
|
||||||
|
if sb and struct.unpack('<f', sb)[0] > 100:
|
||||||
|
ctx = cand
|
||||||
|
qb = rd(cand + 0x540668, 8)
|
||||||
|
firptr = struct.unpack('<Q', qb)[0]
|
||||||
|
break
|
||||||
|
j = d.find(vt, j + 1)
|
||||||
|
a += CH
|
||||||
|
if firptr is None:
|
||||||
|
print('NO CTX/FIR')
|
||||||
|
return 1
|
||||||
|
print('host %d ctx %#x fir %#x at %.2fs' % (host, ctx, firptr, time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
seized = {host}
|
||||||
|
armed = set()
|
||||||
|
pt(PTRACE_SEIZE, host, 0, PTRACE_O_TRACECLONE)
|
||||||
|
pt(PTRACE_INTERRUPT, host)
|
||||||
|
for _ in range(60):
|
||||||
|
try:
|
||||||
|
pid, st = os.waitpid(host, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == host:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
pt(PTRACE_POKEUSER, host, DR0_OFF, 0x18052b8bb)
|
||||||
|
pt(PTRACE_POKEUSER, host, DR7_OFF, DR7_VAL)
|
||||||
|
armed.add(host)
|
||||||
|
print('placeholder armed', flush=True)
|
||||||
|
|
||||||
|
def arm_dbg(tid):
|
||||||
|
pass_off=(0x350,)
|
||||||
|
for off in pass_off:
|
||||||
|
r = pt(PTRACE_POKEUSER, tid, off, 0xdeadbeef if off == DR0_OFF else DR7_VAL)
|
||||||
|
v = pt(PTRACE_PEEKUSER, tid, off)
|
||||||
|
print(' dbg poke %#x -> r=%s read=%#x' % (off, 'ok' if r is not None else 'EIO', v or 0), flush=True)
|
||||||
|
|
||||||
|
def arm(tid):
|
||||||
|
pt(PTRACE_POKEUSER, tid, DR0_OFF, firptr + 43 * 8) # bin43 re (hot word)
|
||||||
|
pt(PTRACE_POKEUSER, tid, DR7_OFF, DR7_VAL)
|
||||||
|
v = pt(PTRACE_PEEKUSER, tid, DR7_OFF)
|
||||||
|
# bit10 of DR7 reads as always-1 (RA1)
|
||||||
|
return v is not None and (v & ~0x400) == (DR7_VAL & ~0x400)
|
||||||
|
|
||||||
|
# stop host briefly to verify arming works at all
|
||||||
|
pt(PTRACE_INTERRUPT, host)
|
||||||
|
for _ in range(50):
|
||||||
|
try:
|
||||||
|
pid, st = os.waitpid(host, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == host:
|
||||||
|
break
|
||||||
|
time.sleep(0.002)
|
||||||
|
arm_dbg(host)
|
||||||
|
ok = arm(host)
|
||||||
|
print('arm check (stopped):', ok, flush=True)
|
||||||
|
pt(PTRACE_CONT, host, 0, 0)
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
armed.add(host)
|
||||||
|
|
||||||
|
hits = []
|
||||||
|
t_end = time.time() + DUR
|
||||||
|
last_sweep = 0.0
|
||||||
|
while time.time() < t_end:
|
||||||
|
now = time.time()
|
||||||
|
if now - last_sweep > 0.03:
|
||||||
|
last_sweep = now
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
if pt(PTRACE_SEIZE, tid, 0, 0) is not None:
|
||||||
|
seized.add(tid)
|
||||||
|
if tid not in armed:
|
||||||
|
pt(PTRACE_INTERRUPT, tid)
|
||||||
|
for _ in range(40):
|
||||||
|
try:
|
||||||
|
pid, st = os.waitpid(tid, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == tid:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
if arm(tid):
|
||||||
|
armed.add(tid)
|
||||||
|
try:
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == 0:
|
||||||
|
time.sleep(0.0005)
|
||||||
|
continue
|
||||||
|
sig = status >> 8
|
||||||
|
if os.WIFEXITED(status) or os.WIFSIGNALED(status):
|
||||||
|
continue
|
||||||
|
if os.WIFSTOPPED(pid) and sig == signal.SIGTRAP:
|
||||||
|
regs = UserRegs()
|
||||||
|
if pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs)) is None:
|
||||||
|
continue
|
||||||
|
rec = dict(rip=regs.rip, rsp=regs.rsp, tid=pid,
|
||||||
|
ret=struct.unpack('<Q', rd(regs.rsp, 8) or b'\0' * 8)[0])
|
||||||
|
hits.append(rec)
|
||||||
|
if len(hits) <= 15:
|
||||||
|
print('WATCH HIT rip=%#x ret=%#x' % (rec['rip'], rec['ret']), flush=True)
|
||||||
|
# pass trap: clear DR6 (poke 0xffff0ff0), single-step, continue
|
||||||
|
pt(PTRACE_POKEUSER, pid, DR0_OFF + 48, 0xFFFF0FF0) # reset DR6
|
||||||
|
regs.eflags |= 0x100
|
||||||
|
pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
try:
|
||||||
|
os.waitpid(pid, os.WUNTRACED)
|
||||||
|
except ChildProcessError:
|
||||||
|
pass
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
elif os.WIFSTOPPED(pid):
|
||||||
|
pt(PTRACE_CONT, pid, 0, sig if 0 < sig < 32 else 0)
|
||||||
|
|
||||||
|
print('total watch hits:', len(hits))
|
||||||
|
import json
|
||||||
|
json.dump(hits, open('/tmp/opencode/fnwatch/hits.json', 'w'), indent=1, default=str)
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
os.makedirs('/tmp/opencode/fnwatch', exist_ok=True)
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""fnwatch4.py — 4 hardware watchpoints simultaneously:
|
||||||
|
DR0/DR1: dwords of the FIR pointer slot ctx+0x540668 (catches loader of ptr)
|
||||||
|
DR2 : kernel word bin43.re
|
||||||
|
DR3 : scratch word bin43
|
||||||
|
RW=read/write (traps readers AND writers), LEN=4B."""
|
||||||
|
import ctypes
|
||||||
|
import glob
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
DUR = float(os.environ.get('WATCH_DUR', '20'))
|
||||||
|
PTRACE_CONT = 7
|
||||||
|
PTRACE_GETREGS = 12
|
||||||
|
PTRACE_SETREGS = 13
|
||||||
|
PTRACE_PEEKUSER = 3
|
||||||
|
PTRACE_POKEUSER = 6
|
||||||
|
PTRACE_SEIZE = 0x4206
|
||||||
|
PTRACE_INTERRUPT = 0x4207
|
||||||
|
PTRACE_O_TRACECLONE = 2
|
||||||
|
|
||||||
|
DR_BASE = 0x350 # u_debugreg[0]
|
||||||
|
DR6_OFF = DR_BASE + 48 # 0x388 - wait: idx6=+48 -> that IS DR6 slot? no:
|
||||||
|
# u_debugreg[0..7]: DR0@+0, DR1@+8, DR2@+16, DR3@+24, DR4@+32, DR5@+40,
|
||||||
|
# DR6@+48, DR7@+56
|
||||||
|
DR7_OFF = DR_BASE + 56
|
||||||
|
DR7_VAL = 0xFFFF000F # L0-3 enabled, all RW=11, all LEN=11 (4B)
|
||||||
|
|
||||||
|
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserRegs(ctypes.Structure):
|
||||||
|
_fields_ = [(n, ctypes.c_ulonglong) for n in (
|
||||||
|
'r15', 'r14', 'r13', 'r12', 'rbp', 'rbx', 'r11', 'r10',
|
||||||
|
'r9', 'r8', 'rax', 'rcx', 'rdx', 'rsi', 'rdi', 'orig_rax',
|
||||||
|
'rip', 'cs', 'eflags', 'rsp', 'ss', 'fs_base', 'gs_base',
|
||||||
|
'ds', 'es', 'fs', 'gs')]
|
||||||
|
|
||||||
|
|
||||||
|
def pt(req, pid, addr=0, data=0):
|
||||||
|
libc.ptrace.restype = ctypes.c_long
|
||||||
|
r = libc.ptrace(req, pid, ctypes.c_void_p(addr), ctypes.c_void_p(data))
|
||||||
|
return None if r == -1 else r
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True)
|
||||||
|
rpp = '/home/m/soothe-bt/dual_b1q_0.5.rpp'
|
||||||
|
wav = rpp.replace('.rpp', '.wav')
|
||||||
|
wt0 = os.path.getmtime(wav) if os.path.exists(wav) else 0
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', rpp],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.002)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
|
||||||
|
def rd(a, n):
|
||||||
|
try:
|
||||||
|
return os.pread(fd, n, a)
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
ctx = firptr = scrptr = None
|
||||||
|
vt = struct.pack('<Q', 0x1824AC210)
|
||||||
|
while time.time() - t0 < 25 and firptr is None:
|
||||||
|
for line in open(f'/proc/{host}/maps'):
|
||||||
|
parts = line.split()
|
||||||
|
if 'rw' not in parts[1]:
|
||||||
|
continue
|
||||||
|
lo, hi = (int(x, 16) for x in parts[0].split('-'))
|
||||||
|
CH = 16 * 1024 * 1024
|
||||||
|
a = lo
|
||||||
|
while a < hi and firptr is None:
|
||||||
|
d = rd(a, min(CH + 4096, hi - a))
|
||||||
|
if not d:
|
||||||
|
break
|
||||||
|
j = d.find(vt)
|
||||||
|
while j >= 0:
|
||||||
|
cand = a + j
|
||||||
|
sb = rd(cand + 0x540870, 4)
|
||||||
|
if sb and struct.unpack('<f', sb)[0] > 100:
|
||||||
|
ctx = cand
|
||||||
|
firptr = struct.unpack('<Q', rd(cand + 0x540668, 8))[0]
|
||||||
|
scrptr = struct.unpack('<Q', rd(cand + 0x540628, 8))[0] or firptr
|
||||||
|
break
|
||||||
|
j = d.find(vt, j + 1)
|
||||||
|
a += CH
|
||||||
|
if firptr is None:
|
||||||
|
print('NO CTX')
|
||||||
|
return 1
|
||||||
|
print('host %d ctx %#x fir %#x scr %#x at %.2fs' %
|
||||||
|
(host, ctx, firptr, scrptr, time.time() - t0), flush=True)
|
||||||
|
|
||||||
|
seized = {host}
|
||||||
|
armed = set()
|
||||||
|
pt(PTRACE_SEIZE, host, 0, PTRACE_O_TRACECLONE)
|
||||||
|
|
||||||
|
def arm(tid):
|
||||||
|
okall = True
|
||||||
|
ovf = struct.unpack('<Q', rd(ctx + 0x5406f8, 8))[0]
|
||||||
|
trk = struct.unpack('<Q', rd(ctx + 0x540768, 8))[0]
|
||||||
|
rax = struct.unpack('<Q', rd(ctx + 0x5407f8, 8))[0]
|
||||||
|
targets = [(DR_BASE + 0, ovf + 8192),
|
||||||
|
(DR_BASE + 8, trk + 43 * 4),
|
||||||
|
(DR_BASE + 16, rax + 43 * 4),
|
||||||
|
(DR_BASE + 24, ctx + 0x2404dc)]
|
||||||
|
for off, addr in targets:
|
||||||
|
pt(PTRACE_POKEUSER, tid, off, addr)
|
||||||
|
v = pt(PTRACE_PEEKUSER, tid, off)
|
||||||
|
if v != addr:
|
||||||
|
okall = False
|
||||||
|
pt(PTRACE_POKEUSER, tid, DR7_OFF, DR7_VAL)
|
||||||
|
return okall
|
||||||
|
|
||||||
|
pt(PTRACE_INTERRUPT, host)
|
||||||
|
for _ in range(60):
|
||||||
|
try:
|
||||||
|
pid, st = os.waitpid(host, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == host:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
ok = arm(host)
|
||||||
|
print('arm(host)=%s' % ok, flush=True)
|
||||||
|
pt(PTRACE_CONT, host, 0, 0)
|
||||||
|
if ok:
|
||||||
|
armed.add(host)
|
||||||
|
|
||||||
|
hits = []
|
||||||
|
t_end = time.time() + DUR
|
||||||
|
last_sweep = 0.0
|
||||||
|
while time.time() < t_end:
|
||||||
|
now = time.time()
|
||||||
|
if now - last_sweep > 0.03:
|
||||||
|
last_sweep = now
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
tid = int(os.path.basename(tid_s))
|
||||||
|
if tid not in seized:
|
||||||
|
if pt(PTRACE_SEIZE, tid, 0, PTRACE_O_TRACECLONE) is not None:
|
||||||
|
seized.add(tid)
|
||||||
|
pt(PTRACE_INTERRUPT, tid)
|
||||||
|
for _ in range(40):
|
||||||
|
try:
|
||||||
|
wpid, _st = os.waitpid(tid, os.WUNTRACED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if wpid == tid:
|
||||||
|
break
|
||||||
|
time.sleep(0.001)
|
||||||
|
if arm(tid):
|
||||||
|
armed.add(tid)
|
||||||
|
try:
|
||||||
|
pt(PTRACE_CONT, tid, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
pid, status = os.waitpid(-1, os.WSTOPPED | os.WNOHANG)
|
||||||
|
except ChildProcessError:
|
||||||
|
break
|
||||||
|
if pid == 0:
|
||||||
|
time.sleep(0.0005)
|
||||||
|
continue
|
||||||
|
sig = status >> 8
|
||||||
|
if os.WIFEXITED(status) or os.WIFSIGNALED(status):
|
||||||
|
continue
|
||||||
|
if os.WIFSTOPPED(pid) and sig == signal.SIGTRAP:
|
||||||
|
regs = UserRegs()
|
||||||
|
if pt(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs)) is None:
|
||||||
|
continue
|
||||||
|
dr6v = pt(PTRACE_PEEKUSER, pid, DR_BASE + 48)
|
||||||
|
rec = dict(rip=regs.rip, rsp=regs.rsp, tid=pid,
|
||||||
|
dr6=dr6v,
|
||||||
|
ret=struct.unpack('<Q', rd(regs.rsp, 8) or b'\0' * 8)[0])
|
||||||
|
hits.append(rec)
|
||||||
|
if len(hits) <= 20:
|
||||||
|
print('HIT rip=%#x ret=%#x dr6=%#x'
|
||||||
|
% (rec['rip'], rec['ret'], dr6v or 0), flush=True)
|
||||||
|
# clear DR6 and single-step past
|
||||||
|
pt(PTRACE_POKEUSER, pid, DR_BASE + 48, 0xFFFF0FF0)
|
||||||
|
regs.eflags |= 0x100
|
||||||
|
pt(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
||||||
|
pt(PTRACE_SINGLESTEP, pid, 0, 0)
|
||||||
|
try:
|
||||||
|
os.waitpid(pid, os.WUNTRACED)
|
||||||
|
except ChildProcessError:
|
||||||
|
pass
|
||||||
|
pt(PTRACE_CONT, pid, 0, 0)
|
||||||
|
elif os.WIFSTOPPED(pid):
|
||||||
|
pt(PTRACE_CONT, pid, 0, sig if 0 < sig < 32 else 0)
|
||||||
|
|
||||||
|
if not hasattr(main, '_wv') and os.path.exists(wav) and os.path.getmtime(wav) > wt0:
|
||||||
|
main._wv = True
|
||||||
|
print('wav fresh at %.2fs' % (time.time() - t0), flush=True)
|
||||||
|
if not hasattr(main, '_wv'):
|
||||||
|
main._wv = False
|
||||||
|
fresh = main._wv
|
||||||
|
print('hits=%d render_fresh=%s armed=%d/%d' % (len(hits), fresh, len(armed), len(seized)))
|
||||||
|
json.dump(hits[:200], open('/tmp/opencode/fnwatch/hits4.json', 'w'),
|
||||||
|
indent=1, default=str)
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
os.makedirs('/tmp/opencode/fnwatch', exist_ok=True)
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""g_s12 pipeline: clean render ref + deepest scratch capture + render48k tract.
|
||||||
|
Then compute implied-res vs our-res for the frontend clamp analysis."""
|
||||||
|
import subprocess, os, sys, time, glob
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
import glob as g
|
||||||
|
for p in g.glob('/proc/[0-9]*'):
|
||||||
|
pid=int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd=open(f'/proc/{pid}/cmdline','rb').read().replace(b'\0',b' ').decode('utf8','replace')
|
||||||
|
maps=open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception: continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd: return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
def sh(cmd):
|
||||||
|
return subprocess.run(cmd,shell=True,capture_output=True,text=True).stdout
|
||||||
|
|
||||||
|
sh("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1")
|
||||||
|
|
||||||
|
# 1. clean ref render
|
||||||
|
wav='/tmp/opencode/g_s12_ref.wav'
|
||||||
|
if os.path.exists(wav): os.remove(wav)
|
||||||
|
pr=subprocess.Popen(['/usr/bin/reaper','-nosplash','-ignoreerrors','-renderproject','/tmp/opencode/g_s12.rpp'],
|
||||||
|
stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
|
||||||
|
t0=time.time(); host=None
|
||||||
|
while time.time()-t0<30 and not host: host=find_host(); time.sleep(0.002)
|
||||||
|
print('host',host)
|
||||||
|
for _ in range(300):
|
||||||
|
if pr.poll() is not None: break
|
||||||
|
time.sleep(0.1)
|
||||||
|
for _ in range(50):
|
||||||
|
if pr.poll() is not None: break
|
||||||
|
time.sleep(0.1)
|
||||||
|
print('ref done rc=',pr.poll())
|
||||||
|
|
||||||
|
# 2. deepest scratch capture
|
||||||
|
sh("pkill -9 -x reaper; sleep 1; rm -rf /tmp/opencode/sc_g_s12")
|
||||||
|
r=sh('cd /home/m/re-tools && timeout 60 python3 scripts/rendersnap2.py /tmp/opencode/g_s12.rpp 400 /tmp/opencode/sc_g_s12')
|
||||||
|
print('capture tail:',r.strip().splitlines()[-1] if r.strip() else 'empty')
|
||||||
|
|
||||||
|
# 3. render48k tract (same input tone1k, band fc1000 q1 s12)
|
||||||
|
tf='/tmp/opencode/tract_g_s12.txt'
|
||||||
|
if os.path.exists(tf): os.remove(tf)
|
||||||
|
env=dict(os.environ); env['RT_DUMP_BIN']=tf
|
||||||
|
subprocess.run(['/home/m/re-tools/dsp/build/render48k','/home/m/soothe-bt/tone1k.wav',
|
||||||
|
'/tmp/o48_g.wav','1000,1.0,12'],capture_output=True,env=e if False else env)
|
||||||
|
print('tract done')
|
||||||
|
|
||||||
|
# 4. analysis
|
||||||
|
import wave
|
||||||
|
def loadwav(p):
|
||||||
|
w=wave.open(p,'rb'); n=w.getnframes(); ch=w.getnchannels(); sw=w.getsampwidth()
|
||||||
|
d=w.readframes(n); w.close()
|
||||||
|
if sw==2: return np.frombuffer(d,dtype=np.int16).astype(np.float64).reshape(-1,ch).mean(1)/32768
|
||||||
|
raw=np.frombuffer(d,dtype=np.uint8).reshape(-1,ch,3)
|
||||||
|
s=raw[:,:,0].astype(np.int64)|(raw[:,:,1].astype(np.int64)<<8)|(raw[:,:,2].astype(np.int64)<<16)
|
||||||
|
return np.where(s>=0x800000,s-0x1000000,s).astype(np.float64).reshape(-1,ch).mean(1)/8388608
|
||||||
|
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-12))
|
||||||
|
|
||||||
|
inp=loadwav('/home/m/soothe-bt/tone1k.wav')
|
||||||
|
ref=loadwav(wav)
|
||||||
|
rcut=-db(ta(ref,1000)/ta(inp,1000))
|
||||||
|
|
||||||
|
best=None
|
||||||
|
for fn in sorted(glob.glob('/tmp/opencode/sc_g_s12/ph*.npz')):
|
||||||
|
d=np.load(fn)
|
||||||
|
if '0x540628' not in d.files: continue
|
||||||
|
s=d['0x540628'].astype(np.float64)
|
||||||
|
if best is None or s[85]<best[0]: best=(s[85],s)
|
||||||
|
cutD=-best[0]*8.685889638 if best else float('nan')
|
||||||
|
|
||||||
|
lv=res85=None
|
||||||
|
for ln in open(tf):
|
||||||
|
if ln.startswith('#'): continue
|
||||||
|
p=ln.split()
|
||||||
|
if int(p[0])==85: lv=float(p[3]); res85=float(p[2])
|
||||||
|
|
||||||
|
alpha,beta,c=3.2193,0.4927,0.5423 # dual constants
|
||||||
|
li=beta*np.expm1((cutD-c)/alpha)
|
||||||
|
print('\n=== fc1000 q1 SENS12 (недостающая точка) ===')
|
||||||
|
print('our lvl@85=%.3f res@85=%.5f' % (lv,res85))
|
||||||
|
print('scratch cut_D=%.2f dB' % cutD)
|
||||||
|
print('law-inverse impl lvl=%.3f -> implied res=%.5f' % (li, 0.7307*3.2309/max(li,1e-9)))
|
||||||
|
print('REF goertzel cut@1000=%.2f dB' % rcut)
|
||||||
|
print('\ncontext: sens18 impl res=0.15244 (плато); ours@s18=0.0284')
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""hotips.py — sample thread RIPs aggressively during the render burst to find
|
||||||
|
which module code actually executes (the real DSP path)."""
|
||||||
|
import collections
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
MOD_LO, MOD_HI = 0x180001000, 0x181baa000
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', '/home/m/soothe-bt/dual_b1q_0.5.rpp'],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
host = None
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.002)
|
||||||
|
print('host', host, 'at %.3fs' % (time.time() - t0))
|
||||||
|
ips = collections.Counter()
|
||||||
|
t_end = time.time() + float(os.environ.get('HOT_DUR', '6'))
|
||||||
|
n_ok = n_fail = 0
|
||||||
|
while time.time() < t_end:
|
||||||
|
for tid_s in glob.glob(f'/proc/{host}/task/*'):
|
||||||
|
try:
|
||||||
|
parts = open(f'{tid_s}/syscall').read().split()
|
||||||
|
ip = int(parts[-1], 16)
|
||||||
|
n_ok += 1
|
||||||
|
if MOD_LO <= ip < MOD_HI:
|
||||||
|
ips[ip] += 1
|
||||||
|
except Exception:
|
||||||
|
n_fail += 1
|
||||||
|
proc.kill()
|
||||||
|
print('samples ok=%d fail=%d, in-module=%d' % (n_ok, n_fail, sum(ips.values())))
|
||||||
|
# bucket to 64-byte lines, aggregate by function-ish regions
|
||||||
|
agg = collections.Counter()
|
||||||
|
for ip, c in ips.items():
|
||||||
|
agg[(ip >> 6) << 6] += c
|
||||||
|
for addr, c in agg.most_common(40):
|
||||||
|
print('%#x %d' % (addr, c))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""hunt2.py — enumerate ALL soothe2 module instances during offline render.
|
||||||
|
|
||||||
|
Chunk-correct full-heap scan (rendersnap-style) collecting EVERY ctx candidate
|
||||||
|
(vtable 0x1824AC210 or m48 marker), not just the first. Per candidate: sens,
|
||||||
|
fir43/fir171 (via ctx+0x540668 ptr), scalar bank snapshot. Goal: find the
|
||||||
|
GUI/DSP pair (24c/24j): visible instance holds shallow kernel while audio is
|
||||||
|
processed by another instance with the deep one.
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
VT = struct.pack('<Q', 0x1824AC210)
|
||||||
|
M48 = struct.pack('<I', 0x473b8000)
|
||||||
|
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
import glob
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid = int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace')
|
||||||
|
maps = open(f'/proc/{pid}/maps').read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd:
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
rpp = sys.argv[1] if len(sys.argv) > 1 else '/tmp/opencode/multi.rpp'
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True)
|
||||||
|
proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors',
|
||||||
|
'-renderproject', rpp],
|
||||||
|
stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT)
|
||||||
|
t0 = time.time()
|
||||||
|
host = None
|
||||||
|
while time.time() - t0 < 30 and not host:
|
||||||
|
host = find_host()
|
||||||
|
time.sleep(0.001)
|
||||||
|
if not host:
|
||||||
|
print('NO HOST')
|
||||||
|
return 1
|
||||||
|
fd = os.open(f'/proc/{host}/mem', os.O_RDONLY)
|
||||||
|
|
||||||
|
def rd(a, n):
|
||||||
|
try:
|
||||||
|
return os.pread(fd, n, a)
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def scan_all():
|
||||||
|
found = {}
|
||||||
|
for line in open(f'/proc/{host}/maps'):
|
||||||
|
parts = line.split()
|
||||||
|
if 'rw' not in parts[1]:
|
||||||
|
continue
|
||||||
|
lo, hi = (int(x, 16) for x in parts[0].split('-'))
|
||||||
|
CH = 16 * 1024 * 1024
|
||||||
|
a = lo
|
||||||
|
while a < hi:
|
||||||
|
d = rd(a, min(CH + 4096, hi - a))
|
||||||
|
if not d:
|
||||||
|
break
|
||||||
|
for pat, off in ((VT, 0), (M48, -0x24)):
|
||||||
|
j = d.find(pat)
|
||||||
|
while j >= 0:
|
||||||
|
cand = a + j + off
|
||||||
|
if cand not in found:
|
||||||
|
sb = rd(cand + 0x540870, 4)
|
||||||
|
if sb and struct.unpack('<f', sb)[0] > 100:
|
||||||
|
found[cand] = True
|
||||||
|
j = d.find(pat, j + 1)
|
||||||
|
a += CH
|
||||||
|
return list(found)
|
||||||
|
|
||||||
|
known = {}
|
||||||
|
rounds = 0
|
||||||
|
while time.time() - t0 < 25:
|
||||||
|
rounds += 1
|
||||||
|
try:
|
||||||
|
os.kill(host, signal.SIGSTOP)
|
||||||
|
except ProcessLookupError:
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
cands = scan_all()
|
||||||
|
new = [c for c in cands if c not in known]
|
||||||
|
for c in new:
|
||||||
|
known[c] = rounds
|
||||||
|
pb = rd(c + 0x540668, 8)
|
||||||
|
m43 = m171 = -1
|
||||||
|
if pb:
|
||||||
|
p = struct.unpack('<Q', pb)[0]
|
||||||
|
if p > 0x10000:
|
||||||
|
fb = rd(p, 2049 * 8)
|
||||||
|
if fb:
|
||||||
|
arr = np.frombuffer(fb[:2049 * 8], dtype='<f4')
|
||||||
|
mag = np.hypot(arr[0::2], arr[1::2])
|
||||||
|
m43, m171 = float(mag[43]), float(mag[171])
|
||||||
|
sb = rd(c + 0x540888, 4)
|
||||||
|
s888 = struct.unpack('<f', sb)[0] if sb else -1
|
||||||
|
print('NEW ctx %#x @r%d t=%.2f fir43=%.4f fir171=%.4f s888=%.4f'
|
||||||
|
% (c, rounds, time.time() - t0, m43, m171, s888), flush=True)
|
||||||
|
# status of known ones every round
|
||||||
|
for c in known:
|
||||||
|
pb = rd(c + 0x540668, 8)
|
||||||
|
if pb:
|
||||||
|
p = struct.unpack('<Q', pb)[0]
|
||||||
|
if p > 0x10000:
|
||||||
|
fb = rd(p, 2049 * 8)
|
||||||
|
if fb:
|
||||||
|
arr = np.frombuffer(fb[:2049 * 8], dtype='<f4')
|
||||||
|
mag = np.hypot(arr[0::2], arr[1::2])
|
||||||
|
print(' st ctx %#x t=%.2f fir43=%.4f' % (c, time.time() - t0, mag[43]), flush=True)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
os.kill(host, signal.SIGCONT)
|
||||||
|
except ProcessLookupError:
|
||||||
|
pass
|
||||||
|
time.sleep(0.05)
|
||||||
|
print('total instances: %d' % len(known))
|
||||||
|
proc.kill()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""iat_name.py — resolve imported-function names for bigkernel dispatch targets.
|
||||||
|
Reads runtime IAT values, finds owning module, parses PE exports."""
|
||||||
|
import struct, subprocess, sys, time
|
||||||
|
import glob, os
|
||||||
|
|
||||||
|
BASE=0x180000000
|
||||||
|
data=open('/home/m/re-tools/soothe_mem.bin','rb').read()
|
||||||
|
|
||||||
|
def rd(fd,a,n):
|
||||||
|
try: return os.pread(fd,n,a)
|
||||||
|
except OSError: return None
|
||||||
|
|
||||||
|
def find_host():
|
||||||
|
for p in glob.glob('/proc/[0-9]*'):
|
||||||
|
pid=int(os.path.basename(p))
|
||||||
|
try:
|
||||||
|
cmd=open('/proc/%d/cmdline'%pid,'rb').read().replace(b'\0',b' ').decode('utf8','replace')
|
||||||
|
maps=open('/proc/%d/maps'%pid).read()
|
||||||
|
except Exception: continue
|
||||||
|
if 'soothe2' in maps and 'reaper' not in cmd: return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
def iat_slot(stub):
|
||||||
|
# pattern: mov rax,[rip+rel] (48 8b 05 rel32)
|
||||||
|
off=stub-BASE
|
||||||
|
b=data[off:off+7]
|
||||||
|
if b[:2]!=b'\x48\x8b': return None
|
||||||
|
rel=struct.unpack('<i',b[2:6])[0]
|
||||||
|
return stub+6+rel
|
||||||
|
|
||||||
|
def pe_exports(path):
|
||||||
|
"""Parse PE export table -> {name: rva}"""
|
||||||
|
try:
|
||||||
|
f=open(path,'rb').read()
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
if f[:2]!=b'MZ': return {}
|
||||||
|
pe=struct.unpack('<I',f[0x3c:0x40])[0]
|
||||||
|
if f[pe:pe+4]!=b'PE\0\0': return {}
|
||||||
|
nsec=struct.unpack('<H',f[pe+6:pe+8])[0]
|
||||||
|
optsz=struct.unpack('<H',f[pe+20:pe+22])[0]
|
||||||
|
magic=struct.unpack('<H',f[pe+24:pe+26])[0]
|
||||||
|
ddir=pe+24+(0x70 if magic==0x20b else 0x60)+0*8 # data dir[0]=export
|
||||||
|
exp_rva,exp_sz=struct.unpack('<II',f[ddir:ddir+8])
|
||||||
|
if not exp_rva: return {}
|
||||||
|
# sections
|
||||||
|
secs=[]
|
||||||
|
so=pe+24+optsz
|
||||||
|
for i in range(nsec):
|
||||||
|
s=f[so+i*40:so+i*40+40]
|
||||||
|
va,sz=struct.unpack('<II',s[12:20])
|
||||||
|
raw,rsz=struct.unpack('<II',s[20:28])
|
||||||
|
secs.append((va,sz,raw,rsz))
|
||||||
|
def r2o(rva):
|
||||||
|
for va,sz,raw,rsz in secs:
|
||||||
|
if va<=rva<va+max(sz,rsz): return raw+(rva-va)
|
||||||
|
return None
|
||||||
|
eo=r2o(exp_rva)
|
||||||
|
if eo is None: return {}
|
||||||
|
nnames=struct.unpack('<I',f[eo+24:eo+28])[0]
|
||||||
|
nrva=struct.unpack('<I',f[eo+32:eo+36])[0]
|
||||||
|
names_rva=struct.unpack('<I',f[eo+32+4:eo+32+8])[0]
|
||||||
|
funcs_rva=struct.unpack('<I',f[eo+28:eo+32])[0]
|
||||||
|
no=r2o(names_rva); fo=r2o(funcs_rva)
|
||||||
|
out={}
|
||||||
|
if no is None or fo is None: return {}
|
||||||
|
for i in range(nnames):
|
||||||
|
nrva_i=struct.unpack('<I',f[no+i*4:no+i*4+4])[0]
|
||||||
|
noff=r2o(nrva_i)
|
||||||
|
if noff is None: continue
|
||||||
|
end=f.find(b'\0',noff)
|
||||||
|
nm=f[noff:end].decode('ascii','replace')
|
||||||
|
frva=struct.unpack('<I',f[fo+i*4:fo+i*4+4])[0]
|
||||||
|
out[nm]=frva
|
||||||
|
return out
|
||||||
|
|
||||||
|
subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; "
|
||||||
|
"rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True)
|
||||||
|
proc=subprocess.Popen(['/usr/bin/reaper','-nosplash','-ignoreerrors','-renderproject','/tmp/opencode/multi.rpp'],
|
||||||
|
stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT)
|
||||||
|
t0=time.time(); host=None
|
||||||
|
while time.time()-t0<30 and not host:
|
||||||
|
host=find_host(); time.sleep(0.002)
|
||||||
|
print('host',host,flush=True)
|
||||||
|
import signal as sg
|
||||||
|
os.kill(host,sg.SIGSTOP)
|
||||||
|
fd=os.open('/proc/%d/mem'%host,os.O_RDONLY)
|
||||||
|
|
||||||
|
# build module map
|
||||||
|
mods=[]
|
||||||
|
for line in open('/proc/%d/maps'%host):
|
||||||
|
parts=line.split()
|
||||||
|
if len(parts)<6 or 'x' not in parts[1]: continue
|
||||||
|
lo,hi=(int(x,16) for x in parts[0].split('-'))
|
||||||
|
mods.append((lo,hi,parts[5]))
|
||||||
|
print('modules:',len(mods))
|
||||||
|
|
||||||
|
def owner(addr):
|
||||||
|
for lo,hi,path in mods:
|
||||||
|
if lo<=addr<hi: return (lo,addr-lo,path)
|
||||||
|
return None
|
||||||
|
|
||||||
|
targets={}
|
||||||
|
TBL={0x180140b30:0x1826176c8,0x180140b60:0x182617708,0x1801409e0:0x182617508,
|
||||||
|
0x180140ad0:0x182617648,0x180140a40:0x182617588}
|
||||||
|
for stub,tbl in TBL.items():
|
||||||
|
v=rd(fd,tbl+32,8)
|
||||||
|
tgt=struct.unpack('<Q',v)[0] if v else 0
|
||||||
|
# second level: tgt code = mov rax,[rip+rel]; jmp rax -> IAT slot
|
||||||
|
print(' L2: tgt=%x' % tgt, flush=True)
|
||||||
|
if 0x180000000 <= tgt < 0x187000000:
|
||||||
|
off2=tgt-BASE
|
||||||
|
b2=data[off2:off2+7] if 0<=off2<len(data)-7 else rd(tgt,7)
|
||||||
|
if b2[:2]==b'\x48\x8b':
|
||||||
|
rel2=struct.unpack('<i',b2[3:7])[0]
|
||||||
|
slot=tgt+7+rel2
|
||||||
|
print(' L2: b2=%s rel2=%x slot=%x' % (b2.hex(),rel2&0xffffffff,slot), flush=True)
|
||||||
|
fv=rd(fd,slot,8)
|
||||||
|
if fv:
|
||||||
|
tgt=struct.unpack('<Q',fv)[0]
|
||||||
|
else:
|
||||||
|
print(' slot read FAIL',slot,flush=True)
|
||||||
|
else:
|
||||||
|
print(' no mov-rax pattern at %x: %s'%(tgt,b2[:3].hex() if b2 else '-'),flush=True)
|
||||||
|
ow=owner(tgt)
|
||||||
|
print('%x idx4->%x runtime=%x owner=%s' % (stub,tbl,tgt,ow[2] if ow else '?'),flush=True)
|
||||||
|
if ow:
|
||||||
|
lo,rva,path=ow
|
||||||
|
exps=pe_exports(path)
|
||||||
|
best=[nm for nm,r in exps.items() if r==rva]
|
||||||
|
print(' export:',best,flush=True)
|
||||||
|
os.close(fd)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user