diff --git a/src/livekit/SileroVADGate.ts b/src/livekit/SileroVADGate.ts index e7bd9751..86814bcf 100644 --- a/src/livekit/SileroVADGate.ts +++ b/src/livekit/SileroVADGate.ts @@ -82,9 +82,10 @@ export class SileroVADGate { onSpeechRealStart: (): void => {}, }); - // Gate starts closed — audio is muted until the first speech frame arrives. - this.gateOpen = false; - this.onClose(); + // Gate starts OPEN so audio flows immediately. The first silence frame + // will close it. This also means a failed model load degrades gracefully + // (audio still flows) rather than permanently muting the user. + this.gateOpen = true; await this.vad.start(); log.info("MicVAD started"); diff --git a/src/state/CallViewModel/localMember/Publisher.ts b/src/state/CallViewModel/localMember/Publisher.ts index faed893b..77e8e419 100644 --- a/src/state/CallViewModel/localMember/Publisher.ts +++ b/src/state/CallViewModel/localMember/Publisher.ts @@ -454,7 +454,7 @@ export class Publisher { void vadGate.destroy(); vadGate = null; } - // Reset gate to open so audio flows if VAD is toggled off mid-call + // Always reopen gate when VAD stops so audio flows without VAD transformer?.setVADOpen(true); }; @@ -468,8 +468,6 @@ export class Publisher { } const stream = new MediaStream([rawTrack]); vadGate = new SileroVADGate(stream, ctx); - // Close the gate immediately — VAD will open it once speech is confirmed. - transformer?.setVADOpen(false); vadGate.onOpen = (): void => transformer?.setVADOpen(true); vadGate.onClose = (): void => transformer?.setVADOpen(false); vadGate.start().catch((e: unknown) => {