Malheureusement la gestion du son sous linux est particulièrement pénible et surtout très compliquée. Cet aspect de l'application RadioK est celui qui demande le plus d'efforts pour obtenir un résultat satisfaisant. La contrainte à respecter pour établir la configuration audio était de rester le plus simple possible tout en permettant de gérer des haut-parleurs ou des casques, différents types de micro, branchés en usb ou non et de rester homogène sur la raspberry et sur des desktops. Pour tenir cette contrainte, sans s'arracher les cheveux, on ne peut guère faire l'économie de la lecture et de la compréhension de la documentation de base sur le sujet.
La première chose à faire consiste à vérifier que la sortie audio fonctionne correctement sur la rpi. On peut tester par exemple dans l'ordre :
Il faut également vérifier que l'entrée audio est correctement prise en compte. Pour ce faire on peut utiliser ce genre de commande :
ma_voix.wav
que l'on doit
pourvoir écouter ensuite avec aplay
.
Pour régler les niveaux de sortie et d'entrée on peut
utiliser la commande alsamixer(1)
depuis un
terminal xterm
. L'interface est des plus
frustes mais elle permet de définir les volumes des
haut-parleurs et du micro.
La solution pour avoir du son sur les haut-parleurs sans utiliser la sortie hdmi et pour pouvoir brancher un micro sur la rpi consiste à utiliser une carte son comme la Logilink 250743. L'installation ne pose guère de problème car le module driver est disponible dans les distributions linux.
La difficulté consiste à bien identifier le nom
du control pour modifier le volume de sortie et
utiliser le même nom - PCM
- sur différents
matériels de manière à avoir les scripts pour trouver la
valeur du volume ou pour la changer qui soient identiques sur
différentes machines.
Ces scripts qui encapsulent amixer get PCM
et
amixer set PCM
sont ainsi portables sans
modification. Mais sur la rpi avec certaines cartes son il
arrive que PCM
n'existe pas. Il faut alors
créer un fichier ~/.asoundrc
pour configurer
la bibliothèque
alsa
.
La syntaxe des fichiers de configuration alsa est
particulièrement imbittable. Ce fichier produit l'effet
escompté :
pcm.softvol { type softvol slave { pcm "cards.pcm.default" } control { name "PCM" card 0 } } pcm.!default { type plug slave.pcm "softvol" }Le contenu de ce fichier est décrit sur cette page.
Une première implémentation a été expérimentée. Elle se base sur les routines disponibles dans la bibliothèque pocket sphinx. Le programme réalisé s'exécute localement sur le processeur de la machine, et il fonctionne en tentant de déchiffrer des mots prononcés en anglais.
Le principe de fonctionnement est le suivant : une liste
de mots à reconnaître est préparée, une fois compilée elle
est fournie en paramètre du programme de reconnaissance
vocale. Celui-ci prend biensûr aussi en entrée le signal
provenant du micro. Il attend en continu les paroles
prononcées devant le micro et génère la transcription en chaîne de
caractères des mots reconnus. Ces mots sont transmis au
server http qui les utilisent pour commander la radio. Les
fichiers de cette implémentation sont dans le répertoire
RADIOK_HOME/vox/ps
.
La liste des mots est stockée dans le fichier
corpus-en.txt
. La version actuelle du
programme travaille en anglais, il y aura peut-être par la
suite une version comprenant le français. La liste
proposée contient plus de mots que nécessaire mais ça
permet d'expérimenter et de choisir ensuite le vocabulaire
le plus efficace. La liste doit être compilée par le
progamme
lmtool
. Les fichiers produits portant des
noms comme nnnn.lm
et nnnn.dic
sont fournis en paramètres du programme de
reconnaissance. À chaque fois que le corpus est modifié
il faut recompiler la liste. On obtient alors de
nouveaux fichiers nnnn.*
.
Ce nom - nnnn
- est assigné à la variable
corpus
dans les scripts listen.sh
et
trywords.sh
il faut donc les mettre à jour.
Le programme de reconnaissance
s'appelle whatusay
(what you say
?). Le fichier source est whatusay.c
. Il
s'agit tout simplement d'une version simplifiée de
pocketsphinx
. Une grande partie du code
qui n'était pas pertinent
pour RadioK a été purement et
simplement supprimée. Inversement du code nécessaire à
la communication avec le server web a été ajouté. Ce
code s'appuie sur la bibliothèque
curl
. Chaque
mot décodé est transmis par une requête http au server.
Le programme whatusay
accepte un certain
nombre d'arguments dont les 2 principaux utilisés pour
RadioK sont -adcdev
et
-url
. Le premier -adcdev
permet
de spécifier le nom du device d'entrée. Là aussi il faut
faire un effort pour comprendre quelle valeur
mettre. Normalement quelque chose comme hw:n
avec n
égal à l'indice du device d'entrée (0 ou 1 ou
plus) doit marcher. Le deuxième argument, spécifique à
RadioK, -url
permet de
définir l'url du serveur auquel envoyer les commandes
interprétées par whatusay
.
Pour refabriquer le programme il faut installer
cmusphinx
et curl
puis lancer
make
dans le répertoire
RADIOK_HOME/vox/ps
.
Le Makefile
a été gardé délibérément très
simple. Il doit éventuellement être modifié en
fonction de la plateforme.
Pour tester le programme simplement sans communiquer avec
le serveur web afin de voir comment l'analyse vocale
fonctionne il suffit de le lancer avec comme url la chaîne
'null'
. Le petit
script trywords.sh
permet de faire des tests
simplement. Une fois lancé il suffit de parler devant le
micro. Les mots compris sont affichés sur la sortie
standard. Le taux de réussite est assez
statisfaisant. L'expérience montre qu'il dépend beaucoup
du volume reçu par le micro : en parlant à voix
suffisamment haute à
une distance comprise entre 10 et 60 cm la plupart des
mots sont compris et plus on s'éloigne du micro plus il
faut parler fort. Le taux de réussite dépend aussi des
mots : plus ils sont long mieux ils sont décodés, le
programme fonctionne mieux avec des mots de 3 syllabes
qu'avec les monosyllables. Dans l'autre sens le décodage
peut se déclencher sur les bruits s'ils sont forts : le
fait d'éternuer à 3 mètres du micro peut activer une commande.
Le programme whatusay
peut communiquer avec
le serveur web qui gère les scripts de contrôle de la
radio. L'url du serveur est par default
http://localhost:18000
mais peut être
spécifiée différemment sur la ligne de commande. On peut
donc contrôller l'application en faisant aussi tourner
whatusay
sur une autre machine que la
raspberry. Le programme génère la requête
/vox/process/mot
pour le serveur à
chaque traitement d'une parole, mot
étant la
transcription de la parole comprise.
Le code du server pour traiter les requêtes de commandes
vocales se trouve dans le fichier vox.js
dans
le sous-répertoire www/kontrol/server
. Les
mots connus sont regroupés en liste de synonymes,
c'est-à-dire conduisant à la même opération. Cela donne
une certaine souplesse pour tester différentes
expressions. Certains mots sont mieux compris ou plus
faciles à prononcer pour un non-anglophone.
Les commandes comprises sont les suivantes:
music, wake up, begin, radio, play, run, start
terminate, shut up, silence, cancel, halt,
stop, quiet, sleep
first, last, next, previous
zero, one, two, three, four, five,
six, seven
more, louder, plus, higher
less, softer, minus, lower
La méthode app.get
extrait le paramètre
word
et regarde dans quel groupe de synomymes
il est présent puis exécute le shell script qui
correspond. La reconnaissance vocale est loin d'être
fiable à 100% aussi il est important de donner un feed
back pour informer l'utilisateur si la commande a été
comprise ou non.
Ce retour est lui aussi sous forme audio. Des phrases ont
été enregistrées et sauvegardées dans des fichiers
.wav
. Elles sont jouées par la commande
aplay(1)
après la prise en compte des
requêtes vocales.
Un des problèmes à résoudre pour la mise en place de cette
fonctionnalité est le réglage des volumes relatifs du son
produit par la radio via mplayer
et du son de
feed back produit par aplay
. Pour que ce feed
back serve à quelque chose son volume doit être légèrement
supérieur à celui de la radio afin d'être entendu
distinctement. La solution
consiste à éditer les fichiers de feed back à l'aide du
programme audacity.
Ce programme est très bien fait et très complet. Il permet de
créer assez facilement les fichiers son dont on a besoin,
de les modifier à sa guise et de régler l'amplitude des
signaux enregistrés.
Sur le GitHub de RadioK dans le
répertoire
sounds
on trouve les fichiers son
utilisés pour le feed back.
Cette version fonctionne relativement correctement. Les performances sur la raspberry sont acceptables. Il faut en général 2 à 3 secondes pour être compris. Cependant l'utilisation de l'anglais comme langue implique une bonne prononciation, en particulier un effort sur l'accent tonique. Par ailleurs l'application peut se déclencher sur des bruits parasites : il faut mieux éviter d'éternuer trop près du micro et de claquer trop fort une porte.
Une deuxième approche a été tentée. Elle se base sur l'utilisation du moteur de reconnaissance vocale de google qui est disponible en ligne. Avec cette technique on peut utiliser le français comme vocabulaire de commande. On devrait d'ailleurs pouvoir utiliser n'importe quelle langue gérée par google en changeant simplement un paramètre lors du lancement du programme. Cette implémentation utilise des requêtes http pour communiquer avec google, c'est à dire pour envoyer un fichier son et recevoir en retour les chaines de caractères résultant de l'interprétation par le moteur de google.
Le code de cette version se trouve dans le
répertoire vox/fr
. Le programme final
s'appelle command
. Les autres programmes ont
été écrits pour tester les différentes étapes à coder pour
arriver au résultat final. L'intégration des différents
morceaux n'a pas été facile car ils sont écrits en
utilisant des routines venant de bibliothèques
différentes, à la documentation limitée et au style de
programmation particulièrement dégoûtant.
La première étape est classique : elle consiste à décoder
les options données sur la ligne de commande.
Mais en général les paramètres par défaut sont suffisants pour
faire fonctionner le programme : on peut le lancer
simplement en tapant : command
.
En revanche il est indispensable d'avoir défini la
variable d'environnement GOOGLE_KEY
. La
valeur est fournie par Google quand on s'identifie comme
utilisateur de l'API - Voir
la documentation.
Ensuite on doit initialiser les paramètres
de curl
pour communiquer et avec google et
avec radiok. Il n'y a rien de très compliqué, il suffit de
lire la doc de
libcurl.
Une fois les initialisations effectuées on entre dans la boucle permanente pour écouter et tenter de reconnaître les paroles prononcées. Pour en sortir on peut dire à haute voix "abandon" ou on peut tout simplement balancer un Control-C.
Au départ de cette boucle on appelle la
routine get_utterance()
qui détecte les sons
entendus. Un son borné par des instants de silence est
stocké sous forme brute dans le tableau
d'échantillons utterance
.
Pour passer ces données son à google il va falloir
d'abord les compresser en utilisant un encodage
flac. Cet algorithme est
adapté aux fichiers son, il compresse les données sans
perte. L'ennui est que la bibliothèque disponible pour
manipuler les fichier à compresser est particulièrement
mal foutue et mal documentée. Après pas mal d'essais je
suis finalement parvenu à quelquechose. La routine
make_flac_encoder()
initialise le traitement
puis enflac_utterance()
effectue la
compression. Le résultat est mis dans un fichier
temporaire /tmp/utterance.flac
que je n'ai
pas réussi à éviter.
La suite du traitement est codé dans la fonction
interpret_flac()
.
Le fichier flac est transmis à google en utilisant
curl_easy_setopt()
pour passer son contenu en
paramètre POST.
La réponse de google est analysée en utilisant la
fonction json_parse()
qui permet de retrouver
les éléments d'un contenu formatté en json. Finalement, si
tout s'est bien passé, on obtient un mot en sortie de
parse_google_content()
. Il correspond à
l'interprétation par google de ce qui a été entendu.
Le mot est ensuite passé au serveur de radiok par la
routine send_command()
. Cette fois aussi on
utilise curl
pour communiquer. Comme pour la
première implémentation le serveur effectue une opération
choisie en fonction du mot reçu.
Après plusieurs semaines d'utilisation je constate que cette implémentation marche très bien et vraiment mieux que l'implémentation basée sur un traitement local. En revanche il faut un peu plus de temps pour obtenir le résultat d'un commande.