services(cartesia): allow setting language and language voices
This commit is contained in:
@@ -9,7 +9,7 @@ import io
|
|||||||
import wave
|
import wave
|
||||||
|
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import AsyncGenerator, Optional
|
from typing import AsyncGenerator, Mapping, Optional
|
||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
AudioRawFrame,
|
AudioRawFrame,
|
||||||
@@ -20,16 +20,20 @@ from pipecat.frames.frames import (
|
|||||||
LLMFullResponseEndFrame,
|
LLMFullResponseEndFrame,
|
||||||
StartFrame,
|
StartFrame,
|
||||||
StartInterruptionFrame,
|
StartInterruptionFrame,
|
||||||
|
TTSLanguageUpdateFrame,
|
||||||
|
TTSLanguageVoicesUpdateFrame,
|
||||||
TTSSpeakFrame,
|
TTSSpeakFrame,
|
||||||
TTSStartedFrame,
|
TTSStartedFrame,
|
||||||
TTSStoppedFrame,
|
TTSStoppedFrame,
|
||||||
TTSVoiceUpdateFrame,
|
TTSVoiceUpdateFrame,
|
||||||
TextFrame,
|
TextFrame,
|
||||||
|
TranscriptionFrame,
|
||||||
UserImageRequestFrame,
|
UserImageRequestFrame,
|
||||||
VisionImageRawFrame
|
VisionImageRawFrame
|
||||||
)
|
)
|
||||||
from pipecat.processors.async_frame_processor import AsyncFrameProcessor
|
from pipecat.processors.async_frame_processor import AsyncFrameProcessor
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
|
from pipecat.transcriptions.languages import Language
|
||||||
from pipecat.utils.audio import calculate_audio_volume
|
from pipecat.utils.audio import calculate_audio_volume
|
||||||
from pipecat.utils.string import match_endofsentence
|
from pipecat.utils.string import match_endofsentence
|
||||||
from pipecat.utils.utils import exp_smoothing
|
from pipecat.utils.utils import exp_smoothing
|
||||||
@@ -177,6 +181,14 @@ class TTSService(AIService):
|
|||||||
async def set_voice(self, voice: str):
|
async def set_voice(self, voice: str):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def set_language(self, language: Language):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def set_language_voices(self, voices: Mapping[Language, str]):
|
||||||
|
pass
|
||||||
|
|
||||||
# Converts the text to audio.
|
# Converts the text to audio.
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def run_tts(self, text: str) -> AsyncGenerator[Frame, None]:
|
async def run_tts(self, text: str) -> AsyncGenerator[Frame, None]:
|
||||||
@@ -235,6 +247,10 @@ class TTSService(AIService):
|
|||||||
await self._push_tts_frames(frame.text, False)
|
await self._push_tts_frames(frame.text, False)
|
||||||
elif isinstance(frame, TTSVoiceUpdateFrame):
|
elif isinstance(frame, TTSVoiceUpdateFrame):
|
||||||
await self.set_voice(frame.voice)
|
await self.set_voice(frame.voice)
|
||||||
|
elif isinstance(frame, TTSLanguageUpdateFrame):
|
||||||
|
await self.set_language(frame.language)
|
||||||
|
elif isinstance(frame, TTSLanguageVoicesUpdateFrame):
|
||||||
|
await self.set_language_voices(frame.voices)
|
||||||
else:
|
else:
|
||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ import base64
|
|||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator, Mapping
|
||||||
|
|
||||||
from pipecat.processors.frame_processor import FrameDirection
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
CancelFrame,
|
CancelFrame,
|
||||||
ErrorFrame,
|
ErrorFrame,
|
||||||
@@ -26,6 +25,8 @@ from pipecat.frames.frames import (
|
|||||||
TextFrame,
|
TextFrame,
|
||||||
LLMFullResponseEndFrame
|
LLMFullResponseEndFrame
|
||||||
)
|
)
|
||||||
|
from pipecat.processors.frame_processor import FrameDirection
|
||||||
|
from pipecat.transcriptions.languages import Language
|
||||||
from pipecat.services.ai_services import TTSService
|
from pipecat.services.ai_services import TTSService
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -40,6 +41,15 @@ except ModuleNotFoundError as e:
|
|||||||
raise Exception(f"Missing module: {e}")
|
raise Exception(f"Missing module: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def language_to_cartesia_language(language: Language) -> str | None:
|
||||||
|
match language:
|
||||||
|
case Language.EN:
|
||||||
|
return "en"
|
||||||
|
case Language.ES:
|
||||||
|
return "es"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class CartesiaTTSService(TTSService):
|
class CartesiaTTSService(TTSService):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -79,6 +89,7 @@ class CartesiaTTSService(TTSService):
|
|||||||
"sample_rate": sample_rate,
|
"sample_rate": sample_rate,
|
||||||
}
|
}
|
||||||
self._language = language
|
self._language = language
|
||||||
|
self._language_voices = {}
|
||||||
|
|
||||||
self._websocket = None
|
self._websocket = None
|
||||||
self._context_id = None
|
self._context_id = None
|
||||||
@@ -94,6 +105,17 @@ class CartesiaTTSService(TTSService):
|
|||||||
logger.debug(f"Switching TTS voice to: [{voice}]")
|
logger.debug(f"Switching TTS voice to: [{voice}]")
|
||||||
self._voice_id = voice
|
self._voice_id = voice
|
||||||
|
|
||||||
|
async def set_language(self, language: Language):
|
||||||
|
cartesia_language = language_to_cartesia_language(language)
|
||||||
|
if cartesia_language and language in self._language_voices:
|
||||||
|
logger.debug(f"Switching TTS language to: [{language}]")
|
||||||
|
self._language = cartesia_language
|
||||||
|
await self.set_voice(self._language_voices[language])
|
||||||
|
|
||||||
|
async def set_language_voices(self, voices: Mapping[Language, str]):
|
||||||
|
logger.debug(f"Setting TTS language voices to: {voices}")
|
||||||
|
self._language_voices = voices
|
||||||
|
|
||||||
async def start(self, frame: StartFrame):
|
async def start(self, frame: StartFrame):
|
||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
await self._connect()
|
await self._connect()
|
||||||
|
|||||||
Reference in New Issue
Block a user