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