From 4c8c44ecc35c0652ff93f7a9eabe771c39a35c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 6 Nov 2025 11:16:34 -0800 Subject: [PATCH] BaseOutputTransport: send silence when EndFrame is received --- CHANGELOG.md | 5 +++++ src/pipecat/transports/base_output.py | 13 +++++++++++++ src/pipecat/transports/base_transport.py | 2 ++ 3 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9baa61a9c..5c6ff6143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `TransportParams.audio_out_silence_secs`, which specifies how many + seconds of silence to output when an `EndFrame` reaches the output + transport. This can help ensure that all audio data is fully delivered to + clients. + - Added new `FrameProcessor.broadcast_frame()` method. This will push two instances of a given frame class, one upstream and the other downstream. diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index 3edf434a9..ace1e2edf 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -729,11 +729,24 @@ class BaseOutputTransport(FrameProcessor): else: return without_mixer(BOT_VAD_STOP_SECS) + async def _send_silence(self, secs: int): + if secs <= 0: + return + + sample_width = 2 + silence = b"\x00" * self.sample_rate * sample_width * secs + silence_frame = OutputAudioRawFrame( + audio=silence, sample_rate=self.sample_rate, num_channels=1 + ) + await self._transport.write_audio_frame(silence_frame) + async def _audio_task_handler(self): """Main audio processing task handler.""" async for frame in self._next_frame(): # No need to push EndFrame, it's pushed from process_frame(). if isinstance(frame, EndFrame): + # Send some final silence so words don't cut out. + await self._send_silence(self._params.audio_out_end_silence_secs) break # Handle frame. diff --git a/src/pipecat/transports/base_transport.py b/src/pipecat/transports/base_transport.py index b48cec02d..e1f7ecb50 100644 --- a/src/pipecat/transports/base_transport.py +++ b/src/pipecat/transports/base_transport.py @@ -83,6 +83,7 @@ class TransportParams(BaseModel): audio_out_10ms_chunks: Number of 10ms chunks to buffer for output. 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_in_enabled: Enable audio input streaming. audio_in_sample_rate: Input audio sample rate in Hz. audio_in_channels: Number of input audio channels. @@ -131,6 +132,7 @@ class TransportParams(BaseModel): audio_out_10ms_chunks: int = 4 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_in_enabled: bool = False audio_in_sample_rate: Optional[int] = None audio_in_channels: int = 1