virtual-characters / src /tts_engine.py
ShadowInk's picture
Upload complete Space runtime files
6bcddd0 verified
Raw
History Blame Contribute Delete
1.6 kB
import os
import tempfile
import uuid
from pathlib import Path
from urllib.parse import urlparse
DEFAULT_TTS_URL = "https://veronicaulises0--virtual-characters-tts-charactertts-tts.modal.run"
def synthesize_sentence(text: str, character: dict, voice_state: dict | None = None) -> str | None:
url = _tts_endpoint_url(os.environ.get("VC_MODAL_TTS_URL") or DEFAULT_TTS_URL)
if not url or not text.strip():
return None
import httpx
voice = character.get("voice", {})
voice_state = voice_state or {}
payload = {
"text": text,
"voice_id": voice_state.get("voice_id") or voice.get("voice_id", "default"),
"emotion": voice_state.get("emotion") or voice_state.get("style") or "neutral",
"speed": voice_state.get("speed", 1.0),
"energy": voice_state.get("energy", voice.get("energy", 0.5)),
"audio_prompt_path": voice_state.get("audio_prompt_path") or voice.get("audio_prompt_path"),
"backend": voice.get("backend", "chatterbox"),
}
response = httpx.post(url, json=payload, timeout=180, trust_env=False)
response.raise_for_status()
path = Path(tempfile.gettempdir()) / f"virtual_characters_tts_{uuid.uuid4().hex}.wav"
path.write_bytes(response.content)
return str(path)
def _tts_endpoint_url(url: str | None) -> str | None:
if not url:
return None
base = url.rstrip("/")
parsed = urlparse(base)
if not parsed.path or parsed.path == "/":
return base
if parsed.path.rstrip("/").rsplit("/", 1)[-1] == "tts":
return base
return base + "/tts"