Conversation
Refactor Audio Stack, Implement Offline Voice Agent and Implement Comprehensive Audio Testing and Diagnostics
LiveReview Pre-Commit Check: vouched (iter:12, coverage:69%)
…loading - Updated `test_e2e_mic_asr.py` to replace pyaudio stream handling with AudioRecorder for improved audio capture. - Simplified audio frame reading logic and ensured proper cleanup of resources. - Removed unnecessary calls to load TTS engine in `test_tts_queue.py` to enhance test efficiency. - Set audio backend to "pyaudio" in `test_vad_wakeword_units.py` to ensure consistent configuration for wake word detection tests. LiveReview Pre-Commit Check: skipped
LiveReview Pre-Commit Check: vouched (iter:1, coverage:0%)
LiveReview Pre-Commit Check: vouched (iter:1, coverage:0%)
LiveReview Pre-Commit Check: vouched (iter:1, coverage:0%)
LiveReview Pre-Commit Check: vouched (iter:1, coverage:0%)
LiveReview Pre-Commit Check: vouched (iter:1, coverage:0%)
LiveReview Pre-Commit Check: vouched (iter:2, coverage:0%)
LiveReview Pre-Commit Check: vouched (iter:1, coverage:0%)
LiveReview Pre-Commit Check: vouched (iter:1, coverage:0%)
LiveReview Pre-Commit Check: ran (iter:4, coverage:100%)
LiveReview Pre-Commit Check: ran (iter:3, coverage:100%)
LiveReview Pre-Commit Check: ran (iter:3, coverage:99%)
LiveReview Pre-Commit Check: ran (iter:1, coverage:0%)
LiveReview Pre-Commit Check: ran (iter:2, coverage:77%)
LiveReview Pre-Commit Check: ran (iter:3, coverage:59%)
LiveReview Pre-Commit Check: ran (iter:2, coverage:90%)
LiveReview Pre-Commit Check: ran (iter:1, coverage:0%)
LiveReview Pre-Commit Check: ran (iter:1, coverage:0%)
LiveReview Pre-Commit Check: ran (iter:1, coverage:0%)
Code Review by Qodo
1. TTS interrupt hangs wait
|
PR Summary by QodoRefactor: Migrate audio stack to sounddevice-exclusive backend and expand test coverage WalkthroughsDescription• Replaces PyAudio with sounddevice as the exclusive audio backend across ASR, TTS, and wake-word. • Centralizes device/stream management via new AudioRecorder/AudioPlayer utilities in audio_utils. • Expands test + example infrastructure to validate audio capture, playback, and offline pipelines. Diagramgraph TD
subgraph src_audio["src/audio"]
AU["audio_utils.py\n(Recorder/Player/Streams)"] --> SD[("sounddevice")]
ASR["asr.py\n(ASREngine)"] --> AU
TTS["tts.py\n(TTSEngine)"] --> AU
WW["wake_word.py\n(WakeWordDetector)"] --> AU
VAD["vad.py\n(VADEngine)"] --> AU
end
LOG["log.json"] --> CFG["config.py\n(Config + logging)"] --> AU
subgraph tests["tests"]
CONF["conftest.py"]
TAU["test_audio_utils.py"]
TENG["test_audio_engine_units.py"]
TBK["test_audio_backends.py"]
TWW["test_wake_word_coverage.py"]
end
AU --> TAU --> CONF
ASR --> TENG
TTS --> TENG
AU --> TBK
WW --> TWW
subgraph Legend
direction LR
_mod["Module"] ~~~ _ext[("External")] ~~~ _file["Config/File"]
end
High-Level AssessmentThe following are alternative approaches to this PR: 1. Maintain dual backend (sounddevice + PyAudio fallback)
2. Split into smaller PRs (backend refactor / tests+examples / CI+deps)
Recommendation: The sounddevice-exclusive consolidation is strategically sound and simplifies long-term maintenance. The main improvement would be splitting the change-set (core backend refactor vs testing/examples vs CI/deps) to reduce review risk; also watch for side-effectful logging setup in File ChangesEnhancement (4)
Bug fix (1)
Refactor (18)
Documentation (9)
Other (29)
|
| """Clear queue and stop current playback.""" | ||
| while not self._tts_queue.empty(): | ||
| try: | ||
| self._tts_queue.get_nowait() | ||
| _item = self._tts_queue.get_nowait() | ||
| except queue.Empty: | ||
| break | ||
| logger.debug("TTS interrupted") |
There was a problem hiding this comment.
1. Tts interrupt hangs wait 🐞 Bug ☼ Reliability
TTSEngine.interrupt() drains _tts_queue without calling task_done(), so wait() (Queue.join) and speak(blocking=True) can block indefinitely. It also never stops in-progress sounddevice playback, so “interrupt” does not actually interrupt current audio output.
Agent Prompt
## Issue description
`TTSEngine.interrupt()` removes queued items using `Queue.get_nowait()` but does not call `Queue.task_done()` for those items. Since `wait()` uses `Queue.join()`, this can leave `unfinished_tasks` > 0 forever and hang `wait()` / `speak(blocking=True)`.
Additionally, `interrupt()` claims to stop current playback but does not stop the active sounddevice playback.
## Issue Context
The playback loop correctly calls `task_done()` for items it consumes, but `interrupt()` is another consumer of the same queue and must also decrement the unfinished task counter for every removed item.
## Fix Focus Areas
- src/audio/tts.py[159-170]
- src/audio/tts.py[181-203]
### Implementation notes
- In `interrupt()`, for every successfully dequeued item, call `self._tts_queue.task_done()`.
- If the dequeued item is the `None` sentinel, consider re-enqueueing it (or avoid consuming it) so shutdown semantics remain intact.
- To actually stop current playback, add a backend stop call (e.g., `sounddevice.stop()`) via `AudioPlayer` (preferred) or directly in `TTSEngine.interrupt()`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
No description provided.