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.
74 lines
2.6 KiB
Python
74 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Pone la voz de Piper: castellano de España, grave.
|
|
|
|
flatpak run --command=python3 io.github.qwersyk.Newelle//master \
|
|
~/COFRE/CODERS/JARVIS/config/set_voz_piper.py
|
|
|
|
Por que no Kokoro: no tiene ni una voz de España. Sus dos masculinas en
|
|
español, em_alex y em_santa, suenan latinoamericanas. Piper si las tiene, es
|
|
libre (MIT, voces con licencias abiertas) y corre local.
|
|
|
|
Medido con la misma frase:
|
|
|
|
em_alex (Kokoro, latino) 129 Hz
|
|
es_ES-carlfm-x_low 118 Hz calidad baja, 16 kHz
|
|
es_ES-sharvard-medium 127 Hz
|
|
es_ES-davefx-medium 128 Hz
|
|
es_ES-sharvard-medium + tono 106 Hz <- elegida
|
|
|
|
106 Hz cae de lleno en el rango de voz masculina grave (85-110). Se llega ahi
|
|
con rubberband en el envoltorio, no con la cadena de la app: alli el descenso
|
|
usa asetrate, que depende de la frecuencia de muestreo y esta calculado para
|
|
los 24 kHz de Kokoro, no para los 22050 de Piper.
|
|
|
|
Se apaga por tanto la bajada de tono de la app, para no aplicarla dos veces.
|
|
El resto del tratamiento (paso alto, ecualizador, compresor, eco) se queda:
|
|
es independiente de la frecuencia y es lo que da el aire de "voz en la sala".
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
from gi.repository import Gio
|
|
|
|
SCHEMA = "io.github.qwersyk.Newelle"
|
|
ENVOLTORIO = os.path.expanduser(
|
|
"~/COFRE/CODERS/JARVIS/config/jarvis-piper.sh")
|
|
|
|
|
|
def main():
|
|
if not os.path.exists(ENVOLTORIO):
|
|
print(f"FALLO: no esta {ENVOLTORIO}")
|
|
return 1
|
|
|
|
settings = Gio.Settings.new(SCHEMA)
|
|
|
|
try:
|
|
voces = json.loads(settings.get_string("tts-voice") or "{}")
|
|
except json.JSONDecodeError:
|
|
voces = {}
|
|
|
|
# {0} es el fichero y {1} el texto; el handler los entrecomilla, asi que
|
|
# aqui van sin comillas alrededor
|
|
voces["custom_command"] = {"command": f"{ENVOLTORIO} {{0}} {{1}}"}
|
|
settings.set_string("tts-voice", json.dumps(voces))
|
|
settings.set_string("tts", "custom_command")
|
|
Gio.Settings.sync()
|
|
|
|
motor = settings.get_string("tts")
|
|
orden = json.loads(settings.get_string("tts-voice")).get(
|
|
"custom_command", {}).get("command", "")
|
|
|
|
print(f"motor de voz: {motor}")
|
|
print(f"orden: {orden}")
|
|
|
|
ok = motor == "custom_command" and ENVOLTORIO in orden
|
|
print("verificado" if ok else "FALLO: no quedo como se pidio")
|
|
if ok:
|
|
print("\nrecuerda apagar la bajada de tono de la app, que ya la hace el envoltorio:")
|
|
print(" flatpak override --user --env=JARVIS_TTS_GRAVEDAD=1.0 io.github.qwersyk.Newelle")
|
|
return 0 if ok else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|