Nucleo propio: oye con whisper.cpp, piensa con un modelo de Ollama, habla con Piper, y hace RAG sobre los apuntes del usuario. 100% local, sin cuentas ni claves. Escrito bajo una restriccion dura, 4 GB de VRAM: el cerebro y whisper comparten tarjeta y solo caben porque estan dimensionados para ello. El RAG usa embeddings estaticos con busqueda hibrida; la voz clonada se sirve de una cache de frases. Incluye instalador (install.sh), requisitos, y documentacion del stack, del manejo de root y de las acciones. Los apuntes indexados y el diario NO se incluyen: son privados y el .gitignore los bloquea.
92 lines
3.6 KiB
Python
92 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""¿El clon falla porque las frases son cortas?
|
|
|
|
La referencia son 25 segundos de habla seguida. Lo que JARVIS dice de verdad
|
|
son acuses de segundo y medio: "Voy, señor". XTTS arranca cada generacion sin
|
|
contexto, y en frase corta no le da tiempo a asentar la voz antes de terminar.
|
|
Si es eso, el parecido deberia subir con la longitud del texto — y la solucion
|
|
no seria tocar parametros sino generar mas largo y recortar.
|
|
|
|
Se mide con repeticiones porque XTTS muestrea: una sola pasada por longitud
|
|
seria ruido, que es el error que ya se cometio dos veces hoy.
|
|
|
|
python prueba_longitud.py [cuda|cpu] [repeticiones]
|
|
"""
|
|
import os
|
|
import statistics
|
|
import sys
|
|
import tempfile
|
|
|
|
import torch
|
|
|
|
DISPOSITIVO = sys.argv[1] if len(sys.argv) > 1 else "cuda"
|
|
REPES = int(sys.argv[2]) if len(sys.argv) > 2 else 4
|
|
|
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, BASE)
|
|
from clonador import CONDICIONADO, GENERACION, referencias
|
|
|
|
os.environ.setdefault("COQUI_TOS_AGREED", "1")
|
|
PATRON = os.path.join(BASE, "ref", "jarvis_lento_b.wav")
|
|
MEDIR = dict(gpt_cond_len=30, gpt_cond_chunk_len=6, max_ref_length=30)
|
|
|
|
# mismo registro, longitud creciente
|
|
TEXTOS = [
|
|
("muy corta (~1s)", "Voy, señor."),
|
|
("corta (~2s)", "Enseguida, señor. Ahora mismo."),
|
|
("media (~5s)", "Enseguida, señor. He revisado el sistema y todo está en orden."),
|
|
("larga (~10s)", "Enseguida, señor. He revisado el sistema y todo está en orden. "
|
|
"La temperatura ronda los sesenta y tres grados y quedan "
|
|
"cuarenta y un gigabytes de memoria libre."),
|
|
("muy larga (~20s)", "Enseguida, señor. He revisado el sistema y todo está en orden. "
|
|
"La temperatura ronda los sesenta y tres grados y quedan "
|
|
"cuarenta y un gigabytes de memoria libre. El disco principal "
|
|
"va al sesenta por ciento y no he detectado ningún proceso "
|
|
"anómalo. ¿Desea que ejecute el análisis de red ahora o "
|
|
"prefiere que espere?"),
|
|
]
|
|
|
|
|
|
def main():
|
|
from TTS.api import TTS
|
|
import soundfile as sf
|
|
|
|
api = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(DISPOSITIVO)
|
|
modelo = api.synthesizer.tts_model
|
|
|
|
def emb(ruta):
|
|
_, e = modelo.get_conditioning_latents(audio_path=[ruta], **MEDIR)
|
|
return e
|
|
|
|
patron = emb(PATRON)
|
|
|
|
def parecido(ruta):
|
|
return float(torch.nn.functional.cosine_similarity(
|
|
patron.squeeze().float(), emb(ruta).squeeze().float(), dim=0))
|
|
|
|
gpt, hablante = modelo.get_conditioning_latents(
|
|
audio_path=referencias(), **CONDICIONADO)
|
|
|
|
print(f" {REPES} repeticiones por longitud · referencia a batir: 0.84-0.95\n")
|
|
print(f" {'texto':18s} {'dur':>6s} {'parecido':>9s} {'desv':>7s}")
|
|
print(" " + "-" * 45)
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
for etiqueta, texto in TEXTOS:
|
|
puntos, duraciones = [], []
|
|
for r in range(REPES):
|
|
out = modelo.inference(
|
|
text=texto, language="es", gpt_cond_latent=gpt,
|
|
speaker_embedding=hablante, enable_text_splitting=False,
|
|
**GENERACION)
|
|
d = os.path.join(tmp, f"{len(puntos)}_{r}.wav")
|
|
sf.write(d, out["wav"], 24000)
|
|
duraciones.append(len(out["wav"]) / 24000)
|
|
puntos.append(parecido(d))
|
|
print(f" {etiqueta:18s} {statistics.mean(duraciones):5.1f}s "
|
|
f"{statistics.mean(puntos):9.4f} {statistics.pstdev(puntos):7.4f}",
|
|
flush=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|