From af11d8021617bb9ec252f945152c3bde291bbb0b Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Sun, 31 May 2026 19:30:42 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20voice=20mic=20race=20condition=20?= =?UTF-8?q?=E2=80=94=20onend=20blocks=20restart=20during=20TTS,=20clean=20?= =?UTF-8?q?abort+restart=20after=20speech,=20stuck=20isSpeaking=20watchdog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public_html/index.html | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/public_html/index.html b/public_html/index.html index 4967592..61b717b 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -1012,6 +1012,13 @@ function showApp(name, greeting, silent = false) { exitVoiceMode(); } }, 60000); + // Watchdog: reset isSpeaking if stuck (TTS error left it true) + setInterval(() => { + if (isSpeaking && !_ttsAudio && !window.speechSynthesis?.speaking) { + isSpeaking = false; + if (isListening) try { recognition?.start(); } catch(_) {} + } + }, 5000); startListening(); loadNetwork(); loadHA(); @@ -1785,8 +1792,10 @@ function initVoice() { }; recognition.onend = () => { - // Always restart for continuous wake word / command listening - if (isListening) setTimeout(() => { try { recognition.start(); } catch(_) {} }, 100); + // Only restart when not speaking — _resumeMic() handles restart after TTS + if (isListening && !isSpeaking) { + setTimeout(() => { try { recognition.start(); } catch(_) {} }, 150); + } }; recognition.onerror = (e) => { @@ -1908,8 +1917,13 @@ async function speak(text) { const _resumeMic = () => { isSpeaking = false; reactor?.classList.remove('speaking'); - // Let onend restart recognition automatically (300ms buffer after audio ends) - if (isListening) setTimeout(() => { try { recognition?.start(); } catch(_) {} }, 300); + if (isListening) { + // Abort any stale session then restart cleanly + setTimeout(() => { + try { recognition?.abort(); } catch(_) {} + setTimeout(() => { try { recognition?.start(); } catch(_) {} }, 150); + }, 200); + } }; try { const res = await fetch('/api/tts', { @@ -1942,7 +1956,12 @@ function _speakFallback(text) { utter.onend = () => { reactor?.classList.remove('speaking'); isSpeaking = false; - if (isListening) setTimeout(() => { try { recognition?.start(); } catch(_) {} }, 300); + if (isListening) { + setTimeout(() => { + try { recognition?.abort(); } catch(_) {} + setTimeout(() => { try { recognition?.start(); } catch(_) {} }, 150); + }, 200); + } }; synth.speak(utter); }