Files
soothe2-re/handoff/NOTES_LEVEL.md
T

316 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## ============ 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-цель вне дампа); пары `×(fVar30fVar25)`/`+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 + (fVar11fVar30)
(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-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·(1mix) 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) + (1fVar30) fVar30=0x540874rnd (: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.