Update TTS service settings
This commit is contained in:
@@ -9,8 +9,8 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, AsyncGenerator, Mapping, Optional, Self
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, AsyncGenerator, Optional
|
||||
|
||||
import aiohttp
|
||||
from loguru import logger
|
||||
@@ -27,7 +27,7 @@ from pipecat.frames.frames import (
|
||||
TTSStoppedFrame,
|
||||
)
|
||||
from pipecat.processors.frame_processor import FrameDirection
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import AudioContextTTSService, TextAggregationMode, TTSService
|
||||
from pipecat.transcriptions.language import Language, resolve_language
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
@@ -75,28 +75,9 @@ def language_to_async_language(language: Language) -> Optional[str]:
|
||||
|
||||
@dataclass
|
||||
class AsyncAITTSSettings(TTSSettings):
|
||||
"""Settings for Async AI TTS services.
|
||||
"""Settings for Async AI TTS services."""
|
||||
|
||||
Parameters:
|
||||
output_container: Audio container format (e.g. "raw").
|
||||
output_encoding: Audio encoding format (e.g. "pcm_s16le").
|
||||
output_sample_rate: Audio sample rate in Hz.
|
||||
"""
|
||||
|
||||
output_container: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
output_encoding: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
output_sample_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
@classmethod
|
||||
def from_mapping(cls, settings: Mapping[str, Any]) -> Self:
|
||||
"""Construct settings from a plain dict, destructuring legacy nested ``output_format``."""
|
||||
flat = dict(settings)
|
||||
nested = flat.pop("output_format", None)
|
||||
if isinstance(nested, dict):
|
||||
flat.setdefault("output_container", nested.get("container"))
|
||||
flat.setdefault("output_encoding", nested.get("encoding"))
|
||||
flat.setdefault("output_sample_rate", nested.get("sample_rate"))
|
||||
return super().from_mapping(flat)
|
||||
pass
|
||||
|
||||
|
||||
class AsyncAITTSService(AudioContextTTSService):
|
||||
@@ -176,9 +157,6 @@ class AsyncAITTSService(AudioContextTTSService):
|
||||
model="async_flash_v1.0",
|
||||
voice=None,
|
||||
language=None,
|
||||
output_container=container,
|
||||
output_encoding=encoding,
|
||||
output_sample_rate=0,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -215,6 +193,11 @@ class AsyncAITTSService(AudioContextTTSService):
|
||||
self._api_version = version
|
||||
self._url = url
|
||||
|
||||
# Init-only audio format config (not runtime-updatable).
|
||||
self._output_container = container
|
||||
self._output_encoding = encoding
|
||||
self._output_sample_rate = 0 # Set in start()
|
||||
|
||||
self._receive_task = None
|
||||
self._keepalive_task = None
|
||||
|
||||
@@ -262,7 +245,7 @@ class AsyncAITTSService(AudioContextTTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.output_sample_rate = self.sample_rate
|
||||
self._output_sample_rate = self.sample_rate
|
||||
await self._connect()
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
@@ -319,9 +302,9 @@ class AsyncAITTSService(AudioContextTTSService):
|
||||
"model_id": self._settings.model,
|
||||
"voice": {"mode": "id", "id": self._settings.voice},
|
||||
"output_format": {
|
||||
"container": self._settings.output_container,
|
||||
"encoding": self._settings.output_encoding,
|
||||
"sample_rate": self._settings.output_sample_rate,
|
||||
"container": self._output_container,
|
||||
"encoding": self._output_encoding,
|
||||
"sample_rate": self._output_sample_rate,
|
||||
},
|
||||
"language": self._settings.language,
|
||||
}
|
||||
@@ -567,9 +550,6 @@ class AsyncAIHttpTTSService(TTSService):
|
||||
model="async_flash_v1.0",
|
||||
voice=None,
|
||||
language=None,
|
||||
output_container=container,
|
||||
output_encoding=encoding,
|
||||
output_sample_rate=0,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -602,6 +582,11 @@ class AsyncAIHttpTTSService(TTSService):
|
||||
self._base_url = url
|
||||
self._api_version = version
|
||||
|
||||
# Init-only audio format config (not runtime-updatable).
|
||||
self._output_container = container
|
||||
self._output_encoding = encoding
|
||||
self._output_sample_rate = 0 # Set in start()
|
||||
|
||||
self._session = aiohttp_session
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
@@ -630,7 +615,7 @@ class AsyncAIHttpTTSService(TTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.output_sample_rate = self.sample_rate
|
||||
self._output_sample_rate = self.sample_rate
|
||||
|
||||
@traced_tts
|
||||
async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]:
|
||||
@@ -653,9 +638,9 @@ class AsyncAIHttpTTSService(TTSService):
|
||||
"transcript": text,
|
||||
"voice": voice_config,
|
||||
"output_format": {
|
||||
"container": self._settings.output_container,
|
||||
"encoding": self._settings.output_encoding,
|
||||
"sample_rate": self._settings.output_sample_rate,
|
||||
"container": self._output_container,
|
||||
"encoding": self._output_encoding,
|
||||
"sample_rate": self._output_sample_rate,
|
||||
},
|
||||
"language": self._settings.language,
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ class AzureTTSSettings(TTSSettings):
|
||||
|
||||
Parameters:
|
||||
emphasis: Emphasis level for speech ("strong", "moderate", "reduced").
|
||||
language: Language for synthesis. Defaults to English (US).
|
||||
pitch: Voice pitch adjustment (e.g., "+10%", "-5Hz", "high").
|
||||
rate: Speech rate adjustment (e.g., "1.0", "1.25", "slow", "fast").
|
||||
role: Voice role for expression (e.g., "YoungAdultFemale").
|
||||
@@ -83,7 +82,6 @@ class AzureTTSSettings(TTSSettings):
|
||||
"""
|
||||
|
||||
emphasis: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
language: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pitch: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
rate: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
role: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
@@ -143,7 +141,6 @@ class AzureBaseTTSService:
|
||||
*,
|
||||
api_key: str,
|
||||
region: str,
|
||||
voice: str = "en-US-SaraNeural",
|
||||
):
|
||||
"""Initialize Azure-specific configuration.
|
||||
|
||||
@@ -152,7 +149,6 @@ class AzureBaseTTSService:
|
||||
Args:
|
||||
api_key: Azure Cognitive Services subscription key.
|
||||
region: Azure region identifier (e.g., "eastus", "westus2").
|
||||
voice: Voice name to use for synthesis. Defaults to "en-US-SaraNeural".
|
||||
"""
|
||||
self._api_key = api_key
|
||||
self._region = region
|
||||
@@ -343,7 +339,7 @@ class AzureTTSService(TTSService, AzureBaseTTSService):
|
||||
)
|
||||
|
||||
# Initialize Azure-specific functionality from mixin
|
||||
self._init_azure_base(api_key=api_key, region=region, voice=default_settings.voice)
|
||||
self._init_azure_base(api_key=api_key, region=region)
|
||||
|
||||
self._speech_config = None
|
||||
self._speech_synthesizer = None
|
||||
@@ -842,7 +838,7 @@ class AzureHttpTTSService(TTSService, AzureBaseTTSService):
|
||||
)
|
||||
|
||||
# Initialize Azure-specific functionality from mixin
|
||||
self._init_azure_base(api_key=api_key, region=region, voice=default_settings.voice)
|
||||
self._init_azure_base(api_key=api_key, region=region)
|
||||
|
||||
self._speech_config = None
|
||||
self._speech_synthesizer = None
|
||||
|
||||
@@ -14,7 +14,7 @@ streaming audio output.
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, AsyncGenerator, Optional
|
||||
|
||||
from loguru import logger
|
||||
@@ -33,20 +33,16 @@ from pipecat.frames.frames import (
|
||||
)
|
||||
from pipecat.processors.frame_processor import FrameDirection
|
||||
from pipecat.services.aws.sagemaker.bidi_client import SageMakerBidiClient
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import TTSService
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
|
||||
|
||||
@dataclass
|
||||
class DeepgramSageMakerTTSSettings(TTSSettings):
|
||||
"""Settings for Deepgram SageMaker TTS service.
|
||||
"""Settings for Deepgram SageMaker TTS service."""
|
||||
|
||||
Parameters:
|
||||
encoding: Audio encoding format (e.g. "linear16").
|
||||
"""
|
||||
|
||||
encoding: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pass
|
||||
|
||||
|
||||
class DeepgramSageMakerTTSService(TTSService):
|
||||
@@ -107,10 +103,9 @@ class DeepgramSageMakerTTSService(TTSService):
|
||||
voice = voice or "aura-2-helena-en"
|
||||
|
||||
default_settings = DeepgramSageMakerTTSSettings(
|
||||
model=voice,
|
||||
model=None,
|
||||
voice=voice,
|
||||
language=None,
|
||||
encoding=encoding,
|
||||
)
|
||||
if settings is not None:
|
||||
default_settings.apply_update(settings)
|
||||
@@ -126,6 +121,7 @@ class DeepgramSageMakerTTSService(TTSService):
|
||||
|
||||
self._endpoint_name = endpoint_name
|
||||
self._region = region
|
||||
self._encoding = encoding
|
||||
|
||||
self._client: Optional[SageMakerBidiClient] = None
|
||||
self._response_task: Optional[asyncio.Task] = None
|
||||
|
||||
@@ -11,7 +11,7 @@ for generating speech from text using various voice models.
|
||||
"""
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, AsyncGenerator, Optional
|
||||
|
||||
import aiohttp
|
||||
@@ -30,7 +30,7 @@ from pipecat.frames.frames import (
|
||||
TTSStoppedFrame,
|
||||
)
|
||||
from pipecat.processors.frame_processor import FrameDirection
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import TTSService, WebsocketTTSService
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
|
||||
@@ -47,13 +47,9 @@ except ModuleNotFoundError as e:
|
||||
|
||||
@dataclass
|
||||
class DeepgramTTSSettings(TTSSettings):
|
||||
"""Settings for Deepgram TTS service.
|
||||
"""Settings for Deepgram TTS service."""
|
||||
|
||||
Parameters:
|
||||
encoding: Audio encoding format (linear16, mulaw, alaw).
|
||||
"""
|
||||
|
||||
encoding: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pass
|
||||
|
||||
|
||||
class DeepgramTTSService(WebsocketTTSService):
|
||||
@@ -105,10 +101,9 @@ class DeepgramTTSService(WebsocketTTSService):
|
||||
|
||||
# 1. Initialize default_settings with hardcoded defaults
|
||||
default_settings = DeepgramTTSSettings(
|
||||
model="aura-2-helena-en",
|
||||
model=None,
|
||||
voice="aura-2-helena-en",
|
||||
language=None,
|
||||
encoding=encoding,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -132,6 +127,7 @@ class DeepgramTTSService(WebsocketTTSService):
|
||||
|
||||
self._api_key = api_key
|
||||
self._base_url = base_url
|
||||
self._encoding = encoding
|
||||
|
||||
self._receive_task = None
|
||||
self._context_id: Optional[str] = None
|
||||
@@ -236,7 +232,7 @@ class DeepgramTTSService(WebsocketTTSService):
|
||||
# Build WebSocket URL with query parameters
|
||||
params = []
|
||||
params.append(f"model={self._settings.voice}")
|
||||
params.append(f"encoding={self._settings.encoding}")
|
||||
params.append(f"encoding={self._encoding}")
|
||||
params.append(f"sample_rate={self.sample_rate}")
|
||||
|
||||
url = f"{self._base_url}/v1/speak?{'&'.join(params)}"
|
||||
@@ -422,10 +418,9 @@ class DeepgramHttpTTSService(TTSService):
|
||||
"""
|
||||
# 1. Initialize default_settings with hardcoded defaults
|
||||
default_settings = DeepgramTTSSettings(
|
||||
model="aura-2-helena-en",
|
||||
model=None,
|
||||
voice="aura-2-helena-en",
|
||||
language=None,
|
||||
encoding=encoding,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -447,6 +442,7 @@ class DeepgramHttpTTSService(TTSService):
|
||||
self._api_key = api_key
|
||||
self._session = aiohttp_session
|
||||
self._base_url = base_url
|
||||
self._encoding = encoding
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if the service can generate metrics.
|
||||
@@ -476,7 +472,7 @@ class DeepgramHttpTTSService(TTSService):
|
||||
|
||||
params = {
|
||||
"model": self._settings.voice,
|
||||
"encoding": self._settings.encoding,
|
||||
"encoding": self._encoding,
|
||||
"sample_rate": self.sample_rate,
|
||||
"container": "none",
|
||||
}
|
||||
|
||||
@@ -201,9 +201,6 @@ class ElevenLabsTTSSettings(TTSSettings):
|
||||
style: Style control for voice expression (0.0 to 1.0).
|
||||
use_speaker_boost: Whether to use speaker boost enhancement.
|
||||
speed: Voice speed control (0.7 to 1.2).
|
||||
auto_mode: Whether to enable automatic mode optimization.
|
||||
enable_ssml_parsing: Whether to parse SSML tags in text.
|
||||
enable_logging: Whether to enable ElevenLabs logging.
|
||||
apply_text_normalization: Text normalization mode ("auto", "on", "off").
|
||||
"""
|
||||
|
||||
@@ -212,9 +209,6 @@ class ElevenLabsTTSSettings(TTSSettings):
|
||||
style: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
use_speaker_boost: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
speed: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
auto_mode: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
enable_ssml_parsing: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
enable_logging: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
apply_text_normalization: Literal["auto", "on", "off"] | None | _NotGiven = field(
|
||||
default_factory=lambda: NOT_GIVEN
|
||||
)
|
||||
@@ -228,8 +222,6 @@ class ElevenLabsTTSSettings(TTSSettings):
|
||||
{"stability", "similarity_boost", "style", "use_speaker_boost", "speed"}
|
||||
)
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {"voice_id": "voice"}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ElevenLabsHttpTTSSettings(TTSSettings):
|
||||
@@ -255,8 +247,6 @@ class ElevenLabsHttpTTSSettings(TTSSettings):
|
||||
default_factory=lambda: NOT_GIVEN
|
||||
)
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {"voice_id": "voice"}
|
||||
|
||||
|
||||
def calculate_word_times(
|
||||
alignment_info: Mapping[str, Any],
|
||||
@@ -430,6 +420,11 @@ class ElevenLabsTTSService(AudioContextTTSService):
|
||||
model="eleven_turbo_v2_5",
|
||||
)
|
||||
|
||||
# Track init-only URL params through the override chain
|
||||
_auto_mode = True
|
||||
_enable_ssml_parsing = None
|
||||
_enable_logging = None
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
if voice_id is not None:
|
||||
_warn_deprecated_param("voice_id", ElevenLabsTTSSettings, "voice")
|
||||
@@ -456,11 +451,11 @@ class ElevenLabsTTSService(AudioContextTTSService):
|
||||
if params.speed is not None:
|
||||
default_settings.speed = params.speed
|
||||
if params.auto_mode is not None:
|
||||
default_settings.auto_mode = str(params.auto_mode).lower()
|
||||
_auto_mode = str(params.auto_mode).lower()
|
||||
if params.enable_ssml_parsing is not None:
|
||||
default_settings.enable_ssml_parsing = params.enable_ssml_parsing
|
||||
_enable_ssml_parsing = params.enable_ssml_parsing
|
||||
if params.enable_logging is not None:
|
||||
default_settings.enable_logging = params.enable_logging
|
||||
_enable_logging = params.enable_logging
|
||||
if params.apply_text_normalization is not None:
|
||||
default_settings.apply_text_normalization = params.apply_text_normalization
|
||||
if _pronunciation_dictionary_locators is None:
|
||||
@@ -485,6 +480,11 @@ class ElevenLabsTTSService(AudioContextTTSService):
|
||||
self._api_key = api_key
|
||||
self._url = url
|
||||
|
||||
# Init-only WebSocket URL params (not runtime-updatable).
|
||||
self._auto_mode = _auto_mode
|
||||
self._enable_ssml_parsing = _enable_ssml_parsing
|
||||
self._enable_logging = _enable_logging
|
||||
|
||||
self._output_format = "" # initialized in start()
|
||||
self._voice_settings = self._set_voice_settings()
|
||||
self._pronunciation_dictionary_locators = _pronunciation_dictionary_locators
|
||||
@@ -518,20 +518,7 @@ class ElevenLabsTTSService(AudioContextTTSService):
|
||||
return language_to_elevenlabs_language(language)
|
||||
|
||||
def _set_voice_settings(self):
|
||||
ts = self._settings
|
||||
voice_setting_keys = [
|
||||
"stability",
|
||||
"similarity_boost",
|
||||
"style",
|
||||
"use_speaker_boost",
|
||||
"speed",
|
||||
]
|
||||
voice_settings = {}
|
||||
for key in voice_setting_keys:
|
||||
val = getattr(ts, key, None)
|
||||
if val is not None:
|
||||
voice_settings[key] = val
|
||||
return voice_settings or None
|
||||
return build_elevenlabs_voice_settings(self._settings)
|
||||
|
||||
async def _update_settings(self, delta: TTSSettings) -> dict[str, Any]:
|
||||
"""Apply a settings delta, reconnecting as needed.
|
||||
@@ -670,13 +657,13 @@ class ElevenLabsTTSService(AudioContextTTSService):
|
||||
voice_id = self._settings.voice
|
||||
model = self._settings.model
|
||||
output_format = self._output_format
|
||||
url = f"{self._url}/v1/text-to-speech/{voice_id}/multi-stream-input?model_id={model}&output_format={output_format}&auto_mode={self._settings.auto_mode}"
|
||||
url = f"{self._url}/v1/text-to-speech/{voice_id}/multi-stream-input?model_id={model}&output_format={output_format}&auto_mode={self._auto_mode}"
|
||||
|
||||
if self._settings.enable_ssml_parsing:
|
||||
url += f"&enable_ssml_parsing={self._settings.enable_ssml_parsing}"
|
||||
if self._enable_ssml_parsing:
|
||||
url += f"&enable_ssml_parsing={self._enable_ssml_parsing}"
|
||||
|
||||
if self._settings.enable_logging:
|
||||
url += f"&enable_logging={self._settings.enable_logging}"
|
||||
if self._enable_logging:
|
||||
url += f"&enable_logging={self._enable_logging}"
|
||||
|
||||
if self._settings.apply_text_normalization is not None:
|
||||
url += f"&apply_text_normalization={self._settings.apply_text_normalization}"
|
||||
|
||||
@@ -52,25 +52,19 @@ class FishAudioTTSSettings(TTSSettings):
|
||||
"""Settings for Fish Audio TTS service.
|
||||
|
||||
Parameters:
|
||||
fish_sample_rate: Audio sample rate sent to the API.
|
||||
latency: Latency mode ("normal" or "balanced"). Defaults to "normal".
|
||||
format: Audio output format.
|
||||
normalize: Whether to normalize audio output. Defaults to True.
|
||||
prosody_speed: Speech speed multiplier (0.5-2.0). Defaults to 1.0.
|
||||
prosody_volume: Volume adjustment in dB. Defaults to 0.
|
||||
reference_id: Reference ID of the voice model.
|
||||
"""
|
||||
|
||||
fish_sample_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
latency: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
format: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
normalize: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
prosody_speed: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
prosody_volume: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
reference_id: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {"voice_id": "voice", "sample_rate": "fish_sample_rate"}
|
||||
|
||||
@classmethod
|
||||
def from_mapping(cls, settings: Mapping[str, Any]) -> Self:
|
||||
"""Construct settings from a plain dict, destructuring legacy nested ``prosody``."""
|
||||
@@ -179,9 +173,7 @@ class FishAudioTTSService(InterruptibleTTSService):
|
||||
default_settings = FishAudioTTSSettings(
|
||||
model="s1",
|
||||
voice=None,
|
||||
fish_sample_rate=0,
|
||||
latency="normal",
|
||||
format=output_format,
|
||||
normalize=True,
|
||||
prosody_speed=1.0,
|
||||
prosody_volume=0,
|
||||
@@ -228,6 +220,10 @@ class FishAudioTTSService(InterruptibleTTSService):
|
||||
self._receive_task = None
|
||||
self._request_id = None
|
||||
|
||||
# Init-only audio format config (not runtime-updatable).
|
||||
self._fish_sample_rate = 0 # Set in start()
|
||||
self._output_format = output_format
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if this service can generate processing metrics.
|
||||
|
||||
@@ -262,7 +258,7 @@ class FishAudioTTSService(InterruptibleTTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.fish_sample_rate = self.sample_rate
|
||||
self._fish_sample_rate = self.sample_rate
|
||||
await self._connect()
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
@@ -312,9 +308,9 @@ class FishAudioTTSService(InterruptibleTTSService):
|
||||
|
||||
# Send initial start message with ormsgpack
|
||||
request_settings = {
|
||||
"sample_rate": self._settings.fish_sample_rate,
|
||||
"sample_rate": self._fish_sample_rate,
|
||||
"latency": self._settings.latency,
|
||||
"format": self._settings.format,
|
||||
"format": self._output_format,
|
||||
"normalize": self._settings.normalize,
|
||||
"prosody": {
|
||||
"speed": self._settings.prosody_speed,
|
||||
|
||||
@@ -494,7 +494,6 @@ class GoogleHttpTTSSettings(TTSSettings):
|
||||
Range [0.25, 2.0].
|
||||
volume: Volume adjustment (e.g., "loud", "soft", "+6dB").
|
||||
emphasis: Emphasis level for the text.
|
||||
language: Language for synthesis. Defaults to English.
|
||||
gender: Voice gender preference.
|
||||
google_style: Google-specific voice style.
|
||||
"""
|
||||
@@ -506,7 +505,6 @@ class GoogleHttpTTSSettings(TTSSettings):
|
||||
emphasis: Literal["strong", "moderate", "reduced", "none"] | None | _NotGiven = field(
|
||||
default_factory=lambda: NOT_GIVEN
|
||||
)
|
||||
language: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
gender: Literal["male", "female", "neutral"] | None | _NotGiven = field(
|
||||
default_factory=lambda: NOT_GIVEN
|
||||
)
|
||||
@@ -520,11 +518,9 @@ class GoogleStreamTTSSettings(TTSSettings):
|
||||
"""Settings for Google streaming TTS service.
|
||||
|
||||
Parameters:
|
||||
language: Language for synthesis. Defaults to English.
|
||||
speaking_rate: The speaking rate, in the range [0.25, 2.0].
|
||||
"""
|
||||
|
||||
language: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
speaking_rate: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
|
||||
@@ -533,13 +529,11 @@ class GeminiTTSSettings(TTSSettings):
|
||||
"""Settings for Gemini TTS service.
|
||||
|
||||
Parameters:
|
||||
language: Language for synthesis. Defaults to English.
|
||||
prompt: Optional style instructions for how to synthesize the content.
|
||||
multi_speaker: Whether to enable multi-speaker support.
|
||||
speaker_configs: List of speaker configurations for multi-speaker mode.
|
||||
"""
|
||||
|
||||
language: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
prompt: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
multi_speaker: bool | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
speaker_configs: list[dict[str, Any]] | None | _NotGiven = field(
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import base64
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, AsyncGenerator, Optional
|
||||
|
||||
from loguru import logger
|
||||
@@ -22,7 +22,7 @@ from pipecat.frames.frames import (
|
||||
TTSStartedFrame,
|
||||
TTSStoppedFrame,
|
||||
)
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import AudioContextTTSService
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
|
||||
@@ -40,13 +40,9 @@ SAMPLE_RATE = 48000
|
||||
|
||||
@dataclass
|
||||
class GradiumTTSSettings(TTSSettings):
|
||||
"""Settings for the Gradium TTS service.
|
||||
"""Settings for the Gradium TTS service."""
|
||||
|
||||
Parameters:
|
||||
output_format: Audio output format.
|
||||
"""
|
||||
|
||||
output_format: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pass
|
||||
|
||||
|
||||
class GradiumTTSService(AudioContextTTSService):
|
||||
@@ -108,7 +104,6 @@ class GradiumTTSService(AudioContextTTSService):
|
||||
model="default",
|
||||
voice="YTpq7expH9539ERJ",
|
||||
language=None,
|
||||
output_format="pcm",
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import io
|
||||
import wave
|
||||
from dataclasses import dataclass, field
|
||||
from typing import AsyncGenerator, ClassVar, Dict, Optional
|
||||
from typing import AsyncGenerator, Optional
|
||||
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel
|
||||
@@ -39,16 +39,10 @@ class GroqTTSSettings(TTSSettings):
|
||||
"""Settings for the Groq TTS service.
|
||||
|
||||
Parameters:
|
||||
output_format: Audio output format.
|
||||
speed: Speech speed multiplier. Defaults to 1.0.
|
||||
groq_sample_rate: Audio sample rate.
|
||||
"""
|
||||
|
||||
output_format: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
speed: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
groq_sample_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {"voice_id": "voice", "sample_rate": "groq_sample_rate"}
|
||||
|
||||
|
||||
class GroqTTSService(TTSService):
|
||||
@@ -122,9 +116,7 @@ class GroqTTSService(TTSService):
|
||||
model="canopylabs/orpheus-v1-english",
|
||||
voice="autumn",
|
||||
language="en",
|
||||
output_format=output_format,
|
||||
speed=1.0,
|
||||
groq_sample_rate=sample_rate,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
|
||||
@@ -71,8 +71,6 @@ class InworldTTSSettings(TTSSettings):
|
||||
"""Settings for Inworld TTS services.
|
||||
|
||||
Parameters:
|
||||
audio_encoding: Audio encoding format (e.g. LINEAR16).
|
||||
audio_sample_rate: Audio sample rate in Hz.
|
||||
speaking_rate: Speaking rate for speech synthesis.
|
||||
temperature: Temperature for speech synthesis.
|
||||
auto_mode: Whether to use auto mode. Recommended when texts are sent
|
||||
@@ -84,8 +82,6 @@ class InworldTTSSettings(TTSSettings):
|
||||
timestamp_transport_strategy: Strategy for timestamp transport ("ASYNC" or "SYNC").
|
||||
"""
|
||||
|
||||
audio_encoding: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
audio_sample_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
speaking_rate: float | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
temperature: float | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
auto_mode: bool | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
@@ -93,7 +89,6 @@ class InworldTTSSettings(TTSSettings):
|
||||
timestamp_transport_strategy: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {
|
||||
"voice_id": "voice",
|
||||
"voiceId": "voice",
|
||||
"modelId": "model",
|
||||
"applyTextNormalization": "apply_text_normalization",
|
||||
@@ -107,8 +102,6 @@ class InworldTTSSettings(TTSSettings):
|
||||
flat = dict(settings)
|
||||
nested = flat.pop("audioConfig", None)
|
||||
if isinstance(nested, dict):
|
||||
flat.setdefault("audio_encoding", nested.get("audioEncoding"))
|
||||
flat.setdefault("audio_sample_rate", nested.get("sampleRateHertz"))
|
||||
flat.setdefault("speaking_rate", nested.get("speakingRate"))
|
||||
return super().from_mapping(flat)
|
||||
|
||||
@@ -184,8 +177,6 @@ class InworldHttpTTSService(TTSService):
|
||||
model="inworld-tts-1.5-max",
|
||||
voice="Ashley",
|
||||
language=None,
|
||||
audio_encoding=encoding,
|
||||
audio_sample_rate=0,
|
||||
speaking_rate=None,
|
||||
temperature=None,
|
||||
timestamp_transport_strategy="ASYNC",
|
||||
@@ -239,6 +230,10 @@ class InworldHttpTTSService(TTSService):
|
||||
|
||||
self._cumulative_time = 0.0
|
||||
|
||||
# Init-only audio format config (not runtime-updatable).
|
||||
self._audio_encoding = encoding
|
||||
self._audio_sample_rate = 0 # Set in start()
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if this service can generate processing metrics.
|
||||
|
||||
@@ -254,7 +249,7 @@ class InworldHttpTTSService(TTSService):
|
||||
frame: The start frame.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.audio_sample_rate = self.sample_rate
|
||||
self._audio_sample_rate = self.sample_rate
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
"""Stop the Inworld TTS service.
|
||||
@@ -334,8 +329,8 @@ class InworldHttpTTSService(TTSService):
|
||||
logger.debug(f"{self}: Generating TTS [{text}] (streaming={self._streaming})")
|
||||
|
||||
audio_config = {
|
||||
"audioEncoding": self._settings.audio_encoding,
|
||||
"sampleRateHertz": self._settings.audio_sample_rate,
|
||||
"audioEncoding": self._audio_encoding,
|
||||
"sampleRateHertz": self._audio_sample_rate,
|
||||
}
|
||||
if self._settings.speaking_rate is not None:
|
||||
audio_config["speakingRate"] = self._settings.speaking_rate
|
||||
@@ -611,8 +606,6 @@ class InworldTTSService(AudioContextTTSService):
|
||||
model="inworld-tts-1.5-max",
|
||||
voice="Ashley",
|
||||
language=None,
|
||||
audio_encoding=encoding,
|
||||
audio_sample_rate=0,
|
||||
speaking_rate=None,
|
||||
temperature=None,
|
||||
apply_text_normalization=None,
|
||||
@@ -686,6 +679,10 @@ class InworldTTSService(AudioContextTTSService):
|
||||
# Track the end time of the last word in the current generation
|
||||
self._generation_end_time = 0.0
|
||||
|
||||
# Init-only audio format config (not runtime-updatable).
|
||||
self._audio_encoding = encoding
|
||||
self._audio_sample_rate = 0 # Set in start()
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if this service can generate processing metrics.
|
||||
|
||||
@@ -701,7 +698,7 @@ class InworldTTSService(AudioContextTTSService):
|
||||
frame: The start frame.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.audio_sample_rate = self.sample_rate
|
||||
self._audio_sample_rate = self.sample_rate
|
||||
await self._connect()
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
@@ -1051,8 +1048,8 @@ class InworldTTSService(AudioContextTTSService):
|
||||
context_id: The context ID.
|
||||
"""
|
||||
audio_config = {
|
||||
"audioEncoding": self._settings.audio_encoding,
|
||||
"sampleRateHertz": self._settings.audio_sample_rate,
|
||||
"audioEncoding": self._audio_encoding,
|
||||
"sampleRateHertz": self._audio_sample_rate,
|
||||
}
|
||||
if self._settings.speaking_rate is not None:
|
||||
audio_config["speakingRate"] = self._settings.speaking_rate
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"""Kokoro TTS service implementation using kokoro-onnx."""
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import AsyncGenerator, Optional
|
||||
|
||||
@@ -23,7 +23,7 @@ from pipecat.frames.frames import (
|
||||
TTSStartedFrame,
|
||||
TTSStoppedFrame,
|
||||
)
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import TTSService
|
||||
from pipecat.transcriptions.language import Language, resolve_language
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
@@ -91,13 +91,9 @@ def language_to_kokoro_language(language: Language) -> str:
|
||||
|
||||
@dataclass
|
||||
class KokoroTTSSettings(TTSSettings):
|
||||
"""Settings for the Kokoro TTS service.
|
||||
"""Settings for the Kokoro TTS service."""
|
||||
|
||||
Parameters:
|
||||
lang_code: Kokoro language code for synthesis.
|
||||
"""
|
||||
|
||||
lang_code: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pass
|
||||
|
||||
|
||||
class KokoroTTSService(TTSService):
|
||||
@@ -156,7 +152,6 @@ class KokoroTTSService(TTSService):
|
||||
model=None,
|
||||
voice=None,
|
||||
language=language_to_kokoro_language(Language.EN),
|
||||
lang_code=language_to_kokoro_language(Language.EN),
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -168,9 +163,7 @@ class KokoroTTSService(TTSService):
|
||||
if params is not None:
|
||||
_warn_deprecated_param("params", KokoroTTSSettings)
|
||||
if not settings:
|
||||
lang_code = language_to_kokoro_language(params.language)
|
||||
default_settings.language = lang_code
|
||||
default_settings.lang_code = lang_code
|
||||
default_settings.language = language_to_kokoro_language(params.language)
|
||||
|
||||
# 4. Apply settings delta (canonical API, always wins)
|
||||
if settings is not None:
|
||||
@@ -181,8 +174,6 @@ class KokoroTTSService(TTSService):
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self._lang_code = default_settings.lang_code
|
||||
|
||||
model_file = Path(model_path) if model_path else KOKORO_CACHE_DIR / "kokoro-v1.0.onnx"
|
||||
voices = Path(voices_path) if voices_path else KOKORO_CACHE_DIR / "voices-v1.0.bin"
|
||||
|
||||
@@ -196,6 +187,17 @@ class KokoroTTSService(TTSService):
|
||||
"""Indicate that this service supports TTFB and usage metrics."""
|
||||
return True
|
||||
|
||||
def language_to_service_language(self, language: Language) -> str:
|
||||
"""Convert a Language enum to kokoro-onnx language format.
|
||||
|
||||
Args:
|
||||
language: The language to convert.
|
||||
|
||||
Returns:
|
||||
The kokoro-onnx language code.
|
||||
"""
|
||||
return language_to_kokoro_language(language)
|
||||
|
||||
@traced_tts
|
||||
async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]:
|
||||
"""Synthesize speech from text using kokoro-onnx.
|
||||
@@ -215,7 +217,7 @@ class KokoroTTSService(TTSService):
|
||||
yield TTSStartedFrame(context_id=context_id)
|
||||
|
||||
stream = self._kokoro.create_stream(
|
||||
text, voice=self._settings.voice, lang=self._lang_code, speed=1.0
|
||||
text, voice=self._settings.voice, lang=self._settings.language, speed=1.0
|
||||
)
|
||||
|
||||
async for samples, sample_rate in stream:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"""LMNT text-to-speech service implementation."""
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, AsyncGenerator, Optional
|
||||
|
||||
from loguru import logger
|
||||
@@ -17,14 +17,13 @@ from pipecat.frames.frames import (
|
||||
EndFrame,
|
||||
ErrorFrame,
|
||||
Frame,
|
||||
InterruptionFrame,
|
||||
StartFrame,
|
||||
TTSAudioRawFrame,
|
||||
TTSStartedFrame,
|
||||
TTSStoppedFrame,
|
||||
)
|
||||
from pipecat.processors.frame_processor import FrameDirection
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import InterruptibleTTSService
|
||||
from pipecat.transcriptions.language import Language, resolve_language
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
@@ -75,13 +74,9 @@ def language_to_lmnt_language(language: Language) -> Optional[str]:
|
||||
|
||||
@dataclass
|
||||
class LmntTTSSettings(TTSSettings):
|
||||
"""Settings for LMNT TTS service.
|
||||
"""Settings for LMNT TTS service."""
|
||||
|
||||
Parameters:
|
||||
format: Audio output format. Defaults to "raw".
|
||||
"""
|
||||
|
||||
format: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pass
|
||||
|
||||
|
||||
class LmntTTSService(InterruptibleTTSService):
|
||||
@@ -130,7 +125,6 @@ class LmntTTSService(InterruptibleTTSService):
|
||||
model="blizzard",
|
||||
voice=None,
|
||||
language=self.language_to_service_language(language),
|
||||
format="raw",
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -156,6 +150,7 @@ class LmntTTSService(InterruptibleTTSService):
|
||||
)
|
||||
|
||||
self._api_key = api_key
|
||||
self._output_format = "raw"
|
||||
self._receive_task = None
|
||||
self._context_id: Optional[str] = None
|
||||
|
||||
@@ -262,7 +257,7 @@ class LmntTTSService(InterruptibleTTSService):
|
||||
init_msg = {
|
||||
"X-API-Key": self._api_key,
|
||||
"voice": self._settings.voice,
|
||||
"format": self._settings.format,
|
||||
"format": self._output_format,
|
||||
"sample_rate": self.sample_rate,
|
||||
"language": self._settings.language,
|
||||
"model": self._settings.model,
|
||||
|
||||
@@ -12,7 +12,7 @@ for streaming text-to-speech synthesis.
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, AsyncGenerator, ClassVar, Dict, Mapping, Optional, Self
|
||||
from typing import Any, AsyncGenerator, Mapping, Optional, Self
|
||||
|
||||
import aiohttp
|
||||
from loguru import logger
|
||||
@@ -100,10 +100,6 @@ class MiniMaxTTSSettings(TTSSettings):
|
||||
"disgusted", "surprised", "calm", "fluent").
|
||||
text_normalization: Enable text normalization (Chinese/English).
|
||||
latex_read: Enable LaTeX formula reading.
|
||||
audio_bitrate: Audio bitrate in bps.
|
||||
audio_format: Audio output format.
|
||||
audio_channel: Number of audio channels.
|
||||
audio_sample_rate: Audio sample rate in Hz.
|
||||
language_boost: Language boost string for multilingual support.
|
||||
"""
|
||||
|
||||
@@ -114,14 +110,8 @@ class MiniMaxTTSSettings(TTSSettings):
|
||||
emotion: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
text_normalization: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
latex_read: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
audio_bitrate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
audio_format: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
audio_channel: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
audio_sample_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
language_boost: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {"voice_id": "voice"}
|
||||
|
||||
@classmethod
|
||||
def from_mapping(cls, settings: Mapping[str, Any]) -> Self:
|
||||
"""Construct settings from a plain dict, destructuring legacy nested dicts.
|
||||
@@ -140,13 +130,6 @@ class MiniMaxTTSSettings(TTSSettings):
|
||||
flat.setdefault("text_normalization", voice.get("text_normalization"))
|
||||
flat.setdefault("latex_read", voice.get("latex_read"))
|
||||
|
||||
audio = flat.pop("audio_setting", None)
|
||||
if isinstance(audio, dict):
|
||||
flat.setdefault("audio_bitrate", audio.get("bitrate"))
|
||||
flat.setdefault("audio_format", audio.get("format"))
|
||||
flat.setdefault("audio_channel", audio.get("channel"))
|
||||
flat.setdefault("audio_sample_rate", audio.get("sample_rate"))
|
||||
|
||||
return super().from_mapping(flat)
|
||||
|
||||
|
||||
@@ -258,10 +241,6 @@ class MiniMaxHttpTTSService(TTSService):
|
||||
emotion=None,
|
||||
text_normalization=None,
|
||||
latex_read=None,
|
||||
audio_bitrate=128000,
|
||||
audio_format="pcm",
|
||||
audio_channel=1,
|
||||
audio_sample_rate=0,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -335,6 +314,12 @@ class MiniMaxHttpTTSService(TTSService):
|
||||
self._base_url = f"{base_url}?GroupId={group_id}"
|
||||
self._session = aiohttp_session
|
||||
|
||||
# Init-only audio format config
|
||||
self._audio_bitrate = 128000
|
||||
self._audio_format = "pcm"
|
||||
self._audio_channel = 1
|
||||
self._audio_sample_rate = 0 # Set in start()
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if this service can generate processing metrics.
|
||||
|
||||
@@ -361,7 +346,7 @@ class MiniMaxHttpTTSService(TTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.audio_sample_rate = self.sample_rate
|
||||
self._audio_sample_rate = self.sample_rate
|
||||
logger.debug(f"MiniMax TTS initialized with sample_rate: {self.sample_rate}")
|
||||
|
||||
@traced_tts
|
||||
@@ -399,10 +384,10 @@ class MiniMaxHttpTTSService(TTSService):
|
||||
|
||||
# Build audio_setting dict for API
|
||||
audio_setting = {
|
||||
"bitrate": self._settings.audio_bitrate,
|
||||
"format": self._settings.audio_format,
|
||||
"channel": self._settings.audio_channel,
|
||||
"sample_rate": self._settings.audio_sample_rate,
|
||||
"bitrate": self._audio_bitrate,
|
||||
"format": self._audio_format,
|
||||
"channel": self._audio_channel,
|
||||
"sample_rate": self._audio_sample_rate,
|
||||
}
|
||||
|
||||
# Create payload from settings
|
||||
|
||||
@@ -80,13 +80,9 @@ class NeuphonicTTSSettings(TTSSettings):
|
||||
|
||||
Parameters:
|
||||
speed: Speech speed multiplier. Defaults to 1.0.
|
||||
encoding: Audio encoding format.
|
||||
sampling_rate: Audio sample rate.
|
||||
"""
|
||||
|
||||
speed: float | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
encoding: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
sampling_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
|
||||
class NeuphonicTTSService(InterruptibleTTSService):
|
||||
@@ -160,8 +156,6 @@ class NeuphonicTTSService(InterruptibleTTSService):
|
||||
voice=None,
|
||||
language=self.language_to_service_language(Language.EN),
|
||||
speed=1.0,
|
||||
encoding=encoding,
|
||||
sampling_rate=sample_rate,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -200,6 +194,8 @@ class NeuphonicTTSService(InterruptibleTTSService):
|
||||
self._receive_task = None
|
||||
self._keepalive_task = None
|
||||
self._context_id: Optional[str] = None
|
||||
self._encoding = encoding
|
||||
self._sampling_rate = sample_rate
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if this service can generate processing metrics.
|
||||
@@ -327,8 +323,8 @@ class NeuphonicTTSService(InterruptibleTTSService):
|
||||
tts_config = {
|
||||
"lang_code": self._settings.language,
|
||||
"speed": self._settings.speed,
|
||||
"encoding": self._settings.encoding,
|
||||
"sampling_rate": self._settings.sampling_rate,
|
||||
"encoding": self._encoding,
|
||||
"sampling_rate": self._sampling_rate,
|
||||
"voice_id": self._settings.voice,
|
||||
}
|
||||
|
||||
@@ -503,8 +499,6 @@ class NeuphonicHttpTTSService(TTSService):
|
||||
voice=None,
|
||||
language=self.language_to_service_language(Language.EN) or "en",
|
||||
speed=1.0,
|
||||
encoding=encoding,
|
||||
sampling_rate=sample_rate,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -536,6 +530,7 @@ class NeuphonicHttpTTSService(TTSService):
|
||||
self._api_key = api_key
|
||||
self._session = aiohttp_session
|
||||
self._base_url = url.rstrip("/")
|
||||
self._encoding = encoding
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if this service can generate processing metrics.
|
||||
@@ -629,7 +624,7 @@ class NeuphonicHttpTTSService(TTSService):
|
||||
payload = {
|
||||
"text": text,
|
||||
"lang_code": self._settings.language,
|
||||
"encoding": self._settings.encoding,
|
||||
"encoding": self._encoding,
|
||||
"sampling_rate": self.sample_rate,
|
||||
"speed": self._settings.speed,
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
import base64
|
||||
import json
|
||||
from dataclasses import dataclass, field
|
||||
from typing import AsyncGenerator, ClassVar, Dict, Optional
|
||||
from dataclasses import dataclass
|
||||
from typing import AsyncGenerator, Optional
|
||||
|
||||
from loguru import logger
|
||||
|
||||
@@ -23,7 +23,7 @@ from pipecat.frames.frames import (
|
||||
TTSStartedFrame,
|
||||
TTSStoppedFrame,
|
||||
)
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import AudioContextTTSService
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
|
||||
@@ -38,22 +38,9 @@ except ModuleNotFoundError as e:
|
||||
|
||||
@dataclass
|
||||
class ResembleAITTSSettings(TTSSettings):
|
||||
"""Settings for Resemble AI TTS service.
|
||||
"""Settings for Resemble AI TTS service."""
|
||||
|
||||
Parameters:
|
||||
precision: PCM bit depth (PCM_32, PCM_24, PCM_16, or MULAW).
|
||||
output_format: Audio format (wav or mp3).
|
||||
resemble_sample_rate: Audio sample rate sent to the API.
|
||||
"""
|
||||
|
||||
precision: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
output_format: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
resemble_sample_rate: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {
|
||||
"voice_id": "voice",
|
||||
"sample_rate": "resemble_sample_rate",
|
||||
}
|
||||
pass
|
||||
|
||||
|
||||
class ResembleAITTSService(AudioContextTTSService):
|
||||
@@ -100,21 +87,12 @@ class ResembleAITTSService(AudioContextTTSService):
|
||||
model=None,
|
||||
voice=None,
|
||||
language=None,
|
||||
precision="PCM_16",
|
||||
output_format="wav",
|
||||
resemble_sample_rate=22050,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
if voice_id is not None:
|
||||
_warn_deprecated_param("voice_id", ResembleAITTSSettings, "voice")
|
||||
default_settings.voice = voice_id
|
||||
if precision is not None:
|
||||
default_settings.precision = precision
|
||||
if output_format is not None:
|
||||
default_settings.output_format = output_format
|
||||
if sample_rate is not None:
|
||||
default_settings.resemble_sample_rate = sample_rate
|
||||
|
||||
# 3. No params for this service
|
||||
|
||||
@@ -133,6 +111,11 @@ class ResembleAITTSService(AudioContextTTSService):
|
||||
self._api_key = api_key
|
||||
self._url = url
|
||||
|
||||
# Init-only audio format config (not runtime-updatable).
|
||||
self._precision = precision or "PCM_16"
|
||||
self._output_format = output_format or "wav"
|
||||
self._resemble_sample_rate = 0 # Set in start()
|
||||
|
||||
self._websocket = None
|
||||
self._request_id_counter = 0
|
||||
self._receive_task = None
|
||||
@@ -174,9 +157,9 @@ class ResembleAITTSService(AudioContextTTSService):
|
||||
"data": text,
|
||||
"binary_response": False, # Use JSON frames to get timestamps
|
||||
"request_id": self._request_id_counter, # ResembleAI only accepts number
|
||||
"output_format": self._settings.output_format,
|
||||
"sample_rate": self._settings.resemble_sample_rate,
|
||||
"precision": self._settings.precision,
|
||||
"output_format": self._output_format,
|
||||
"sample_rate": self._resemble_sample_rate,
|
||||
"precision": self._precision,
|
||||
"no_audio_header": True,
|
||||
}
|
||||
|
||||
@@ -190,7 +173,7 @@ class ResembleAITTSService(AudioContextTTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.resemble_sample_rate = self.sample_rate
|
||||
self._resemble_sample_rate = self.sample_rate
|
||||
await self._connect()
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
|
||||
@@ -76,8 +76,6 @@ class RimeTTSSettings(TTSSettings):
|
||||
"""Settings for Rime WS JSON and HTTP TTS services.
|
||||
|
||||
Parameters:
|
||||
audioFormat: Audio output format.
|
||||
samplingRate: Audio sample rate.
|
||||
segment: Text segmentation mode ("immediate", "bySentence", "never").
|
||||
speedAlpha: Speech speed multiplier (mistv2 only).
|
||||
reduceLatency: Whether to reduce latency at potential quality cost (mistv2 only).
|
||||
@@ -91,8 +89,6 @@ class RimeTTSSettings(TTSSettings):
|
||||
top_p: Cumulative probability threshold (arcana only, 0.0-1.0).
|
||||
"""
|
||||
|
||||
audioFormat: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
samplingRate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
segment: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
speedAlpha: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
reduceLatency: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
@@ -113,16 +109,12 @@ class RimeNonJsonTTSSettings(TTSSettings):
|
||||
"""Settings for Rime non-JSON WS TTS service.
|
||||
|
||||
Parameters:
|
||||
audioFormat: Audio output format.
|
||||
samplingRate: Audio sample rate.
|
||||
segment: Text segmentation mode ("immediate", "bySentence", "never").
|
||||
repetition_penalty: Token repetition penalty (1.0-2.0).
|
||||
temperature: Sampling temperature (0.0-1.0).
|
||||
top_p: Cumulative probability threshold (0.0-1.0).
|
||||
"""
|
||||
|
||||
audioFormat: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
samplingRate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
segment: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
repetition_penalty: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
temperature: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
@@ -230,8 +222,6 @@ class RimeTTSService(AudioContextTTSService):
|
||||
default_settings = RimeTTSSettings(
|
||||
model="arcana",
|
||||
voice=None,
|
||||
audioFormat="pcm",
|
||||
samplingRate=0, # updated in start()
|
||||
language=None,
|
||||
segment=None,
|
||||
inlineSpeedAlpha=None,
|
||||
@@ -293,6 +283,10 @@ class RimeTTSService(AudioContextTTSService):
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Init-only audio format fields (not runtime-updatable)
|
||||
self._audio_format = "pcm"
|
||||
self._sampling_rate = 0 # updated in start()
|
||||
|
||||
if not text_aggregator:
|
||||
# Always skip tags added for spelled-out text
|
||||
# Note: This is primarily to support backwards compatibility.
|
||||
@@ -342,8 +336,8 @@ class RimeTTSService(AudioContextTTSService):
|
||||
params: dict[str, Any] = {
|
||||
"speaker": self._settings.voice,
|
||||
"modelId": self._settings.model,
|
||||
"audioFormat": self._settings.audioFormat,
|
||||
"samplingRate": self._settings.samplingRate,
|
||||
"audioFormat": self._audio_format,
|
||||
"samplingRate": self._sampling_rate,
|
||||
}
|
||||
if self._settings.language is not None:
|
||||
params["lang"] = self._settings.language
|
||||
@@ -437,7 +431,7 @@ class RimeTTSService(AudioContextTTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.samplingRate = self.sample_rate
|
||||
self._sampling_rate = self.sample_rate
|
||||
await self._connect()
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
@@ -737,8 +731,6 @@ class RimeHttpTTSService(TTSService):
|
||||
model="mistv2",
|
||||
voice=None,
|
||||
language="eng",
|
||||
audioFormat="pcm",
|
||||
samplingRate=0,
|
||||
segment=None,
|
||||
speedAlpha=None,
|
||||
reduceLatency=None,
|
||||
@@ -789,6 +781,9 @@ class RimeHttpTTSService(TTSService):
|
||||
self._session = aiohttp_session
|
||||
self._base_url = "https://users.rime.ai/v1/rime-tts"
|
||||
|
||||
# Init-only audio format fields (not runtime-updatable)
|
||||
self._audio_format = "pcm"
|
||||
|
||||
def can_generate_metrics(self) -> bool:
|
||||
"""Check if this service can generate processing metrics.
|
||||
|
||||
@@ -974,8 +969,6 @@ class RimeNonJsonTTSService(InterruptibleTTSService):
|
||||
default_settings = RimeNonJsonTTSSettings(
|
||||
voice=None,
|
||||
model="arcana",
|
||||
audioFormat=audio_format,
|
||||
samplingRate=sample_rate,
|
||||
language=None,
|
||||
segment=None,
|
||||
repetition_penalty=None,
|
||||
@@ -1017,6 +1010,11 @@ class RimeNonJsonTTSService(InterruptibleTTSService):
|
||||
settings=default_settings,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Init-only audio format fields (not runtime-updatable)
|
||||
self._audio_format = audio_format
|
||||
self._sampling_rate = sample_rate
|
||||
|
||||
self._api_key = api_key
|
||||
self._url = url
|
||||
# Add any extra parameters for future compatibility
|
||||
@@ -1053,7 +1051,7 @@ class RimeNonJsonTTSService(InterruptibleTTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.samplingRate = self.sample_rate
|
||||
self._sampling_rate = self.sample_rate
|
||||
await self._connect()
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
@@ -1101,8 +1099,8 @@ class RimeNonJsonTTSService(InterruptibleTTSService):
|
||||
settings_dict = {
|
||||
"speaker": self._settings.voice,
|
||||
"modelId": self._settings.model,
|
||||
"audioFormat": self._settings.audioFormat,
|
||||
"samplingRate": self._settings.samplingRate,
|
||||
"audioFormat": self._audio_format,
|
||||
"samplingRate": self._sampling_rate,
|
||||
}
|
||||
if self._settings.language is not None:
|
||||
settings_dict["lang"] = self._settings.language
|
||||
|
||||
@@ -250,7 +250,6 @@ class SarvamHttpTTSSettings(TTSSettings):
|
||||
"""Settings for Sarvam HTTP TTS service.
|
||||
|
||||
Parameters:
|
||||
language: Sarvam language code.
|
||||
enable_preprocessing: Whether to enable text preprocessing. Defaults to False.
|
||||
**Note:** Always enabled for bulbul:v3-beta (cannot be disabled).
|
||||
pace: Speech pace multiplier. Defaults to 1.0.
|
||||
@@ -263,16 +262,13 @@ class SarvamHttpTTSSettings(TTSSettings):
|
||||
temperature: Controls output randomness for bulbul:v3-beta (0.01 to 1.0).
|
||||
Lower values = more deterministic, higher = more random. Defaults to 0.6.
|
||||
**Note:** Only supported for bulbul:v3-beta. Ignored for v2.
|
||||
sample_rate: Audio sample rate.
|
||||
"""
|
||||
|
||||
language: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
enable_preprocessing: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pace: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pitch: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
loudness: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
temperature: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
sarvam_sample_rate: int | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -280,19 +276,12 @@ class SarvamTTSSettings(TTSSettings):
|
||||
"""Settings for Sarvam WebSocket TTS service.
|
||||
|
||||
Parameters:
|
||||
language: Sarvam language code (e.g. ``"hi-IN"``). Uses the standard
|
||||
``TTSSettings.language`` field.
|
||||
speech_sample_rate: Audio sample rate as string.
|
||||
enable_preprocessing: Enable text preprocessing. Defaults to False.
|
||||
**Note:** Always enabled for bulbul:v3-beta.
|
||||
min_buffer_size: Minimum characters to buffer before generating audio.
|
||||
Lower values reduce latency but may affect quality. Defaults to 50.
|
||||
max_chunk_length: Maximum characters processed in a single chunk.
|
||||
Controls memory usage and processing efficiency. Defaults to 150.
|
||||
output_audio_codec: Audio codec format. Options: linear16, mulaw, alaw,
|
||||
opus, flac, aac, wav, mp3. Defaults to "linear16".
|
||||
output_audio_bitrate: Audio bitrate (32k, 64k, 96k, 128k, 192k).
|
||||
Defaults to "128k".
|
||||
pace: Speech pace multiplier. Defaults to 1.0.
|
||||
- bulbul:v2: Range 0.3 to 3.0
|
||||
- bulbul:v3-beta: Range 0.5 to 2.0
|
||||
@@ -307,12 +296,9 @@ class SarvamTTSSettings(TTSSettings):
|
||||
|
||||
_aliases: ClassVar[Dict[str, str]] = {"target_language_code": "language"}
|
||||
|
||||
speech_sample_rate: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
enable_preprocessing: bool | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
min_buffer_size: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
max_chunk_length: int | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
output_audio_codec: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
output_audio_bitrate: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pace: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pitch: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
loudness: float | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
@@ -587,7 +573,6 @@ class SarvamHttpTTSService(TTSService):
|
||||
frame: The start frame containing initialization parameters.
|
||||
"""
|
||||
await super().start(frame)
|
||||
self._settings.sarvam_sample_rate = self.sample_rate
|
||||
|
||||
@traced_tts
|
||||
async def run_tts(self, text: str, context_id: str) -> AsyncGenerator[Frame, None]:
|
||||
@@ -881,12 +866,9 @@ class SarvamTTSService(InterruptibleTTSService):
|
||||
model="bulbul:v2",
|
||||
voice="anushka",
|
||||
language="en-IN",
|
||||
speech_sample_rate="22050",
|
||||
enable_preprocessing=False,
|
||||
min_buffer_size=50,
|
||||
max_chunk_length=150,
|
||||
output_audio_codec="linear16",
|
||||
output_audio_bitrate="128k",
|
||||
pace=1.0,
|
||||
pitch=None,
|
||||
loudness=None,
|
||||
@@ -901,6 +883,10 @@ class SarvamTTSService(InterruptibleTTSService):
|
||||
_warn_deprecated_param("voice_id", SarvamTTSSettings, "voice")
|
||||
default_settings.voice = voice_id
|
||||
|
||||
# Init-only audio format fields (not runtime-updatable)
|
||||
output_audio_codec = "linear16"
|
||||
output_audio_bitrate = "128k"
|
||||
|
||||
# 3. Apply params overrides — only if settings not provided
|
||||
if params is not None:
|
||||
_warn_deprecated_param("params", SarvamTTSSettings)
|
||||
@@ -916,9 +902,9 @@ class SarvamTTSService(InterruptibleTTSService):
|
||||
if params.max_chunk_length is not None:
|
||||
default_settings.max_chunk_length = params.max_chunk_length
|
||||
if params.output_audio_codec is not None:
|
||||
default_settings.output_audio_codec = params.output_audio_codec
|
||||
output_audio_codec = params.output_audio_codec
|
||||
if params.output_audio_bitrate is not None:
|
||||
default_settings.output_audio_bitrate = params.output_audio_bitrate
|
||||
output_audio_bitrate = params.output_audio_bitrate
|
||||
if params.pace is not None:
|
||||
default_settings.pace = params.pace
|
||||
if params.pitch is not None:
|
||||
@@ -943,7 +929,6 @@ class SarvamTTSService(InterruptibleTTSService):
|
||||
# Set default sample rate based on model if not specified
|
||||
if sample_rate is None:
|
||||
sample_rate = self._config.default_sample_rate
|
||||
default_settings.speech_sample_rate = str(sample_rate)
|
||||
|
||||
# Set default voice based on model if not specified via any mechanism
|
||||
if voice_id is None and (settings is None or settings.voice is NOT_GIVEN):
|
||||
@@ -986,6 +971,11 @@ class SarvamTTSService(InterruptibleTTSService):
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Init-only audio format fields (not runtime-updatable)
|
||||
self._speech_sample_rate = str(sample_rate)
|
||||
self._output_audio_codec = output_audio_codec
|
||||
self._output_audio_bitrate = output_audio_bitrate
|
||||
|
||||
# WebSocket endpoint URL with model query parameter
|
||||
self._websocket_url = f"{url}?model={resolved_model}"
|
||||
self._api_key = api_key
|
||||
@@ -1022,7 +1012,7 @@ class SarvamTTSService(InterruptibleTTSService):
|
||||
await super().start(frame)
|
||||
|
||||
# WebSocket API expects sample rate as string
|
||||
self._settings.speech_sample_rate = str(self.sample_rate)
|
||||
self._speech_sample_rate = str(self.sample_rate)
|
||||
await self._connect()
|
||||
|
||||
async def stop(self, frame: EndFrame):
|
||||
@@ -1140,12 +1130,12 @@ class SarvamTTSService(InterruptibleTTSService):
|
||||
config_data = {
|
||||
"target_language_code": self._settings.language,
|
||||
"speaker": self._settings.voice,
|
||||
"speech_sample_rate": self._settings.speech_sample_rate,
|
||||
"speech_sample_rate": self._speech_sample_rate,
|
||||
"enable_preprocessing": self._settings.enable_preprocessing,
|
||||
"min_buffer_size": self._settings.min_buffer_size,
|
||||
"max_chunk_length": self._settings.max_chunk_length,
|
||||
"output_audio_codec": self._settings.output_audio_codec,
|
||||
"output_audio_bitrate": self._settings.output_audio_bitrate,
|
||||
"output_audio_codec": self._output_audio_codec,
|
||||
"output_audio_bitrate": self._output_audio_bitrate,
|
||||
"pace": self._settings.pace,
|
||||
"model": self._settings.model,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ This module provides integration with Coqui XTTS streaming server for
|
||||
text-to-speech synthesis using local Docker deployment.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
import aiohttp
|
||||
@@ -25,7 +25,7 @@ from pipecat.frames.frames import (
|
||||
TTSStartedFrame,
|
||||
TTSStoppedFrame,
|
||||
)
|
||||
from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven, _warn_deprecated_param
|
||||
from pipecat.services.settings import TTSSettings, _warn_deprecated_param
|
||||
from pipecat.services.tts_service import TTSService
|
||||
from pipecat.transcriptions.language import Language, resolve_language
|
||||
from pipecat.utils.tracing.service_decorators import traced_tts
|
||||
@@ -72,13 +72,9 @@ def language_to_xtts_language(language: Language) -> Optional[str]:
|
||||
|
||||
@dataclass
|
||||
class XTTSTTSSettings(TTSSettings):
|
||||
"""Settings for XTTS TTS service.
|
||||
"""Settings for XTTS TTS service."""
|
||||
|
||||
Parameters:
|
||||
base_url: Base URL of the XTTS streaming server.
|
||||
"""
|
||||
|
||||
base_url: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
|
||||
pass
|
||||
|
||||
|
||||
class XTTSService(TTSService):
|
||||
@@ -123,7 +119,6 @@ class XTTSService(TTSService):
|
||||
model=None,
|
||||
voice=None,
|
||||
language=self.language_to_service_language(language),
|
||||
base_url=base_url,
|
||||
)
|
||||
|
||||
# 2. Apply direct init arg overrides (deprecated)
|
||||
@@ -140,6 +135,10 @@ class XTTSService(TTSService):
|
||||
settings=default_settings,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Init-only fields (not runtime-updatable)
|
||||
self._base_url = base_url
|
||||
|
||||
self._studio_speakers: Optional[Dict[str, Any]] = None
|
||||
self._aiohttp_session = aiohttp_session
|
||||
|
||||
@@ -175,7 +174,7 @@ class XTTSService(TTSService):
|
||||
if self._studio_speakers:
|
||||
return
|
||||
|
||||
async with self._aiohttp_session.get(self._settings.base_url + "/studio_speakers") as r:
|
||||
async with self._aiohttp_session.get(self._base_url + "/studio_speakers") as r:
|
||||
if r.status != 200:
|
||||
text = await r.text()
|
||||
await self.push_error(
|
||||
@@ -203,7 +202,7 @@ class XTTSService(TTSService):
|
||||
|
||||
embeddings = self._studio_speakers[self._settings.voice]
|
||||
|
||||
url = self._settings.base_url + "/tts_stream"
|
||||
url = self._base_url + "/tts_stream"
|
||||
|
||||
payload = {
|
||||
"text": text.replace(".", "").replace("*", ""),
|
||||
|
||||
Reference in New Issue
Block a user