Reconnaissance vocale Android en tant que service sur Android 4.1 et 4.2

J’ai réussi à faire fonctionner la reconnaissance vocale continue (en utilisant la classe SpeechRecognizer) en tant que service sur toutes les versions d’Android jusqu’à la version 4.1. Ma question concerne le fait qu’elle fonctionne sur les versions 4.1 et 4.2, car il est connu que l’API ne fait pas ce qui est documenté, car quelques secondes après le démarrage de la reconnaissance vocale, si aucune entrée vocale n’a été détectée, alors si le dispositif de reconnaissance vocale meurt silencieusement. ( http://code.google.com/p/android/issues/detail?id=37883 )

J’ai trouvé une question qui propose une solution à ce problème (la reconnaissance vocale cesse d’écouter après quelques secondes ), mais je ne sais pas comment implémenter le gestionnaire requirejs pour cette solution. Je suis conscient du “bip” qui se produira toutes les quelques secondes que cette solution de contournement provoquera, mais obtenir une reconnaissance vocale continue est plus important pour moi.

Si quelqu’un a d’autres solutions de rechange, j’aimerais les entendre aussi.

Ceci est un travail autour de la version Android 4.1.1.

public class MyService extends Service { protected AudioManager mAudioManager; protected SpeechRecognizer mSpeechRecognizer; protected Intent mSpeechRecognizerIntent; protected final Messenger mServerMessenger = new Messenger(new IncomingHandler(this)); protected boolean mIsListening; protected volatile boolean mIsCountDownOn; private boolean mIsStreamSolo; static final int MSG_RECOGNIZER_START_LISTENING = 1; static final int MSG_RECOGNIZER_CANCEL = 2; @Override public void onCreate() { super.onCreate(); mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this); mSpeechRecognizer.setRecognitionListener(new SpeechRecognitionListener()); mSpeechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, this.getPackageName()); } protected static class IncomingHandler extends Handler { private WeakReference mtarget; IncomingHandler(MyService target) { mtarget = new WeakReference(target); } @Override public void handleMessage(Message msg) { final MyService target = mtarget.get(); switch (msg.what) { case MSG_RECOGNIZER_START_LISTENING: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // turn off beep sound if (!mIsStreamSolo) { mAudioManager.setStreamSolo(AudioManager.STREAM_VOICE_CALL, true); mIsStreamSolo = true; } } if (!target.mIsListening) { target.mSpeechRecognizer.startListening(target.mSpeechRecognizerIntent); target.mIsListening = true; //Log.d(TAG, "message start listening"); //$NON-NLS-1$ } break; case MSG_RECOGNIZER_CANCEL: if (mIsStreamSolo) { mAudioManager.setStreamSolo(AudioManager.STREAM_VOICE_CALL, false); mIsStreamSolo = false; } target.mSpeechRecognizer.cancel(); target.mIsListening = false; //Log.d(TAG, "message canceled recognizer"); //$NON-NLS-1$ break; } } } // Count down timer for Jelly Bean work around protected CountDownTimer mNoSpeechCountDown = new CountDownTimer(5000, 5000) { @Override public void onTick(long millisUntilFinished) { // TODO Auto-generated method stub } @Override public void onFinish() { mIsCountDownOn = false; Message message = Message.obtain(null, MSG_RECOGNIZER_CANCEL); try { mServerMessenger.send(message); message = Message.obtain(null, MSG_RECOGNIZER_START_LISTENING); mServerMessenger.send(message); } catch (RemoteException e) { } } }; @Override public void onDestroy() { super.onDestroy(); if (mIsCountDownOn) { mNoSpeechCountDown.cancel(); } if (mSpeechRecognizer != null) { mSpeechRecognizer.destroy(); } } protected class SpeechRecognitionListener implements RecognitionListener { @Override public void onBeginningOfSpeech() { // speech input will be processed, so there is no need for count down anymore if (mIsCountDownOn) { mIsCountDownOn = false; mNoSpeechCountDown.cancel(); } //Log.d(TAG, "onBeginingOfSpeech"); //$NON-NLS-1$ } @Override public void onBufferReceived(byte[] buffer) { } @Override public void onEndOfSpeech() { //Log.d(TAG, "onEndOfSpeech"); //$NON-NLS-1$ } @Override public void onError(int error) { if (mIsCountDownOn) { mIsCountDownOn = false; mNoSpeechCountDown.cancel(); } mIsListening = false; Message message = Message.obtain(null, MSG_RECOGNIZER_START_LISTENING); try { mServerMessenger.send(message); } catch (RemoteException e) { } //Log.d(TAG, "error = " + error); //$NON-NLS-1$ } @Override public void onEvent(int eventType, Bundle params) { } @Override public void onPartialResults(Bundle partialResults) { } @Override public void onReadyForSpeech(Bundle params) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mIsCountDownOn = true; mNoSpeechCountDown.start(); } Log.d(TAG, "onReadyForSpeech"); //$NON-NLS-1$ } @Override public void onResults(Bundle results) { //Log.d(TAG, "onResults"); //$NON-NLS-1$ } @Override public void onRmsChanged(float rmsdB) { } } } 

16/02/2013 – Correction du bip sonore si vous utilisez Text To Speech dans votre application, assurez-vous de désactiver le stream Solo dans onResults

Si vous voulez vraiment mettre en place une écoute continue sans connexion Internet, vous devez considérer les paquets tiers, dont CMUSphinx, consultez la démo Android Pocketsphinx, par exemple, comment écouter efficacement les mots clés hors ligne et réagir aux commandes spécifiques comme une phrase clé “oh puissant ordinateur”. Le code à utiliser est simple:

vous créez un identifiant et ajoutez simplement la recherche de repérage par mot clé:

 recognizer = defaultSetup() .setAcousticModel(new File(modelsDir, "hmm/en-us-semi")) .setDictionary(new File(modelsDir, "lm/cmu07a.dic")) .setKeywordThreshold(1e-5f) .getRecognizer(); recognizer.addListener(this); recognizer.addKeywordSearch(KWS_SEARCH_NAME, KEYPHRASE); switchSearch(KWS_SEARCH_NAME); 

et définir un auditeur:

 @Override public void onPartialResult(Hypothesis hypothesis) { Ssortingng text = hypothesis.getHypstr(); if (text.equals(KEYPHRASE)) // do something } 

Pour ceux d’entre vous qui essaient de faire taire le bip sonore, reclasser la réponse @HoanNguyen qui est très bonne mais faire attention comme dit dans le set api setStreamSolo est cumulatif donc s’il ya une erreur dans la reconnaissance vocale et une erreur est appelée ( par exemple pas de connexion internet), alors setStremSolo true est appelé encore et encore, ce qui aura pour effet de rendre votre application silencieuse sur tout le téléphone (très mauvais)! La solution consiste à append le paramètre setStremMute (false) au paramètre speechRecognizer onError.

Découvrez mon application de démonstration: https://github.com/galrom/ContinuesVoiceRecognition

Je recommande d’utiliser à la fois PockeySphix et SpeechRecognizer.