| """ |
| tts_sample.py — genera y reproduce una muestra de las 3 voces (lesky, monstruo, |
| gnomos) llamando al endpoint Modal. Guarda los WAV en samples/ y los reproduce |
| en orden (macOS `afplay`). Sirve para oír cómo quedan las clonaciones. |
| |
| Uso: |
| python tools/tts_sample.py |
| python tools/tts_sample.py "https://otra-url.modal.run" # URL distinta (opcional) |
| |
| OJO: si una voz suena neutra (no clonada), falta `modal deploy tools/tts_modal.py` |
| tras añadir su .mp3 — los conds se cargan al arrancar el contenedor. |
| """ |
|
|
| import os |
| import sys |
| import time |
| import json |
| import subprocess |
| import urllib.request |
|
|
| URL = sys.argv[1] if len(sys.argv) > 1 else \ |
| "https://vegagallegopablo--chatterbox-tts-turbo-tts-speak.modal.run" |
|
|
| SAMPLES = { |
| "lesky": "Heh heh heh! Welcome to my shop, little one. The treasure? Oh, it's mine.", |
| "monstruo": "So... another little challenger crawls into my alley. How adorable. I will crush you.", |
| "gnomos": "Wait, wait, no! It was MY turn to be on top! Move over, you're squashing my hat!", |
| } |
|
|
| OUT_DIR = "samples" |
| os.makedirs(OUT_DIR, exist_ok=True) |
|
|
|
|
| def synth(text, voice): |
| d = json.dumps({"text": text, "voice": voice}).encode() |
| req = urllib.request.Request(URL, data=d, headers={"Content-Type": "application/json"}) |
| t = time.time() |
| with urllib.request.urlopen(req, timeout=180) as r: |
| audio = r.read() |
| return audio, time.time() - t |
|
|
|
|
| for voice, text in SAMPLES.items(): |
| print(f"\n🎙 {voice}: «{text}»") |
| try: |
| audio, dt = synth(text, voice) |
| except Exception as e: |
| print(f" ❌ fallo: {e}") |
| continue |
| path = os.path.join(OUT_DIR, f"sample_{voice}.wav") |
| with open(path, "wb") as f: |
| f.write(audio) |
| print(f" ✅ {len(audio)} bytes · {dt:.2f}s · {path}") |
| try: |
| subprocess.run(["afplay", path]) |
| except FileNotFoundError: |
| print(" (sin afplay: reprodúcelo a mano)") |
|
|
| print(f"\nListo. WAV en {OUT_DIR}/") |