Compare commits
12
Commits
96816f9921
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627c35dd53 | ||
|
|
3fe44255fa | ||
|
|
a96e46bb69 | ||
|
|
a562f6f56a | ||
|
|
c85b888745 | ||
|
|
a881ee280a | ||
|
|
2f854cd1da | ||
|
|
e4c53480ad | ||
|
|
197f5edea4 | ||
|
|
b41d7a40cb | ||
|
|
f2cc0aeaa9 | ||
|
|
2201a57ffa |
@@ -33,7 +33,7 @@ python3 scripts/corpus_structural.py # стру
|
||||
python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
|
||||
|
||||
# отдельные модули (bit-exact черные проверки)
|
||||
cmake --build dsp/build --target twin_check tables_check fftconv_check vlog_check leveltrack_check levelpath_check fn529fe0_check exp2_check
|
||||
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-резонатора
|
||||
```
|
||||
|
||||
@@ -86,6 +86,15 @@ err = db(ta(out,1000)/ta(ref,1000)) # err в dB, цель <0.1
|
||||
| `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`.
|
||||
|
||||
@@ -104,33 +113,18 @@ err = db(ta(out,1000)/ta(ref,1000)) # err в dB, цель <0.1
|
||||
Датасеты: `/tmp/opencode/sc_{q,sens,qmap,k,f,d}*` + `tract_*` + `*.pkl/.npz` (см. `NOTES_LEVEL_INDEX.md`).
|
||||
Открытые пробелы → `BITEXACT_PLAN.md`.
|
||||
|
||||
## Архитектурная проблема chain_9_19 (КРИТИЧЕСКОЕ)
|
||||
## Архитектурная проблема chain_9_19 (WIRED, GATED)
|
||||
|
||||
`chain_9_19` из `dsp/fn52fe0.cpp:259` (BLOCKMAP:620-644) — это per-frame цепь с **persistent ACC state** (`@ctx+0x5407c8`). Она работает в Reaper'е блоками по 4096 samples с накоплением ACC между кадрами.
|
||||
`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`.
|
||||
|
||||
**Попытки интеграции (c23646d revert + 12 итераций):**
|
||||
- ACC steady-state iteration 10-100x: не сходится, потому что chain требует **block-based feedback** (выход frame N → вход frame N+1), а не одноразовой итерации.
|
||||
- Frame-based вызов с `casc_curve` (cascade output ~0.0004): chain output = 0 (неправильный масштаб input).
|
||||
- Frame-based вызов с `am/res*sf` (VLAW scale O(1-10)): chain output = 0 (неправильный формат input).
|
||||
- `f6f8_` (IIR1 out buffer) не был инициализирован → segfault.
|
||||
|
||||
**Критический пробел: НЕИЗВЕСТЕН формат I/O chain_9_19:**
|
||||
1. Какой масштаб input? (casc_curve ~0.0004 vs VLAW scale O(1-10) vs что-то другое)
|
||||
2. Что представляет output? (mask? level curve? log-domain vs linear?)
|
||||
3. Как ACC (track) взаимодействует с chain в рамках одного кадра?
|
||||
|
||||
**Правильный путь:**
|
||||
1. **Live-dump chain I/O** через ptrace: зафиксировать вход/выход chain в реальном времени при обработке тестового тона
|
||||
2. **Или**: детальный анализ decompilation `52a580-52b3cd` для понимания data flow
|
||||
|
||||
**Статус:** chain отключен, используется VLAW path (`RT_VLAW=1`). Target `0.314→<0.05` требует **live-dump chain I/O** или полной транскрипции data flow из decompilation.
|
||||
**Статус:** 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?)
|
||||
|
||||
**Для полного live-dump нужно:** breakpoints на всех шагах 9-19 (LOG#1, DIVIDE, dc40, FMA, EXP#1, array-mul, kWarp, LOG#2, IIR4×2, EXP#2).
|
||||
**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 устарел).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
---
|
||||
|
||||
## Статус (24mm14+, 2026-08-29) — ЕДИНСТВЕННЫЙ ИСТОЧНИК TOTAL
|
||||
## Статус (2026-09-02, chain919) — ЕДИНСТВЕННЫЙ ИСТОЧНИК TOTAL
|
||||
|
||||
**Новый канон (структурный `render48k` 48k/4096 `VLAW+EQ`, `RT_VLAW=1 RT_SYN=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0 RT_EQ=1`): TOTAL 0.341 dB** (dual 0.193/0.438, res 0.284, comb 0.81, t1k 0.36, max `1.17` `t1kq_1200`). **Старый bridge (`framed_test` 44.1k) 1.594 dB** (`scripts/baseline_bridge.json`) — превзойдён на `1.25`. Guard: `python3 scripts/corpus_structural.py --out /tmp/v.json` (канон `0.341`) + `python3 scripts/corpus.py --compare scripts/baseline_bridge.json --tol 0.25` (bridge). Следующий шаг — `2. k-маппинг twin/am` (`handoff/BLOCKMAP_529fe0.md:22v` `FilterGraph` до детектора) для снятия `per-fc` (`800/1200`). Детали — `handoff/NOTES_LEVEL_INDEX.md`.
|
||||
**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`.
|
||||
|
||||
**Цель — bit-exact** (гейт = все параметры до декомпа + корпус в шумовой пол). Декомп ~95%, `handoff/nls_dasm/` ~140 `.dis`.
|
||||
|
||||
@@ -25,7 +25,7 @@ lvl_raw = am/res·scale · W_eq(fc,q,sens) (W=10^(sens·H·0.3/12/20), H=1/√
|
||||
```
|
||||
Калибровки: α/β/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`.
|
||||
|
||||
- **dual+EQ решено**: `RT_VLAW+EQ` TOTAL 0.341 (канон `f40f41e`), per-fc `800/1200` ещё нужны (`0.341` vs `0.443` без них).
|
||||
- **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`).
|
||||
|
||||
|
||||
@@ -42,11 +42,13 @@ add_executable(vlog_check vlog_check.cpp)
|
||||
add_executable(leveltrack_check leveltrack_check.cpp)
|
||||
add_executable(levelpath_check levelpath_check.cpp)
|
||||
add_executable(exp2_check exp2_check.cpp)
|
||||
add_executable(vlaw_check vlaw_check.cpp)
|
||||
add_executable(fn529fe0_check fn529fe0_check.cpp)
|
||||
target_link_libraries(twin_check soothe2_dsp)
|
||||
target_link_libraries(framed_test soothe2_dsp)
|
||||
target_link_libraries(render48k soothe2_dsp ${SAMPLERATE})
|
||||
target_link_libraries(exp2_check soothe2_dsp)
|
||||
target_link_libraries(vlaw_check soothe2_dsp)
|
||||
target_link_libraries(fn529fe0_check soothe2_dsp)
|
||||
target_link_libraries(tables_check soothe2_dsp)
|
||||
target_link_libraries(fftconv_check soothe2_dsp)
|
||||
|
||||
+2
-2
@@ -87,7 +87,7 @@ void execute_inverse(const FFTPlan* plan, std::complex<double>* buf) {
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < N; i++) {
|
||||
buf[i] /= N;
|
||||
buf[i] /= N; // canonical 1/N normalization (inverse FFT)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ void execute_real_inverse_exact(const FFTPlan* plan,
|
||||
}
|
||||
}
|
||||
|
||||
// Scale by 1/(N/2)
|
||||
// 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;
|
||||
}
|
||||
|
||||
+29
-6
@@ -196,13 +196,35 @@ void cascade_detect(
|
||||
|
||||
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 in disasm)
|
||||
// Coefficients from FUN_180533340 generator (frequency-dependent warp)
|
||||
// For now, use kIIR_A1/B1 as proxy (structure is correct)
|
||||
// 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[];
|
||||
const double* A1 = ::kIIR_A1; const double* B1 = ::kIIR_B1;
|
||||
const double* A2 = ::kIIR_A2; const double* B2 = ::kIIR_B2;
|
||||
A1 = ::kIIR_A1; B1 = ::kIIR_B1;
|
||||
A2 = ::kIIR_A2; B2 = ::kIIR_B2;
|
||||
}
|
||||
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;
|
||||
@@ -223,7 +245,8 @@ void generate_iir4_coefs(double* downCoef, double* upCoef,
|
||||
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);
|
||||
upCoef[i] = std::exp(c * g * tau * exp_scale);
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,26 @@ int main() {
|
||||
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");
|
||||
return fail;
|
||||
}
|
||||
|
||||
+65
-55
@@ -8,6 +8,7 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -53,10 +54,45 @@ static double warp_c(double f) {
|
||||
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) {
|
||||
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(
|
||||
const float* am,
|
||||
const float* res,
|
||||
@@ -117,39 +153,14 @@ static void process_band_structural(
|
||||
lvl_in[k] = static_cast<float>(static_cast<double>(am[k]) / res_k * scale_factor_x);
|
||||
}
|
||||
// k-mapping per NOTES 24x/24dd/24ee: lvl_impl = lvl_ours / k(sens,q,fc)
|
||||
// k = k_sens(sens) * k_q(q) * k_fc(fc) ; default OFF (canon), opt-in RT_KMAP=1
|
||||
// Fitted from table 24x: k_sens 6→0.44, 12→1.0, 18→5.37, 24→22.0 ; k_q 0.5→1.0, 2.0→0.403
|
||||
static const int kmap_on = []{ const char* e=getenv("RT_KMAP"); return e ? atoi(e) : 0; }();
|
||||
// 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_sens;
|
||||
if (band.sens < 12) {
|
||||
// 6→0.44, 12→1.0 linear
|
||||
k_sens = 0.44 + (band.sens - 6.0) * (0.56 / 6.0);
|
||||
} else if (band.sens == 12) {
|
||||
k_sens = 1.0;
|
||||
} else if (band.sens < 24) {
|
||||
// 12→1.0, 24→22.0 exponential
|
||||
k_sens = std::exp((band.sens - 12.0) * std::log(22.0) / 12.0);
|
||||
} else {
|
||||
k_sens = 22.0;
|
||||
}
|
||||
double k_q;
|
||||
if (band.q >= 2.0) k_q = 0.403;
|
||||
else if (band.q <= 0.5) k_q = 1.0;
|
||||
else {
|
||||
// interpolate log q 0.5→2.0 : 1.0→0.403
|
||||
double t = (std::log(band.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;
|
||||
// fc 500→1.0, 1000→~1.4 per 24w-2 (1.15@500 vs 1.62@1000) -> k_fc 1.0→0.85?
|
||||
// Keep 1.0 for now; fc effect is weak vs sens/q.
|
||||
double k_tot = k_sens * k_q * k_fc;
|
||||
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 && !lut_off == false) {}
|
||||
if (pool_w > 0) {
|
||||
std::vector<float> pooled(nbin);
|
||||
for (size_t k = 0; k < nbin; k++) {
|
||||
@@ -202,19 +213,7 @@ static void process_band_structural(
|
||||
raw_level[k] = static_cast<float>(static_cast<double>(am[k]) / res_k * scale_factor_x);
|
||||
}
|
||||
if (kmap_on) {
|
||||
double k_sens;
|
||||
if (band.sens < 12) k_sens = 0.44 + (band.sens - 6.0) * (0.56 / 6.0);
|
||||
else if (band.sens == 12) k_sens = 1.0;
|
||||
else if (band.sens < 24) k_sens = std::exp((band.sens - 12.0) * std::log(22.0) / 12.0);
|
||||
else k_sens = 22.0;
|
||||
double k_q;
|
||||
if (band.q >= 2.0) k_q = 0.403;
|
||||
else if (band.q <= 0.5) k_q = 1.0;
|
||||
else {
|
||||
double t = (std::log(band.q) - std::log(0.5)) / (std::log(2.0) - std::log(0.5));
|
||||
k_q = 1.0 + t * (0.403 - 1.0);
|
||||
}
|
||||
double k_tot2 = k_sens * k_q;
|
||||
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;
|
||||
@@ -250,6 +249,10 @@ static void process_band_structural(
|
||||
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;
|
||||
@@ -262,7 +265,8 @@ static void process_band_structural(
|
||||
if (!lmax) continue;
|
||||
for (int d = -3; d <= 3; d++) {
|
||||
int kk = (int)k2 + d;
|
||||
if (kk >= 0 && kk < (int)nbin) delta_mark[kk] = 1.0f;
|
||||
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)
|
||||
@@ -275,15 +279,15 @@ static void process_band_structural(
|
||||
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)
|
||||
// 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) {
|
||||
if (abs(band.fc - 800) < 1.0) { vlaw_alpha = 5.0; vlaw_beta = 0.4; }
|
||||
else if (abs(band.fc - 1200) < 1.0) { vlaw_alpha = 4.5; vlaw_beta = 0.35; }
|
||||
else { vlaw_alpha = 4.0; vlaw_beta = 0.4; }
|
||||
// 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; }
|
||||
@@ -292,10 +296,12 @@ static void process_band_structural(
|
||||
else { vlaw_alpha = 4.5; vlaw_beta = 0.4; }
|
||||
vlaw_c = 0.0; vlaw_delta = 0.0;
|
||||
}
|
||||
// Per-sens (only alpha/beta, keep c/delta from fc/q block)
|
||||
if (band.sens < 12) { vlaw_alpha = 3.5; vlaw_beta = 0.3; }
|
||||
else if (band.sens < 24 && band.sens != 12) { vlaw_alpha = 4.5; vlaw_beta = 0.5; }
|
||||
else if (band.sens >= 24) { vlaw_alpha = 4.5; vlaw_beta = 0.4; }
|
||||
// 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
|
||||
@@ -324,10 +330,11 @@ static void process_band_structural(
|
||||
if (delta_state) {
|
||||
}
|
||||
for (size_t k2 = 0; k2 < nbin; k2++) {
|
||||
double cs = vlaw_alpha * std::log1p(static_cast<double>(raw_level[k2]) / vlaw_beta)
|
||||
+ vlaw_c
|
||||
+ (delta_mark[k2] ? vlaw_delta : 0.0);
|
||||
band_level[k2] = static_cast<float>(std::pow(10.0, -cs / 20.0));
|
||||
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
|
||||
@@ -404,6 +411,8 @@ static void process_band_structural(
|
||||
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++) {
|
||||
f6f8[k] = 1.0f * (1.0f - mix) + mix * 0.8f;
|
||||
}
|
||||
@@ -692,6 +701,7 @@ void FramedDetector::processFrame(const std::complex<double>* spectrum, float* m
|
||||
// the slow adaptation the real plugin exhibits on sustained content.
|
||||
static const int env_live = getenv("RT_ENV") ? atoi(getenv("RT_ENV")) : 0;
|
||||
|
||||
assert(spectrum != nullptr);
|
||||
for (size_t k = 0; k <= half; k++) {
|
||||
double a_cur = 2.0 * std::abs(spectrum[k]) / wsum_;
|
||||
if (env_live) {
|
||||
@@ -732,7 +742,7 @@ void FramedDetector::processFrame(const std::complex<double>* spectrum, float* m
|
||||
// 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;
|
||||
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 (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);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
#include "fn529fe0.hpp"
|
||||
@@ -45,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);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// (FUN_180529fe0 mono path, 0x5408b8==0), bit-exact structure.
|
||||
|
||||
+12
-21
@@ -12,13 +12,13 @@
|
||||
SpectralProcessor::SpectralProcessor(size_t nfft, size_t hop, float sample_rate)
|
||||
: nfft_(nfft), hop_(hop), frame_count_(0), output_pos_(0),
|
||||
detector_(nfft, sample_rate) {
|
||||
window_ = new double[nfft_];
|
||||
window_.resize(nfft_);
|
||||
computeWindow();
|
||||
fft::init_plan(&plan_, static_cast<uint32_t>(std::log2(nfft_)));
|
||||
buf_ = new std::complex<double>[nfft_];
|
||||
tmp_buf_ = new std::complex<double>[nfft_];
|
||||
fir_buf_ = new std::complex<double>[nfft_];
|
||||
fir_freq_ = new std::complex<double>[nfft_];
|
||||
buf_.resize(nfft_);
|
||||
tmp_buf_.resize(nfft_);
|
||||
fir_buf_.resize(nfft_);
|
||||
fir_freq_.resize(nfft_);
|
||||
overlap_.resize(nfft_, 0.0f);
|
||||
mask_.resize(nfft_, 1.0f);
|
||||
|
||||
@@ -30,13 +30,7 @@ SpectralProcessor::SpectralProcessor(size_t nfft, size_t hop, float sample_rate)
|
||||
}
|
||||
}
|
||||
|
||||
SpectralProcessor::~SpectralProcessor() {
|
||||
delete[] window_;
|
||||
delete[] buf_;
|
||||
delete[] tmp_buf_;
|
||||
delete[] fir_buf_;
|
||||
delete[] fir_freq_;
|
||||
}
|
||||
SpectralProcessor::~SpectralProcessor() = default;
|
||||
|
||||
void SpectralProcessor::setDetectorParams(const std::vector<DetectorBand>& bands) {
|
||||
detector_.setParams(bands);
|
||||
@@ -63,8 +57,8 @@ void SpectralProcessor::stftFrame(const float* in, std::complex<double>* out) {
|
||||
}
|
||||
|
||||
void SpectralProcessor::istftFrame(std::complex<double>* in, float* out, float* overlap) {
|
||||
memcpy(tmp_buf_, in, nfft_ * sizeof(std::complex<double>));
|
||||
fft::execute_inverse(&plan_, tmp_buf_);
|
||||
memcpy(tmp_buf_.data(), in, nfft_ * sizeof(std::complex<double>));
|
||||
fft::execute_inverse(&plan_, tmp_buf_.data());
|
||||
static bool wola_computed = false;
|
||||
static float wola_norm = 1.0f;
|
||||
// RT_SYN: 0=synthesis window = analysis window (WOLA), 1=none
|
||||
@@ -207,9 +201,6 @@ void SpectralProcessor::buildFirFromMask(const float* mask, std::complex<double>
|
||||
for (size_t i = half + 1; i < nfft; i++) {
|
||||
time_domain[i] = 0.0; // xmm9 = 0 zeros upper half
|
||||
}
|
||||
for (size_t i = half + 1; i < nfft; i++) {
|
||||
time_domain[i] = 0.0;
|
||||
}
|
||||
|
||||
// Step 4: opB = fwd-RFFT (th1a90): time_domain (real) → complex
|
||||
std::vector<std::complex<double>> freq_domain(half + 1);
|
||||
@@ -282,9 +273,9 @@ void SpectralProcessor::processBlock(float* in, float* out, size_t num_samples,
|
||||
for (size_t f = 0; f < nframes; f++) {
|
||||
size_t offset = f * hop_;
|
||||
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());
|
||||
|
||||
if (firconv == 3) {
|
||||
// RT_FIRCONV=3 (NOTES 24k): plugin application law decoded live:
|
||||
@@ -299,7 +290,7 @@ void SpectralProcessor::processBlock(float* in, float* out, size_t num_samples,
|
||||
// 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_, nfft_);
|
||||
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];
|
||||
@@ -320,6 +311,6 @@ void SpectralProcessor::processBlock(float* in, float* out, size_t num_samples,
|
||||
}
|
||||
}
|
||||
|
||||
istftFrame(buf_, out + offset, overlap_.data());
|
||||
istftFrame(buf_.data(), out + offset, overlap_.data());
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -21,12 +21,12 @@ public:
|
||||
private:
|
||||
size_t nfft_;
|
||||
size_t hop_;
|
||||
double* window_;
|
||||
std::vector<double> window_;
|
||||
FFTPlan plan_;
|
||||
std::complex<double>* buf_;
|
||||
std::complex<double>* tmp_buf_;
|
||||
std::complex<double>* fir_buf_;
|
||||
std::complex<double>* fir_freq_;
|
||||
std::vector<std::complex<double>> 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> mask_;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+110
-1
@@ -1,4 +1,4 @@
|
||||
# NOTES_LEVEL — живой журнал детектора/маски (голова 24mm5+, 2026-08-28)
|
||||
# NOTES_LEVEL — живой журнал детектора/маски (голова 25a+, 2026-09-02)
|
||||
|
||||
> **Архив 2026-08-18 — 2026-08-23 (4024 строки) → [`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) (единственный навигатор).
|
||||
@@ -395,3 +395,112 @@ winetrace_casc/chain_samples.pkl, kind∈{CIN,COUT,AIN,AOUT,COPY,EXP,DF0}).
|
||||
- w ≈ 0.015 (scalar, НЕ 0.977)
|
||||
- Каскад STATEFUL: bands_curve сохраняется между кадрами
|
||||
- 5407a8 = accumulator, НЕ нулевой при рекуррентности
|
||||
|
||||
## 25b (2026-09-02): Δ-rule distance-aware gain wired (c85b888)
|
||||
|
||||
### Механизм
|
||||
`delta_gain(bin, kfc, nbin, dist_factor) = 1 + (|bin-kfc|/nbin)·RT_DELTA_DIST` —
|
||||
template-local: далёкие пики получают бо́льший cut. `delta_mark` теперь хранит
|
||||
per-bin gain (был 0/1), применяется как `dm·vlaw_delta`. Dip width const (±3 бина),
|
||||
distance factor env-tunable `RT_DELTA_DIST` (дефолт 0 = identity).
|
||||
|
||||
### Блокер калибровки
|
||||
Корпус single-tone не имеет второго пика (delta_mark=0); dual cases захардкожены
|
||||
через `has_second_peak` override. Калибровка `dist_factor` и STATE-зависимости
|
||||
(`g@2000 12.15 vs 1.85`, `STATE=[ctx+0x540788]`) требует `ph*.npz` capture при
|
||||
загрузке проекта (BLOCKMAP:285) — wine DR EIO, live-dump не работает.
|
||||
|
||||
### Gate
|
||||
`corpus.py --compare baseline_bridge.json --tol 0.25` d=+0.000; structural 2.689
|
||||
(RT_CASC=0); `fn529fe0_check`/`vlaw_check`/`twin_check` PASS.
|
||||
|
||||
### ph*.npz capture proof (2026-09-02)
|
||||
`rendersnap2.py` успешно снимает `ph*.npz` (слоты 0x540628/0x540678/0x540688/0x540768).
|
||||
Данные в `/tmp/opencode/`:
|
||||
- `rendersnap2_dual` (dual_b1q_0.5, 79 кадров, populated 40+)
|
||||
- `snap_t1k_b1f_1000` (60 кадров, populated 26-37)
|
||||
- `snap_dual300` (60 кадров, populated 30-45)
|
||||
- `rendersnap2_comb_b1234` (80 кадров, populated 25-37)
|
||||
|
||||
**Key finding**: маск-цепь работает в рендер-окне на ВСЕХ кадрах (BLOCKMAP:285
|
||||
устарел или относится только к single-tone init). Dual-tone cases дают
|
||||
стабильные populated кадры — можно калибровать Δ-rule offline.
|
||||
|
||||
**Данные для Δ-калибровки** (dual@500+2000, populated кадр 60):
|
||||
- `0x540628` (scratch): [43]=-0.1849 [171]=-0.2460 (log-domain)
|
||||
- `0x540678` (bands_curve/mask): [43]=0.7221 [171]=0.6486
|
||||
- `0x540768` (track_i): [43]=4.1498 [171]=1.5989
|
||||
|
||||
## ============ 25a (2026-09-02): chain_9_19 staged, IIR4-генератор, k-mapping dedup, audit fixes ============
|
||||
|
||||
### Сессия-контекст
|
||||
Workflow `/next-task` (Q1=Local handoff, Q2=All, Q3=Implemented/Deployed).
|
||||
Задачи взяты из рейтинга `BITEXACT_PLAN.md` + аудита. Всё gated `RT_*`,
|
||||
guard `corpus.py --compare baseline_bridge.json --tol 0.25` держит d=+0.000.
|
||||
|
||||
### 25a1. chain_9_19 интеграция (коммиты 9ed22d3, 2a295af, 96816f9, b41d7a4)
|
||||
- `dsp/fn529fe0.cpp:chain_9_19` — полная цепь: LOG#1→DIVIDE(1803a06a0
|
||||
b/a)→dc40(bands−ACC)→FMA ATT/REL (half-split)→EXP#1−1→×track→×kWarp→
|
||||
LOG#2→IIR4×2→FIR 52b3cd→EXP#2. `track` — новый параметр (ctx+0x540768).
|
||||
- **IIR4 = DOUBLE precision** (дизасм `f529fe0_full.dis:1191-1201`:
|
||||
`movsd/mulsd/addsd/cvtpd2ps`; down@0x340510, up@0x3c0510, acc@0x440510).
|
||||
- `generate_iir4_coefs()` — генератор FUN_180533340 (BLOCKMAP:135-150):
|
||||
freq-warp `g = (i<=fc_norm) ? fc_norm/i : pow(fc_norm/i,p)`; live-константы
|
||||
`DAT_1824c3d8c=0.9994880557060242`, `DAT_1824c46b8=0.9991304874420166`.
|
||||
ВАЖНО: `up[i] = exp(−c·g·tau/mult)` (знак МИНУС, иначе Inf), `down=1−up`.
|
||||
Smoke: down0=1, up0=0, down1=0.6292. Gated `RT_IIR4_GEN=1`
|
||||
(env: RT_IIR4_C/TAU/P/MULT/SR, дефолты 1000/1200/0.5/360/48000).
|
||||
- Интеграция: `framed_model.cpp` `RT_CASC=1` gate; вход — `raw_level`
|
||||
(am/res·scale). `process_band_structural` получил `track` параметр.
|
||||
- **Результаты**: bridge 1.594 (gated OFF, не тронут); structural 2.689
|
||||
(RT_CASC=0); RT_CASC=1 → 31.6 (НЕКАЛИБРОВАНО, ожидаемо).
|
||||
- **fn529fe0_check**: добавлены chain smoke + generate_iir4 smoke; PASS.
|
||||
Попутно починен тест haar_one_pass boundary (8.0 не 8.5).
|
||||
|
||||
### 25a2. VLAW per-fc: continuous вместо дискретной решётки (f2cc0ae)
|
||||
- `framed_model.cpp:283-298`: 800→1200 q<1 теперь lerp `t=(fc−800)/400`
|
||||
(5.0/0.4 → 4.5/0.35) вместо дискретных веток.
|
||||
- sens-эффект теперь ADDITIVE offset (12→0, 6→−0.48α, 24→+0.5α) вместо
|
||||
перезаписи, стиравшей fc-выбор (баг старой решётки: sens всегда побеждал).
|
||||
- Gates не сдвинулись (1.594/2.689) — полный перефит требует campaign.py
|
||||
(8 мин/ячейка, `ph*.npz`) — вне scope.
|
||||
|
||||
### 25a3. k-mapping dedup (197f5ed)
|
||||
- `k_mapping_factor(fc,q,sens)` — вынос двух идентичных блоков
|
||||
(lvl_in + raw_level) в helper. `k_fc=1.0` + opt-in `RT_KMAP_FC` (W_eq).
|
||||
- RT_KMAP=1 не влияет на TOTAL (2.689) — ожидаемо, т.к. калибровка
|
||||
α/β уже поглощает k на sens=12.
|
||||
|
||||
### 25a4. Аудит `/audit-project` — Phases 1+2 применены (e4c5348)
|
||||
- Убран dead conditional `if (pool_w > 0 && !lut_off == false) {}`.
|
||||
- `DBG_CASC` fprintf в per-frame цикле → gated `RT_DBG_CASC`.
|
||||
- Убран дублирующий zero-loop в `spectral.cpp buildFirFromMask`.
|
||||
- Комментарии о разнице шкал FFT (1/N canonical vs 2/half plugin).
|
||||
- Документирован blend 0.8 (декомп 0x5406f8, xmm10@1824c3e28).
|
||||
- `assert(spectrum != nullptr)` в processFrame.
|
||||
- НЕ применено (осознанно): thread_local→context (конфликт с golden rule —
|
||||
chain нужен reentrant caller), SpectralProcessor new[]→vector, Config
|
||||
struct для getenv — оставлено как техдолг.
|
||||
|
||||
### 25a5. Блокеры (без изменений)
|
||||
- ph*.npz нет на этой машине; live-dump не работает (маск-цепь при загрузке,
|
||||
BLOCKMAP:285; wine DR EIO). Chain калибровка ждёт capture при INIT.
|
||||
- prd.md создан (карта проекта, 293 строки).
|
||||
- Structural baseline в README/prd: 2.689 (L/R), исторический 0.341
|
||||
недостижим на HEAD после M/S→L/R (e343b0a).
|
||||
|
||||
### Коммиты сессии
|
||||
```
|
||||
e343b0a prd.md + render48k L/R baseline
|
||||
9ed22d3 chain_9_19: IIR4 double + FIR, haar fix
|
||||
2a295af chain integrate RT_CASC gate
|
||||
96816f9 chain: IIR4 generator + audio path
|
||||
2201a57 docs: baseline 2.689
|
||||
f2cc0ae vlaw continuous per-fc + additive sens
|
||||
b41d7a4 chain: RT_IIR4_GEN wire + smoke tests
|
||||
197f5ed k_mapping_factor dedup
|
||||
e4c5348 audit fixes Phases 1+2
|
||||
2f854cd spectral vectors + vlaw_check target
|
||||
a881ee2 AGENTS.md: vlaw_check in test list
|
||||
c85b888 Δ-rule: distance-aware template-local gain (blocked on ph*.npz)
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# NOTES_LEVEL — оглавление журнала
|
||||
|
||||
> **Навигатор:** `NOTES_LEVEL.md` — живая голова (24mm5+, 2026-08-28, 389 строк). Архив 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).
|
||||
> **Навигатор:** `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`)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
| 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`) — по периодам
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
**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. Current best metric: **0.341 dB TOTAL** (structural 48k chain). Target: **<0.05 dB** (bit-exact gate).
|
||||
**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`.
|
||||
|
||||
@@ -155,10 +155,10 @@ Host 44.1k → resample → 48k → [per-channel processing] → resample → 44
|
||||
|
||||
| Path | File | Grid | Use |
|
||||
|------|------|------|-----|
|
||||
| Bridge | `framed_model.cpp` | 44.1k/2048 | Legacy, TOTAL 1.594 |
|
||||
| Structural | `render48k.cpp` | 48k/4096 | Canon, TOTAL 0.341 |
|
||||
| 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`
|
||||
**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
|
||||
|
||||
@@ -244,25 +244,28 @@ python3 scripts/corpus_structural.py --vs-bridge scripts/baseline_bridge.json
|
||||
|
||||
---
|
||||
|
||||
## 7. Current Status (2026-08-29, 24mm14)
|
||||
## 7. Current Status (2026-09-02, chain919)
|
||||
|
||||
| Group | Bridge | Structural (VLAW) |
|
||||
|-------|--------|-------------------|
|
||||
| t1kq (fc-scan) | 0.226 | 0.426–0.852 |
|
||||
| t1k (loud) | 1.801 | 0.577–0.930 |
|
||||
| al (level) | 0.638 | 0.090–0.804 |
|
||||
| res | 0.628 | 0.395 |
|
||||
| dual (q-sweep) | 0.726 | **0.193** ✓ |
|
||||
| comb (4-band) | 10.149 | 2.678–4.335 |
|
||||
| **TOTAL** | **1.594** | **0.341** |
|
||||
| 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): Dataflow decoded, bigkernel bodies known, I/O format unknown. Target: <0.05.
|
||||
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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user