From a3c4d43b4674528533da9835f3990bea73e840ed Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Wed, 6 Dec 2023 15:01:08 -0600 Subject: [PATCH] Add VAD --- requirements.txt | 2 +- sounds/awake.wav | Bin 0 -> 17666 bytes sounds/done.wav | Bin 0 -> 49242 bytes wyoming_satellite/__main__.py | 373 +++++++++++++++++++++++++++------- 4 files changed, 295 insertions(+), 80 deletions(-) create mode 100644 sounds/awake.wav create mode 100644 sounds/done.wav diff --git a/requirements.txt b/requirements.txt index 9aa282d..2667ffe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -wyoming==1.3.0 +wyoming==1.4.0 zeroconf==0.88.0 pyring-buffer==1.0.0 diff --git a/sounds/awake.wav b/sounds/awake.wav new file mode 100644 index 0000000000000000000000000000000000000000..8cb6535ae897dfffd94d6999818f74f69b99f2d3 GIT binary patch literal 17666 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 literal 0 HcmV?d00001 diff --git a/sounds/done.wav b/sounds/done.wav new file mode 100644 index 0000000000000000000000000000000000000000..d76ec5b4780b2420f663aa54e185193ee5d754a9 GIT binary patch literal 49242 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

YPow&Wf`TCnbj; zA}fa%PV=H+7mp>o5{ws|5+8IJ;cQv3K`0WQM8i_C$$5vnq5+v*EY#g62Be+6Cloj&6;E=AcLR2Nim zN9CBS^`+bGP*ILK`xcsHc9+Mwxcdl`V?HBRBAJF@l~}UqSdcnKlics_75jdag^9jn zoXR?vs}*CBQ+RUOjf+4rToviLh#-=79Jz=p*NL9pm@$iVqdG}TCgScF?wzDucH*cP z%X77x)3s<+Y}!RmXXTE5p;oduk&{@OV4~o#<6b1_^6vSBz8VW1^Es zxaxE#cMF}4Cb1@G(?VB_Q?Y!pGKUf|Z*)i|R0;ipfnqt%pPV;~Cp)h}Wc-K^2u(3> za@m92AH$KDkI6BYtvMfczAO~P{6Ww`e#Dm@{i2f?r7^FSV=n%SS36E)8IpLH-0L*r zqM@L=yc0@YM3)hTd%0ie6nk>BxQxfmF4s8O$*6(}a-|zfsFX2fHir@7&9QkzQi3MV z$DKr+jk#+aYPm6E_$9K8-S6k0QIw^@vh8 zhQl+l5}`_t$(@c)hu6_z{xg!imr-J)3J*>u!oS=vKO*Hv*EqV|buryWFNY|XFMUD!mj!v1uNlwO)Yu)|w z-remubx}r+xF09~N78sC4>=#3!;S6cbK|+YWfq}8=yx1ElBUe$jyXPslK=j3T*T&+ zPcds2j^riw{NGZNV=}wU<=#8G+Q)E({fD5F$u-Sk&zzB(n<2aM|5Y! z^Mop~GRM__Tgf9n*dm2R! zNw{`68r4_cN&h3=SGw}V@ZA5jZx}+K(7Dqdt>+i@A$DX|F2~{;$B9L*@GL= zeG+QiJ2!&N<$mN|ITriuxR?3m#~pR=Tq)opJxWGxGj_uXX3;xZL^ZlN&Ln zEqBIoBiFdkZnoH6vHxRN#bo+uZg*$wd~6i?{ODM0ba%wf5WDjKjwDyaBp18O(eS^N z$l2H!kN%fqZWhO@e2)D*(p2nROcMVqsn{Kl{+BV`)o%3InHVqfId&#SsT`9zg~R^` D^5usr literal 0 HcmV?d00001 diff --git a/wyoming_satellite/__main__.py b/wyoming_satellite/__main__.py index f5f2b07..b4b30e5 100644 --- a/wyoming_satellite/__main__.py +++ b/wyoming_satellite/__main__.py @@ -2,18 +2,22 @@ import argparse import asyncio import logging +import math import shlex import sys import time import uuid +import wave from enum import Enum from functools import partial from pathlib import Path -from typing import Callable, Final, Optional +from typing import Callable, Final, Iterator, Optional, Union +from pyring_buffer import RingBuffer from wyoming.asr import Transcribe, Transcript from wyoming.audio import AudioChunk, AudioStart, AudioStop from wyoming.client import AsyncClient +from wyoming.error import Error from wyoming.event import Event from wyoming.info import Attribution, Describe, Info, Satellite from wyoming.mic import MicProcessAsyncClient @@ -34,6 +38,8 @@ _DIR = Path(__file__).parent class SatelliteState(str, Enum): WAKE = "wake" ASR = "asr" + VAD_RESET = "vad-reset" + VAD_WAIT = "vad-wait" async def main() -> None: @@ -67,6 +73,7 @@ async def main() -> None: default=1024, help="Sample per chunk for mic-command (default: 1024)", ) + parser.add_argument("--mic-volume-multiplier", type=float, default=1.0) # Sound output parser.add_argument("--snd-uri", help="URI of Wyoming sound service") @@ -89,6 +96,7 @@ async def main() -> None: default=1, help="Sample channels of snd-command (default: 1)", ) + parser.add_argument("--snd-volume-multiplier", type=float, default=1.0) # Local wake word detection parser.add_argument("--wake-uri", help="URI of Wyoming wake word detection service") @@ -120,24 +128,31 @@ async def main() -> None: # Voice activity detector parser.add_argument( - "--vad", choices=("disabled", "webrtcvad", "silero"), default="disabled" + "--vad", action="store_true", help="Wait for speech before streaming audio" ) parser.add_argument("--vad-threshold", type=float, default=0.5) - parser.add_argument("--vad-trigger-level", type=int, default=3) - parser.add_argument("--vad-buffer-chunks", type=int, default=40) + parser.add_argument("--vad-trigger-level", type=int, default=1) + parser.add_argument("--vad-buffer-seconds", type=float, default=1) # Audio enhancement (requires webrtc-noise-audio) parser.add_argument( "--noise-suppression", type=int, default=0, choices=(0, 1, 2, 3, 4) ) parser.add_argument("--auto-gain", type=int, default=0, choices=list(range(32))) - parser.add_argument("--volume-multiplier", type=float, default=1.0) # External event handlers parser.add_argument( "--event-uri", help="URI of Wyoming service to forward events to" ) + # Sounds + parser.add_argument( + "--awake-wav", help="WAV file to play when wake word is detected" + ) + parser.add_argument( + "--done-wav", help="WAV file to play when voice command is done" + ) + # Satellite details parser.add_argument("--uri", default="stdio://", help="unix:// or tcp://") parser.add_argument("--name", required=True, help="Name of the satellite") @@ -175,6 +190,17 @@ async def main() -> None: _LOGGER.fatal("Install extras for silero") sys.exit(1) + if args.awake_wav and (not Path(args.awake_wav).is_file()): + _LOGGER.fatal("%s does not exist", args.awake_wav) + sys.exit(1) + + if args.done_wav and (not Path(args.done_wav).is_file()): + _LOGGER.fatal("%s does not exist", args.done_wav) + sys.exit(1) + + if args.vad and (args.wake_uri or args.wake_command): + _LOGGER.warning("VAD is not used with local wake word detection") + logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO) _LOGGER.debug(args) @@ -232,24 +258,42 @@ class SatelliteEventHandler(AsyncEventHandler): self.wyoming_info_event = wyoming_info.event() self.client_id = str(time.monotonic_ns()) + self.is_running = True self.state: Optional[Satellite] = None self.satellite_task: Optional[asyncio.Task] = None - self.volume_multiplier = self.cli_args.volume_multiplier - self.has_audio_processing = needs_webrtc(self.cli_args) or ( - self.volume_multiplier != 1.0 - ) - self.has_wake = bool(self.cli_args.wake_uri or self.cli_args.wake_command) - self.has_snd = bool(self.cli_args.snd_uri or self.cli_args.snd_command) - self.snd_client: Optional[AsyncClient] = None - self.events_client: Optional[AsyncClient] = None - self.is_running = True + self.mic_volume_multiplier = self.cli_args.mic_volume_multiplier + self.has_mic_audio_processing = needs_webrtc(self.cli_args) or ( + self.mic_volume_multiplier != 1.0 + ) self.process_webrtc: Optional[Callable[[bytes], bytes]] = None if needs_webrtc(self.cli_args): self.process_webrtc = WebRtcAudio( self.cli_args.auto_gain, self.cli_args.noise_suppression ) + self.has_wake = bool(self.cli_args.wake_uri or self.cli_args.wake_command) + + self.has_snd = bool(self.cli_args.snd_uri or self.cli_args.snd_command) + self.snd_client: Optional[AsyncClient] = None + self.snd_volume_multiplier = self.cli_args.snd_volume_multiplier + self.has_snd_audio_processing = self.snd_volume_multiplier != 1.0 + + self.process_vad: Optional[Callable[[Optional[bytes]], bool]] = None + self.vad_buffer: Optional[RingBuffer] = None + if needs_silero(self.cli_args): + self.process_vad = SileroVad( + self.cli_args.vad_threshold, self.cli_args.vad_trigger_level + ) + if self.cli_args.vad_buffer_seconds > 0: + # Assume 16Khz, 16-bit mono samples + vad_buffer_bytes = int( + math.ceil(self.cli_args.vad_buffer_seconds * 16000 * 2) + ) + self.vad_buffer = RingBuffer(vad_buffer_bytes) + + self.events_client: Optional[AsyncClient] = None + _LOGGER.debug("Client connected: %s", self.client_id) async def handle_event(self, event: Event) -> bool: @@ -261,35 +305,22 @@ class SatelliteEventHandler(AsyncEventHandler): if RunSatellite.is_type(event.type) and (self.satellite_task is None): if self.has_wake: # Local wake word detection - await self.write_event( - RunPipeline( - start_stage=PipelineStage.ASR, - end_stage=PipelineStage.TTS - if self.has_snd - else PipelineStage.HANDLE, - name=self.cli_args.pipeline, - ).event() - ) self.state = SatelliteState.WAKE self.satellite_task = asyncio.create_task(self.run_satellite_wake()) else: # Remote wake word detection - await self.write_event( - RunPipeline( - start_stage=PipelineStage.WAKE, - end_stage=PipelineStage.TTS - if self.has_snd - else PipelineStage.HANDLE, - name=self.cli_args.pipeline, - ).event() - ) - self.state = None + self.state = SatelliteState.VAD_RESET self.satellite_task = asyncio.create_task(self.run_satellite()) elif Transcript.is_type(event.type): - if self.state == SatelliteState.ASR: + if self.cli_args.done_wav: + await self._play_wav(self.cli_args.done_wav) + + if self.has_wake: # Ready for next wake word detection self.state = SatelliteState.WAKE _LOGGER.info("Detecting wake word") + else: + self.state = SatelliteState.VAD_RESET # STT transcript await self.forward_event(event) @@ -299,38 +330,32 @@ class SatelliteEventHandler(AsyncEventHandler): or AudioChunk.is_type(event.type) or AudioStop.is_type(event.type) ): - if self.snd_client is None: - if self.cli_args.snd_command: - _LOGGER.debug("Running %s", self.cli_args.snd_command) - program, *program_args = shlex.split(self.cli_args.snd_command) - self.snd_client = SndProcessAsyncClient( - rate=self.cli_args.snd_command_rate, - width=self.cli_args.snd_command_width, - channels=self.cli_args.snd_command_channels, - program=program, - program_args=program_args, - ) - else: - _LOGGER.debug("Connecting to %s", self.cli_args.snd_uri) - self.snd_client = AsyncClient.from_uri(self.cli_args.snd_uri) + if AudioStart.is_type(event.type): + # TTS start + await self._ensure_snd_client() + if self.snd_client is not None: + await self.snd_client.connect() - await self.snd_client.connect() - - assert self.snd_client is not None - - try: - # Forward to sound service - await self.snd_client.write_event(event) - except Exception: - _LOGGER.exception("Unexpected error while sending snd audio") - - # Reconnect - self.snd_client = None - - if AudioStart.is_type(event.type) or AudioStop.is_type(event.type): - # TTS start/stop await self.forward_event(event) _LOGGER.debug(event) + elif AudioChunk.is_type(event.type) and (self.snd_client is not None): + # Forward to sound service + event = self._process_snd_audio(event) + await self.snd_client.write_event(event) + elif AudioStop.is_type(event.type): + # TTS stop + await self.forward_event(event) + _LOGGER.debug(event) + + if self.snd_client is not None: + await self.snd_client.disconnect() + self.snd_client = None + elif Detection.is_type(event.type): + if self.cli_args.awake_wav: + await self._play_wav(self.cli_args.awake_wav) + + await self.forward_event(event) + _LOGGER.debug(event) elif ( Detect.is_type(event.type) or Detection.is_type(event.type) @@ -341,12 +366,14 @@ class SatelliteEventHandler(AsyncEventHandler): ): # Other client events: # - Detect for start of wake word detection - # - Detection for when wake word is detected # - Transcribe for when STT starts # - VoiceStarted/VoiceStopped for when user starts/stops speaking # - Synthesize for TTS text await self.forward_event(event) _LOGGER.debug(event) + elif Error.is_type(event.type): + await self.forward_event(event) + _LOGGER.warning(event) else: _LOGGER.debug("Unexpected event: type=%s, data=%s", event.type, event.data) @@ -369,21 +396,100 @@ class SatelliteEventHandler(AsyncEventHandler): _LOGGER.debug("Connecting to %s", self.cli_args.mic_uri) return AsyncClient.from_uri(self.cli_args.mic_uri) + async def _ensure_snd_client(self) -> None: + if (not self.has_snd) or (self.snd_client is not None): + return + + if self.cli_args.snd_command: + _LOGGER.debug("Running %s", self.cli_args.snd_command) + program, *program_args = shlex.split(self.cli_args.snd_command) + self.snd_client = SndProcessAsyncClient( + rate=self.cli_args.snd_command_rate, + width=self.cli_args.snd_command_width, + channels=self.cli_args.snd_command_channels, + program=program, + program_args=program_args, + ) + else: + _LOGGER.debug("Connecting to %s", self.cli_args.snd_uri) + self.snd_client = AsyncClient.from_uri(self.cli_args.snd_uri) + async def run_satellite(self) -> None: try: mic_client = self._make_mic_client() async with mic_client: - _LOGGER.info("Streaming audio") - while True: mic_event = await mic_client.read_event() if mic_event is None: _LOGGER.warning("Microphone service disconnected") break - if AudioChunk.is_type(mic_event.type): - mic_event = self._process_audio(mic_event) + if not AudioChunk.is_type(mic_event.type): + continue + mic_event = self._process_mic_audio(mic_event) + + if self.state == SatelliteState.VAD_RESET: + # Reset vad state + if self.process_vad is not None: + self.process_vad(None) + + if self.vad_buffer is not None: + self.vad_buffer.put(bytes(self.vad_buffer.maxlen)) + + self.state = SatelliteState.VAD_WAIT + + if self.state == SatelliteState.VAD_WAIT: + if self.process_vad is None: + # No VAD + self.state = SatelliteState.ASR + _LOGGER.info("Streaming audio") + + # Start pipeline + await self.write_event( + RunPipeline( + start_stage=PipelineStage.WAKE, + end_stage=PipelineStage.TTS + if self.has_snd + else PipelineStage.HANDLE, + name=self.cli_args.pipeline, + ).event() + ) + else: + # Wait for speech + chunk = AudioChunk.from_event(mic_event) + if self.process_vad(chunk.audio): + # Ready to stream + self.state = SatelliteState.ASR + + # Start pipeline + await self.write_event( + RunPipeline( + start_stage=PipelineStage.WAKE, + end_stage=PipelineStage.TTS + if self.has_snd + else PipelineStage.HANDLE, + name=self.cli_args.pipeline, + ).event() + ) + + if self.vad_buffer: + # Send contents of VAD buffer first + await self.write_event( + AudioChunk( + rate=chunk.rate, + width=chunk.width, + channels=chunk.channels, + audio=self.vad_buffer.getvalue(), + ).event() + ) + + _LOGGER.info("Streaming audio") + elif self.vad_buffer is not None: + # Save audio right before speech + self.vad_buffer.put(chunk.audio) + + if self.state == SatelliteState.ASR: # Forward all audio to server await self.write_event(mic_event) @@ -440,7 +546,7 @@ class SatelliteEventHandler(AsyncEventHandler): break if AudioChunk.is_type(mic_event.type): - mic_event = self._process_audio(mic_event) + mic_event = self._process_mic_audio(mic_event) # Forward to wake service await wake_client.write_event(mic_event) @@ -464,6 +570,17 @@ class SatelliteEventHandler(AsyncEventHandler): _LOGGER.debug("Wake word detected: %s", wake_event) self.state = SatelliteState.ASR + # Start pipeline + await self.write_event( + RunPipeline( + start_stage=PipelineStage.ASR, + end_stage=PipelineStage.TTS + if self.has_snd + else PipelineStage.HANDLE, + name=self.cli_args.pipeline, + ).event() + ) + # Forward to server await self.write_event(wake_event) @@ -478,9 +595,9 @@ class SatelliteEventHandler(AsyncEventHandler): except Exception: _LOGGER.exception("Unexpected error in run_satellite") - def _process_audio(self, audio_event: Event) -> Event: - """Perform audio processing.""" - if not self.has_audio_processing: + def _process_mic_audio(self, audio_event: Event) -> Event: + """Perform microphone audio processing, if necessary.""" + if not self.has_mic_audio_processing: # No processing needed return audio_event @@ -490,8 +607,27 @@ class SatelliteEventHandler(AsyncEventHandler): if self.process_webrtc is not None: audio_bytes = self.process_webrtc(audio_bytes) - if self.volume_multiplier != 1.0: - audio_bytes = multiply_volume(audio_bytes, self.volume_multiplier) + if self.mic_volume_multiplier != 1.0: + audio_bytes = multiply_volume(audio_bytes, self.mic_volume_multiplier) + + return AudioChunk( + rate=chunk.rate, + width=chunk.width, + channels=chunk.channels, + audio=audio_bytes, + ).event() + + def _process_snd_audio(self, audio_event: Event) -> Event: + """Perform output audio processing, if necessary.""" + if not self.has_snd_audio_processing: + # No processing needed + return audio_event + + chunk = AudioChunk.from_event(audio_event) + audio_bytes = chunk.audio + + if self.snd_volume_multiplier != 1.0: + audio_bytes = multiply_volume(audio_bytes, self.snd_volume_multiplier) return AudioChunk( rate=chunk.rate, @@ -516,6 +652,56 @@ class SatelliteEventHandler(AsyncEventHandler): async def disconnect(self) -> None: self.is_running = False + async def _play_wav(self, wav_path: Union[str, Path]) -> None: + await self._ensure_snd_client() + + if self.snd_client is None: + _LOGGER.debug("Cannot play WAV, no snd service") + return + + try: + async with self.snd_client: + for event in self._wav_to_events(wav_path): + await self.snd_client.write_event(event) + except Exception: + _LOGGER.exception("Unexpected error while playing WAV: %s", wav_path) + finally: + self.snd_client = None + + def _wav_to_events( + self, wav_path: Union[str, Path], samples_per_chunk: int = 1024 + ) -> Iterator[Event]: + """Load WAV audio for playback on an event (wake/done).""" + with wave.open(str(wav_path), "rb") as wav_file: + rate = wav_file.getframerate() + width = wav_file.getsampwidth() + channels = wav_file.getnchannels() + + timestamp = 0 + yield AudioStart( + rate=rate, width=width, channels=channels, timestamp=timestamp + ).event() + + audio_bytes = wav_file.readframes(samples_per_chunk) + while audio_bytes: + if self.snd_volume_multiplier != 1.0: + audio_bytes = multiply_volume( + audio_bytes, self.snd_volume_multiplier + ) + + chunk = AudioChunk( + rate=rate, + width=width, + channels=channels, + audio=audio_bytes, + timestamp=timestamp, + ) + yield chunk.event() + timestamp += int(chunk.seconds * 1000) + audio_bytes = wav_file.readframes(samples_per_chunk) + + yield AudioStop(timestamp=timestamp).event() + # ----------------------------------------------------------------------------- @@ -532,18 +718,18 @@ def get_mac_address() -> str: def needs_webrtc(args: argparse.Namespace) -> bool: """Return True if webrtc must be used.""" - return ( - (args.noise_suppression > 0) or (args.auto_gain > 0) or (args.vad == "webrtc") - ) + return (args.noise_suppression > 0) or (args.auto_gain > 0) def needs_silero(args: argparse.Namespace) -> bool: """Return True if silero-vad must be used.""" - return args.vad == "silero" + return args.vad class WebRtcAudio: - _sub_chunk_samples: Final = 160 + """Audio processing using webrtc.""" + + _sub_chunk_samples: Final = 160 # 10ms @ 16Khz _sub_chunk_bytes: Final = _sub_chunk_samples * 2 # 16-bit def __init__(self, auto_gain: int, noise_suppression: int) -> None: @@ -553,6 +739,7 @@ class WebRtcAudio: self.audio_buffer = AudioBuffer(self._sub_chunk_bytes) def __call__(self, audio_bytes: bytes) -> bytes: + """Process in 10ms chunks.""" clean_chunk = bytes() for sub_chunk in chunk_samples( audio_bytes, self._sub_chunk_bytes, self.audio_buffer @@ -563,6 +750,34 @@ class WebRtcAudio: return clean_chunk +class SileroVad: + def __init__(self, threshold: float, trigger_level: int) -> None: + from pysilero_vad import SileroVoiceActivityDetector + + self.detector = SileroVoiceActivityDetector() + self.threshold = threshold + self.trigger_level = trigger_level + self._activation = 0 + + def __call__(self, audio_bytes: Optional[bytes]) -> bool: + if audio_bytes is None: + # Reset + self._activation = 0 + return False + + if self.detector(audio_bytes) >= self.threshold: + # Speech detected + self._activation += 1 + if self._activation >= self.trigger_level: + self._activation = 0 + return True + else: + # Silence detected + self._activation = max(0, self._activation - 1) + + return False + + # ----------------------------------------------------------------------------- if __name__ == "__main__":