Architecture et modèle d'exécution
Threads et file d'attente, la cascade d'injection de texte, le socket de contrôle et la typographie par langue.
Trois threads, une file d’attente
La capture audio, la reconnaissance et l’interface en terminal tournent sur des threads daemon séparés dans src/main.py, reliés par une queue.Queue thread-safe.
audio_capture.py lit du PCM mono 16 kHz issu de PortAudio et l’empile ; recognition.py le tire dans un KaldiRecognizer local.
Quand la reconnaissance prend du retard, le plus ancien bloc audio est abandonné plutôt que de laisser filer la latence — le compteur de débordement apparaît dans --status et --diagnose.
Injection de texte : un collage par le presse-papiers, avec cascade de moteurs
Une fois que text_processing.py a mis en forme un énoncé reconnu, system_control.py le place dans le presse-papiers et simule un collage. Le moteur est choisi une seule fois au démarrage.
| Voie | Outils | Condition |
|---|---|---|
| Wayland | wtype + wl-clipboard | Indépendant de la disposition du clavier (wtype travaille par keysym). Requiert un compositeur qui expose le protocole de clavier virtuel (bureaux wlroots) ; sinon repli sur ydotool. |
| X11 | xdotool + xclip | Voie standard sous X11. |
| Repli | ydotool + wl-clipboard | Injecte au niveau du pilote (/dev/uinput) via le démon ydotoold ; keycodes dépendants de la disposition du clavier ; requiert l’appartenance au groupe input. |
Le moteur de frappe a besoin d’un outil de presse-papiers à ses côtés : wl-clipboard sous Wayland — requis même sur le repli ydotool — et xclip sous X11. Les paquets natifs tirent wl-clipboard (le paquet Debian recommande en plus xclip) ; une installation depuis les sources les ajoute elle-même. Sans cela, le texte dicté n’atteint jamais le presse-papiers et le collage réinsère le dernier contenu copié à la main.
Le contenu précédent du presse-papiers est relu puis restauré après le collage (réglable par clipboard.restore, actif par défaut). Un contenu non textuel — image, fichiers — est laissé intact. Sous Wayland avec une application GTK, la restauration peut devancer la lecture du presse-papiers par la fenêtre ; clipboard.defer_restore diffère la restauration à la fin de la session de dictée pour que le collage ne reprenne jamais l’ancien contenu.
Si aucun moteur adapté à la session courante ne fonctionne, l’outil ne plante pas et n’abandonne rien en silence : il passe en mode dégradé. Le texte reconnu continue de s’afficher dans le terminal et la frappe automatique est désactivée pour le reste de la session, plutôt que d’être retentée — sans succès — à chaque mot.
Quelques fenêtres refusent aussi le collage — certaines applications Java, des champs protégés. À la première de ces situations, l’outil passe de la même façon en mode dégradé.
Le socket de contrôle
Une session en cours ouvre un socket Unix privé à $XDG_RUNTIME_DIR/vosk-cli-dictation.sock (mode 0600). La même commande appelée avec --toggle, --finalize, --sleep-toggle, --status ou --stop se connecte à ce socket au lieu de démarrer une seconde session.
C’est ce qui fait fonctionner les raccourcis clavier du bureau partout sous Wayland, y compris pour les fenêtres Wayland natives que les raccourcis globaux pynput ne voient jamais.
Une seule session tourne par utilisateur à la fois ; un socket laissé par un plantage est détecté et remplacé au démarrage suivant.
Deux niveaux de correction du vocabulaire
Un mot faux et un mot mal capitalisé sont deux défauts distincts, et config.yaml garde leurs corrections séparées à dessein.
recognition_aliases
Le modèle a entendu les mauvais mots ; on les remplace directement ("tirez" → "tiret").
custom_vocabulary
Le modèle a bien entendu mais a mis un nom en minuscule ; on force sa casse ("vosk" → "Vosk").
Typographie par langue
text_processing.py applique les règles de sortie de la langue dictée, pas seulement ses mots de ponctuation.
- Français — insère une espace fine insécable (U+202F) avant
? ! : ;et». - Allemand — utilise les guillemets
„ "et ne met la majuscule qu’en début de phrase. - Espagnol — insère
¿et¡seulement sur commande explicite.
Chaque jeu vit sous language_settings.<lang>.typography, parce que le comportement correct diffère vraiment d’une langue à l’autre, pas seulement par le vocabulaire.
Traduction de l’interface
La langue de l’interface de l’outil et sa langue de dictée sont deux réglages distincts.
Langue de l'interface
Les chaînes de l’outil lui-même passent par la chaîne GNU gettext — catalogues .po compilés en .mo, extraits du source Python avec Babel.
Langue de dictée
language_settings dans config.yaml régit la langue que vous parlez, ses mots de ponctuation et sa typographie — pas la langue de l’interface.
Voir docs/configuration.md pour chaque clé, et Guides pour démarrer.