diff --git a/src/pipecat/transports/base_transport.py b/src/pipecat/transports/base_transport.py index a78e1046c..33616a6ea 100644 --- a/src/pipecat/transports/base_transport.py +++ b/src/pipecat/transports/base_transport.py @@ -84,6 +84,8 @@ class TransportParams(BaseModel): audio_out_mixer: Audio mixer instance or destination mapping. audio_out_destinations: List of audio output destination identifiers. audio_out_end_silence_secs: How much silence to send after an EndFrame (0 for no silence). + audio_out_insert_silence: Insert silence frames when the audio output queue is empty. + When False, the transport will wait for audio data instead of inserting silence. audio_in_enabled: Enable audio input streaming. audio_in_sample_rate: Input audio sample rate in Hz. audio_in_channels: Number of input audio channels. @@ -144,6 +146,7 @@ class TransportParams(BaseModel): audio_out_mixer: Optional[BaseAudioMixer | Mapping[Optional[str], BaseAudioMixer]] = None audio_out_destinations: List[str] = Field(default_factory=list) audio_out_end_silence_secs: int = 2 + audio_out_insert_silence: bool = True audio_in_enabled: bool = False audio_in_sample_rate: Optional[int] = None audio_in_channels: int = 1 diff --git a/src/pipecat/transports/smallwebrtc/transport.py b/src/pipecat/transports/smallwebrtc/transport.py index 36f883278..cfe2b7f49 100644 --- a/src/pipecat/transports/smallwebrtc/transport.py +++ b/src/pipecat/transports/smallwebrtc/transport.py @@ -79,14 +79,17 @@ class RawAudioTrack(AudioStreamTrack): supporting queued audio data with proper synchronization. """ - def __init__(self, sample_rate): + def __init__(self, sample_rate: int, insert_silence: bool = True): """Initialize the raw audio track. Args: sample_rate: The audio sample rate in Hz. + insert_silence: If True, emit silence when the queue is empty. If False, + wait until audio data is available. """ super().__init__() self._sample_rate = sample_rate + self._insert_silence = insert_silence self._samples_per_10ms = sample_rate * 10 // 1000 self._bytes_per_10ms = self._samples_per_10ms * 2 # 16-bit (2 bytes per sample) self._timestamp = 0 @@ -131,12 +134,19 @@ class RawAudioTrack(AudioStreamTrack): if wait > 0: await asyncio.sleep(wait) - if self._chunk_queue: + if not self._chunk_queue: + if self._insert_silence: + chunk = bytes(self._bytes_per_10ms) + else: + while not self._chunk_queue: + await asyncio.sleep(0.005) + chunk, future = self._chunk_queue.popleft() + if future and not future.done(): + future.set_result(True) + else: chunk, future = self._chunk_queue.popleft() if future and not future.done(): future.set_result(True) - else: - chunk = bytes(self._bytes_per_10ms) # silence # Convert the byte data to an ndarray of int16 samples samples = np.frombuffer(chunk, dtype=np.int16) @@ -484,7 +494,10 @@ class SmallWebRTCClient: self._video_input_track = self._webrtc_connection.video_input_track() self._screen_video_track = self._webrtc_connection.screen_video_input_track() if self._params.audio_out_enabled: - self._audio_output_track = RawAudioTrack(sample_rate=self._out_sample_rate) + self._audio_output_track = RawAudioTrack( + sample_rate=self._out_sample_rate, + insert_silence=self._params.audio_out_insert_silence, + ) self._webrtc_connection.replace_audio_track(self._audio_output_track) if self._params.video_out_enabled: