Arquitectura y modelo de ejecución
Hilos y cola, la cascada de inyección de texto, el socket de control y la tipografía por idioma.
Tres hilos, una cola
La captura de audio, el reconocimiento y la interfaz de terminal se ejecutan en hilos daemon separados dentro de src/main.py, unidos por una queue.Queue segura entre hilos.
audio_capture.py lee PCM mono de 16 kHz de PortAudio y lo encola; recognition.py lo pasa a un KaldiRecognizer local.
Cuando el reconocimiento se queda atrás, se descarta el bloque de audio más antiguo en vez de dejar que la latencia se desvíe — el contador de desbordamiento aparece en --status y --diagnose.
Inyección de texto: un pegado desde el portapapeles, con cascada de backends
Cuando text_processing.py ha formateado un enunciado reconocido, system_control.py lo coloca en el portapapeles y simula un pegado. El backend se elige una sola vez al arrancar.
| Vía | Herramientas | Condición |
|---|---|---|
| Wayland | wtype + wl-clipboard | Independiente de la distribución del teclado (wtype trabaja por keysym). Necesita un compositor que exponga el protocolo de teclado virtual (escritorios wlroots); si no, recurre a ydotool. |
| X11 | xdotool + xclip | Vía estándar bajo X11. |
| Respaldo | ydotool + wl-clipboard | Inyecta a nivel de controlador (/dev/uinput) vía el demonio ydotoold; keycodes dependientes de la distribución del teclado; exige pertenecer al grupo input. |
El motor de pulsaciones necesita una herramienta de portapapeles a su lado: wl-clipboard bajo Wayland —necesaria incluso en el respaldo ydotool— y xclip bajo X11. Los paquetes nativos arrastran wl-clipboard (el paquete Debian recomienda además xclip); una instalación desde las fuentes las añade por su cuenta. Sin una de ellas, el texto dictado nunca llega al portapapeles y el pegado reinserta lo último que se copió a mano.
El contenido anterior del portapapeles se vuelve a leer y se restaura tras el pegado (configurable con clipboard.restore, activo por defecto). El contenido no textual —imágenes, archivos— se deja intacto. Bajo Wayland con una aplicación GTK, la restauración puede adelantarse a la lectura del portapapeles por la ventana; clipboard.defer_restore aplaza la restauración hasta el final de la sesión de dictado para que el pegado nunca recupere el contenido anterior.
Si ningún backend de la sesión actual funciona, la herramienta no se bloquea ni descarta nada en silencio: pasa a modo degradado. El texto reconocido sigue mostrándose en el terminal y la escritura automática se desactiva durante el resto de la sesión, en lugar de reintentarla —sin éxito— en cada palabra.
Algunas ventanas también rechazan el pegado —ciertas aplicaciones Java, campos protegidos. La primera vez que ocurre, la herramienta pasa a modo degradado del mismo modo.
El socket de control
Una sesión en marcha abre un socket Unix privado en $XDG_RUNTIME_DIR/vosk-cli-dictation.sock (modo 0600). La misma orden invocada con --toggle, --finalize, --sleep-toggle, --status o --stop se conecta a ese socket en vez de iniciar una segunda sesión.
Eso es lo que hace que los atajos de teclado del escritorio funcionen en todas partes en Wayland, incluidas las ventanas Wayland nativas que los atajos globales de pynput nunca ven.
Solo se ejecuta una sesión por usuario a la vez; un socket obsoleto dejado por un fallo se detecta y se reemplaza en el siguiente arranque.
Dos capas de corrección de vocabulario
Una palabra equivocada y una palabra mal capitalizada son fallos distintos, y config.yaml mantiene sus correcciones separadas a propósito.
recognition_aliases
El modelo oyó las palabras equivocadas; se sustituyen directamente ("tirez" → "tiret").
custom_vocabulary
El modelo oyó bien pero escribió un nombre en minúscula; se fuerza su grafía ("vosk" → "Vosk").
Tipografía por idioma
text_processing.py aplica las reglas de salida del idioma dictado, no solo sus palabras de puntuación.
- Francés — inserta un espacio fino de no separación (U+202F) antes de
? ! : ;y». - Alemán — usa las comillas
„ "y solo pone mayúscula al inicio de frase. - Español — inserta
¿y¡solo mediante una orden explícita.
Cada conjunto vive en language_settings.<lang>.typography, porque el comportamiento correcto difiere de verdad según el idioma, no solo por el vocabulario.
Traducción de la interfaz
El idioma de la interfaz de la herramienta y su idioma de dictado son dos ajustes distintos.
Idioma de la interfaz
Los textos de la propia herramienta usan la cadena GNU gettext — catálogos .po compilados a .mo, extraídos del fuente Python con Babel.
Idioma de dictado
language_settings en config.yaml rige el idioma que usted habla, sus palabras de puntuación y su tipografía — no el idioma de la interfaz.
Consulte docs/configuration.md para cada clave, y Guías para empezar.