Skip to content

Restore echo cancellation on the microphone without silencing capture #59

Description

@westerosweb

Summary

Acoustic echo cancellation is currently off. Without it the microphone re-hears remote participants coming out of the speakers, so remote speech can be transcribed twice — once from the system tap, once from the mic — and diarization may split one person across two clusters.

Deferred from #58, where the obvious fix (setVoiceProcessingEnabled(true)) silenced the microphone entirely and had to be reverted. This is a quality issue, not data loss, and anyone using headphones is unaffected — hence not a release blocker.

Background: why the obvious fix failed

AVAudioEngine's voice-processing I/O gives echo cancellation, noise suppression and automatic gain in one call, with no third-party dependency. Enabling it on the input node produced complete silence on the microphone while the level meter kept moving, because the meter shows max(system, mic) and system audio was still live.

Every timeline window logged:

Timeline: microphone contributed 10s at RMS 0.00000

Removing the call restored capture immediately (RMS 0.01200 on the next run). The suspected cause is that voice-processing I/O is a duplex unit and wants a running output chain, which this engine does not have — but that was never confirmed.

The revert and its reasoning are at Logue/Engine/AudioRecorder.swift:78.

What good looks like

Any approach is fine as long as capture is proven to survive it. Options worth exploring:

  • Give the engine a real output chain (e.g. a connected mixer at zero volume) so the duplex unit has both halves, then re-enable voice processing.
  • Configure ducking separately via voiceProcessingOtherAudioDuckingConfiguration so re-enabling does not also quieten the system audio being recorded — this was already needed once and is easy to forget.
  • Apply echo cancellation only to the transcriber's copy of the audio, leaving the saved recording and the diarization timeline on the raw signal.
  • Do nothing in the audio path and suppress the echo downstream instead — the system tap knows exactly what was played, so mic content matching it within a short window could be discarded after transcription.

Verification

This must not be re-landed on inspection alone. TimelineContributionLog exists precisely to catch it — see Logue/Engine/TimelineContributionLog.swift:19. A recording where the microphone reports a non-zero RMS is the minimum bar, and it should be checked with system audio simultaneously armed, since that is the configuration that failed.

Acceptance criteria

  • Recording with the microphone live logs a non-zero Timeline: microphone contributed … RMS in every window, with system audio armed at the same time
  • A meeting played through laptop speakers does not produce duplicated remote speech in the transcript
  • Diarization does not gain an extra speaker cluster from echoed audio
  • System audio recorded while the microphone is live is not quieter than when it is muted
  • The saved recording remains audible and aligned with its transcript
  • A regression test or documented manual check exists so a future change cannot silence capture unnoticed

Notes

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions