I am using TextToSpeech
to play some long texts sometimes, and I have noticed that since Android 4.1.2
if the text is longer than 4000 chars, it does not play.
I do not get any errors, but the text won't be played. Until now I was able to reproduce this just on Android 4.1.2
(Samsung Galaxy Nexus, Nexus7).
Is this a bug just in 4.1.2
or is this normal (although I did not find any documentation regarding this behaviour)?
Also I have found a post : onUtteranceCompleted() lost if TTS received is too long which indicates different problems with texts longer than 4000 chars.
EDIT: I tried to split my string in 4k length chunks, and send it to TTS
using QUEUE_ADD
and I came across another bug: QUEUE_ADD
does not work, instead it flushes the existing queue, and only the last chunk gets played.
EDIT2 : this is my call to TTS
mTTS.speak(longText, TextToSpeech.QUEUE_FLUSH, null);
MAX_SPEECH_ITEM_CHAR_LENGTH = 4000 in TtsService.java, on 4.1 I see a warn in the code:
@Override
public boolean isValid() {
if (mText == null) {
Log.wtf(TAG, "Got null text");
return false;
}
if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) {
Log.w(TAG, "Text too long: " + mText.length() + " chars");
return false;
}
return true;
}
looks like 2.3 splits the text instead, so teorically your code should work on android < 4.1 and not on newer (I don't known when the split was removed), instead you have the opposite :) that is a bit strange
No problem on 4.4.2... I split my Strings like this...
//textToSpeech can only cope with Strings with < 4000 characters
int dividerLimit = 3900;
if(textForReading.length() >= dividerLimit) {
int textLength = textForReading.length();
ArrayList<String> texts = new ArrayList<String>();
int count = textLength / dividerLimit + ((textLength % dividerLimit == 0) ? 0 : 1);
int start = 0;
int end = textForReading.indexOf(" ", dividerLimit);
for(int i = 1; i<=count; i++) {
texts.add(textForReading.substring(start, end));
start = end;
if((start + dividerLimit) < textLength) {
end = textForReading.indexOf(" ", start + dividerLimit);
} else {
end = textLength;
}
}
for(int i=0; i<texts.size(); i++) {
textToSpeech.speak(texts.get(i), TextToSpeech.QUEUE_ADD, null);
}
} else {
textToSpeech.speak(textForReading, TextToSpeech.QUEUE_FLUSH, null);
}
My solution was to use onUtteranceCompleted(String utteranceId)
to know when the first chunk has finished, and then, feed the next chunk to TextToSpeech
until they are all finished.
@Override
public void onInit(int status) { //On TTS init
//guava Splitter
mChunks=Lists.newLinkedList(Splitter.fixedLength(3999).split(mExtractedText));
mTTS.setOnUtteranceCompletedListener(this);
playNextChunk();
}
private void playNextChunk(){
HashMap<String, String> params = new HashMap<String, String>();
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, ""+mChunks.size());
mTTS.speak(mChunks.poll(), TextToSpeech.QUEUE_FLUSH, params);
}
@Override
public void onUtteranceCompleted(String utteranceId) {
playNextChunk();
}