diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b4660d89..415fb1f74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `speed` as an `InputParam` for both `ElevenLabsTTSService` and + `ElevenLabsHttpTTSService`. + - Added new `LLMFullResponseAggregator` to aggregate full LLM completions. At every completion the `on_completion` event handler is triggered. diff --git a/src/pipecat/services/elevenlabs.py b/src/pipecat/services/elevenlabs.py index 4e6c84307..c41e5de02 100644 --- a/src/pipecat/services/elevenlabs.py +++ b/src/pipecat/services/elevenlabs.py @@ -116,6 +116,44 @@ def output_format_from_sample_rate(sample_rate: int) -> str: return "pcm_16000" +def build_elevenlabs_voice_settings( + settings: Dict[str, Any], +) -> Optional[Dict[str, Union[float, bool]]]: + """Build voice settings dictionary for ElevenLabs based on provided settings. + + Args: + settings: Dictionary containing voice settings parameters + + Returns: + Dictionary of voice settings or None if required parameters are missing + """ + voice_settings = {} + if settings["stability"] is not None and settings["similarity_boost"] is not None: + voice_settings["stability"] = settings["stability"] + voice_settings["similarity_boost"] = settings["similarity_boost"] + if settings["style"] is not None: + voice_settings["style"] = settings["style"] + if settings["use_speaker_boost"] is not None: + voice_settings["use_speaker_boost"] = settings["use_speaker_boost"] + if settings["speed"] is not None: + voice_settings["speed"] = settings["speed"] + else: + if settings["style"] is not None: + logger.warning( + "'style' is set but will not be applied because 'stability' and 'similarity_boost' are not both set." + ) + if settings["use_speaker_boost"] is not None: + logger.warning( + "'use_speaker_boost' is set but will not be applied because 'stability' and 'similarity_boost' are not both set." + ) + if settings["speed"] is not None: + logger.warning( + "'speed' is set but will not be applied because 'stability' and 'similarity_boost' are not both set." + ) + + return voice_settings or None + + def calculate_word_times( alignment_info: Mapping[str, Any], cumulative_time: float ) -> List[Tuple[str, float]]: @@ -145,6 +183,7 @@ class ElevenLabsTTSService(InterruptibleWordTTSService): similarity_boost: Optional[float] = None style: Optional[float] = None use_speaker_boost: Optional[bool] = None + speed: Optional[float] = None auto_mode: Optional[bool] = True @model_validator(mode="after") @@ -202,6 +241,7 @@ class ElevenLabsTTSService(InterruptibleWordTTSService): "similarity_boost": params.similarity_boost, "style": params.style, "use_speaker_boost": params.use_speaker_boost, + "speed": params.speed, "auto_mode": str(params.auto_mode).lower(), } self.set_model_name(model) @@ -224,28 +264,7 @@ class ElevenLabsTTSService(InterruptibleWordTTSService): return language_to_elevenlabs_language(language) def _set_voice_settings(self): - voice_settings = {} - if ( - self._settings["stability"] is not None - and self._settings["similarity_boost"] is not None - ): - voice_settings["stability"] = self._settings["stability"] - voice_settings["similarity_boost"] = self._settings["similarity_boost"] - if self._settings["style"] is not None: - voice_settings["style"] = self._settings["style"] - if self._settings["use_speaker_boost"] is not None: - voice_settings["use_speaker_boost"] = self._settings["use_speaker_boost"] - else: - if self._settings["style"] is not None: - logger.warning( - "'style' is set but will not be applied because 'stability' and 'similarity_boost' are not both set." - ) - if self._settings["use_speaker_boost"] is not None: - logger.warning( - "'use_speaker_boost' is set but will not be applied because 'stability' and 'similarity_boost' are not both set." - ) - - return voice_settings or None + return build_elevenlabs_voice_settings(self._settings) async def set_model(self, model: str): await super().set_model(model) @@ -441,6 +460,7 @@ class ElevenLabsHttpTTSService(TTSService): similarity_boost: Optional[float] = None style: Optional[float] = None use_speaker_boost: Optional[bool] = None + speed: Optional[float] = None def __init__( self, @@ -470,6 +490,7 @@ class ElevenLabsHttpTTSService(TTSService): "similarity_boost": params.similarity_boost, "style": params.style, "use_speaker_boost": params.use_speaker_boost, + "speed": params.speed, } self.set_model_name(model) self.set_voice(voice_id) @@ -479,34 +500,8 @@ class ElevenLabsHttpTTSService(TTSService): def can_generate_metrics(self) -> bool: return True - def _set_voice_settings(self) -> Optional[Dict[str, Union[float, bool]]]: - """Configure voice settings if stability and similarity_boost are provided. - - Returns: - Dictionary of voice settings or None if required parameters are missing. - """ - voice_settings: Dict[str, Union[float, bool]] = {} - if ( - self._settings["stability"] is not None - and self._settings["similarity_boost"] is not None - ): - voice_settings["stability"] = float(self._settings["stability"]) - voice_settings["similarity_boost"] = float(self._settings["similarity_boost"]) - if self._settings["style"] is not None: - voice_settings["style"] = float(self._settings["style"]) - if self._settings["use_speaker_boost"] is not None: - voice_settings["use_speaker_boost"] = bool(self._settings["use_speaker_boost"]) - else: - if self._settings["style"] is not None: - logger.warning( - "'style' is set but will not be applied because 'stability' and 'similarity_boost' are not both set." - ) - if self._settings["use_speaker_boost"] is not None: - logger.warning( - "'use_speaker_boost' is set but will not be applied because 'stability' and 'similarity_boost' are not both set." - ) - - return voice_settings or None + def _set_voice_settings(self): + return build_elevenlabs_voice_settings(self._settings) async def start(self, frame: StartFrame): await super().start(frame)