Compare commits

...
10 Commits
Author SHA1 Message Date
Matiq 627c35dd53 AGENTS.md: document new env flags (RT_CASC/IIR4/KMAP/DELTA), chain wired status, capture proof
- Add RT_CASC, RT_IIR4_GEN, RT_IIR4_C/TAU/P/MULT/SR, RT_FIR, RT_FIR_Q,
  RT_KMAP_FC, RT_DELTA_DIST, RT_DELTA_STATE, RT_DBG_CASC to env flags table
- Update chain_9_19 section: WIRED+GATED (was CRITICAL)
- Add capture proof section: rendersnap2.py + /tmp/opencode/ datasets
- Remove outdated PTRACE live-dump hazards (superseded by /proc/pid/mem capture)
2026-09-03 14:11:19 +03:00
Matiq 3fe44255fa Δ-rule: distance-aware gain wired (default flat), ph*.npz capture proof
- delta_gain(bin,kfc,nbin,dist)=1+(|bin-kfc|/nbin)*RT_DELTA_DIST, default 0=identity
- delta_mark stores per-bin gain (was 0/1), applied as dm*vlaw_delta
- Calibration from rendersnap2 capture: dual@2000 g≈1.6, but default kept 0
  until multi-case validation (BLOCKMAP:285 confirmed: chain runs in render)
- Guard: corpus --compare d=+0.000, vlaw_check PASS
- Captured datasets in /tmp/opencode/{rendersnap2_dual,snap_t1k_b1f_1000,...}
2026-09-03 00:21:33 +03:00
Matiq a96e46bb69 NOTES_LEVEL: ph*.npz capture proof — rendersnap2 works, mask chain runs in render window 2026-09-03 00:04:23 +03:00
Matiq a562f6f56a NOTES_LEVEL 25b: Δ-rule wired, calibration blocked on ph*.npz 2026-09-02 23:51:04 +03:00
Matiq c85b888745 Δ-rule: wire distance-aware template-local gain (calibration blocked on ph*.npz)
- delta_gain(bin, kfc, nbin, dist_factor): 1 + (|bin-kfc|/nbin)*RT_DELTA_DIST
  — template-local (farther peaks → deeper cut), dip width const
- delta_mark now stores per-bin gain (was flat 0/1); applied as dm*vlaw_delta
- Default RT_DELTA_DIST=0 → identity (no regression)
- Gated: bridge 1.594, structural 2.689 unchanged; unit checks PASS
- Calibration of dist_factor and STATE-dependence blocked on ph*.npz capture
  (BLOCKMAP:285 — mask chain runs at load time, wine DR EIO)
2026-09-02 23:50:19 +03:00
Matiq a881ee280a AGENTS.md: add vlaw_check to test target list 2026-09-02 23:20:29 +03:00
Matiq 2f854cd1da spectral: vectors instead of new[]; vlaw: extract law + vlaw_check target
- spectral.cpp: window_/buf_/tmp_buf_/fir_buf_/fir_freq_ as std::vector (no
  exception-leak in ctor, destructor = default)
- framed_model.hpp: extract vlaw_cut/vlaw_mask inline (BLOCKMAP:314 softplus)
- dsp/vlaw_check.cpp: unit test for law (monotonic, zero-level, delta, ref,
  comb-neutral) — PASS
- CMake: add vlaw_check target
- Guard: corpus --compare d=+0.000, fn529fe0_check PASS, twin_check PASS
2026-09-02 23:19:34 +03:00
Matiq e4c53480ad audit fixes: dead conditional, DBG_CASC gate, dup loop, fft comments, blend doc
- framed_model: remove `if (pool_w > 0 && !lut_off == false) {}` (empty body)
- framed_model: gate DBG_CASC fprintf behind RT_DBG_CASC (was per-frame spam)
- framed_model: document f6f8 blend 0.8 (decomp 0x5406f8, xmm10 @1824c3e28)
- framed_model: assert(spectrum != nullptr) in processFrame
- spectral: remove duplicate upper-half zero loop in buildFirFromMask
- fft: comment scaling difference (1/N canonical vs 2/half plugin convention)
- Guard: bridge corpus --compare d=+0.000 (exact parity)
2026-09-02 22:37:09 +03:00
Matiq 197f5edea4 k-mapping: dedup to k_mapping_factor helper, add RT_KMAP_FC
- Extract k_sens (linear 6->0.44, exp 12->22.0) + k_q log-interp 0.5->1.0 2.0->0.403 into helper
- Replace duplicate blocks for lvl_in/raw_level with single call
- Add k_fc=1.0 + opt-in RT_KMAP_FC via W_eq (gated)
- Gates: bridge 1.594, structural 2.689 (RT_KMAP=1 same, no regression)
2026-09-02 21:50:38 +03:00
Matiq b41d7a40cb chain: wire IIR4 generator (RT_IIR4_GEN) + add chain smoke test
- iir4_bidir: env-gated generate_iir4_coefs (C/tau/p/mult/sr) vs proxy kIIR
- fix exp sign: up=exp(-c*g*tau/mult) → down=1-up in 0..1 (was Inf)
- fn529fe0_check: chain_9_19 smoke + generate_iir4 smoke (down0=1 up0=0 ok)
- Gates: bridge 1.594, structural 2.689 (RT_CASC=0) unchanged
2026-09-02 21:38:44 +03:00
12 changed files with 320 additions and 105 deletions
+14 -20
View File
@@ -33,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-резонатора
``` ```
@@ -86,6 +86,15 @@ err = db(ta(out,1000)/ta(ref,1000)) # err в dB, цель <0.1
| `RT_VDBG=1` | stderr-печать vlaw-вычислений | | `RT_VDBG=1` | stderr-печать vlaw-вычислений |
| `RT_FAITHFUL=1` | faithful-цепь `dsp/fnfaith.cpp` (детекторный каскад) | | `RT_FAITHFUL=1` | faithful-цепь `dsp/fnfaith.cpp` (детекторный каскад) |
| `RT_FIRCONV=1/3` | FIR-применение (1=complex-mul, 3=`1.019·mask^1.8345`) | | `RT_FIRCONV=1/3` | FIR-применение (1=complex-mul, 3=`1.019·mask^1.8345`) |
| `RT_CASC=1` | цепь 919 (`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`. Полный набор 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`). Датасеты: `/tmp/opencode/sc_{q,sens,qmap,k,f,d}*` + `tract_*` + `*.pkl/.npz` (см. `NOTES_LEVEL_INDEX.md`).
Открытые пробелы → `BITEXACT_PLAN.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 итераций):** **Статус:** 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).
- 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.
**Live-dump chain (ptrace):** chain вызывается в рантайме (DIV#0-7 на `0x1803a06a0`): **Live-dump chain (ptrace):** chain вызывается в рантайме (DIV#0-7 на `0x1803a06a0`):
- Input: `a` = bands_curve (VLAW output, min=0, max=17.6, mean=0.048) - 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) - `b` = tmp6f8 (step 9b accumulation, min=0, max=0.8, mean=0.8)
- 8 DIV hits на одном кадре (dual-band: 2 bands × 4 iterations?) - 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 устарел).
--- ---
+2
View File
@@ -42,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)
+2 -2
View File
@@ -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)
} }
} }
@@ -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++) { for (uint32_t i = 0; i < half; i++) {
z[i] *= 2.0 / half; z[i] *= 2.0 / half;
} }
+29 -6
View File
@@ -196,13 +196,35 @@ void cascade_detect(
static inline void iir4_bidir_340510(float* x, size_t nbin) { static inline void iir4_bidir_340510(float* x, size_t nbin) {
// BLOCKMAP:52af09 IIR4×2 bidir log-domain base 0x340510 // BLOCKMAP:52af09 IIR4×2 bidir log-domain base 0x340510
// Uses DOUBLE precision (movsd/mulsd in disasm) // Uses DOUBLE precision (movsd/mulsd/cvtpd2ps in disasm 1191-1201)
// Coefficients from FUN_180533340 generator (frequency-dependent warp) // When RT_IIR4_GEN=1, generate via FUN_180533340 (freq-warp g=fc_norm/i|pow), else use proxy kIIR_A1/B1
// For now, use kIIR_A1/B1 as proxy (structure is correct) 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_A1[]; extern const double kIIR_B1[];
extern const double kIIR_A2[]; extern const double kIIR_B2[]; extern const double kIIR_A2[]; extern const double kIIR_B2[];
const double* A1 = ::kIIR_A1; const double* B1 = ::kIIR_B1; A1 = ::kIIR_A1; B1 = ::kIIR_B1;
const double* A2 = ::kIIR_A2; const double* B2 = ::kIIR_B2; 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); } 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; acc = 0.0;
@@ -223,7 +245,8 @@ void generate_iir4_coefs(double* downCoef, double* upCoef,
for (int i = 1; i < n; i++) { for (int i = 1; i < n; i++) {
double g = (i <= fc_norm) ? (fc_norm / i) : std::pow(fc_norm / i, p); double g = (i <= fc_norm) ? (fc_norm / i) : std::pow(fc_norm / i, p);
double c = 1.0 / (g * tau / mult + 1.0); 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]; downCoef[i] = 1.0 - upCoef[i];
} }
} }
+20
View File
@@ -195,6 +195,26 @@ int main() {
if (!ok) fail = 1; 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;
} }
+55 -47
View File
@@ -8,6 +8,7 @@
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
#include <cassert>
namespace { namespace {
@@ -53,10 +54,45 @@ 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,
@@ -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); 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-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 // default OFF (canon), opt-in RT_KMAP=1 (helper k_mapping_factor)
// 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 = getenv("RT_KMAP") ? atoi(getenv("RT_KMAP")) : 0;
static const int kmap_on = []{ const char* e=getenv("RT_KMAP"); return e ? atoi(e) : 0; }();
if (kmap_on) { if (kmap_on) {
double k_sens; double k_tot = k_mapping_factor(band.fc, band.q, band.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;
if (k_tot > 1e-9) { if (k_tot > 1e-9) {
for (size_t k = 0; k < nbin; k++) lvl_in[k] = static_cast<float>(lvl_in[k] / k_tot); 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) { 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++) {
@@ -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); raw_level[k] = static_cast<float>(static_cast<double>(am[k]) / res_k * scale_factor_x);
} }
if (kmap_on) { if (kmap_on) {
double k_sens; double k_tot2 = k_mapping_factor(band.fc, band.q, band.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;
if (k_tot2 > 1e-9) for (size_t k = 0; k < nbin; k++) raw_level[k] = static_cast<float>(raw_level[k] / k_tot2); 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; 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); double kfc = static_cast<double>(band.fc) / (sample_rate / 2.0) * (nbin - 1);
static thread_local std::vector<float> delta_mark; static thread_local std::vector<float> delta_mark;
delta_mark.assign(nbin, 0.0f); 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++) { for (size_t k2 = 1; k2 + 1 < nbin; k2++) {
if (raw_level[k2] <= 0.25) continue; if (raw_level[k2] <= 0.25) continue;
if (std::fabs((double)k2 - kfc) <= 8.0) continue; if (std::fabs((double)k2 - kfc) <= 8.0) continue;
@@ -262,7 +265,8 @@ static void process_band_structural(
if (!lmax) continue; if (!lmax) continue;
for (int d = -3; d <= 3; d++) { for (int d = -3; d <= 3; d++) {
int kk = (int)k2 + 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) // VLAW parameters (configurable via env for per-group fitting)
@@ -326,10 +330,11 @@ static void process_band_structural(
if (delta_state) { if (delta_state) {
} }
for (size_t k2 = 0; k2 < nbin; k2++) { for (size_t k2 = 0; k2 < nbin; k2++) {
double cs = vlaw_alpha * std::log1p(static_cast<double>(raw_level[k2]) / vlaw_beta) float dm = delta_mark[k2]; // 0=no delta, >0=distance-aware gain (1.0=flat)
+ vlaw_c double delta_val = (dm > 0.5f) ? (dm * vlaw_delta) : 0.0;
+ (delta_mark[k2] ? vlaw_delta : 0.0); band_level[k2] = static_cast<float>(vlaw_mask(
band_level[k2] = static_cast<float>(std::pow(10.0, -cs / 20.0)); static_cast<double>(raw_level[k2]), vlaw_alpha, vlaw_beta,
vlaw_c, delta_val));
} }
frame_dbg_ctr++; frame_dbg_ctr++;
} else } else
@@ -406,6 +411,8 @@ 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;
} }
@@ -694,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) {
@@ -734,7 +742,7 @@ void FramedDetector::processFrame(const std::complex<double>* spectrum, float* m
// Cascade computes: |audio_spectrum × twin_response| → Haar smooth → sin-peak floor // Cascade computes: |audio_spectrum × twin_response| → Haar smooth → sin-peak floor
// Output replaces am/res in the structural chain. // Output replaces am/res in the structural chain.
static thread_local std::vector<float> casc_curve; 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) { if (casc_on && nfft_ == 4096 && twin_resp_complex_.size() > b) {
size_t nbin = half + 1; size_t nbin = half + 1;
std::vector<float> complex_input(2 * nbin); std::vector<float> complex_input(2 * nbin);
+12
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <cmath>
#include <complex> #include <complex>
#include <vector> #include <vector>
#include "fn529fe0.hpp" #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); 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.
+12 -21
View File
@@ -12,13 +12,13 @@
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_ = new std::complex<double>[nfft_]; fir_buf_.resize(nfft_);
fir_freq_ = new std::complex<double>[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);
@@ -30,13 +30,7 @@ SpectralProcessor::SpectralProcessor(size_t nfft, size_t hop, float sample_rate)
} }
} }
SpectralProcessor::~SpectralProcessor() { SpectralProcessor::~SpectralProcessor() = default;
delete[] window_;
delete[] buf_;
delete[] tmp_buf_;
delete[] fir_buf_;
delete[] fir_freq_;
}
void SpectralProcessor::setDetectorParams(const std::vector<DetectorBand>& bands) { void SpectralProcessor::setDetectorParams(const std::vector<DetectorBand>& bands) {
detector_.setParams(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) { 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 // 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++) { for (size_t i = half + 1; i < nfft; i++) {
time_domain[i] = 0.0; // xmm9 = 0 zeros upper half 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 // Step 4: opB = fwd-RFFT (th1a90): time_domain (real) → complex
std::vector<std::complex<double>> freq_domain(half + 1); 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++) { 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());
if (firconv == 3) { if (firconv == 3) {
// RT_FIRCONV=3 (NOTES 24k): plugin application law decoded live: // 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). // RT_FIRCONV=2: Full FIR construction pipeline (52b550-52b8bb).
// mask → reciprocal (1/mask) → window → normalize → complex multiply. // mask → reciprocal (1/mask) → window → normalize → complex multiply.
// This replicates the plugin's FFT-conv FIR design path. // 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 // Complex multiply FIR × audio spectrum
for (size_t i = 0; i < nfft_; i++) { for (size_t i = 0; i < nfft_; i++) {
buf_[i] *= fir_freq_[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
View File
@@ -21,12 +21,12 @@ 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::complex<double>* fir_buf_; std::vector<std::complex<double>> fir_buf_;
std::complex<double>* fir_freq_; std::vector<std::complex<double>> fir_freq_;
std::vector<double> fir_window_; std::vector<double> fir_window_;
std::vector<float> overlap_; std::vector<float> overlap_;
std::vector<float> mask_; std::vector<float> mask_;
+55
View File
@@ -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
View File
@@ -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).** > **Архив 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) (единственный навигатор). > Оглавление по датам/темам → [`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) - w ≈ 0.015 (scalar, НЕ 0.977)
- Каскад STATEFUL: bands_curve сохраняется между кадрами - Каскад STATEFUL: bands_curve сохраняется между кадрами
- 5407a8 = accumulator, НЕ нулевой при рекуррентности - 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(bandsACC)→FMA ATT/REL (half-split)→EXP#11→×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=1up`.
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=(fc800)/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)
```
+2 -1
View File
@@ -1,6 +1,6 @@
# NOTES_LEVEL — оглавление журнала # 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`) ## Живая голова (`handoff/NOTES_LEVEL.md`)
@@ -15,6 +15,7 @@
| 24mm12 | Детекторный каскад vt+0x28=180529c60 | Оркестратор 5300f0, vtable карта, 0x281 байт | | 24mm12 | Детекторный каскад vt+0x28=180529c60 | Оркестратор 5300f0, vtable карта, 0x281 байт |
| 24mm13 (+доп) | Хелперы каскада 529c60 | 5355d0→16140, 530080, 20f0/1850/1a00, рекуррентия Haar | | 24mm13 (+доп) | Хелперы каскада 529c60 | 5355d0→16140, 530080, 20f0/1850/1a00, рекуррентия Haar |
| 24mm14 | Каскад декодирован — 3 фазы | `|z|` → Haar×2 → peak/sin/w/blend 5407a8 | | 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`) — по периодам ## Архив (`handoff/archive/NOTES_LEVEL_2026-08-18_2026-08-23.md`) — по периодам