Reconnaissance vocale

Création d’une application android pour le système de domotique qui sera capable de reconnaitre la parole pour actionner certaines actions. Le but est de remplacer “Tasker + auto voice” par une application fait maison et plus plus adapte au système.

on utilise l API google et c est google qui fait tout le boulot de décodage de la parole (et plutôt bien) :

public void speak(View view) {
 Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
// Specify the calling package to identify your application
 intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getClass().getPackage().getName());
// Given an hint to the recognizer about what the user is going to say
 //There are two form of language model available
 //1.LANGUAGE_MODEL_WEB_SEARCH : For short phrases
 //2.LANGUAGE_MODEL_FREE_FORM  : If not sure about the words or phrases and its domain.
 intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
// Specify how many results you want to receive. The results will be sorted where the first result is the one with higher confidence.
 int noOfMatches = 4;
 intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, noOfMatches);
 //Start the Voice recognizer activity for the result.
 startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
 }

et on attend la réponse de l’activité :

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 if (requestCode == VOICE_RECOGNITION_REQUEST_CODE)
//If Voice recognition is successful then it returns RESULT_OK
 if(resultCode == RESULT_OK) {
ArrayList<java.lang.String> textMatchList = data
 .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
if (!textMatchList.isEmpty()) {
 mlvTextMatches
 .setAdapter(new ArrayAdapter<java.lang.String>(this,
 android.R.layout.simple_list_item_1,
 textMatchList));
 sendMsgToServer(textMatchList);
}

ensuite avec les résultats (plusieurs phrases correspondant a ce que google a décoder) on appel une fonction “sendMsgToServer” qui va envoyer ses phrases aux serveur python qui gère tout la centrale domotique.

public void sendMsgToServer(ArrayList<java.lang.String> aStrings){
String url = "http://192.168.0.6/Sender/XbeeWrapper.php?iCmdType=CMD_SPEAK&iCmdStrings=";
 String aCmd = combine(aStrings,"_");
 url = url + aCmd;
 url = url.replace(' ','-');
 url = Normalizer.normalize(url, Normalizer.Form.NFD);
 url = url.replaceAll("[^\\p{ASCII}]", "");
 grabURL(url);
 }

Attention : pour faire cela on utilise une autre classe qui dérive de classe asynch car dans android toutes les opérations longues doivent être faites ailleurs que dans le thread principale.

private class GrabURL extends AsyncTask<String, Void, Void> {

je vais pas détailler ici pkoi et comment car il existe une multitude de référence sur le sujet sur internet. Bref le serveur python reçoit les phrases et parcours tous les objets du réseaux domotique pour savoir auquel est destine la commande :

def TranslateVocalAction(self,Sentences, Devices):
 logging.warning("Translate : " + str(Sentences))
 #Weight, Device ID, CommandID
 aBestDeviceMatch=(0,0,0)
 for aOneObj in Devices.registeredDevices:
 (aWeight,aCmdId)=aOneObj.getDeviceWeightSpeech(Sentences)
 logging.warning ("DeviceId : " + str(aOneObj.id) + " has weight : " + str(aWeight))
 if(aWeight > aBestDeviceMatch[0]):
 aBestDeviceMatch=(aWeight,aOneObj.id,aCmdId)
 logging.warning("Device is : " + str(aBestDeviceMatch))
 self.SendMessage( str(aBestDeviceMatch[2]),"VoiceAction",Devices)
 return (Devices.getDevice(aBestDeviceMatch[1]).description,aBestDeviceMatch[2])

Pour cela il parcours la description et les commandes possible de tous les objets qui se présentes sous la forme :

VoletSalon = InterupteurMultiStable()
 VoletSalon.id =5
 VoletSalon.description="volets salon"
 VoletSalon.InPossibleCmd ={ "10" : "off fermer ferme","9" : "on ouvre ouvrir","25" : "stop arreter arrete"}
 VoletSalon.InActionsCommands={ "9" : "self.turnOn(9)","10" : "self.turnOff(10)","25" : "self.turnOff(10)"}
 self.registeredDevices.append(VoletSalon)
LumiereSalonHalogene = InterupteurBiStable()
 LumiereSalonHalogene.id =6
 LumiereSalonHalogene.description="lumiere halogene salon"
 LumiereSalonHalogene.InPossibleCmd ={ "13" : "off eteint eteindre","14" : "on allume allumer"}
 LumiereSalonHalogene.InActionsCommands={ "13" : "self.turnOff(13)","14" : "self.turnOn(14)"}
 self.registeredDevices.append(LumiereSalonHalogene)
....

et donne un poids pour chaque objet en fonction de sa description et de ses possibles commandes. Le poids dépend du nombre de mots commun entre les strings et les mots présent dans la phrase (pondéré par leur longueur)

Ensuite il exécute la commande qui a le poids le plus grand. C’est assez facile en fin de compte. En parallèle le serveur python renvoi la commande exécuté et l’objet associe :

return (Devices.getDevice(aBestDeviceMatch[1]).description,aBestDeviceMatch[2])

Ces infos sont ensuite affiches sur la UI du telephonne portable pour que l utilisateur puisse vérifier :

protected void onPostExecute(Void unused) {
 Dialog.dismiss();
 if (Error != null) {
 Toast.makeText(VoiceRecognitionActivity.this, Error, Toast.LENGTH_LONG).show();
 } else {
 Toast.makeText(VoiceRecognitionActivity.this, "Source: " + Content, Toast.LENGTH_LONG).show();
 }
 }

Je ne détaille pas bcp plus car tous le code est tjs visible au même endroit sur le dépôt central de mon projet domotique et assez court pour être compris facilement.

Je ferais une vidéo du système en action ASAP.