From 8a6abdd44b896da85c5c386c85e903581207efcd Mon Sep 17 00:00:00 2001 From: Thu Nguyen Date: Thu, 30 Oct 2025 17:09:41 +0700 Subject: [PATCH] feat: Add dynamic speaking_rate control for Google TTS Chirp voices --- src/pipecat/services/google/tts.py | 38 +++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/pipecat/services/google/tts.py b/src/pipecat/services/google/tts.py index bfda3292a..1ce1cc02c 100644 --- a/src/pipecat/services/google/tts.py +++ b/src/pipecat/services/google/tts.py @@ -22,7 +22,7 @@ from pipecat.utils.tracing.service_decorators import traced_tts # Suppress gRPC fork warnings os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "false" -from typing import AsyncGenerator, List, Literal, Optional +from typing import Any, AsyncGenerator, List, Literal, Mapping, Optional from loguru import logger from pydantic import BaseModel @@ -248,7 +248,8 @@ class GoogleHttpTTSService(TTSService): Parameters: pitch: Voice pitch adjustment (e.g., "+2st", "-50%"). - rate: Speaking rate adjustment (e.g., "slow", "fast", "125%"). + rate: Speaking rate adjustment (e.g., "slow", "fast", "125%"). Used for SSML prosody tags (non-Chirp voices). + speaking_rate: Speaking rate for AudioConfig (Chirp/Journey voices). Range [0.25, 4.0]. volume: Volume adjustment (e.g., "loud", "soft", "+6dB"). emphasis: Emphasis level for the text. language: Language for synthesis. Defaults to English. @@ -258,6 +259,7 @@ class GoogleHttpTTSService(TTSService): pitch: Optional[str] = None rate: Optional[str] = None + speaking_rate: Optional[float] = None volume: Optional[str] = None emphasis: Optional[Literal["strong", "moderate", "reduced", "none"]] = None language: Optional[Language] = Language.EN @@ -291,6 +293,7 @@ class GoogleHttpTTSService(TTSService): self._settings = { "pitch": params.pitch, "rate": params.rate, + "speaking_rate": params.speaking_rate, "volume": params.volume, "emphasis": params.emphasis, "language": self.language_to_service_language(params.language) @@ -360,6 +363,21 @@ class GoogleHttpTTSService(TTSService): """ return language_to_google_tts_language(language) + async def _update_settings(self, settings: Mapping[str, Any]): + """Override to handle speaking_rate updates for Chirp/Journey voices. + Args: + settings: Dictionary of settings to update. Can include 'speaking_rate' (float) + """ + if "speaking_rate" in settings: + rate_value = float(settings["speaking_rate"]) + if 0.25 <= rate_value <= 4.0: + self._settings["speaking_rate"] = rate_value + else: + logger.warning( + f"Invalid speaking_rate value: {rate_value}. Must be between 0.25 and 4.0" + ) + await super()._update_settings(settings) + def _construct_ssml(self, text: str) -> str: ssml = "" @@ -436,10 +454,18 @@ class GoogleHttpTTSService(TTSService): voice = texttospeech_v1.VoiceSelectionParams( language_code=self._settings["language"], name=self._voice_id ) - audio_config = texttospeech_v1.AudioConfig( - audio_encoding=texttospeech_v1.AudioEncoding.LINEAR16, - sample_rate_hertz=self.sample_rate, - ) + # For Chirp and Journey voices, include speaking_rate in AudioConfig + if (is_chirp_voice or is_journey_voice) and self._settings["speaking_rate"] is not None: + audio_config = texttospeech_v1.AudioConfig( + audio_encoding=texttospeech_v1.AudioEncoding.LINEAR16, + sample_rate_hertz=self.sample_rate, + speaking_rate=self._settings["speaking_rate"], + ) + else: + audio_config = texttospeech_v1.AudioConfig( + audio_encoding=texttospeech_v1.AudioEncoding.LINEAR16, + sample_rate_hertz=self.sample_rate, + ) request = texttospeech_v1.SynthesizeSpeechRequest( input=synthesis_input, voice=voice, audio_config=audio_config