From 81d1973b7d6298b806cbaf2994beed42ce0cce5d Mon Sep 17 00:00:00 2001 From: Jatin K Malik Date: Thu, 5 Feb 2026 18:31:14 -0800 Subject: [PATCH] fix(recognition): prevent stop sound from being transcribed (#150) When stopping voice recognition, the stop sound was being captured by the microphone and transcribed as "thank you" or similar text. This happened because the sound was played BEFORE stopping recording. Fix: - Stop recording FIRST by setting should_record = False - Wait briefly for audio thread to stop - Discard the last ~1 second of audio buffer (15 chunks) which would contain the feedback sound - THEN play the stop sound after recording has stopped - Process remaining valid audio This ensures that audio captured during the stop sound is discarded and won't be transcribed. Technical details: - Audio recorded in 1024-sample chunks at 16000 Hz = ~64ms per chunk - Discarding 15 chunks = ~960ms of audio - Uses existing _buffer_lock for thread safety Closes #150 --- .../speech_recognition/recognition_manager.py | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/vocalinux/speech_recognition/recognition_manager.py b/src/vocalinux/speech_recognition/recognition_manager.py index 104f49a..c62a54c 100644 --- a/src/vocalinux/speech_recognition/recognition_manager.py +++ b/src/vocalinux/speech_recognition/recognition_manager.py @@ -921,12 +921,31 @@ class SpeechRecognitionManager: logger.info("Stopping speech recognition") - # Play the stop sound - play_stop_sound() - - # Clear recording flag + # Stop recording FIRST to prevent capturing the stop sound self.should_record = False + # Wait briefly for audio thread to stop recording + if self.audio_thread and self.audio_thread.is_alive(): + self.audio_thread.join(timeout=0.1) + + # Discard the last ~1 second of audio to avoid transcribing the stop sound + # Audio is recorded in 1024-sample chunks at 16000 Hz = ~64ms per chunk + # We discard the last 15 chunks (~1 second) which should contain the feedback sound + with self._buffer_lock: + if len(self.audio_buffer) > 15: + discarded_chunks = self.audio_buffer[-15:] + self.audio_buffer = self.audio_buffer[:-15] + logger.debug( + f"Discarded {len(discarded_chunks)} audio chunks to avoid transcribing feedback sound" + ) + elif self.audio_buffer: + # If buffer is small, just clear it entirely to be safe + logger.debug(f"Clearing small audio buffer ({len(self.audio_buffer)} chunks)") + self.audio_buffer = [] + + # Now play the stop sound (after recording has stopped) + play_stop_sound() + # Wait for threads to finish if self.audio_thread and self.audio_thread.is_alive(): self.audio_thread.join(timeout=1.0)