Android text to speech voice distortion at the beginning - android

In Android app I have a text, which after the user press a button, it will be spoken by TTS:
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
final Button speech = findViewById(R.id.speech);
speech.setOnClickListener(speech1 -> {
if (!isPackageInstalled("com.google.android.tts")) {
showMsgSnack(getString(R.string.noTTS));
} else {
Boolean speak = checkSpeak();
if (!speak) {
speech.setCompoundDrawablesWithIntrinsicBounds(R.drawable.stop, 0, 0, 0);
if (history.length() > 3999) {
String var = history.substring(0, 3999);
ConvertTextToSpeech(var, "test");
String var2 = history.substring(3999);
ConvertTextToSpeech(var2, "test");
} else {
ConvertTextToSpeech(history, "test");
}
saveSpeak(true);
} else {
speech.setCompoundDrawablesWithIntrinsicBounds(R.drawable.play, 0, 0, 0);
tts.stop();
saveSpeak(false);
}
}
});
private void ConvertTextToSpeech(String history, String par) {
String readableText = fromHtml(history).toString(); //remove HTML tags -> do not read <br>
Bundle params = new Bundle();
params.putString(KEY_PARAM_UTTERANCE_ID, "");
tts.speak(readableText, TextToSpeech.QUEUE_ADD, params, par);
}
The stuff about TTS:
if ((isPackageInstalled("com.google.android.tts"))) {
tts=new TextToSpeech(SingleitemView.this, status -> {
if(status == TextToSpeech.SUCCESS){
if (isLangAvailable(this,tts,locale)) {tts.setLanguage(locale);}
else {tts.setLanguage(new Locale("en"));}
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String utteranceId) {
// Log.d("Speak", "TTS finished");
if (utteranceId.equals("test")) {
saveSpeak(false);
runOnUiThread(() -> {
Button view2 = findViewById(R.id.speech);
view2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.play, 0, 0, 0);
});
}
}
#Override
public void onError(String utteranceId) {
}
#Override
public void onStart(String utteranceId) {
}
});
}
},"com.google.android.tts");}
This worked for years without problems, the TTS read the text even if it was short, or more than 3999 characters. Suddenly a few months ago (without any change from my side in application) when it starts to speak longer text, the first 3-4 words are distorted and hardly understandable in every text.
Looks like as it is doing something in the background while it is reading the text in the same time.
Not sure what happened, because I dod no change in my code and it worked till now.
So now I tried to update all libraries and dependencies, but it didn't help.
The problem is only with the text, which is longer than 3999 chars. The shorter text is working with no issues.
I also tried to put this condition outside of onClickListener, so the text is prepared first:
if (history.length() > 3999) {
String var = fromHtml(history.substring(0, 3999)).toString();
String var2 = fromHtml(history.substring(3999)).toString(); }
and then in onClickListener I just call the TTS twice:
ConvertTextToSpeech(var, "test");
ConvertTextToSpeech(var2, "test");
private void ConvertTextToSpeech(String history, String par) {
Bundle params = new Bundle();
params.putString(KEY_PARAM_UTTERANCE_ID, "");
tts.speak(history, TextToSpeech.QUEUE_ADD, params, par);
}
But I have the same issue, this solution didn't helped.
I thought the issue is that I call immediately two ConvertTextToSpeech after each other, but even if I remove the second one, the problem is there.
So definitely, the problem is with the longer text, but I can't find the solution why it started to happen. I checked this on many devices, same issue everywhere.
The only thing that helps is to create much smaller chunks of text:
for (int a=1; a <= history.length(); a+=100) {
if((history.length() - (a + 100)) > 0) {ConvertTextToSpeech(history.substring(a, a+100), "test");}
else {ConvertTextToSpeech(history.substring(a), "test");}
However, this causes a noticeable pause between speaking the chunks, often also inside the word, so this is not a good solution.

Ok, so even if the max limit for google TTS is 3999 characters, for some reason it started to have issues also with ca. 3000 characters. Not sure why it happened, but it just happened without any code change on my side. Seems an issue related to google TTS.
So, as in my case the text blocks are separated by a break line <br>, and I know each block has no more than 1000-2000 chars, I now splitted all my text like this:
String[] parts = history.split("<br>");
for(String part: parts) {ConvertTextToSpeech(part, "test");}
So now this is working for any text with any length. Problem solved.

Related

Using .getSpeedLimit() also makes warning sounds . How to Override?

Unable to override Here SDK to disable sound effect on the onSpeedExceeded event.
Using the Here Developer tutorial, (https://developer.here.com/blog/android-premium-sdk-speed-limit-warning-example), I succeeded in running the sample app. But...
While driving, when I exceed the speed limit, there is a doot doot doot. I want to override this behaviour as I intend to use my own sounds.
I guessed that I might override the code by creating a NavigationManager.SpeedWarningListener. Unfortunately I can not disable or defeat the 'onSpeedExceeded' sound effects.
NavigationManager.SpeedWarningListener speedWarningListener = new NavigationManager.SpeedWarningListener() {
#Override
public void onSpeedExceeded(String s, float v) {
//super.onSpeedExceeded(s, v);
//Log.v(Global.TAG, "onSpeedExceeded");
Global.SpeedLimitExceeded = true;
}
#Override
public void onSpeedExceededEnd(String s, float v) {
//super.onSpeedExceededEnd(s, v);
//Log.v(Global.TAG, "onSpeedExceededEnd");
Global.SpeedLimitExceeded = false;
}
};
EDITED ANSWER: This method needs to be amended to stop the speed warning:
private void startNavigationManager() {
NavigationManager.Error navError = NavigationManager.getInstance().startTracking();
// added by suggestion from stackoverflow
NavigationManager.getInstance().stopSpeedWarning();
if (navError != NavigationManager.Error.NONE) {
Log.d(Global.TAG, "NavigationManager: false");
//handle error navError.toString());
} else {
//Log.d(Global.TAG, "NavigationManager: true");
}
}
Please set speedWarningEnabled accordingly for NMANavigationManager
navigationManager:didUpdateSpeedingStatus:forCurrentSpeed:speedLimit: will be sent to the delegate when speeding is detected or when a correction is made.
Also refer http://developer.here.com/documentation/ios-premium/api_reference_jazzy/Classes/NMANavigationManager.html

Use arraylist for multiple if statements (with buttons)

I'm currently working on a school project in Android Studio and so far I've written a code which generates random equations.
Now I display two equations on the screen and the user then has to decide wether the second equation is bigger or smaller than the first one. If the second is bigger, the user presses the button 'bigger', if the second one is smaller, the user presses the button with 'smaller'.
Now I'm trying to write the code that if the user pressed correct, it generates a new equation, if he was wrong, the process stops.
I was thinking of if statements like so:
final ArrayList<String> arrayListCheck = new ArrayList<String>();
if(doubleAnswer1 > doubleAnswer2){
arrayListCheck.add("smaller");
} else {
arrayListCheck.add("bigger");
}
final Button buttonBigger = (Button)findViewById(R.id.button_bigger);
final Button buttonSmaller = (Button)findViewById(R.id.button_smaller);
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.equals(buttonBigger)){
arrayListCheck.add("bigger");
} else {
arrayListCheck.add("smaller");
}
}
};
buttonBigger.setOnClickListener(listener);
buttonSmaller.setOnClickListener(listener);
In the arraylist arrayListCheck it will store either 'bigger' or 'smaller'. Now I want to check if the elements in the arraylist are both the same (either both 'bigger' or 'smaller'), if so a new equation will be generated. If the elements in the arraylist are different (not both the same), the process will be stopped.
I don't know if that really works, so it would be nice if someone could help me with this.
If there is anything unclear in my question, feel free to ask and I will try to clarify the problem :)
Thank you already in advance for your help!
I wouldn't use an ArrayList for that.
I would do the following:
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.equals(buttonBigger) && doubleAnswer1 < doubleAnswer2) {
Log.v("TAG", "you are right");
} else if(v.equals(buttonSmaller) && doubleAnswer1 > doubleAnswer2) {
Log.v("TAG", "you are right");
} else {
Log.v("TAG", "you are wrong");
}
}
};
Arrays are not really neccessary for this kind of simple comparison.

EditText.setText()/getText() appears to be too slow to be used in TextWatcher

I have a TextWatcher that adds a dollar sign in front of an EditText value (it's a price field). Everything works fine except that if you type two first digits fast enough, the second digit won't appear. Once you past the first two digits it's all fine. If you type them slowly (almost a second in between) it also works fine.
Here is the TextWatcher code I am using:
#AfterTextChange(R.id.add_itemPrice) // android annotations way
void addDollar(Editable e) {
if (priceFieldBeingModified) {
return;
}
if (!e.toString().startsWith(CURRENCY_SYMB)) {
priceFieldBeingModified = true;
String newValue = CURRENCY_SYMB + e;
priceField.setText(newValue);
if (priceField.getSelectionStart() == 0) {
// move the cursor to the end
priceField.setSelection(priceField.getText().length());
}
priceFieldBeingModified = false;
}
}
priceField EditText has fixed layout_width/layout_height (in dp). From what I can gather, setText()/getText() are just too expensive but I don't know how to avoid them in this case.
priceField.getText().insert(0, CURRENCY_SYMB) doesn't do anything for some reason.
EDIT:
Looks like the problem only happens on Android 4.3 (or an Xperia Z phone). Tried a 4.1 phone - works like a charm.
Any suggestions would be appreciated, I am out of ideas with this one!
This is working fine for me:
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
if(s.toString().equalsIgnoreCase(""))
{
}
else if (!s.toString().startsWith("$")) {
priceFieldBeingModified = true;
String newValue = "$" + s;
e.setText(newValue);
if (e.getSelectionStart() == 0) {
// move the cursor to the end
e.setSelection(e.getText().length());
}
priceFieldBeingModified = false;
}
It checks if string is not null and it doesn't start with "$", then it adds "$" at start and moves cursor to end.

Accessibility function implementation problems in Android

I'm developing application that views books. There is a screen (Activity) which shows a book. It has custom view, something similar to ViewSwitcher and every page is a bitmap that is rendered by a custom View.
Now I should implement accessibility function - book should be read by the phone (audio).
I've read Accessibility section here https://developer.android.com/guide/topics/ui/accessibility/index.html but it is not clear enough.
I use SupportLibrary for accessibility management and now I have this code in ViewGroup (which manages book pages). Code 1:
private class EditionPagesViewSwitcherAccessibilityDelegate extends AccessibilityDelegateCompat {
private int mPageCount;
private double[] mPageRange;
#Override
public void onInitializeAccessibilityEvent(final View host, final AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setClassName(EditionPagesViewSwitcher.class.getName());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
event.setScrollable(canScroll());
}
if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED && updatePageValues()) {
event.setItemCount(mPageCount);
// we use +1 because of user friendly numbers (from 1 not 0)
event.setFromIndex((int) (mPageRange[0] + 1));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
event.setToIndex((int) (mPageRange[1] + 1));
}
}
}
#Override
public void onInitializeAccessibilityNodeInfo(final View host, final AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(EditionPagesViewSwitcher.class.getName());
info.setScrollable(canScroll());
info.setLongClickable(true);
if (canScrollForward()) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
}
if (canScrollBackward()) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
}
}
#Override
public boolean performAccessibilityAction(final View host, final int action, final Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
if (canScrollForward()) {
showNext();
return true;
}
}
return false;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
if (canScrollBackward()) {
showPrevious();
return true;
}
}
return false;
}
return false;
}
Here is code from page view Code 2:
#Override
public void onInitializeAccessibilityEvent(final View host, final AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setClassName(EditionPageView.class.getName());
if (hasText()) {
event.getText().add(getPageRangeText());
final String trimText = mSurfaceUpdateData.getPageText().trim();
if (trimText.length() > MAX_TEXT_LENGTH) {
event.getText().add(trimText.substring(0, MAX_TEXT_LENGTH));
// event.getText().add(trimText.substring(MAX_TEXT_LENGTH, trimText.length()));
}
else {
event.getText().add(trimText);
}
}
}
#Override
public void onInitializeAccessibilityNodeInfo(final View host, final AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(EditionPageView.class.getName());
}
Because page text data loads asynchronous first time accessibility don't have any text while executes onInitializeAccessibilityEvent code. And then when data have been loaded I fire AccessibilityEvent.TYPE_VIEW_SELECTED and AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED events. Then onInitializeAccessibilityEvent executes again and phone "read" book text.
So my questions:
Is my Accessibility implementation right? May be it is design wrong? Because I didn't find any good tutorial about this feature.
Why I need to use SDK versions checks in Support implementations in Code 1? Why support implementation doesn't handle it correctly?
Is firing TYPE_VIEW_SELECTED and TYPE_VIEW_TEXT_CHANGED really needed? Or may be some other code should be implemented?
The main question. In Code 2 there is commented code line. This code statement substring text to be less then MAX_TEXT_LENGTH (it's 3800) because if text is bigger nothing is played. Nothing. Is it accessibility restriction? Any other text that is less then this value is played well.
Does anyone know where I can find any good tutorial? (yes I saw samples).
Does anyone have any custom realizations to look through?
UPDATED
Well. Here is some answers:
As I can see TYPE_VIEW_SELECTED and TYPE_VIEW_TEXT_CHANGED events are not needed if you don't want this text to be read as soon as you get it.
On Nexus 7 all large text is played well (text up to 8000 symbols), so this issue doesn't reproduce on it, but on Samsung Galaxy Tab 10.1 (Android 4.0.4) and Genymotion emulator of Tab 10.1 with Android 4.3 does. And this is strange...
4.. According to the documentation of String.substring()
The first argument you pass is the start index in the original string, the second argument is the end index in the original string.
Example:
String text = "Hello";
partOfText = text.substring(2,text.length() - 1);
partOfText equals to "llo" (the first char is index 0)
So by putting your constant MAX_TEXT_LENGTH as a first argument, it would start at index 3800 to take out the substring.
http://developer.android.com/reference/java/lang/String.html#substring(int)
You are right MAX_TEXT_LENGTH is 3800.
About your doubt,
this code:
event.getText().add(trimText.substring(MAX_TEXT_LENGTH, trimText.length()));
}
you are trying to substring "trimText" from MAX_TEXT_LENGTH to trimText.length() !
Supposing that trimText = "STACK", trimText.length() = 5, then trimText.substring(3800,5) is going to be ?
At first, this doesn't have sense, using correctly would be like this:
trimText.substring(0,2) = "ST";

Android speech - how can you read text in Android?

How can you read data, i.e. convert simple text strings to voice (speech) in Android?
Is there an API where I can do something like this:
TextToVoice speaker = new TextToVoice();
speaker.Speak("Hello World");
Using the TTS is a little bit more complicated than you expect, but it's easy to write a wrapper that gives you the API you desire.
There are a number of issues you must overcome to get it work nicely.
They are:
Always set the UtteranceId (or else
OnUtteranceCompleted will not be
called)
setting OnUtteranceCompleted
listener (only after the speech
system is properly initialized)
public class TextSpeakerDemo implements OnInitListener
{
private TextToSpeech tts;
private Activity activity;
private static HashMap DUMMY_PARAMS = new HashMap();
static
{
DUMMY_PARAMS.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "theUtId");
}
private ReentrantLock waitForInitLock = new ReentrantLock();
public TextSpeakerDemo(Activity parentActivity)
{
activity = parentActivity;
tts = new TextToSpeech(activity, this);
//don't do speak until initing
waitForInitLock.lock();
}
public void onInit(int version)
{ //unlock it so that speech will happen
waitForInitLock.unlock();
}
public void say(WhatToSay say)
{
say(say.toString());
}
public void say(String say)
{
tts.speak(say, TextToSpeech.QUEUE_FLUSH, null);
}
public void say(String say, OnUtteranceCompletedListener whenTextDone)
{
if (waitForInitLock.isLocked())
{
try
{
waitForInitLock.tryLock(180, TimeUnit.SECONDS);
}
catch (InterruptedException e)
{
Log.e("speaker", "interruped");
}
//unlock it here so that it is never locked again
waitForInitLock.unlock();
}
int result = tts.setOnUtteranceCompletedListener(whenTextDone);
if (result == TextToSpeech.ERROR)
{
Log.e("speaker", "failed to add utterance listener");
}
//note: here pass in the dummy params so onUtteranceCompleted gets called
tts.speak(say, TextToSpeech.QUEUE_FLUSH, DUMMY_PARAMS);
}
/**
* make sure to call this at the end
*/
public void done()
{
tts.shutdown();
}
}
Here you go . A tutorial on using the library The big downside is that it requires an SD card to store the voices.
A good working example of tts usage can be found in the "Pro Android 2 book". Have a look at their source code for chapter 15.
There are third-party text-to-speech engines. Rumor has it that Donut contains a text-to-speech engine, suggesting it will be available in future versions of Android. Beyond that, though, there is nothing built into Android for text-to-speech.
Donut has this: see the android.speech.tts package.

Categories

Resources