Skip to content

Commit 22cd603

Browse files
committed
fix vad stuck until mute/unmute
1 parent 75bac31 commit 22cd603

File tree

1 file changed

+18
-15
lines changed

1 file changed

+18
-15
lines changed

src/index.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -731,27 +731,35 @@ class LayercodeClient implements ILayercodeClient {
731731
*/
732732
private _handleDataAvailable(data: { mono: Int16Array<ArrayBufferLike> }): void {
733733
try {
734-
const base64 = arrayBufferToBase64(data.mono);
735-
736-
// Don't send audio if muted
734+
// Don't send or buffer audio if muted. Also clear any stale buffer so we
735+
// don't accidentally flush old audio after unmute.
737736
if (this.isMuted) {
737+
this.audioBuffer = [];
738738
return;
739739
}
740740

741741
// Determine if we should gate audio based on VAD configuration
742-
const shouldGateAudio = this.vadConfig?.gate_audio !== false; // Default to true if not specified
743-
const bufferFrames = this.vadConfig?.buffer_frames ?? 10; // Default to 10 if not specified
742+
const shouldGateAudio = this.vadConfig?.gate_audio !== false; // default true
743+
const bufferFrames = this.vadConfig?.buffer_frames ?? 10; // default 10
744+
745+
// If VAD is disabled or failed to init, gating would deadlock (userIsSpeaking never flips true).
746+
// Only gate if we actually have a running VAD instance.
747+
const vadEnabledByConfig = this.vadConfig?.enabled !== false; // default true
748+
const vadAvailable = vadEnabledByConfig && !!this.vad && !this.pushToTalkEnabled;
744749

745750
let sendAudio: boolean;
746751
if (this.pushToTalkEnabled) {
747752
sendAudio = this.pushToTalkActive;
748753
} else if (shouldGateAudio) {
749-
sendAudio = this.userIsSpeaking;
754+
// Key fix: if VAD isn't available, don't gate — send audio.
755+
sendAudio = vadAvailable ? this.userIsSpeaking : true;
750756
} else {
751757
// If gate_audio is false, always send audio
752758
sendAudio = true;
753759
}
754760

761+
const base64 = arrayBufferToBase64(data.mono);
762+
755763
if (sendAudio) {
756764
// If we have buffered audio and we're gating, send it first
757765
if (shouldGateAudio && this.audioBuffer.length > 0) {
@@ -937,7 +945,7 @@ class LayercodeClient implements ILayercodeClient {
937945
this._sendReadyIfNeeded();
938946
}
939947

940-
const reportedDeviceId = this.activeDeviceId ?? (this.useSystemDefaultDevice ? 'default' : (this.deviceId ?? 'default'));
948+
const reportedDeviceId = this.activeDeviceId ?? (this.useSystemDefaultDevice ? 'default' : this.deviceId ?? 'default');
941949
if (reportedDeviceId !== this.lastReportedDeviceId) {
942950
this.lastReportedDeviceId = reportedDeviceId;
943951
if (this.options.onDeviceSwitched) {
@@ -1314,7 +1322,7 @@ class LayercodeClient implements ILayercodeClient {
13141322
const newStream = this.wavRecorder.getStream();
13151323
await this._reinitializeVAD(newStream);
13161324
}
1317-
const reportedDeviceId = this.lastReportedDeviceId ?? this.activeDeviceId ?? (this.useSystemDefaultDevice ? 'default' : (normalizedDeviceId ?? 'default'));
1325+
const reportedDeviceId = this.lastReportedDeviceId ?? this.activeDeviceId ?? (this.useSystemDefaultDevice ? 'default' : normalizedDeviceId ?? 'default');
13181326
console.debug(`Successfully switched to input device: ${reportedDeviceId}`);
13191327
} catch (error) {
13201328
console.error(`Failed to switch to input device ${deviceId}:`, error);
@@ -1373,7 +1381,7 @@ class LayercodeClient implements ILayercodeClient {
13731381
this._sendReadyIfNeeded();
13741382
}
13751383

1376-
const reportedDeviceId = this.activeDeviceId ?? (this.useSystemDefaultDevice ? 'default' : (this.deviceId ?? 'default'));
1384+
const reportedDeviceId = this.activeDeviceId ?? (this.useSystemDefaultDevice ? 'default' : this.deviceId ?? 'default');
13771385
if (reportedDeviceId !== previousReportedDeviceId) {
13781386
this.lastReportedDeviceId = reportedDeviceId;
13791387
if (this.options.onDeviceSwitched) {
@@ -1634,12 +1642,7 @@ class LayercodeClient implements ILayercodeClient {
16341642
private async _shouldWarnAudioDevicesRequireUserGesture(error: unknown): Promise<boolean> {
16351643
const e: any = error as any;
16361644
const name = typeof e?.name === 'string' ? e.name : '';
1637-
const msg =
1638-
typeof e?.message === 'string'
1639-
? e.message
1640-
: typeof error === 'string'
1641-
? error
1642-
: '';
1645+
const msg = typeof e?.message === 'string' ? e.message : typeof error === 'string' ? error : '';
16431646

16441647
const isPermissionLike = name === 'NotAllowedError' || name === 'SecurityError' || name === 'PermissionDeniedError';
16451648
if (!isPermissionLike) return false;

0 commit comments

Comments
 (0)