Projet SARAH: Implémentation C#
Cet article fait suite à un précédent article dans lequel j’essayais de mettre en place un système de maison intelligente nommée S.A.R.A.H.
Speech to Text en C#
Les API de reconnaissance vocale de Microsoft marchent très bien. Malheureusement, les implémentations sont très mauvaises:
- Windows Speech Recognition Macro (WSR Macro) n’est pas maintenu
- Peu d’évolution des technos depuis 2007-2008 (à part SIRI)
- Les produits Microsoft Tell Me sont très mal décrits
- La doc du Kinect est très dense … beta/1.0/1.5
Bref, comme il n’est pas possible de configurer WSRMacro pour n’utiliser QUE MES macros, j’ai donc décidé de coder ma propre implémentation en C#.
J’ai donc un projet C# qui:
- Monitore un répertoire de Macros
- Charge des grammaires XML
- Envoie une requête HTTP pour traiter l’action reconnue
Ainsi, le traitement des actions peut être déporté sur un serveur Java, NodeJS, … potentiellement sur une autre machine.
Je mettrai les sources en lignes plus tard car pour le moment « c’est pas sec ». Voici l’exécutable.
Grammaire
Le principe de la grammaire Microsoft.Speech est très simple. Des petits morceaux de règle XML se référencent pour former une phrase.
<grammar version="1.0" xml:lang="fr-FR" mode="voice" root="ruleColors" xmlns="http://www.w3.org/2001/06/grammar" tag-format="semantics/1.0"> <rule id="ruleColors" scope="public"> <tag> out.Action=new Object(); out.Action._value = "Sarah Value"; </tag> <item>Je voudrais une voiture <tag>out.Action._attributes.uri="http://127.0.0.1:8080/sarah/car";</tag> </item> <one-of> <item>rouge <tag>out.Action._attributes.speech ="rouge";</tag> </item> <item>blanche <tag>out.Action._attributes.speech ="blanche";</tag> </item> <item>verte <tag>out.Action._attributes.speech ="verte";</tag> </item> </one-of> <tag> out.Action._attributes.speech = "Alors ce sera " + out.Action._attributes.speech; </tag> </rule> </grammar>
La balise <tag> permet d’exécuter du code pour alimenter un objet « Sémantique ».
- L’attribut URI définit l’adresse à attaquer.
- L’attribut speech déclenche un Text to Speech.
- Les attributs sont transformés en paramètre de requête HTTP.
Text to Speech en NodeJS
L’objectif du serveur NodeJS est de dispatcher les commandes vers:
- Un script spécifique
- L’exécution d’un process windows
- L’exécution d’une requête HTTP
- L’exécution d’une réponse vocale
- Un scraping PhantomJS
Synthèse vocale
J’ai trouvé un petit programme qui utilise la synthèse vocale de l’OS (sinon c’est très simple à recoder en C#).
Ensuite, il faut installer des voix de qualité pour avoir un meilleur rendu.
- Loquendo (qui s’est fait racheter par Nuance) a les meilleurs voix. Mais impossible d’avoir plus d’infos …
- Au détour d’un blog, j’ai vu que ScanSoft proposait une voix correcte « Virginie »
Note: pour utiliser des voix 32bit sur un windows 64bit, il faut lancer le sapi.cpl 32bit (%windir%SysWOW64speechSpeechUXsapi.cpl
)
Conclusion
Pour le moment, je n’en suis qu’à l’état de POC. Mais assez satisfait du résultat avec un Kinect.
Prochaines étapes:
- Tester les fonctions natives de Kinect comme microphone.
- Créer plusieurs grammaires fonctionnelles
- Faire un peu de scraping PhantomJS
- Piloter la box Eedomus
- Piloter le karotz en C#/Protobuff (ça marche bien)
Pour « tester » (rien d’extraordinaire et très verbeux):
J’ai fais un peu de refactoring du code car le recognizer est très sensible. Il a un cycle start/stop qu’il ne faut pas titiller sous peine d’exception. J’ai donc fait une sorte de mini automate pour gérer:
– Le chargement de la grammaire
– Les chargements de FileSystem
– …
Côté NodeJS, j’ai codé une mini architecture pour appeler des scripts.
Je mets à jour les archives:
– WSRMacro
– WSRNode
Je pense que le serveur NodeJS va répondre dans la requête HTTP le TTS à lire par le code C#. Ainsi Sarah n’essaiera pas d’écouter sa propre voix ^^.
Après avoir trifouillé pendant plusieurs soirée le programme j’ai réussi à me faire un ordre en 2 étapes:
1. Sarah il y a quoi au ciné à Parly II ?
=> NodeJS => PhantomJS => Allocine
=> Reponse HTTP « Il y a … » => TTS
=> Ecriture d’un bout de grammaire en live
2. Sarah quand passe The Dark Night ?
=> Reload de la Grammaire
=> NodeJS …
Bon le seul petit problème dans ce cas précis c’est prononcer « The Dark Night Rise » comme elle à la française 😛
Mais je suis content de pouvoir mettre a jour « son cerveau ».
PS: J’ai fais tout le framework en générique tout propre je ferais un billet sur le sujet plus tard :-)
Une question que je me pose, j’ai pas encore testé : si on veut obtenir les horaires de films dans un autre cinéma on doit lui redemander ce qu’il y a dans tel ciné pour lui demander les horaires de tel film ou c’est directement possible de demander « Sarah, quand passe The Dark Knight au Pathé Vaise ? » ? Merci 🙂
Mon implémentation se fait en 2 étapes:
1. Qu’est ce qu’il y a au ciné (Identifiant du ciné)
=> Je requête la page du ciné et récupère les infos
=> Je construit une liste XML des films et mémorise le ciné
2. Quand passe le film (Identifiant du film)
=> Je requête le cine, je vais cherché le film dans le DOM (je prends le Nième DIV)
Problèmes:
1. The Dark Night doit se prononcer à la française « the dirque nitch »
2. Je pourrais préciser le ciné mais si le film ne passe pas que faire ?
3. Si le DOM du site change faut refaire le script
Donc en pratique il faudrait coder un truc plus fonctionnel du genre:
– Qu’est qu’il y a « dans mes cinés » en ce moment
– Quand passe machin (dans mes ciné)
– (Ou récupérer la géolocalisation)
Enfin c’est un POC qui démontre qu’on fait ce qu’on veux ce n’est qu’une requête HTTP vers un site puis un parcours du DOM pour aller chercher les infos. Et du chercher/remplacer pour avoir des dates « lisibles ».
La mécanique est générique quand on attaque /phantom/script ca appel le script phantom js du bon nom.
Merci de l’explication =]
Ping : HomePi – Projet domotique « PHP Visions
Ping : HomePi – Projet domotique