"""TTS backend abstraction and automatic backend selection. The dataset builder is agnostic to which engine produces audio. Two backends are provided: * :class:`~src.backends.gcp.GCPTTSBackend` — Google Cloud TTS via REST API key. Used when a key is available. * :class:`~src.backends.piper.PiperTTSBackend` — free, local Piper TTS. Used as the fall back. """ from __future__ import annotations from typing import List, Optional from .base import SynthesisResult, TTSBackend, Voice from .gcp import GCPTTSBackend from .piper import PiperTTSBackend __all__ = [ "SynthesisResult", "TTSBackend", "Voice", "GCPTTSBackend", "PiperTTSBackend", "select_backend", ] def select_backend( gcp_api_key: Optional[str], language_prefixes: List[str], max_gcp_voices_per_locale: int, max_piper_voices: int, sample_rate_hz: int, ) -> TTSBackend: """Return a ready TTS backend. Prefers Google Cloud TTS when ``gcp_api_key`` is provided and the key validates. Otherwise (no key, or the key fails to initialise) falls back to the free Piper backend. """ if gcp_api_key and gcp_api_key.strip(): backend = GCPTTSBackend( api_key=gcp_api_key.strip(), language_prefixes=language_prefixes, max_voices_per_locale=max_gcp_voices_per_locale, sample_rate_hz=sample_rate_hz, ) try: backend.prepare() return backend except Exception as exc: # noqa: BLE001 - any failure => fall back print(f"[backend] Google Cloud TTS unavailable ({exc}); falling back to Piper.") backend = PiperTTSBackend( max_voices=max_piper_voices, sample_rate_hz=sample_rate_hz, ) backend.prepare() return backend