3641 lines
276 KiB
Markdown
3641 lines
276 KiB
Markdown
## ============ 2026-08-18 (LEVEL-PATH STRUCTURE DECODED) ============
|
||
|
||
## Две twin-цепочки (аудио vs маска-таблица)
|
||
(a) FUN_180536300 (float, N=0x200=512, per-channel AUDIO):
|
||
вызван FUN_18058e380: FUN_180536300(state+0x18, in+0x8e0+ch*0x800, in+0xe0, band, 0x200).
|
||
lVar1=scratch+N*32; normalize lVar1[i]=in[i]*2pi/(os*sr) (0x18052da00=pointwise scale);
|
||
per band: FUN_180536f90(lVar2, base, band_i, lVar1, N) -> copy lVar2->base(0x1800096c0);
|
||
base*=product (0x180008700); if band_active: save->lVar4, product=1.0; out=copy lVar4->param_2.
|
||
=> out = PROD_active_bands mask_band(норм. аудио). На |x|<1 z=ротор(1e-5)=i -> маска ~пост.
|
||
(b) FUN_18056e3e0 (double, N=0x400=1024): вызван FUN_180563440 (6 band-слотов). Аналогично.
|
||
Вход param_3 в контексте FUN_180563440 = LUT-рампа [0,1] (param_1+0x198, шаг 1/1023),
|
||
shape через кривую param_1+0x188 => это УРОВНЕВАЯ LUT-маска, не аудио.
|
||
|
||
## FUN_180563440 (per-block маска-таблица, 6 полос)
|
||
- param_1+0x198[i] = i*(1/1023), затем маппинг кривой param_1+0x188:
|
||
pfVar10[0]=min, [1]=max, [3]=gamma-кривая, [4]=флаг linear, [+0x14]=nodes.
|
||
linear: val = min+(max-min)*exp(log(x)/c) = min+(max-min)*x^(1/c) (gamma).
|
||
- 6x FUN_18056e3e0 -> 6 полосных масок (param_1+0x4198+0x2000k); комбайн -> param_1+0x2198;
|
||
затем LUT (param_1+0x98): пары (x=i*fVar3, y=mask) на 0x3ff точек (уровень->маска).
|
||
- вызывается FUN_1805631c0 (vtable 0x1824b14f0) + FUN_180563ce0/563fa0 (следующие шаги).
|
||
|
||
## FUN_180563ce0 — per-bin IIR level-трекеры (342 бина, 2nd-order state pairs)
|
||
- Константы 0x1824c4780/4788/4790/4798 (double A/B), 0x40400000=3.0f (порядок).
|
||
- Буферы: param_1+0x28 (float order), +0x40/+0x58 (double state A/B), stride 0x60, 0x156 итераций.
|
||
=> на каждый бин ставится 2nd-order IIR (атака/релиз) для сглаживания уровня.
|
||
|
||
## FUN_180530b60 — параметры сглаживания (log-interp + exp)
|
||
- cac=exp, cd0=log. freq-диапазон: online 800..1200 Гц (DAT_1824c4594=800/45a8=1200),
|
||
offline 15..180 Гц (4418=15, 4518=180), gain 0.01..1.0 (3c70=0.01, 3ea4=1.0).
|
||
- FUN_180533340(+0x2404e8/0x340500/0x440518, ...) — инициализация smoothing-фильтров
|
||
(3 экземпляра: 3 канала/oversample?).
|
||
|
||
## FUN_180530d30 — per-bin СПЕКТРАЛЬНЫЕ ВЕСА (0x5406b8/0x5406c8/0x5406d8/0x5406e8)
|
||
- fVar9 = cdc(2000/(sr*0.5)*513/(i+1)) = cdc(2000/f_bin) — pow? (IAT-стаб 0x181b3b378).
|
||
- fVar11 = [param_1+0x540880]*0.25*fVar9*fVar13 (0x540880/884 = per-band уровень/сенс).
|
||
- buf[i] = cd6(0.1, 1.0/(exp(1/(1+x))*dVar1)); buf_c[i]=1-buf[i]. cd6 = log10? (0x1824c3f70=0.1).
|
||
- dVar1 = (sr/[param_1+0x1a0])*[param_1+0x1ac]*0.001.
|
||
- => частотно-зависимый вес: 2000 Гц (f_bin больший) -> fVar9 другой, чем 500 Гц.
|
||
Кандидат объяснения red2000>red500 (dual_b1q): вес нормирует уровень бина выше на
|
||
высоких частотах -> избыток уровня больше -> глубже крас. ПОТРЕБУЕТ ВЕРИФИКАЦИЮ.
|
||
|
||
## IAT-хелперы (0x181a14xxx — jmp [GOT 0x181b3b3xx], цели 0x6fffff, динамич. загрузка)
|
||
- Последовательность стабов каждые ~6-12 байт => стандартные CRT math (в порядке импорта).
|
||
- Семантика по алгебре: cac=exp, cd0=log (log-interp в 530b60), cdc=pow (2-арг, pow(x,0.7/2.0)),
|
||
cd6=log10? (cd6(0.1, y)=log10(y)/log10(0.1) в 530d30), cd6 может быть log2.
|
||
- cf4/ce2/c9a/cc4/cfa — для case1/case8 генераторов (cfa в FUN_1805343e0: w=1/cfa(fc*pi/fs)
|
||
=> cfa=sin, cf4-цепочка для q-клипинга).
|
||
|
||
## КЛЮЧЕВОЙ ВЫВОД
|
||
- Частотная СЕЛЕКТИВНОСТЬ маски = freq-path нотч |2B/A| (m2c, центр=fc) на оси бинов
|
||
(t1kq fc-скан rmse=0.007).
|
||
- ГЛУБИНА/уровневая зависимость = level-path LUT (уровень->маска, кривая +0x188) +
|
||
per-bin IIR-трекеры уровня (0x563ce0) + спектр. веса (0x530d30, 2000/f_bin).
|
||
- dual_b1q (red2000>red500 при q<1) требует совместной модели level*веса*нотч —
|
||
единственный способ получить больше cut на 2000 чем на 500 при band=500.
|
||
|
||
## ============ UPDATE: CONSUMER OF PER-BIN WEIGHTS FOUND (FUN_180529fe0) ============
|
||
|
||
## FUN_180529fe0 — per-block СПЕКТРАЛЬНЫЙ ПРИМЕНИТЕЛЬ (5 каналов, ключевой DSP-цикл)
|
||
Сигнатура (param_1=DSP-объект, param_2, param_3, param_4=nBands). Для каждой полосы:
|
||
1. 0x540678[band] *= (fVar30/sr) * 0x540870 (нормализация уровня × sens 0x540870)
|
||
2. IIR-сглаживание спектра (5 каналов, состояния 0x540528/2c04f8/3404f8/4c0528/440510,
|
||
через DBL-петли: y += x*a + y*b, backward/forward проходы) => level-трекер per-bin
|
||
3. 0x5406f8 = резонансная реакция полосы (копия 0x540678[band], каскад 0x540688-коэфф,
|
||
scale fVar30/nBands) [здесь twin-результат 0x5406f8]
|
||
4. 0x5406f8 *= 0x540698 (частотная ось, log-интерп) и *= 0x5406a8 (axis)
|
||
5. *** ПРИМЕНЕНИЕ per-bin ВЕСОВ: ***
|
||
thunk_180003c40(lVar21=0x5406f8+N, 0x5406c8, 0x5407c8[band], iVar19, 4) -> 0x5407c8 += 0x5406c8*contrib
|
||
thunk_180003c40(lVar17=0x5406f8, 0x5406e8, 0x5407c8[band], iVar19) -> 0x5407c8 += 0x5406e8*contrib
|
||
(0x5406c8=1-0x5406b8, 0x5406e8=1-0x5406d8 — КОМПЛЕМЕНТАРНЫЕ веса)
|
||
6. накопление: thunk_180005a20(0x5407c8[band], 0x540678[band], iVar19)
|
||
7. dry/wet: 0x1c[band]*scale (0x5408b8==0: +0x540678*0x1c[band]*...)
|
||
8. FFT-свёртка в time-domain: FUN_180535a70(0x540628, 0x540678[band], nBins+1) -> frame;
|
||
FFT-таблицы 0x540548/550/598; 0x540668 -> out-буфер (0x3f800000=1.0 в DC-бин).
|
||
|
||
## FUN_180530d30 — точная формула per-bin весов (0x5406b8/6c8/6d8/6e8)
|
||
iVar6 = NFFT/2+1; fVar12 = 2000/(0x24*0.5) [p24=40000 -> fVar12=0.1]
|
||
fVar13 = 4.0 (или 1.0 если 0x5408b8); dVar1 = (0x24/sr)*0x1ac*0.001 [секунды]
|
||
for i in bins:
|
||
fVar9 = cdc( (fVar12*iVar6)/(i+1) ) = exp?/pow? (0.1*513=51.3 / (i+1))
|
||
fVar11 = 0x540880 * 0.25 * fVar9 * fVar13
|
||
dVar10 = 1/(1 + fVar11/(0x540880*4)) = 1/(1+fVar11/(4*level))
|
||
dVar10 = exp(dVar10 * fVar11)
|
||
dVar10 = cd6(0.1, 1.0/(dVar10*dVar1)) = log10(1/(dVar10*dVar1))/log10(0.1)
|
||
buf0x5406b8[i] = dVar10; buf0x5406c8[i] = 1-dVar10
|
||
(аналогично 0x540884 -> 0x5406d8/6e8)
|
||
Численно (p24=40000, sr=48000, 0x540880=1, 0x5408b8=0):
|
||
bin500: fVar9=exp(4.4)=81 -> w=2.49 ; комплемент 1-w=-1.49
|
||
bin2000: fVar9=exp(1.17)=3.2 -> w=1.60 ; комплемент = -0.60
|
||
=> частотно-зависимые (сильно убывающие с частотой) веса. НО q входит через
|
||
0x540880/884 (устанавливается из полосы) и через 0x5406f8-резонанс.
|
||
|
||
## BUFFERS (map окончательный)
|
||
0x540698 freq-ось (log-интерп, множится в band-ответ)
|
||
0x5406a8 axis-буфер (множится в band-ответ)
|
||
0x5406b8/6c8, 0x5406d8/6e8 per-bin level-веса (пары w, 1-w) — потребляются в FUN_180529fe0
|
||
0x540678[band] per-band спектр/вход (0x10-стрide, 2 слова = {ptr,count}? нет, 0x540678+0x10k)
|
||
0x5407c8[band] per-band маска-аккумулятор
|
||
0x540748 warp-ось = 8.3 - 7/(1+exp((x*20000-120)*(-0.01))) ~= 1.3 (DC-буст 6.68), НЕ f/(f+K)!
|
||
0x540628 FFT-work, 0x540548/550/598 FFT-таблицы, 0x540668 out, 0x5406f8 band-фильтр/рабочий
|
||
|
||
## FUN_180535ae0 = конструктор DSP-объекта: 0x540874=1.0, 0x54087c=0.5, 0x540884=1.0,
|
||
0x54088c=1.0, 0x540894=1.0, 0x24=0x472c4400=40000.0f (!), 0x5408ac=0x01000000.
|
||
|
||
## FUN_180535f10 = деструктор (free всех 0x5406xx-буферов).
|
||
|
||
## ИТОГ: dual_b1q объясняется комбинацией
|
||
mask(bin) = |резонанс_полосы|(bin) × freq_axis(bin) × level_weight(bin, 2000/f_bin)
|
||
где level_weight через exp(51.3/(bin+1)) даёт РАЗНЫЕ веса на 500 и 2000 Гц, и на 2000 Гц
|
||
маска глубже при низком Q (резонанс шире/мелкие). Верифицировать в standalone-харнессе.
|
||
|
||
## ============ UPDATE 2026-08-18: 0x530d30 weights DECODED + NUMERICALLY NEGATIVE ============
|
||
- Exact formula (decomp 22330, FUN_180530d30): per bin
|
||
base = (2000.0/(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, else 1.0]
|
||
q = 1/(1 + v/([0x540880]*4)) (second pair 0x540884: q2=1/(1+v2/[0x540884]))
|
||
dVar1= (sr/[0x1a0])*[0x1ac]*0.001
|
||
w = 0.1^(1/(max(q*v,floor)*dVar1)) [0x1824c3f70=0.1, 0x1824c3e30=0.001]
|
||
buf0x5406b8 = w ; buf0x5406c8 = 1-w (and 0x5406d8/6e8 with 0x540884)
|
||
- Constants locked: c3d3c=0.25, c4334=4.0, c3d8c=0.5, c45b4=2000.0, c41e0=2.0.
|
||
- NUMERIC RESULT: at any plausible runtime (sr=44100, NFFT=1024, L=0.05..1.0, hop 1024..4096,
|
||
n1ac 1..12) -> w(500)=w(1000)=w(2000) ~= 0 (=10^-20..10^-0.07); complements ~= 1.
|
||
=> the 0x530d30 level-weights DO NOT create the 2000-vs-500 tilt (ratio2000/500 ~1.0).
|
||
- CONCLUSION: dual_b1q red2000>red500 does NOT come from 0x530d30. Suspect = freq-axis warp
|
||
(0x540698 '8.3-7/(1+exp((x*20000-120)*(-0.01)))' ~1.3 DC boost) x resonance. The model's
|
||
empirical tilt {500:1.414,1000:1.454,2000:1.795} stays as the placeholder; replacing it
|
||
requires FUN_180529fe0 consumer step-5 algebra + runtime level values (Phase-4/5 dive).
|
||
|
||
## ============ UPDATE 2026-08-18 (CHECKPOINT): tilt != warp, level-path decoded ============
|
||
- FUN_180563ce0 = INIT of per-bin IIR level trackers (not update): 0x156=342 bins, order=3.0f (0x40400000),
|
||
coeff A/B mvaps 0x24c4780/4790, strided fill; 3rd pass fills 0x40e00000=7.0f (post-gain). FULL: /tmp/opencode/f_563ce0.dis.
|
||
- Level-path map: 0x563440 (LUT curve +0x188: [min,max,gamma,linear flag,nodes]; 6 bands via 0x56e3e0
|
||
double-twin N=1024 with LUT-ramp input; combine -> +0x2198; LUT pairs +0x98 at 0x3ff pts level->mask).
|
||
NOT in decomp_funs.txt; use /tmp/opencode/f_563440.dis + level_notes.md (LOCAL: /home/m/re-tools/handoff/).
|
||
- **DECISIVE NEGATIVE**: warp(2000)=0.773 cannot reach dual red2000=15.2dB — C=depth·warp·LUTmax=0.864·0.772·0.667
|
||
=0.445 < needed 0.826 (C=1-10^(-15.2/20)). warp is NOT the empirical tilt. The gap is closed by the blend
|
||
step (0x5407c8 += weights·res_upper/lower, freq-axis 0x540698) + FFT-conv 0x535a70 shaping.
|
||
- CHECKPOINT decision (user): STOP; next session = diagnostic bridge (two-term mask: warp·LUT + α·freq-axis_term,
|
||
fit vs measured curves) BEFORE transcribing FFT. do NOT curve-fit without the blend term.
|
||
|
||
## ============ UPDATE 2026-08-18 v2: BRIDGE DONE + FUN_180529fe0 full algebra ============
|
||
- **Полный декомп FUN_180529fe0: /tmp/consumers_out.txt:471-1277** (f529fe0.dis в /tmp обрезан на 52a813).
|
||
Поправки к прежним заметкам:
|
||
- 0x5408b0 = таблица ГРАНИЦ бинов (прелюдия: cvttss2si+imul+idiv, iVar20=band-start, iVar19=len), НЕ LUT.
|
||
- Уровневая маска резолвится ИНЛАЙН: fVar30=0x5408b0[idx]; fVar25=(float)FUN_181a14cdc()=pow (IAT→GOT
|
||
0x181bab378, CRT-цель вне дампа); пары `×(fVar30−fVar25)`/`+fVar25·` (dry/wet уровня).
|
||
- Цепочка per-band (по декомпу): 0x540678=res; res*=∏0x540688[k]; res*=(fVar30/nBands); *=fVar29·0x54088c
|
||
(fVar29=(fVar30/0x1a0)·0x540870); IIR 0x52d650 state 0x440518; двойное IIR-сглаживание (double,
|
||
states 0x3404f8/2c04f8 и 0x440510/3c0510); 0x5407c8+=0x5406c8·res_up + 0x5406e8·res_low + 0x540678
|
||
(003c40, kernel vfmadd213pd) — **АДДИТИВНЫЙ аккумулятор**; 0x540678 *= res(008700) и *= warp 0x5406a8;
|
||
offline: *=0x1c[band]·fVar27 (глубина, D=-0.5@0x24c4670); dry/wet: mask·fVar30·0x540888 + (fVar11−fVar30)
|
||
(fVar11=1.0 c3ea4) — **педастал**; FFT-conv 0x535a70 (fwd, window 0x540550/598, kill mirror,
|
||
inv, ×0x540658, fwd, inv, 0x540668[0]=1.0[1]=0, apply FIR к param_2[band]).
|
||
- Константы offline: c3ea4=1.0, c3e28=0.8, c4670=−0.5, c4674=−0.7, c4680=−1.0, c3c58=0.001.
|
||
- **BRIDGE RESULT (model_fir.py, rmse=0.236 dB)**: C(f)=1.221·LUT(log10(L0/res)) + 0.358·warp(f)^3.143.
|
||
res(500;fc500)=0.117 Q-независим -> C500-константа без tilt; xv(2000)=0.308..−0.656 (Q) -> C2000-и-гг-форма;
|
||
аддитивный терм warp^3.14 даёт +0.15 на 2000 и ≤0.02 на 1000 (иначе t1k рушится) — единственная точка,
|
||
совместимая с 36 замерами. Мультипликативный warp·LUT проваливается (>10 dB). warp^3.14≈π: кратный каскад?
|
||
Остаток 0.7 dB (Q=0.1) = LUT-колено 0.574 -> нужен FFT-уровень (0x535a70 + окно 0x540658 + freq-axis
|
||
0x540698), см. SESSION_HANDOFF §5-6.
|
||
|
||
## ============ UPDATE 2026-08-20h: P2 mask-accumulator combine kernels DECODED ============
|
||
Raw bytes from rt snap (objdump of image 0x180008d60/5a20/3c40). Implemented in dsp/levelpath.cpp.
|
||
|
||
### Exact CRT thunk semantics (per-bin double ops in FUN_180529fe0):
|
||
- **0x8d60 combine3(dst,a,b,n) = sub**: out[i] = a[i] − b[i] (vsubpd; dst is the 3rd pointer).
|
||
Per-band call: 0x5406f8[i] = 0x540678[i] − 0x5407c8[i]. (corrects NOTES:348 "combine" TBD)
|
||
- **0x5a20 acc_add(dst,src,n)**: dst[i] += src[i] (double; kernel 0x18001a5a0).
|
||
- **0x3c40 acc_fma(dst,a,b,n)**: dst[i] += a[i]·b[i] (double; vfmadd213pd).
|
||
- 0x11940 = copy/mirror (complex, 5th arg stride 0/4 → kernel 0x1a88300), 0x6e40 = dst=src·scalar,
|
||
0x15060 = buf+=scalar, 0x8700 = dst*=src (float, warp), 0x9be0 = buf=scalar·buf.
|
||
|
||
### Full mask-accumulator per band (0x5408b8==0):
|
||
1. 0x5406f8 = 0x540678 − 0x5407c8 (8d60 sub)
|
||
2. mirror 0x5406f8 halves (11940)
|
||
3. 0x5407c8 += 0x5406c8 · 0x5406f8_upper (3c40, stride4)
|
||
4. 0x5407c8 += 0x5406e8 · 0x5406f8_lower (3c40)
|
||
5. 0x5407c8 += 0x540678 (5a20)
|
||
|
||
## ============ UPDATE 2026-08-20g: P2.5 twin-mask factory + band LUT apply DECODED ============
|
||
Source: decomp_funs2.txt (FUN_18056e3e0:7988, FUN_180563a60:8975, FUN_180563440:7697) +
|
||
f_56e3e0.dis + f_563a60.dis. Implemented in dsp/levelpath.cpp (levelpath_check ALL OK).
|
||
|
||
### FUN_18056e3e0 = "twin-mask factory" is a CONSTANT FILL, NOT per-bin twin resonance:
|
||
s = DAT_1824c4248 / (double)((float)[ctx+0x240080] * [ctx+0x24])
|
||
= 2π / (count · SR) (0x24c4248 = 6.283185307 = 2π)
|
||
fills 0x400 bins of the mask (stride 0x2000/band) with s. The twin resonance shape enters
|
||
elsewhere (via the LUT curve FUN_180563440), NOT here. (corrects NOTES:547 "twin resonance".)
|
||
|
||
### FUN_180563a60 = band LUT apply (level -> gain), 6 bands × 0x400 bins:
|
||
level_dB = logf(mask[band][bin]) · 8.6859 (20·log10)
|
||
axis[bin] = bin · (1/1024) (0x24c3c50 = 0.0009765625, DIFFERENT from 1/1023)
|
||
t = clamp((dB − A)/(B − A), 0, 1) (BandConfig ctx+0x180: A=min,B=max,gamma,flag)
|
||
if gamma == 1.0: val = t
|
||
elif flag == 0 (linear): val = t^gamma (powf, DIRECT gamma)
|
||
else (power-law): val = 0.5·(1 + sign(2t−1)·|2t−1|^gamma)
|
||
axis[bin+1] = val (pairs level, gain)
|
||
NOTE: this is the INVERSE of FUN_180563440 (which uses x^(1/γ)); 563a60 uses t^γ.
|
||
|
||
### Constants locked (from rt snap rodata):
|
||
0x24c4248 = 2π (double), 0x24c4230 = 5 (double), 0x24c3c50 = 1/1024, 0x24c3c54 = 1/1023.
|
||
|
||
## ============ UPDATE 2026-08-19e: FUN_180529fe0 FULL DECOMP CONFIRMS MASK-CANON ============
|
||
Source: /tmp/consumers_out.txt:471-1277 (full Ghidra decomp). Validates + completes the
|
||
"CONFIRMED per-band pipeline" section below (lines 237-253). Notes:
|
||
|
||
### Mask chain (0x5408b8==0 mono path; 0x5408b8!=0 = dynamic/stereo-extra path)
|
||
Per band (band in [iVar20, lVar18)):
|
||
1. scale: 0x540678[band] *= (fVar30/nBands)·0x540870·0x54088c (0x9be0 = buf*scalar)
|
||
2. IIR smoother: FUN_18052d650(state 0x440518, out 0x5406f8, in 0x540678) then a 2nd IIR
|
||
(states 0x3404f8/0x2c04f8, count 0x2404e8) — double smoothing (att/release).
|
||
3. online blend (0x5408b8==0):
|
||
0x5406f8 = 0x540698·(1-mix), mix=0x54087c (0x6e40)
|
||
0x5406f8 += mix·0.8 (fVar25=0x24c3e28=0.8) (0x15060)
|
||
0x540678[band] = bigkernel(0x540678,0x5406f8) (0x26b820)
|
||
4. combine: 0x5407c8[band] = combine3(0x5407c8,0x540678,0x5406f8) (0x8d60)
|
||
+ mirror both halves (0x11940) + WEIGHT appends:
|
||
0x5407c8 += 0x5406c8·0x5406f8_upper (0x3c40, stride4)
|
||
0x5407c8 += 0x5406e8·0x5406f8_lower (0x3c40)
|
||
+ 0x5407c8 += 0x540678[band] (0x5a20)
|
||
5. (0x5408b8!=0 branch: freq-axis blend 0x6e40/0x15060 + 0x26b820 + 0x4f0400 weight)
|
||
6. WARP (tilt): 0x540678 *= 0x5406a8 (freqpath warp) (0x8700)
|
||
7. dry/wet: mask = mask·(fVar30·0x540888) + (1-fVar30) fVar30=0x540874−rnd (0x8800-ish)
|
||
8. FFT-conv (0x52b550 steps 1-6) -> FIR -> conv with audio -> out[band]
|
||
- 0x5407c8 / 0x540678 / 0x540768: pointer arrays stride 0x10 (per band), 0x5406c8/6e8 = weight tables.
|
||
- Constants: fVar11=1.0 (c3ea4), fVar25=0.8 (c3e28), fVar28=0x24c4674(-0.7), fVar27=0x24c4670(-0.5).
|
||
|
||
### Confirmed scalar A/R (0x540888/88c) = expf(p·0.11513), captured window tail degenerate flat.
|
||
=> Mask-canonic is now FULLY mapped structurally. Remaining for bit-exact = exact scalar values
|
||
(XMM9/XMM13, 0x540870/874/87c/88c from RPP) + the level-LUT curve A/B/γ (ctx+0x188 BandConfig).
|
||
Structural C++ port of steps 3-8 exists: dsp/fftconv.cpp (P1.3) + dsp/levelpath.cpp (LUT curve).
|
||
|
||
## ============ UPDATE 2026-08-19d: f_52d990 / f_52d920 / f_52db50 DECODED (CORRECTION) ============
|
||
Disasm: handoff/nls_dasm/f_52d990.dis (52 B), f_52d920.dis, f_52db50.dis; caller f_52b570.dis.
|
||
|
||
### Identity (they are CRT-style thunks, NOT arithmetic kernels):
|
||
- **f_52d990(rcx=dst, rdx=src, r8d=count) = memmove/memcpy thunk.**
|
||
Prologue saves rbx/rsi/rdi; movs args into callee regs; indirect call through GOT 0x1bab008
|
||
(the CRT "select scalar vs vector" resolver, same 0x2650db8 pair used everywhere); then tail-jumps
|
||
to 0x2000 (scalar path) or 0x1c40 (vector path). Returns void. This is a COPY, not a multiply.
|
||
- **f_52d920(rcx=dst, xmm1=val, r8d=count) = scalar fill (low-half filler).** tail 0x2030(float)/0x1d30(double).
|
||
- **f_52db50(rcx=dst, xmm1=val, r8d=count) = scalar fill variant.** tail 0x19a0/0x22d0.
|
||
=> The earlier NOTES "0x52d990 = FIR *= WINDOW" was WRONG. Corrected step map below.
|
||
|
||
### Corrected step 5 (f_52b570:52b75e-52b78c):
|
||
```
|
||
rax = [ctx+0x540534] ; N (FFT size)
|
||
rbp = N/2
|
||
FIR[N] = 0 ; zero the Nyquist bin
|
||
rax = [ctx+0x540658] ; window base (WIN_WINDOW, 8193 f32)
|
||
rdx = rax + N/2*4 ; src = window + N/2
|
||
call f_52d990(FIR, window+N/2, N/2) ; FIR[0..N/2-1] = window[N/2..N-1] (COPY)
|
||
call f_52db50(FIR+N/2, xmm9, N/2) ; FIR[N/2..N-1] = xmm9 (const fill)
|
||
inverse FFT
|
||
```
|
||
So step 5 replaces the FIR's lower half with the SECOND half of the captured window
|
||
(window saturates to 1.0 at idx 2049; so window[N/2:] is all 1.0 WHATEVER N/2>=2049 — the tail
|
||
copy degenerates to a flat 1.0 unless N/2 < 2049, i.e. FFT size would have to be < 4098 for the
|
||
window shape to matter here). Upper half filled with a constant xmm9.
|
||
|
||
### Corrected step 3 (f_52b570:52b685-52b6b3):
|
||
```
|
||
rax = N; rbp = N/2
|
||
FIR[N] = 0
|
||
call f_52d920(FIR+4, xmm13, N/2-1) ; FIR[1..N/2-1] = xmm13 (LOW fill)
|
||
call f_52db50(FIR+4+N/2, xmm9, N/2-1) ; FIR[N/2+1..N-1] = xmm9 (HIGH fill)
|
||
inverse FFT
|
||
```
|
||
|
||
### Size inference & effective semantics
|
||
- window = 8193 f32; it saturates to 1.0 at idx 2049 (w[2048]=0.8, plateau 1.0 beyond).
|
||
- freq-axis = 2048 f32, spacing 48000/4096 = 11.718 (matches 4096 FFT half). => N≈4096, N/2≈2048.
|
||
- Then step 5 copies window[2048:4096] = {0.8 -> 1.0 (mostly flat)} into FIR[0:2048],
|
||
upper FIR[2048:4096]=xmm9(=0?), FIR[4096]=0. Effective = gentle low-pass ramp, near-flat.
|
||
This matches NOTES line 416: captured window adds almost nothing over empirical (it's essentially 1.0).
|
||
|
||
### Remaining unknowns (now narrowed):
|
||
- xmm9 / xmm13 scalar values = still the FUN_180529fe0 prologue constants (need prologue read or live capture).
|
||
- window[N/2:] copy + xmm9 fill IS the whole "window" mechanism; the earlier "FIR *= WINDOW"
|
||
and "0x540658 is freq-shaped" speculation is replaced by: **FIR spectrum lower half = window tail,
|
||
upper half = const xmm9**. Window content already captured (rtwin_freq_44100.npy → WIN_WINDOW).
|
||
|
||
## ============ UPDATE 2026-08-18c: FFT-CONV LOOP FULLY MAPPED (Phase 5 step 3 partial) ============
|
||
Per-band FFT-conv loop disasm: /tmp/opencode/f_52b570.dis (0x52b550-0x52b8b5 == consumers :1195-1271).
|
||
|
||
### What 0x535a70 / helpers ARE (they're dispatchers, real kernels in base-CRT):
|
||
- 0x535a70 = FFT fwd dispatcher -> 0x140a10 (scalar) / 0x140a70 (vector). NOT hand-transcribable, unnecessary.
|
||
- 0x540548/0x540550/0x540598 = FFT-PLAN table ptrs (plan 0x540530; +0x18/+0x20/+0x68), NOT windows.
|
||
Plan built by FUN_18052dc30(param_1+0x540530, [0x64]=FFT size, 0x540868). 0x540534 = plan+0x4 = N/2 usage.
|
||
- Per-band ops (dispatch variants collapsed): iVar20=N/2; r14=FIR(0x540668);
|
||
1) 0x535a70(fftctx 0x540628, mask, N/2+1) forward FFT of mask
|
||
2) 0x2210(0x540628, 0, r14, N/2+1) inv FFT into FIR
|
||
3) fwd FFT (0x2180/0x1bb0) ; zero @[N] ; 0x52d920(r14+4, xmm13, N/2-1)=fill LOW half;
|
||
0x52db50(r14+4+N/2, xmm9, N/2-1)=fill HIGH half; inv FFT (0x1a90/0x19d0)
|
||
4) 0x140b30/0x140aa0(r14,r14,N/2+1) in-place complex op
|
||
5) fwd FFT ; zero @[N] ; 0x52d990(r14, 0x540658+N/2, N/2) = FIR *= WINDOW 0x540658;
|
||
0x52db50(r14+N/2, xmm9, N/2) = fill HIGH half; inv FFT
|
||
6) FIR[0]=1.0f, FIR[1]=0; if 0x540890!=0: complex ops (0x1880/0x1ca0) + FIR *= 0x540888 over 2*N;
|
||
copy FIR->out (0x1df0/0x1f70).
|
||
|
||
### Key unknowns (static analysis exhausted):
|
||
- **0x540658 window content**: ONLY 1 direct ref in whole dump (the read at 0x52b771). Not allocated in
|
||
consumers_out (:119-135 alloc 668/698/6a8/6b8/6c8/6d8/6e8/6f8/708/718/798), NOT set by 52dc30 (plan tables
|
||
only at +0x18/0x20/0x48/0x58/0x68). Written only indirectly -> needs runtime capture OR plan-internal
|
||
region. If 0x540658 is freq-shaped, it is the source of warp^3.14 (~π) & LUT-knee 0.574.
|
||
- xmm13/xmm9 fills = config constants from FUN_180529fe0 prologue (offline), unknown values.
|
||
- FFT size N = 2*0x540534; 0x1a0 = pow(2,floor(log2(SR/44100)))*412 (SR 44.1k -> 412); 0x540868=0x1a0*iVar9*8.
|
||
|
||
### Conclusions:
|
||
- freq-axis NOT the boost source (offline const). FFT tables NOT windows. The whole freq-shaping funnels
|
||
through 0x540658 window in step 5 + the offline fill constants. To resolve: runtime capture of 0x540658
|
||
(needs the plugin under a debugger) OR empirically back out its response from the model residual.
|
||
- Recommend: since bridge already at 0.236 and FFT window content is static-invisible, next high-value move =
|
||
full pipeline render (step 5) rather than more static FFT mining.
|
||
|
||
## ============ UPDATE 2026-08-18b: FREQ-AXIS 0x540698 BUILDER (decomp :21735-21757) ============
|
||
- Allocation+fill of freq-axis 0x540698 lives in the SAME function as the 0x540668 FIR window alloc
|
||
(decomp_funs.txt :21735-21757, the builder calls FUN_18052e190(param_1+0x540698, count=0x54086c)).
|
||
- OFFLINE branch (param_2=='\0'): the whole 0x540698 array is filled with ONE scalar
|
||
fVar29 = (DAT_18262b5c8 * LUT_BANDBOUNDS[uVar3+1] * DAT_18262b704 * LUT_BANDBOUNDS[uVar3] + fVar22)
|
||
* DAT_18262b700
|
||
(DAT_18262b5c8/DAT_18262b704/DAT_18262b700 = Soothe2ModuleBase statics, uVar3 = 0x5408b0 boundary index).
|
||
=> OFFLINE freq-axis is CONSTANT (per-band-band-width scalar), NOT the '8.3-7/(1+exp...)' formula.
|
||
The exp-formula suspicion was wrong for offline path. Any per-bin frequency dependence must enter via
|
||
WARP 0x5406a8 (freqpath.cpp, already transcribed) and the 0x540668 FIR (FFT-conv impulse).
|
||
- 0x540668 (FIR window, count 0x54086c*2) allocated right before; filled with 1.0f (0x3f800000) in offline
|
||
init at :21798-21804 (<=> flat impulse). So in offline the FFT-conv kernel starts as a pure del
|
||
a; the real kernel arrives from 0x535a70 processing (to transcribe next).
|
||
- 0x5406a0 = element count used by the fill loop (large, ~0x54086c). Buffer 0x5406b8..0x5406e8
|
||
(level-weights caps) allocated at :21753-21757 with the same count.
|
||
|
||
## ============ UPDATE 2026-08-18 (Phase 5 step 2): FUN_180529fe0 COMBINE ALGEBRA — FULL CHAIN DECODED ============
|
||
Source of truth: FULL decomp in /tmp/consumers_out.txt:471-1277 (the decomp_funs.txt copy at :11425 is
|
||
TRUNCATED — Ghidra dropped the middle because of the noreturn/dispatch thunks `(*DAT_181bab008)(&..,&..)`
|
||
guarding small-vs-vectorized variants; the consumers_out.txt copy came from a previous session's
|
||
getFunctionContaining(0x52ac64) and has the complete per-band loop + FFT-conv).
|
||
|
||
### Data-type map (from allocation sites, consumers_out :119-265)
|
||
- 0x5406f8 double[], size (0x540868)<<3 — resonance/freq-blend buffer (upper half = mirror)
|
||
- 0x5406a8 float[] size 0x54086c — WARP (the tilt!)
|
||
- 0x5406c8/6e8 float[] size 0x54086c — level-weights' complements (0x530d30 writer)
|
||
- 0x540678[band], 0x540768[band], 0x5407c8[band]: pointer arrays stride 0x10 (re/im or cur/prev)
|
||
- IIR states: 0x440518 (+0x4c0528), 0x3404f8, 0x3c0510/0x440510
|
||
|
||
### Thunk identity table (full decomp pairs; A = small stub, B = vectorized body)
|
||
- 009be0/00ffe0 : buf = scalar·buf (float; 004700-pair: double) BODY: mulss
|
||
- 008700/00fb60 : dst *= src (in-place, float, vmulss) ← WARP APPLY (dst=2nd arg)
|
||
- 011940/010860 : copy/mirror (double, complex stride via 5th arg==4; kernel 0x181a99c40)
|
||
- 006e40/004720 : dst = src·scalar (B double: vmovsd) ← freq-axis blend
|
||
- 015060/014c40 : buf += scalar (B double: vaddsd) ← dry offset
|
||
- 005a20/006840 : acc += src (kernel 0x18001a5a0, double)
|
||
- 003c40/00ee20 : acc += a·b (kernel 0x1800752e0: vfmadd213pd, DOUBLE) ← WEIGHT APPLY
|
||
- 008d60/00dc40 : 3-ptr combine, TBD (mask accumulate)
|
||
- 26b820/3a06a0 : big kernel (sets MXCSR; loads rodata 0x181f31680/34a80 tables) → likely exp/sat s.t. begin? TBD
|
||
- 2dc0e0/367980 : fills dst buffer with constant pattern 1.0/0.0 (vmovdqu rodata) — TBD (dry/wet?)
|
||
|
||
### CONFIRMED per-band pipeline (lines ref 638-1269) for the rendered (online) stage
|
||
per band (band in [iVar20, lVar18)):
|
||
1. scale: 0x540678[band] = ((fVar30/0x1a0)·0x540870 · 0x54088c) · 0x540678[band] (:656)
|
||
2. IIR: FUN_18052d650(state=0x440518, out=0x5406f8, in=0x540678[band]) → smoother (:668)
|
||
3. online: blend & weight into mask:
|
||
0x5406f8 = 0x540698·(1−mix) 006e40 mix=0x54087c (:814)
|
||
0x5406f8 += mix·0.8 015060 c3e28=0.8 (:823)
|
||
0x540678[band] = bigkernel(0x540678[band],0x5406f8) 26b820 (:832)
|
||
0x5407c8[band] = combine(0x5407c8[band],0x540678[band],0x5406f8) 00dc40 (:843)
|
||
mirror 0x5406f8 halves (011940, n and n+stride4) (:852 861)
|
||
WEIGHTS: 0x5407c8[band] += 0x5406c8·0x5406f8_upper 003c40(stride4) (:869)
|
||
0x5407c8[band] += 0x5406e8·0x5406f8_lower 003c40 (:877)
|
||
0x5407c8[band] += 0x540678[band] 005a20 (:885)
|
||
4. WARP (TILT): 0x540678[band] *= 0x5406a8 008700 (:938,:947)
|
||
5. dry/wet: 0x540678[band] = mask·(fVar30·0x540888) + (1−fVar30) fVar30=0x540874−rnd (:1184,:1193)
|
||
6. FFT-conv: FUN_180535a70(0x540628, mask, n+1) fwd-FT → window×0x540658 (52d990) → inv-FT
|
||
→ impulse 0x540668, DC=(1.0,0.0) (:1245) → ×0x540888 → conv(impulse, audio param_2) → out
|
||
|
||
### THE TILT (replaces empirical {500:1.414,1000:1.454,2000:1.795})
|
||
warp 0x5406a8 = 0.87·x/(1+x/K), K=exp(2.0723267)≈7.942, x = f_bin/2000 [c3e50=0.87, c4208, c45b4=2000]
|
||
warp(500)=0.211 warp(1000)=0.409 warp(2000)=0.773 (linear depth ratio 2000/500 = 3.67)
|
||
applied directly to the FLOAT mask before FFT-conv ⇒ enters the dB via the impulse response.
|
||
NOTE: 3.67 linear is ≫ the fitted tilt ratio (1.27) ⇒ the rest comes from the LUT-level flattening
|
||
(step 5 dry/wet + 0x540888) and the level sidechain 0x563xxx — that is the Phase-5 step-4 integration.
|
||
|
||
## ============ 2026-08-18e (STEP4: runtime-values, TIME-DOMAIN from renders) ============
|
||
|
||
## Атака уровня: fast (≤ ~1 STFT frame / ~20ms) — ИЗМЕРЕНО на рендерах
|
||
- Goertzel-огибающая 2000Hz по dual_b1q_*.wav (100ms/20ms окна): env ПЛОСКАЯ 0.18 (q0.1),
|
||
0.30 (q1), 0.32 (q10) уже на 0.2s; пик 0.208/0.344/0.364 (атака, ~первые 50ms меньше подавления).
|
||
- red(dB) по времени (вход dual 500+2000, канал 2000): t=0: 13.2 dB -> t=0.02s: 15.2 dB (steady).
|
||
=> reduction выходит на стационар к ~20ms. 0.540658-окно/level-smooth = быстрые → для parity
|
||
доминирует steady-state C (B.12, 0.268 dB); атака нужна лишь как короткий onset (~1 frame).
|
||
- Релиз НЕ наблюдается на tone->silence (out=G*in, in=0→out=0): для релиза нужен burst-тест с
|
||
second tone-активностью между бёрстами; dburst-огибающая зашумлена 50ms-окном поверх 100ms бёрстов.
|
||
|
||
## Runtime-константы (FUN_180537e90, quality-setter, decomp ≈:8534)
|
||
- 0x1ac = oversample-фактор ПО КАЧЕСТВУ: quality1->4, 2->8, 3->0x10(16), else->2.
|
||
- 0x1a4 = fVar6 / (float)0x1ac (см. строку 482: есть деление на 0x1ac => 0x1a4 = analysis-res".
|
||
- 0x1a0 = pow(2,floor(log2(SR/44100)))*412 (@44.1k = 412) — база cell-count геометрии.
|
||
|
||
## Коэф-таблицы IIR 0x24c4780/0x24c4790 (soothe_mem.bin offset 0x24c4780)
|
||
- floats: {1,0,0,0,-1,0,0,0,0,1,0,0,1,1,0,0} (A) / {-1,0,...} (B) — шаблон блочный, НЕ чистая
|
||
attack/decay константа; doubles: 0.0078125=1/128 на 4-й/6-й слотах. => отложить, не декодить.
|
||
- Более информативна ctor-инициализация 0x540880=0x540884=1.0f (movl 0x3f800000, disasm 0x535d19).
|
||
|
||
## ============ 2026-08-18f (STEP5b: FULL FRAME-RENDER PILOT - WORKS) ============
|
||
## framed_render.py: STFT(2048/512, sqrt-Hann) -> per-bin amp 2|X|/wsum (att 11ms/rel 80ms)
|
||
## -> B.12 mask C=g*LUT(xv)+w*warp^a, gain=1-C -> OLA. VALID: dynamics match, steady ~B.12.
|
||
## al_* DATASET (центр band fc=1000 sens12, tone1000, level-свип 0..-24dBFS):
|
||
## lvl 0,-3,-6,-9,-12,-18,-24 dBFS -> red -104,-6.1,-7.9,-9.7,-11.6,-15.4,-19.5 dB.
|
||
## xv=-0.269..0.931 (res_center=0.1171) => REAL LUT-нога КРУЧЕ frozen-узлов (cap 0.667):
|
||
## на xv=0.931 C->1 (клиф), у B.12 только -9.5. КОНФЛИКТ с t1k fc-scan (15.6@0dBFS там же)
|
||
## => премьюза fc-scan рендеров неверна (вход/настройки) - ПЕРЕСОГЛАСОВАТЬ.
|
||
## al_* = калибровочный датасет для замены LUT-узлов B.11 (structural 0.540658-piece).
|
||
|
||
## ============ 2026-08-18g (STEP5c: DATASET INCONSISTENCIES DECODED) ============
|
||
## КЛЮЧ: RPP несёт base64 XML "<SOOTHE2STATE><PARAM id= value=/>" — декодер:
|
||
## b64 = join всех строк-бейс64 (>40 chars) из .rpp; decode utf8; regex id/value.
|
||
## 1) t1k/t1kq _b1f_* = МУЛЬТИБАНД: band1 on sens=12 + band2 on sens=-12 (fc=1778.7, q=4.5)
|
||
## + band3/4 on (defaults) => НЕ single-band. B.12 фит впитал multi-band depth-split
|
||
## => LUT_obs у b1f сидит на ДРУГОЙ кривой (0.07-0.17 vs 0.38-0.63 у al_/dual).
|
||
## 2) t1kq_only1_* = НАСТОЯЩИЙ single-band fc-скан (band2 on=0). НО в RPP НЕТ 'depth' =>
|
||
## default depth (не 0.8639736) => нельзя мешать с al_/dual без нормализации depth.
|
||
## 3) tone_amp АБСОЛЮТНАЯ калибровка НЕСОСТОЯТЕЛЬНА (t1k: 1.058 vs true 0.215; t1kq: 0.284 vs 0.058).
|
||
## Для xv использовать FFT-амплитуду: seg=1..4s, Hann, A=4|X|/N (проверено: lv12 0.17759==peak).
|
||
## 4) ЧИСТЫЕ single-band датасеты (depth=0.8639736, band1 один):
|
||
## dual_b1q (22) + al_* (6) — СОВМЕСТИМЫ ~0.05 LUT_obs:
|
||
## dual@2000 xv -0.66..0.31 -> LUTobs 0.434..0.547 (гладкая S-кривая)
|
||
## dual@500 xv=0.574 -> 0.564
|
||
## al_* xv -0.42..0.63 -> 0.381..0.626 (круче внизу, ~0.05-0.08 выше dual@2000)
|
||
## Остаток dual@2000 vs al_ на низких xv => семантика уровня: per-band bandpass-энергия
|
||
## (включает ликование др. тонов) vs per-bin амплитуда — НЕ РЕШЕНО. Кандидат-тест Q=10
|
||
## (рес-широкая, ликование 500-тона в band1 при измерении 2000).
|
||
## 5) b2_* семейство (band2 fc=1778.7, sens=12): b2only_12, b2q_*, b2_b1s0/on, b1on12_b2on12
|
||
## (band1 500 + band2 1778, оба sens=12) — датасет multi-band depth-split для проверки.
|
||
## 6) ПЛАН (следующая сессия): (а) refit single-band LUT по dual_b1q+al_ (не b1f!);
|
||
## (б) уточнить depth-контекст t1kq_only1 перед микшированием; (в) решить семантику уровня
|
||
## через dual Q=10; (г) влить в framed_render.py.
|
||
|
||
## ============ 2026-08-18h (STEP5c: REFIT FAILS - LEVEL SEMANTICS UNRESOLVED) ============
|
||
## Совместный LUT-фит dual_b1q+al_ (9-узловой PCHIP + g,w,a):
|
||
## g=1.1895 w=0.3749 a=2.0 (на границе!) -> TOTAL 0.474 dB, dual 0.26, al 0.894.
|
||
## АЛ-точки не ложатся на dual-кривую: xv>0.3 al хочет +1.3..-1.0 dB сверху.
|
||
## КЛЮЧЕВОЙ ФАКТ (оба = тон в ЦЕНТРЕ полосы, res=0.117, depth/sens одинаковы):
|
||
## dual@500 (A=0.4397, вход=ДВА тона) xv=0.574 -> red 10.22
|
||
## al_3 (A=0.5005, вход=тон) xv=0.631 -> red 13.38 (ВЫШЕ при меньшем заxode-sharing)
|
||
## al_6 (A=0.3543) xv=0.481 -> red 12.21
|
||
## => при практически одном xv dual режет МЕНЬШЕ (10.2) чем al_4/5 (12.2-13.4).
|
||
## Больше энергии (dual=2 тона, A=0.44) -> МЕНЬШЕ reduction = ПРОТИВОРЕЧИТ al_-монотонности.
|
||
## Per-bin и per-band интерпретации обе проваливаются => механизм уровня (0x540678 per-bin
|
||
## IIR, точная схема отбора) НЕВОССТАНОВИМ статически из wav-пар. НУЖЕН runtime-захват:
|
||
## под отладчиком снять маску (0x5407c8/0x540658-окно) и per-bin уровень (0x540678) для
|
||
## dual_b1q_1.0 и al_6 при одинаковом цене -> дифферить механизм.
|
||
## Именно поэтому dB-потолок B.12-суррогата ~0.20-0.27 dB — линия DATA-уровня, не тюнинг.
|
||
## СЛЕДУЮЩИЙ ШАГ (рекомендация): runtime-захват уровня/маски под gdb (soothe2_x64.vst3), файл
|
||
## двойного стимула; после локализации механизма — закрыть LUT и 0.540658 эмпирически в C++.
|
||
|
||
## ============ 2026-08-18i (MULTI-BAND COMBINATION - MEASURED, MODEL FAILS) ============
|
||
## Эксперимент (вход tone1778 A=0.707, band2 fc=1778.7 = центр; depth=0.86397 mix=100):
|
||
## red@1778: b2only_12/b2_b1s0/b2_b1s0on = 16.55 (ВСЕ ОДИНАКОВО)
|
||
## b2_b3s12=16.62 b2_b4s12=16.51
|
||
## b1on12_b2on12 = b1s12_b2s12 = b1on12_b2s12 = 17.08 (идентичные)
|
||
## b1only_12 (band1 fc=500, band2 off) red@1778 = 11.47 (soothe режет ВНЕ полосы сильно!)
|
||
## ВЫВОДЫ:
|
||
## 1) depth НЕ делится по числу активных полос (b2only 1-полоса == b2_b1s0on 2-полосы 16.55).
|
||
## 2) Полоса sens=0 даёт НУЛЕВОЙ вклад (b2_b1s0on == b2_b1s0 == b2only).
|
||
## 3) Комбинация band1+band2: 16.55->17.08 (+0.53 над макс), НЕ product (дал бы ~27),
|
||
## НЕ additive-C (насыщает в +180). => общая маска строится из СУММЫ displacement по полосам
|
||
## (аккумулятор 0x5407c8 += ...) через НАСЫЩЕНИЕ. exp-насыщение(0.9) даёт 19.5 > 17.08
|
||
## => точная насыщающая кривая/уровень требуют runtime-захвата (0x5407c8/0x540658).
|
||
## 4) B.12 single-band band1@1778 предсказал 10.04 vs измеренных 11.47 (уровневая семантика
|
||
## опять +1.4 dB).
|
||
## => мультибанд: сумма displacements -> sat-кривая. Каждая полоса отдельно калибруется single-
|
||
## band рендерами; комбинация закрывается runtime или sat-подгонкой на b1on12_b2on12.
|
||
|
||
|
||
## ============ 2026-08-18h2: RUNTIME CAPTURE PARTIAL (base 0x180000000, 0x540658 = FIELD NOT RVA) ============
|
||
|
||
### Runtime capture infra (rtcapture2.py, WORKS):
|
||
- Launch: `reaper -nosplash -renderproject X.rpp`; host pid found by scanning /proc/*/maps for 'soothe2'
|
||
(lowest map addr with soothe2 in path = PE base). **vst3 loads AT ImageBase 0x180000000** (no reloc).
|
||
- Reading /proc/pid/mem (+0x180000000) works DURING render; process dies right after render finishes.
|
||
- Section table (Authoritative): .text rva 0x1000 raw 0x600 vsize 0x1a52000; IPPCODE 0x1a53000/0x1a51e00;
|
||
.rdata 0x1baa000/0x1ba8400 vsize 0xa60000; .data 0x260a000/0x2608200 vsize 0x72000; .pdata 0x267c000.
|
||
- FILE->RVA mapping: raw off F maps to rva = F - raw_sec + va_sec (e.g. .text: F=0x53F658<->rva=0x541058).
|
||
|
||
### CRITICAL: 0x540658 (and 0x540678/0x5407c8/0x5408b0/etc) are FIELD OFFSETS, NOT RVAs!
|
||
- Disasm at rva 0x52b771: `mov rax,[rdi+0x540658]` -> window read is [ctx+0x540658] (ctx = DSP object).
|
||
- Static soothe_mem.bin is RVA-linear; reading at 0x540658 gives .text CODE bytes (garbage floats).
|
||
- So the "window" (FIR*=WINDOW in FFT-conv step 5) lives in the HEAP DSP object at offset 0x540658,
|
||
NOT in module image. Runtime must locate `ctx` = base of DSP object.
|
||
- Beacon idea: phase_table_1024 (twiddle doubles 0x182615608 in .data) is the FFT-plan table;
|
||
scan host heap for u64==0x182615608, then the pointer owner is plan (+0x18/+0x20/+0x68 fields)
|
||
-> ctx = byte_addr_of_(pointer_field) - field_offset; window at ctx+0x540658.
|
||
(rtdeep2.py implements this; currently racy - host may exit before numpy import + first scan.)
|
||
|
||
### Notes for next session:
|
||
- Render lifetime is short (~2-19s); to win the race: import numpy BEFORE Popen, scan /proc instantly,
|
||
and re-run if host missed. Could also pre-load module into gdb for direct ctx inspection.
|
||
- Alternative: find ctx via 'consumers_out' alloc chain (note :119-135) if heap layout known.
|
||
- Verified render outputs: out_dual300.wav etc; run_sweep works; pkill -9 -x reaper hangs shell -
|
||
use `pkill -9 -f "reap[e]r"` style to avoid killing own bash.
|
||
|
||
|
||
## ============ 2026-08-18h3: LIVE GUI CAPTURE SUCCESS (pid 652462, SR=48000) ============
|
||
|
||
### Infra achieved:
|
||
- User ran REAPER GUI + soothe2 (yabridge-host.exe.so pid 652462), tone playing, band1 fc=500, Q=1, stereo balance.
|
||
- rtsnap.py: page-wise snapshot of all readable regions -> /tmp/rtA|B|C|D.{raw,idx} (~821MB each), skips EIO pages.
|
||
- Live process loads vst3 at ImageBase 0x180000000; `.data` shifted +0x1e00 vs static RVA.
|
||
- Diff A/B (Q turned) and C/D (idle 6s) both dominated by audio-buffer noise; direct ctx discovery by
|
||
(len,ptr)-registry + static curves instead.
|
||
|
||
### Registry of DSP buffers (found at 0x28b06c0, vector<{u64 count, u64 ptr}>):
|
||
- [0x00] count=8193 ptr=0x2c1a680 (all 1.0f) - identity/gain table
|
||
- [0x10] count=8193 ptr=0x1930100 **STATIC** 0.5->0.8 (freq-path window, monotonic)
|
||
- [0x20] count=8193 ptr=0x1938180 **STATIC** 0.0->3.899
|
||
- [0x30] count=8193 ptr=0x19401c0 **STATIC** 0.596->0.126
|
||
- [0x40] count=8193 ptr=0x29000c0 0.404->0.874 (dynamic)
|
||
- [0x50] count=8193 ptr=0x2908100 (dynamic)
|
||
- [0x70] count=8193 ptr=0x2c72800 (dynamic)
|
||
- [0xd0] count=2049 ptr=0x2e79040 freq-axis 0..23988.3 Hz (linear, spacing 11.71 = 48000/4096)
|
||
- [0xe0] count=8193 ptr=0x2e810c0 (dynamic)
|
||
- [0x110] count=8193 ptr=0x2c62740, [0x120] 0x2c12600, [0x130] 0x2c6a780, [0x140] 0x2eb1180 (zeros/ones)
|
||
- STEREO PAIR: 0x1930100 == 0x1a04240 (identical) => two copies (per-channel).
|
||
- "static" = identical bytes between snapshot C and D (idle) => candidate WINDOW tables.
|
||
|
||
### THE WINDOW (0x540658 area) - EXTRACTED:
|
||
- rwin_A0.npy (0x1930100): 2048 floats, 0.50000006 .. 0.79990, strictly monotonic, reaches 1.0 at idx 2049+
|
||
(saturates: plateau 1.0 after bin 2048). Shape = 0.5 + 0.3*g where g = K*x/(K+x), x=f/24000, K≈1.9
|
||
(fit rmse 0.0024; K sweep 1.9 best). freq-axis r_freqaxis.npy (2049 floats, 48000/4096 spacing).
|
||
- Interpretation: freq-path window = 0.5+0.3*warp(f); warp = K·x/(K+x), x = f/(SR/2).
|
||
NOTE: GUI SR=48000; offline renders SR=44100 (out_dual*.wav all 44100) - must renormalize x by actual Nyquist.
|
||
|
||
### Integration attempt (framed_render refit, G/W/p free, cases dual_b1q q=0.1/1/10 @500+2000):
|
||
- baseline warp^p: G=1.278 W=0.130 p=6.077 meanerr=0.323 dB (warp=(f/2000)^p)
|
||
- real window as warp term: G=1.199 W=1.328 meanerr=0.359 dB (NOT better)
|
||
- window*input-amp + warp^p: meanerr=0.470 dB (worse)
|
||
=> The real 0x540658 window does NOT beat empirical (f/2000)^p when used as the mask warp term.
|
||
BOTTLENECK remains the LUT-leg (level semantics), not the window. See al_* conflict in framed_render.py.
|
||
|
||
### Remaining hints for next session:
|
||
- The 8193-count tables vs 2049 freq-axis => N=4096 FFT at 48k (rfft bins 2049). Window arrays sized 8193
|
||
= complex bins? or 2*N? Actually 8193 = 4096*2+1 => likely full complex spectrum storage per channel.
|
||
- Re-running parity: use tone_cmp() (ndarray) not render_parity.tone_amp (file path) when testing live windows.
|
||
- For Q/depth level-path: the live diff method (snap A, change, snap B) is viable; noise is huge, use
|
||
(len,ptr) registry+static checks as anchors instead of raw byte diffs.
|
||
|
||
## ============ 2026-08-19: LUT-LEG CALIBRATED VIA al_* DATASET (JOINT FIT, DONE) ============
|
||
|
||
### What was done:
|
||
1. Decoded full al_* RPPs (binary b64 header): trim to len%4==0, find `<?xml`@92, regex PARAM; only band1
|
||
active (fc=1000, Q=0.9999978, sens=12, mode=1), depth=0.864, input = lvl_tone_lv{3,6,9,12,18,24}.wav.
|
||
Bug fixed: dual_b1q_*.rpp had 6 band entries (band0/2-5 off) - only band1 matters, model was right.
|
||
2. Measured steady reduction at 1000Hz (tone amplitude ratio m=amp_out/amp_in, sin/cos correlation metric):
|
||
lv3: m=0.2080 (-13.64) lv6: 0.2387 (-12.44) lv9: 0.2730 (-11.28) lv12: 0.3114 (-10.13)
|
||
lv18: 0.3996 (-7.97) lv24: 0.5006 (-6.01) -> reduction INCREASES with input level.
|
||
3. Pipeline-computed xv = log10(am_i / res_i) at bin=1000 (am=smoothed 2|X|/wsum, tatt=11ms/trel=80ms):
|
||
lv3:.5488 lv6:.3988 lv9:.2488 lv12:.0988 lv18:-.2012 lv24:-.5012.
|
||
NOTE: earlier al_* xv (0.631 etc.) were computed with different am normalization - ALWAYS use pipeline's.
|
||
4. Pure-LUT nodes at each xv: lut = ((1-m) - W*warp^A) / G (mask C = G*LUT + W*warp^A).
|
||
5. JOINT FIT (dual + al_*, objective=mean|err| over 6 dual tones + 6 al levels):
|
||
best: G=1.0850 W=0.2819 A=1.1377 -> al_* err <=0.19 dB ALL; dual err <=0.62 dB ALL.
|
||
The LUT leg is a SLANTED curve (~0.36 at xv=-0.5 rising to ~0.64 at xv=+0.55), NOT the flat B.12 (~0.5).
|
||
6. framed_render.py updated: LX/LY = merged anchors + al_* nodes; G/W/A = 1.0850/0.2819/1.1377.
|
||
CRITICAL BUGFIX: lut clip must be [LY.min(), LY.max()] (0.366..1.0) not LY[0] (0.4402) - node at
|
||
xv=-0.5012 (0.366) was being floored, breaking q10@2000 by 1.4 dB.
|
||
|
||
### Final validation (framed_render.py dual):
|
||
q0.1 @500 -0.55, @2000 +0.14; q1 @500 -0.62, @2000 -0.57; q10 @500 +0.01, @2000 -0.00
|
||
envRmse@steady: 0.64/0.04/0.71/0.78/0.11/0.11 dB. al_* lv3..24 err: +0.11~+0.19 / +0.01 / -0.02.
|
||
|
||
### Conflict (t1k fc-scan vs al_* clif at xv=0.93) - RESOLVED:
|
||
al_* "-104 dB @ lvl 0dBFS" comes from overdriven input (am >> any res), saturating mask to C->1.
|
||
t1k 15.6 dB @ xv=0.931 was a *different* level point (soothe's own fc-scan dataset). Both acceptable
|
||
once we fit AT THE MEASURED level points (which the joint fit does); no premise is wrong, input differs.
|
||
|
||
### Next: (1) hammer the residual -0.6 dB on dual 500Hz cases (structural); (2) multi-band combos (band>=2); (3) C++ port.
|
||
|
||
## ============ UPDATE 2026-08-19f: LEVEL-TRACKER UPDATE-LOOP LOCATED (structural) ============
|
||
The UPDATE loop is NOT a separate function — it is the inline bidirectional IIR smoothing
|
||
inside FUN_180529fe0 per-band loop (consumers_out.txt). Three first-order IIR stages:
|
||
- (a) FUN_18052d650(state 0x440518, out 0x5406f8, in 0x540678[band]) — outer smoother
|
||
- (b) inline: coeff-A array 0x4c0528, scalar-acc state 0x540528, mask src/dst 0x5406f8 (count iVar19)
|
||
- (c) inline: coeff-A array 0x3c0510 / 0x2c04f8, acc 0x440510 / 0x3404f8+0x2404e8 (two more)
|
||
Scalar form (proven, consumers 745-1015):
|
||
acc = 0; for i in 0..N-1: y = x[i]*A[i] + acc; acc = y; x[i] = (float)y
|
||
i.e. cumulative leaky-integrator over A[i] (running-acc coefficient). Vector variant does 4/iter.
|
||
=> The sample-path smoothing the tracker needs is STATICALLY known in structure; only the
|
||
coefficient arrays A (0x4c0528/0x3c0510/0x2c04f8) + the 0x530d30 per-bin level-weight caps
|
||
remain runtime values (0x530d30 shown numerically flat w~=0, NOT the tilt source).
|
||
Bit-exact port of the UPDATE requires A[] live capture (or FUN_18052d650 body) — next P1.5-ish.
|
||
Boundary: structural scaffolding now complete (levelpath + fftconv + mask-canon + tracker form);
|
||
all remaining unknowns are runtime scalar/array values, not structure.
|
||
|
||
## ============ 2026-08-19 continuation: LEVEL-PATH DECOMPILED — FUN_180563440 + FUN_180563ce0 ============
|
||
|
||
### FUN_180563440 (dsp/levelpath.cpp dump, f_563440.dis 222 lines) — LUT curve + twin-mask + combine
|
||
Three-phase per-frame structure:
|
||
1. **1024-bin LUT loop**: bin k → x = clamp(k/1024, 0, 1). Band config at context+0x188:
|
||
{A(+0x00), B(+0x04), threshold(+0x0c), flag(+0x10), callback(+0x50)}. Three paths:
|
||
- vtable callback (dynamic, when 0x50 non-null)
|
||
- **power-law**: centered=2x-1 → `sign()·10^(log10(|x|)/C)` — compression by sharpness
|
||
- **linear**: `(B-A)·x + A` — plain interpolation
|
||
Output stored DOUBLE at +0x198.
|
||
2. **Twin-mask factory** FUN_18056e3e0: 6 bands × 1024 bins, stride 0x2000.
|
||
3. **Combine**: max 2 channels (stereo), 6 bands, `1 - Σ weights`.
|
||
|
||
### FUN_180563ce0 (f_563ce0.dis 163 lines) — IIR level-tracker INIT (NOT update)
|
||
- 341 bins (0x155), **order-3 IIR** (3 coeffs/bin: 0x40400000 = 3.0 markers).
|
||
- Level coefficient: **0.1** (0x3dcccccd IEEE 754) — this is the attack/release α.
|
||
- State layout: rcx+0x28/+0x40/+0x58 (3 buffers). Init [1,0,0,0] / [-1,0,0,0].
|
||
- **UPDATE loop NOT found yet** — the actual sample-path smoothing is elsewhere.
|
||
|
||
### Key constants (all verified from soothing_mem.bin):
|
||
| Address | Value | Meaning |
|
||
|---|---|---|
|
||
| 0x24c3c54 | 0.0009775 | 1/1024 bin scale |
|
||
| 0x24c3ea4 | 1.0 | clamp max |
|
||
| 0x24c41e0 | 2.0 | power-law centering |
|
||
| 0x24c4680 | -1.0 | sign flip |
|
||
| 0x24c3d8c | 0.5 | threshold |
|
||
| 0x24c4334 | 4.0 | depth range (oversample os=4) |
|
||
| 0x24c43e0 | 8.6859 | 20/ln(10) dB conversion |
|
||
| 0x24c4704 | -6.9078 | ln(0.001) floor |
|
||
|
||
### CRITICAL: The "warp" is NOT a table — computed at runtime
|
||
- `rwin_A0.npy` (live 0.5→0.8) is the **frequency WINDOW** (per-bin mask shaping), NOT the warp.
|
||
- Empirically fitting `W·warp^A` was approximating the runtime LUT curve evaluation.
|
||
- The LUT is **parametric** (linear or power-law by band flag), not a fixed lookup.
|
||
- To reach bit-exact, replace PCHIP-fitted LUT with the parametric curve from FUN_180563440.
|
||
|
||
### Structural finding (per-bin gain from reference, avg over steady frames):
|
||
- Reference mask is FLAT ~-10.2 dB across 100-540 Hz REGARDLESS of Q (dual_b1q q0.1/1/10).
|
||
- Model produces res-shaped notch → source of the -0.6 dB residual at 500 Hz.
|
||
- **Hypothesis to test**: mask uses a SCALAR per-frame level (broadband), not per-bin am/res
|
||
(this matches the "twin-mask factory" combining band contributions). Test in /tmp/scalartest.py.
|
||
|
||
## ============ 2026-08-19h3: RES_POWER OPENING — 500Hz RESIDUAL SOLVED ============
|
||
|
||
### The fix: gain_k = (1-C) × res_k^rp (rp ≈ 0.05)
|
||
The reference applies the mask NOT as `(1-C)` directly to the per-bin gain, but as
|
||
`(1-C) * res^rp` — a small res-dependent correction. This flattens the per-bin gain
|
||
across the band center (where res=1 → correction=1, matching baseline) while dampening
|
||
off-center bins (res<1 → correction<1, reducing the notch).
|
||
|
||
### Validation (test_joint_rp2.py, multi-start Nelder-Mead, 5000 iter):
|
||
```
|
||
BEST: G=0.9696 W=0.3503 A=1.0887 rp=0.0516 mean=0.208 (dual only, 6 pts)
|
||
q0.1@500: err=+0.00 (was -0.55)
|
||
q0.1@2000: err=+0.00 (was +0.15)
|
||
q1@500: err=-0.05 (was -0.62)
|
||
q1@2000: err=-0.41 (was -0.57)
|
||
q10@500: err=+0.57 (was +0.01)
|
||
q10@2000: err=+0.22 (was -0.00)
|
||
```
|
||
The rp parameter SOLVES the 500Hz residual for q0.1 and q1 (the main bottleneck),
|
||
but slightly degrades q10 (the narrow-Q case).
|
||
|
||
### Physical interpretation:
|
||
In soothe2, the mask is computed in the RESONANCE-DOMAIN (xv = am/res), but the
|
||
applied gain has an additional res-dependency. This matches FUN_180563440's structure
|
||
where the LUT curve is evaluated per-bin (1024 bins) and the twin-mask factory
|
||
(FUN_18056e3e0) combines band contributions with a res-weighted path.
|
||
|
||
### Next steps:
|
||
1. Refine rp jointly with al_* (need faster al_* rendering — batch the 6 lv cases)
|
||
2. Update framed_render.py with rp parameter
|
||
3. C++ port of res_power term (trivial: multiply gain by pow(res, rp))
|
||
|
||
## ============ 2026-08-19 (Q-DEP RP + HONEST METRIC) ============
|
||
|
||
### CRITICAL: earlier "mean=0.160/0.144" were ARTIFACTS (untracked output length)
|
||
- Fit scripts (qdep_rp.py) rendered OUT of len nfr*HOP+N (untracked tail) and measured tone_cmp on last 0.75s of that extended buffer.
|
||
- framed_render.synthe trims to len(x). The two disagree by ~+0.2dB systematically.
|
||
- Honest (trimmed) numbers for scalar rp=0.0169 (h3): mean=0.280 max=0.618 (q10@500).
|
||
- Earlier claim "dual mean=0.160" does NOT reproduce on trimmed metric.
|
||
|
||
### Q-dependent rp: rp(Q) = rp0 * Q^drp (physical warp kept)
|
||
Fitted on TRIMMED metric (render_trim same as synthe):
|
||
G/W/A/rp0/drp = 0.9963/0.3335/0.9807/0.0275/0.2159
|
||
mean=0.175 max=0.707:
|
||
q0.1: 500 +0.000, 2000 +0.001
|
||
q1: 500 -0.256, 2000 -0.707
|
||
q10: 500 +0.086, 2000 +0.001
|
||
q0.1 and q10 both essentially perfect. Bottleneck is q1@2000 (over-reduction -0.71).
|
||
Weighted refits move error around but never reduce max < 0.7:
|
||
- w(q1@2000)=3, w(q10@500)=3: q0.1@2000 -> +0.885 (rows shift, no global win).
|
||
- free warp (K/alpha/p): mean=0.139 but q1@2000 -0.534 and warp non-physical (alpha=3.19).
|
||
|
||
### Committed to framed_render.py (Q-dep rp, trimmed honest baseline):
|
||
G/W/A = 0.9963/0.3335/0.9807, RES_POWER = lambda q: 0.0275*q**0.2159
|
||
|
||
### Interpretation
|
||
rp(Q) ~ Q^0.216 means the res-power correction grows with Q: high-Q resonance pit
|
||
dips harder off-center. Consistent with decomp (res-weighted gain path in twin-mask
|
||
factory). The q1@2000 residual is structural warp/model mismatch, not rp.
|
||
|
||
## ============ 2026-08-19: STATIC DECOMP LOCKED (all DSP bodies recovered) ============
|
||
New full decomp run (DumpFuns2.java, no haveDec gate, seeds=dsp+vtables+reverse-callers,
|
||
skip<8): decomp_funs2.txt (7746 bodies), fun_map2.txt (7928 funcs), consts2.txt (41622).
|
||
Previously-cold roots now present: 180529fe0 (2051in), 180563440, 180563ce0 (IIR INIT),
|
||
18056e3e0, 18052e9b0 (3167in), 18052e260, 18052e190/52e9b0 allocators.
|
||
|
||
### MAIN RENDER LOOP (found): FUN_18052e260
|
||
- Signature (param_1=DSP obj, param_2=nSamples). iVar5=NFFT/2+1; loop per band
|
||
(count=0x2404d0): FUN_180536300(param_1+0x3d8, band_in_ptr, 0x540758+bufoff, band, n).
|
||
Uses full-STFT frame budget 0x2404d8; tail marks bands needing rebuild (0x240467 flags).
|
||
- => 52e260 = per-frame dual-band SIDE-CHAIN driver; twin = per-band.
|
||
|
||
### FULL decomp FUN_18052e260 (decomp_funs2.txt:348915) — the frame driver:
|
||
```
|
||
iVar5 = NFFT/2+1; iVar2 = min(iVar5, [0x2404d8]); [0x2404d8] = iVar2; param_2=min(iVar2,param_2)
|
||
for band in 0..[0x2404d0]: FUN_180536300(ctx+0x3d8, band_in + (iVar5-iVar2)*4,
|
||
[0x540758] + (iVar5-iVar2)*4, band, param_2)
|
||
[0x2404d8] -= param_2
|
||
if [0x2404d8]<1 && [0x30]>0: fill(0x540788, 1.0, iVar5) // frame done -> reset dry/wet
|
||
```
|
||
So 52e260 only DRIVES the per-band twin side-chain (536300); the LUT (563440) +
|
||
mask-apply (529fe0) + FFT-conv (52b550) run elsewhere (triggered when [0x2404d8]
|
||
drains, i.e. per STFT frame). Full pipeline assembly (P4) = wire these in order:
|
||
twin(536300) -> level -> LUT(563440/56e3e0/563a60) -> mask(529fe0) -> FFT-conv(52b550).
|
||
|
||
### FUN_180536300 (twin caller, size=475) — audio path per band:
|
||
- lVar1 normalize = FUN_18052da00(scratch, in, scale_bin, n): pointwise in[i]*scale.
|
||
- band active (0x814): twin kernel FUN_180535880/180536f90(lVar2, base, band_i, lVar1, n),
|
||
else fill FUN_18052db50 (1.0 / 0.0).
|
||
- combine lVar4/lVar3 (52d990, 0x814>1), output copy 52dbc0(base, lVar4, n).
|
||
- TWIN KERNELS (535880/536f90): Ghidra emits ONLY the tail-call frame —
|
||
"WARNING: Removing unreachable block ..." => state shown = {pdVar1,*pdVar2} double pairs
|
||
read from param_3/param_4, then noreturn tail into thunk_FUN_181ba94b0. **The recursive
|
||
per-bin IIR level-smoothing (tatt/trel) lives INSIDE these two tail-calls, which static
|
||
decomp cannot recover** — same family as 0x540658 window (step-5 window). Confirmed
|
||
statically-invisible frontier = {532a715 twin kernels, 0x540658}.
|
||
|
||
### Level-param setters (vtable stubs, auto-named .?AV?$Soothe2Module@M$01@@::vtbl_...):
|
||
- 18052bba0: 0x540870 = expf((p*DAT_1824c4348 + DAT_1824c44a4)*DAT_1824c3cd4) [gain/sens]
|
||
- 18052bb80/bb60: 0x540878 / 0x54087c = raw int [freq?/bandwidth?]
|
||
- 18052bb40/bb20: 0x540880 / 0x540884 = raw int [per-band level / dur]
|
||
- 18052bb00: 0x540874 = p (locked); 18052bad0/baa0: 0x540888 / 0x54088c =
|
||
expf(p*DAT_1824c3cd4) [attack / release coeff db]
|
||
- DAT_1824c3cd4 = 0.115129255 = ln(10)/20 => expf(p*0.11513) = 10^(p/20): **all these
|
||
setters convert dB-speed params to linear coeffs**. 0x540888/88c feed FUN_180529fe0
|
||
step-5 dry/wet (mask peak) — the real attack/release coeffs.
|
||
- FUN_180530d30 (0x5406b8/6c8/6d8/6e8 weights): fVar9 = 2000.0/NFFT? base; w8=pow(base,0.25);
|
||
v = level*0.25*w8*factor(4.0); q=1/(1+v/(level*4)); dVar1=(sr/0x1a0)*0x1ac*0.001;
|
||
w = 0.1^(1/(max(q*v)*dVar1)); pairs (w,1-w).
|
||
- Numeric check (this session, real values): bin500 w~10^-6, bin2000 similar => 0x530d30
|
||
does NOT tilt 2000>500. Tilt stays = warp (0x5406a8, K=7.942) + twin-mask curving.
|
||
Static decomp now COMPLETE up to the statically-invisible frontier confirmed above.
|
||
|
||
## ============ 2026-08-19 (runtime capture SETTLED): registry heartbeat + live tables ============
|
||
## **Window 0x540658 + freq-axis CAPTURED live. Internal DSP sample rate = 48000 (not 44100).**
|
||
- rtsnap_fast.py: spawn reaper render_long offline -> find host (soothe2 in /proc maps, not reaper
|
||
cmd) -> sleep 6s -> snapshot ALL readable maps chunked 8MB -> /tmp/snap_all.bin (318MB, 546 regs,
|
||
entries {lo,sz,bytes}+pad, idx in /tmp/snap_all.idx). Chunked pread REQUIRED: whole-region pread EIO.
|
||
- **registry heartbeat**: heap run of {u64 count, u64 ptr} pairs (stride 0x10) at **0x29b06c0**
|
||
(arena anon 0x2922000, 0x77e000), 36 entries — analog of GUI note 0x28b06c0. Every DSP buffer
|
||
base is a registry entry: [00] identity, **[01] = WIN_freq window 0.5->1.0 (saturates)** (this is
|
||
live 0x540658 8193 f32: 0.5, idx512=0.345?; idx1024=0.68, idx2048=0.8, tail 1.0),
|
||
[02] 0->~0.01, [03]=rwin_C0 0.596->0.126, [04]=0.404->0.874 complement, [05]=0.0435->~0,
|
||
[06]=0.9565->~1.0, [07] zeros+neg, [0d]=**freq-axis 0..23988.3 spacing 11.713 = 48000/4096**,
|
||
[0e]=2.017->0, [0f] doubled, [10]=1.2914->0, FFT work bufs 131072/32768/65536/16384.
|
||
- **CRITICAL**: freq-axis spacing 11.713 Hz => internal SR = 48000 regardless of project 44100.
|
||
Registry[01] window & weights are therefore the 48k tables; rwin_C0[03] byte-matches GUI 44100 file
|
||
(same table reused). Saved live tables:
|
||
- handoff/rtfreqaxis_48000_internal.npy (2048 f32, 0..23988.287, spacing 11.713)
|
||
- handoff/rtwin_freq_44100.npy (8193 f32 WIN window 0.5->1.0) <-- THE 0x540658 live capture
|
||
- handoff/rtwa_596.npy, rtwb_404.npy, rtwc_043.npy, rtwd_956.npy (registry [03][04][05][06])
|
||
- vptr/44100-marker object-scan (rtobj/rtdump2/rtall) remains DEAD: no vtable 0x1824abb90 bank hit in
|
||
heap, fresh-render host has no 44100.0 constants (internal is 48k). The registry run is the beacon.
|
||
- twin IIR per-bin attack/release: NOT in registry (those are {att,rel} pair floats at 0x540888/88c,
|
||
expf(p*0.11513), per-note-band smoothing inside tail-call thunks). Live capture needs the twin band
|
||
object; not yet located. Registry gives per-bin WEIGHTS (done) — attack/release remain static-only.
|
||
- Registry dump + all raw tables saved: /tmp/rtcapt/registry.txt, /tmp/rtcapt/*.f32.
|
||
- Scripts captured into repo: rtsnap_fast.py (snapshot), findctx.py (locate), rtchunk.py (chunked IO).
|
||
|
||
## ============ 2026-08-19 (STRUCTURAL LUT CURVE FUN_180563440 — EXACT FORMULAS) ============
|
||
- Verified against f_563440.dis + PE constants 0x1824c3c54=0.0009775171(=1/1023? 1/0x3ff),
|
||
0x1824c3ea4=1.0, 0x1824c41e0=2.0, 0x1824c4680=-1.0, 0x1824c3d8c=0.5, 0x1824c4f10=0x7fff.. (double |x| mask).
|
||
- Loop: 0x400 iterations (0..0x3ff), x = i*(1/1023)? const 0.0009775171 = 1/1023 → last bin x≈1.0.
|
||
(prior note said 1/1024 scale — corrected: constant value is 1/1023.)
|
||
- Curve config at ctx+0x188: word A(+0x00)=min, B(+0x04)=max, gamma(+0x0c), flag(+0x10),
|
||
callback(+0x50). Output written as double to ctx+0x198[i*8] (0x400 doubles).
|
||
- **Linear path (flag 0x10==0, 0x563595)**: t = x; if gamma!=1.0 and x>0: t = exp(log(x)/gamma);
|
||
val = A + (B-A)*t. (A=min of output, B=max).
|
||
- **Power-law path (flag!=0, 0x5635cd)**: t = 2x-1 (centered -1..1); if gamma!=1.0:
|
||
t = sign(t) * exp(log(|t|)/gamma) (abs-mask 0x24c4f10, log DIVSS gamma, exp, sign via xmm11/xmm8);
|
||
val = A + (B-A)*0.5*(1+t). When gamma=1.0 → val = A + (B-A)*x (identity linear).
|
||
- This is the exact runtime curve to replace Pchip/LUT empirical (roadmap Q1). Need live (A,B,gamma)
|
||
per band config from ctx+0x188 — requires DSP ctx base (registry doesn't own the curve struct).
|
||
- Level-tracker IIR (FUN_180563ce0) INIT confirmed: 341 bins x2?, state rows at +0x28/+0x40/+0x58
|
||
init [1,0,0,0]/[-1,0,0,0], level coeff 0.1 (3dcccccd) = attack/release α; UPDATE loop remains
|
||
unmapped (field for future live capture; per registry tables stable between snapshots).
|
||
|
||
## ============ UPDATE 2026-08-20i: REAL MASK CHAIN IN C++ (P4) ============
|
||
Replaced empirical PCHIP detector with the live-calibrated mask chain:
|
||
|
||
### Structure (mirrors FUN_180529fe0 mono path):
|
||
level_k = am_k * res_b[k] * level_scale
|
||
track += w_k * (level - track) per-bin attack/release (rt_weights.hpp)
|
||
acc_k = (level - track) + level accumulator (live peak ~10.08 at tone)
|
||
mask_k = (1/(1+K*acc))^n K=9.8026, n=0.25966 (fit to live mask)
|
||
final = min over bands
|
||
|
||
### Live calibration zipped:
|
||
- live acc 0x5407c8 @1000Hz = 10.08 ; mask @1000 = 0.2976 (level_scale 1630 makes
|
||
am*res*1630*... hit acc ~78 at tone -> mask 0.178, matches ref -14.98dB).
|
||
- 678[0]=mask (0..1) is what we model as gain; warp/band-mult still missing.
|
||
|
||
### Results (framed_test, N=2048 hop=512, sqrt-Hann OLA):
|
||
- t1kq single band (678.76,q~1,sens12,scale1630): ref -14.98 dB, out -14.47 dB, err +0.51 dB
|
||
- comb 4-band (678.76/q1/s12,1778.7/q3/2.24,195.1/q3/2.24,13408/q3/s12): per-tone err
|
||
500:+5.8, 1000:+1.3, 1500:+1.8, 2000:-3.4, 3000:+5.6 dB. RMS err -16.8 dB (ref rms +4.3).
|
||
- Old PCHIP cut comb by ~6dB; new mask chain preserves comb (mostly ~1dB off).
|
||
|
||
### Remaining:
|
||
- warp tilt (0x5406a8) + per-band 0x540768 mult + FFT-conv smoothing (step 6-8 decomp)
|
||
- exact BandConfig (A/B/gamma) for band LUT 0x563a60
|
||
- rt_weights tables only cover bins 0..1024 (2049 len, rest zero); extend if higher bins matter
|
||
|
||
## ============ UPDATE 2026-08-20j: FUN_180529fe0 MONO-PATH FULLY DECODED + TABLES EXTRACTED ============
|
||
Read /tmp/consumers_out.txt:471-1277 (full decomp) + f529fe0.dis + live snap_rt.bin (ctx 0x2370040).
|
||
|
||
### Exact per-band mono-path (0x5408b8==0), FUN_180529fe0:
|
||
1. scale: 0x540678[band] *= (fVar30/0x1a0)·0x540870·0x54088c (0x9be0) [fVar30=PRNG, 0x540870=440.95]
|
||
2. IIR1: FUN_18052d650(state 0x440518, out 0x5406f8, in 0x540678) = leaky:
|
||
y[i] = A[i]·acc + B[i]·x[i]; acc=y; x[i]=(float)y
|
||
A@0x4c0528 B@0x440528 (2049 doubles), B=1−A, A ramps 0→0.692 (fast attack at low bins)
|
||
3. copy: 0x5406f8 <- 0x540678[band] (0x5160) — actually the IIR1 in/out feed; then
|
||
4. IIR2: inline on 0x540678[band]: A@0x2c04f8 B@0x2404f8 (A ramps 0→0.086, SLOW release)
|
||
5. mirror halves (0x11940)
|
||
6. blend: 0x5406f8 = 0x540698·(1−mix) + mix·0.8; mask[band] = bigkernel(mask,0x5406f8)
|
||
bigkernel 0x26b820 = VECTORIZED exp2 (tables log2e=1.4424@0x1f31740, floor −708.9,
|
||
mantissa 2^(k/256)@0x1f38c80) — computes mask[i]=exp2-based op on (mask,0x5406f8)
|
||
7. combine: 0x5407c8[band] = 0x540678 − 0x5406f8 (0x8d60 sub); mirror;
|
||
+= 0x5406c8·upper (0x3c40 stride4); += 0x5406e8·lower (0x3c40); += 0x540678 (0x5a20)
|
||
8. WARP: 0x540678 *= 0x540768[band] (0x8700); *= 0x5406a8 (0x8700) [TWO warp mults]
|
||
9. IIR3: inline on 0x540678[band] TWICE: A@0x3c0510 B@0x340510 (=A2/B2 shape)
|
||
10. dry/wet: 0x540678 = mask·(fVar30·0x540888) + (1−fVar30); fVar30=0x540874−rnd
|
||
11. FFT-conv (0x535a70) -> FIR -> conv(audio param_2)
|
||
|
||
### Runtime tables EXTRACTED -> dsp/rt_mask_tables.{hpp,cpp} (2049 doubles each, live):
|
||
- kIIR_A1 (0x4c0528): 0→0.692 rising; kIIR_B1 (0x440528) = 1−A1
|
||
- kIIR_A2/B2 (0x2c04f8/0x2404f8): A 0→0.086 (slow); kIIR_A3/B3 (0x3c0510/0x340510) = same
|
||
- kWarp (0x5406a8): 0→3.899 (freqpath tilt, 8193 f32)
|
||
- kBand768 (0x540768[band0]): 2.017→0.271 (per-band warp mult, 2049 f32)
|
||
- kPRNGLut (0x5408b0 -> 0x152a3d0): 512 f32 (0.35..6.66, 189 nz)
|
||
- PRNG state 0x2404e0 = 112; DAT_18262b5c8=0.4552, b704=0.6089, b700=0.6070 (int32->float)
|
||
- scalars: 0x540870=440.95, 0x540874=1.0, 0x54087c=1.0, 0x540888=1.0, 0x54088c=1.0
|
||
- 0x1a0=2048, 0x540868=16384, 0x54086c=8193, band count 0x2404d0=7, 0x30=2 (stereo)
|
||
|
||
### NOTE (SR mismatch): internal DSP SR=48000, N=4096 (bin spacing 11.713 Hz). Host refs 44100.
|
||
IIR tables indexed 0..2048 (N/2+1). For bit-exact the detector must run N=4096@48k internally.
|
||
|
||
## ============ UPDATE 2026-08-20k: bigkernel 0x26b820 = vectorized exp2 (semantics LOCKED) ============
|
||
Read the SIMD loop (objdump 0x18026b820-0x18026bcaf). Key line 0x18026bba0:
|
||
vmulpd ymm12 (exp2 result) by ymm11 (loaded from rbp = blend buffer 0x5406f8).
|
||
=> mask[i] = exp2(x) * blend[i], where x is the mask value (0x540678[band]) and
|
||
blend = freqaxis*(1-mix)+mix*0.8 (=0.8 at mix=1.0). sign: to get attenuation
|
||
(mask<1) x must be negative, so mask = exp2(-level) * blend. Locked.
|
||
exp2 is standard log2e/poly/mantissa-table (0x1f34a80..., log2e~1.4424, floor -708.9).
|
||
|
||
### Calibration (framed_test, structural chain now in framed_model.cpp):
|
||
- t1kq single band (678.76,q~1,sens12): level_scale=600 -> err -0.06 dB (ref -14.98).
|
||
- comb 4-band at scale 600: per-tone err 300:-5.1, 500:+7.8, 1000:+4.4, 1500:+3.9,
|
||
2000:-1.3, 3000:+4.8 dB (max 7.8). Mask shape (warp tilt + FFT-conv smoothing)
|
||
still approximated.
|
||
|
||
## ============ UPDATE 2026-08-20m: PRNG prologue transcribed (structure) ============
|
||
FUN_180529fe0 prologue (decomp :515-583) = PRNG coefficient generation feeding
|
||
scale fVar30 and dry/wet, via LCG + PRNG LUT (0x5408b0 -> 0x152a3d0, 512 f32, live).
|
||
|
||
LCG: state += (round offsets) & 0x8000007f with sign fixup. Constants:
|
||
DAT_18262b5c8=0.4552, DAT_18262b704=0.6089, DAT_18262b700=0.6070, DAT_1824c3c58=0.001
|
||
Round products fVar27*LUT[..]*fVar28*LUT[..]+0.001 are ~0.28 at PRNG state 112
|
||
(live), so (int) truncates to 0 => fVar30 can be 0..~ range frame-to-frame.
|
||
This is genuine per-frame RANDOMIZATION in the scale step (x *= (fVar30/0x1a0)*...).
|
||
structural model averages it via level_scale calibration; bit-exact requires the
|
||
exact LCG sequence per frame. scale already calibrated (t1kq -0.43 dB).
|
||
|
||
### Remaining bit-exact gaps (documented, next sessions):
|
||
1. exact LCG/LUT per-frame fVar30 (scale + dry/wet randomization)
|
||
2. FFT-conv (0x535a70) mask smoothing before FIR
|
||
3. bit-exact vectorized exp2 (0x26b820) vs std::exp2
|
||
4. combine/accumulator (0x5407c8) exact feedback (1500Hz comb err +6.4 dB)
|
||
5. internal SR=48000/N=4096 vs host 44100/N=2048 and twin per-bin IIR (statically
|
||
invisible, FUN_180535880 tail-calls)
|
||
|
||
## ============ UPDATE 2026-08-20n: PRNG fVar30 LOCKED + CONSTANT FIX ============
|
||
### CRITICAL CORRECTION: DAT_18262b5c8/b704/b700 == 1 (NOT 0.4552/0.6089/0.6070)
|
||
soothe_mem.bin is a VA-linear dump: file offset = RVA = VA - 0x180000000.
|
||
Earlier 0.4552/0.6089/0.6070 came from adding a spurious (+0x1e00-ish) section
|
||
adjustment that is NOT applicable. Verified at raw=RVA=0x262b5c8/b704/b700:
|
||
bytes 01 00 00 00 => int 1. Code does cvtdq2ps => (float)1 = 1.0. All three = 1.
|
||
Verified constants at raw=RVA: 0x24c3c58=0.001, 0x24c3e28=0.8, 0x24c4674=-0.7,
|
||
0x24c4670=-0.5 (match NOTES).
|
||
|
||
### PRNG prologue (FUN_180529fe0 :515-583) TRANSCRIBED to C++ (prng_fvar30):
|
||
LCG state 0x2404e0 (live=112), round offsets: +0x3cdca,+0x140236,+0x10d56,+0xdf6b6
|
||
fVar30(line554) = (int)(f32(LUT[s+1])*f32(LUT[s]) + 0.001f) [b5c8=b704=1]
|
||
=> at live state 112, fVar30 == 1.0 deterministically over 300 frames.
|
||
=> scale step: level *= (fVar30/0x1a0)*0x540870*0x54088c = /2048*440.95*1.0.
|
||
scale is NOT effectively randomized for this LUT/state.
|
||
dry/wet fVar30 (line1163) = 0x540874 - rnd, rnd from IAT stub 0x181a14cac
|
||
(jmp *0x181bab330, CRT import) - random dither, not statically lockable.
|
||
|
||
### framed_model.cpp: scale coeff now computed via prng_fvar30() (prng_state_=112),
|
||
not a hardcoded constant. t1kq still -0.43 dB at level_scale=600 (unchanged).
|
||
|
||
## ============ UPDATE 2026-08-20o: CRITICAL — level = am / res (not am * res) ============
|
||
Found + fixed a sign/direction bug in framed_model.cpp level computation.
|
||
|
||
res = |2B/A| (twin) is MINIMAL at band centre (0.117), NOT maximal. The model uses
|
||
xv = log10(A_k / res_k), i.e. LEVEL = am / res. My code had level = am * res which
|
||
inverted the fc-resonance: it cut MORE off-center and LESS at fc, producing a flat
|
||
inverted fc-response.
|
||
|
||
Fix: level = am_ / res_[b][k] (with 1e-12 floor). Now the fc-scan (tone1kq, band fc
|
||
swept 800..1200, refs t1kq_only1_<fc> = true single-band) tracks the reference shape
|
||
(deepest at fc==tone):
|
||
scale=42: fc900 +0.3, fc950 +1.3, fc1000 +0.9, fc1100 +0.2; mean|err| 2.46 dB,
|
||
max 5.3 dB (worst at edges fc800 +3.9, fc1200 +5.3 - ref cuts broader than model,
|
||
likely needs FFT-conv mask smoothing / wider effective notch).
|
||
Remaining under-cut off-center: real soothe reduces ~16-22 dB broadly across
|
||
800-1200 while model narrows; candidate = FFT-conv smoothing + warp + q shape.
|
||
DEFAULT level_scale for am/res path ~= 35-45 (was 600 for the wrong am*res path).
|
||
|
||
## ============ UPDATE 2026-08-20p: Phase B — framed_model RE-TRANSCRIBED ============
|
||
Re-transcribed dsp/framed_model.cpp per confirmed chain (NOTES:199-226, source
|
||
/tmp/consumers_out.txt:638-1111). Structural divergences from the old approximation:
|
||
- IIR1 (FUN_18052d650, state 0x440518) writes into the SHARED 0x5406f8 buffer,
|
||
then bridges into the band's 0x540678 (:731 0x5160); IIR2 (0x3404f8/0x2c04f8 on
|
||
the band mask) kept separate.
|
||
- mirror 0x11940: upper half = reversed lower (Hermitian), full-nfft scratch.
|
||
- blend step: f6f8 = 0x540698·(1−mix) + mix·0.8; mask = exp2(−mask)·f6f8. C_AXIS=1.3
|
||
placeholder (offline 0x540698 = per-band scalar, NOT the exp-formula; NOTES:354).
|
||
- combine (0x8d60/0x3c40/0x5a20): track = (mask−blend) + kRTAtt·blend +
|
||
kRTRel·blend + mask, using live weight tables kRTAtt/kRTRel (0x5406c8/6e8).
|
||
- warp (0x540768·0x5406a8), IIR3 (A3/B3)x2, dry/wet identity.
|
||
|
||
VALIDATION (scale sweep):
|
||
- DUAL (band fc=500, tones 500+2000 at −7.23 dBFS): ref @500 −53.7/@2000 −29.6.
|
||
model @500 matches over scale (s30 −51.5, s42 −58.1); @2000 stuck ~−4..−13 (way
|
||
short). The DAZING far-field reduction (reduce 2000 by −29.6 when band is at 500)
|
||
does NOT come from the exp2(−linear·level) chain — exp2(am/res) collapses to ~0
|
||
once res@2000 (1.344) ≫ res@500 (0.1175).
|
||
- T1KQ only1 fc1000: −21.1 vs ref −22.1 (OK). fc-scan shape intact.
|
||
=> Phase B confirms structurally: the missing stage for dual-far-field is the
|
||
dB-domain LUT FIX_180563a60 (level_dB = 20·log10(mask) → t=(dB−A)/(B−A) clamp →
|
||
t^γ), which COMPRESSES the exp2 collapse into a smooth log curve. NEXT = decode
|
||
BandConfig ctx+0x188 writers (A/B/gamma) + 0x540698 -> then wire into the chain.
|
||
|
||
## ============ UPDATE 2026-08-20q: METRIC FIX + LOG-DOMAIN LUT PORTED (honest baseline) ============
|
||
### CRITICAL METRIC BUG FOUND: 24-bit refs mis-decoded as 16-bit
|
||
Earlier dual
|
||
|
||
## ============ UPDATE 2026-08-20r: FULL-CORPUS HONEST ASSESSMENT (committed baseline) ============
|
||
Honest 24-bit metric across ALL datasets (input=3.5s window, correct sw decoding):
|
||
dual (band fc=500, q-sweep 11): mean|err| 0.41-0.61, max 0.72 ✓
|
||
al_* (fc=1000==tone, level sweep): lv3-12 err 0.05-0.43; lv18 -1.12, lv24 -1.80 (LOW-level over-cut)
|
||
res_only1 (resonant.wav, fc 300..700): -1.71..+0.81 (under-cut off-center, shape)
|
||
t1k_b1f (tone1k 0dBFS loud, fc sweep): +1.6..+2.12 (LOUD under-cut: LUT cap 0.667 too low)
|
||
t1kq_only1 (tone1kq, fc 800..1200): -0.59..+0.01 ✓
|
||
OVERALL mean|err|=0.77, max 2.12.
|
||
Systematics: Pchip LUT frozen (floor 0.366/cap 0.667 from al_* mid-level) under-predicts
|
||
BOTH the loud tail (t1k_b1f xv>0.8 needs LUT~0.70) and over-predicts low tail (al lv24
|
||
xv<-0.4 needs LUT~0.35). Real LUT extracted from refs: 0.351@-0.5, 0.60@0.07, 0.70@0.85.
|
||
Parametric FUN_180563a60 fit to extracted points: A=-13.78dB B=68.29dB gamma=0.344
|
||
(mean|err| 0.017 over extraction pts) but full-render VALIDATION regressed dual (real
|
||
multiband interplay absent) -> keep Pchip baseline committed; A/B/gamma = runtime
|
||
BandConfig ctx+0x188, NOT live-captured (SET TO: static path uses preset curve).
|
||
Structural next: decode BandConfig ctx+0x188 writers (FUN_1804835d0/487200 at
|
||
decomp_funs2.txt:21937/21962), wire combine 0x5407c8 + FFT-conv 0x535a70.
|
||
|
||
## ============ UPDATE 2026-08-20s: BROADBAND HYPOTHESIS TESTED (NEGATIVE) + LUT re-cut attempt ============
|
||
Tested the mask
|
||
|
||
## ============ UPDATE 2026-08-20s: BROADBAND HYPOTHESIS TESTED (NEGATIVE) + LUT re-cut attempt ============
|
||
Tested the "mask uses scalar broadband level" hypothesis (NOTES:637) directly: windowed
|
||
FFT of dual_b1q_1.0 ref vs dual.wav input (2.5-3.5s steady):
|
||
gain@500 = 0.309 (-10.2dB), gain@2000 = 0.292 (-10.7dB), gain@1000 (NO tone) = 0.995 (-0.05dB),
|
||
gain@1500/3000/5000 = 0.85/1.00/1.00.
|
||
=> REDUCTION IS PER-BIN: only bins *with a tone* are cut, empty bins pass through (~1.0).
|
||
So the level path is am_k/res_k (per-bin), NOT a scalar broadband level. Model structure correct.
|
||
LUT re-cut attempt (floor 0.35/cap 0.70 from t1k_b1f+al_ extraction, monotone 16 knots):
|
||
REGRESSED whole corpus (mean|err| 0.77 -> 1.09, dual/t1kq/local worse). Reverted to the
|
||
joint-fit 12-knot Pchip (dual<=0.72, fc-scan<=0.59, corpus mean 0.77, max 2.12) = KEEP.
|
||
Root cause of t1k_b1f residual (+2.1 loud) and al low-level (-1.8): NOT LUT cap shape alone;
|
||
al/t1k use DIFFERENT input (0dBFS vs -18dBFS) and the am normalization (pipeline am vs
|
||
tone_amp metric) differs; the joint-fit nodes were fit AT the al_* pipeline xv, so they
|
||
don't extrapolate cleanly to t1k_b1f's xv~0.85. Structural A/B/gamma (BandConfig ctx+0x188)
|
||
would give the *curve*, but writers are JUCE param plumbing (FUN_1804835d0/487200 = UI curve
|
||
setters, not DSP scalar setters) - static decode exhausted for now.
|
||
NEXT: wire combine/accumulator 0x5407c8 + FFT-conv 0x535a70 into the chain (structural, may
|
||
close the t1k_b1f/al tail by smoothing the mask) - todo #3.
|
||
|
||
## ============ UPDATE 2026-08-20t: FIT EXPLORATION (todo #3 diagnostics) ============
|
||
Tracked what DOESN'T move the needle (all reverted, committed 12-knot Pchip + G/W/A/rp
|
||
= 0.9963/0.3335/0.9807/0.0275/0.2159 remains optimal, corpus mean|err| 0.774 max 2.12):
|
||
1. sqrt-Hann STFT analysis window (matching framed_render canon) REGRESSED to 0.788 max
|
||
2.91 -> kept Hann-Hann (the C++ full-render metric is authoritative, not the python
|
||
mask-level canon).
|
||
2. Runtime (env) full-corpus fit of G/W/A/rp via framed_test renderer - mask-level
|
||
optimizer said G=0.877/W=0.402/A=0.673/rp0=0.0041/drp=0.28 (mean 0.66 mask-level) but
|
||
FULL RENDER regressed to 0.812 max 2.37 -> reverted. Mask-level objectives do NOT
|
||
transfer to the OLA render (mask applied per-bin interacts with window vs tone_amp
|
||
metric); the render metric is the ONLY honest one.
|
||
3. Residuals are level-dependent (t1k_b1f loud +0.8..2.9, al low -1.1..-1.8, everything
|
||
else <=0.9): the empirical LUT is optimal AT the fit levels; structural A/B/gamma
|
||
(FUN_180563a60 band curve) is the missing level-dependence.
|
||
TODO todo#3 remains: structural combine/acc 0x5407c8 + FFT-conv 0x535a70 wiring (NOTES:
|
||
FFT-conv window ~flat, likely marginal; combine adds band-weight interplay).
|
||
|
||
### TWIN RES vs PY CANON (todo#3 check)
|
||
C++ twin |2B/A| (fs=44100,N=2048, sens_stored=24.65, built via build_twin_coeff/twin_apply)
|
||
vs Python bandres (GAIN=4.132=10^(24.65/40), render_parity): match within ~1.5% at fc
|
||
300..2000 (fc300: 1.1798 vs 1.1887; fc1000: 0.11746 vs 0.11714; fc2000: 0.7057 vs 0.6963).
|
||
=> res shape is NOT the source of the res_only1 fc=300 residual (-1.71 over-cut); that
|
||
residual is level-LUT related (input resonant.wav level differs from fit levels).
|
||
|
||
## ============ UPDATE 2026-08-20u: F0 GATE — DETERMINISM VERIFIED (bit-exact REACHABLE) ============
|
||
Re-rendered test RPPs twice each (reaper -nosplash -renderproject on /tmp copies with
|
||
RENDER_FILE -> /tmp), then byte-compared data chunks:
|
||
|
||
1. res_only1_500.rpp (4s):
|
||
- a vs b (two fresh renders): data chunk IDENTICAL (max|dx|=0 over 352800 samples).
|
||
- a vs committed res_only1_500.wav: data chunk IDENTICAL.
|
||
2. t1kq_only1_1000.rpp (6s, 1587600 data bytes):
|
||
- a vs b: IDENTICAL. a vs committed t1kq_only1_1000.wav: IDENTICAL.
|
||
|
||
Whole-file cmp differs ONLY in the bext chunk timestamp (offset ~381: "02-08-2020")
|
||
= Reaper render-metadata, NOT audio. Dither/PRNG does not corrupt the rendered signal
|
||
(dry/wet rnd in FUN_180529fe0:1163 evidently locked/stabilized in offline render).
|
||
|
||
CONCLUSION: soothe2 output is byte-deterministic and our Reaper/yabridge CLI re-renders
|
||
reproduce the committed references EXACTLY. => Bit-exact (bytes) is REACHABLE; the gate
|
||
that could have made it impossible (per-run randomization) is closed. The remaining work
|
||
to bytes-parity is purely: exact structural chain + exact kernels + internal 48000/4096
|
||
geometry + live A/B/gamma, NOT fighting host nondeterminism.
|
||
|
||
## ============ UPDATE 2026-08-20v: EXISTING PARAMETRIC LUT SETS BENCHED (F1 closure) ============
|
||
All previously-derived parametric-LUT sets (from repo scripts + NOTES) tested on the
|
||
FULL honest 24-bit corpus (59 cases) inside ONE fixed bridge pipeline
|
||
(C = G*LUT(xv)+W*warp^A, gain=(1-C)*res^rp, committed G/W/A/rp). Result:
|
||
|
||
LUT form mean|err| max
|
||
------------------ -------- -----
|
||
pchip (committed) 0.773 3.426 <-- BEST (unchanged)
|
||
linear (0.483/0.717) 1.017 3.116
|
||
realpts (lutpts.npy) 1.134 3.192
|
||
u563a60 (A=-13.78/B=68.29/g=0.344) 1.669 6.004
|
||
lut5 (0.4356/0.5914/1.999/0.9364) 1.785 3.951
|
||
lut4 (fit_lut) 1.785 3.951
|
||
powerlaw(C=8) 6.809 11.880
|
||
|
||
=> The empirical Pchip curve remains the best form-factor; NO existing parametric set
|
||
beats it at fixed bridge params. Realistic conclusion: the recovered runtime curve
|
||
(NOTES:967) was fit to extracted (xv,C) points, but those points themselves came from
|
||
the SAME bridge-shaped model (LUT=xv->C), so parameteric fits overfit their source
|
||
and lose on the honest render metric. Pchip baseline (0.773) stays canonical.
|
||
F1 (params of the LUT curve) is hereby CLOSED as "already produced, none accepted";
|
||
the structural A/B/gamma from ctx+0x188 remains the only path to bytes (blocked:
|
||
level-path object not live-captured; see F2/F5.)
|
||
|
||
## ============ UPDATE 2026-08-20w: F2 STRUCTURAL WIRING TESTS — BOTH REGRESS (documented) ============
|
||
Tested wiring the two remaining structural chain pieces onto the committed bridge
|
||
(quick python precompute+OLA on a representative subset; honest 24-bit tone metric):
|
||
base : mean|err| 0.75-0.80, max 2.1
|
||
FFT-conv step5 (mask *= win8193 0.5->1.0) : mean 5.22 (worse) — masking the gain by
|
||
the freq window is WRONG direction; the 0x540658 window enters the FIR step, not a
|
||
simple gain multiply (confirms NOTES "window ~flat, adds almost nothing").
|
||
combine 0x5407c8 accumulator (acc += att*df+rel*df+g, df=g-acc, per-bin kRTAtt/kRTRel):
|
||
mean 4.68 (worse) — naive acc toward 2.36*g overd red by ~6x; the decomp combine
|
||
operates in the REDUCTION/exp2 domain (blend 0.8 pedestal + bigkernel exp2), NOT on
|
||
the fitted bridge final-gain. Mismatched domain => not a valid wiring test.
|
||
CONCLUSION: F2 "wire combine+FFT-conv into the bridge" is rejected in naive form. The
|
||
structural pieces belong to the exp2(mask) chain which regressed (Phase B, da63adc
|
||
replaced it with the log-domain LUT bridge). The bridge (Pchip, 0.773) remains canonical.
|
||
Path to bytes stays: exact A/B/gamma (blocked live), SR geom 48000/4096, bit-exact
|
||
exp2/FFT. No code change made this session; NOTE-only.
|
||
|
||
## ============ UPDATE 2026-08-20x: F2/F4 structural geometry test — NOT the gap ============
|
||
Tested internal-geometry hypothesis (internal DSP SR=48000/N=4096 vs host 44100/2048):
|
||
render the bridge at 48000/4096 with scipy resample_poly in/out to 44100:
|
||
host44100/2048 : mean|err| 0.683 (subset)
|
||
internal48000/4096: mean|err| 1.004 (worse)
|
||
=> geometry/SR-mismatch is NOT the source of the level-dependent residuals.
|
||
ALL structural adaptations tried and rejected (doc'd): parametric LUT sets (6), FFT-conv
|
||
window (step5), combine 0x5407c8 accumulator, internal 48000/4096 geometry. The bridge
|
||
(Pchip, full-corpus mean 0.774) remains canonical; empirical ceiling.
|
||
Remaining real paths to bytes: (F1) live capture of level-path object (A/B/gamma) OR
|
||
(F5) bit-exact exp2/FFT kernels — neither changes the mean but both are required for
|
||
byte parity. No code change; NOTE-only.
|
||
|
||
## ============ UPDATE 2026-08-20y: F5b — exp2 0x26b820 (assets + numeric fallback) ============
|
||
- 0x26b820 = SCALAR double exp2 (vmulsd/vaddsd family; vmovups wrapper 0x26c220 =
|
||
32-at-a-time bigkernel with MXCSR/fnstcw control). Table-driven fdlibm-style,
|
||
Cody-Waite hi/lo: 8x16 irr tables + 0x181f31940.. series (-708.449.. step, companion
|
||
-1.684e-9) + kExp2_f2f4e0.. (log2e~1.4428 head) + vfmadd213sd poly chain.
|
||
- EXTRACTED bit-exact P3 assets: dsp/exp2_tables.{hpp,cpp} (8x16 doubles straight from
|
||
soothe_mem.bin file=RVA; lead-in series kExp2_big). NOT yet wired 1:1 (algorithm body
|
||
has many special branches: subnormal/overflow/precision split).
|
||
- exp2_dsp() (dsp/exp2.cpp): numerically correct double exp2 (=std::exp2) for wiring;
|
||
RELIABLY NOT bit-exact to plugin (documented). exp2_check: 2e6 dense grid + edges,
|
||
PASS vs std::exp2 (0 cells >1e-13) -> ensures fallback correctness only.
|
||
Bit-exact exp2 transcription stays P3 (same bucket as DSP-FFT butterfly wiring).
|
||
|
||
## ============ UPDATE 2026-08-20z: F5c — DSP-FFT 0x140a70 status (P3 confirmed) ============
|
||
Full dsp/ check run (twin/tables/fftconv/vlog/leveltrack/levelpath/exp2): ALL PASS.
|
||
FFT status: dsp/fft.cpp = numerical radix-2 (std::cos twiddle) used by the STFT
|
||
framework — produces correct transform, but NOT bit-exact to the plugin DSP-FFT
|
||
0x140a70. P3 bit-exact DSP-FFT plan: twiddle source (DAT_182616800 sin-table, sin(k*2pi/
|
||
1024), loader FUN_180039b00 stride 2^(10-m)) + split-radix 2/4/8 in-place butterflies
|
||
(FUN_18000bfc0/18000c5e0 with 0x8440 elementwise mul + FUN_1800437c0 acc) + plan-gen
|
||
giant (FUN_18002f980 recursion + per-log2 factor tables) remain unwired. This is the
|
||
multi-week gap (roadmap P3), required only for byte-parity of the FFT-conv path; the
|
||
bridge renderer does not depend on it.
|
||
|
||
## ============ UPDATE 2026-08-21: FIX — restore Pchip LUT (revert 12094f8) + comb diagnosis ============
|
||
### FIX (commit e5e9700): revert parametric LUT regression.
|
||
12094f8 replaced the empirical Pchip LUT (framed_model) with the parametric linear
|
||
form (CAP_A_LEVEL=-24/B=28/gamma=1). With gamma=1 that reduces to IDENTITY on
|
||
xv=log10(am/res), breaking the whole mask chain: t1kq fc-scan err jumped +7.9 dB.
|
||
Re-instated lut_pchip: t1kq mean|err| 0.226 (was 7.9). Confirms NOTES:1060 F1 closure
|
||
(no parametric set beats Pchip at fixed bridge params). lut_parametric helper + CAP_
|
||
consts remain in framed_model.hpp as live-capture reference (currently unused).
|
||
|
||
### 24-bit metric loader CHECK
|
||
render_parity.py:43 already uses correct 24->64-bit sign extension (x>=0x800000 =>
|
||
x-0x1000000), NOT the buggy OR-0xFF000000. Repo metric harness is correct; an earlier
|
||
symptom of ~50 dB phantom ref levels was an artifact of a scratch loader, not the repo.
|
||
|
||
### COMB DIAGNOSIS (biggest open gap, mean 10 dB / max 14.7)
|
||
comb.wav tones are at ~-25.6 dBFS (500/1000/1500/2000/3000). comb_ref reduce = +0.02 dB
|
||
= soothe2 lets ALL tones pass ~unity on this input. Our model over-cuts -7..-15 dB.
|
||
=> NOT a multiband-combine problem; it is LEVEL-dependent: at input ~-25.6 dBFS with
|
||
band sens 2.24 for two of four bands, real reducer output is ~neutral, ours still cuts.
|
||
Likely the bridge LUT/level_scale is calibrated on the al_* fit levels, not on this
|
||
low/wide band config. Same class as the known low-level over-cut (al lv18/24, NOTES:964).
|
||
Next: calibrate level_scale per input level / verify sens enters the level path correctly.
|
||
|
||
## ============ UPDATE 2026-08-21: COMB PARAMS VERIFIED — two SEPARATE phenomena ============
|
||
Decoded true comb wav params from comb_all.rpp (real 4-band config, NOT what tests assumed):
|
||
band1 fc=1000 q=1.0 sens=+12 mode 1
|
||
band2 fc=1778.7 q=4.5 sens=-12 mode 1 <-- NEGATIVE sens
|
||
band3 fc=195.1 q=0.494 sens=+2.24 mode 0
|
||
band4 fc=13408 q=1.0 sens=+12 mode 3
|
||
depth=0.864, mode=1, selectivity=10, sharpness=10, resolution=1
|
||
Earlier comb tests fed wrong params (band1=678.76, band2/3 sens=+2.24, band4 q3) — those
|
||
were NOT the reference config. Even with corrected fc/q/sens the model still over-cuts -7..-15 dB.
|
||
|
||
### Two SEPARATE problems (verified):
|
||
1. band1-ONLY (fc=1000 q1 s12, input comb.wav -25.6 dBFS): real soothe2 cuts tones ~5.9 dB
|
||
@1000 (comb_b1_1000 real ref, NOT neutral); our model cuts -8..-15 (out-real = -2.8..-7.7).
|
||
=> BASELINE depth is too big at low level (level-scale/LUT low-tail over-cut).
|
||
2. comb_ALL (=comb_ref≈comb_byp, reduce ~0 dB on all tones): real soothe2 returns to NEUTRAL
|
||
despite band1 active. => MULTIBAND interplay (combine/acc 0x5407c8) must cancel band1's cut,
|
||
consistent with M11 additive red=base+ΣW·H and band2 sens=-12 acting as a positive adder.
|
||
This is the combine gap (NOTES:1071 F2), and it needs negative-sens + per-band mode handling.
|
||
|
||
### Model gaps surfaced:
|
||
- negative sens (band2=-12) not modeled -> band2 treated as +suppressor instead of detractor.
|
||
- multiband min() (not additive combine) -> cannot reproduce the all-band cancellation to ~0.
|
||
- per-band mode 0/3 (peak vs shelf/tilt band types, M10) not dispatched.
|
||
No code change; investigation only. These require the structural combine/acc + per-band type
|
||
before 4-band comb can close.
|
||
|
||
## ============ UPDATE 2026-08-21: multi-band parser fix + combine/sens experiments ============
|
||
### infra FIX: framed_test comma form only parsed argv[3] -> multiband comb tests actually
|
||
ran a SINGLE band (first comma-arg ignored the rest). Now parses every comma-arg from
|
||
argv[3..]. With correct 4-band params and faithful min(), honest comb = mean 10.15 (over-cut
|
||
-7..-15 dB). Single-band corpus unchanged (t1kq 0.226, dual 0.726) => parser fix is safe.
|
||
|
||
### combine/sens experiments (reverted, doc only)
|
||
Tried closing comb via additive dB-sum (red=sum_i red_i) and signed sens-weight
|
||
S=sign(sens)*min(1,|sens|/12) applied to C. Neither reproduces comb_ref neutrality (~0 dB):
|
||
min() comb mean 10.15 (baseline)
|
||
additive comb mean 15.68 (worse: all bands cut, no compensation)
|
||
additive+S comb mean 15.68 (neg-sens band2 at fc=1778 doesn't counter band1 @1000)
|
||
Real soothe2 comb is neutral DESPITE band1 (fc=1000 sens+12) active. band2 sens=-12 at
|
||
fc=1778 has res~1.9 on the 500..3000 tones, so its naive compensation is tiny vs band1's
|
||
cut. => Naive additive/sens formulas CANNOT model the negative-sens band interaction; it
|
||
lives in the structural combine/acc 0x5407c8 (per-bin weights kRTAtt/kRTRel + negative
|
||
sens entering the level path with opposite sign), which requires the real exp2-domain
|
||
chain, not the bridge. comb stays OPEN (structural combine/acc, P4/P5).
|
||
|
||
## ============ UPDATE 2026-08-21: LUT moved to LEVEL domain (structural chain) ============
|
||
### Discovery: mask-domain LUT clamps on loud input
|
||
- Mask-domain LUT (committed 9b7e9d3) computes t=(mask_dB-A)/(B-A); for loud input
|
||
(tone1k 0dBFS) post-exp2 mask_dB < A=-13.78 => t clamps to 0 => val=0 => FULL cut.
|
||
Measured t1k err -24.04 dB. Root cause: exp2(-level) output dB range is unbounded
|
||
below, LUT domain [A,B] assumes bounded input.
|
||
- FIX: apply FUN_180563a60 power-law to LEVEL (am/res*scale) BEFORE IIR/exp2:
|
||
lvl = pow(t, gamma)*MULT, t=(20*log10(lvl)-A)/(B-A) clamp [0,1].
|
||
Level dB stays inside [A,B] for both quiet (t1kq: +6.2dB) and loud (t1k: +24dB).
|
||
Result: t1k err -24.04 -> +2.45 dB.
|
||
|
||
### TOOLING HAZARD: stale-binary sweeps
|
||
- cmake --build skips recompile when source mtime unchanged within same second;
|
||
param sweeps silently re-run the SAME binary (9-point sweep -> 1 unique result).
|
||
Earlier "gamma doesn't matter" observation was this artifact, NOT DSP behavior.
|
||
- Protocol now: os.utime(src) before build + assert binary mtime fresh (sweep_fresh.py).
|
||
|
||
### Fresh 2D sweep (g x mult, verified builds), 3-case smoke (render48k 48k/4096):
|
||
g=0.344 m=3.8 optimum: t1kq=-0.788 t1k=+2.453 al12=+0.037 mean|err|=1.093
|
||
(committed version was mean 8.2). gamma>=0.9 ALL worse (+2..+5): spatial IIR
|
||
after LUT redistributes peaks; naive ratio math (need g=1.17 from raw t ratio
|
||
1.744->2.0) does NOT survive the IIR chain. Empirics rule.
|
||
- MULT=3.8 is a PLACEHOLDER (maps t^gamma back to level domain; no decomp basis yet).
|
||
A/B/gamma are decomp-extracted (BandConfig ctx+0x188 refs, NOTES:967).
|
||
|
||
### Scope clarification
|
||
- scripts/corpus.py exercises the BRIDGE path only (44.1k non-internal grid):
|
||
full corpus 1.594 mean / comb 10.15 — UNCHANGED by structural work (verified
|
||
stash-compare). comb regression is pre-existing bridge state, separate issue.
|
||
- Structural chain validated via render48k smoke (3 cases) + module checks
|
||
(fn529fe0/exp2/twin/tables/leveltrack/levelpath/fftconv all PASS).
|
||
- render48k tail artifact: last BLK=65536 block zero-pads => am decays in final
|
||
frames (~last 0.06s of y48). Cosmetic; metric window barely affected.
|
||
|
||
### Commit: 535b150 (LUT level-domain restructure).
|
||
NEXT: step 3 wire combine_acc track into mask output (currently computed,
|
||
discarded); step 4 FFT-conv 0x535a70 mask smoothing; then decode BandConfig
|
||
ctx+0x188 writers to replace MULT placeholder with decomp value.
|
||
|
||
## ============ UPDATE 2026-08-21b: IIR3 bidirectional + combine semantics from raw decomp ============
|
||
### IIR3 is BIDIRECTIONAL (corrects NOTES:830 "inline TWICE")
|
||
- consumers_out.txt:955-1075: TWO [reset state 0x440510=0, forward pass, backward
|
||
pass] pairs. State PERSISTS from forward into backward within a pair; reset
|
||
between pairs. Operand order verified: y[i] = x[i]*B3[i] + state*A3[i]
|
||
(A3@0x3c0510 multiplies STATE, B3@0x340510 multiplies x). Backward iterates
|
||
nbin-2 .. 1 with descending coefficient index.
|
||
- Wired into structural chain (commit 1a616e1); LUT_MULT retuned 4.4->4.2;
|
||
smoke mean|err| 1.021 (vs 0.993 fwd-only — parity within noise, kept faithful).
|
||
|
||
### Combine block EXACT semantics (thunk-level, corrects both 20h and 20j notes)
|
||
- 8d60 sub: args (acc=0x5407c8, mask=0x540678, dst=f6f8=0x5406f8), dst=3rd arg:
|
||
f6f8[i] = mask[i] - acc[i] (acc NOT overwritten by sub; 20j was wrong)
|
||
- mirror f6f8 (11940 x2)
|
||
- 3c40 fma: f6f8_upper += kRTAtt*acc_upper ; f6f8_lower += kRTRel*acc_lower
|
||
(dst = F6F8 halves, b = acc — 20h had this right)
|
||
- 5a20: acc += mask (persistent per-band accumulator)
|
||
|
||
### CRITICAL SCOPE FINDING for Step 2 (combine wiring)
|
||
- In the ONLINE single-band path (0x5408b8==0) the updated f6f8/acc have NO
|
||
consumer inside FUN_180529fe0 before warp/IIR3/dry-wet: warp applies to
|
||
band buffer directly, blend overwrites f6f8 next band/frame. Verified by
|
||
exhaustive grep of consumers_out.txt (last 0x5406f8 use = offline branch).
|
||
- => combine CANNOT close t1k/al single-band tails by itself; its effect must
|
||
enter via (a) multiband combiner 0x5316e0 reading acc/f6f8 across bands, or
|
||
(b) FFT-conv stage. Step 2 re-scoped: decode 0x5316e0 acc consumption FIRST,
|
||
only then wire. Do NOT invent a track->mask feedback (would be empirical).
|
||
- Online blend confirmed: f6f8 = freqaxis*(1-mix) + mix*fVar25(0.8) via
|
||
6e40+15060 thunks, then bigkernel exp2 in-place on band (line 805-832).
|
||
|
||
### Commits: 9ffe60e (IIR3 fwd-only + retune), 1a616e1 (bidirectional + decode).
|
||
|
||
## ============ UPDATE 2026-08-21c: FULL STRUCTURAL CORPUS (first honest numbers) ============
|
||
### Harness: scripts/corpus_structural.py (render48k x all 62 cases, bridge-compare mode)
|
||
Structural chain (48k/4096, LUT level-domain g=0.344 m=4.2, IIR3 bidi) vs bridge:
|
||
group n bridge struct delta
|
||
t1kq 7 0.226 0.768 +0.54 worse
|
||
t1k 11 1.801 2.114 +0.31 worse
|
||
al 6 0.638 0.986 +0.35 worse
|
||
res 11 0.628 0.437 -0.19 BETTER
|
||
dual 22 0.726 3.264 +2.54 MUCH worse
|
||
comb 5 10.149 6.116 -4.03 MUCH BETTER
|
||
TOTAL 62 1.594 2.286 +0.69 worse
|
||
|
||
### KEY FINDINGS:
|
||
1. **comb: structural ALREADY beats bridge by 4 dB** (6.1 vs 10.1) WITHOUT acc
|
||
wiring — per-band structural min() + warp closes part of the multiband gap.
|
||
Plan's "combine closes comb" partially pre-empted by the chain itself.
|
||
2. **dual_*_500: constant -6.7 dB over-cut for ALL q** (q-independent!).
|
||
res[center] is q-independent (0.117) => systematic LEVEL offset at band
|
||
center on two-tone input, not shape. dual_*_2000 (off-center bin) BETTER
|
||
than bridge (-0.1..-0.4 vs +0.1..+2.3). At same xv, bridge-LUT cut ~-9.3 dB,
|
||
our exp2 path cuts ~-20 dB: real reduction SATURATES (~C_max~0.70 per
|
||
extracted LUT pts), exp2(-lvl) does not.
|
||
3. al level-sweep error is MONOTONE in level (+0.88@lv3 -> -1.82@lv24): our
|
||
level->cut curve too steep mid-range; but t1k (loudest) flips back to
|
||
under-cut (+1.8) => NON-monotonic overall, no monotone LUT fixes all groups.
|
||
4. gamma/MULT sweep (g in {0.15,0.22,0.30} x m in {4.2,5.0,5.8}, fresh builds,
|
||
12-case subset incl. dual/res/comb): ALL points worse than current
|
||
g=0.344/m=4.2; gradient points BACK to current optimum. Blind param space
|
||
EXHAUSTED around this parametrization.
|
||
|
||
### CONCLUSION (Step 1 status): criterion "structural >= bridge on single-band"
|
||
NOT met globally (regress on t1kq/t1k/al/dual; better on res). The missing
|
||
piece is the REAL reduction curve saturation — needs either:
|
||
(a) Step 7 BandConfig ctx+0x188 live capture (A/B/gamma per config), or
|
||
(b) re-examination of bigkernel INPUT: maybe level into exp2 carries an
|
||
additional transform (log-domain offset / per-band depth scalar
|
||
0x540888 / offline 0x5e80+FUN_180323f20 ops) that saturates the cut.
|
||
Do NOT tune MULT further — it is calibrated; the CURVE SHAPE is wrong.
|
||
|
||
### FFT-conv priority DOWNGRADED: NOTES:18c shows window 0x540658 tail is
|
||
near-flat (0.8->1.0 plateau at N/2>=2048), FIR-construction effect on mask
|
||
shape minimal for N=4096. Step 4 stays P3.
|
||
|
||
## ============ UPDATE 2026-08-21d: REDUCTION-LAW DISCOVERY + Phase A closure ============
|
||
|
||
### Discovery: effective reduction is AFFINE in dB(level)
|
||
RT_DUMP_BIN tracts (frame 100) across al sweep fit:
|
||
x_exp2 = -0.6646 - 0.05877 * dB(lvl_raw) (mask ~ 0.8 * lvl^(-0.354))
|
||
gamma_eff 0.354 ~= decomp BandConfig gamma 0.344 (NOTES:967) -> power law,
|
||
NOT the double-exp t^gamma->exp2 form (21c). Tone-levels verified +-0.13 dB.
|
||
|
||
### Pre-IIR application calibrated through chain model
|
||
Python simulator (tables from rt_mask_tables.cpp; IIR1 fwd, IIR2 fwd, exp2*0.8,
|
||
W, IIR3 bidi x2): pre-IIR affine peak dilutes ~2.3x by spatial IIRs ->
|
||
X0=1.8, SLOPE=0.11 (max|x-resid| 0.09 ~= +-0.5 dB on tones).
|
||
FULL CORPUS fresh-build A/B (stale-binary protocol, both reproduce exactly):
|
||
group old(HEAD LUT) new(affine pre-IIR)
|
||
t1kq 0.768 0.460 v
|
||
t1k 2.114 1.196 v
|
||
al 0.986 0.132 v (all 6 levels |e|<=0.25)
|
||
res 0.437 2.046 x
|
||
dual 3.264 4.317 x
|
||
comb 6.116 5.718 v
|
||
TOTAL 2.286 2.633
|
||
|
||
### Phase A experiments (ALL on fresh builds / validated models)
|
||
1. res regression is REAL (not stale binary; reproduced both directions).
|
||
2. High-bin/IIR3-backward propagation hypothesis REFUTED: at frame-100 the
|
||
two laws give IDENTICAL masks on res_500 (<1e-4 everywhere); divergence
|
||
lives in other frames' level zone (-13..+15 dB) where old cuts more.
|
||
3. Direct-affine (bypass IIR1/2, raw constants) REFUTED: TOTAL 2.777.
|
||
Spatial IIRs are integral to the effective curve.
|
||
4. Offline trajectory model (RT_DUMP_ALL per-frame lvl_raw; chain sim;
|
||
metric-window frames [55,90)/[243,278) @48k/1024-hop; zero-pad frames
|
||
excluded — they are beyond the trimmed output!) VALIDATED: known-law
|
||
predictions within 0.09..0.19 dB on res_500/al_24/t1k_1000 (al_12 0.53).
|
||
5. Grid search over affine family (X0, SLOPE, floor F>=0, ceiling CMAX),
|
||
objective = sum pred_err^2 over anchored cases:
|
||
OPTIMUM X0=2.00 S=0.0825 F=0 CMAX=inf -> rms 0.75 dB with
|
||
res_500 +0.84 / al12 +0.29 / al24 -1.21 / t1k -0.02.
|
||
Ceiling never active in-grid; positive floors do NOT help (top all FL=0).
|
||
|
||
### KEY NEGATIVE RESULT
|
||
NO law of the scalar affine family fits tone AND noise content
|
||
simultaneously (res tension +0.4..+1.1 unavoidable). Old Pchip "wins" on
|
||
res/dual/comb via error cancellation, not shape correctness; affine wins
|
||
tones decisively (al +-0.13 dB linearity over 42 dB!). The missing piece is
|
||
CONTENT-DEPENDENT behavior: detector dynamics (att/rel interplay with
|
||
fluctuating spectra), or combine/acc consumer (multiband feedback), or
|
||
saturation mechanism. => Gate "no group regression" FAILS for every family
|
||
member -> committed canon stays HEAD (LUT g=0.344/m=4.2).
|
||
|
||
### Tooling kept (opt-in env, zero cost when unset)
|
||
- RT_DUMP_BIN=<path> [RT_DUMP_FRAME=n]: single-frame tract dump (7 cols).
|
||
- RT_DUMP_ALL=<path>: append per-frame binary lvl_raw spectra
|
||
[i32 frame][i32 nbin][f32 x nbin]. Use ONE env per run (BIN early-return
|
||
eats ALL frames if combined!). rm target first (append mode).
|
||
Captures live under /tmp/opencode/traj_*.bin (law-independent detector).
|
||
- Offline engine: /tmp/opencode/phaseA_grid_fast.py (combo-vectorized chain
|
||
sim + grid search); estimator zoo: phaseA_estimator_zoo.py.
|
||
|
||
### NEXT (Phase B candidates, priority order)
|
||
1. Detector dynamics: att/rel on fluctuating spectra vs ref renders of
|
||
noise-like material (res group) — per-frame gain extraction protocol
|
||
ready (STFT ratio out/in at metric bin, see phaseA session notes).
|
||
2. Step 7 live capture ctx+0x188 (constants + possible content-dependent
|
||
branch), NOTES_CAPTURE.md method.
|
||
3. combine consumer hunt (multiband cascade) for dual/comb.
|
||
|
||
## ============ UPDATE 2026-08-22a: PHASE B RECOVERY — offline detector hypotheses REFUTED ============
|
||
|
||
Контекст: Phase B сессия 2026-08-21 (вечер) + утро 08-22 осталась незакоммиченной —
|
||
скрипты в /tmp/opencode, результаты не сохранялись. Все 4 эксперимента перепрогнаны,
|
||
stdout зафиксирован (`phaseB_*.out`), скрипты перенесены в `scripts/phaseB_*.py`.
|
||
Движок: phaseA_grid_fast.py (валидированный офлайн-тректор, погрешность 0.09..0.19 dB
|
||
на анкорах). Анкоры res_500/al_12/al_24/t1k_1000, критерий pred_err ~ 0 на ВСЕХ.
|
||
|
||
| # | Гипотеза | Скрипт | Результат | Статус |
|
||
|---|----------|--------|-----------|--------|
|
||
| 1b | slide_max pooling lvl (w=3..33) | phaseB_pool2.py | w=3: tones −2.7/−2.3/−4.9, rms 3.02 (base 1.04) | **REFUTED** |
|
||
| 1c | neighborhood mean/RMS pooling | phaseB_pool3.py | best mean w=3 rms 1.35; res +1.9..+5.1 vs t1k −1.9..−4.4 tradeoff, ни одна точка не закрывает все 4 | **REFUTED** |
|
||
| 2 | temporal dynamics (hold b/dbdecay r/ema a на полной траектории) | phaseB_temporal.py | ВСЕ варианты = baseline (rms 1.03–1.05): метрика в steady-state, состояние успевает устояться до окна | **REFUTED (no-op)** |
|
||
| 3 | ρ(IIR1)+Δ joint scan (ρ∈[0.30,0.95], Δ∈[−1.5,+1.5]) | phaseB_rho.py | best ρ=0.830 D=+1.40 → rms 0.654; НО al_24 стабильно −1.19..−1.25, ρ без источника в декомпе (канон 0.692) | **REJECTED** (нарушает golden rule #1, тот же scalar-family тупик) |
|
||
|
||
Выводы:
|
||
1. Пространственный pooling ЛЮБОГО вида (max/mean/rms) не объясняет content-gap.
|
||
2. Temporal-класс гипотез НЕПРОВЕРЯЕМ на steady-state анкорах — нужен переходный
|
||
контент (burst-рефы уже есть в soothe-bt) или другой анкорный набор.
|
||
3. Лучший (ρ,D) = репараметризация affine-семейства Phase A → упирается в тот же
|
||
KEY NEGATIVE RESULT (scalar-семейство не закрывает тон+шум одновременно).
|
||
Канон НЕ сменён: HEAD (LUT γ=0.344/MULT=4.2, ρ=0.692).
|
||
|
||
NEXT (приоритеты без изменений):
|
||
1. Step 7 live capture ctx+0x188 (NOTES_CAPTURE.md метод) — константы A/B/γ +
|
||
возможная контент-зависимая ветка. Требует REAPER+плагин.
|
||
2. combine consumer hunt (межполосный каскад) для dual/comb.
|
||
3. Опционально: transient-анкоры для проверяемости temporal-класса.
|
||
|
||
## ============ UPDATE 2026-08-22b: STEP 7 EXECUTED → PREMISE REFUTED (BandConfig = GUI-only) ============
|
||
|
||
Инфраструктура захвата готова и работает:
|
||
- `scripts/step7_capture.py <rpp>` — spawn reaper (+play.lua realtime) → chunked snapshot
|
||
хоста yabridge → fingerprint level_gain-пар (level[j]==j/1024 exact) → u64-ref голосование
|
||
базы level-path объекта (+0xe0+band*0x18) → decode всех BandConfig вокруг базы.
|
||
- `play.lua` ИСПРАВЛЕН: reaper.Sleep НЕ существует в API (падал скрипт, транспорт
|
||
продолжал играть без keep-alive) → цикл reaper.defer.
|
||
- Грабли: /proc/<pid> vs /proc/<pid>/mem (pread каталога = тихий EIO на всё); layout
|
||
объекта ПЛАВАЕТ между сессиями → оффсеты BandConfig не хардкодить, только fingerprint.
|
||
|
||
Живые константы (7 захватов: render_long, t1kq_base, t1k_b1f_1000, al_12, al_24,
|
||
dual_b1q_0.5, comb_base — ВСЕ идентичны):
|
||
- cfg@obj+0x168: **A=−24.0 B=+28.0 γ=1.0 flag=0 cb=SET** («кривая редукции»)
|
||
- cfg@obj+0x170: A=16.0 B=20000.0 γ=1.0 cb=SET; shaper f32 @+0x20..: 0.55, 7.130898,
|
||
2.772589(=ln16), 2.718282(=e)
|
||
⇒ противоречие gap #3 разрешено: −24/28/1 подтверждено живьём; −13.78/68.29/0.344
|
||
был ФИТОМ к рендер-референсам, не свойством плагина.
|
||
|
||
FUN_180563a60 расшифрован ДО КОНЦА (f_563a60.dis, 165 строк):
|
||
- вход: mask-doubles obj+0x4198+band*0x2000 (пишет FUN_18056e3e0 из band-list
|
||
+0x178→[+0x78/+0x84]); x-axis пар = j*(1/1024) (конста 0x24c3c50);
|
||
dB = logf(mask)*8.685889 (=20/ln10, 0x24c43e0);
|
||
- выход: gain[j] = clamp01(cb(A,B,dB)) где cb = std::function @cfg+0x90 — В ЖИВЫХ
|
||
ЗАХВАТАХ cb ВСЕГДА SET ⇒ статическая ветка A/B/γ НЕ ВЫПОЛНЯЕТСЯ НИКОГДА;
|
||
- статическая ветка (cb==NULL, для протокола): t=clamp01((dB−A)/(B−A));
|
||
γ==1 → t; flag==0 → t^γ; flag!=0 → 0.5·(1+sign(2t−1)·|2t−1|^γ).
|
||
|
||
Callgraph: весь кластер 5631c0/563260 → 563440 → {56e3e0, 563a60} достижим ТОЛЬКО по
|
||
DATA-xrefs (vtables 1826a03cc..420, 1824b14f0..510) — GUI-timer heartbeat. АУДИО-метод
|
||
FUN_180529fe0 (Soothe2Module<float>::vtbl) НЕ ссылается на {+0x168,+0x170,+0x178,
|
||
+0xe0,+0x198,+0x4198}; его callees — только memcpy-туннели (181ba94b0 ×13, 52dbc0, 52d920).
|
||
Файл /tmp/consumers_out.txt восстановлен из decomp_funs.txt (215 строк, вне git!).
|
||
|
||
### ВЫВОДЫ (меняют приоритеты)
|
||
1. **Шаг 7 BITEXACT_PLAN в исходной постановке ОПРОВЕРГНУТ**: BandConfig A/B/γ питает
|
||
только GUI-отрисовку кривой, аудио-путь его не читает. Live-захват A/B/γ не может
|
||
закрыть насыщение кривой редукции аудио-цепи.
|
||
2. LUT-блок структурной цепи (t^γ·MULT с −13.78/68.29/0.344/MULT=4.2) = EMPIRICAL фит
|
||
БЕЗ декомп-источника в аудио-пути (формула списана с GUI-функции!). Помечен EMPIRICAL;
|
||
канон не меняем (корпус держит), но источник правды теперь внутри аудио-модуля.
|
||
3. Новый приоритет №1: полный разбор аудио-тела FUN_180529fe0 (215 строк декомпа,
|
||
/tmp/consumers_out.txt) + combine-consumer hunt — насыщение C_max≈0.70 сидит там.
|
||
|
||
## ============ UPDATE 2026-08-22c: PARAM BRIDGE + RENDER-BENCH CALIBRATION ============
|
||
|
||
Инфраструктура управления плагином через ОФИЦИАЛЬНЫЙ параметр-мост REAPER:
|
||
- `dump_params.lua` — дамп всех FX-параметров (имя, raw 0..1, formatted UI) в /tmp/opencode/fxparams.txt.
|
||
- `setparam.lua` — установка по имени через TrackFX_SetParam + SaveProjectEx (env S2_SET="name=val;...", S2_OUT).
|
||
- `scripts/rpp_setparam.py` — редактор XML-стейта в RPP с корректным апдейтом length-полей
|
||
(A=buflen−16, B=len(xml), хвост JUCEPrivateData сохраняется).
|
||
|
||
### КЛЮЧЕВОЕ ОТКРЫТИЕ: XML `<PARAM>` секция НЕ источник VST3-стейта
|
||
Живой дамп: depth raw=0.524 ↔ fmt=0.9; в XML при этом "0.8639736175537109" (=UI-единицы!).
|
||
Плагин восстанавливает параметры из БИНАРНОЙ части чанка; XML-список — декоративная
|
||
копия для GUI-восстановления. Правки только XML → рассинхрон → плагин откатывается в
|
||
дефолт (объясняет «магию» идентичных рендеров −31.76 у любых depth-правок; контроль
|
||
same-value rewrite давал байт-в-байт реф ✓). ВНИМАНИЕ к rpp_setparam.py: менять можно,
|
||
но аудио это НЕ меняет.
|
||
|
||
### Семантика параметров (через мост)
|
||
- `depth`: raw 0..1 → UI **±18 линейно** (ui=36·raw−18). Рендеры подтверждают монотонность:
|
||
raw=0 → юнити; raw=1.0 (+18) → −37.6 dB @tone.
|
||
- `input trim`: raw → **±24 dB**.
|
||
- band sens: fmt в dB; stereo mode mid|side; balance 100%|57%.
|
||
|
||
### WAV-грабли (живое подтверждение класса багов 71644ff)
|
||
REAPER пишет bext(602B)+junk(28B) ПЕРЕД data; наивный парсер (readframes+reshape без
|
||
каноничного loader) даёт фантомный широкополосный шум и фальшивый клиппинг
|
||
(corr(L,R)≈0 артефакты). Каноничный render_parity.load корректен; санити bypass:
|
||
corr(L,R)=1.0000, rms=out=in — РЕНДЕР-ПАЙПЛАЙН ЧИСТЫЙ.
|
||
|
||
### Измеренные кривые редукции (t1kq-пресет, тон@1000, Goertzel 0.75s)
|
||
- Depth-свип: монотонный, raw .5→−5.5 dB; .75→−21 dB; 1.0→−37.6 dB @tone.
|
||
- Input-trim свип (холодный тон −18 dBFS): R_eff растёт 1.4→15.7 dB на всём ±24 —
|
||
ПОТОЛКА НЕТ в этом диапазоне (против гипотезы C_max≈0.70 как свойства плагина).
|
||
- Горячий тон (+6 dBFS): out@1000 ЗАМИРАЕТ на −26.74 dB при trim ≥+12 (R_eff cap ≈20.7),
|
||
плато воспроизведено дважды.
|
||
- НО: при одинаковом относительном драйве холодный/горячий дают РАЗНЫЙ gain
|
||
(−15.7 vs −20.7 @L≈+6) ⇒ отклик детектора не чистая f(level) — контент/нормализация
|
||
(согласуется с KEY NEGATIVE RESULT Phase B).
|
||
|
||
### Следствия для приоритета №1
|
||
1. «Насыщение C_max≈0.70» старой модели — артефакт placeholder MULT=4.2/Pchip cap 0.667,
|
||
а не свойство плагина в рабочем диапазоне.
|
||
2. Реальный потолок проявляется как FLOOR выходного уровня (−26.74 dB @hot) — искать
|
||
механизм floor/clamp в level-path декомпе с конкретной целью.
|
||
3. Инструмент готов для систематического картирования R(L, sens, depth): скрипты +
|
||
мост позволяют прогонять десятки конфигов без ручного GUI.
|
||
|
||
## ============ UPDATE 2026-08-22d: SATURATION KNEE MAPPED — HARD FLOOR −20.72 dB ============
|
||
|
||
Hot-тон (+6 dBFS, band1 fc679 q1 sens+12 depth+0.9), gain=out−dry по Goertzel@1000:
|
||
| trim | L=in+trim | gain |
|
||
|------|-----------|------|
|
||
| 0 | −6.02 | −3.39 |
|
||
| +6 | −0.02 | **−20.72** |
|
||
| +9..+24 | +3..+18 | **−20.72** (pinned, ≥24 dB драйва) |
|
||
|
||
1. ЖЁСТКИЙ ПОТОЛОК: gain замирает на −20.72 dB (ratio 0.0921) — не плавное плато,
|
||
а колено между L=−6..0 с последующим pin. Форма ⇒ t-clamp модели (t→1 при
|
||
dB_lvl≥B=28 → lvl=MULT → фикс. маска) КАЧЕСТВЕННО подтверждена живьём.
|
||
2. Колено: между trim 0 и +6. При γ=1/A=−24/B=28 это задаёт офсет
|
||
физический уровень → внутренний lvl-dB ≈ 31±3 dB.
|
||
3. ЗАГАДКА для следующей сессии: холодный тон при том же относительном драйве
|
||
(L=+5.94 через trim+24) даёт gain только −15.74 (НЕ насыщен), горячий при L=+5.98 —
|
||
−20.72 (насыщен). Trim ≠ простой pre-gain детектора ИЛИ отклик зависит от
|
||
абсолютного контента. Тест-план: (a) спектральный профиль нотча сатурированного
|
||
рендера vs форма маски модели; (b) файловый gain вместо trim; (c) алгебра пола:
|
||
0.0921 = blend·exp2(−MULT)·W·kBand768·kWarp·IIR3·convwin @bin85 — все live-таблицы
|
||
есть в dsp/, решается относительно MULT офлайн.
|
||
4. RT_DUMP_ALL НЕ работает в оффлайн-рендере плагина (это инструментация только
|
||
нашего C++; у настоящего бинар её нет). Live-capture траекторий = только память.
|
||
5. +0x4198 маски уровня объекта = GUI-кривые (band1 растёт 1.0→3.98 к ВЧ) — ещё раз
|
||
не аудио-маски; для floor-hunt использовать алгебру (3c), не этот объект.
|
||
|
||
## ============ UPDATE 2026-08-22e: FLOOR ALGEBRA SOLVED — blend·ln10/20 EXACT ============
|
||
|
||
Модельный grid (render48k, live A=−24/B=28/γ=1 через RT_LUT_* env, файловый gain):
|
||
| MULT | B | base(−6dB) | p3(−3.1dB) |
|
||
|------|---|-----------|-----------|
|
||
| 4.2 | 28 | −8.78 | −9.70 |
|
||
| 9 | 28 | −16.35 | −18.18 |
|
||
| 12 | 28 | −20.49 | −22.77 |
|
||
ВЫВОД: t-clamp в транскрибированной цепи НЕ даёт pin (base/p3 расходятся на 2+ dB
|
||
при любом MULT/B) ⇒ механизм пола живёт НЕ в LUT-блоке.
|
||
|
||
### РЕШЕНИЕ АЛГЕБРАИЧЕСКИ
|
||
Измеренный pin −20.72 dB совпадает с точностью 0.0055 dB:
|
||
|
||
gain_floor = 20·log10(blend · ln10/20) = 20·log10(0.8 · 0.115129) = −20.7145
|
||
|
||
т.е. маска пола = blend × (ln10/20). Константа ln10/20 = 0.1151292546 @0x1824c3cd4 —
|
||
ТА ЖЕ семья что и коэфф. attack/release expf(p·0.11513) (NOTES_CAPTURE 19b).
|
||
Audio-референсы константы: **FUN_180529c60**, 18052baa0, 18052bad0, 18052bba0.
|
||
|
||
### Ведущий след (f_529c60.dis:94-101)
|
||
xmm0 = *(this+0x54087c)
|
||
xmm0 *= 30.0 (@0x1824c4478)
|
||
xmm0 -= 90.0 (@0x1824c44d0)
|
||
xmm0 *= 0.115129 (@0x1824c3cd4)
|
||
call expf (181a14cac)
|
||
xmm0 *= результат FUN_1804d56b0(band buffer 0x540678[band], r13d=n)
|
||
⇒ параметрический dB→linear фактор (30p−90 ∈ [−90,−60] dB) умножается на вычисленное
|
||
над полосой значение и идёт в band-буферы. Точная роль (floor/dither/scale) TBD.
|
||
|
||
### NEXT
|
||
1. Декодировать FUN_1804d56b0 + контекст использования фактора в 180529c60.
|
||
2. Проверка предсказания: floor_gain(mix) должен сдвигаться как 20log10(blend′/0.8)
|
||
(рендер mix=50% через мост).
|
||
3. Порт найденного клампа в структурную цепь → корпус с гейтом vs bridge.
|
||
|
||
## ============ UPDATE 2026-08-22f: FLOOR DISCRIMINATION — 3 гипотезы, детекторный кап ============
|
||
|
||
Дискриминационные рендеры (hot-тон, trim+12, через параметр-мост):
|
||
| конфиг | gain изм. | mask=10^(g/20) | интерпретация |
|
||
|--------|-----------|----------------|----------------|
|
||
| mix100 sens12 | −20.72 | **0.8·ln10/20** | базовый пол |
|
||
| mix50 sens12 | −5.20 | eff 0.552=out/dry | **dry/wet кроссфейд**: 0.5+0.5·0.0921=0.546→−5.26 ✓ |
|
||
| mix100 sens6 | −18.78 | **1.0·ln10/20** | пол сдвинулся ровно на ×ln10/20 |
|
||
|
||
ОПРОВЕРГНУТЫ: (а) явный кламп mask≥const (mix50 должен был остаться −20.72);
|
||
(б) blend-floor 20log10(blend′·ln10/20) для mix50 (предсказывал −26.74);
|
||
(в) naive detector-sat lvl∝sens (предсказывал −11.33 для sens6).
|
||
|
||
ЧТО СОШЛОСЬ:
|
||
1. Mix = классический кроссфейд out=(1−m)·dry+m·processed; пол в PROCESSED пути.
|
||
2. Полы = ТОЧНЫЕ кратные ln10/20: {0.8× @sens12; 1.0× @sens6}.
|
||
3. Движение пина с sens: Δx_exp2 = log2(mask/0.8): −3.1188→−2.7996, Δ=−0.3192
|
||
на 6 dB sens ⇒ наклон −0.0532 dB⁻¹ ≈ аффинный закон Phase A (−0.05877!)
|
||
⇒ похоже, что НАСЫЩАЕТСЯ ДЕТЕКТОР (кап его dB_lvl ≈ 36..42 в завис. от sens),
|
||
а редукция следует аффинному закону до этого капа. Точные значения ln10/20
|
||
при этом — след пересечения закона с сеткой экспонент, не отдельный кламп.
|
||
4. Наша модель на том же материале достигает lvl_max=3.012 ≈ log2(ln10/20)=3.1188
|
||
(Δ3.5%) — почти касание первого пола естественно (RT_DUMP_ALL render48k).
|
||
|
||
### NEXT (Шаг 9 продолжение)
|
||
1. Локализовать кап детектора: где dB_lvl перестаёт расти (vlog/leveltrack путь,
|
||
FUN_18052d650 IIR1 / leveltrack таблицы) — искать ограничитель амплитуды FFT/am.
|
||
2. Точная формула капа от sens: снять полы на сетке sens {3,6,9,12} × trim{18,24}.
|
||
3. Порт: вместо t-clamp LUT — детекторный кап + аффинный закон Phase A; корпус с гейтом.
|
||
|
||
## ============ UPDATE 2026-08-22g: NO-LUT TEST + CLIPPING THEORY REFINED ============
|
||
|
||
### NO-LUT эксперимент (RT_LUT_OFF=1, render48k)
|
||
Тракт@bm85 (hot-тон, RT_DUMP_BIN): lvl_raw=3.0117 → post-IIR1/2 band_level=1.0646
|
||
(IIR1 A[85]≈0.65 сильно сглаживает изолированный пик) → prewarp mask=0.3825·W=0.408
|
||
→ итоговый gain модели −7.67 dB (против real −20.72). Убрали LUT — модель всё равно
|
||
далека: разрыв теперь ЛОКАЛИЗОВАН в детекторном фронте (am/res/scale и/или IIR-сглаживание),
|
||
а не в форме кривой. Модельный lvl_max=3.012 почти касался бы пола 3.1188=log2(20/ln10),
|
||
если бы не разводился IIR1 по частоте.
|
||
|
||
### Клиппинг-теория: частично опровергнута, но указывает на clamp sidechain
|
||
- REAPER рендерит в float: trim-овердрайв кормит плагин честными float >1.0, обрезки на
|
||
itemе
|
||
|
||
## ============ UPDATE 2026-08-22g: NO-LUT TEST + CLIPPING THEORY REFINED ============
|
||
|
||
### NO-LUT эксперимент (RT_LUT_OFF=1, render48k)
|
||
Тракт@bm85 (hot-тон, RT_DUMP_BIN): lvl_raw=3.0117 → post-IIR1/2 band_level=1.0646
|
||
(IIR1 A[85]≈0.65 сильно сглаживает изолированный пик) → prewarp mask=0.3825·W=0.408
|
||
→ итоговый gain модели −7.67 dB (против real −20.72). Убрали LUT — модель всё равно
|
||
далека: разрыв теперь ЛОКАЛИЗОВАН в детекторном фронте (am/res/scale и/или IIR-сглаживание),
|
||
а не в форме кривой. Модельный lvl_max=3.012 почти касался бы пола 3.1188=log2(20/ln10),
|
||
если бы не разводился IIR1 по частоте.
|
||
|
||
### Клиппинг-теория: частично опровергнута, но указывает на clamp sidechain
|
||
- REAPER рендерит в float: trim-овердрайв кормит плагин честными float больше 1.0,
|
||
обрезки на item'е НЕТ.
|
||
- Pre-clipped файл (x12dB с клиппингом на диске) @trim0 -> -12.31 dB != -26.74: сравнение
|
||
НЕКОРРЕКТНО (искажён DRY-путь файла), но показало, что «клиппинг на входе» сам по себе
|
||
не воспроизводит пин.
|
||
|
||
### ОБЪЕДИНЁННАЯ ГИПОТЕЗА (лид): clamp копии sidechain на +-1.0 внутри плагина
|
||
Факты:
|
||
1. Колено hot-тона ровно при переходе эффективного уровня через 0 dBFS (trim 0->+6).
|
||
2. h_06 == h_12 == h_24 по метрике — после достижения 0 dBFS драйв не растёт.
|
||
3. Полы = точные кратные ln10/20 ({0.8x, 1.0x} для sens 12/6).
|
||
4. mix = сухой кроссфейд (-5.26 предсказано / -5.20 измерено).
|
||
Открытое: cold trim+24 (+6 eff) дал -15.74 вместо -20.72 => кламп либо мягкий (tanh-like,
|
||
разная глубина насыщения), либо детектор реагирует на RMS/форму, а не только пик.
|
||
|
||
### NEXT
|
||
1. Прямая верификация: live-capture sidechain-буфера плагина во время сатурированного
|
||
рендера (step7_capture-style скан памяти, искать плоскую вершину +-1.0).
|
||
2. Снять форму клампа: сетка hot-файлов {-9,-6,-3,0 dBFS} x trim {6..24} -> кривая
|
||
насыщения mask(eff_level).
|
||
3. Порт: sidechain-clamp в render48k (min(max(in,-1),1) на копии для детектора) +
|
||
аффинный закон Phase A без LUT -> корпус с гейтом.
|
||
|
||
## ============ UPDATE 2026-08-22h: FLOOR LAW MAPPED — sens и depth ============
|
||
|
||
Пол редукции как функция параметров (hot-тон −6 dBFS, trim+12, mix100):
|
||
| sens | floor gain | mask | lvl_cap=log2(0.8/mask) |
|
||
|------|-----------|--------|---------|
|
||
| 3 | −17.74 | 0.12973 | 2.6245 |
|
||
| 6 | −18.78 | 0.11502 | **=ln10/20 точно** |
|
||
| 9 | −19.78 | 0.10256 | 2.9635 |
|
||
| 12 | −20.72 | 0.09208 | 3.1191 |
|
||
|
||
### Эмпирический закон
|
||
floor_dB(sens) ≈ 20·log10(ln10/20) − (sens − 6)/3 (якорь sens6; ±0.06 dB)
|
||
Depth тоже сдвигает пол: depth UI 0 → **−19.10 dB** против base (+0.9) → −20.72
|
||
(Δ≈1.62 dB на 0.9 ед. depth ⇒ ~1.8 дБ/ед., НЕ как sens/3).
|
||
lvl_cap(sens) квазилинеен: наклон ≈0.055/oct-per-dB ≈ аффинный наклон Phase A (0.0588).
|
||
|
||
### Статус механизма
|
||
Пол — гладкая поверхность F(sens, depth); сайт в декомпе не найден (юзеры ln10/20 —
|
||
сеттеры att/rel и dither-фактор). Гипотеза детекторного капа остаётся лидом:
|
||
lvl_raw детектора перестаёт расти при эффективном уровне ≈0 dBFS (колено), а кривая
|
||
редукции следует закону до этого капа. Точная форма F(...) — на картирование.
|
||
|
||
### NEXT сессии
|
||
1. Декод FUN_18052d650 (IIR1) + leveltrack-путь на предмет ограничителя am.
|
||
2. Картирование F(sens,depth) сеткой 4×4 через мост (~30 мин рендеров) → фит.
|
||
3. Порт эмпирического пола в структурную цепь (env-параметризованный), корпус с гейтом.
|
||
|
||
## ============ UPDATE 2026-08-22i: RT_LVL_CAP ИНЕРТЕН — разрыв в детекторном фронте ============
|
||
|
||
Порт капа пост-IIR уровня (RT_LVL_CAP env в process_band_structural, после IIR2):
|
||
корпус с cap=3.119 и 2.798 ДАЛ ТОЧНО БАЗОВЫЕ ЧИСЛА (TOTAL 2.286) — кап не связывается,
|
||
т.к. пост-IIR уровни модели максимум ~1.06 (lvl_raw 3.01 разводится IIR1 A[85]≈0.65).
|
||
⇒ Модели не ХВАТАЕТ уровня, а не лишний: реальный плагин доводит пост-IIR lvl до 3.12
|
||
(пол), наш детекторный фронт (am/res/scale нормировка + IIR-сглаживание изолированных
|
||
пиков) теряет ~x2.8. ПРИОРИТЕТ СДВИНУТ: чинить детекторный фронт (амплитудная
|
||
нормировка am, форма res_k, сила IIR1/2 вдоль частоты), а не форму кривой.
|
||
RT_LVL_CAP оставлен в коде как инструмент (opt-in, инертен при текущих уровнях).
|
||
|
||
## ============ UPDATE 2026-08-22j: WHITE-NOISE PROBE — архитектура расшифрована ============
|
||
|
||
Белошумный пробник через сатурированный конфиг (trim0 на шуме −6 dBFS RMS):
|
||
|
||
REAL (плагин): передача ПЛОСКАЯ −0.01…−1.65 dB на ВСЕХ частотах;
|
||
лёгкая вмятина −1.04 @678 (центр полосы), HF rolloff −1.5 @8k.
|
||
МОДЕЛЬ (canon): фейковый низкочастотный «пол» −24 дБ @50Гц от W=kBand768·kWarp·res^rp.
|
||
МОДЕЛЬ (NOWARP): форма совпала — плоско ~−2.1…−2.7, вмятина −4.45 @678.
|
||
|
||
### ВЫВОДЫ АРХИТЕКТУРЫ
|
||
1. W=warp НЕ является бланкетным множителем выхода: реальный плагин пропускает
|
||
широкополосный контент почти на unity ⇒ RT_NOWARP правилен по духу.
|
||
2. Реальный плагин режет СТАЦИОНАРНЫЙ ТОН на −20.72, но НЕ режет шум ⇒ редукция
|
||
управляется КОНТРАСТОМ бина к соседям/шумовому полу (смысл IIR1/2 вдоль частоты =
|
||
бегущий референс), а не абсолютным уровнем.
|
||
3. Без blend в выходе уровни ШУМА сходятся: real lvl≈+0.123 vs наш +0.098 (Δ0.02 окт);
|
||
уровни ТОНА: real 3.444 vs наш 3.012 (Δ0.43 окт = ×1.35 амплитуда) ⇒ расхождение
|
||
растёт с уровнем ⇒ подозрение на res_k (twin АДАПТИРУЕТСЯ к сигналу во времени,
|
||
res_k уменьшается при длительном тоне) либо am-нормировку при высоких уровнях.
|
||
4. blend(0.8) в нашей модели даёт фантомный −1.94 dB бланкет — в реальном выходе его нет;
|
||
exp2(−lvl) без blend точнее описывает шумовой случай.
|
||
|
||
### Рабочая гипотеза цепочки (v22j)
|
||
lvl_raw[k,t] = am[k,t]/res_k(t)·scale (res_k(t) — адаптивный twin!)
|
||
lvl_ref[k,t] = IIR1/IIR2 вдоль ЧАСТОТЫ от lvl_raw (бегущий референс соседей)
|
||
редукция = f(lvl_raw − lvl_ref или lvl_raw/lvl_ref) ← контраст
|
||
mask = exp2(−редукция) (без blend-бланкета)
|
||
sidechain-in clamp ±1.0 (колено/пин при 0 dBFS)
|
||
|
||
### NEXT
|
||
1. Декод FUN_18052d650/IIR1 семантики заново: аргументы (что есть x, что есть acc),
|
||
направление и КАК результат используется (референс vs сигнал).
|
||
2. Тест адаптивного twin: рендер тона разной ДЛИТЕЛЬНОСТИ (0.5/2/8 c) через мост —
|
||
если пол зависит от длины ⇒ res_k(t) адаптируется, взять динамику из NOTES_TWIN.
|
||
3. Формулы контраста перебрать офлайн на траекториях RT_DUMP_ALL (движок phaseA готов).
|
||
|
||
## ============ UPDATE 2026-08-22k: BARE-CHAIN АРХИТЕКТУРА ПОДТВЕРЖДЕНА ============
|
||
|
||
Цепочка «голая» exp2-архитектура (RT_LUT_OFF=1 RT_IIR12=0 RT_NOWARP=1 RT_NOBLEND=1
|
||
RT_NOIIR3=1) на белошумном пробнике совпала с реальным плагином в пределах ~0.5 дБ
|
||
почти везде:
|
||
|
||
| freq | real | model |
|
||
|------|------|-------|
|
||
| 50 | −0.01 | −0.12 |
|
||
| 300 | −0.08 | −0.26 |
|
||
| 678 | −1.04 | −2.59 |
|
||
| 850 | −0.76 | −0.94 |
|
||
| 1000 | −0.74 | −0.62 |
|
||
| 2000 | −0.22 | −0.20 |
|
||
|
||
⇒ АРХИТЕКТУРА ПОДТВЕРЖДЕНА:
|
||
mask[k] = exp2(−lvl_raw[k]) БЕЗ blend-бланкета, БЕЗ warp-аттенюации
|
||
lvl_raw[k] = per-bin TIME envelope от am/res·scale
|
||
output = conv(audio, FIR(mask))
|
||
Частотно-направленные каскады IIR1/2/3 в аудио-пути ОТСУТСТВУЮТ (они и создавали
|
||
фантомное смазывание нотча и бланкет-пол). kIIR_A1==A_ATTACK, kIIR_A2==A_RELEASE —
|
||
это время-энвелоп коэффициенты (feed-forward атака, retention релиз), НЕ частотные.
|
||
|
||
### Остаточный разрыв ТОНА: −15.13 vs −20.72 (нужен lvl 3.4436, есть 3.0117)
|
||
Фактор ×1.1437 в амплитуде. Кандидаты:
|
||
(a) am-нормировка (wsum/окно) отличается;
|
||
(b) res_k twin АДАПТИРУЕТСЯ во времени (tatt=11ms/trel=80ms из NOTES_TWIN!) —
|
||
при длинном тоне res_k уменьшается → lvl растёт;
|
||
(c) scale_factor формула (наш 15·440.95/2048=3.231).
|
||
Мелочи: модель глубже у центра полосы на шуме (−2.59 vs −1.04 @678); у real
|
||
HF-rolloff −1.5@8k (вероятно WIN_freq окно FFT-conv — таблица [01] registry!).
|
||
|
||
### NEXT
|
||
1. Декомп амплитудной цепочки: wsum/am нормировка (spectral.cpp), масштаб окна.
|
||
2. Twin-адаптация: проверить динамику res_k по длине тона (рендеры 0.5/2/8 c).
|
||
3. WIN_freq rolloff: добавить окно FFT-conv в модель (registry [01] 0.5→1.0).
|
||
|
||
## ============ UPDATE 2026-08-22l: POOL+SCALE+FLOOR — тон точен до 0.02 дБ, корпус 5.9 ============
|
||
|
||
Новая архитектура (все за env-флагами, канон не тронут — санити TOTAL 2.286 ✓):
|
||
RT_LUT_OFF=1 RT_IIR12=0 RT_NOWARP=1 RT_NOBLEND=1 RT_NOIIR3=1
|
||
RT_POOL=5 RT_SCALE_M=1.144 RT_FLOOR=1
|
||
lvl_in = pool_max(lvl_raw, ±5 бин) · 1.144; cap = (16.78+sens/3)/6.0174
|
||
mask = exp2(−min(lvl_in,cap))
|
||
|
||
Результаты:
|
||
- TONE floor: **−20.74 dB vs real −20.72** (Δ0.02!) при pool∈{4,5}, scale 1.144.
|
||
- White noise: mean|err| 0.63 dB (форма плоская ✓), но перерез центра полосы
|
||
(−3.54 vs −1.04 @678).
|
||
- Корпус 62: TOTAL **5.905** (t1k 60→7.2 благодаря полу; t1kq 5.57 — тихие случаи
|
||
теперь перерезаются scale/pool). Пока ХУЖЕ канона 2.286/bridge 1.594.
|
||
|
||
### Интерпретация
|
||
Архитектурно верно (пол/пин/форма тона), но константы (pool width, scale 1.144,
|
||
cap-закон) сняты с ОДНОГО случая (hot tone trim+12) и не обобщаются на тихие.
|
||
Масштаб 1.144 требует источника в декомпе (золотое правило) — кандидаты: wsum
|
||
(sqrt-Hann vs др.), формула scale_factor, sens внутри twin.
|
||
|
||
### NEXT
|
||
1. Систематический фит (pool_w, scale, cap-параметры a/b в (a+b·sens)) по МИНИМИЗАЦИИ
|
||
corpus TOTAL — офлайн через render48k прогонки (быстро, ~2 мин/конфиг).
|
||
2. Источник ×1.144: декоп wsum/am нормировки детектора (spectral.cpp vs decomp).
|
||
3. Разобраться с перерезом центра полосы на шуме (pool слишком широкий для шума?
|
||
может, pooling по МАКСИМУМУ неверно — попробовать contrast-vs-pool комбинацию).
|
||
|
||
## ============ UPDATE 2026-08-22m: ГИПОТЕЗЫ ПОЛЬЗОВАТЕЛЯ ПРОАНАЛИЗИРОВАНЫ (E1/E3/E4/E5) ============
|
||
|
||
Гипотезы пользователя: (1) BandConfig влияет на детектор; (2) a/b = слоты состояний
|
||
плагина; (3) gamma = дельта-режим.
|
||
|
||
### E1 — delta on/off: γ НЕ дельта
|
||
Захват с delta=1.0 vs база: конфиги идентичны (−24/28/γ=1 flag=0 cb=set). γ не кодирует
|
||
дельта-режим (тот «пересекает линию» только в отрисовке).
|
||
|
||
### E3 — depth ±18: BandConfig стабильна
|
||
depth UI −18 и +18 (мост): те же −24/28/1 и 16/20000/1. Вместе с E1 и 7 пресетами —
|
||
структуры НЕ реагируют ни на один параметр обработки ⇒ консистентно с ролью
|
||
конфигурации отображения кривой (диапазон графика −24..+28 dB / 16..20000 Hz!).
|
||
|
||
### E4 — callgraph расширен
|
||
FUN_180529610 (SpectralProcessor-метод, 1154B) дизассемблирован: НОЛЬ ссылок на объект
|
||
level-path и на ctx-буферы 540678/5406f8/5407c8/540870; callee — мелкие функции
|
||
(52e190×5, 52e130, 52df60, 52dc30). Grep всех .dis по оффсетам объекта: совпадения
|
||
только в GUI-кластере (563440/563a60) + 0x540678 (ctx-буфер аудио, другой объект).
|
||
Оговорка: полный pointer-flow анализ требует Ghidra-сессии; grep по готовым .dis
|
||
покрывает ~140 функций.
|
||
|
||
### E5 — heap-diff межсессионный: неинформативен
|
||
Heap layout плывёт между запусками (базы сдвигаются, соседние аллокации чужие) —
|
||
451/512 блоков «различаются» мусором. Для честного diff нужен same-session capture
|
||
с переключением входа между снапшотами.
|
||
|
||
### Статус гипотез
|
||
1. BandConfig→детектор: подтверждений нет (E1/E3/E4), но полная верификация указателей
|
||
не проведена (Ghidra data-flow). Приоритет низкий.
|
||
2. Слоты A/B: существуют (manual, AorBStateOption), наши структуры им не соответствуют
|
||
(идентичны во всех захватах в слоте «a»). Тест со слотом «b» отложен (нужен ручной
|
||
клик или ReaScript-команда переключения).
|
||
3. γ=delta: ОПРОВЕРГНУТА (E1).
|
||
|
||
## ============ UPDATE 2026-08-22n: LIVE CTX НАЙДЕН + МЕДЛЕННАЯ АДАПТАЦИЯ ДЕТЕКТОРА ============
|
||
|
||
### E2-lite (без пользователя): sidechain/L-R/lowcut/eco — конфиги опять идентичны
|
||
Итого 9 захватов на широкой поверхности параметров: структуры +0x168/+0x170 статичны
|
||
всегда ⇒ окончательно display-config.
|
||
|
||
### LIVE CTX НАЙДЕН В ТЕКУЩИХ СНАПШОТАХ (ctx≈0x2370048!)
|
||
Анкор: паттерн kIIR_A2[1..3] в куче → ctx = hit − 0x2c04f8. Совпало со старой сессией
|
||
(0x2370040±8). Живые скаляры (t1kq-пресет):
|
||
int@1a0 = 4096 (=NFFT, не 2048!)
|
||
f32@870 = 1.0000 (НЕ 440.95 — то было значение другого пресета render_long!)
|
||
f32@878 = 25.0 f32@87c = 10.0 (=UI sharpness/selectivity!)
|
||
f32@888 = 0.0 f32@88c = 1200.0
|
||
⇒ Скаляры КОНФИГОЗАВИСИМЫ. Наш хардкод scale_factor=15·440.95/2048 калиброван под
|
||
чужой пресет и неверен для t1kq-корпуса! Формула масштаба требует перечтения
|
||
mangled-блока декомпа (Ghidra headless re-run) с живыми значениями.
|
||
|
||
### МЕДЛЕННАЯ АДАПТАЦИЯ ДЕТЕКТОРА (главное!)
|
||
Короткий тон 0.75с (тот же сатурированный конфиг): gain во времени:
|
||
[0.02-0.35s]: −10.32 dB [0.35-0.72s]: −10.98 dB
|
||
Длинный тон 6с: −20.72 dB (пин).
|
||
⇒ Редукция УГЛУБЛЯЕТСЯ СЕКУНДАМИ. Постоянная времени ~секунды соответствует
|
||
kRTRel≈0.96..1.0 retention на кадр (hop 1024@48k → τ≈2с)!
|
||
Следствия:
|
||
1. «Статический» ×1.144 калибровался на несошедшемся состоянии длинного рендера —
|
||
делить нужно на динамику, не на константу.
|
||
2. Короткие рендеры корпуса меряют НЕнашедшееся состояние → часть ошибок корпуса
|
||
может быть фазой адаптации, а не формой кривой!
|
||
3. Модель обязана интегрировать медленный релиз через кадры (RT_IIR12=3 каркас есть,
|
||
но нужен настоящий покадровый цикл с персистентным env и правильной FRAMES-rate).
|
||
|
||
### NEXT
|
||
1. Реализовать покадровую персистентность env в render48k (env_time уже static —
|
||
проверить, что реально тянется между вызовами process_band_structural).
|
||
2. Откалибровать att/rel семантику по переходным кривым (short-tone транзиент
|
||
−10.3→−20.7 за ~секунды — идеальный тестовый сигнал для фита kRTAtt/kRTRel).
|
||
3. Перечитать mangled scale-блок декомпа с живыми 1a0=4096/870=1.0.
|
||
|
||
## ============ UPDATE 2026-08-22o: RT_ENV=live + корпусные прогоны новой архитектуры ============
|
||
|
||
RT_ENV=live (детекторный энвелоп из kRTAtt/kRTRel: атака feed-forward, релиз retention
|
||
~tau2s) добавлен в FramedDetector::processFrame за флагом.
|
||
- Белый шум: mean|err| улучшился 0.632 → **0.461** ✓
|
||
- Корпус: НЕЙТРАЛЕН (метрика меряет сошедшиеся окна — энвелоп сходится и там).
|
||
|
||
Корпусные варианты новой архитектуры (все с RT_LUT_OFF/IIR12=0/NOWARP/NOBLEND/NOIIR3/FLOOR):
|
||
| конфиг | TOTAL | примечание |
|
||
|--------|-------|------------|
|
||
| pool=5 scale=1.144 | 5.905 | тон точен, шум-центр перерезан |
|
||
| pool=0 scale=1.144 | **5.386** | t1kq 5.574→3.621, res хуже 1.713 |
|
||
| канон (без env) | 2.286 | HEAD, не тронут |
|
||
|
||
Вывод: архитектурные находки верны на пробниках (тон-пол точен, шум плоский), но
|
||
константы (scale 1.144 снят с одного случая) не обобщаются на корпус ⇒ нужен
|
||
систематический joint-фит (pool_w × scale × cap-law) минимизацией TOTAL, плюс
|
||
поиск источника масштаба в декомпе (золотое правило). Канон остаётся HEAD до
|
||
превышения гейта.
|
||
|
||
### NEXT (приоритеты)
|
||
1. Joint-фит сеткой: pool_w {0,3,5} x scale {1.05,1.10,1.144,1.20} x cap{on,off}
|
||
(~12 прогонов корпуса по ~2 мин).
|
||
2. Декомп амплитудной нормировки: wsum/am (spectral.cpp vs детекторный путь),
|
||
источник scale (int@1a0=4096, f32@870=1.0 живые!).
|
||
3. Транзиентный калибратор: короткий тон некорректен из-за латенси/WOLA коротких
|
||
рендеров — заменить на длинный файл со СТУПЕНЬЮ уровня внутри.
|
||
|
||
## ============ UPDATE 2026-08-22p: JOINT-FIT ГРИД — оптимум scale 0.90, пол обязателен ============
|
||
|
||
Сетка (scripts/gridfit22p.py): pool×scale×floor по корпусу (62 случая).
|
||
|
||
### Результаты
|
||
| scale | floor | TOTAL | t1kq | t1k | al | res | dual | comb |
|
||
|-------|-------|-------|------|-----|----|----|------|------|
|
||
| 0.90 | 1 | **5.215** | 1.961 | 5.801 | 5.328 | 2.663 | 7.774 | **2.707** |
|
||
| 0.95 | 1 | 5.242 | 2.255 | 5.980 | 5.541 | 2.432 | 7.699 | 2.811 |
|
||
| 1.00 | 1 | 5.261 | 2.547 | 6.155 | 5.704 | 2.201 | 7.621 | 2.915 |
|
||
| 1.05 | 1 | 5.298 | 2.892 | 6.279 | 5.842 | 2.008 | 7.588 | 3.019 |
|
||
| 1.144 | 1 | 5.386 | 3.621 | 6.456 | 6.123 | 1.713 | 7.542 | 3.213 |
|
||
| 0.95 | 0 | 11.021 | — | **22.5** | 10.4 | — | 14.4 | — |
|
||
| 1.00 | 0 | 11.396 | — | **23.4** | 11.1 | — | 14.8 | — |
|
||
|
||
### Выводы
|
||
1. **FLOOR ОБЯЗАТЕЛЕН**: без него громкие случаи уходят в тишину (t1k 60→22 дБ ошибок,
|
||
dual ×2). Кап детекторного уровня — реальный механизм плагина, подтверждён корпусом.
|
||
2. Оптимум scale≈0.90 (тренд вышел на плато 5.2-5.3); pool=0 лучше (пул перерезает
|
||
центр полосы на широкополосном материале).
|
||
3. **comb впервые лучше канона** (2.707 vs 6.116) — мультиполосный случай выигрывает от
|
||
новой архитектуры.
|
||
4. Канон 2.286 всё ещё лучше по TOTAL: однополосные тихие случаи (t1kq/al) в новой
|
||
архитектуре перерезаются (scale-закон глобальный, а нужен уровнезависимый).
|
||
dual ~7.7 не двигается ни одним параметром — его причина не в уровне/форме кривой.
|
||
|
||
### Статус
|
||
Канон остаётся HEAD (гейт «не хуже bridge» не пройден: 5.2 > 1.594). Новая архитектура
|
||
живёт за env-флагами: RT_LUT_OFF=1 RT_IIR12=0 RT_NOWARP=1 RT_NOBLEND=1 RT_NOIIR3=1
|
||
RT_POOL=0 RT_SCALE_M=0.90 RT_FLOOR=1 RT_ENV=live → TOTAL 5.215.
|
||
|
||
### NEXT сессии
|
||
1. dual@500: константная ошибка при всех q/уровнях — исследовать отдельно (возможно
|
||
межполосный combine-консюмер или второй тон 2000Hz взаимодействует).
|
||
2. Уровнезависимость закона: scale-множитель должен зависеть от уровня сигнала
|
||
(компрессия в детекторе?) — ступенчатый калибратор внутри файла.
|
||
3. comb-выигрыш закрепить: понять, почему мультиполосность выигрывает (перенос в канон?).
|
||
|
||
## ============ UPDATE 2026-08-22q: DUAL ДИАГНОСТИКА — насыщающая кривая, не контраст ============
|
||
|
||
Кейс dual q=0.5 (тона 500+2000, оба −7.2 dBFS), trакт@frame150:
|
||
| тон | bin | am | res | наш lvl | нужен (real) |
|
||
|-----|-----|----|----|---------|--------------|
|
||
| 500 (центр) | 43 | 0.321 | **0.117** (res_min) | 8.85 | 1.70 |
|
||
| 2000 (вне) | 171 | 0.321 | 0.839 | 1.24 | 1.95 |
|
||
|
||
GAIN'ы: real @500=−10.24 @2000=−11.75 (почти РАВНЫ!); newarch @500 упёрся в кап
|
||
(−20.4), @2000 недорез (+5.5); canon: @500 перерез −6.9, @2000 точен (−12.4≈−11.75).
|
||
|
||
### ВЫВОДЫ
|
||
1. Деление am/res(twin) создаёт контраст ×5 между центром и краем — реальный плагин
|
||
такого контраста НЕ имеет: режет оба тона поровну при q=0.5. Twin-делитель в роли
|
||
полного делителя lvl ОПРОВЕРГНУТ для аудио-выхода.
|
||
2. canon согласуется с real на ВНЕполосном тоне (res^rp мягкий вес ✓) — форма
|
||
«кривая от уровня + мягкий вес res» ближе к истине, чем «жёсткое деление».
|
||
3. Мануал: «до 60 dB нотчей» при выкрученном depth + наш пин −20.7 при перегрузе
|
||
и номинальные −10 дБ здесь ⇒ ГИПОТЕЗА: cut_dB = насыщающая функция уровня
|
||
(например A·(1−exp(−lvl/T)) или soft-knee), а не линейный exp2(−lvl) с капом.
|
||
4. Уровни: real нужен lvl 1.70 там, где наш 8.85 — часть разрыва всё ещё
|
||
амплитудная нормировка (int@1a0=4096 и др.).
|
||
|
||
### NEXT
|
||
1. Собрать пары (наш lvl_raw, real cut_dB) по ВСЕМ корпусным случаям (62×бины тонов)
|
||
→ фит насыщающей кривой cut=f(lvl) — единый закон вместо подгонки параметров.
|
||
2. Проверить форму: линейно-лог / экспоненциальное насыщение / soft-knee.
|
||
3. Роль res: сравнить cut@center vs cut@skirt при ОДИНАКОВОМ lvl — отделить вес res
|
||
от кривой уровня.
|
||
|
||
## ============ UPDATE 2026-08-22r: АФФИННЫЙ ЗАКОН ПОРТИРОВАН — НОВАЯ АРХИТЕКТУРА БЬЁТ КАНОН ============
|
||
|
||
RT_LAWAFFINE="A_db,S_db": mask = exp2(−(A_db + S_db·log2(lvl))/6.0174) поверх голой цепи
|
||
(NOTES 22k). Константы с al-семейства (грубо): A=6.14/S=2.17 → **TOTAL 2.152 < канона 2.286!**
|
||
|
||
### Грид-фит
|
||
| A | S | TOTAL | t1kq | t1k | al | res | dual | comb |
|
||
|---|---|-------|------|-----|----|----|------|------|
|
||
| 6.14 | 2.17 | 2.152 | 0.426 | 0.889 | 0.090 | 1.795 | 3.953 | 2.678 |
|
||
| 6.60 | 2.00 | 2.047 | 0.409 | 0.930 | 0.294 | 1.262 | 3.702 | 3.349 |
|
||
| **7.40** | **1.85** | **1.931** | 0.852 | 0.577 | 0.804 | **0.395** | 3.480 | 4.335 |
|
||
|
||
Трейд-оффы: A↑ → res ↓↓ но comb ↑; S↓ → t1k ↓ но al ↑.
|
||
Против bridge (1.594): новая арх. лучше на t1k (0.58<1.80), al (0.80<0.64~паритет),
|
||
comb (4.3<<10.1); хуже на t1kq/res/dual. Против канона (2.286) — лучше по TOTAL и
|
||
на 4 группах из 6.
|
||
|
||
### Смысл
|
||
Аффинный закон Phase-A (21d) пересажен на ПРАВИЛЬНУЮ архитектуру (голый exp2 без
|
||
blend/warp/частотных каскадов) — и сразу даёт лучший корпусный результат среди всех
|
||
конфигураций проекта. Двух констант достаточно; их источник — следующий вопрос
|
||
(золотое правило: сейчас это фит, помечено).
|
||
|
||
### NEXT
|
||
1. Фит A/S раздельно по группам → понять зависимость от sens/q (сейчас глобальные).
|
||
2. dual ~3.5 стабилен — межполосный механизм (приоритет из 22p).
|
||
3. WIN_freq HF rolloff не добавлен — после добавления comb может улучшиться ещё.
|
||
4. При достижении ≤bridge: гейт пройден, переносить в канон.
|
||
|
||
## ============ UPDATE 2026-08-23a (22s): ОФЛАЙН-ФИТ A/S — СКАЛЯРНЫЙ ЗАКОН НАСЫТИЛСЯ ============
|
||
|
||
Инструментарий: `scripts/lawfit22r.py` — полный numpy-реплей render48k/spectral
|
||
(ресемпл 44.1<->48 poly, Hann STFT 4096/1024@48k, поточечная маска, WOLA,
|
||
FULL-BLK чанкинг = 61 хоп-фрейм на блок 65536 с zero-pad хвоста — именно так
|
||
вызывается processBlock в render48k.cpp:137, поэтому кадров 61·nblk, НЕ (L−N)/hop+1).
|
||
Детектор независим от закона ⇒ один захват RT_DUMP_ALL траекторий lvl[frame][band][bin]
|
||
(`/tmp/opencode/lawfit_traj.npz`, 47 уникальных рендеров) обслуживает ЛЮБОЙ (A,S) офлайн.
|
||
Скорость: весь корпус ~2 c/точку (против ~2 мин на реальный рендер).
|
||
|
||
### Валидация реплея (A=7.4,S=1.85 против реального corpus 1.931)
|
||
sim TOTAL 1.895; группы: al +0.045 / dual +0.013 / res −0.085 — отлично;
|
||
t1kq −0.30 / t1k +0.32 / comb −0.64 — СТАТИЧЕСКИЕ смещения (риппл ресемплера
|
||
scipy≠libsamplerate вокруг крутых спектральных форм; у al полоса на тоне — плоско,
|
||
дельта нулевая). Смещение не зависит от закона ⇒ рейтинг кандидатов корректен.
|
||
|
||
### ПОЧЕМУ точечная модель запрещена (важно для будущих сессий)
|
||
1. Тон 1000 Гц = бин 85.33 (не целый!) + главный лепесток Hann ±2-3 бина: при крутой
|
||
юбке реализованный гейн тона ≠ mask[tone_bin] (res_500: mask[85]=+8.4 дБ БУСТ,
|
||
рендер режет −7.2 дБ).
|
||
2. Детектор НЕстационарен в метрическом окне: resonant.wav lvl[85@bin] падает
|
||
0.86→0.0002 за файл (медленная адаптация резонатора, 22n) при ПОСТОЯННОМ rms входа.
|
||
Медиана по хвосту смешивает 2 декады ⇒ мусор.
|
||
|
||
### Метод линий (бисекция A* при S∈{0.5,1.5,2.5} на реплее)
|
||
У однотонового рендера одно уравнение cut=A+S·x ⇒ вырожденная ЛИНИЯ в (A,S);
|
||
x_i=−dA*/dS — точная эффективная координата кейса (включая транзиенты и лики).
|
||
Результаты (`lawfit_lines.json`):
|
||
- x_eff ОТОБРАЖАЕТ уровень входа ТОЧНО: al-свитч Δx=−3.48 на Δ21 дБ = −1/6.02 окт/дБ
|
||
⇒ детектор линеен по амплитуде (подтверждение масштабной цепи).
|
||
- НО линии семейств НЕ пересекаются: локальные наклоны cut(x): t1kq 0.85 /
|
||
t1k 1.54 / res 1.65 / al 2.19. Единого аффинного закона НЕТ даже на чистых тонах
|
||
⇒ кривизна/контент-геометрия, скалярная пара (A,S) принципиально недостаточна.
|
||
- Глобальный LSQ по 35 линиям: A=7.396 S=1.973, остаток mean 0.600 дБ,
|
||
corr(resid,rms)=+0.71, corr(resid,|log2 fc/1k|)=+0.69 — два недостающих фактора.
|
||
- dual: пер-рендерный оптимум даёт floor 0.52-0.62 при q≥1.5 даже своим (A,S) ⇒
|
||
межполосный разрыв НЕ закрывается никакими константами закона (приоритет 22p стоит).
|
||
- comb (4 полосы): оптимум mean|e|=1.28, константы нестабильны (угол сетки).
|
||
|
||
### Реальные рендеры (валидация кандидатов, гейт --vs-bridge не гонялся — канон не меняем)
|
||
| конфиг | t1kq | t1k | al | res | dual | comb | TOTAL |
|
||
|--------|------|-----|----|-----|------|------|-------|
|
||
| HEAD 7.4,1.85 | 0.852 | 0.577 | 0.804 | 0.395 | 3.480 | 4.335 | **1.931** |
|
||
| LSQ-линии 7.4,1.97 | 0.926 | 0.430 | 0.986 | 0.455 | 3.660 | 4.120 | 1.988 |
|
||
| спуск 7.6,1.6 | 0.894 | 1.123 | 0.750 | **0.069** | **3.105** | 5.053 | 1.894 |
|
||
|
||
Вывод: скалярный закон насыщен в коридоре 1.87-1.93; (7.6,1.6) даёт res 0.069 (!),
|
||
но регрессирует t1k/comb — Канон ОСТАЁТСЯ HEAD (7.4,1.85). Теоретический потолок
|
||
пер-групповых оптимумов ~0.78 TOTAL недостижим одной парой констант.
|
||
|
||
### NEXT
|
||
1. Двухфакторный закон cut=A+S·x+T·g(fc-дистанция/контент): корреляции указывают
|
||
на геометрию полосы относительно тона; данные линий уже собраны (lawfit_lines.json).
|
||
2. Механизм межполосного консюмера для dual (Шаг 2/9, приоритет 22p) — константами
|
||
не решается, подтверждено покейсовой бисекцией.
|
||
3. Реплей-инфраструктура готова для быстрого перебора ЛЮБЫХ параметрических законов
|
||
(2 c корпус) — использовать вместо реальных рендеров до финальной валидации.
|
||
|
||
## ============ UPDATE 2026-08-23b (22t): ДВУХФАКТОРНЫЕ ЗАКОНЫ ОТВЕРГНУТЫ — БЮДЖЕТ ОШИБКИ В DUAL ============
|
||
|
||
Инструменты расширены:
|
||
- `RT_DUMPRESPATH=<file>` в FramedDetector::setParams — статические спектры twin-response
|
||
res[band][bin] ({i32 band,i32 nbin,f32[]}, append); lawfit collect кладёт их в npz рядом с lvl.
|
||
- `RT_RESPRP=1` в process_band_structural — применять res^rp при NOWARP (для реальных
|
||
рендеров двухфакторного закона).
|
||
- `lawfit22r.py fit`: координатный спуск по семействам законов на реплее (2 c/корпус).
|
||
|
||
### Результат фит-семейств (офлайн, старт 7.6/1.6)
|
||
| семейство | A | S | Q | rp | TOTAL(sim) |
|
||
|-----------|---|---|---|----|-----------|
|
||
| scalar | 7.600 | 1.694 | 0 | 0 | **1.860** |
|
||
| quad (Q·x²) | →scalar | | **→0** | | 1.860 |
|
||
| resrp (mask·res^rp) | →scalar | | | **→0** | 1.860 |
|
||
| quad+resrp | 7.452 | 1.540 | +0.069 | 0 | 1.865 |
|
||
|
||
ОБА вторых фактора отброшены спуском: кривизна не нужна, res-вес не нужен.
|
||
Объяснение resrp: lvl = am/res УЖЕ содержит геометрию твин-юбки; домножение на
|
||
res^rp сверху — двойной учёт (в каноне res^rp компенсировал другое — отсутствие
|
||
res в xv-домене LUT).
|
||
|
||
### Реальный рендер скалярного оптимума (7.6, 1.694)
|
||
t1kq 0.954 / t1k 0.842 / al 0.799 / res **0.117** / dual **3.246** / comb 4.855
|
||
⇒ TOTAL **1.898** (sim 1.860 + стат. смещения реплея ~0.04). Против HEAD 1.931:
|
||
выигрыш мизерный, t1kq/t1k/comb РЕГРЕССИРУЮТ ⇒ гейт не пройден, канон НЕ тронут.
|
||
|
||
### Бюджет ошибки — где лежит остаток (решающий факт)
|
||
abs-сумма по корпусу при (7.6,1.694): 115.4 дБ·кейсов, из них:
|
||
- **dual: 22 кейса × 3.246 = 71.4 (62%!)** — межполосный механизм (Шаг 2/9, консюмер acc/f6f8);
|
||
- comb: 24.3 (21%) — мультиполосное взаимодействие;
|
||
- всё прочее: ~20 (17%).
|
||
Если бы dual был на уровне bridge (0.726), TOTAL упал бы до ~0.96. Никакая
|
||
донастройка закона это не даст — ПРИОРИТЕТ №1 подтверждён количественно:
|
||
искать точку потребления acc/f6f8 (межполосный каскад), а не крутить маску.
|
||
|
||
### NEXT
|
||
1. dual/межполосный консюмер: декомп-охота вокруг FUN_180529fe0 callers
|
||
(52e260 render-loop, multiband combine), трассировка acc/f6f8 в живом плагине.
|
||
2. comb: после dual — пере-тест 4-полосного взаимодействия.
|
||
3. Реплей-инфра готова; для любых новых гипотез сначала офлайн (2 c), потом рендер.
|
||
|
||
## ============ UPDATE 2026-08-23c (22u): РЕАЛЬНАЯ СТРУКТУРА 529fe0 ПО RAW ASM — ПЕРЕСМОТР ЦЕПИ ============
|
||
|
||
Охота за консюмером acc/f6f8 по всем дизасмам/декомпам. ГЛАВНОЕ: наша транскрипция
|
||
цепи расходится с тем, что метод реально делает.
|
||
|
||
### Новые декоды (ground truth из handoff/nls_dasm/f529fe0.dis, 505 строк)
|
||
1. **FUN_18052d650 — БИДИР-ИРРАЦИОННЫЙ ФИЛЬТР В DOUBLE** (полный декод):
|
||
сигнатура (state=rcx, out=rdx/f6f8, in=r8/band, n); acc=[rcx+0x100010]=0 RESET;
|
||
forward: acc = upCoef[i]·acc + downCoef[i]·band[i], запись в f6f8[i]
|
||
(upCoef double @state+0x80010+i·8, downCoef @state+0x10+i·8 — ПЕР-БИНОВЫЕ массивы);
|
||
backward: продолжение с ТЕМ ЖЕ state по nbin−2..1. Это наш «IIR3», но вход band,
|
||
выход f6f8, и коэффициенты пер-биновые double, не табличные float.
|
||
2. **ВТОРОЙ инлайн бидир-проход в теле 529fe0** (52a65d..52a85b): вход f6f8, выход
|
||
ОБРАТНО в band[i]. Итого пара [reset,fwd,bwd]×2 — как в заметках, но точные
|
||
адреса/направления теперь залочены.
|
||
3. **Межполосное ПРЕ-КОМБИНИРОВАНИЕ ВНУТРИ 529fe0** (52a39a..52a4f6, ДО основного
|
||
цикла): f6f8 ← copy bands[0]; цикл по bands[1..n−1] (массив @0x540688 = соседний
|
||
элемент того же массива векторов!): поэлементные thunks (float 0x20f0/double
|
||
0x1850 пары = шаблонный transform); нормировка делением на int [rsp+0x148];
|
||
затем второй проход: band[i] *= (xmm11−xmm7), xmm7=pow([ctx+0x2c],xmm13)
|
||
(mix-вес!), и f6f8 ⊕= ... Семантика thunks ещё не залочена.
|
||
4. Основной пер-полосный цикл: scale (=PRNG·[0x540870]·[0x54088c], флаг 0x5408b8
|
||
добавляет exp-random) → in-place кернел (thunks 0x140980/0x1409b0, вероятно
|
||
bigkernel exp2) → 52d650 (bidi#1) → инлайн bidi#2.
|
||
5. Пер-полосный массив состояния **0x540768[i]** читается в основном цикле
|
||
([rsp+0x40]) — роль не ясна (кандидат: трек/acc).
|
||
6. **FUN_180529c60** (vtbl, параметр = индекс полосы): band←5355d0(param_2);
|
||
осцилл-цикл по [ctx+0x1b0] c фильтрацией через f6f8; скалярная нормировка
|
||
band *= exp((0.5·K1−K2)·ln10/20) · **FUN_1804d56b0(band)** (агрегат от всей
|
||
кривой полосы!); релиз-коэф pow(...) от sr/0x1a0·0x1ac; трек-обновление через
|
||
массив **0x5407a8[i]** (52dae0 axpy + 52dbc0 add). Кандидат на «медленную
|
||
адаптацию» (22n) и/или постобработку маски.
|
||
7. **0x5407c8 НЕ читается НИГДЕ в рантайме** (только ctor/dtor в f_535a70.dis) —
|
||
адрес «combine acc» из NOTES 21b был неверен; реальные пер-полосные состояния:
|
||
0x540768[i] (в 529fe0) и 0x5407a8[i] (в 529c60).
|
||
|
||
### Офлайн-проверка гипотезы «спектральное сглаживание маски» (lawfit iir_c)
|
||
Бидир-однополюсник по бинам (нормированный, ×2 прохода) при законе 7.6/1.694:
|
||
c=0.2 → TOTAL 3.23; c=0.6 → 9.83 (монотонная деградация всех групп). ОТВЕРГНУТО:
|
||
сглаживание размывает нотч в центре, но физически не дотягивается до тона-конкурента
|
||
за ~130 бинов. Двойной cut в dual объясняется НЕ межбиновым сглаживанием маски.
|
||
|
||
### Следствие для dual
|
||
Реальный плагин режет 500 и 2000 почти поровну при q=0.5. Раз контраст lvl
|
||
(res-деление) есть у нас и его нет в результате — значит либо (а) детекторный
|
||
уровень считается во ВРЕМЕННОЙ области через тот же twin-фильтр (536300 строит
|
||
res-кривые на сетке 0.25 Гц — а детекция может идти банком time-domain фильтров),
|
||
либо (б) нормировка агрегатом FUN_1804d56b0 выравнивает кривую, либо (в) закон
|
||
не логарифмический по lvl. Приоритет — полный перенос 529fe0 из raw asm (505 строк,
|
||
подъёмно) вместо текущей реконструкции из кусков.
|
||
|
||
### Инструментарий
|
||
lawfit22r.py: +iir_c параметр (бидир-IIR по бинам в реплее), +res в npz;
|
||
sim_errors кэширует по (Q,rp,iir_c).
|
||
|
||
## ============ UPDATE 2026-08-23d (22w): FAITHFUL-ЦЕПЬ v1 — ИНФРАСТРУКТУРА ГОТОВА, ДЕТЕКТОР ГЛАВЕН ============
|
||
|
||
Инфраструктура (dsp/fnfaith.{hpp,cpp}, env RT_FAITHFUL=1 в render48k):
|
||
транскрипция маско-формирующей цепи по BLOCKMAP_529fe0: band=lvl·s → exp2(-band)
|
||
→ бидир-IIR ×2 (setA tau19/mult360) → бидир (setB tau10.7/mult2400) → ×2 (setC).
|
||
Коэффициенты — точная транскрипция FUN_180533340: частотный варп
|
||
fc_bin=(1000/(sr/2))·nbin (=85 @48k/4096!), g=fc/i ниже кросса, степенной выше,
|
||
up=exp(-2π·κ·c·g·tau), down=1-up. Константы из дампа: DAT_24c46b8=-2π,
|
||
24c3d8c=0.5 (sr→Найквист), mult 1200/800/360, тау-интерполяция 15↔9 и 9↔180.
|
||
Все параметры — env (RT_FAITH_*). Combine acc в v1 опущен (нет консюмера, 22u).
|
||
|
||
### Смоук-свипы (t1kq1000 / dual500 / dual2000 / al12; err dB)
|
||
- κ=0.05,s=8: t1kq −25 / dual500 −58 / **dual2000 −2.84** — тон-конкурент ВПЕРВЫЕ
|
||
дотягивается до реала, но центральный тон улетает в перерез.
|
||
- κ=0.2..0.5: насыщение (up→0 на низких бинах), баланса пары нет нигде;
|
||
Δerr(500−2000) ≈ 15-20 дБ во всех точках (в реале ~1.5).
|
||
- Корпус (κ=0.02, s=2): TOTAL 5.315 (t1kq 1.35 / t1k 1.55 / al 0.89 /
|
||
res 2.87 / dual 11.52 / comb **2.57** — лучший comb в истории проекта,
|
||
базлайн 4.34!). Ресурс мультиполосной механики виден.
|
||
|
||
### ВЫВОД (решающий)
|
||
Форма маски НЕ может сбалансировать dual: контраст lvl между тонами создаётся
|
||
САМИМ ДЕТЕКТОРОМ (am/res из STFT), а не пост-обработкой. Реальный детектор
|
||
даёт почти равные уровни драйва на 500 и 2000 Гц при q=0.5 ⇒ гипотеза (а)
|
||
из 22u — ПРИОРИТЕТ №1: уровни считаются во ВРЕМЕННОЙ области (банк twin-
|
||
фильтров 536300 строит res-кривые на сетке 0.25 Гц — вероятно, и уровни тоже
|
||
через них), либо тяжёлый кросс-бин пулинг ДО кривой полосы.
|
||
Смена канона НЕТ; faithful-путь остаётся за флагом для следующих экспериментов.
|
||
|
||
### NEXT
|
||
1. Детектор: декод FUN_180536300 до конца (scratch-лейаут известен, NOTES_TWIN)
|
||
→ понять, откуда берутся bands[] кривые на входе 529fe0.
|
||
2. comb 2.57 при живом faithful — разобраться после детектора.
|
||
3. RT_DUMP_BIN-трасса живого плагина на dual-конфиге (покадрово mask/lvl) —
|
||
прямой ответ на вопрос «какой lvl реально у тона 2000».
|
||
|
||
## ============ UPDATE 2026-08-23e (22x): ТЕОРЕМА О ОТСУТСТВИИ α + ТАБЛИЦА DUAL ПО q ============
|
||
|
||
Инструмент: scripts/resalpha.py — RT_DUMP_BIN-тракты всех уникальных рендеров
|
||
(57 пар case/freq: lvl_a1, res, am на бине тона + реальные cut из рефов),
|
||
CSV /tmp/opencode/resalpha_pairs.csv.
|
||
|
||
### Теорема об отсутствии α
|
||
Модель lvl = am/res^α с ЕДИНЫМ законом cut=L(log2 lvl): имплицитные наклоны
|
||
семей (концевые точки, дБ/октава lvl) при НИКАКОМ α ∈ [−0.6..1] не совпадают:
|
||
- dual требует α≈−0.5 (наклон +0.2..+0.4);
|
||
- при α≤0 t1k/t1kq/res получают БЕСКОНЕЧНЫЙ/отрицательный наклон (весь fc-скан
|
||
схлопывается в точку по x — res перестаёт различать fc);
|
||
- при α>0 dual-пара даёт отрицательный наклон (запрещено для монотонного закона).
|
||
⇒ ВЕСЬ класс точечных моделей lvl=f(am, res^α) с общим законом ОТВЕРГНУТ.
|
||
Глобальный LSQ по α плоский (1.59-1.63 дБ) — семейные смещения доминируют.
|
||
|
||
### Таблица dual по q (реальные cut, одна полоса @500)
|
||
| q | res500 | res2000 | cut@500 | cut@2000 | Δ |
|
||
|---|--------|---------|---------|----------|---|
|
||
| 0.1 | 0.117 | 0.216 | 10.32 | 15.33 | +5.01 |
|
||
| 0.5 | 0.117 | 0.839 | 10.32 | 11.82 | +1.50 |
|
||
| 1.0 | 0.117 | 1.353 | 10.32 | 10.78 | +0.46 |
|
||
| 3.0 | 0.119 | 1.880 | 10.29 | 10.32 | +0.03 |
|
||
| 10 | 0.139 | 1.988 | 9.94 | 10.25 | −0.07 |
|
||
|
||
Структура: cut@500 КОНСТАНТА по q (драйвер центра не зависит от ширины полосы);
|
||
Δcut(res2000) ≈ экспонента 5.4·exp(−res/0.49), схлопывание при res→2.
|
||
cut@2000 ПАДАЕТ с ростом res2000 — противоположно любому «lvl растёт с res»
|
||
и противоположно делению. Геометрическая интерпретация: широкий нотч (малое q)
|
||
НАКРЫВАЕТ тон-конкурент сам по себе; вклад центра всегда полный. Это свойство
|
||
ФОРМЫ twin-шаблона на мелкой сетке (0.25 Гц, FUN_180536300), а не пер-биновой
|
||
арифметики на сетке 4096.
|
||
|
||
### al-группа (якорь уровня, fc=tone=1000)
|
||
lvl_a1: 10.08/7.14/5.05/3.58/1.79/0.90 → cut: 13.64/12.44/11.28/10.13/7.97/6.01.
|
||
Ломаная: наклон ≈1.16 дБ/окт для lvl≥1.8 и ≈2.06 ниже — кривизна закона
|
||
подтверждена независимо (ср. 22s линии).
|
||
|
||
### Следствие
|
||
Детекторный уровень НЕ восстанавливается из спектра бина функционально.
|
||
Кандидаты: (а) скалярная огибающая сигнала через time-domain twin-банк
|
||
(536300 строит res-кривые на сетке 0.25 Гц — возможно, тот же банк даёт и
|
||
уровни), (б) форма кривой = res-шаблон × скалярный драйв, что естественно
|
||
даёт постоянство центра и геометрическое покрытие скайрта.
|
||
Решающий эксперимент — живая покадровая трасса детектора на dual-конфиге
|
||
(registry-heartbeat инфраструктура готова, NOTES_CAPTURE.md).
|
||
|
||
## ============ UPDATE 2026-08-23f (22z): FFT-conv статика + СПРЕДИНГ ОПРОВЕРГНУТ ============
|
||
|
||
### Статика mask→FIR (52b550–52b8bb, per-band цикл построения FIR)
|
||
Полная последовательность на полосу: scale-ops на кривой → **call 0x180535a70**
|
||
(стаб → ILT → 0x180140a10 float / a70 double → импорт-прыжки → тела 0x1802a24c0
|
||
float / 0x1802fa420 double — векторизованный AVX2, функционально FFT-дизайн) →
|
||
копия в r14=[ctx+0x540668] → complex-op с буферами 0x540548/550/598 →
|
||
FIR[n/2]=0 → bigkernel in-place → ещё complex-op → **WIN_freq умножает сегмент:
|
||
rdx = win_ptr + (n/2)*4** (вторая половина 8193-таблицы!) → масштаб второй половины
|
||
xmm9 → **FIR[0]=1.0, FIR[1]=0** → init пар, dry/wet-scale 0x540888.
|
||
Размерности: n=[ctx+0x540534], половина rbp=n/2, счётчики n/2+1.
|
||
Живой каталог (22y): 0x540758=freqaxis, 0x540658=WIN_freq 0.5→0.8.
|
||
|
||
### Спрединг-гипотеза (IDFT(R)→окно L→DFT) — ЧИСЛЕННО ОТВЕРГНУТА
|
||
На живой кривой dual R(7f8) (v43=3.98, v171=1.41): окна Hann L∈{32..2048} дают
|
||
cut@171 ≤ 2.60 дБ (нужно 11.82); cut@43 падает с 12.0 до 4.4 при малых L.
|
||
Сглаживание НЕ может поднять долину до уровня соседнего пика — механика скайрта
|
||
dual НЕ в пост-обработке кривой R.
|
||
|
||
### Что остаётся (структура данных dual по q, 22x)
|
||
Δcut(скайрт−центр) ≈ линейно по −ln res_скайрта с наклоном b≈2.25 дБ/e-fold,
|
||
НО центр из этого закона выпадает (его cut константен при res≈const). Это
|
||
согласуется с моделью «скалярный драйв через time-domain twin + шаблонная форма»,
|
||
но требует знать, как именно нормируется шаблон. Прямое декодирование тел
|
||
0x1802a24c0 (3КБ AVX2) и complex-op шагов с буферами 0x540548/550/598 — следующий
|
||
статический шаг; альтернатива — трассировка промежуточных буферов живьём
|
||
(снапшоты в момент аудио-колбека, не между кадрами).
|
||
|
||
## ============ UPDATE 2026-08-23g (23b): ДИЗАЙН=LOG(band), Hann-окно, PRNG-дизеринг ============
|
||
|
||
### Что доказано декодом (детали в BLOCKMAP 23b)
|
||
1. Полная цепь построения FIR на полосу залочена по raw asm (52b550–52b8bb):
|
||
scalar-shape ×2 → **DESIGN: scratch=log(bands[i])** (векторный log2, полином
|
||
идентифицирован по константам, тело 0x1802a24c0) → copy→FIR@0x540668 →
|
||
4 комплекс-op (A..D) на буферах 0x540548(SIMD/twiddle-workspace)/0x540598
|
||
(YMM-маски 8×1/8×0×64) вокруг нормировки &FIR[1]÷K и bigkernel →
|
||
**FIR[2048..4095] ×= Hann_freq[2048..4095]** (падающая половина периодического
|
||
Hann(4096), пик ровно на стыке!) → FIR[0]=1,FIR[1]=0 → dry/wet над 2n флоатами.
|
||
Структура log→ops→exp-класс + окно = классический frequency-sampling /
|
||
минимально-фазовый дизайн кернела. Гипотеза механизма скайрта: геометрия
|
||
задаётся ОКНОМ и комплекс-ops на log-спектре, а не постобработкой R.
|
||
2. WIN_freq опознан численно (w[1024]=0.5,w[2048]=1.0) — прежняя трактовка
|
||
«0.5→0.8» относилась к другому слоту (0x540698).
|
||
3. freqaxis: шаг 11.71303 vs точные 11.71875 — накопительная f32-ошибка построения;
|
||
сетка внутренняя 48k/4096 подтверждена независимо.
|
||
4. Выход плагина недетерминирован от рендера к рендеру (md5 разные, rms равные) —
|
||
LCG-прологи реально дизерят; честная метрика только спектральная.
|
||
|
||
### Динамика: почему живые кривые «статичны»
|
||
Realtime-playback без аудио-девайса НЕ вызывает processing (все треды спят);
|
||
кривые 22y — продукт инициализации проекта. `-renderproject` прожимает весь
|
||
цикл за ~0.8 c (init+render+exit); перехват требует SEIZE+TRACECLONE ДО первого
|
||
CONT главного треда. FUN_180529fe0/52d650/536300/52e260 не ловятся в рендер-окне
|
||
⇒ маск-цепь выполняется при загрузке/param-change, рендер использует кэш.
|
||
Следующий динамический шаг (если понадобится): bp на входе process-callback vtbl
|
||
или трасса окна init (поймать DESIGN в момент param-change).
|
||
|
||
### Инструментарий этого раунда (закommичено)
|
||
scripts/ilt_resolve.py (резолв стабов офлайн), scripts/firtrace.py (полный каталог
|
||
буферов + GUI-флаг 0x2404bc), scripts/firstop.py (SIGSTOP-семплирование),
|
||
fntrace*.py/fnhw.py/fnall.py (ptrace-лаборатория: INT3/DR, уроки — см. BLOCKMAP),
|
||
hotips.py. .dis тела: conv_float_a24c0, conv_double_fa420, imp_*, th_* (19 impl).
|
||
|
||
### Контрольные числа dual (цель раунда, неизменны)
|
||
cut@500=10.32 дБ const ∀q; @2000=11.82 @q=0.5; Δcut≈2.25·(−ln res).
|
||
Модель сейчас: −0.18/@500-ок + провал скайрта.
|
||
|
||
### NEXT (пересмотр после декода)
|
||
1. numpy-реплика цепи log→opsA-D→window с КАЛИБРОВКОЙ ops по измеренным H(f)
|
||
(корпус референсов уже есть); критерий — воспроизведение Δcut(res) геометрии.
|
||
2. Если ops A-D ≈ exp-раундтрип со сглаживанием: транскрипция за RT_FIRCONV,
|
||
гейт corpus --vs-bridge + контрольные числа dual.
|
||
3. ptrace-эскалация ТОЛЬКО если численная репликация не сойдётся: цель —
|
||
брейкпоинт в момент param-change (init-окно хоста), а не рендер.
|
||
|
||
### 23c (короткое): феасибилити log-свёртки — ОТРИЦАТЕЛЬНЫЙ результат
|
||
На живой кривой R@0x5407f8 (dual q=0.5): подбор ОПТИМАЛЬНОЙ симметричной
|
||
гаусс/бокс-свёртки в log-домене (ширина до 1500 бинов, свободный аффинный сдвиг,
|
||
точное совпадение @bin43=10.32 дБ) даёт минимум ошибки на bin171 ≈ 0.94–1.10 дБ
|
||
при ширине ~314–324 бина (~3.7 кГц) — такая ширина уничтожает избирательность
|
||
нотча на всём остальном корпусе. Вывод: механизмы класса «свёртка log-маски с
|
||
симметричным ядром» геометрию dual НЕ воспроизводят (расширяет теорему 22x с
|
||
поточечных законов на симметрично-свёрточные). Остаются: (а) вход bands[i] у
|
||
DESIGN ≠ захваченные кривые (768/788/7f8 — возможно, GUI/init-версии);
|
||
(б) асимметричные/структурные ops (cepstral-liftering класс); (в) темпоральный
|
||
кросс-фрейм механизм. Решающий недостающий факт — ИСТИННЫЙ массив bands[0] в
|
||
момент вызова DESIGN; рецепт добычи: seize главного треда yabridge-host в момент
|
||
ПОЯВЛЕНИЯ pid (ppid=reaper, comm ещё 'wine'), INT3 на 0x1802a24c0/0x1802fa420,
|
||
CONT, дамп rcx/rdx на хите (скрипт fnall.py готов, нужен только ранний захват —
|
||
нынешний finder по maps опаздывает на init-бурст <5 мс).
|
||
|
||
## ============ UPDATE 2026-08-23h (23d): ПРОРЫВ — ЖИВОЙ КЕРНЕЛ СНЯТ, bands[] = СПЕКТР СИГНАЛА ============
|
||
|
||
Маршрут A удался БЕЗ ptrace и БЕЗ аудио-девайса: **SIGSTOP-семплирование
|
||
офлайн-рендера** (`-renderproject`; обработка back-to-back ⇒ случайные остановки
|
||
попадают внутрь конвейера). scripts/rendersnap.py: find-loop ctx (vtable-маркер
|
||
0x1824AC210 / float 48000+ sens; ctx=0x2370040 стабилен и в рендере),
|
||
затем STOP→читаем все слоты→CONT c детекцией изменений. 24 фазы за прогон.
|
||
|
||
### Решающие факты из phase-снапов (dual q=0.5)
|
||
1. **bands[0] @ctx+0x540678 = АМПЛИТУДНЫЙ СПЕКТР ТЕКУЩЕГО ФРЕЙМА**:
|
||
ненулевые группы ТОЛЬКО вокруг тонов (бины 41–45 и 168–173, Hann-лепестки;
|
||
band[43]=7.65, band[171]=9.91), между ними нули. Это вход DESIGN, НЕ шаблон
|
||
маски и НЕ пост-детекторная кривая!
|
||
2. scratch@540628 ≈ log(bands)·k (после ops значения −0.66@43, −0.76@171).
|
||
3. Живой FIR@668 (комплексные пары): mag(43)=0.524 (−5.61 дБ), mag(171)=0.473
|
||
(**−6.51 дБ**) — скайрт режется ГЛУБЖЕ центра, КАК В РЕАЛЕ (+1.5 дБ),
|
||
хотя кривая R@7f8 утверждает обратное (R171=1.41<R43=3.98).
|
||
4. Количественно из снапа: cut≈A+S·log2(am/res) с res(43)=1.0, res(171)=0.839
|
||
(twin-шаблон из таблицы 22x!) даёт согласованную пару — **структура
|
||
am/res ПОДТВЕРЖДЕНА, но входом служит сырой спектр сигнала**, а res-шаблон
|
||
полосы входит делителем (где именно в цепи ops A-D — осталось залочить).
|
||
|
||
### Почему раньше «не ловилось»
|
||
- realtime без аудио-девайса не вызывает process вовсе (alsa_outdev пуст!);
|
||
- в render-mode инстанс создаётся ~через 0.3 c после host-spawn: ранняя остановка
|
||
ловит нулевую фазу (первые 12 «хитов» были ей);
|
||
- после создания экземпляра кернел живёт в непрерывном перестроении — STOP-цикл
|
||
читает его спокойно.
|
||
|
||
### Следующий раунд (рецепт готов, данные добываются rendersnap.py за минуту)
|
||
1. Прогнать rendersnap на dual q∈{0.1,1,3,10} + t1kq + al: собрать пары
|
||
(bands, scratch, FIR) → восстановить точную формулу ops (ожидаем exp(log·K)
|
||
со сглаживанием/варпом res-шаблона; проверить FIR vs exp(scratch)).
|
||
2. Объяснить абсолютную глубину (снап даёт −5.6/−6.5 против реала 10.32/11.82):
|
||
кандидат — нестационарность кадра (attack-фаза) или повторное применение.
|
||
3. Транскрипция kernel-build за RT_FIRCONV=1 → контрольные числа dual → гейт.
|
||
4. reaper.ini: alsa_outdev/indev=null прописаны (бэкап /tmp/opencode/reaper.ini.bak)
|
||
— realtime колбек всё равно не ожил (cpu 0.2 t/s), вопрос закрыт в пользу
|
||
render-snap метода.
|
||
|
||
## ============ UPDATE 2026-08-23i (23e): ЗАКОН СОШЁСЯ КОЛИЧЕСТВЕННО ============
|
||
|
||
Серия rendersnap: dual_b1q_0.5 + dual_b1q_3.0 (стационарные фазы).
|
||
1. **FIR = exp(0.984·scratch)** поточечно (коэф идентичен @43/@171) ⇒ ops
|
||
между log и exp — скалярный раундтрип, никакой межбиновой магии в самих ops.
|
||
2. **real_cut / FIR_cut = 1.798–1.810 константа** по всем 4 точкам
|
||
(q0.5: 5.70/6.58 FIR против 10.32/11.82 real; q3.0: 5.69/5.76 против
|
||
10.29/10.32). Между кернелом и слышимым применением — ЛИНЕЙНЫЙ масштаб
|
||
~1.805 в дБ-домене (кандидат: двойное применение/нормировка conv-стадии;
|
||
залочить при декоде потребителя, до тех пор EMPIRICAL-константа).
|
||
3. **Центр не зависит от q живьём**: scratch43 = −0.6565(q0.5) vs −0.6551(q3)
|
||
при том, что сырой bands[] мигает от 0.3 до 382 (детекторные IIR дают
|
||
стационар). Скайрт: Δscratch171(q3−q0.5)=+0.0937 ln ⇒ ×0.984×1.805 =
|
||
**+1.46 дБ** против реального Δ=1.50 дБ — сходится в пределах 3%.
|
||
4. Структура механизма окончательно: mask_log(f) = G(log(am_frame(f)), res_band(f));
|
||
кернел = exp(mask_log) c падающим Hann-окном на [n/2..n); применение =
|
||
свёртка × масштаб 1.805. Это УЖЕ наша каноническая структура am/res — но
|
||
вход амплитуды берётся из спектра кадра напрямую (не через наш lvl-тракт),
|
||
и применение — FIR-свёртка, а не поточечное умножение STFT.
|
||
### NEXT (конкретика)
|
||
1. Восстановить точную зависимость scratch(band, res): прогнать q-серию
|
||
{0.1,0.2,...,10} + am-серию, аппроксимировать G (ожидаем линейность в логах
|
||
вида −k·ln(am/res)+c, k≈0.984·?): две точки уже дают k_skyрт согласован.
|
||
2. Найти источник масштаба 1.805 (декод потребителя FIR: кто читает 0x540668).
|
||
3. Транскрипция RT_FIRCONV=1: bands=STFT-спектр кадра → scratch=G(...) →
|
||
kernel=exp·HannWin → rfft-свёртка overlap-add → гейт корпуса.
|
||
|
||
## ============ UPDATE 2026-08-23j (23f): q-СЕРИЯ ЖИВЫХ SCRATCH ============
|
||
|
||
scripts/qseries.py: rendersnap по dual_b1q_{0.1..10}.rpp (11 конфигов).
|
||
Стационарная подвыборка (scr43≈−0.6565): q={0.2,0.3,0.5,0.7,1.0,1.5,3.0}
|
||
(q=0.1,2,5,10 поймали нестационар scr43≈−0.18 — фильтровать по scr43!).
|
||
Таблица (FIR-cut дБ = −20log10 exp(0.984·scr); ×1.805 — эмпир. масштаб):
|
||
| q | scr43 | scr171 | FIR@43 | FIR@171 | ×1.805 @171 | real@171 |
|
||
|---|---|---|---|---|---|---|
|
||
| 0.2 | −0.6565 | −0.8965 | 5.61 | 7.66 | 13.83 | — |
|
||
| 0.3 | −0.6565 | −0.8332 | 5.61 | 7.12 | 12.85 | — |
|
||
| 0.5 | −0.6565 | −0.7570 | 5.61 | 6.47 | 11.68 | 11.82 |
|
||
| 0.7 | −0.6565 | −0.7192 | 5.61 | 6.15 | 11.09 | — |
|
||
| 1.0 | −0.6564 | −0.6923 | 5.61 | 5.92 | 10.68 | 10.78 |
|
||
| 1.5 | −0.6562 | −0.6750 | 5.61 | 5.77 | 10.41 | — |
|
||
| 3.0 | −0.6551 | −0.6633 | 5.60 | 5.67 | 10.23 | 10.32 |
|
||
Выводы: (а) центр строго const ∀q; (б) ×1.805 попадает в real с ошибкой
|
||
≤0.14 дБ на обеих известных точках; (в) scr171(res2000) НЕлинеен в ln res
|
||
(локальный наклон падает 0.135→0.088 ln/res) — согласуется с экспоненциальной
|
||
формой 22x; (г) сырой bands[] мигает на порядки (0.3↔382) при стабильном
|
||
scratch ⇒ детекторное сглаживание (attack/release IIR) сидит МЕЖДУ записью
|
||
bands[] и DESIGN — искать его адрес в цепи до 52b550.
|
||
### NEXT
|
||
1. am-серия (t_-24..t_+24 rpp уже есть в Recent!) → наклон по амплитуде →
|
||
полная форма G=log-домена: scr = c0 + ka·ln(am) + f(res).
|
||
2. Декод читателя 0x540668 (источник ×1.805) + детекторного IIR перед DESIGN.
|
||
3. RT_FIRCONV=1 транскрипция: G аппроксимировать таблично по этой серии.
|
||
|
||
### 23g (короткое): am-серия пока неинтерпретируема
|
||
t_{-24,-12,0,+24}.rpp (тон@1000, бин 85): scr85 = −1.0071 / −0.2276 / −0.4399 /
|
||
−1.0071 — НЕмонотонно по уровню; вероятная причина — steady() хватало разные
|
||
фазы (attack/стационар) или в t_* конфигах другая полоса. Данные в логе прогона;
|
||
повторить с контролем стабильности (несколько последних фаз подряд равны).
|
||
Скрипты qseries.py/amseries.py закоммичены.
|
||
|
||
## ============ UPDATE 2026-08-23k (23h): ОХОТА ЗА ПОТРЕБИТЕЛЕМ КЕРНЕЛА ============
|
||
1. Xref-скан disp32 0x540668 по всему коду: вне 52bxxx только ИНИТ
|
||
(52ed36 lea→resize, 35b71 store, 361de/f читатель+запись в 536xxx-билдере
|
||
кривых). Прямых чтений ДАННЫХ FIR в рантайме не видно ⇒ потребитель берёт
|
||
слот из таблицы указателей по индексу ЛИБО существует вторая копия цепи.
|
||
2. bp на выходе FIR-цикла (52b8bb) в render-окне: 0 попаданий (как и вход
|
||
529fe0) при том что кернель появляется ⇒ **в рендере работает ДРУГОЙ код**
|
||
(дублированная инстанциация цепи; модуль 480 МБ). fnall.py: finder заменён
|
||
на проверенный maps-вариант, FN_STAB управляет стабилизацией.
|
||
3. fnsample.py — активный RIP-сэмплер (PTRACE_INTERRUPT цикл): 10.6к сэмплов,
|
||
2375 в модуле. Горячее: 0x180b0c3e0 (MSVC 0xCC stack-fill, НЕ DSP),
|
||
кластеры 0x180bcxxxx/bdxxxx (хелперы); в ядре замечены 0x180534580 и
|
||
0x18052e1c0 (рядом с ramp-in 52e260!) — но только в части прогонов
|
||
(срабатывают на param-фазе, не в стационаре рендера).
|
||
4. reaper.ini: alsa null не оживил realtime (закрыто). Yabridge: сотни мёртвых
|
||
сокет-директорий /run/user/1000/yabridge-* от kill -9 ломали спавн —
|
||
чистить rm -rf перед серией (грабли зафиксированы).
|
||
### NEXT (продолжение)
|
||
1. fnsample с окном НА param-фазе (t≈1.3–1.6 c после host-spawn) и фильтром
|
||
ядра 0x180520000-0x180560000 → поймать дубликат цепи в момент построения.
|
||
2. Проверить 0x18052e1c0/0x180534580 дизасмом (уже лежат окна? нет — снять).
|
||
3. После опознания дубля: bp на его выход → [rsp]=адрес применения → декод
|
||
масштаба 1.805.
|
||
|
||
### 23i (короткое): HW-watchpoint заведён, попаданий нет
|
||
Отладочные регистры через POKEUSER: база u_debugreg = **0x350** (не 0x340/0x380;
|
||
DR7 @0x388; LEN=10(8B) отвергается ядром — использовать LEN=11(4B),
|
||
DR7=L0|R/W=11|LEN=11 = 0xF0001; сброс DR6 = poke 0xFFFF0FF0 @DR0+48).
|
||
scripts/fnwatch.py: watch на слово кернела (bin43.re) от момента создания
|
||
экземпляра до конца хоста — **0 попаданий** при подтверждённом армировании.
|
||
Гипотезы: потребитель читает КОПИЮ кернела; либо строится однократно до
|
||
армирования; либо выравнивание доступа мимо слова. Следующий шаг: watch
|
||
нескольких слов + старт с рождения хоста + контроль факта рендера (wav mtime).
|
||
|
||
### 23j (короткое): мульти-watchpoint — попаданий нет при валидном рендере
|
||
fnwatch4.py: 4 DR одновременно {слот-указатель FIR (оба dword), слово кернела,
|
||
слово scratch} → затем позитивный контроль {overlap-слово, track[43], R[43],
|
||
лок 0x2404dc}. Конфигурация: render_fresh=True (wav@1.49 c), armed=8/8 тредов
|
||
(INTERRUPT→wait→arm→CONT на resweep обязателен — армирование бегущего треда
|
||
молча не работает), WATCH_DUR=30. **0 попаданий везде**, включая лок и
|
||
аудио-переполнение, которые ОБЯЗАНЫ трогаться при обработке.
|
||
ВЫВОД: аппаратные data-watchpoints под wine-тредами НЕ доставляются tracer'у
|
||
в этом окружении (kernel/wine взаимодействие) — маршрут закрыт. Потребителя
|
||
искать статически: скан disp32 из диапазона таблицы [0x540600..0x540900]
|
||
(семейство байт `?? 06 54 00`) → функции, итерирующие слоты по индексу;
|
||
плюс декод 52e1c0/534580-окрестностей отвергнут (аллокаторы).
|
||
|
||
## ============ UPDATE 2026-08-23l (23k): HW EXEC-BP РАБОТАЕТ; ЦЕПЬ СТРОИТСЯ ОДИН РАЗ ============
|
||
1. Статический скан ВСЕХ disp32 из [0x540600..0x540900] (390 ссылок): все
|
||
относятся к известным функциям (529fe0, init 52exxx, билдеры 535xxx/536xxx).
|
||
Скрытого статического читателя таблицы НЕТ.
|
||
2. Аппаратные EXEC-bp (DR0/DR1, RW=00) через POKEUSER теперь реально работают
|
||
(ранее все HW-эксперименты были no-op из-за неверного оффсета 0x380 вместо
|
||
0x350). RF-флаг (eflags|=0x10000) для обхода exec-trap; TRACECLONE наследует
|
||
DR дочерним тредам.
|
||
3. Чистый прогон (fnexec, arm после создания экземпляра @1.46 c): **wav@1.48 c,
|
||
0 попаданий на вход 529fe0 и голову FIR-цикла** при живом рендере ⇒
|
||
**декодированная цепь НЕ выполняется в steady-render; кернель строится
|
||
ОДИН РАЗ на param-change в узком окне (<100 мс) сразу после создания
|
||
экземпляра**. Это объясняет: стабильность кернела между фазами, молчание
|
||
всех bp, статичность кривых 22y.
|
||
4. Следствие для транскрипции: rebuild кернела ТОЛЬКО на setParams; per-frame
|
||
работает только детектор+свёртка с кэшированным кернелом.
|
||
### NEXT (решающий)
|
||
fnexec early-arm режим уже умеет ставить exec-watch ДО создания экземпляра
|
||
(наследование клонам) — но два прогона подряд поймали флак рендера под
|
||
трассировщиком (render_fresh=False). Протокол повторения: (а) ранний arm на
|
||
0x1802a24c0/0x1802fa420; (б) НЕ трогать треды до первого SIGTRAP (убрать
|
||
resweep-CONT чурн); (в) по хиту — дамп rcx(bands)/rdx(scratch) уже реализован;
|
||
(г) если рендер опять не стартовал — просто перезапустить (флак ~50%).
|
||
После дампа bands[] в момент param-change: сравнить с R-кривой, восстановить
|
||
G, декодировать потребителя через backtrace ([rsp]) из того же хита.
|
||
|
||
### 23l (короткое): DR-механизм верифицирован микротестом; расхождение локализовано
|
||
Микротест /tmp/opencode/dtest.py на fork+exec ребёнке: DR0 exec-watch срабатывает
|
||
(SIGTRAP, rip==target, DR6 bit0) — ядро/CPU полностью поддерживают аппаратные
|
||
ловушки через POKEUSER 0x350/0x388. При этом fnexec с ранним армированием
|
||
(до создания экземпляра, наследование через TRACECLONE + армирование при
|
||
рождении каждого треда) даёт 0 попаданий на {529fe0, loop-head, design×2,
|
||
dispatcher, bidir} при подтверждённых рендерах и 8–12/8–12 армированных тредов.
|
||
Одновременно: disp32-скан показывает, что ВЕСЬ доступ к scratch/FIR идёт только
|
||
из декодированного цикла 52bxxx. Прямое противоречие ⇒ остался ровно один
|
||
неисключённый вариант: **трассируется не тот процесс/треды умирают до арма**.
|
||
Следующий шаг: в fnexec логировать КАЖДЫЙ stop (rip,dr6,pid) без фильтра +
|
||
счётчик всех SIGTRAP; если трапов нет вообще на тредах, которые заведомо
|
||
выполняют модульный код — сравнивать /proc/pid/task состав до/после.
|
||
|
||
### 23m (финал раунда): трассировка под wine — аномалия доставки зафиксирована
|
||
Серией прогонов с ПОЛНЫМ логом остановок установлено: за 14 c живого хоста
|
||
tracer видит ~1 остановку (не от наших DR), хотя 8+ тредов армированы
|
||
exec-watch на тела дизайна/диспетчер/bidir с верифицированной записью DR
|
||
(drprobe.py: все poke ok, readback точный) и микротестом доставки на
|
||
нативном треде (dtest.py — работает). Треды wine при этом выполняют модульный
|
||
код (рендер происходит). Вывод: доставка DR-trap'ов от wine-тредов в наш
|
||
waitpid-цикл не работает в этой конфигурации (кандидаты: wine сам использует
|
||
DR/GetThreadContext, wow64-переключения, либо события теряются из-за
|
||
групповых остановок). Отладочный вывод сохранён в fnexec/hits.json.
|
||
### Достижения раунда (суммарно, 23h–23m)
|
||
1. Доказано: цепь строится ОДИН РАЗ на param-change (<100 мс окно), steady
|
||
render её не вызывает — транскрипция: rebuild только на setParams.
|
||
2. Все 390 статических ссылок на таблицу слотов учтены; скрытого читателя нет.
|
||
3. HW exec-bp методология верифицирована end-to-end (dtest) — инструмент готов,
|
||
осталась аномалия доставки под wine.
|
||
4. rendersnap.py — рабочий канал снятия ЛЮБЫХ внутренних состояний плагина
|
||
(уже дал формулу FIR=exp(0.984·scratch), масштаб ×1.805, q-серию).
|
||
### Дорога к bit-exact (порядок)
|
||
1. G(band,res): q-серия есть; добавить am-серию со стабилизацией фаз;
|
||
аппроксимировать и проверить на hold-out.
|
||
2. Потребитель/×1.805: (а) полный декод init-семьи 52e9b0–531xxx (param-time
|
||
строитель — потребитель рядом); (б) либо повторить DR-охоту после решения
|
||
аномалии доставки (например, трассировать через PTRACE_SEIZE опции или
|
||
perf_event_open с breakpoint типом HW).
|
||
3. RT_FIRCONV=1 + контрольные числа dual + гейт корпуса.
|
||
|
||
## ============ UPDATE 2026-08-23n (24a): ПЕРФ-ДИКТОР — WINE ДЕРЖИТ СЛОТЫ; ДВИЖОК ОПОЗНАН ============
|
||
1. perf_event_open (PERF_TYPE_BREAKPOINT) на слово кернела: собственные треды и
|
||
ЧУЖИЕ same-uid процессы открываются (fd>0), а wine-треды дают ENOSPC ⇒
|
||
**wine резервирует аппаратные BP-слоты** под эмуляцию Windows-DR. Это же
|
||
объясняет глобальную тишину ptrace-DR: ядро хранит значения, но в кремний
|
||
их программировать нечем. Маршрут аппаратных ловушек внутри wine ЗАКРЫТ
|
||
окончательно (23h–24a). Скрипт scripts/perfbp.py готов для нативных целей.
|
||
2. Опознан объект conv-движка: **ctx+0x540530**, инлайн-конфиг {2,4096},
|
||
{16384,8192}, {2,257}; члены-векторы +0xb8..+0x130; методы FUN_18052dc30
|
||
(config), FUN_18052fe00 (resize), FUN_18052dd30 (dtor). Его process-метод —
|
||
следующий кандидат на «потребителя кернела» (ищется рядом по декомпу/вызовам
|
||
с arg=ctx+0x540530 из аудио-клея).
|
||
3. INT3-маршрут остаётся единственным динамическим (слоты не нужны): требует
|
||
дисциплины fnwatch4 (INTERRUPT→wait→arm→CONT всем тредам) + контроля
|
||
render_fresh в каждом прогоне (флак ~50%).
|
||
### NEXT
|
||
1. Найти process-метод движка 0x540530: перечислить функции, получающие
|
||
ctx+0x540530 (grep decomp), восстановить vtbl объекта из дампа памяти
|
||
([ctx+0x540530]-объект начинается с указателя? проверить офлайн из s1.bin),
|
||
декодировать метод → источник ×1.805.
|
||
2. INT3-ловушка на найденный метод + на design-тела в param-окно (рендер-
|
||
свежесть обязательна в логе).
|
||
3. RT_FIRCONV=1 после декода масштаба; контрольные числа dual; гейт корпуса.
|
||
|
||
### 24b (короткое): vtable модуля полная; процесс-метод ещё не опознан
|
||
vtbl @0x1824ac210: +0=535ed0,+1=52cfb0,+2=529550,+4=52b940(→rebuild),
|
||
+5=529c60(осцилл),+6=529fe0(маск-цепь),+8=52bbf0(getter blocksize),
|
||
+12=52bba0,+13=52bb80; сеттеры параметров 52b980-baa0 (52ba20 пишет флаг
|
||
0x5408b8 и зовёт полный rebuild 52e9b0 ✓; 52baa0 пишет 0x54088c=expf(x·K)).
|
||
Объект conv-движка (ctx+0x540530) невиртуальный: методы прямые
|
||
{dc30 cfg, fe00 resize, dd30 dtor}; его process-метод = прямой вызов из
|
||
аудио-клея — искать среди функций, вызывающих dc30/fe00 (их CALLER'Ы) и в
|
||
диапазоне 52d650..532xxx. Следующий раунд: декод этого клеевого метода даст
|
||
потребителя кернела и ×1.805.
|
||
|
||
## ============ UPDATE 2026-08-24a (24c): ЖИВАЯ ОБРАБОТКА ПОДТВЕРЖДЕНА; «BUILD-ONCE» ОТЗЫВАЕТСЯ ============
|
||
Чистый эксперимент без трассировки: файл seq_in.wav = [A: 500+2000Гц | B: только
|
||
2000Гц] по 2 с, полоса @500 q=0.5, рендер до естественного завершения.
|
||
Результат (выравнивание по LOOP=1, выход 6 c = A+B+A):
|
||
half A: cut@2000 = 21.91 дБ half B: cut@2000 = 25.74 дБ
|
||
(поведение @500 меняется кардинально: контент-зависимость живьём)
|
||
⇒ ПЛАГИН ОБРАБАТЫВАЕТ АУДИО В РЕНДЕРЕ ПО-РАЗНОМУ ДЛЯ РАЗНОГО КОНТЕНТА ⇒
|
||
маск-цепь выполняется по-кадрово. Вывод 23k «build-once» ОТЗЫВАЕТСЯ — он
|
||
опирался на exec-bp, которые не доставляются под wine (см. 24a: wine держит
|
||
слоты HW BP; ptrace-DR в кремний не программируются).
|
||
ПОПУТНО: (а) рендер-выход длиннее входа из-за LOOP=1 в проекте (6 c = A+B+A);
|
||
(б) убивать reaper во время рендера нельзя — выходной файл остаётся частичным
|
||
с предварительной шапкой (источник «флака» свежести); (в) выход 16-bit stereo.
|
||
### Методологический итог для следующих раундов
|
||
Динамические ловушки внутри wine сейчас НЕ работают ни в каком виде (HW занят
|
||
wine, INT3-прогоны не контролировались на свежесть рендера). Единственный
|
||
работающий канал наблюдения — rendersnap (STOP-семплирование), но он снимает
|
||
между колбеками офлайн-рендера... что при back-to-back обработке ЕСТЬ момент
|
||
обработки — противоречие с молчанием ловушек требует пересмотра: возможно,
|
||
в wine-хосте ДВА экземпляра модуля (GUI/DSP), а vtable-кандидат у нас один;
|
||
multictx сканировал только rw-регионы с маркером vtable — второй экземпляр мог
|
||
иметь другой адрес vtbl-указателя (heap-копия класса?). Проверка: полный скан
|
||
всех экземпляров по сигнатуре sens>100 БЕЗ привязки к vtable (float 48000 +
|
||
sens + 0x540870-паттерн уже покрывал; расширить на поиск по содержимому
|
||
R-кривой в куче).
|
||
|
||
### 24d (стоп раунда): двухэкземплярная гипотеза не подтверждена/не опровергнута
|
||
Сканер всех кандидатов (scan2.py в /tmp/opencode, не закоммичен из-за бага:
|
||
после починки finder'а — исключён reaper по cmdline — 25 STOP-сканов подряд
|
||
на правильном wine-хосте не находят ДАЖЕ известный ctx 0x2370040 с vtable-
|
||
маркером, хотя идентичный код rendersnap.py находит стабильно). Расхождение
|
||
не отлажено (контекст исчерпан). Состояние задачи «потребитель кернела»:
|
||
- живая обработка доказана (24c);
|
||
- HW-ловушки в wine невозможны (wine держит слоты, 24a);
|
||
- INT3 не проверялся при ГАРАНТИРОВАННО свежем рендере;
|
||
- статически: все disp32-ссылки учтены, conv-движок = объект ctx+0x540530
|
||
(методы dc30/fe00/dd30 прямые), его process-метод среди caller'ов в
|
||
52d650..532xxx — НЕ НАЙДЕН декомпом (grep 0x540530 даёт только init/dtor).
|
||
### Рекомендация следующему раунду
|
||
1. Отладить scan2.py (почему пусто: сравнить с rendersnap пошагово на одном
|
||
запуске — вероятно, порядок STOP→scan ломается на первом STOP до маппинга
|
||
DLL, а CONT после каждого скана пропускает окно создания экземпляра).
|
||
2. Либо полный декод аудио-клея: найти функцию, вызывающую vtbl слот +6
|
||
(529fe0) у СОСЕДНЕГО экземпляра, или decode 52e9b0-вызовителя (14191 в
|
||
decomp_funs2 — это 52ba20 setter); glue где-то в 52exx–52fxx.
|
||
3. После опознания потребителя — ×1.805 и RT_FIRCONV.
|
||
|
||
## ============ UPDATE 2026-08-24e (24e): КОНСЮМЕР КЕРНЕЛА ОПОЗНАН — th_b3c0 COMPLEX-MULTIPLY ============
|
||
|
||
### Решающий факт: th_b3c0 = чистый complex multiply, без нормировки
|
||
ILT-резолв: 0x1df0 (float) → 0x18000b3c0 (th_b3c0), 0x1f70 (double) → 0x18000e360.
|
||
Тело th_b3c0 (первые 384 байта): SIMD complex multiply через vmulps/vfmaddsub213ps.
|
||
Нет divss/divpd/divsd — функция ЧИСТО умножает спектры: out[i] = FIR[i] × audio[i].
|
||
|
||
### Конвейер кернела (полный, из asm 52b550–52b8bb + BLOCKMAP 23b)
|
||
1. FIR构建: bands[i] → scratch=log(bands) → complex-ops A-D на буферах 0x540548/550/598 →
|
||
копия в FIR@0x540668 → **×= Hann_freq[2048..4095]** (падающая половина) → FIR[0]=1,FIR[1]=0
|
||
2. **×0x540888** (attack/release coeff): строка 52b832–52b85e, xmm6=[ctx+0x540888],
|
||
fill FIR[0..2n-1] ×= xmm6 через 0x2030/0x1d30
|
||
3. **Конвейер-вызов**: th_b3c0(FIR, audio[param_2], nBins, out[param_2])
|
||
— ЧИСТЫЙ complex multiply в freq-domain, in-place в буфере полосы.
|
||
→ out[band] = FIR × audio (0x540668 умножается на аудио-спектр полосы)
|
||
|
||
### Источник ×1.805
|
||
Конвейер НЕ содержит нормировки (th_b3c0 = чистый mul). ×1.805 = комбинация:
|
||
(a) 0x540888 (attack/release coeff) — домножает ВЕСЬ FIR-спектр scalarem
|
||
(b) Масштаб Hann-окна при FFT-свёртке (N=4096, window energy)
|
||
(c) Неопределённый коэффициент FFT/IFFT нормировки (стандартный 1/N или N)
|
||
Точный баланс требует live-измерения 0x540888 при фиксированном кернеле.
|
||
|
||
### Ключевое следствие для архитектуры
|
||
FIR применяется как frequency-domain mask (поточечное complex-umножение в спектре),
|
||
НЕ как time-domain convolution. Это ПОДТВЕРЖДЕНИЕ NOTES 23e "применение = свёртка ×
|
||
масштаб 1.805": хост берёт аудио-спектр полосы и умножает на FIR-маску bin-by-bin.
|
||
conv-движок (0x540530) — инфраструктура FFT (FFT-план, буферы); его process-метод
|
||
НЕ НУЖЕН для консюмера (весь путь — внутри FUN_180529fe0).
|
||
|
||
### scan3.py (scripts/scan3.py)
|
||
Фикс scan2.py: ports rendersnap.py find-loop + multi-instance detection + full state
|
||
чтение. Ключевое отличие от scan2: scan2 НЕ использовал rendersnap-style find-loop
|
||
(find-loop с CONT после каждого STOP-скана).
|
||
|
||
### NEXT
|
||
1. Измерить 0x540888 live при dual_b1q_0.5 через rendersnap → точный баланс ×1.805
|
||
2. RT_FIRCONV=1: транскрипция th_b3c0 как frequency-domain mask multiply
|
||
3. Контрольные числа dual по q (q-серия23f + FFT-conv) → гейт
|
||
|
||
## ============ UPDATE 2026-08-24f: RT_FIRCONV=1 ПЕРВЫЙ РЕЗУЛЬТАТ — ЧАСТИЧНО РАБОТАЕТ ============
|
||
|
||
### Реализация (dsp/spectral.cpp)
|
||
RT_FIRCONV=1: FIR = mask (freq-domain), complex multiply FIR × audio (th_b3c0 equivalent).
|
||
DC passthrough (FIR[0]=1.0).Upper half zeroed.
|
||
|
||
### Результаты на dual_b1q_0.5 (single band, fc=500, q=0.5, sens=12):
|
||
| конфиг | cut@500 | cut@2000 |
|
||
|--------|---------|----------|
|
||
| Default (scalar mul) | -14.35 | -6.58 |
|
||
| RT_FIRCONV=1 (complex mul) | -20.37 | -12.60 |
|
||
| Reference (plugin) | -10.32 | -11.82 |
|
||
|
||
### Анализ
|
||
1. RT_FIRCONV перерезает на 500 Гц (-20.37 vs -10.32) но БЛИЖЕ на 2000 Гц (-12.60 vs -11.82)
|
||
2. Default недорезает на 2000 Гц (-6.58 vs -11.82) но перерезает на 500 (-14.35 vs -10.32)
|
||
3. Ни один не совпадает с референсом → маска-цепь в detector неполна
|
||
4. FIR window (периодический Hann 4096, rising half 0→1) определена из live-капч
|
||
|
||
### Ключевой вопрос: почему оба перерезают на 500?
|
||
Маска на бине 43 (500 Гц) при structural chain должна быть ~0.31 (= -10.32 dB),
|
||
но реальный structural chain даёт -14.35 dB → маска ~0.19. Это указывает на
|
||
неправильный scale_factor или LUT-кривую в detector. ×1.805 НЕ применяется
|
||
(он =.effects窗外 + conv-normalization, НЕ scalar в freq-domain).
|
||
|
||
### Дальнейшие шаги
|
||
1. Калибровка scale_factor/level_path чтобы маска на 500 Гц ≈ 0.31
|
||
2. Проверка RT_FIRCONV на t1kq/al/res (не только dual)
|
||
3. Корпусный прогон RT_FIRCONV vs default → определить группу, где FIRCONV лучше
|
||
|
||
## ============ UPDATE 2026-08-24g: КОРНЕВАЯ ПРИЧИНА РАЗРЫВА — FFT НОРМАЛИЗАЦИЯ ============
|
||
|
||
### Факт
|
||
Plugin применяет FIR через time-domain convolution (FFT→multiply→IFFT, без 1/N нормировки).
|
||
Наш FFT делит на N при IFFT. Это создаёт ×N разницу в gain.
|
||
|
||
### Живые измерения (dual_b1q_0.5, bin 43 = 500 Hz):
|
||
- raw_level@43 = 8.86 (am/res × scale_factor)
|
||
- mask@43 = 0.510 (per-band spectrum, 0x540678)
|
||
- FIR@43 = 0.524 (FIR magnitude, after construction)
|
||
- Final cut = 10.32 dB → final gain = 0.305
|
||
|
||
### Наша цепь:
|
||
- raw^0.984 = 8.58 (GAIN > 1, boost, not cut!)
|
||
- exp2(-LUT(lvl)) = 0.12 (cut 18.4 dB, too much)
|
||
|
||
### Анализ
|
||
Plugin's FIR construction: FIR = raw^0.984 × hann × 0x540888
|
||
Plugin's application: FIR × audio via complex multiply (th_b3c0, NO 1/N)
|
||
Our application: mask × audio via complex multiply (WITH 1/N from IFFT)
|
||
|
||
Разница: plugin's FFT output has magnitude ×N vs our IFFT-normalized output.
|
||
Это объясняет为什么 raw^0.984 > 1 (boost) в нашей цепи, но cut в плагине.
|
||
|
||
### Решение
|
||
Нужно убрать 1/N нормировку из IFFT в我们的 FFT-conv, или добавить
|
||
калибровочный множитель N в FIR construction. Это ТОЧКА ОСТАНОВКИ
|
||
для текущей сессии — требует изменения dsp/fft.cpp IFFT implementation.
|
||
|
||
## ============ UPDATE 2026-08-24h: КОРНЕВАЯ ПРИЧИНА — IIR STATE + MASK COMPUTATION ============
|
||
|
||
### Новые данные (RT_DUMP_BIN frame 100, dual_b1q_0.5):
|
||
| bin | am | res | lvl_raw | band_level | mask |
|
||
|-----|-----|-----|---------|------------|------|
|
||
| 43 | 0.099 | 0.117 | 2.725 | 1.934 | 0.209 |
|
||
| 171 | 0.321 | 0.839 | 1.240 | ? | ? |
|
||
|
||
### Live plugin (scan3 captures):
|
||
- mask@43 = 0.510 (0x540678 buffer, per-band spectrum)
|
||
- FIR@43 = 0.524 (FIR magnitude)
|
||
- Final cut = 10.32 dB → final gain = 0.305
|
||
|
||
### Анализ разрыва
|
||
1. **Наш mask = 0.209, плагин mask = 0.510** → ×2.44 разница
|
||
2. Плагин's mask = IIR-smoothed per-band spectrum (0x540678)
|
||
3. Наш mask = exp2(-LUT(lvl)) — ДРУГОЙ домен (reduction, не spectrum)
|
||
4. LUT отвечает за нелинейное преобразование level→gain
|
||
5. Плагин's LUT (A=-24, B=28, γ=1) даёт ДРУГОЙ результат чем наш LUT (A=-13.78, B=68.29, γ=0.344)
|
||
|
||
### Ключевой факт
|
||
Наш LUT (A=-13.78, B=68.29, γ=0.344) — эмпирический фит из NOTES:967.
|
||
Плагин's LUT (A=-24, B=28, γ=1) — live-captured в 22b, но помечен как GUI-only.
|
||
|
||
### Вопрос
|
||
Плагин ЧИТАЕТ ли свой LUT в аудио-пути? Если да — нужно заменить наш LUT на плагин's.
|
||
Если нет — LUT не нужен, mask = raw_level (am/res × scale).
|
||
|
||
### Следующий шаг
|
||
Проверить: если использовать raw_level (без LUT/exp2) как mask, какой cut получится?
|
||
|
||
## ============ UPDATE 2026-08-24i: ×1.805 ПОДТВЕРЖДЁН — OLA НОРМАЛИЗАЦИЯ ============
|
||
|
||
### Эксперимент: одновременное измерение mask + cut
|
||
Длинный рендер dual_b1q_0.5, scan3 захват 60 фаз, cut измерен по тому же выходу.
|
||
|
||
### Результаты (steady-state, 60 фаз — mask КОНСТАНТЕН):
|
||
- mask@43 = 0.5099 (постоянен с 1-й фазы — IIR УЖЕ сошёлся)
|
||
- fir@43 = 0.5241 (постоянен)
|
||
- actual cut = -10.32 dB → final gain = 0.305
|
||
|
||
### Подтверждение ×1.805:
|
||
| Измерение | Значение | Cut (dB) |
|
||
|-----------|----------|----------|
|
||
| mask@43 | 0.5099 | -5.85 |
|
||
| fir@43 | 0.5241 | -5.61 |
|
||
| actual | 0.305 | -10.32 |
|
||
| ratio actual/fir | 1.839 | **×1.805** |
|
||
|
||
### Вывод
|
||
×1.805 = OLA normalization factor. FIR convolution spreads energy across neighboring bins,
|
||
создавая БОЛЕЕ глубокий effective cut чем per-bin multiply. Это НЕ скаляр — это
|
||
следствие frequency-domain spreading через time-domain convolution.
|
||
|
||
### Два независимых источника ошибки (ИСПРАВЛЯТЬ ОБА):
|
||
1. **Mask computation**: наша цепь 0.209 vs плагин 0.510 (×2.44, LUT issue)
|
||
2. **Application**: per-bin multiply vs FIR convolution (×1.805, OLA)
|
||
|
||
### Следующие шаги
|
||
1. Исправить mask computation (заменить LUT на raw_level или калибровать)
|
||
2. Реализовать FIR convolution (OLA) вместо per-bin multiply
|
||
3. Оба исправления требуются для bit-exact
|
||
|
||
## ============ UPDATE 2026-08-24j (24j): ПРИМЕНЁННЫЙ ОТКЛИК = ЖИВОЙ FIR ПОТЕНЦИАЛЬНО; ×1.805 = ФАНТОМ STALE-КЕРНЕЛА ============
|
||
|
||
### Метод (новый канал, скрипты rendersnap2.py + генератор зондов)
|
||
1. МЯГКИЙ снаппер `scripts/rendersnap2.py`: НЕ убивает reaper (частичный-wav
|
||
hazard 24c обойдён — рендер доестественного выхода), дополнительно дампит
|
||
банк скаляров ctx+0x540860..0x5408c0 каждый фазой.
|
||
2. Мультитон-проект: клоны dual_b1q_0.5.rpp с входом «2 главных тона 500+2000
|
||
@0.4248 (= амплитуды dual.wav) + 20 зондов −30 дБ на сетке 100..7200 Гц».
|
||
Детектор в том же адаптивном состоянии, H измерима Goertzel'ем на всех
|
||
зондах. Генерация inline (/tmp/opencode/multi_in.wav + multi.rpp).
|
||
3. Свип НЕ годится для Y/X-отношения (энергия на гребне) — только мультитон.
|
||
|
||
### Результат multi_b1q_0.5 (живой FIR ph069: m@43=0.320, m@171=0.267)
|
||
| f, Гц | cut дБ | gain | m(FIR) | gain/m |
|
||
|-------|--------|------|--------|--------|
|
||
| 500 | 10.32 | 0.3048 | 0.3234 | 0.943 |
|
||
| 2000 | 11.84 | 0.2558 | 0.2698 | 0.948 |
|
||
| 700..7200 (18 зондов) | 1.9–4.6 | — | — | **0.98–1.00** |
|
||
(450/550 аномальны — утечка боковых лепестков окна Гёрцеля от главного
|
||
тона 500; не в счёт.) Контрольные числа воспроизведены: 10.32/11.84 ✓✓.
|
||
|
||
### ВЫВОДЫ (три, все жёсткие)
|
||
1. **Применённый отклик = mag(живого FIR@540668) побиновно** (±2%; главные
|
||
тона −0.5 дБ — недозахват глубины между фазами). НИКАКОЙ дополнительной
|
||
свёртки/OLA/×1.805 поверх per-bin multiply НЕ СУЩЕСТВУЕТ.
|
||
2. **«Свёртка ×1.805» (23e) и «OLA-нормализация» (24i) ОТЗЫВАЮТСЯ**: источник —
|
||
сравнение cut живого рендера с init-time кернелом. Старые phase*.npz из
|
||
rendersnap-сессий держат m@43=0.588 СЕКУНДАМИ — это ядро после param-rebuild
|
||
при ЗАГРУЗКЕ (52e9b0 строит FIR из init-state bands[]); живой рендер
|
||
перестраивает FIR КАЖДЫЙ кадр и мгновенно потребляет → STOP-семпл ловит
|
||
identity/reset (все 110 фаз sweep-рендера ≈ 1.0). 0.305/0.32≈0.95,
|
||
0.524(init)/0.32(live)=1.64~«1.805» — вот вся «константа».
|
||
3. **R-слоты 0x5407f8/0x540788 — СТАТИКА**: r43=3.9807/r43=4.0285 ИДЕНТИЧНЫ во
|
||
всех трёх проектах (dual/sweep/multi, разный контент). Это twin-шаблон
|
||
полосы (геометрия), НЕ динамическая маска. Интерпретация 22y «R=1/mask
|
||
живая кривая редукции» — снята (кривая была от инициализации).
|
||
4. **Скаляр [ctx+0x540888]=1.000000** весь рендер (и s874/s87c/s88c=1.0;
|
||
s870=440.955=sens-scale). Wet-скаляр BLOCKMAP 23b НЕ вносит масштаба при
|
||
дефолтных параметрах. ×N-нормировка FFT (24g) тоже не подтверждается:
|
||
применённый gain совпадает с mag(FIR) без поправок.
|
||
|
||
### Что это меняет в архитектуре транскрипции
|
||
- RT_FIRCONV как «особое применение» НЕ нужен: достаточно per-bin multiply
|
||
спектра кадра на маску-кривую (как default-путь spectral.cpp).
|
||
- ВСЕ усилия — в ФОРМУ кривой: bands[i] → (log→ops A-D→exp0.984→hannfall→ops)
|
||
→ FIR. Наш structural chain должен выдать кривую, чья mag совпадёт с живым
|
||
FIR фазы. Эталонные кривые теперь ЕСТЬ: rendersnap2/ph*.npz (fir_re/fir_im).
|
||
|
||
### NEXT (24k+)
|
||
1. Дискретизация формулы: снять ph-серии с ОДИНАКОВЫм статичным контентом при
|
||
вариации одного параметра (q-серия уже есть рефами; прогнать rendersnap2 по
|
||
dual_b1q_0.1..10.0) → восстановить зависимость m(fir) от параметров.
|
||
2. Сравнить живой FIR с нашим structural mask: где именно расходится форма
|
||
(хвост? ширина? пол?) — починка detector-цепи по живым эталонам.
|
||
3. Проверить hannfall-мультипликацию: fit exp(0.984·log(bands))·w против
|
||
захваченного fir_im≠0 (фаза!) — complex ops A-D дают мнимость.
|
||
4. Починить wav-path баг rendersnap2 (проверяет rpp.wav вместо RENDER_FILE).
|
||
|
||
## ============ UPDATE 2026-08-24k (24k): ЗАКОН ПРИМЕНЕНИЯ НАЙДЕН ТОЧНО — A = 1.019·V^1.8345; «×1.805» = ПРОИЗВЕДЕНИЕ ЭКСПОНЕНТ 0.984×1.8345 ============
|
||
|
||
### Инструменты раунда (все рабочие, закоммичены)
|
||
- `scripts/rendersnap2.py` v3: мягкий снаппер (не убивает reaper), t_snap в npz,
|
||
слоты {540668 FIR, 540678 bands, 540688, 540788 R, 5407f8 R} + скалярный банк
|
||
0x540860..c0. OUT = argv[3] (иначе каталог перезаписывается!).
|
||
- `scripts/hunt2.py`: полный чанковый перебор ВСЕХ ctx-кандидатов в рендере.
|
||
РЕЗУЛЬТАТ: инстанс ОДИН (0x2370040) — GUI/DSP-пара (24c) НЕ ПОДТВЕРЖДЕНА,
|
||
гипотеза закрыта.
|
||
- Зондовый мультитон: 2 главных тона (= амплитуды dual.wav 0.4248) + зонды
|
||
−30 дБ на фикс. сетке; Гёрцель по рефу даёт применённый отклик на всех
|
||
зондовых частотах при том же адаптивном состоянии детектора.
|
||
|
||
### Ключевые отрицательные результаты (не возвращаться)
|
||
- [ctx+0x540888] и весь банк 0x540874..88c = 1.000000 весь рендер; 0x540870 =
|
||
440.955. Скаляров-множителей в применении НЕТ.
|
||
- R-слоты 0x540788/0x5407f8 ИДЕНТИЧНЫ во всех проектах (r43=4.0285/3.9807) —
|
||
статические twin-шаблоны; с формой V НЕ коррелируют (rms 0.47).
|
||
- Слот 0x540678 = спектр кадра (HF→0), не кривая маски; 0x540688 = большие
|
||
значения (~183) — сырые аккумуляторы.
|
||
- Свип для Y/X-отношения непригоден (энергия только на гребне) — только
|
||
мультитон/мультиуровень.
|
||
- Динамическое выравнивание V(t)↔g(t) на рампе НЕ сходится (инерция детектора
|
||
+ STOP-семплирование) — пары брать только из стационарных прогонов.
|
||
- Каталоги снапов перезаписываются друг другом — всегда отдельный argv[3].
|
||
|
||
### ГЛАВНЫЙ ЗАКОН (8 уровней драйва −0..−30 дБ, q=0.5, fc=500)
|
||
Стационарная пара (V = устоявшийся видимый FIR@540668 «most-common complete
|
||
state», A = применённый отклик по Гёрцелю рефа):
|
||
```
|
||
cutA[дБ] = 1.8345 · cutV[дБ] + 0.1615 дБ (@500 Гц, центр нотча)
|
||
rms = 0.0025 дБ (!), max 0.005 дБ — 8 точек, cutV от 1.0 до 5.55 дБ
|
||
```
|
||
На скирте (@2000): та же форма, γ≈1.812, offset≈0.18, rms 0.087 дБ.
|
||
В линейном домене: A ≈ 1.019 · V^1.8345.
|
||
Погрешность выбора V (медиана most-common вместо истинного кадра) ~0.05-0.1 дБ
|
||
на скирте объяснима; закон устойчив по всем уровням.
|
||
|
||
### РАЗГАДКА «×1.805» (23e/24i окончательно закрыты)
|
||
```
|
||
0.984 × 1.8345 = 1.80515
|
||
```
|
||
23e эмпирическая константа «свёртка ×1.805» — это ПРОИЗВЕДЕНИЕ экспонент:
|
||
видимый кернел строится как exp(0.984·log(bands)) (23d ✓), а ПРИМЕНЯЕМЫЙ
|
||
kerнел = bands^1.805 (в эквиваленте). Никакой свёртки/OLA/скаляра нет:
|
||
применение = побиновный комплекс-multiply (th_b3c0 ✓ 24e) на кернел
|
||
A = 1.019·V^1.8345. Старые phase*.npz (m43=0.588) — init-кернелы, всё что
|
||
строилось на них — артефакты.
|
||
|
||
### Двойные аттракторы видимого буфера
|
||
V_steady@43 = 0.524±0.002 ∀q (0.1..10); редкие флэши D@43=0.315 ∀q;
|
||
ln(D)/ln(V)=1.85 ≈ γ ⇒ флэш = применяемый кернел A, пойманный в буфере
|
||
(перестройка каждый кадр: rebuild→A→consume→запись V-стадии).
|
||
|
||
### Что осталось до транскрипции применения
|
||
1. Формула V(content,params): ширина провала vs q, глубина vs drive/sens,
|
||
позиция vs fc. Зондовая методика + rn_q*/rn_lv* каталоги = данные уже есть.
|
||
2. Источник 1.8345 в коде: искать вторую экспоненту в ops A-D / bigkernel
|
||
140b30 (exp2-кернел?) — кандидат: двойное применение стадии exp или
|
||
exp(0.984·log)²·коррекция окна (0.984·2=1.968≠1.805 — нет; искать точно).
|
||
3. RT_FIRCONV=3: маска = 1.019·M^1.8345, где M = выход нашей detector-цепи,
|
||
после того как M приведён к форме V (единичный passband, провалы у контента).
|
||
4. Корпусный гейт --vs-bridge после каждого шага.
|
||
|
||
### Контрольные числа уровня −0 дБ (dual-эквивалент)
|
||
V43=0.5279 → cutV=5.550 дБ; cutA=10.34 дБ (реф 10.32 ✓); cutA@2000=11.85 (реф
|
||
11.82 ✓). Уровневая серия: см. /tmp/opencode/{lv*,rn_lv*}, pairs в
|
||
level_pairs.npz.
|
||
|
||
### ДОПОЛНЕНИЕ 24k-2: чистая q-таблица V (most-common стеади, vfamily_clean.npz)
|
||
| q | m43 | m171 | примечание |
|
||
|-----|-------|-------|------------|
|
||
| 0.1 | 0.5240 | 0.3781 | |
|
||
| 0.2 | 0.5240 | 0.4099 | |
|
||
| 0.3 | 0.5240 | 0.4373 | |
|
||
| 0.5 | 0.5241 | 0.4728 | контроль 10.32/11.82 ✓ |
|
||
| 0.7 | 0.5241 | 0.4915 | |
|
||
| 1.5 | 0.5245 | 0.5141 | |
|
||
| 2.0 | 0.5250 | 0.5176 | |
|
||
| 3.0 | 0.5262 | 0.5203 | |
|
||
| 5.0 | 0.5298 | 0.5217 | |
|
||
| 10 | 0.5431 | 0.5223 | |
|
||
|
||
1. **Глубина в центре константна ∀q** (0.524±0.002, лёгкий подъём к q=10).
|
||
2. **Форма узкого провала ИНВАРИАНТНА по q**: ширина на полуглубине 6–7 бинов,
|
||
на 3/4 глубины 8–9 бинов ∀q; хвост +128 бинов = 1.0000 ∀q.
|
||
3. Вся q-зависимость — ШИРОКАЯ ФОНОВАЯ КОМПОНЕНТА: V(f;q) = D(f)·Wq(f),
|
||
где D — фиксированный узкий провал у контент-бина (форма от q не зависит),
|
||
Wq — широкая юбка с глубиной, растущей при q↓ (Wq@171: 0.72@q0.1 … 1.00@q10).
|
||
Проверка соответствия Wq статическому twin-шаблону R^−p — ПЕРВЫЙ кандидат
|
||
следующего раунда (данные: vfamily_clean.npz + слоты R788/R7f8).
|
||
4. Через степенной закон применения это даёт большие dB на скирте:
|
||
A@2000(q=0.1) = 1.019·0.378^1.8345 → 15.5 дБ vs реф 15.33 ✓.
|
||
5. Каталоги данных раунда: /tmp/opencode/rn_q* (q-серия), rn_lv* (уровни −0..−30),
|
||
rn_ramp (рампа, для динамических опытов непригоден), multi2 (зонды+слоты),
|
||
level_pairs.npz, vfamily_clean.npz.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24k-3: структура V окончательна; законы ветвей; разрыв единого закона ============
|
||
|
||
### V = сумма независимых узких провалов на контентных бинах
|
||
Сырые кривые (vfamily_clean + сырой вывод): passband РОВНО 1.0000, провалы
|
||
~7 бинов ТОЛЬКО на бинах тонов (43 и 171), между ними 1.0000. Никакой широкой
|
||
юбки в V нет (dU вне контентных бинов = 0.000 во всех q). Корреляция dU со
|
||
статическими R-шаблонами = 0 ⇒ Wq-гипотеза 24k-2 снята; q меняет ГЛУБИНУ
|
||
провала на дальнем от fc бине, форма локального провала инвариантна.
|
||
|
||
### Железный закон применения (не менялся): A = 1.019·V^1.8345
|
||
Проверен побиновно на скирте: q=0.1: A@2000=15.31 дБ vs реф 15.33 ✓;
|
||
q=10: 10.13 vs 10.25 ✓.
|
||
|
||
### Закон ветви ЦЕНТРА (уровневая серия, q=0.5, sens=12)
|
||
```
|
||
cutV@fc[дБ] = 1.7436 · ln(1 + lvl_raw(fc)/0.3824) rms 0.045 дБ, 8 точек
|
||
```
|
||
lvl_raw — НАШ тракт (am/res·scale_factor, render48k RT_DUMP_BIN): наш детектор-
|
||
фронтенд совместим с плагином по шкале (без подгоночного k!).
|
||
|
||
### Закон ветви СКИРТА (bin 171, q-серия + уровневая)
|
||
Чисто лог-аффинный: cutV = 1.2775·ln(lvl_raw)+6.383, rms 0.087 дБ (q-серия);
|
||
в ln(1+x/b)-форме требует множитель аргумента λ≈12±6, НЕ константу ⇒ единый
|
||
однoаргументный закон от lvl_raw ОТСУТСТВУЕТ. Двухаргументная регрессия
|
||
ln(am)+c·ln(res) по всем 36 точкам: rms 0.64 дБ, систематика на скирте низких q
|
||
(+2.6 дБ) — скирт структурно круче.
|
||
|
||
### Следствия для транскрипции
|
||
1. Применение = готовая формула (выше), можно внедрять хоть сейчас: маска из
|
||
ЛЮБОЙ кривой M даёт корректную глубину через ^1.8345.
|
||
2. Осталась ОДНА неизвестная — отображение (frame spectrum, band params) →
|
||
V-провалы. Датасеты для идентификации полные: 36 стационарных точек
|
||
(уровни × q × {центр, скирт}) с нашими трактами.
|
||
3. Кандидаты механизма на проверку: (а) пер-бин уровень ПОСЛЕ частотных
|
||
бидир-IIR (сглаживание тянет центральный уровень в соседей — но ширина
|
||
провала 7 бинов против тау-IIR не сходится тривиально); (б) clamp/LUT
|
||
FUN_180563a60 (A,B,γ) на уровне полосы; (в) отдельная обработка
|
||
«центр vs вне» из BLOCKMAP (шаги 9-13: разные ветки флага 0x5408b8).
|
||
4. sens-зависимость ещё не снималась (все данные @sens=12).
|
||
|
||
### Статус канона
|
||
Канон НЕ тронут; все результаты — новые факты поверх. Коммиты раунда:
|
||
6629e8b (24j) → cc8ba34 (24k) → 6dc2967 (24k-2) → этот.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24k-4: RT_FIRCONV=3 внедрён; ХАЗАРД перезаписи рефов; корпус восстановлен ============
|
||
|
||
### RT_FIRCONV=3 (spectral.cpp): применение по закону 24k
|
||
`buf *= 1.019·mask^1.8345` побиновно. Первый прогон dual_b1q_0.5:
|
||
| конфиг | cut@500 | cut@2000 |
|
||
|--------|---------|----------|
|
||
| default | 16.73 | 12.01 |
|
||
| FIRCONV=3 | 30.31 | 21.71 |
|
||
| реф | 10.32 | 11.82 |
|
||
Ожидаемо: наш детектор M ≠ V (перерез в центре), экспонента углубляет.
|
||
FIRCONV=3 = правильный ПРИМЕНЯЮЩИЙ слой; дальше править только детектор.
|
||
Канон (default-путь) не тронут, corpus подтверждён (ниже).
|
||
|
||
### ХАЗАРД (был, исправлен): rendersnap2 удалял rpp.replace('.rpp','.wav')
|
||
При сэмплировании soothe-bt проектов это САМ РЕФЕРЕНСНЫЙ WAV → удалялся и
|
||
перезаписывался свежим рендером плагина. Пострадал dual_b1q_1.0.wav (обрыв,
|
||
EOFError в corpus). Остальные q-рефы перезаписались, но спектрально
|
||
идентичны (контрольные числа 10.32/11.82 воспроизвелись ✓). ФИКС: путь берётся
|
||
из RENDER_FILE проекта; удаление только по нему же. dep0b_rt.wav / out_rt.wav /
|
||
out-nofx.wav / out_dual300.wav битЫЕ СТАРОГО происхождения (rt-сессии),
|
||
корпусом не используются — не трогать.
|
||
Корпус после восстановления: t1kq 0.768 / t1k 2.114 / al 0.986 / res 0.437 /
|
||
dual 3.264 / comb 6.116 / TOTAL **2.286** (dual улучшился против 3.578 —
|
||
свежие рефы).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24l-2: ЖИВАЯ ПАРА (scratch, FIR); FIR = exp(log(bands)); поддержка log ОГРАНИЧЕНА ============
|
||
|
||
### Решающий снапшот
|
||
rendersnap2 v4 (слоты +540548/550/598/628) на multi.rpp → multi3/ph040.npz:
|
||
в ОДНОМ STOP пойманы scratch@540628 НЕнуловой (231 бин, max |s|=0.75) и FIR.
|
||
Проверка поточечно:
|
||
```
|
||
bin43: scratch=-0.6472 exp(s)=0.5235 FIR=0.5292 (+1.1%)
|
||
bin171: scratch=-0.7498 exp(s)=0.4724 FIR=0.4763 (+0.8%)
|
||
```
|
||
⇒ **FIR ≈ exp(scratch) = bands[i] напрямую** (расхождение ~1% — окна/ops).
|
||
«Строение кернела» не преобразует кривую — оно ВОСПРОИЗВОДИТ её через
|
||
log→exp-тракт (инверсия знака/твидлы/окна из 24l почти взаимно компенсируются;
|
||
их остаточная роль ~1% — выяснить отдельно).
|
||
|
||
### Поддержка лога ОГРАНИЧЕНА
|
||
scratch непулевой ТОЛЬКО в бинах 6..623 (~70..7300 Гц) при нотчах на 43 и 171.
|
||
Вне поддержки scratch=0 ⇒ exp=1 ⇒ ЕДИНичный passband кернела — вот механизм
|
||
ровной единички пасбэнда! Границы поддержки = детекторная активность/порог
|
||
(уточнить законом).
|
||
|
||
### Сшивка всех фактов раунда
|
||
```
|
||
applied_A = 1.019 · B^1.8345 (24k, rms 0.0025 дБ)
|
||
B = exp(scratch) ≈ bands[i] (24l-2, ±1%)
|
||
⇒ остаётся ОДНА неизвестная: кривая bands[i] детектора (наша M ≠ B).
|
||
Контроль: A(B@43=0.5235)=0.310 → −10.17 дБ vs реф −10.32 ✓;
|
||
глубокий флэш D@43=0.315..0.32 = ровно значение, дающее реф-глубину при
|
||
подстановке в закон (D и есть B другой мгновенной фазы адаптива).
|
||
```
|
||
|
||
### Буферы
|
||
buf548: стабилен внутри сессии, span 4..8193, НЕ классические твидлы
|
||
(pair512=(−0.707,−0.724)) — построение при init, реплицировать численно.
|
||
mask598: паттерн lane-select подтверждён.
|
||
|
||
### NEXT (24m)
|
||
1. Формула B(b): центр-закон 1.7436·ln(1+lvl/0.3824) уже есть; скирт-ветка +
|
||
границы поддержки (бины 6..623!) — следующие цели. Датасеты: multi3/
|
||
rn_lv*/rn_q* + тракты.
|
||
2. Численная репликация log→exp тракта (ops A-D, buf548) до ±1% для bit-exact.
|
||
3. sens/fc-серии для масштабирования закона.
|
||
4. RT_FIRCONV=3 уже даёт правильную композицию — детектор доводить до B.
|
||
|
||
## ============ UPDATE 2026-08-24m: ДВУХСТАДИЙНЫЙ ДЕТЕКТОР — ГЛУБОКАЯ/МЕЛКАЯ СВЯЗАНЫ КОНСТАНТОЙ γ₀≈1.79 В дБ; ЕДИНАЯ АРХИТЕКТУРА ЗАКОНА ============
|
||
|
||
### Данные
|
||
rendersnap2 v4 (со слотом scratch@540628) прогнан по всем q (sc_q*) и уровням
|
||
(sc_lv*): 18 полных кривых поддержки (deepest-scratch отбор). Стеади-scratch
|
||
внутри рендера КОНСТАНТЕН (s43=−0.6477±0.0005 через 50 фаз!), дискретные
|
||
состояния {0, релиз, стеади, глубина}.
|
||
|
||
### ГЛАВНЫЙ ФАКТ: cut_deep = γ₀ · cut_shallow, γ₀ = 1.79±0.02
|
||
| точка | shallow дБ | deep дБ | отношение |
|
||
|-------|-----------|---------|-----------|
|
||
| центр @43, drive 0 | 5.550 | 9.90-10.04 | 1.789 |
|
||
| @171 q=0.1 | 8.447 | 14.89 | 1.763 |
|
||
| @171 q=0.5 | 6.442 | 11.57 | 1.796 |
|
||
| @171 q=10 | 5.642 | 10.07 | 1.785 |
|
||
Отношение константанно по всем бинам/q/уровням ⇒ «таинственные» 1.805/1.8345 —
|
||
ЭТО ОНО: применяемый кернел = стадия-2 детектора, буфер обычно держит стадию-1,
|
||
отношение их дБ-масштабов = γ₀. (Точная природа двух стадий — кандидат:
|
||
двойной exp-проход тракта 24l или итеративный детекторный контур.)
|
||
⇒ закон применения A = 1.019·V^1.8345 и «audio = deep напрямую» — ЭКВИВАЛЕНТНЫЕ
|
||
записи одного факта.
|
||
|
||
### Формы ветвей стадии-S (shallow; стадия-D = ×γ₀)
|
||
```
|
||
центр (bin fc): cutS = 1.7436·ln(1 + lvl_raw/0.3824) rms 0.045 дБ (8 уровней)
|
||
скирт (@171 все q): cutS = 7.57 + 2.70·ln(1 + lvl_raw/0.341) rms 0.22 дБ (holdout q=1.5/3/10)
|
||
```
|
||
Скирт-офсет +7.57 дБ НЕ объяснён (кандидат: вторая компонента детектора /
|
||
кросс-тон вклад). Ширина провалов 5-6 бинов константна ∀q; между контентными
|
||
пиками B≡1.00000 ровно. Глубина центра сатурируется ~9.9-10.0 дБ (S-стадия)
|
||
∀q при drive 0; @171 монотонно растёт 10.07→14.89 при q↓.
|
||
|
||
### Уровневая серия (стадия-D, sc_lv*, центр):
|
||
drive −0..−30 дБ → s43: −1.143, −1.021, −0.903, −0.791, −0.685, −0.494, −0.339,
|
||
−0.222 (= cut_D 9.93…1.93 дБ).
|
||
|
||
### Следствия для транскрипции
|
||
Детектор = вычислить cutS(b) побиновно, затем маска = 10^(−γ₀·cutS/20).
|
||
Для bit-exact остались: (а) источник γ₀ и двух стадий в декомпе;
|
||
(б) объяснение офсета +7.57 скирта; (в) правило границ поддержки лога.
|
||
Датасет: /tmp/opencode/scratch_family.pkl (18 кривых) + tract_*.
|
||
Инструмент-обновление: rendersnap2 v4 пишет полный набор слотов включая
|
||
scratch — канал прямого наблюдения кривой детектора БЕЗ инверсии.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24m-2: RT_VLAW=1 внедрён; формула в коде верна; end-to-end разрыв = слой применения ============
|
||
|
||
### Реализация (framed_model.cpp, env RT_VLAW=1)
|
||
Ветка заменяет LUT/exp2/blend: `band_level = 10^(−γ₀·cutS/20)`,
|
||
`cutS = 1.7436·ln1p(lvl_raw/0.3824)` (центр-форма глобально), γ₀=1.79.
|
||
Проверено stderr-принтом (RT_VDBG): k=43 lvl=8.815→g=0.3189 ✓ формула в коде
|
||
даёт точные значения стадии-D. Маска идёт напрямую (exp2/warp/IIR3 отключаемы
|
||
RT_NOWARP/RT_NOIIR3).
|
||
|
||
### Статус end-to-end (dual_b1q_0.5):
|
||
```
|
||
REF: cut@500=10.32 cut@2000=11.82
|
||
VLAW: cut@500= 7.56 cut@2000= 6.41 (+NOWARP+NOIIR3: 5.36 / 1.73)
|
||
```
|
||
Формула кривой верна (маска@43=0.319 ≈ плагинная B@43=0.320!), но сквозной
|
||
результат недобирает ⇒ расхождение живёт В СЛОЕ ПРИМЕНЕНИЯ (STFT/WOLA):
|
||
эффективный gain на тоне = усреднение маски по главному лепестку окна;
|
||
узкий дип 5-6 бинов размывается. Исторически канон-константы были ПОДГОНЯНЫ
|
||
сквозь этот слой (смещение впитано), поэтому старые группы корпуса сходились.
|
||
Глубокая симуляция WOLA-нормировок не завершена (следующий раунд): кандидат —
|
||
плагин нормирует окно/хоп иначе (cfg {16384,8192} движка?), либо анализ без
|
||
окна/с другим окном. Ключевой контрольный эксперимент: прогнать ОДИНАКОВУЮ
|
||
кривую через наш слой и измерить эффективную глубину на тоне против плагина.
|
||
|
||
### Гигиена
|
||
TOOLING HAZARD снова сработал (same-second build → stale бинарник дал ложные
|
||
5.36/1.73). Протокол touch+mtime-проверка ОБЯЗАТЕЛЕН перед каждым замером.
|
||
RT_VDBG stderr-принт оставлен (env-gated, нулевая цена).
|
||
|
||
## ============ UPDATE 2026-08-24n: ЕДИНЫЙ ЗАКОН СТАДИИ-S; RT_IIR12=0 КРИТИЧЕН; ЦЕНТР 9.40/10.32; РАМПА ПОДТВЕРЖДАЛА γ₀ ============
|
||
|
||
### Единая форма стадии-S (shallow) — обе ветви одной формулой:
|
||
```
|
||
cutS(b) = α · ln(1 + lvl_raw(b)/0.3824) + Δ(b)
|
||
центр (бин fc): α=1.729 Δ=0.00 rms 0.044 дБ (уровневая серия)
|
||
второй контент-пик: α=1.656 Δ=+4.177 rms 0.051 дБ (q-серия @171)
|
||
```
|
||
Одинаковый масштаб β=0.3824 (!), близкие α. Δ эквивалентен сдвигу уровня
|
||
на +2.23 в lvl-домене (детекторный порог/вторая компонента) ТОЛЬКО у
|
||
внецентрового пика. У зондов вне пиков B≡1 (Δ не действует).
|
||
Стадия-D = ×γ₀=1.79 (см. 24m).
|
||
|
||
### РАМПА подтверждает закон на независимых данных
|
||
Траектория u(t) через 30 дБ драйва (ramp_ref, скользящий Гёрцель):
|
||
измеренное vs 1.79·cutS: остатки +0.12..+0.23 дБ системно (лаг атаки при
|
||
падающей рампе — детектор видит исторически больший уровень). Тройное
|
||
подтверждение γ₀ (уровневая серия rms 0.0025 + q-таблица + рампа).
|
||
|
||
### RT_VLAW статуса-2
|
||
КРИТИЧНО: частотные бидир-IIR1/2 (RT_IIR12) работают ПОВЕРХ vlaw-маски и
|
||
размывают плечи дипа ⇒ рабочий набор флагов:
|
||
```
|
||
RT_VLAW=1 RT_NOWARP=1 RT_NOIIR3=1 RT_IIR12=0
|
||
```
|
||
Результат dual_b1q_0.5: **cut@500=9.40** (реф 10.32, зазор −0.92 дБ),
|
||
cut@2000=4.14 (реф 11.82 — Δ-ветка ещё не реализована в коде).
|
||
Зазор центра-0.9 дБ: кандидат — окно/нормировка нашего STFT-слоя либо тонкая
|
||
калибровка α/γ₀. Следующие шаги: (а) реализовать Δ-ветку (правило второго
|
||
пика); (б) разобрать слой применения до сходимости; (в) источник γ₀/стадий
|
||
в декомпе; (г) корпус с полным флаг-набором.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24n-2: Δ-ветка в коде; dual 0.708 (было 3.264); константы НЕ универсальны ============
|
||
|
||
### Реализовано (framed_model.cpp, vlaw-ветка v2)
|
||
Δ-правило: локальные максимумы lvl_raw>0.25 вне ±8 бинов от fc помечают
|
||
окрестность ±3 бина → +4.177 дБ. Двухпроходно (без O(n²)).
|
||
dual_b1q_0.5 end-to-end: **9.32 / 11.58** vs реф 10.32 / 11.82
|
||
(скирт −0.24 дБ!, центр −1.0).
|
||
|
||
### Корпус с флагами (62 случая):
|
||
| group | canon | VLAW |
|
||
|-------|-------|------|
|
||
| t1kq | 0.768 | 3.633 |
|
||
| t1k | 2.114 | 3.296 |
|
||
| al | 0.986 | 2.620 |
|
||
| res | 0.437 | 3.896 |
|
||
| **dual** | 3.264 | **0.708** |
|
||
| comb | 6.116 | 4.014 |
|
||
| TOTAL | 2.286 | 2.515 |
|
||
|
||
### Диагноз регресса одноканальных групп
|
||
t1kq-диагностика (тон@1000, fc∈{500,1000,2000}):
|
||
- fc=500 (тон вне центра): реф 5.45, VLAW 10.37 ⇒ Δ НЕ должен применяться к
|
||
этому случаю (реф = shallow-уровень!); при q≈1 и отступе 500 Гц офсета нет,
|
||
а в dual (q=0.5, отступ 1500 Гц) был +4.18 ⇒ правило Δ зависит от q/дистанции.
|
||
- fc=1000 (тон в центре): реф 8.70 vs dual-центр 10.32 при близком lvl ⇒
|
||
глубина зависит от q/fc — константы α_eff/γ₀_dual не универсальны.
|
||
Вывод: закон верен В СЕМЕЙСТВЕ калибровки (dual_b1q_0.5: q=0.5, sens=12).
|
||
Параметризация (α, γ₀, Δ-правило) по (q, fc, sens) — следующая большая задача;
|
||
методика (rendersnap2+зонды+рампа) готова, датасеты расширяются механически.
|
||
Канон не тронут: default-корпус воспроизведён (TOTAL 2.286 ✓).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24o: точки параметризации — sens/q серии; закон НЕ монотонен по lvl между конфигами ============
|
||
|
||
### Новые захваты (rendersnap2 v4, deepest scratch, ПРАВИЛЬНЫЙ центр-бин!)
|
||
ВНИМАНИЕ баг анализа: для fc≠500 центр-бин K=round(fc/48000·4096)
|
||
(fc=1000 → K=85!). Данные:
|
||
```
|
||
проект lvl_ours@K cut_D@K реф-кат
|
||
dual q0.5 s12 8.55 10.03 10.32
|
||
qmap q2 s12 20.11 15.44 15.94
|
||
qmap q3 s12 20.06 15.43 15.93
|
||
qmap q6 s12 19.78 15.39 15.90
|
||
sens_6 q1 4.88 10.68 10.90
|
||
sens_9 q1 9.91 11.20 11.44
|
||
sens_18 q1 83.1 11.74 12.01
|
||
sens_24 q1 341 11.74 12.01
|
||
```
|
||
ref_cut ≈ cut_D везде (−0.3..−0.5 дБ) — аудио слышит глубокую стадию,
|
||
подтверждение универсальное.
|
||
|
||
### Наблюдения
|
||
1. **γ₀ проверяется и тут**: shallow=q-таблицы дают те же ×1.79 отношения.
|
||
2. **Насыщение по sens**: lvl растёт 70× (4.9→341), кат меняется лишь
|
||
10.68→11.74 и выходит на плато ≈11.74 (q=1).
|
||
3. **Не монотонно по lvl МЕЖДУ конфигами**: (q2,lvl20)→15.44 но
|
||
(q1,lvl83)→11.74. Закон зависит от q ОТДЕЛЬНО от lvl (форма/масштаб).
|
||
Наивная инверсия даёт β(q2..6)≈0.138 против β(q0.5)≈0.35 — но это
|
||
подгонка одной точки; нужна фит-форма с q-аргументом.
|
||
4. Кат всегда = плато конкретной конфигурации: cap(q,sens).
|
||
Гипотеза: cap ∝ глубина полосы (зависит от q через геометрию шаблона
|
||
и/или sens-гейн детектора).
|
||
|
||
### Датасеты раунда
|
||
/tmp/opencode/sc_qmap_1000_q{2,3,6}, sc_sens_{6,9,18,24} (+sc_q*, sc_lv*),
|
||
param_points.pkl, tract_qmap*/tract_sens*.
|
||
Рефы qmap_*/sens_* в soothe-bt целы (перезаписаны свежими рендерами
|
||
rendersnap2 v4 с RENDER_FILE-фиксом — контроль чисел сошёлся).
|
||
|
||
### NEXT (24p)
|
||
1. Полный кривой фит: cut(b)=F(lvl(b),res(b);q,sens) по ВСЕМ бинам всех
|
||
проектов (сотни точек с формой дипа) вместо одиночных бинов.
|
||
2. sens-плато: найти механизм насыщения (кандидат: LUT FUN_180563a60 верхний
|
||
упор B=28 дБ? 11.74·2стадии? нет — искать точно).
|
||
3. Δ-правило для t1kq-семейства (fc-дистанция в октавах? q-нормировка?).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24o-2: глобальный фит формы НЕ сошёлся — путь к механизму через декомп ============
|
||
|
||
### Попытка: cut = α·[ln1p(k·L/β) − ln1p(k·L/C)] (двусторонняя сатурация,
|
||
α,β,C общие + пер-проектный масштаб k) по 13 кривым × все значимые бины.
|
||
Результат: rms 1.40 дБ, шкалы k хаотичны (k(dual)=19.9 при том что
|
||
константы откалиброваны на dual!), qmap-кривые не вошли (тракт-файлы
|
||
перепроверить). ВЫВОД: форма «мягкий предел с общими константами» не
|
||
описывает семейство; пер-конфигурационные капы (q0.5:10.03 / q1s24:11.74 /
|
||
q2:15.44) не выводятся из одной (β,C).
|
||
|
||
### Интерпретация
|
||
Кап растёт с q (шаблон острее ⇒ глубже детекция?) и слегка с sens.
|
||
Кандидаты механизма насыщения и q-зависимости:
|
||
(а) частотно-варпированные коэф. генератора FUN_180533340 (p-экспонента!)
|
||
— сила IIR зависит от fc/sr и p;
|
||
(б) LUT FUN_180563a60 (A,B,γ в dB-домене);
|
||
(в) mix^p / blend-цепочка BLOCKMAP pre-combine;
|
||
(г) двухстадийность = двойной проход exp-тракта (24l) — тогда γ₀ должно
|
||
быть точной функцией отношений окон/нормировок, вычислимо из декомпа.
|
||
Путь по золотому правилу: СТАТИЧЕСКИЙ ДЕКОД этих тел (все адреса известны),
|
||
затем параметризация станет следствием, а не подгонкой.
|
||
|
||
### Датасеты для будущих фитов сохранены
|
||
/tmp/opencode/mega_curves.pkl (13 полных кривых cut+lvl), param_points.pkl,
|
||
scratch_family.pkl, плюс сырые ph*.npz каталоги sc_*.
|
||
### Коммиты раунда 24j..24o-2
|
||
6629e8b, cc8ba34, 6dc2967, 1605e0b, 65fb983, fe01ef5(?), b00a8a6(?), fc224b0,
|
||
31cc554, 5f822eb + этот. Все запушены в origin/main.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24p: СКАЛЯРНОСТЬ СТАДИЙ ПОБИННО; ГИПОТЕЗА BLEND; mix-тест некорректен ============
|
||
|
||
### Побиновое отношение стадий (multi3, чистые данные)
|
||
deep/steady по 82 значимым бинам: **среднее 1.760, std 0.0115** (min 1.698,
|
||
max 1.822 на краях с малыми катами) ⇒ стадии = ОДНА ФУНКЦИЯ × константа в
|
||
дБ-домене, не разные формы. Механизм — скалярный множитель dB-стадии.
|
||
|
||
### Гипотеза blend: γ₀ = 1 + blend (=1+0.8=1.8)
|
||
xmm10=0.8 (blend!) стоит в парно-скалярной ветке FIR-цикла (24l). γ₀≈1.76-1.83
|
||
во всех измерениях — 1.80 в центре. ПРОВЕРЯЕМОЕ ПРЕДСКАЗАНИЕ: γ₀(mix) =
|
||
1 + f(mix); при mix→0 γ₀→1.
|
||
|
||
### mix-тест (s_corr_mix_*) НЕ КОРРЕКТЕН
|
||
Проекты используют другой вход (resonant.wav, кат 0.6 дБ против наших 5.5) и
|
||
разные уровни; глубокие состояния не отделяются. Нужен клон multi.rpp с
|
||
вариацией ТОЛЬКО mix внутри VST-чанка (требует decode/encode чанка или
|
||
REAPER-автоматизации setparam.lua) — отдельная задача.
|
||
Захваты sc_s_corr_mix_* сохранены (вход resonant).
|
||
|
||
### Статус раунда 24j..24p
|
||
- Применение: per-bin complex-multiply, A=1.019·V^1.8345 (≡ deep=γ₀×shallow)
|
||
- Стадия-S: α·ln1p(lvl/β=0.3824)+Δ, центр откалиброван (rms 0.044)
|
||
- RT_VLAW=1 в коде: dual 9.32/11.58 vs реф 10.32/11.82; корпус dual 0.708
|
||
- Открыто: q/sens-параметризация капов; Δ-правило для других семейств;
|
||
источник γ₀/двух стадий в декомпе; mix-тест с контролируемой вариацией.
|
||
- Канон не тронут; все эксперименты env-gated.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24q: КОНТРОЛИРУЕМЫЙ depth/mix СВИП (patchparam.py!) — blend-гипотеза опровергнута; +0.4 дБ константа ============
|
||
|
||
### Инструмент
|
||
patchparam.py правит <PARAM id value> внутри base64-VST-чанка RPP. Полная
|
||
карта параметров multi.rpp получена (band1: 500/q0.5/sens12/on ✓;
|
||
**depth=0.864**, mix=100, selectivity=10, sharpness=10, resolution=1,
|
||
offline oversample=3). Созданы контролируемые клоны: depth∈{0.5,0.7,0.9},
|
||
mix∈{25,50,75}, вход/прочее идентичны.
|
||
|
||
### Результаты свипов
|
||
DEPTH (mix=100): γ₀ = 1.7597 / 1.7600 / 1.7608 для depth 0.5/0.7/0.9 —
|
||
γ₀ ОТ ГЛУБИНЫ НЕ ЗАВИСИТ; cutS слегка растёт (5.30→5.66).
|
||
MIX: cut_S = 1.10 / 2.36 / 3.84 / 5.55 (25/50/75/100) — стадия-S масштабируется
|
||
миксом (см. BLOCKMAP mix^p); глубокие состояния в захватах m50/m75 отсутствуют.
|
||
|
||
### Аудио (чистые рендеры рефов) против стадии-S:
|
||
```
|
||
вариант cut@500 cut@2000 S@43 S@171 audio/S@500
|
||
m25 1.79 1.93 1.098 — 1.63
|
||
m50 3.79 4.14 2.363 — 1.61
|
||
m75 6.44 7.15 3.841 — 1.68
|
||
d0.5(m100)9.70 11.21 5.296 — 1.83
|
||
m100 orig 10.32 11.84 5.550 6.442 1.859/1.839
|
||
```
|
||
⇒ **blend-гипотеза «γ₀=1+mix» ОПРОВЕРГНУТА**: отношение audio/steady НИКОГДА
|
||
не падает до 1 (даже при mix=25 оно 1.63); слабо растёт с mix (1.6→1.86).
|
||
Двухстадийность существует ВО ВСЕХ mix, микс лишь модулирует величину.
|
||
Константа раунда: audio − самый-глубокий-захваченный-кернел = **+0.40..+0.45 дБ**
|
||
(d0.5: 9.70−9.32=+0.38; m100: 10.32−9.93=+0.39) — стабильный зазор захвата.
|
||
|
||
### ХАЗАРД (новый): rendersnap2-клоны с одним RENDER_FILE
|
||
Все клоны multi.rpp наследуют RENDER_FILE=/tmp/opencode/multi_ref.wav ⇒ каждый
|
||
захват перезаписывал общий реф последней конфигурацией. При работе с клонами —
|
||
ПЕРВЫМ ДЕЛОМ переписывать RENDER_FILE (как сделано для *_ref вариантов).
|
||
|
||
### NEXT (24r)
|
||
1. Зазор +0.4 дБ: либо мгновенная фаза адаптива, либо пост-df0 обработка —
|
||
поймать пару (pre-df0, post-df0) в одном STOP (слот track@768 уже пишется).
|
||
2. Источник γ₀ и двух стадий в декомпе: df0(FIR,track_i) семантика!
|
||
3. Δ/q/sens параметризация по накопленным датасетам.
|
||
4. Корпус dual-семейства через RT_VLAW после фиксации зазора.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24r: track статичен; DTFT-тест кернелов; упаковка потребления — последний разрыв ============
|
||
|
||
### Слот track@768 добавлен в rendersnap2 v5; multi4 снят
|
||
track ИДЕНТИЧЕН во всех фазах (deep==steady бит-в-бит) — статическая таблица,
|
||
не динамическая вторая стадия.
|
||
|
||
### DTFT-тест (ирfft кернелов → точный отклик на частотах тонов):
|
||
```
|
||
@500 @2000
|
||
shallow 0.524 (5.61 дБ) 0.474 (6.49)
|
||
deep 0.294 (10.63)✓ 0.594 (4.52)✗✗
|
||
REF 0.305 (10.32) 0.257 (11.82)
|
||
```
|
||
Deep-кернел совпадает с рефом НА ЦЕНТРЕ (+0.3 дБ) но проваливается на скирте
|
||
⇒ irfft(captured_spectrum) НЕ есть тот фильтр, что слышен на скирте.
|
||
Причина: спектр кернела в буфере НЕ эрмитов (fir_im≠0, антисимметрика), а
|
||
потребление идёт через комплексные стадии ops A–D (твидлы buf548) — их
|
||
упаковка/распаковка определяет действительный фильтр. Это ПОСЛЕДНИЙ
|
||
нерасшифрованный кусок применения.
|
||
|
||
### Инструменты состояния
|
||
rendersnap2 v5 (SLOTS+768), patchparam.py — рабочий правщик VST-чанка
|
||
(параметры: depth/mix/bands/selectivity/sharpness/...), полная карта PARAM id
|
||
получена из multi.rpp. Клоны проектов: multi_d{0.5,0.7,0.9}, multi_m{25,50,75}
|
||
(+_ref рендеры).
|
||
|
||
### NEXT (24s)
|
||
1. Декод ops A–D (воркер 4ca80→диспетчер типов; твидл-стадии над упакованным
|
||
вещественным спектром) — замкнёт путь преобразования буфер⇒фильтр.
|
||
2. После этого: bit-exact репликация применения станет механической.
|
||
3. Параллельно: Δ/q/sens по датасетам (mega_curves.pkl).
|
||
|
||
## ============ UPDATE 2026-08-24v: СЛОЙ STFT НАЙДЕН — БЕЗ СИНТЕЗ-ОКНА; DUAL КОРПУС 0.193 ============
|
||
|
||
### Эксперимент RT_SYN/RT_WIN (spectral.cpp, env-gated)
|
||
Варианты слоя на dual_b1q_0.5 (маска VLAW v3 фиксирована):
|
||
```
|
||
база sym-hann + syn-окно : 9.47 / 11.16
|
||
периодический ханн : 9.48 / 11.16
|
||
прямоугольный анализ : 10.18 / 11.27
|
||
БЕЗ СИНТЕЗ-ОКНА : 10.27 / 11.59 ← победитель
|
||
```
|
||
⇒ плагин НЕ домножает выход обратного преобразования на окно
|
||
(архитектура partitioned-conv движка; нормировка Σw_ana·1/hop).
|
||
Зазор к рефу: −0.05 (@500) / −0.23 (@2000).
|
||
|
||
### Корпус с RT_VLAW+RT_SYN=1 (+NOWARP/NOIIR3/IIR12=0):
|
||
| group | canon | VLAW+SYN |
|
||
|-------|-------|----------|
|
||
| t1kq | 0.768 | 3.380 |
|
||
| t1k | 2.114 | 3.271 |
|
||
| al | 0.986 | 1.872 |
|
||
| res | 0.437 | 3.861 |
|
||
| **dual** | 3.264 | **0.193 (max 0.438!)** |
|
||
| comb | 6.116 | 4.410 |
|
||
| TOTAL | 2.286 | 2.252 |
|
||
|
||
### Статус
|
||
dual-семейство РЕШЕНО (все 22 случая <0.44 дБ). Одноканальные группы ждут
|
||
параметризации α/β/c/Δ по (q, fc, sens) — константы откалиброваны на
|
||
dual(q=0.5). Механика применения и слой полностью декодированы.
|
||
|
||
### NEXT (24w)
|
||
1. Параметризация закона по q/sens/fc (датасеты sc_* готовы; методика
|
||
deepest-scratch + Гёрцель рефов).
|
||
2. Δ-правило вторых пиков из декомпа pre-combine.
|
||
3. Решение о каноне: после параметризации сравнить TOTAL vs 2.286.
|
||
|
||
## ============ UPDATE 2026-08-24s: ПРИМЕНЁННЫЙ ЗАКОН ПРЯМО (rms 0.016 дБ); АУДИО = exp(глубокого scratch); RT_VLAW v3 ============
|
||
|
||
### Развязка объектов (главная путаница раундов устранена)
|
||
Для deep-кадра multi4-ph063 одновременно:
|
||
```
|
||
exp(scratch)[43]=0.320 [171]=0.267 ↔ аудио 0.305 / 0.257 ✓✓
|
||
FIR-буфер [43]=0.528 [171]=0.475 ← промежуточное представление
|
||
```
|
||
⇒ **АУДИО = exp(глубокого scratch) НАПРЯМУЮ** (оба тона!). Буферные состояния
|
||
и все γ₀-соотношения — внутренняя кухня тракта построения (мин.-фазовая
|
||
упаковка exp(s−iH(s)) подтверждена на re/im ±1%), для транскрипции НЕ нужны.
|
||
Применение = побиновное умножение кадра на ВЕЩЕСТВЕННУЮ маску exp(s_deep).
|
||
|
||
### ПРИМЕНЁННЫЙ ЗАКОН (центр, 8 уровней драйва):
|
||
```
|
||
cut_dB(b) = 3.2193 · ln(1 + lvl_raw(b)/0.4927) + 0.5423 rms 0.016 дБ
|
||
скирт-офсет (второй пик): +7.46 − 0.54 ≈ +6.92 дБ сверху (глуб-домен,
|
||
согласован с ×γ₀ старого 4.18)
|
||
```
|
||
Контроль lvl=8.85 → 10.02 vs реф 10.32 (зазор 0.30 — фаза адаптива).
|
||
|
||
### RT_VLAW v3 (константы применённой стадии напрямую):
|
||
dual_b1q_0.5: **9.47 / 11.16** vs реф 10.32 / 11.82 (было 9.32/11.58).
|
||
Остаток −0.85/−0.66 дБ = СЛОЙ STFT (усреднение по лепестку, диагноз 24m-2)
|
||
— следующий кандидат на разбор: параметры окна/hop нашего render48k против
|
||
движка {16384,8192}.
|
||
|
||
### Формулировки для транскрипции (итог раунда)
|
||
```
|
||
lvl_raw(b) = am(b)/res(b)·scale (наш детектор-фронтенд ✓)
|
||
cut_D(b) = 3.2193·ln1p(lvl/0.4927)+0.54 [+6.92 у вторых пиков]
|
||
mask = 10^(−cut_D/20) (вещественная!)
|
||
применение = per-bin multiply кадра
|
||
```
|
||
Открыто: параметризация (α=3.22, β=0.49, Δ) по q/sens; слой STFT;
|
||
мин.-фазовая упаковка буфера как объект декодера ops A–D.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24w-2: параметризация — скалярные комбинации lvl/res отвергнуты ============
|
||
|
||
### Данные центров всех конфигов (lvl_ours, res_c, cut_D):
|
||
dual q0.5 (8.55,.117,10.03); qmap q2/3/6 (~20,.117,15.4);
|
||
sens6 (4.88,.484,10.68); sens9 (9.91,.238,11.20); sens18 (83,.028,11.74);
|
||
sens24 (341,.0069,11.74).
|
||
Замечание: наш twin res@центра почти НЕ зависит от q (0.117 при q0.5 и q2!),
|
||
зависит от sens (s18→0.028, s24→0.0069) — sens реализован усилением шаблона.
|
||
lvl между конфигами масштабируется по sens корректно (×2.03/+3дБ ✓).
|
||
|
||
### Отвергнуты (немонотонность против фактов):
|
||
- F(lvl) одна на все конфиги: (q2,L20)→15.44 vs (q1s18,L83)→11.74 ✗
|
||
- X=L·R^b, L/R, L·R² и др.: порядок точек противоречит катам ✗
|
||
⇒ q входит в закон через МЕХАНИЗМ (частотные IIR с коэф. FUN_180533340,
|
||
зависящими от selectivity→p; либо LUT-индексация), не скаляром.
|
||
Следующий бит-exact шаг: декод p/tau/mult-параметров 533340 (сеттеры
|
||
selectivity/sharpness) и их роль в формировании bands[].
|
||
|
||
### Итог раундов 24j..24w-2 (все запушены)
|
||
Применение декодировано до формул; слой STFT найден (без синтез-окна);
|
||
dual-семейство решено корпусно (0.193 mean). Параметризация остальных
|
||
семейств = следующий мейлстоун (методика готова).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24x: структурный диф кривых; таблица k-маппинга эффективных уровней ============
|
||
|
||
### Структурный диф (dual q0.5, plug exp(scratch) vs наш канон-prewarp)
|
||
```
|
||
plug_B our причина расхождения
|
||
passband 1.0000 0.8000 blend-флор (у VLAW=1 ✓)
|
||
меж дипами 1.0000 0.60-0.80 warp/res^rp-одеяло (VLAW отключает ✓)
|
||
дип@43 0.315 0.159 глубина-закон (VLAW ✓)
|
||
плечи узкие/крутые широкие IIR3/freq-IIR (VLAW без них ✓)
|
||
```
|
||
⇒ RT_VLAW-обход структурно ВЕРЕН; канон-цепь не воспроизводит ни одну зону.
|
||
|
||
### Таблица k-маппинга (lvl_plugin_implied / lvl_ours, инверсия dual-закона)
|
||
```
|
||
конфиг lvl_ours lvl_impl k
|
||
dual q0.5 s12 8.550 8.607 0.9934 ← калибровка
|
||
qmap q2 s12 20.107 49.896 0.4030
|
||
qmap q3 s12 20.055 49.740 0.4032 ← константа ∀q≥2!
|
||
qmap q6 s12 19.780 49.119 0.4027
|
||
sens6 q1 4.875 10.994 0.4434
|
||
sens9 q1 9.909 13.008 0.7618
|
||
sens18 q1 83.085 15.473 5.3696
|
||
sens24 q1 341.03 15.473 22.0402
|
||
```
|
||
Свойства: k(q≥2)=const 0.403; k(sens) растёт экспоненциально на верхнем крае;
|
||
ни аффинная, ни степенная формы не покрывают ⇒ фронтенд детектора плагина
|
||
(am/res/scale) отличается от нашего конфиг-зависимо. Bit-exact замыкание =
|
||
точный декод twin_coeff + амплитудного тракта детектора (модуль twin/fn529fe0).
|
||
|
||
### Практическое следствие
|
||
Пока k-маппинг не декодирован, VLAW-константы действительны только для
|
||
калибровочного семейства (dual q0.5 s12) — что уже даёт корпус 0.193.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24y: закон подтверждён на fc500-q2 (−0.06 дБ); САТУРАЦИЯ уровня плагина обнаружена ============
|
||
|
||
### Расширение валидации закона
|
||
```
|
||
конфиг lvl_ours cut_D предсказание (dual-константы) ошибка
|
||
fc500 q2 s12 8.782 10.02 9.96 −0.06 ✓
|
||
fc1000 q2 s12 20.107 15.44 12.32 −3.12 ✗
|
||
```
|
||
Закон работает ЗА пределами калибровочной точки по q (та же fc!) —
|
||
семейство шире, чем думали.
|
||
|
||
### Обнаружена САТУРАЦИЯ эффективного уровня плагина
|
||
На tone1k-проектах (am@центра константна 0.7307):
|
||
```
|
||
конфиг наш lvl impl-lvl (инверсия закона)
|
||
q2 s12 20.1 50.2 ← impl РАСТЁТ
|
||
s18 q1 83.1 15.47 ← упёрлось
|
||
s24 q1 341 15.47 ← жёсткое плато
|
||
```
|
||
Их детекторный уровень НАСЫЩАЕТСЯ (плато impl≈15.47 при q1), тогда как наш
|
||
lvl растёт линейно с sens. Плато q≥2 на 49.9 — аналогичный эффект от q.
|
||
Формы-кандидаты: лог-компрессия res-гейна, деление на (res+floor),
|
||
или LUT FUN_180563a60 (A,B домен!). Это и есть источник «капов» 24o.
|
||
|
||
### Следствие для транскрипции
|
||
Между нашим lvl и законом нужна СТАДИЯ КОМПРЕССИИ уровня (насыщаемая),
|
||
параметризованная по конфигу. Кандидат №1 — именно LUT 563a60 (дБ-домен,
|
||
A/B упоры = насыщение!). Её декод = следующий мейлстоун.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24z-2: попытка живого съёма A/B/γ — объект банды не найден пока ============
|
||
|
||
rendersnap2 v6 пытается читать цепочку [ctx+0x540678]→band→[+0x180]→sub→A/B/γ.
|
||
Результат: поля отсутствуют ⇒ [ctx+0x540678] указывает на ДАННЫЕ кривой
|
||
(массив float'ов), а не на объект банды. Адрес массива ОБЪЕКТОВ банд
|
||
(тех, что с [+0x180]-LUT и [+0x4198]-входом из 563a60) нужно найти отдельно:
|
||
кандидат — скан heap'а на структуры с валидным [sub+0x90]/[sub+0xc] паттерном,
|
||
либо трассировка инициализации (563a60 получает rcx=band obj — ловить её
|
||
аргументы при param-rebuild через rendersnap2-стиль STOP в момент загрузки).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24aa: 563a60 — GUI-ветка; компрессия аудио в семантиках ops шагов 1–19 ============
|
||
|
||
Скан всей rw-памяти при рендере (scan_pairs.py): пары (A<0<B) детекторной
|
||
LUT В ПАМЯТИ ОТСУТСТВУЮТ (только статический мусор данных). ⇒ FUN_180563a60
|
||
и её объекты — GUI-ветка построения кривой для отображения (согласуется с
|
||
пометкой 22b «GUI-only»!). Аудио-компрессия уровня реализована ВНУТРИ
|
||
семантик ops шагов 1–19 BLOCKMAP (bigkernels exp/mask-семейство,
|
||
combine/fma/axpy с att/rel массивами) — т.е. ровно те тела, что наш
|
||
структурный канон транскрибировал частично/плейсхолдерами.
|
||
|
||
### Финальная карта остатка (bit-exact):
|
||
1. Декод тел bigkernel-семейства 140950..140b60 + fma/axpy/sub/add тунков
|
||
шагов 9–19 → точная математика bands[]→scratch.
|
||
2. После неё α/β/c/Δ становятся вычисленными (не фитовыми) для ВСЕХ
|
||
конфигураций автоматически.
|
||
3. Слой STFT уже закрыт (RT_SYN=1); dual-корпус 0.193.
|
||
|
||
Инструменты раунда: scan_lutsub.py, scan_pairs.py (диагностика памяти).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24ab: opB диспетчер; ops A–D паркованы как buffer-level; итог сессии ============
|
||
|
||
### opB (th1a90→5560→1a0c0) структурно
|
||
Аргументы: (dst, src, descriptor); дескриптор: тег 6, подтег [obj+4]≤4 →
|
||
диспатч-таблица кернелов, скаляр [obj+0x10]; после вызова — Хермитова
|
||
упаковка (элемент[1]→позиция[маски], обнуление мнимых). Полные формулы
|
||
подтег-кернелов — в диспатч-таблицах за пределами практической нужды:
|
||
эксп. подтверждение exp(conj-analytic(s)) ±1% + corpus dual 0.193 с
|
||
чисто вещественной маской закрывают слышимый путь.
|
||
|
||
### РЕШЕНИЕ: ops A–D паркованы (buffer-level only)
|
||
Их внутренности влияют только на байты буфера FIR@540668, не на звук
|
||
(аудио=exp(scratch) напрямую, 24s). Возвращаться — только при требовании
|
||
побайтовой репликации буфера.
|
||
|
||
### ИТОГ СЕССИИ (24j..24ab, ~42 коммита, все запушены)
|
||
ПРИМЕНЕНИЕ ДЕКОДИРОВАНО ДО ФОРМУЛ:
|
||
```
|
||
mask(b) = 10^(−cut_D(b)/20)
|
||
cut_D(b) = α·ln1p(lvl_raw(b)/β)+c [+Δ у вторых пиков]
|
||
калибровка dual(q0.5,s12): α=3.2193 β=0.4927 c=0.54 Δ=+6.92, rms 0.016 дБ
|
||
слой = STFT БЕЗ синтез-окна, анализ-окно из WIN_WINDOW-семейства
|
||
детектор-фронтенд lvl_raw = am/res·scale (наш, float-parity ✓)
|
||
```
|
||
Корпус: dual 0.193 (было 3.264). Канон нетронут (2.286).
|
||
Осталось для полного покрытия конфигураций: k-маппинг фронтенда
|
||
(k=0.403 ∀q≥2; экспонента по sens — см. 24x) и Δ-правило.
|
||
Инструменты: rendersnap2 v7, patchparam.py, disasm_func.py, probe/scan-*.
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24bb: P1 итог — простые отображения отвергнуты; форма нотчей плато+асимметричные края; коэф. 533340 требуют переверификации ============
|
||
|
||
### Отвергнуты (P1, чистый анализ данных):
|
||
1. k(b) константный внутри конфига ✗ (вариация до 80×)
|
||
2. bidir-сглаживание lvl коэф. 533340 (tau1200/360/p1/C1000) ✗ не меняет центр
|
||
3. B ∝ am ✗ (cv 400%)
|
||
4. Параметрический бамп (cos^s, W свободный): rms ≈4 дБ на всех конфигах ✗
|
||
|
||
### НОВЫЙ ФАКТ — ФОРМА НОТЧЕЙ:
|
||
Провала ПЛОСКИЕ сверху: @dual q0.5 вокруг 171: B=[0.596,0.334,0.271,0.264,
|
||
0.302,0.439,0.779] для бинов [168..174] — плато 169–173 с крутыми краями.
|
||
Наш lvl асимметричен (7.07/8.85/2.51 на 42/43/44), плагин симметричен
|
||
относно дробной позиции тона ⇒ кривая = функция ОТСТУПА от пика, не lvl!
|
||
|
||
### Универсальный профиль края снят (3 конфига):
|
||
левый край пологий (~7 бинов до 1.0), правый крутой (~3 бина),
|
||
плато |d|<1.5. Таблица в логе анализа; для встраивания годится эмпирика.
|
||
|
||
### ПРОТИВОРЕЧИЕ, УКАЗЫВАЮЩЕЕ НА ОШИБКУ ТРАНСКРИПЦИИ 533340:
|
||
В дизасме после c=1/(1+g/mult·tau) идёт ЕЩЁ ОДНО умножение на g перед exp
|
||
(xmm2*=xmm3) — моя ген-формула это пропускает ⇒ реальные коэф. сильнее
|
||
на низких бинах. Переверифицировать пошагово — приоритет следующего
|
||
раунда: правильные коэф. могут дать сглаживание, симметризующее дипы
|
||
и объясняющее плато.
|
||
|
||
### Статус: применение dual-семейства решено (0.193); кросс-конфиг ждёт
|
||
корректных коэф. частотного сглаживания (переверификация 533340).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24dd: клампы опровергнуты; сатурация локализована во фронтенде (twin gain пол) ============
|
||
|
||
### Клампы параметров ОПРОВЕРГНУТЫ рефами
|
||
dual q2/q3/q5/q10 скирты различаются плавно (max|diff| q2-q3=0.13,
|
||
q5-q10=1.17 дБ) — никакого жёсткого клампа q; плато impl-уровня qmap
|
||
(49.90/49.74/49.12) — просто сильная компрессия закона, не кламп.
|
||
Аналогично sens: реф-различия есть.
|
||
|
||
### Локализация сатурации: ФРОНТЕНД (twin gain пол)
|
||
Их эффективный уровень (impl) по sens-серии fc1000q1:
|
||
```
|
||
sens impl-lvl Δimpl на +3дБ sens
|
||
6 11.03 —
|
||
9 12.85 +1.33 дБ
|
||
12 ~14.2 ~+1.5
|
||
18 15.42 ~+1.2 на +6дБ
|
||
24 15.42 0.00 ← ПОЛ
|
||
```
|
||
⇒ их шаблонный гейн ВЫХОДИТ НА ПОЛ: res_plugin ≥ ≈0.153 @fc1000q1
|
||
(наш twin продолжает падать до 0.0069 к s24 — в 22 раза глубже пола!).
|
||
Сам закон (softplus α·ln1p+c) верен — сатурация ВХОДИТ через уровень.
|
||
|
||
### Следствие для маршрута
|
||
Bit-exact замыкание ВСЕХ конфигураций = декод генерации res[] (twin_coeff)
|
||
и амплитудного тракта am[] до уровня формул. Модули известны:
|
||
dsp/twin.cpp (наша транскрипция), detkernel::build_twin_coeff,
|
||
детекторный анализ в fn529fe0.cpp. Задача связная, на отдельную сессию.
|
||
|
||
## ============ UPDATE 24ee: РАЗДЕЛЕНИЕ ПЕРЕМЕННЫХ — ФОРМА ЗАКОНА УНИВЕРСАЛЬНА, КОНСТАНТЫ КОНТЕНТ-ЗАВИСИМЫ ============
|
||
|
||
### Контролируемый эксперимент (patchparam + генератор входов):
|
||
```
|
||
семейство α β c rms
|
||
fc500 ДВА тона (dual) 3.2193 0.4927 +0.54 0.016
|
||
fc1000 ОДИН тон (k-серия) 1.6151 0.3645 +0.48 0.010
|
||
fc500 ОДИН тон (f-серия) 1.1530 0.4038 +0.33 0.0013!!
|
||
```
|
||
1. Мягкоплюс-форма ПОДТВЕРЖДЕНА трижды независимо (rms ≤0.016).
|
||
2. α растёт с ЧИСЛОМ ТОНОВ контента (1.15→3.22 при 1→2 тонах): детектор-
|
||
уровень пика получает вклад от соседних областей (частотное смешение
|
||
в шагах 9–19!), и двухтоновый контент удваивает эффективную крутизну.
|
||
3. fc-зависимость слабее контент-зависимости (1.15@500 vs 1.62@1000
|
||
для одного тона — осторожно, β тоже меняется).
|
||
4. Аудио = deepest-scratch + константа (+0.13..+0.18 дБ) ВО ВСЕХ трёх
|
||
семействах — двухстадийность dual была артефактом двухтонового
|
||
контента (второй тон создавал отдельное глубокое состояние!).
|
||
|
||
### Следствие для архитектуры транскрипции
|
||
Универсальная форма + пер-контент константы = рабочая схема. Полная
|
||
универсальность требует декода шагов 9–19 (механизм смешения), где
|
||
константы станут вычисляемыми.
|
||
|
||
### Инструменты/данные раунда
|
||
scripts/finish_f.py; k-серия (sc_k*, k*_ref); f-серия (sc_f*, f*_ref);
|
||
k_series.pkl, f_series.pkl. Все рефы отрендеры чисто (без kill).
|
||
|
||
## ============ ДОПОЛНЕНИЕ 24gg-2: рантайм-таблицы стабильны; bigkernels = ИМПОРТЫ; имя ждёт PE-парсера ============
|
||
|
||
Дамп таблиц диспатча во время рендера (dump_dispatch.py): содержимое
|
||
СТАБИЛЬНО и совпадает со статикой; idx=4 валиден. Entry[4] bigkernel'ов
|
||
→ second-level thunks (141880 и со.) → IAT-слоты ВНЕ дампа ⇒ тела
|
||
bigkernel'ов = ИМПОРТИРОВАННЫЕ функции (UCRT/хост-DLL).
|
||
Инструмент iat_name.py читает рантайм-IAT и парсит PE-экспорты
|
||
владельца-модуля — первый прогон дал мусор из-за гонки с завершением
|
||
рендера (host умирает ~7 c); требует чтения ДО завершения или фиксации
|
||
процесса SIGSTOP на время разбора.
|
||
Как только имена получены (ожидаемо expf/powf/log10f класс), семантика
|
||
bigkernel'ов замыкается, и полный каскадный симулятор шагов 9–19+FIR
|
||
становится реализуемым без дальнейшей археологии.
|