#!/usr/bin/env python3 """ tts_eval.py — Genera la MISMA frase de feadulta con varias voces/modelos TTS para compararlas (evaluación de voz, issue #76). Incluye: - edge-tts Ximena (referencia, gratis, ya la usamos) — siempre. - Modelos premium vía Hugging Face Inference Providers (consume crédito HF) — opcional. Objetivo: ELEGIR voz. Para producción en masa NO se usa HF (sale caro); el modelo abierto ganador se corre en LOCAL (RTX 5060 Ti) gratis. Ver análisis en #76. Uso: # Solo la referencia local (gratis): python3 scripts/tts_eval.py # Con modelos HF (necesita token; gasta unos céntimos del crédito): HF_TOKEN=hf_xxx python3 scripts/tts_eval.py --hf Salida: ./tts-eval/.mp3 (escúchalos y elige). """ from __future__ import annotations import argparse, os, subprocess, sys from pathlib import Path SAMPLE = ( "Bienvenido a Fe Adulta. La humanidad abriga una esperanza: verse liberada de la " "esclavitud y alcanzar la libertad de los hijos de Dios. Una fe adulta es una fe " "personal, valiente, sin miedos infantiles. Detente un instante y respira." ) OUT = Path(__file__).resolve().parent.parent / "tts-eval" EDGE = os.path.expanduser("~/.hermes/hermes-agent/venv/bin/edge-tts") # Candidatos vía HF Inference Providers (provider, model). Verifica disponibilidad en la # pestaña "Inference Providers" de cada modelo en huggingface.co — el routing cambia. HF_CANDIDATES = [ ("fal-ai", "fal-ai/f5-tts"), ("fal-ai", "fal-ai/chatterbox/text-to-speech"), ("hf-inference", "myshell-ai/MeloTTS-Spanish"), ] def edge_samples(): OUT.mkdir(exist_ok=True) for voz in ("es-ES-XimenaNeural", "es-ES-ElviraNeural", "es-MX-JorgeNeural"): dst = OUT / f"edge-{voz}.mp3" print(f"edge-tts {voz} ...", flush=True) subprocess.run([EDGE, "--voice", voz, "--text", SAMPLE, "--write-media", str(dst)], capture_output=True) print(f" -> {OUT}") def hf_samples(): try: from huggingface_hub import InferenceClient except ImportError: sys.exit("Falta huggingface_hub: pip install huggingface_hub") token = os.environ.get("HF_TOKEN") if not token: sys.exit("Define HF_TOKEN para usar --hf") OUT.mkdir(exist_ok=True) for provider, model in HF_CANDIDATES: name = model.replace("/", "_") try: client = InferenceClient(provider=provider, api_key=token) audio = client.text_to_speech(SAMPLE, model=model) dst = OUT / f"hf-{name}.mp3" dst.write_bytes(audio) print(f"OK {provider}:{model} -> {dst.name}") except Exception as exc: # noqa: BLE001 print(f"FALLO {provider}:{model} -> {exc}") def main(): ap = argparse.ArgumentParser() ap.add_argument("--hf", action="store_true", help="También generar con modelos HF (gasta crédito).") args = ap.parse_args() edge_samples() if args.hf: hf_samples() print("\nEscucha los .mp3 en", OUT, "y elige. Para producción: correr el modelo abierto ganador en local.") if __name__ == "__main__": raise SystemExit(main())