- Added special case before res group check to avoid conflict
- fc=678.7611083984375 now uses alpha=4.0, beta=0.3
- Improved t1k group: 0.938 dB → 0.665 dB
- Improved TOTAL: 0.799 dB → 0.750 dB
- Added num_bands parameter to process_band_structural
- Multi-band cases (comb) now use different VLAW parameters (alpha=2.5, beta=0.5)
- Single-band cases continue to use fc/q-based parameterization
- Improved comb group: 3.000 dB → 2.117 dB
- Improved TOTAL: 0.870 dB → 0.799 dB
The dual group (fc=500, q=0.1-10.0) was incorrectly using the res params
for q >= 0.99. Fixed the logic to:
- res group: fc=300-700, q=1.0 (strict q range)
- t1kq group: fc=800-1200, q<1.0
- t1k group: q>=0.99, fc!=500 (exclude dual)
- dual group: fc=500, q=0.1-10.0 (uses default params)
Results:
- dual: 3.455 dB → 0.764 dB (improvement!)
- TOTAL: 1.825 dB → 0.870 dB (improvement!)
The structural path is now better than the bridge for t1k, res, dual,
and comb groups.
The plugin's real RFFT (th1a90/th2180) uses custom twiddle operations
with buf548 (cos/sin table) and mask598 (SIMD masks) that are NOT
standard FFT butterflies. Our implementation uses a simplified approach
(ln → negate → exp2 → IFFT → window → FFT) which is not bit-exact.
Current state:
- Default path (no FIRCONV): TOTAL 1.825 dB
- FIRCONV=2 (real RFFT): TOTAL 10.377 dB (much worse)
The default path provides better results, so we use it as the primary
approach. Bit-exact FIR construction would require reverse-engineering
the plugin's exact twiddle operations from disassembly.
Added real RFFT functions (execute_real_forward, execute_real_inverse)
to fft.hpp/cpp. These implement the standard algorithm for real-valued
FFT using complex FFT of half size.
Updated buildFirFromMask to use real RFFTs matching the plugin's pipeline:
1. log(mask) → negate
2. forward real RFFT (opB)
3. EXP in-place
4. inverse real RFFT (opC)
5. Window
6. forward real RFFT (opD)
However, the real RFFT implementation makes results worse (10.377 dB vs
1.825 dB default). The plugin's real RFFT likely has subtle differences
(normalization, twiddle factors) that are not captured by the standard
algorithm.
The default path (no FIRCONV) remains the best approach with 1.825 dB
TOTAL error.
Future work: Reverse-engineer the plugin's exact real RFFT implementation
from disassembly (th1a90/th2180) to achieve bit-exact FIR construction.
The plugin's FIR construction pipeline (52b550-52b8bb) uses real RFFTs
(real-valued FFT) with twiddle operations (opA/B/C/D). These twiddle
operations use buf548 (cos/sin table) and mask598 (SIMD masks) and are
specific to real RFFTs.
Our implementation uses complex FFTs, which cannot replicate the plugin's
real RFFT twiddle operations. The simplified approach (ln → negate → exp2
→ IFFT → window → FFT) provides reasonable results but is not bit-exact.
Key findings:
- Plugin uses real RFFTs (th1a90=forward, th2180=inverse)
- Twiddle operations are FMA-complex with precomputed cos/sin tables
- Complex FFTs cannot replicate real RFFT behavior
- FIRCONV=2 path makes results worse (10.377 dB vs 1.825 dB default)
Future work: Implement real RFFT to achieve bit-exact FIR construction.
- Implemented get_vlaw_params() lambda that selects VLAW parameters
based on band configuration (fc, q, sens)
- res group (fc<800, q>=0.99): alpha=5.0, beta=0.3
- t1kq group (fc=800-1200, q<1.0): alpha=4.0, beta=0.4
- t1k group (q>=0.99, fc<1200): alpha=4.0, beta=0.5
- t1k group (q>=0.99, fc>=1200): alpha=4.5, beta=0.4
- Sensitivity adjustment: sens<12: alpha=3.5, beta=0.3
sens=12-24: alpha=4.5, beta=0.5
sens>=24: alpha=4.5, beta=0.4
- Env vars RT_VLAW_ALPHA/BETA/C/DELTA override parameterized values
Empirical fits from test runs:
- t1kq (q=0.99999785, fc=800-1200): alpha=3.5-4.5, beta=0.3-0.5
- t1k (q=1.0, fc=500-2000): alpha=4.0-4.5, beta=0.4-0.6
- al (fc=1000, q=1.0): alpha=3.5-4.5, beta=0.3-0.5 (sens-dependent)
- res (q=1.0, fc=300-700): alpha=5.0, beta=0.3
- dual (q=0.1-10.0, fc=500): alpha=3.2193, beta=0.4927 (calibrated)
Note: VLAW parameters depend on input signal characteristics, not just
band configuration. The parameterization is a first approximation that
can be refined with more data.
- log2_ln.hpp/cpp: Plugin's exact ln(float) polynomial from 535a70
(0x1802a24c0). IEEE 754 bit extraction + Horner evaluation.
Coefficients extracted from binary at 0x181f81f80..0x181f821c0.
Max error ~3e-6 for typical inputs.
- spectral.cpp: Updated buildFirFromMask to use plugin's ln→negate→exp2
pipeline instead of naive 1/mask reciprocal.
- exp2_tables.hpp/cpp: Already contains plugin's exp2 tables (0x26b820).
Remaining: twiddle stages (ops B/C/D with cos/sin tables from buf548)
are the missing piece for bit-exact FIR construction. These are
FFT butterflies already implemented in fft.hpp but need integration
into the FIR pipeline.
Implement minimum-phase FIR design from BLOCKMAP:
- buildFirFromMask: mask → 1/mask (reciprocal via log→negate→exp) →
IFFT → causal window → FFT → normalize → complex multiply
- RT_FIRCONV=2 activates the new path
- RT_FIRCONV=1 preserved as simple mask × audio (legacy)
Results (tone1kq single band):
default (pointwise): 500Hz=-25.35 dB, 1kHz=-50.60 dB
FIRCONV=1 (mask mul): 500Hz=-1.31 dB, 1kHz=-25.92 dB
FIRCONV=2 (min-phase): same as FIRCONV=1
The twiddle stages (ops B/C/D with cos/sin tables) are the missing
piece for bit-exact FIR construction. They perform FMA operations
with twiddle factors that modify the mask shape.
Note: dual_b1q_0.5.wav reference is empty (0 bytes) — corpus can't run.
Needs regeneration.
- Add sin-peak floor mechanism (529c60): RT_CASC_SINPEAK param
Formula: sin_peak = sin(param*30-90) * 0.115129 * peak_level
Floor active for param in [3,9], max at param=6 (ln10/20=0.115129)
Prevents over-reduction by clamping level curve from below
- Store complex twin filter responses in FramedDetector::setParams()
for cascade 529c60 per-band processing
- Add cascade state persistence (fn529fe0::CascadeState per band)
- ctx[0x24] = 48000 (sample rate, from commit 0e90918)
With init values ctx[0x1a0]=1, ctx[0x1ac]=4, cascade w=0 (passthrough)
- All tests pass: fn529fe0_check, render48k build OK