Skip to content

mutap.aec~: selectable post-filter engine — @postfilter 0/1/2 with @model - #17

Merged
tap merged 4 commits into
mainfrom
claude/ml-echo-cancellation-jjwdva
Aug 10, 2026
Merged

mutap.aec~: selectable post-filter engine — @postfilter 0/1/2 with @model#17
tap merged 4 commits into
mainfrom
claude/ml-echo-cancellation-jjwdva

Conversation

@tap

@tap tap commented Aug 8, 2026

Copy link
Copy Markdown
Owner

What this changes

@postfilter grows from a boolean into the post-filter engine selector — 0 off, 1 the classical ITU-certified chain (exactly the old "on"), 2 the learned chain (tap::mu::aec_chain_nn) — with a new @model attribute for user-trained MUNN weights and MuTap's 48 kHz v2 model embedded as the built-in default. Companion PR: tap/MuTap (the library side; this PR's submodule pin points at its branch head).

Why

Users should choose the residual-suppression engine by material and need: the classical coherence suppressor is certified and material-agnostic (still the default); the learned engine measures ~+19 dB single-talk ERLE on speech at better near-end transparency. The enum + @model shape leaves room for future engines (3, 4, …) and future models without new binaries.

Verification

  • Both Catch suites pass (20 assertions / 4 scenarios for mutap.aec~, incl. the 0..2 clamp, new defaults, and the built-in model's @block coercion to 256); ctest green. Built and tested on Linux against the branch library; macOS/Windows externals are CI's gate, not built here.
  • Old-patch compatibility: postfilter $1 toggles still mean exactly 0/1.
  • Engine behavior numbers are the library's, measured by its executed notebooks (see the MuTap PR); this repo ships the surface, not new DSP.
  • A bad @model file posts an error and leaves the running engine untouched (rebuild failures are no longer silent).

Notes for the reviewer

  • Contract change. @postfilter type widens bool→int (values 0/1 keep their meaning; getters now return 0/1/2). With @postfilter 2, @block follows the loaded model's trained block size (coerced with a console notice) and a sample-rate mismatch warns.
  • Submodule pin moved. submodules/MuTap → the tap/MuTap branch head this depends on. After that PR merges by rebase/squash, repoint at the identical tree on main before this merges.
  • Max/Pd package. maxref and help patcher updated for the new attribute surface; no new object, so no new reference page. Universal-binary claim unchanged (CI).
  • mutap_nn_weights_default.h is generated (tools/ml/export_weights.py --header in MuTap) — 776 KB, do not hand-edit.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y3Y23Xavpd31igC1mDDcWg


Generated by Claude Code

claude added 4 commits August 8, 2026 02:27
@PostFilter grows from a boolean into the post-filter ENGINE selector:
0 = off, 1 = the classical ITU-certified chain (exactly the old 'on'),
2 = the learned chain — the same raw-Kalman canceller and receive guard
with the coherence suppressor swapped for MuTap's trained band-gain
network (tap::mu::aec_chain_nn via aec_chain_nn_preset). Old patches
with 0/1 keep their exact meaning; future engines get 3, 4, ...

@model selects the learned engine's weights: a MUNN file path, or empty
for the built-in model — suppressor_v2_48k embedded as a generated
byte-array header (52,570 parameters, trained at this external's native
geometry: 48 kHz, block 256; tools/ml in the MuTap repo, every training
input clean-licensed). A model carries its own geometry: publish()
coerces @block to the trained hop with a console notice, warns on a
sample-rate mismatch, and a bad weights file posts an error and leaves
the running engine untouched. Engine rebuild failures are no longer
silent.

Help patcher and maxref document the engine choice and the measured
trade (learned: stronger single-talk ERLE on speech at equal
transparency; classical: certified, stronger off-domain double-talk —
and still the default). Tests cover the new defaults, the 0..2 clamp,
and the built-in model's block coercion; both suites pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3Y23Xavpd31igC1mDDcWg
The external now includes mutap/nn_chain.h and nn_suppressor.h. After
tap/MuTap's branch merges (rebase/squash), repoint this pin at the
identical tree on main per the template's submodule rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3Y23Xavpd31igC1mDDcWg
MSVC finds min::symbol -> std::string ambiguous (three basic_string
overloads match); go through c_str(). The generated weights header now
carries clang-format off/on guards so the format gate skips the byte
array (regenerated from the same v2 model — content unchanged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3Y23Xavpd31igC1mDDcWg
tap/MuTap#33 merged (rebase); the pin moves from the branch head to the
identical tree on main (1d258d3), per the submodule rule in the PR
notes. Package builds and tests green against the pinned submodule
itself (no MuTap_ROOT override).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y3Y23Xavpd31igC1mDDcWg
@tap
tap merged commit eef3143 into main Aug 10, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants