Compare commits

..

1 Commits

Author SHA1 Message Date
Fabio Manganiello fa49db4107
[assistant.picovoice] Conversation flow improvements.
continuous-integration/drone/push Build is passing Details
- The `Responding` state should be modelled as an extra event/binary
  flag, not as an assistant state. The assistant may be listening for
  hotwords even while the `tts` plugin is responding, and we don't want
  the two states to interfere with each either - neither to build a more
  complex state machine that also needs to take concurrent states into
  account.

- Stop any responses being rendered upon the `tts` plugin when a new
  hotword audio is detected. If e.g. I say "Ok Google", I should always
  be able to trigger the assistant and stop any concurrent audio
  process.

- `SpeechRecognizedEvent` should be emitted even if `cheetah`'s latest
  audio frame results weren't marked as final, and the speech detection
  window timed out. Cheetah's `is_final` detection seems to be quite
  buggy sometimes, and it may not properly detect the end of utterances,
  especially with non-native accents. The workaround is to flush out
  whatever text is available (if at least some speech was detected) into
  a `SpeechRecognizedEvent` upon timeout.
2024-04-13 20:03:35 +02:00
1 changed files with 19 additions and 0 deletions

View File

@ -42,11 +42,17 @@ class AudioRecorder:
)
def __enter__(self):
"""
Start the audio stream.
"""
self._stop_event.clear()
self.stream.start()
return self
def __exit__(self, *_):
"""
Stop the audio stream.
"""
self.stop()
def _audio_callback(self, indata, *_):
@ -59,6 +65,13 @@ class AudioRecorder:
self.logger.warning('Audio queue is full, dropping audio frame')
def read(self, timeout: Optional[float] = None):
"""
Read an audio frame from the queue.
:param timeout: Timeout in seconds. If None, the method will block until
an audio frame is available.
:return: Audio frame or None if the timeout has expired.
"""
try:
return self._audio_queue.get(timeout=timeout)
except TimeoutError:
@ -66,6 +79,9 @@ class AudioRecorder:
return None
def stop(self):
"""
Stop the audio stream.
"""
self._stop_event.set()
self.stream.stop()
@ -73,4 +89,7 @@ class AudioRecorder:
return self._stop_event.is_set() or self._upstream_stop_event.is_set()
def wait(self, timeout: Optional[float] = None):
"""
Wait until the audio stream is stopped.
"""
wait_for_either(self._stop_event, self._upstream_stop_event, timeout=timeout)