transports: allow adding a vad analyzer to BaseInputTransport

This commit is contained in:
Aleix Conchillo Flaqué
2024-05-17 10:25:00 -07:00
parent 8c877d7d8e
commit f432e2b17e
4 changed files with 30 additions and 19 deletions

View File

@@ -18,6 +18,7 @@ from pipecat.processors.aggregators.llm_response import (
from pipecat.services.elevenlabs import ElevenLabsTTSService from pipecat.services.elevenlabs import ElevenLabsTTSService
from pipecat.services.openai import OpenAILLMService from pipecat.services.openai import OpenAILLMService
from pipecat.transports.services.daily import DailyParams, DailyTransport from pipecat.transports.services.daily import DailyParams, DailyTransport
from pipecat.vad.silero import SileroVADAnalyzer
from runner import configure from runner import configure
@@ -41,6 +42,7 @@ async def main(room_url: str, token):
audio_out_enabled=True, audio_out_enabled=True,
transcription_enabled=True, transcription_enabled=True,
vad_enabled=True, vad_enabled=True,
vad_analyzer=SileroVADAnalyzer()
) )
) )

View File

@@ -6,12 +6,16 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from pydantic import ConfigDict
from pydantic.main import BaseModel from pydantic.main import BaseModel
from pipecat.processors.frame_processor import FrameProcessor from pipecat.processors.frame_processor import FrameProcessor
from pipecat.vad.vad_analyzer import VADAnalyzer
class TransportParams(BaseModel): class TransportParams(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
camera_out_enabled: bool = False camera_out_enabled: bool = False
camera_out_is_live: bool = False camera_out_is_live: bool = False
camera_out_width: int = 1024 camera_out_width: int = 1024
@@ -27,6 +31,7 @@ class TransportParams(BaseModel):
audio_in_channels: int = 1 audio_in_channels: int = 1
vad_enabled: bool = False vad_enabled: bool = False
vad_audio_passthrough: bool = False vad_audio_passthrough: bool = False
vad_analyzer: VADAnalyzer | None = None
class BaseTransport(ABC): class BaseTransport(ABC):

View File

@@ -161,12 +161,6 @@ class DailyTransportClient(EventHandler):
"speaker", sample_rate=self._params.audio_in_sample_rate, channels=self._params.audio_in_channels) "speaker", sample_rate=self._params.audio_in_sample_rate, channels=self._params.audio_in_channels)
Daily.select_speaker_device("speaker") Daily.select_speaker_device("speaker")
self._vad_analyzer = None
if self._params.vad_enabled:
self._vad_analyzer = WebRTCVADAnalyzer(
sample_rate=self._params.audio_in_sample_rate,
num_channels=self._params.audio_in_channels)
@property @property
def participant_id(self) -> str: def participant_id(self) -> str:
return self._participant_id return self._participant_id
@@ -174,12 +168,6 @@ class DailyTransportClient(EventHandler):
def set_callbacks(self, callbacks: DailyCallbacks): def set_callbacks(self, callbacks: DailyCallbacks):
self._callbacks = callbacks self._callbacks = callbacks
def vad_analyze(self, audio_frames: bytes) -> VADState:
state = VADState.QUIET
if self._vad_analyzer:
state = self._vad_analyzer.analyze_audio(audio_frames)
return state
def send_message(self, frame: DailyTransportMessageFrame): def send_message(self, frame: DailyTransportMessageFrame):
self._client.send_app_message(frame.message, frame.participant_id) self._client.send_app_message(frame.message, frame.participant_id)
@@ -435,6 +423,12 @@ class DailyInputTransport(BaseInputTransport):
self._video_renderers = {} self._video_renderers = {}
self._camera_in_queue = queue.Queue() self._camera_in_queue = queue.Queue()
self._vad_analyzer = params.vad_analyzer
if params.vad_enabled and not params.vad_analyzer:
self._vad_analyzer = WebRTCVADAnalyzer(
sample_rate=self._params.audio_in_sample_rate,
num_channels=self._params.audio_in_channels)
async def start(self, frame: StartFrame): async def start(self, frame: StartFrame):
if self._running: if self._running:
return return
@@ -461,7 +455,10 @@ class DailyInputTransport(BaseInputTransport):
await self._client.cleanup() await self._client.cleanup()
def vad_analyze(self, audio_frames: bytes) -> VADState: def vad_analyze(self, audio_frames: bytes) -> VADState:
return self._client.vad_analyze(audio_frames) state = VADState.QUIET
if self._vad_analyzer:
state = self._vad_analyzer.analyze_audio(audio_frames)
return state
def read_raw_audio_frames(self, frame_count: int) -> bytes: def read_raw_audio_frames(self, frame_count: int) -> bytes:
return self._client.read_raw_audio_frames(frame_count) return self._client.read_raw_audio_frames(frame_count)

View File

@@ -39,11 +39,10 @@ def int2float(sound):
return sound return sound
class SileroVAD(FrameProcessor, VADAnalyzer): class SileroVADAnalyzer(VADAnalyzer):
def __init__(self, sample_rate=16000, audio_passthrough=False): def __init__(self, sample_rate=16000):
FrameProcessor.__init__(self) super().__init__(sample_rate=sample_rate, num_channels=1)
VADAnalyzer.__init__(self, sample_rate=sample_rate, num_channels=1)
logger.debug("Loading Silero VAD model...") logger.debug("Loading Silero VAD model...")
@@ -52,7 +51,6 @@ class SileroVAD(FrameProcessor, VADAnalyzer):
) )
self._processor_vad_state: VADState = VADState.QUIET self._processor_vad_state: VADState = VADState.QUIET
self._audio_passthrough = audio_passthrough
logger.debug("Loaded Silero VAD") logger.debug("Loaded Silero VAD")
@@ -74,6 +72,15 @@ class SileroVAD(FrameProcessor, VADAnalyzer):
logger.error(f"Error analyzing audio with Silero VAD: {e}") logger.error(f"Error analyzing audio with Silero VAD: {e}")
return 0 return 0
class SileroVAD(FrameProcessor):
def __init__(self, sample_rate=16000, audio_passthrough=False):
super().__init__()
self._vad_analyzer = SileroVADAnalyzer(sample_rate=sample_rate)
self._audio_passthrough = audio_passthrough
# #
# FrameProcessor # FrameProcessor
# #
@@ -89,7 +96,7 @@ class SileroVAD(FrameProcessor, VADAnalyzer):
async def _analyze_audio(self, frame: AudioRawFrame): async def _analyze_audio(self, frame: AudioRawFrame):
# Check VAD and push event if necessary. We just care about changes # Check VAD and push event if necessary. We just care about changes
# from QUIET to SPEAKING and vice versa. # from QUIET to SPEAKING and vice versa.
new_vad_state = self.analyze_audio(frame.audio) new_vad_state = self._vad_analyzer.analyze_audio(frame.audio)
if new_vad_state != self._processor_vad_state and new_vad_state != VADState.STARTING and new_vad_state != VADState.STOPPING: if new_vad_state != self._processor_vad_state and new_vad_state != VADState.STARTING and new_vad_state != VADState.STOPPING:
new_frame = None new_frame = None