feat(calls): verify echo cancellation on every call — call_audio_aec capability#172
Merged
Conversation
…capability (tincan-97mlk.2)
Echo-free calls are the make-or-break requirement of the tincan+iris
stack: without AEC the far party hears themselves and won't stay on the
call. Until now nothing verified the AEC was actually in the audio path —
a regression was only discovered when a human far party complained.
- call_audio.verify_aec_in_path(): inspects the live PipeWire link graph
(pw-link -l) and checks both echo-cancellation invariants, regardless
of who created the links (tincand, WirePlumber, or iris aec_audio.sh):
1. REFERENCE — SCO downlink feeds an echo-cancel sink (far-party
voice must be in the AEC reference)
2. CLEAN MIC — SCO uplink is fed by the echo-cancelled source and
NOT by a raw physical microphone (alsa_input*)
Also flags downlink→speaker bypass. Agent TTS streams into the uplink
are intended and not flagged. Recognizes both module-echo-cancel and
iris_aec_* naming.
- CallController: after SCO routing resolves (or retries exhaust — the
graph may have been routed by WirePlumber/iris), verify and expose the
result as the new call_audio_aec capability + INFO/WARNING log.
Teardown clears the capability and cancels a pending setup timer.
- call_audio: single _run_pw seam (testable); pw-link 'File exists'
treated as already-linked success so pre-linked calls don't read as
routing failures.
- tests: first coverage for the SCO routing path (the invalid-flags bug
shipped because none existed), plus the verifier and controller hooks.
Co-Authored-By: Claude Fable 5 <[email protected]>
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.
Part of the reliability baseline (#167), bead tincan-97mlk.2 — AEC is the hard requirement: without echo suppression the far party hears themselves and the whole stack is moot. Until now nothing verified AEC was actually in the audio path; regressions were discovered by human far parties.
What this adds
call_audio.verify_aec_in_path()— inspects the live PipeWire graph (pw-link -l) and checks both echo-cancellation invariants (same pair iris'saec_audio.shdocuments): the SCO downlink must feed an echo-cancel sink (far-party voice in the AEC reference) and the SCO uplink must be fed by the echo-cancelled source, not a raw mic. Also catches downlink→speaker bypass. Works regardless of who created the links (tincand fix(calls): SCO routing used invalid pw-link flags — never linked call audio (tincan-jukpc) #164, WirePlumber, or iris's bridge). TTS streams into the uplink are intended and not flagged.call_audio_aeccapability — set after routing resolves on every call, cleared on teardown; visible inGetStatus()for clients, evals, and the upcoming doctor check. Failures log a WARNING naming the offending nodes.pw-link"File exists" now counts as already-linked success (pre-linked calls no longer read as routing failures).Testing: full suite green. Live validation: place a call with iris's AEC up —
GetStatusshould showcall_audio_aec: true; tear the AEC down mid-setup and it should go false with a WARNING naming the raw-mic path.🤖 Generated with Claude Code