JARVIS: asistente de voz local para Linux
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.
This commit is contained in:
commit
8e4bc8ad94
125 changed files with 25033 additions and 0 deletions
49
voz/clonar_afinar.py
Normal file
49
voz/clonar_afinar.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env python3
|
||||
# Afinado de XTTS: combina varias referencias (promedia el timbre) y prueba
|
||||
# distintos ajustes de velocidad/temperatura para acercarse al Jarvis original.
|
||||
# Genera variantes en ref/afinar/ para comparar de oido y elegir la buena.
|
||||
import os, sys, time
|
||||
os.environ.setdefault("COQUI_TOS_AGREED", "1")
|
||||
DISP = sys.argv[1] if len(sys.argv) > 1 else "cuda"
|
||||
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(BASE, "ref", "afinar")
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
|
||||
# varias referencias: XTTS calcula el latent del hablante promediandolas
|
||||
REFS = [os.path.join(BASE, "ref", n) for n in
|
||||
("jarvis_lento_b.wav", "jarvis_lento_a.wav", "jarvis_ref.wav")]
|
||||
REFS = [r for r in REFS if os.path.exists(r)]
|
||||
|
||||
FRASES = {
|
||||
"saludo": "Buenas noches, señor. Todo está listo.",
|
||||
"pregunta": "He encontrado tres ficheros que coinciden. ¿Cuál abro?",
|
||||
}
|
||||
|
||||
# (etiqueta, kwargs de inferencia)
|
||||
VARIANTES = [
|
||||
("v1_natural", dict(speed=1.0, temperature=0.65, repetition_penalty=2.0)),
|
||||
("v2_sereno", dict(speed=0.92, temperature=0.60, repetition_penalty=3.0)),
|
||||
("v3_grave", dict(speed=0.88, temperature=0.55, repetition_penalty=5.0)),
|
||||
]
|
||||
|
||||
print(f"referencias ({len(REFS)}): {[os.path.basename(r) for r in REFS]}", flush=True)
|
||||
t0 = time.time()
|
||||
from TTS.api import TTS
|
||||
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(DISP)
|
||||
print(f"modelo cargado en {time.time()-t0:.1f}s", flush=True)
|
||||
|
||||
for fid, frase in FRASES.items():
|
||||
for etiqueta, kw in VARIANTES:
|
||||
dst = os.path.join(OUT, f"{fid}_{etiqueta}.wav")
|
||||
t0 = time.time()
|
||||
try:
|
||||
tts.tts_to_file(text=frase, speaker_wav=REFS, language="es",
|
||||
file_path=dst, **kw)
|
||||
print(f" {fid}/{etiqueta}: {time.time()-t0:4.1f}s {kw}", flush=True)
|
||||
except TypeError as e:
|
||||
# algun kwarg no soportado: reintenta solo con speed+temperature
|
||||
kw2 = {k: kw[k] for k in ("speed", "temperature") if k in kw}
|
||||
tts.tts_to_file(text=frase, speaker_wav=REFS, language="es",
|
||||
file_path=dst, **kw2)
|
||||
print(f" {fid}/{etiqueta}: (sin rep_penalty) {kw2}", flush=True)
|
||||
print("listo.", flush=True)
|
||||
Loading…
Add table
Add a link
Reference in a new issue