From d0b0441f814bb27dbd9647031761df3a61b18090 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:09:03 +0100 Subject: [PATCH] fix: revert echo gate, restore Chrome AEC during screen share The echo gate architecture bypassed the serialized mic management chain and introduced race conditions. Chrome's AEC handles echo properly for both headphone and speaker users without custom intervention. --- packages/web/src/audio/AudioManager.ts | 27 +++---------------- .../src/components/modals/UserSettings.tsx | 2 +- packages/web/src/hooks/useLiveKit.ts | 4 +-- packages/web/src/utils/screenShare.ts | 7 ----- 4 files changed, 6 insertions(+), 34 deletions(-) diff --git a/packages/web/src/audio/AudioManager.ts b/packages/web/src/audio/AudioManager.ts index 5dbad8d0..420ba11d 100644 --- a/packages/web/src/audio/AudioManager.ts +++ b/packages/web/src/audio/AudioManager.ts @@ -23,7 +23,6 @@ export class AudioManager { private voiceEchoCancellation = true; private voiceNoiseSuppression = true; private voiceAutoGainControl = false; - private screenShareActive = false; private streamGeneration = 0; private inputSwitchChain: Promise = Promise.resolve(null); private rnnoiseNode: AudioWorkletNode | null = null; @@ -203,10 +202,8 @@ export class AudioManager { this.currentStream.getTracks().forEach(t => t.stop()); } - // When screen sharing with audio, Chrome's AEC uses the getDisplayMedia - // audio as a reference signal and ducks the mic — even with headphones. - // Force AEC off during screen share to prevent this. - const effectiveEchoCancellation = this.screenShareActive ? false : this.voiceEchoCancellation; + // Chrome AEC stays on during screen share — headphone users unaffected, + // speaker users get proper echo cancellation. // When RNNoise is active, force browser NS off — running both degrades quality. // The user's noiseSuppression preference is preserved in the store for when RNNoise is disabled. @@ -215,12 +212,12 @@ export class AudioManager { const constraints = { audio: { deviceId: deviceId === 'default' ? undefined : { exact: deviceId }, - echoCancellation: effectiveEchoCancellation, + echoCancellation: this.voiceEchoCancellation, noiseSuppression: effectiveNoiseSuppression, autoGainControl: this.voiceAutoGainControl, // Chromium-specific constraints — belt-and-suspenders to ensure // Chrome's internal audio engine respects the standard constraints. - googEchoCancellation: effectiveEchoCancellation, + googEchoCancellation: this.voiceEchoCancellation, googAutoGainControl: this.voiceAutoGainControl, googNoiseSuppression: effectiveNoiseSuppression, googHighpassFilter: false, @@ -341,22 +338,6 @@ export class AudioManager { } } - /** - * When screen sharing with audio is active, Chrome's AEC uses the screen - * share audio as a reference signal and aggressively ducks the microphone. - * Setting this flag forces echoCancellation OFF regardless of user preference, - * severing the software link that causes the ducking. - */ - setScreenShareActive(active: boolean) { - if (this.screenShareActive === active) return; - this.screenShareActive = active; - console.log(`[AudioManager] Screen share active: ${active} — ${active ? 'forcing AEC off' : 'restoring user AEC preference'}`); - if (this.currentStream) { - this.currentStream.getTracks().forEach(t => t.stop()); - this.currentStream = null; - } - } - getStreamGeneration(): number { return this.streamGeneration; } diff --git a/packages/web/src/components/modals/UserSettings.tsx b/packages/web/src/components/modals/UserSettings.tsx index 05c2fcc3..1f3a6f51 100644 --- a/packages/web/src/components/modals/UserSettings.tsx +++ b/packages/web/src/components/modals/UserSettings.tsx @@ -145,7 +145,7 @@ export function UserSettingsModal() {
Echo Cancellation
-
Removes echo when using speakers (auto-disabled during screen share)
+
Removes echo when using speakers