Rama JARVIS-GREEN, independiente de master (el nucleo naranja queda intacto). - README propio del carril verde con diagrama de arquitectura y capturas del panel. - verde/: arranque, panel web, puente de voz (escucha local con faster-whisper + habla con la voz del naranja), pruebas (07 voz, 08 escucha), config y notas. - Integracion RAG: nucleo/saber/busca_cli.py + skill buscar-en-apuntes, para que el agente consulte el mismo indice que el nucleo. - Todo local (127.0.0.1); sin datos personales (rutas y modelo de GPU scrubeados).
88 lines
3.4 KiB
Bash
Executable file
88 lines
3.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Que el verde hable, y que hable IGUAL que el naranja.
|
|
#
|
|
# No es un detalle estetico. Si el verde sonara con una voz que Piper se
|
|
# descarga por su cuenta —la GPL de OHF-Voice que trae Hermes de serie—,
|
|
# comparar los dos carriles no diria nada: la mitad de la impresion de un
|
|
# asistente de voz es el timbre.
|
|
#
|
|
# Por eso el TTS de Hermes va por comando (type: command, sin documentar en su
|
|
# web pero si en tools/tts_tool.py) contra voz-verde.sh, que es un adaptador de
|
|
# 15 lineas al mismo config/jarvis-piper.sh del naranja: mismo binario Piper
|
|
# 1.2.0 MIT, mismo .onnx, misma cadena de audio.
|
|
#
|
|
# La prueba llama al adaptador EXACTAMENTE como lo llama Hermes: dos argumentos,
|
|
# el WAV de salida y un FICHERO con el texto. Es donde estaba la trampa —
|
|
# jarvis-piper.sh quiere el texto como argumento— y una interfaz mal encajada no
|
|
# se ve hasta que suena mal.
|
|
#
|
|
# No suena nada: se genera el fichero y se mide.
|
|
|
|
cd "$(dirname "$0")" && . ./comun.sh
|
|
|
|
titulo "El adaptador"
|
|
|
|
afirma "existe voz-verde.sh" test -x "$VERDE/voz-verde.sh"
|
|
afirma "existe jarvis-piper.sh del naranja" test -x "$RAIZ/config/jarvis-piper.sh"
|
|
|
|
titulo "Generar"
|
|
|
|
TEXTO=$TMP/texto.txt
|
|
WAV=$TMP/voz.wav
|
|
rm -f "$WAV"
|
|
printf 'A sus órdenes, señor. Todos los sistemas responden con normalidad.' > "$TEXTO"
|
|
|
|
INICIO=$(date +%s.%N)
|
|
"$VERDE/voz-verde.sh" "$WAV" "$TEXTO" >"$TMP/voz.log" 2>&1
|
|
SALIO=$?
|
|
TARDO=$(awk "BEGIN{printf \"%.2f\", $(date +%s.%N) - $INICIO}")
|
|
|
|
if [ $SALIO -ne 0 ] || [ ! -s "$WAV" ]; then
|
|
echo " $(rojo FALLO) voz-verde.sh no genero el WAV (codigo $SALIO)"
|
|
sed 's/^/ /' "$TMP/voz.log" | head -8
|
|
exit 1
|
|
fi
|
|
echo " $(verde OK) WAV generado en ${TARDO}s"; _ok=$((_ok + 1))
|
|
|
|
titulo "El fichero"
|
|
|
|
# soxi/ffprobe no estan garantizados: se lee la cabecera RIFF a mano, que son
|
|
# cuatro campos en posiciones fijas y no depende de nada instalado.
|
|
leer=$(python3 - "$WAV" <<'PY'
|
|
import struct, sys, wave
|
|
with wave.open(sys.argv[1]) as w:
|
|
print(w.getframerate(), w.getnchannels(), w.getsampwidth() * 8,
|
|
round(w.getnframes() / w.getframerate(), 2))
|
|
PY
|
|
)
|
|
read -r HZ CANALES BITS SEGUNDOS <<<"$leer"
|
|
|
|
comprueba "22050 Hz, como el naranja" "$HZ" "22050"
|
|
comprueba "mono" "$CANALES" "1"
|
|
comprueba "16 bits" "$BITS" "16"
|
|
|
|
# Frase de ~4 s. Si sale mucho mas corta, Piper se comio el texto; si sale mucho
|
|
# mas larga, el tono esta mal y sonaria a camara lenta.
|
|
mayor_que "dura lo que una frase (s)" "$SEGUNDOS" 2
|
|
menor_que "y no se alarga de mas (s)" "$SEGUNDOS" 12
|
|
|
|
# Tiempo real: 0,34 s para 3,2 s de audio, medido el 15 ago. Es CPU, no toca la
|
|
# grafica, asi que no compite con el modelo.
|
|
RATIO=$(awk "BEGIN{printf \"%.2f\", $TARDO / $SEGUNDOS}")
|
|
nota "sintesis $TARDO s para $SEGUNDOS s de audio (x$RATIO del tiempo real)"
|
|
menor_que "sintetiza mas rapido de lo que dura" "$RATIO" 1
|
|
|
|
titulo "La misma voz que el naranja"
|
|
|
|
# voz-verde.sh copia al NUCLEO (nucleo/boca/voz.py: POR_DEFECTO = "davefx"), no
|
|
# al README, que describe sharvard y es del JARVIS sobre Newelle. Se compara
|
|
# contra el nucleo porque es contra quien se va a comparar el carril.
|
|
VOZ_VERDE=$(grep -oP 'JARVIS_PIPER_VOZ:-\K[^}]+' "$VERDE/voz-verde.sh")
|
|
VOZ_NUCLEO=$(grep -oP 'POR_DEFECTO\s*=\s*"\K[^"]+' "$RAIZ/nucleo/boca/voz.py" 2>/dev/null)
|
|
if [ -z "$VOZ_NUCLEO" ]; then
|
|
nota "no pude leer la voz del nucleo; comparacion omitida"
|
|
else
|
|
comprueba "misma voz que el nucleo" "$VOZ_VERDE" "es_ES-${VOZ_NUCLEO}-medium"
|
|
fi
|
|
|
|
resumen
|