Añadir mu-plugins y scripts de feadulta
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user