How to pause android.speech.tts.TextToSpeech?

Solution 1:

The TTS SDK doesn't have any pause functionality that I know of. But you could use synthesizeToFile() to create an audio file that contains the TTS output. Then, you would use a MediaPlayer object to play, pause, and stop playing the file. Depending on how long the text string is, it might take a little longer for audio to be produced because the synthesizeToFile() function would have to complete the entire file before you could play it, but this delay should be acceptable for most applications.

Solution 2:

I used splitting of string and used playsilence() like below:

public void speakSpeech(String speech) {

    HashMap<String, String> myHash = new HashMap<String, String>();

    myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "done");

    String[] splitspeech = speech.split("\\.");

    for (int i = 0; i < splitspeech.length; i++) {

        if (i == 0) { // Use for the first splited text to flush on audio stream

            textToSpeech.speak(splitspeech[i].toString().trim(),TextToSpeech.QUEUE_FLUSH, myHash);

        } else { // add the new test on previous then play the TTS

            textToSpeech.speak(splitspeech[i].toString().trim(), TextToSpeech.QUEUE_ADD,myHash);
        }

        textToSpeech.playSilence(750, TextToSpeech.QUEUE_ADD, null);
    }
}

Solution 3:

You can make the TTS pause between sentences, or anywhere you want by adding up to three periods (".") all followed by a single space " ". The example below has a long pause at the beginning, and again before the message body. I'm not sure that is what you are after though.

    private final BroadcastReceiver SMScatcher = new BroadcastReceiver() {

    @Override
    public void onReceive(final Context context, final Intent intent) {
        if (intent.getAction().equals(
                "android.provider.Telephony.SMS_RECEIVED")) {
            // if(message starts with SMStretcher recognize BYTE)
            StringBuilder sb = new StringBuilder();

            /*
             * The SMS-Messages are 'hiding' within the extras of the
             * Intent.
             */
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                /* Get all messages contained in the Intent */
                Object[] pdusObj = (Object[]) bundle.get("pdus");
                SmsMessage[] messages = new SmsMessage[pdusObj.length];
                for (int i = 0; i < pdusObj.length; i++) {
                    messages[i] = SmsMessage
                            .createFromPdu((byte[]) pdusObj[i]);
                }
                /* Feed the StringBuilder with all Messages found. */
                for (SmsMessage currentMessage : messages) {
                    // periods are to pause
                    sb.append("... Message From: ");
                    /* Sender-Number */
                    sb.append(currentMessage.getDisplayOriginatingAddress());
                    sb.append(".. ");
                    /* Actual Message-Content */
                    sb.append(currentMessage.getDisplayMessageBody());
                }
                // Toast.makeText(application, sb.toString(),
                // Toast.LENGTH_LONG).show();
                if (mTtsReady) {
                    try {
                        mTts.speak(sb.toString(), TextToSpeech.QUEUE_ADD,
                                null);
                    } catch (Exception e) {
                        Toast.makeText(application, "TTS Not ready",
                                Toast.LENGTH_LONG).show();
                        e.printStackTrace();
                    }
                }
            }

        }
    }
};

If you omit the space after the last period it will (or may) not work as expected.

Solution 4:

In the absence of a pause option, you can add silence for the duration of when you want to delay the TTS Engine speaking. This of course would have to be a predetermined 'pause' and wouldn't help to include functionality of a pause button, for example.

For API < 21 : public int playSilence (long durationInMs, int queueMode, HashMap params)

For > 21 : public int playSilentUtterance (long durationInMs, int queueMode, String utteranceId)

Remember to use TextToSpeech.QUEUE_ADD rather than TextToSpeech.QUEUE_FLUSH otherwise it will clear the previously started speech.

Solution 5:

I used a different approach.

  1. Seperate your text into sentences
  2. Speak every sentence one by one and keep track of the spoken sentence
  3. pause will stop the text instantly
  4. resume will start at the beginning of the last spoken sentence

Kotlin code:

class VoiceService {

    private lateinit var textToSpeech: TextToSpeech    

    var sentenceCounter: Int = 0
    var myList: List<String> = ArrayList()

    fun resume() {
        sentenceCounter -= 1
        speakText()
    }

    fun pause() {
        textToSpeech.stop()
    }

    fun stop() {
        sentenceCounter = 0
        textToSpeech.stop()
    }

    fun speakText() {

        var myText = "This is some text to speak. This is more text to speak."

        myList =myText.split(".")

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, null, utteranceId)
            sentenceCounter++
        } else {
            var map: HashMap<String, String> = LinkedHashMap<String, String>()
            map[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = utteranceId
            textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, map)
            sentenceCounter++
        }
    }

    override fun onDone(p0: String?) {
        if (sentenceCounter < myList.size) {
            speakText()
        } else {
            speakNextText()
        }
    }
}