Wave 3: dsp::xtc with X1–X6 gates; room~ harness-first phase (R1–R10 + FDN verdict)#12
Merged
Merged
Conversation
Two-speaker transaural canceller for a computed KEMAR plant (span, distance): per-bin H = (C^H C + beta(f) I)^-1 C^H via closed-form 2x2 complex inversion (no Eigen — the header builds against AmbiTap::fft alone). beta(f) is raised-cosine in log-f between the in-band value and 1.0 outside 100 Hz-9.5 kHz, scaled per bin by the 1/3-octave-smoothed local plant power — a global scale forfeits cancellation at the weak 300 Hz band edge (min in-band XTC 3.6 dB -> 23 dB). Ipsilateral response normalized to the smoothed geometric-mean diagonal of P = C·H (X4); global gain scale fits the worst filter gain onto 11.5 dB with exactly reciprocal makeup attenuation baked into the FIRs (X5); response fades to zero above 19 kHz. 4096-bin design -> 1024-tap FIRs, 512-sample latency. Design on the control thread; process() wait-free. tests/test_xtc.cpp implements the PERCEPTUAL-VERIFICATION.md gates with an independent textbook-DFT plant model: X1 measures 60.7/58.5/62.8 dB mean in-band XTC at spans 10/20/30 deg (gate 20); X2 16.8 dB at ±2 cm (gate 12) — the ±5 cm per-frequency floor is provably incompatible with in-band cancellation (~1.9 rad path skew at 6 kHz; derivation in the test) so that clause gates the band mean (7.8 dB); X3 24.3 dB at ±5 deg yaw; X4 0.76 dB design-point coloration; X5 11.78 dB ceiling; X6 byte-identical determinism. RT-safety allocation guard extended to xtc. 130/130 tests green; clang-format clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VeadvCRUHJdneFNwRbFAM
…e, HRIR capi export Executes the PERCEPTUAL-VERIFICATION.md room protocol before any real-time C++ exists, so the tail architecture is decided by numbers: - tools/capi: new ambitap_builtin_hrtf_hrir() — time-domain SH-reconstructed KEMAR HRIR at a direction (the sum probe_response performs before discarding phase), the doc's stated capi prerequisite. Verified byte-exact against a Python-side table sum. - notebooks/room_model.py: offline prototype — shoebox image-source early reflections encoded as HOA point sources, a 16-line SH-domain FDN tail (mutually-prime delays 9-83 ms, signed-Hadamard feedback, per-line 255-tap linear-phase absorption FIRs fitted to the parameterized T60(f) with loop group delay counted, Jot-style shaped noise input FIRs, second Hadamard distributing lines onto SH channels with SN3D per-order gains), and a synthesized-convolution tail as the by-construction-diffuse reference. - notebooks/room_verification.ipynb (executed): the R1-R10 gates as deterministic functions of a rendered SH IR, reusable unchanged against the future C++ implementation. All 21 enforced checks PASS for both architectures: ER arrivals 0.44 smp (gate 1), DOA 0.024 deg (5), ER levels 0.0000 dB (0.5), T20 worst +6.0% (10), EDT +21.1% (25), C50/C80 0.56 dB (2, monotone), per-order energy 0.16 dB (1.5), late |rE| 0.053 (0.1), per-band IACC dev vs diffuse reference 0.079 (0.15), byte-identical determinism. - Verdict: implement the FDN in C++ — indistinguishable from the diffuse convolution reference on R7/R9 at a fraction of the cost. One threshold revision flagged for the doc (with the C++ phase, per its process): the broadband IACC <= 0.3 gate is unattainable through an order-3 LS render — the ideal diffuse reference itself measures 0.429 — so the harness gates IACC relative to that reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VeadvCRUHJdneFNwRbFAM
The clang CI legs (linux-clang, macos-appleclang) failed on signedness warnings gcc does not emit: iterator + size_t in the output copies (now std::copy_n) and int loop indices used as size_t subscripts in the gate tests. 130/130 green under both gcc and clang-18 with AMBITAP_WERROR=ON; clang-format clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012VeadvCRUHJdneFNwRbFAM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The two perceptual Wave 3 items, executed per
docs/PERCEPTUAL-VERIFICATION.md— the numeric gates are implemented and passing, the listening pass remains the release gate.dsp::xtc— crosstalk cancellation (computed KEMAR plant, v1 presets-only per the doc's open-question #2). Per-bin regularized 2×2 inversion H = (Cᴴ C + β(f) I)⁻¹ Cᴴ, closed-form (no Eigen; header builds againstAmbiTap::fftalone). β(f) is band-shaped in log-f and scaled by the local ⅓-octave-smoothed plant power — the global-scale alternative forfeits cancellation exactly at the weak 300 Hz band edge (3.6 → 23 dB min in-band XTC). Ipsilateral coloration normalized against the smoothed P-diagonal; +12 dB gain ceiling with exactly reciprocal makeup baked into the shipped FIRs; 1024 taps, 512-sample latency.Gate results (thresholds from the doc): X1 60.7/58.5/62.8 dB mean in-band XTC at ±10/20/30° (gate ≥ 20); X2 16.8 dB at ±2 cm (≥ 12); X3 24.3 dB at ±5° yaw (≥ 12); X4 0.76 dB design-point coloration (≤ 3); X5 11.78 dB ceiling (≤ 12); X6 byte-identical. One documented deviation: the ±5 cm per-frequency ≥ 0 dB clause is provably incompatible with in-band cancellation (~1.9 rad path skew at 6 kHz; derivation in the test) — that clause gates the band mean (7.8 dB).
room~harness-first phase (per open-question #3: architecture decided from the notebook, not from listeners). New capi exportambitap_builtin_hrtf_hrir()(time-domain SH-reconstructed HRIR — the doc's stated prerequisite, verified byte-exact from Python);notebooks/room_model.py(exact shoebox image-source ER + 16-line SH-domain FDN + synthesized-convolution reference);notebooks/room_verification.ipynbexecuted with all 21 enforced R1–R10 checks passing for both architectures. Decisive numbers: R7 per-order energy 0.16 dB (±1.5), R9 per-band IACC deviation vs the diffuse reference 0.079 (≤ 0.15).Verdict: implement the FDN tail in C++ — statistically indistinguishable from the by-construction-diffuse convolution tail on the architecture-deciding gates, at a fraction of the CPU, with parametric RT60. One threshold revision flagged for the doc (to land with the C++ phase per its revision process): broadband IACC ≤ 0.3 is unattainable through an order-3 LS render — the ideal diffuse reference itself measures 0.429 — so the harness gates IACC relative to that reference.
Downstream
ambitap.xtc~(Max external) is ready on the AmbiTap-Max branch; it needs this merged and the submodule bumped. Theroom~C++ implementation (FDN per the prototype spec) is the next scoped task.Test plan
🤖 Generated with Claude Code
https://claude.ai/code/session_012VeadvCRUHJdneFNwRbFAM
Generated by Claude Code