Problem with isSpeaking() when using Text-to-Speec

2020-03-03 06:04发布

I'm having problem with isSpeaking() method. When passing QUEUE_FLUSH to the speak() method, isSpeaking() works fine. However, when I queue multiple utterances (by passing QUEUE_ADD), the isSpeaking() method starts returning false immediately after more than one utterance have been queued.

Then I stumbled across the source code of the TtsService class and saw this code:

public boolean isSpeaking() {
  return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
}

Does anyone have any idea, why was this method implemented in such way?

2条回答
叼着烟拽天下
2楼-- · 2020-03-03 06:42

I stumbled upon this one, too.

It has been exactly one year since you asked this question and no answer is in the horizon...

So I can only guess that whoever wrote this piece of code intended it to mean

"only if speaking the last utterance".

Does this make sense?

The only problem is that, regardless of whether in QUEUE_ADD or QUEUE_FLUSH mode, empirical observations suggest that there are serious timing race conditions involved:

  1. isSpeaking() will always return false if called in onUtteranceCompleted().
  2. isSpeaking() will always return true if called immediately after the first tts.speak() after an onUtteranceCompleted().
  3. isSpeaking() will always return false if called immediately after a tts.speak() that has at least one more tts.speak()s separating between the last onUtteranceCompleted() and itself .

In a way, it behaves exactly the opposite of the "intended":

"only if speaking the first utterance" (after an onUtteranceCompleted())

Why it behaves likes this is beyond me, but that's at least what LogCat shows.

Also note the following addition to the official documentation:

"Note that a speech item is considered complete once it's audio data has been sent to the audio mixer, or written to a file. There might be a finite lag between this point, and when the audio hardware completes playback."

In other words, this method is totally useless.

查看更多
在下西门庆
3楼-- · 2020-03-03 06:55

There is a broadcast intent when the queue is complete: ACTION_TTS_QUEUE_PROCESSING_COMPLETED.

Here is an example class to register a BroadCastReceiver and use this to determine the isSpeaking state.

Call Init and Shutdown from the relevant places in your activity and use the Speak and isSpeaking functions provided in this class:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;

public class SpeechWrapper {

private static TextToSpeech mTts = null;        
private static boolean isSpeaking = false;
private static BroadcastReceiver receiver = new BroadcastReceiver(){
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED) && mTts != null)
        {
            isSpeaking = false;
        }
    }
};

private static void Speak(String sMessage, int intQueueType, int delay) {
    if (mTts == null || sMessage == null) return;
    sMessage = sMessage.trim();
    isSpeaking = true;
    if (delay > 0) {
        mTts.playSilence(delay, intQueueType, null);
        intQueueType = TextToSpeech.QUEUE_ADD;
    }
    mTts.speak(sMessage, intQueueType, null);
}

public static void Init(Context context) {
    mTts = new TextToSpeech(context, (OnInitListener) context);
    IntentFilter filter = new IntentFilter(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
    context.registerReceiver(receiver, filter);
}

public static void Shutdown() {
    if (mTts != null) {
        mTts.stop();
        mTts.shutdown();
    }
}

public static boolean isSpeaking() {
    return isSpeaking;
}

}
查看更多
登录 后发表回答