Files

83 lines
3.1 KiB
Python

#!/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/<nombre>.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())