77 lines
4.8 KiB
Markdown
77 lines
4.8 KiB
Markdown
# Twin FUN_180535880 transcription (Phase 2)
|
|
|
|
## PHASE-3 CALLER + GRID (2026-08-18)
|
|
- Caller FUN_180536300 (decomp 6218 / sp_180536300.txt) decoded at buffer level:
|
|
- params: param_1=top DSP, param_2=out, param_3=?, param_4=band obj, param_5=cell count,
|
|
param_7=scratch (>=52*N bytes complex).
|
|
- scratch: +0..8N A-accum, +8N..16N B-accum, +16N..24N z-powers, +24N..32N rotor-in
|
|
(fill 1.0), +32N..36N phase ramp lVar1, +36N..44N rotor-out z1 (r14), +44N..52N
|
|
lVar3(1.0)/lVar4(0.0) for interleave+negate+exp build.
|
|
- 0x18052da00(lVar1, scale=2pi/(iVar7*sr), N) = ramp fill (float, via 0x180001a00);
|
|
- twin always = FUN_180535880 (type-info compare of same addr -> equal);
|
|
sibling FUN_180536f90 is dead.
|
|
- 0x18052dbc0(param_2, lVar4, N) = cplx interleave -> out=(cos,sin)=e^{+i theta}.
|
|
- GRID LAW (verified from trace.txt case8 + constructor):
|
|
- os = *(int*)(param_1+0x240080) = 4 (written 0x18052ce3e `mov ...,0x4`)
|
|
- sr = *(float*)(param_1+0x24) = 44100.0
|
|
- fs_total (generator) = os*sr = 176400 => resonance shape f/fc invariant to fs.
|
|
- per-cell phase step = 2*pi/(os*sr); cell k <-> freq = k/os Hz (0.25 Hz steps).
|
|
- Phase-1 model FS=44100 was a valid normalization (res(f) depends only on f/fc).
|
|
- case8 generator call (trace 33361..33490, switchD_1805318ab):
|
|
- +0x814 numBands = 1, +0x103 mode flag = 1
|
|
- param_5 (float, xmm4) = 10^(sens_dB/20) (pow10 10.0^([band+0x101]/20.0))
|
|
- gain = sqrtf(param_5) = 10^(sens_dB/40); model fit gain=4.132 => sens_dB@0x101 ~= 24.65
|
|
- freq = [band+0x804], Q = [band+0x80c], fs_total = os*sr (w0 = max(fc,2)*2pi/fs_total)
|
|
|
|
## PHASE-2 GATE: PASSED (2026-08-18)
|
|
- `dsp/twin.hpp` + `dsp/twin.cpp` written: build_twin_coeff (FUN_180533ec0 double pipeline
|
|
-> cvtpd2ps), cplx_div_exact (0x181a77520: mulps |A|^2, rcpps + Newton, NaN guard),
|
|
cplx_mul_exact (0x18000ad60 scalar body @0x18000ae00: vfmaddsub213ps form),
|
|
twin_apply (seed A0/B0 + 2 vfmadd213ss Horner stages + 2*div).
|
|
- twin_check gate: fc-scan grid (7 fc, Q=0.9999978, gain=4.132, tone 1000 Hz)
|
|
max |rel err| = 1.27e-5 vs double reference res_at (float rcpps parity).
|
|
DUAL sweep (Q=0.1..10 x tones 500/2000, fc=500) max = 1.23e-5. Both PASS.
|
|
- Constants locked: A1 = B1 = cos(w0)*-2.0, z1 = conj(exp(+i*theta)),
|
|
z2 = cplx_mul(z1,z1), out = 2*B/A.
|
|
|
|
## Confirmed scalar math (from generator + cplx-div kernels)
|
|
- Generator FUN_180533ec0 (per band, case8):
|
|
`fVar1 = sqrtf(param_5)` (param_5 = pow(10, sens/20) via cd6 IAT stub)
|
|
`w0 = max(2.0, freq)*2*pi / fs_total` (fs_total = 2*FS frame?)
|
|
`p = sin(w0)*0.5 / Q`
|
|
`dVar5 = p*fVar1` (A tap), `param_4 = p/fVar1` (B tap)
|
|
A = [1+dVar5, dVar2, 1-dVar5], B = [1+param_4, dVar2, 1-param_4]
|
|
where dVar2 = cos(w0) * DAT_1824c46b0 (=-2.0? VERIFY)
|
|
- Twin FUN_180535880: for each FFT bin z (rotor):
|
|
`res(z) = 2 * B(z) / A(z)`, B(z)=B0+B1*z+B2*z^2, A(z)=A0+A1*z+A2*z^2
|
|
implemented as complex FMA accumulation over 3 taps:
|
|
r15 (A-accum) and r12 (B-accum) via 0x180001fd0 vfmadd213ss (coeff [rsp+k*4+0x28]=A_k, [+0x38]=B_k)
|
|
rbp buffer = z powers via rotor ops (0x1800019a0 fill / 0x1800021e0 / 0x1800018b0 / 0x180001af0 / 0x180001dc0)
|
|
final 0x180001e20 -> 0x181a775a0 cplx-div: out = 2*B/A (xmm7=0x40000000=2.0, xmm6=NaN guard)
|
|
- cplx-div kernel 0x181a77520: |A|^2 via shufps 0x88/0xdd, rcpps + 1 Newton step (xmm7=2.0), NaN guard via cmpeqps.
|
|
Matches rotor_kernel.hpp cplx-div exactly.
|
|
|
|
## Dispatch (all stubs share global 0x1826159a0 = 4 = AVX2)
|
|
0x180001d00 -> 0x180009860 -> 0x180040d40 (cplx fill const, NT stores)
|
|
0x1800019a0 -> 0x180004200 (float fill -> 0x181a63fe0)
|
|
0x1800021e0 -> 0x1800130e0 (large AVX, 0x450 stack, rotor/exp?)
|
|
0x1800018b0 -> 0x180003040 (-> 0x181a557c0)
|
|
0x180001af0 -> 0x180005a20 (loop -> 0x18001a5a0)
|
|
0x180001dc0 -> 0x18000ad60 (cplx op r14*rbp)
|
|
0x180001e20 -> 0x18000b820 (-> 0x181a775a0 cplx-div 2B/A)
|
|
0x180001fd0 -> 0x18000f620 (vfmadd213ss: out = src*coeff + dst)
|
|
|
|
## Coefficient layout in twin (param_3 = {double A[3], double B[3]})
|
|
stack: +0x28=A0 +0x2c=A1 +0x30=A2 | +0x38=B0 +0x3c=B1 +0x40=B2
|
|
loop k=0..2 uses A_k via [rsp+k*4+0x28], B_k via [rsp+k*4+0x38]
|
|
r15 = out A-accum, r12 = out B-accum, rbp = z^1..z^2 buffer, r14 = rotor(z) input
|
|
|
|
## PHASE-1 LOCK (empirical, 36 points, LUT chain B.11)
|
|
Effective sqrtf(param_5) needed ~= 4.13 (NOT 1.995).
|
|
=> param_5 ~= 17.07 = 10^(1.2325) => internal sens_stored ~= 24.65 dB (not XML 12.0)
|
|
OR equivalent. RULED OUT: sqrt(10^(12/20))=1.995 (rmse 1.38/5.68);
|
|
10^(12/20)=3.981 direct (t1kq 0.056 but t1k 0.934); |2B/A|^2 (1.5/6.6).
|
|
Sens setters (0x18053795d, 0x18056f002) both store RAW value; +0x808 scaling is HOST-side
|
|
(plugin controller, outside dump) => must be resolved via bit-exact render or host trace.
|
|
NOTE: single-gain compromise 4.132; t1kq wants 4.055, t1k wants 4.137 (tilt residual).
|