Añadir mu-plugins y scripts de feadulta

This commit is contained in:
2026-06-28 15:10:46 -04:00
parent bce7e42f44
commit b6116b066d
106 changed files with 17600 additions and 2 deletions
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""Clona una voz con XTTS-v2 (local) y locuta la muestra de feadulta. Issue #76.
Uso:
tts_xtts.py <muestra_voz.wav|mp3> [nombre_salida]
La muestra: 6-20s de voz limpia en español. Salida en uploads/tts-samples/.
NOTA: XTTS-v2 tiene licencia no comercial (CPML). En CPU tarda ~1-2 min por
muestra; con GPU sería casi instantáneo.
"""
import os
import subprocess
import sys
from pathlib import Path
os.environ.setdefault("COQUI_TOS_AGREED", "1") # acepta licencia CPML no-interactivo
import torch # noqa: E402
from TTS.api import TTS # noqa: E402
DEVICE = "cuda" if torch.cuda.is_available() and not os.environ.get("FEA_CPU") else "cpu"
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 / "wordpress/wp-content/uploads/tts-samples"
def main():
if len(sys.argv) < 2:
sys.exit("uso: tts_xtts.py <muestra_voz.wav|mp3> [nombre_salida]")
spk = sys.argv[1]
name = sys.argv[2] if len(sys.argv) > 2 else "xtts-clon"
OUT.mkdir(parents=True, exist_ok=True)
print(f"Cargando XTTS-v2 en {DEVICE}", flush=True)
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(DEVICE)
raw = OUT / f"{name}.raw.wav"
print(f"Clonando voz de {spk} y locutando…", flush=True)
tts.tts_to_file(
text=SAMPLE, speaker_wav=spk, language="es", file_path=str(raw),
temperature=0.65, # menos aleatoriedad → más estable
length_penalty=1.0,
repetition_penalty=5.0, # reduce artefactos/balbuceos en español
top_k=50,
top_p=0.85,
enable_text_splitting=True, # parte por frases → mejor prosodia
)
# Comfort noise: ruido marrón suave y constante que rellena los silencios de
# comas/puntos para que no contrasten con el suelo de ruido del habla clonada.
wav = OUT / f"{name}.wav"
if os.environ.get("FEA_NO_COMFORT"):
subprocess.run(["ffmpeg", "-y", "-i", str(raw), str(wav)], capture_output=True)
else:
subprocess.run([
"ffmpeg", "-y", "-i", str(raw), "-filter_complex",
"anoisesrc=color=brown:amplitude=0.004:sample_rate=24000[n];"
"[n]highpass=f=120,lowpass=f=3800[nf];"
"[0:a][nf]amix=inputs=2:duration=first:dropout_transition=0:normalize=0[a]",
"-map", "[a]", "-ar", "24000", str(wav),
], capture_output=True)
raw.unlink(missing_ok=True)
mp3 = OUT / f"{name}.mp3"
subprocess.run(["ffmpeg", "-y", "-i", str(wav), "-b:a", "96k", str(mp3)],
capture_output=True)
print(f"OK -> {mp3}")
if __name__ == "__main__":
main()