Voice Recognition stops listening after a few seconds

放肆的年华 提交于 2019-11-27 03:20:45

The only solution that will for sure get around this issue is to use a 3rd party service. 4.1.1 and 4.2 both rely on a version of the speech recognition service that does not adhere to the documented behavior in that the service running it dies silently.

If you do not wish to use a 3rd party API, and you need to account for this service death in some manner, it is possible but it's not pretty or ideal.

Once the service dies, none of the following methods will ever be called :

  • onBeginningOfSpeech
  • onError
  • onResults
  • onEndOfSpeech

But if onBeginningOfSpeech is called before the service dies, you can be assured that either onError or onEndOfSpeech will eventually be called.

Therefore, if all you want is to be sure you are made aware of the life and death of the service in Jellybean the workaround for this problem in the built-in SpeechRecognizer is to do the following:

  • Create a boolean flag like isSpeechRecognizerAlive.
  • Any time you start up the SpeechRecognizer, set the above flag to false.
  • In onBeginningOfSpeech, if it is called, set isSpeechRecognizerAlive to true.
  • Maintain a Handler that, on a 4 second delay will check the status of isSpeechRecognizerAlive. If it is false, manually kill the SpeechRecognizer instance. If is is true, do nothing. The normal flow will take care of things for you.

Why this is not an ideal solution to maintain a continuous speech recognition setup

It wasn't directly stated in your question but a few people want to do this so they can have continuous speech recognition. This is not really a good way to do that in 4.1.1 and 4.2 because Google's SpeechRecognition service now kicks off with a non-optional "bloop" sound effect. There appears to be no way to turn this sound off. Nothing is listed in the API to control it. Your users WILL NOT appreciate being "blooped" at on a 4 second repeating loop.

Kaarel

You could try to supply the following RecognizerIntent extras:

  • EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS
  • EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS
  • EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS

although none of them really fits in this case. Android simply doesn't offer a "WAIT_UNTIL_I_START_SPEAKING" extra. It's not a bug (in Jelly Bean or where ever), it's simply a lack of a feature. And even if such an extra is added in a future version of Android there might not be a guarantee that the speech recognition app (such as Google Voice Search) actually implements this. The RecognizerIntent documentation contains plenty of extras with unspecified behavior.

Your best option might be to relaunch the recognizer as soon as you get onEndOfSpeech or onError.

Josh

I have made a Service that bears an Audio to Speech Recognizer that simulates being continuous by restarting itself everytime there is an error or result. As you can see, my service listens to a Broadcast receiver in order to start/stop continuous ASR (it is battery expensive so I recommend you run this continuous ASR service only while your relevant UI is in the foreground). The results of the ASR are also broadcasted to the rest of the app. You can ignore the broadcasting, the service and the Recognizer Listener are the main idea. DONT forget the INTERNET permission and the manifest, and the service declaration:

<service android:name=".speechRecognitionService" />

SpeechRecognitionService:

    /**
     * Created by Josh on 22/07/15.
     *  This service bears an Audio to Speech recognizer (ASR), once this service is started,
     *  it listens a broadcast called "asrService".
     *  the Service starts ASR when it receives a "START-ASR" value inside the "message" parameter of its broadcast receiver
     *  the Service stopss ASR when it receives a  "STOP-ASR" value inside the "message" parameter of its broadcast receiver
     Example:
     Intent intent = new Intent("asrService");
     intent.putExtra("message", "STOP-ASR");
     LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
     Once the ASR Listener that this service bears is running, it will broadcast the results is gets.
     To catch ASR results, implement a Broadcast receiver that listens to app.asrResult="ASRresult", for example:

     private BroadcastReceiver ASRReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String message = intent.getStringExtra("message");
            if(message!=null) {
                app.logwhite("ASR says: "+message);
            }
        }
    };
     LocalBroadcastManager.getInstance(this).registerReceiver(ASRReceiver, new IntentFilter(app.asrResult));

     Unregister the broadcast receiver likewise:
     LocalBroadcastManager.getInstance(this).unregisterReceiver(ASRReceiver);
     */

    public class speechRecognitionService extends Service {

        private static speechRecognitionListenerJosh speechReconListener;
        private static SpeechRecognizer mSpeechRecognizer=null;
        private static Intent mSpeechRecognizerIntent;
        private static boolean mIslistening=false;


        //======== BROADCAST RECEIVERS
        // handler for received Intents for the "my-event" event
        private BroadcastReceiver startASRReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String message = intent.getStringExtra("message");
                if(message.equals("START-ASR")) {
                    if (mIslistening == false) {


                        if (mSpeechRecognizer != null) {
                            mSpeechRecognizer.destroy();
                            mSpeechRecognizer = null;
                        }
                        app.logy("==BROADCAST Rx: START_ASR");

                        mSpeechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
                        mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "en-US");
                        mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
                        //mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
                        //mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE ,   this.getPackageName());
                        mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, 5000);
                        mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS, 5000);
                        //mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
                        //mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);

                        mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(getApplicationContext());
                        speechReconListener = new speechRecognitionListenerJosh();
                        mSpeechRecognizer.setRecognitionListener(speechReconListener);

                        mSpeechRecognizer.startListening(mSpeechRecognizerIntent);
                    } else {
                        app.logy("==BROADCAST Rx: STOP_ASR");
                        mSpeechRecognizer.stopListening();
                        mSpeechRecognizer.destroy();
                        speechReconListener = null;
                    }
                }

                if(message.equals("STOP-ASR")){
                    app.logy("==BROADCAST Rx: STOP_ASR");
                    mSpeechRecognizer.stopListening();
                    mSpeechRecognizer.destroy();
                    speechReconListener = null;
                }
            }
        };


        @Override
        public void onCreate() {
            super.onCreate();

            mSpeechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
            mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "en-US");
            mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL  ,   RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
            //mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
            mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE ,   this.getPackageName());
            mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS, 4000);
            mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
            speechReconListener = new speechRecognitionListenerJosh();
            mSpeechRecognizer.setRecognitionListener(speechReconListener);

            LocalBroadcastManager.getInstance(this).registerReceiver(startASRReceiver, new IntentFilter("asrService"));
            app.toastlog("==ASR Service - CREATED");
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            LocalBroadcastManager.getInstance(this).unregisterReceiver(startASRReceiver);

            if (mSpeechRecognizer != null){
                mSpeechRecognizer.destroy();
                mSpeechRecognizer=null;
            }
            app.toastlog("==ASR Service - DESTROYED");
        }

        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }

        @Override //rather not use, runs before onStart
        public int onStartCommand(Intent intent, int flags, int startId) {
            return super.onStartCommand(intent, flags, startId);
        }

        @Override
        public void onStart(Intent intent, int startId) {
            super.onStart(intent, startId);
            app.toastlog("==ASRservice - onStart");
        }



        private class speechRecognitionListenerJosh  implements RecognitionListener {

            @Override
            public void onBeginningOfSpeech() {
                mIslistening=true;
                app.loge("=ASRListener - onBeginingOfSpeech");
            }

            @Override
            public void onBufferReceived(byte[] buffer){

            }

            @Override
            public void onEndOfSpeech(){
                app.loge("=ASRListener - onEndOfSpeech");
            }

            @Override
            public void onError(int error) {
                mIslistening=false;
                String code= Integer.toString(error);

                if(error==SpeechRecognizer.ERROR_CLIENT){ // 5
                    code="ERROR_CLIENT";
                    mIslistening=false;
                    mSpeechRecognizer.destroy();
                    Intent intent = new Intent("asrService");
                    intent.putExtra("message", "START-ASR");
                    LocalBroadcastManager.getInstance(app.appContext).sendBroadcast(intent);
                }

                if(error==SpeechRecognizer.ERROR_SPEECH_TIMEOUT){ // 6
                    code="SPEECH_TIMEOUT";
                    mSpeechRecognizer.stopListening();
                    mIslistening=false;
                    Intent intent = new Intent("asrService");
                    intent.putExtra("message", "START-ASR");
                    LocalBroadcastManager.getInstance(app.appContext).sendBroadcast(intent);
                    //Usualy bounces back to ERROR_CLIENT.
                }
                if(error==SpeechRecognizer.ERROR_NO_MATCH){ // 7
                    code="ERROR_NO_MATCH";
                    mIslistening=false;
                    Intent intent = new Intent("asrService");
                    intent.putExtra("message", "START-ASR");
                    LocalBroadcastManager.getInstance(app.appContext).sendBroadcast(intent);
                    //Usually bounces back to ERROR_CLIENT.
                }

                app.loge("=ASRListener - ASR Error: "+code);
                /*
                // 1 = NETWORK_TIMEOUT
                // 2 = ERROR_NETWORK
                // 3 = ERROR_AUDIO
                // 4 = ERROR_SERVER
                // 5 = ERROR_CLIENT
                // 8 = ERROR_RECOGNIZER_BUSY
                // 9 = ERROR_INSUFFICIENT_PERMISSIONS
                */

            }

            @Override
            public void onEvent(int eventType, Bundle params){

            }

            @Override //Somehow doesn't trigger upon partial results
            public void onPartialResults(Bundle partialResults){
                ArrayList<String> results=partialResults.getStringArrayList("EXTRA_PARTIAL_RECOGNITION");
                if(results!=null) {
                    if (results.size() > 0) {
                        app.logwhite("=== ASR Partial Results: " + results);
                    }
                }
            }

            @Override
            public void onReadyForSpeech(Bundle params){
                app.loge("=ASRListener - onReadyForSpeech, LISTENING  (((( ");
            }

            @Override
            public void onResults(Bundle resultBundle){
                app.loge("=ASRListener - onResults");
                ArrayList<String> result = resultBundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
                if(result!=null) {  //when speaking timeout happen, results is null

                    // matches are the return values of speech recognition engine

                    if (result.size() > 0) {
                        app.logwhite("=== ASR Results:");
                        app.logwhite(result.get(0));
                        //BC name, message
                        app.broadcast(app.asrResult,result.get(0)); //The result is broadcast to the entire app
                    }

                }

                mIslistening=false;
                Intent intent = new Intent("asrService");
                intent.putExtra("message", "START-ASR");
                LocalBroadcastManager.getInstance(app.appContext).sendBroadcast(intent);
            }

            @Override
            public void onRmsChanged(float rmsdB){
                //VOLUME VUmeter!!!!
            }
        }
    }

I was facing the same problem for a while and I could not find the correct working answer anywhere. To make the listening happen infinitely, post onError() or onResults() I was calling speechRecognizer.startListening(intent); again after re-instantiating the speechRecognizer and re-setting the intent object.

But as you mentioned it eventually used to go off after listening for a while.

Here is what I tried and it worked for me.

I just made sure that before recalling the speechRecognizer.startListening(intent); I would just call

speechRecognizer.stopListening();
speechRecognizer.destroy();

and go about doing things mentioned in the first para.

So far it has been working fine for me.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!