VPIO cancels echo by subtracting whatever the app renders through the unit's
output element. screenpipe doesn't play the far-end (other apps do), so its VPIO
was configured input-only — output element disabled, nothing rendered — and
Apple's AEC had no downlink to subtract, removing 0 dB (mediar/screenpipe#3938).
Add an optional MacosVoiceProcessingInputConfig.aec_reference (a shared mono-f32
ring). When set: enable the output element, force it to mono f32, set
kAUVoiceIOProperty_MuteOutput=1 (reference is for AEC only, never replayed), and
install a render callback that drains the ring into the output element each
cycle. The caller (engine) feeds it the captured system-audio lane. None keeps
the historical input-only behavior. Render callback is realtime-safe: try_lock
+ zero-fill on contention/underflow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gives VoiceProcessingIO the downlink reference its echo canceller needs. Today screenpipe's VPIO is input-only (output element disabled), so Apple's AEC has nothing to subtract and removes 0 dB (screenpipe/screenpipe#3938).
New optional
MacosVoiceProcessingInputConfig.aec_reference(a shared mono-f32 ring). When set: enable + mute the output element, force it to mono f32, and install a realtime render callback that drains the ring into it as the AEC far-end reference. The engine feeds it the captured system-audio lane.None= unchanged input-only behavior.Compiles clean on macOS. Not yet runtime-validated — needs a real Mac (speakers, no headphones, join a call, confirm the remote stops appearing under "me"). Engine-side wiring (feed system-audio into the ring + pass it to this config) is a paired screenpipe PR.