13

I have a service where I'm kicking off a speech recognition listener repeatedly so I can have an open ended session for the user to speak. The class also handles the issue in Jelly Bean where a ERROR_SPEECH_TIMEOUT is thrown if no speech is heard in 5 seconds. So basically this works. However if I repeatedly call recognizer.startListening(recognizerIntent) , it silently fails as evidenced by the fact that onBeginningOfSpeech() is never called in this case. Now IF I just do not talk at all my Jelly Bean timeout handler will restart the listener every time without fail. It seems to only fail after onResults() is called because speech WAS heard. After onResults() is called, recognizer.startListening(recognizerIntent) is definitely called, but like I said, nothing happens. When it fails it is random and there is nothing in Logcat to indicate what the issue is. I just don't know what else to try. Hopefully one of you Android Speech recognition experts out there has seen this before...

Minimum: 2.2 Target: Testing on JB : Android 4.1.2


MORE INFO (11-01-13) The 4.3 Update to my HTC One has definitely addressed this issue. My Speech recognition service below is now reliable & accurate. I has run for at least few minutes without error. As for 4.1.2, before my update to 4.3 it seemed to have been working better (did Google change anything on their end?)...I don't know, but it still would miss some words spoken and occasionally without error, just stop listening (onBeginningOfSpeech() is never called in this case). I suppose I will just have to warn my users about Android 4.1.2 with regards to this issue because I have gone as far as I can go in my code.

MORE INFO (09-17-13) Supposedly, there is an Android update (4.3) coming to HTC ones at the end of September (http://www.ubergizmo.com/2013/09/htc-one-to-receive-android-4-3-jelly-bean-update-this-september/). So hopefully that will address this issue on that device. The issue remains though for my app users that are running Android 4.1.2 and stuck on that version for a while. I still don't know what to do in those cases, and hopefully that is the ONLY Android version with this issue. Is there any way to find out how many devices are running 4.1.2??

MORE INFO (09-15-13) In this post here: Google voice recognizer doesn't starts on Android 4.x. The author state states that he is seeing this issue on his HTC one. I also have an HTC one that I'm seeing this issue on (Android 4.1.2) . I wonder if this is unique to the HTC one? (or any device running Android 4.1.2) - I can't confirm as its difficult to test on all the latest devices running JB. The author further states that his Nexxus with 4.2.2 works fine. Can anyone tell me on what device they see this issue?

MORE INFO (9-08-13) Just to confirm there is no issue with my code, I also tested this on Android 2.3.3 and I was able to call onResult() > startListening() 25 times in a row. When targeting Android 4.1.2 I'm never able to get past 3 or 4 calls. I can't believe no one else has run into this issue?

public class VoiceRecogService extends Service
{
    protected AudioManager mAudioManager; 
    protected SpeechRecognizer mSpeechRecognizer;
    protected Intent mSpeechRecognizerIntent;
    protected RecognitionListener mSpeechRecognizerListner;
    //protected final Messenger mServerMessenger = new Messenger(new IncomingHandler(this));

    protected volatile boolean mIsListening;
    protected volatile boolean mIsCountDownOn;

    static final int MSG_RECOGNIZER_START_LISTENING = 1;
    static final int MSG_RECOGNIZER_CANCEL = 2;

    private int mBindFlag;
    private Messenger mServiceMessenger;

    private Context m_ctx;

    private Handler mHandler = new Handler();
    //private boolean m_bReadyForSpeechReceived = false;

    @Override
    public void onCreate()
    {
        super.onCreate();
        m_ctx = this;

        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 

        //do not mute beep when speech listening first kicks off
        Log.d("TESTING: SPEECH SERVICE: CALL START", "onCreate()"); 
        startListening(false);
    }
    private void startListening(boolean bMuteSound){
        Log.d("TESTING: SPEECH SERVICE: startListening()", mIsListening? "true":"false"); 
        if (bMuteSound==true && Build.VERSION.SDK_INT >= 16)//Build.VERSION_CODES.JELLY_BEAN)
        {
            // turn off beep sound  
            mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, true);
        }
        if (!mIsListening)
        {
             //mSpeechRecognizer.startListening(mSpeechRecognizerIntent);
             recognizeSpeechDirectly ();
             mIsListening = true;

        }
    }

    /////////////////////////////////////////////////////////////////////////
    /**
     * lazy initialize the speech recognizer
     */
    private SpeechRecognizer getSpeechRecognizer()
    {
        if (mSpeechRecognizer == null)
        {
            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(m_ctx);
        }
        return mSpeechRecognizer;
    }
    private RecognitionListener getSpeechRecognizerListner()
    {
        if (mSpeechRecognizerListner == null)
        {
            mSpeechRecognizerListner = new SpeechRecognitionListener();
        }
        return mSpeechRecognizerListner;
    }

    private void recognizeSpeechDirectly()
    {
        Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        // accept partial results if they come
        recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);

        recognizeSpeechDirectly(m_ctx,recognizerIntent, getSpeechRecognizerListner(), getSpeechRecognizer());
    }
    public static void recognizeSpeechDirectly(Context context, 
                                               Intent recognizerIntent, 
                                               RecognitionListener listener,
                                               SpeechRecognizer recognizer)
    {
        //need to have a calling package for it to work
        if (!recognizerIntent.hasExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE))
        {
            recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, "com.dummy");
        }

        recognizer.setRecognitionListener(listener);
        recognizer.startListening(recognizerIntent);
    }
    ////////////////////////////////////////////////////////////////////////////

    public void stop()
    {
        if (getSpeechRecognizer() != null)
        {
            getSpeechRecognizer().stopListening();
            getSpeechRecognizer().cancel();
            getSpeechRecognizer().destroy();

            mIsListening = false;
            if (Build.VERSION.SDK_INT >= 16);//Build.VERSION_CODES.JELLY_BEAN)
                mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, false);
        }
    }

    // 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;
            Log.d("TESTING: SPEECH SERVICE: CALL START", "onFinish()"); 
            startListening(true);
        }
    };

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

        if (mIsCountDownOn)
        {
            mNoSpeechCountDown.cancel();
        }
        if (mSpeechRecognizer != null)
        {
            mSpeechRecognizer.destroy();
        }
    }

    protected class SpeechRecognitionListener implements RecognitionListener
    {
        @Override
        public void onReadyForSpeech(Bundle params)
        {
            if (Build.VERSION.SDK_INT >= 16)//Build.VERSION_CODES.JELLY_BEAN)
            {
                mIsCountDownOn = true;
                mNoSpeechCountDown.start();
            }
            Log.d("TESTING: SPEECH SERVICE", "onReadyForSpeech"); 
        }
        @Override
        public void onBeginningOfSpeech()
        {
            // speech input will be processed, so there is no need for count down anymore
            if (mIsCountDownOn)
            {
                mIsCountDownOn = false;
                mNoSpeechCountDown.cancel();
            }               
        }
        @Override
        public void onEndOfSpeech()
        {
            Log.d("TESTING: SPEECH SERVICE", "onEndOfSpeech"); 
        }

        @Override
        public void onBufferReceived(byte[] buffer)
        {
            //Log.d("TESTING: SPEECH SERVICE", buffer + new String(new byte[] {0x63})); 
        }

        @Override
        public void onError(int error)
        {
            if ((error == SpeechRecognizer.ERROR_NO_MATCH)
                    || (error == SpeechRecognizer.ERROR_SPEECH_TIMEOUT)){
                if (mIsCountDownOn)
                {
                    mIsCountDownOn = false;
                    mNoSpeechCountDown.cancel();
                }
                 mIsListening = false;
                 Log.d("TESTING: SPEECH SERVICE: CALL START", "onError()"); 
                 startListening(true);
            }
        }

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

        }

        @Override
        public void onPartialResults(Bundle partialResults)
        {

        }

        @Override
        public void onResults(Bundle results)
        {
             //String str = new String();
             //Log.d(TAG, "onResults " + results);
             ArrayList data = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);

             //if(data.size() >=1){
             //  //check for save it:
             //}

             for (int i = 0; i < data.size(); i++)
             {
                 Log.d("TESTING: SPEECH SERVICE", (String)data.get(i));
             }

             //if no "save it" somewhere in there, then continue:
             if (mIsCountDownOn)
             {
                 mIsCountDownOn = false;
             }
             mIsListening = false;
             Log.d("TESTING: SPEECH SERVICE: CALL START", "onResults()"); 

             startListening(true);
        }
        @Override
        public void onRmsChanged(float rmsdB)
        {

        }
    }
    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }
}
4

4 回答 4

7

我在 Nexus 5 上使用 Android 4.4 KitKat,它也有同样的问题。我认为这可能是一个 Android 错误,因为我还没有看到任何人提供干净的解决方案。
这个解决方案类似于 Andrew_CS 解决方案,但我认为它实际上可以更好地识别。安德鲁斯解决方案不断启动和停止识别器,您必须跟踪事物的状态,例如您是否正在处理语音。这个新的解决方案/解决方法基本上是这样做的:

  • 一旦调用 onResults 并处理我们的结果,我们就会启动一个计时器。
  • 如果一切正常,onReadyForSpeech 将被调用,我们可以取消我们的计时器。
  • 如果事情没有正常工作,我们的计时器结束,我们重新启动语音识别器并再次启动计时器。
  • 确保您也取消了 OnDestroy 方法中的计时器。

如果您找到更好的方法,请告诉我,但这目前似乎效果很好。如果我们真的可以证明这是一个 Android 错误,我希望有人将其提交给 Google。

            @Override
        public void onReadyForSpeech(Bundle params) {
            Log.d("Speech", "onReadyForSpeech: Cancel Timer");
            if(mTimer != null) {
                mTimer.cancel();
            }
        }


        @Override
        public void onResults(Bundle results) {
            //If the timer is available, cancel it so it doesn't interrupt our result processing
            if(mTimer != null){
                mTimer.cancel();
            }
            Log.d("Speech", "onResults");
            //Start processing data
            ArrayList<String> strlist = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
            for (int i = 0; i < strlist.size();i++ ) {
                Log.d("Speech", "YOU SAID: " + strlist.get(i));
            }
            //Start listening again
            Log.d("Speech", "onResults: Start Listening");
            mSpeechRecognizer.startListening(mRecognizerIntent);
            //Start a timer in case OnReadyForSpeech is never called back (Android Bug?)
            Log.d("Speech", "onResults: Start a timer");
            if(mTimer == null) {
                mTimer = new CountDownTimer(2000, 500) {
                    @Override
                    public void onTick(long l) {
                    }

                    @Override
                    public void onFinish() {
                        Log.d("Speech", "Timer.onFinish: Timer Finished, Restart recognizer");
                        mSpeechRecognizer.cancel();
                        mSpeechRecognizer.startListening(mRecognizerIntent);
                    }
                };
            }
            mTimer.start();
        }
于 2013-11-28T03:19:51.020 回答
2

您可以使用:

intent.putExtra("android.speech.extra.DICTATION_MODE", true);

此代码使服务侦听很长时间。

于 2015-07-24T07:27:03.580 回答
1

尝试使用SpeechRecognizer的单个实例,无需在stop()方法上重新创建它。在 onCreate()调用getSpeechRecognizer( ) 并忘记它。但不要忘记在 onDestroy() 方法中进行销毁。

于 2013-09-19T20:35:31.157 回答
1

为了解决这个问题,我使用了一个 CountDownTimer 来递增地调用 cancel 和 startListening。

_timer = new CountDownTimer(300000000, 1000){
    @Override
    public void onTick(long millisUntilFinished){
        if(!_processingSpeech){
            _mSpeechRecognizer.cancel();
            _mSpeechRecognizer.startListening(_mSpeechRecognizerIntent);
        }
    }

    @Override
    public void onFinish(){
        setUpTimer();
    }
};
_processingSpeech = false;
_timer.start();

我在 RecognitionListener 方法中使用了布尔 processingSpeech 来确保在获取/处理输入时没有取消并重新启动。

让我知道这是否对您有帮助或您是否需要更多信息。祝你好运!

于 2013-09-28T21:33:10.880 回答