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
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();
}
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);
}
Related
This is the code which should generate 6 columns but instead generating 5.
Please point out any logical error in the code due to which desired output cannot be obtained.
sem1Data= new String[18];
sem2Data= new String[18];
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int stdA = 0, stdB = 0;
int counter = 0;
int colCounter = 1;
LabName = editText.getText().toString();
setLabRef = database.getReference("/" + LabName);
String colName;
for (int i = 0; i <6; i++) {
colName = "col" + colCounter;
//Toast.makeText(Activity1.this, colCounter, Toast.LENGTH_SHORT).show();
if ( colCounter%2 == 0) {
for ( ; stdB<18; stdB++)
{
if(counter<6)
{
setLabRef.child(colName).child(String.valueOf(counter)).setValue(sem2Data[stdB]);
counter++;
}
else
{
counter=0;
colCounter++;
break;
}
}
}
else {
for ( ; stdA<18; stdA++)
{
if (counter<6){
setLabRef.child(colName).child(String.valueOf(counter)).setValue(sem1Data[stdA]);
counter++;
}
else {
counter=0;
colCounter++;
break;
}
}
}
}
}
});
The expected result should be 6 generated columns in firebase but instead only five are generated.
firebase screenshot
In first loop change validation block to i <=6 or i < 7
for( int i = 0; i <= 6; i++)
UPD:
if(counter<7)
I'm not sure what is the purpose of your code but here is my opinion , it might help you to clear things out.
if you will add the line Log.d(TAG, "Log track : stdB : " + stdB +" stdA : " +stdA); At the beginning of your for-loop , you will see that the values of stdA and stdB never set to zero in they just keep increasing according to the logic of your code , actually the both end with the values :
Log track : stdB : 12 stdA : 18
now , if you will look at the case where colCounter = 5 , i = 4 the value of stdA is 12
.
Thats mean that the for-loop at the else section will run (colCounter mod 2 is not zero) only 6 times and by that will not execute:
else
{
counter=0;
colCounter++;
break;
}
and by that you are missing the increase of colCounter from 5 to 6 .
I tried to increase++ a number (i) each time the distance between the camera and my face is between 40 cm and 80 cm. Unfortunately when it happen the textview shows (The number of occurrence is: 50). Help me please
public void update(final Messsage msg) {
for(int i = 0; i < 50; ++i) {
if (msg.getDistanceToFace() > 40 && msg.getDistanceToFace() < 80) {
textView.setText("The number of occurrence is: " + i);
textView.setTextColor(Color.GREEN);
}
}
}
you should probably add a break after you set the text, the way you coded it will set a text for all the iteration of i but you will only see the last one (50)
If you only want to increase the number when the face is a certain length away, shouldn't you do something more on the lines of this?
public void update(final Messsage msg) {
int i = 0;
if (msg.getDistanceToFace() > 40 && msg.getDistanceToFace() < 80) {
i++;
textView.setText("The number of occurrence is: " + i);
textView.setTextColor(Color.GREEN);
}
}
Maybe you'll need to have that i a global variable or an argument to the function, not sure without having more of the code.
If you don't want the i to increase really fast (because it increments each time the update function is called and the phone is close to the face), a simple code to do that would be something like
public void update(final Messsage msg) {
boolean isCloseToFace = false;
int i = 0;
if (msg.getDistanceToFace() > 40 && msg.getDistanceToFace() < 80) {
if (!isCloseToFace) {
i++;
isCloseToFace = true;
}
textView.setText("The number of occurrence is: " + i);
textView.setTextColor(Color.GREEN);
} else {
isCloseToFace = false;
}
}
I have an app for people with low vision that relies heavily on TTS. However for some reason when I use the speak method TTS randomly skips the first few letters of a sentence or speaks the first few letters in a ver low volume and the rest in a normal volume.
Any idea why this might happen?
This is my current code:
public class SpeechHelper implements TextToSpeech.OnInitListener {
private Context context = null;
private TextToSpeech tts;
public SpeechHelper(Context context)
{
this.context = context;
try {
tts = new TextToSpeech(context, this);
} catch(Exception e) {
Log.e("Phone Features Exception","Couldn't initiate TTS", e);
}
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.getDefault());
}
}
public void speak(String s, int mode, String messageID) {
Log.d("VOLUME", "getStreamVolume " + am.getStreamVolume(AudioManager.STREAM_MUSIC)); // Always 15
Log.d("VOLUME", "isMusicActive " + (am.isMusicActive() ? "true" : "false")); // Always false
Log.d("VOLUME", "isVolumeFixed " + (am.isVolumeFixed() ? "true" : "false")); // Always false
Log.d("VOLUME", "isSpeakerphoneOn: " + (am.isSpeakerphoneOn() ? "true" : "false")); // Always false
Log.d("VOLUME", "getMode: " + am.getMode()); // Always 0
HashMap<String, String> params = new HashMap<String, String>();
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, messageID);
tts.speak(s, mode, params);
}
}
I've noticed the problem always happens on the next 5 seconds after making a call or when unlocking the phone to the app.
Either you are doing something wrong or Android is doing something wrong. If I had to put money on it I would say it is you.
Start by just calling
tts.speak("the quick brown fox jumps over the lazy dog", tts.QUEUE_FLUSH, null);
to see if you can reproduce the problem in a test app (I couldn't). If you can, then I'd be very curious as to what phone/OS you are running on . Your problem probably lies with truncated strings, or something else programmatically messing with volume.
Updated question: I am trying to connect to a terminal emulator using a library in android, this will connect to a serial device and should show me sent/received data. I should be able to send data over the connection via a text box below the terminal or by typing in the terminal itself and hitting enter on the keyboard in both cases.
When I was sending data via textbox I had to append \n to the data to get to a new line when I pressed the enter key like so:
mEntry = (EditText) findViewById(R.id.term_entry);
mEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
/* Ignore enter-key-up events. */
if (event != null && event.getAction() == KeyEvent.ACTION_UP) {
return false;
}
Editable e = (Editable) v.getText();
String data = e.toString() + "\n";
sendOverSerial(data.getBytes());
TextKeyListener.clear(e);
return true;
}
});
And the write method:
public void write(byte[] bytes, int offset, int count) {
super.write(bytes, offset, count);
if (isRunning()) {
doLocalEcho(bytes);
}
return;
}
When I was hitting enter after typing in the terminal session itself no new line was occurring at all. So I had to test the data for \r and replace it with \r\n:
private void doLocalEcho(byte[] data) {
String str = new String(data);
appendToEmulator(data, 0, data.length);
notifyUpdate();
}
public void write(byte[] bytes, int offset, int count) {
// Count the number of CRs
String str = new String(bytes);
int numCRs = 0;
for (int i = offset; i < offset + count; ++i) {
if (bytes[i] == '\r') {
++numCRs;
}
}
if (numCRs == 0) {
// No CRs -- just send data as-is
super.write(bytes, offset, count);
if (isRunning()) {
doLocalEcho(bytes);
}
return;
}
// Convert CRs into CRLFs
byte[] translated = new byte[count + numCRs];
int j = 0;
for (int i = offset; i < offset + count; ++i) {
if (bytes[i] == '\r') {
translated[j++] = '\r';
translated[j++] = '\n';
} else {
translated[j++] = bytes[i];
}
}
super.write(translated, 0, translated.length);
// If server echo is off, echo the entered characters locally
if (isRunning()) {
doLocalEcho(translated);
}
}
So that worked fine, now when I typed in the terminal session itself and hit enter i got the newline I wanted. However now every time I send data from the text box with with \n there was an extra space between every newline as well as getting the extra newline.
http://i.imgur.com/gtdIH.png
So I thought that when counting the number of carriage returns peek ahead at the next byte, and if it is '\n' don't count it:
for (int i = offset; i < offset + count; ++i) {
if (bytes[i] == '\r' &&
(
(i+1 < offset + count) && // next byte isn't out of index
(bytes[i+1] != '\n')
) // next byte isn't a new line
)
{
++numCRs;
}
}
This fixed the problem of the spaces...but that was rather stupid as I am now back in a circle to the original problem, if I type directly in the terminal there is no new line, as it sees the \r\n and sees the next byte is invalid. What would be the best way to get both working together? I either have these weird extra spaces and all input is fine, or normal spacing and I can't enter text directly from the terminal, only the textbox. I assume it's really easy to fix, I just am scratching my head looking at it.
EDIT: Not fixed, thought I had but then when I enter directly from the terminal enter does not produce a new line. It must be because the \n is ignored after the \r
I have most of this fixed. When counting the number of carriage returns peek ahead at the next byte, and if it is '\n' don't count it:
for (int i = offset; i < offset + count; ++i) {
if (bytes[i] == '\r' &&
(
(i+1 < offset + count) && // next byte isn't out of index
(bytes[i+1] != '\n')
) // next byte isn't a new line
)
{
++numCRs;
}
}
The only problem left now is that I still get the prompt back twice like this:
switch#
switch#
I've decided I have to write my own syntax highlighter. So far it's working but it's realtime (you type, it highlights) and it's slow.
I'll try to explain how it works. Each time the user types something into the EditText it runs the highlighter (via TextWatcher). The highlighter searches through the text until it finds the beginning of a word and then searches until it finds the end of the same word. Once it finds a word it searches through an array of keywords, if it finds a match it sets a spannable at that location. It keeps looping until it reaches the end of the document.
Again, it works so far (just trying out this idea before I continue with this method), but it's so slow. Some times it can take over a second just to go through a few lines. It slows down how fast the text appears in the EditText. - I also set where the highlighter starts after text is entered at the last position where the user typed so it doesnt have to go through the whole doc each time, it helps a little but not much.
Here's the basic of my EditText:
public class CodeView extends EditText {
private int mTxtChangeStart;
String mStructures[] = this.getResources().getStringArray(R.array.structures);
public CodeView(Context context, AttributeSet attrs) {
super(context, attrs);
addTextChangedListener(inputTextWatcher);
...
}
TextWatcher inputTextWatcher = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
syntaxHighlight();
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
//Set where we should start highlighting
mTxtChangeStart = start;
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
};
private void syntaxHighlight() {
//Time how long it takes for debugging
long syntime = System.currentTimeMillis();
Log.d("", "Start Syntax Highlight");
//Get the position where to start searching for words
int strt = mTxtChangeStart;
//Get the editable text
Editable txt = getText();
//Back up the starting position to the nearest space
try {
for(;;) {
if(strt <= 0) break;
char c = txt.charAt(strt);
if(c != ' ' && c != '\t' && c != '\n' && c != '\r') {
strt--;
} else {
break;
}
}
} catch (IndexOutOfBoundsException e) {
Log.e("", "Find start position failed: " + e.getMessage());
}
//Just seeing how long this part took
long findStartPosTime = System.currentTimeMillis();
Log.d("", "Find starting position took " + String.valueOf(System.currentTimeMillis() - findStartPosTime) + " milliseconds");
//the 'end of a word' position
int fin = strt;
//Get the total length of the search text
int totalLength = txt.length();
//Start finding words
//This loop is to find the first character of a word
//It loops until the current character isnt a space, tab, linebreak etc.
while(fin < totalLength && strt < totalLength) {
for(;;) {
//Not sure why I added these two lines - not needed here
//fin++;
//if(fin >= totalLength) { break; } //We're at the end of the document
//Check if there is a space at the first character.
try {
for(;;) { //Loop until we find a useable character
char c = txt.charAt(strt);
if (c == ' ' || c == '\t' || c == '\n' || c == '\r'){
strt++; //Go to the next character if there is a space
} else {
break; //Found a character (not a space, tab or linebreak) - break the loop
}
}
}catch(IndexOutOfBoundsException e) {
Log.e("", e.getMessage());
break;
}
//Make sure fin isnt less than strt
if(strt > fin) { fin = strt; }
//Now we search for the end of the word
//Loop until we find a space at the end of a word
try {
for(;;) {
char c = txt.charAt(fin);
if(c != ' ' && c != '\t' && c != '\n' && c != '\r') {
fin++; //Didn't find whitespace here, keep looking
} else {
break; //Now we found whitespace, end of a word
}
}
break;
} catch (IndexOutOfBoundsException e) {
//If this happens it should mean it just reached the end of the document.
Log.e("", "End of doc? : " + e.getMessage());
break;
}
}
Log.d("", "It took " + String.valueOf(System.currentTimeMillis() - findStartPosTime) + " milliseconds to find a word");
//Make sure fin isnt less that start, again
if(strt > fin) { fin = strt; }
//Debug time, how long it took to find a word
long matchTime = System.currentTimeMillis();
//Found a word, see if it matches a word in our string[]
try {
for(String mStruct : mStructures) {
if(String.valueOf(txt.subSequence(strt, fin)).equals(mStruct)) {
//highlight
Spannable s = (Spannable) txt;
s.setSpan(new ForegroundColorSpan(Color.RED), strt, fin, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//Can someone explain why this is still setting the spannable to the main editable???
//It should be set to txt right???
break;
} else {
/*Spannable s = (Spannable) txt;
s.setSpan(new ForegroundColorSpan(Color.BLACK), strt, fin, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
txt.removeSpan(s);*/
}
}
}catch (IndexOutOfBoundsException e) {
e.printStackTrace();
Log.e("", "word match error: " + e.getMessage());
}
//Finally set strt to fin and start again!
strt = fin;
Log.d("", "match a word time " + String.valueOf(System.currentTimeMillis() - matchTime) + " milliseconds");
}//end main while loop
Log.d("", "Syntax Highlight Finished in " + (System.currentTimeMillis() - syntime) + " milliseconds");
mTextChanged = false;
}
}
"structures" resource (php.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="structures">
<item>if</item>
<item>else</item>
<item>else if</item>
<item>while</item>
<item>do-while</item>
<item>for</item>
<item>foreach</item>
<item>break</item>
<item>continue</item>
<item>switch</item>
<item>declare</item>
<item>return</item>
<item>require</item>
<item>include</item>
<item>require_once</item>
<item>include_once</item>
<item>goto</item>
</string-array>
</resources>
Anyone have any suggestions how to make this search faster? I know I have a lot of loops but I'm not sure how else to do it.
Thanks a lot!
Can you split the string on the delimiters you have there rather than looking at each character? That would speed it up some. (String.split())