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.
46 lines
1.8 KiB
Bash
Executable file
46 lines
1.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Voz de JARVIS con la clonada (XTTS-v2) servida desde cache.
|
|
#
|
|
# jarvis-voz.sh <fichero.wav> <texto>
|
|
#
|
|
# Si el texto esta pre-generado en cache_voz/, lo sirve al instante (un cp, sin
|
|
# tocar GPU). Si no, cae a Piper. La clave es sha1(texto normalizado), la misma
|
|
# que calcula generar_cache_voz.py -> los aciertos son exactos.
|
|
#
|
|
# El clon ya trae el tono correcto, asi que en un acierto NO se aplica el
|
|
# descenso de tono de Piper; solo se copia. En un fallo, Piper hace lo suyo
|
|
# (incluido su rubberband) como hasta ahora.
|
|
set -uo pipefail
|
|
|
|
# La raiz se deduce de donde esta este fichero, no del $HOME de nadie: asi el
|
|
# arbol se puede mover o clonar en otra maquina y sigue funcionando sin editar
|
|
# rutas. `cd -P` resuelve enlaces simbolicos, que el lanzador de escritorio
|
|
# puede invocarlo por uno.
|
|
RAIZ=$(cd -P "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd)
|
|
BASE=$RAIZ/voz
|
|
CACHE=$BASE/cache_voz
|
|
PIPER=$RAIZ/config/jarvis-piper.sh
|
|
|
|
SALIDA=${1:-}
|
|
TEXTO=${2:-}
|
|
|
|
[ -n "$SALIDA" ] || { echo "falta el fichero de salida" >&2; exit 1; }
|
|
[ -n "$TEXTO" ] || { echo "sin texto que decir" >&2; exit 1; }
|
|
|
|
# clave = sha1(texto con espacios colapsados y recortado), identica a la de
|
|
# Python: re.sub(r'\s+',' ',t).strip() -> sha1(utf-8)
|
|
T=$(printf '%s' "$TEXTO" | tr -s '[:space:]' ' ' | sed 's/^ //; s/ $//')
|
|
KEY=$(printf '%s' "$T" | sha1sum | cut -d' ' -f1)
|
|
HIT=$CACHE/$KEY.wav
|
|
|
|
if [ -f "$HIT" ]; then
|
|
cp -f "$HIT" "$SALIDA"
|
|
exit 0
|
|
fi
|
|
|
|
# fallo de cache -> registrar la frase (normalizada) para calentar la cache
|
|
# luego con la voz clonada, y de momento reescribir siglas + Piper.
|
|
printf '%s\n' "$T" >> "$BASE/misses.log" 2>/dev/null || true
|
|
TEXTO2=$(python3 "$BASE/pronuncia.py" "$TEXTO" 2>/dev/null)
|
|
[ -n "$TEXTO2" ] || TEXTO2=$TEXTO
|
|
exec "$PIPER" "$SALIDA" "$TEXTO2"
|