From bfd06b321d8004e9471efe7f9add8e77bff2bba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 1 Apr 2025 10:07:44 -0700 Subject: [PATCH] BaseOutputTransport: optimize BotSpeakingFrames --- CHANGELOG.md | 27 ++++++++++++++++----------- src/pipecat/transports/base_output.py | 13 +++++++++++-- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 649ade028..2e95d9162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,17 +76,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `GladiaSTTService` now uses the `solaria-1` model by default. Other params use Gladia's default values. Added support for more language codes. -### Fixed - -- Fixed an issue that could cause the `TranscriptionUpdateFrame` being pushed - because of an interruption to be discarded. - -- Fixed an issue that would cause `SegmentedSTTService` based services - (e.g. `OpenAISTTService`) to try to transcribe non-spoken audio, causing - invalid transcriptions. - -- Fixed an issue where `GoogleTTSService` was emitting two `TTSStoppedFrames`. - ### Deprecated - All Pipecat services imports have been deprecated and a warning will be shown @@ -104,6 +93,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Deprecated using `GladiaSTTService.InputParams` directly. Use the new `GladiaInputParams` class instead. +### Fixed + +- Fixed an issue that could cause the `TranscriptionUpdateFrame` being pushed + because of an interruption to be discarded. + +- Fixed an issue that would cause `SegmentedSTTService` based services + (e.g. `OpenAISTTService`) to try to transcribe non-spoken audio, causing + invalid transcriptions. + +- Fixed an issue where `GoogleTTSService` was emitting two `TTSStoppedFrames`. + +### Performance + +- `BotSpeakingFrame`s are now sent every 200ms. If the output transport audio chunks + are higher than 200ms then they will be sent at every audio chunk. + ### Other - Added foundational example `37-mem0.py` demonstrating how to use the diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index 7eb446452..178a0741b 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -336,13 +336,22 @@ class BaseOutputTransport(FrameProcessor): return without_mixer(BOT_VAD_STOP_SECS) async def _sink_task_handler(self): + # Push a BotSpeakingFrame every 200ms, we don't really need to push it + # at every audio chunk. If the audio chunk is bigger than 200ms, push at + # every audio chunk. + TOTAL_CHUNK_MS = self._params.audio_out_10ms_chunks * 10 + BOT_SPEAKING_CHUNK_PERIOD = max(int(200 / TOTAL_CHUNK_MS), 1) + bot_speaking_counter = 0 async for frame in self._next_frame(): # Notify the bot started speaking upstream if necessary and that # it's actually speaking. if isinstance(frame, TTSAudioRawFrame): await self._bot_started_speaking() - await self.push_frame(BotSpeakingFrame()) - await self.push_frame(BotSpeakingFrame(), FrameDirection.UPSTREAM) + if bot_speaking_counter % BOT_SPEAKING_CHUNK_PERIOD == 0: + await self.push_frame(BotSpeakingFrame()) + await self.push_frame(BotSpeakingFrame(), FrameDirection.UPSTREAM) + bot_speaking_counter = 0 + bot_speaking_counter += 1 # No need to push EndFrame, it's pushed from process_frame(). if isinstance(frame, EndFrame):