I need the TextToSpeech engine to speak my words one by one, and I am trying to catch the end of speaking of one word to start speaking the next one. But the OnUtteranceCompletedListener cause some delay of the speech.
So my question is, how can I fix this or make a better implementation of the OnUtteranceCompletedListener?
public class AndroidTextToSpeechActivity extends Activity implements TextToSpeech.OnInitListener {
int result = 0, CURRENT_WORD = 0;
HashMap<String, String> myHash;
String[] words;
Button btnSpeak;
TextToSpeech tts;
Handler hand = new Handler();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = "Hi there how are you";
words = text.split(" ", 50);
myHash = new HashMap<String, String>();
tts = new TextToSpeech(this, this);
btnSpeak = (Button) findViewById(R.id.btnSpeak);
btnSpeak.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
hand.postDelayed(run, 300);
}
});
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
result = tts.setLanguage(Locale.getDefault());
tts.setPitch(1f);
tts.setSpeechRate(1f);
} else
Log.e("TTS", "Init failed");
}
Runnable run = new Runnable() {
public void run() {
text = words[CURRENT_WORD];
tts.speak(text, 1, myHash);
tts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener() {
#Override
public void onUtteranceCompleted(String utteranceId) {
CURRENT_WORD++;
hand.post(run1);
}
});
}
};
}
You can speed it up by not recreating the OnUtteranceCompleteListener in each run
OnUtteranceCompletedListener listener=new OnUtteranceCompletedListener(){
#Override
public void onUtteranceCompleted(String utteranceId) {
CURRENT_WORD++;
hand.post(run1);
}
}
tts.setOnUtteranceCompletedListener(listener);
Runnable run = new Runnable() {
public void run() {
text = words[CURRENT_WORD];
tts.speak(text, 1, myHash);
}
};
Furthamore, instead of using a Runnable to call the speek() method of the engine through a handler, you can use the onUtteranceCompleted method to call the speak() method
OnUtteranceCompletedListener listener=new OnUtteranceCompletedListener(){
#Override
public void onUtteranceCompleted(String utteranceId) {
CURRENT_WORD++;
if(CURRENT_WORD<max_words){
String text=words[CURRENT_WORD];
tts.speak(text,1,myHash);
}
}
}
tts.setOnUtteranceCompletedListener(listener);
Runnable run = new Runnable() {
public void run() {
text = words[CURRENT_WORD];
tts.speak(text, 1, myHash);
}
};
Your code will fail if Locale.getDefault() language is not supported or need data files. Also, if onInit() has not returned 300 ms after you pressed the btnSpeak, speak() will not function.
You should disable btnSpeak in the xml layout file and enable it in onInit. In the btnSpeak listener loop through words and call speak()
btnSpeak = (Button) findViewById(R.id.btnSpeak);
btnSpeak.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
for (int i = 0; i < words.length; i++) {
tts.speak(text, 1, myHash);
// call playSilence (long durationInMs, 1, myHash)
// if you want a slight delay between each word.
}
}
});
Related
This is a test activity when the button is pressed the textToSpeech works just fine, but it wont work when the function playString() is called, playString() is being called from the onCreate() of this TestActivity.
public class TestActivity extends Activity {
TextToSpeech textToSpeech;
EditText editText;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
editText=(EditText)findViewById(R.id.editText);
button=(Button)findViewById(R.id.button);
textToSpeech=new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR) {
textToSpeech.setLanguage(Locale.UK);
}
}
});
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String sentence = "Testing String";
textToSpeech.speak(sentence, TextToSpeech.QUEUE_FLUSH, null);
}
});
playString();
}
public void playString(){
String sentence = "Testing String";
textToSpeech.speak(sentence, TextToSpeech.QUEUE_FLUSH, null);
}
public void onPause(){
if(textToSpeech !=null){
textToSpeech.stop();
textToSpeech.shutdown();
}
super.onPause();
}
}
From documentation:
TextToSpeech instance can only be used to synthesize text once it has completed its initialization.
Initialization may take long time (on my device it's take ~30 seconds), so you can't use handler with some random delay.
Instead, you can place playString() in onInit block right after textToSpeech.setLanguage(Locale.UK);, so string will be played when it can be played.
Please use below code in oncreate method to call the texttospeech:
textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if (status != TextToSpeech.ERROR) {
textToSpeech.setLanguage(Locale.UK);
}
}
});
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
String sentence = "Testing String";
textToSpeech.speak(sentence, TextToSpeech.QUEUE_FLUSH, null);
}
}, 500);
This is my code to perform click automatically when the activity opens but it is not working
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notify);
editspeak = (EditText) findViewById(R.id.editspeak);
btspeak=(Button)findViewById(R.id.bt);
// speakout();
// mydb = new DBhandler(this);
SharedPreferences preferences=getSharedPreferences(PREFS,0);
String name=preferences.getString("NAME",null);
editspeak.setText(name);
t1=new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR) {
t1.setLanguage(Locale.US);
}
}
});
btspeak.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String text = editspeak.getText().toString();
t1.speak(text, TextToSpeech.QUEUE_FLUSH, null);
// bt.setPressed(false);
// bt.invalidate();
}
});
btspeak.performClick();
The problem here is that you call speak before the TextToSpeech is fully initialised (you can add a few logs to check this). To fix this behaviour you can use performClick in this way to delay the call until everything else is finished initialising:
btspeak.post(new Runnable() {
#Override
public void run() {
btspeak.performClick();
}
});
I am getting error "android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views" when I call listenButton.setAlpha((float)1) in the code below. I understand why, but how can I then modify a button when I receive the onDone event?
public class MainActivity extends ActionBarActivity implements TextToSpeech.OnInitListener {
[...]
#Override
// OnInitListener method to receive the TTS engine status
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
ttsOK = true;
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String utteranceId) {
Button listenButton = (Button) findViewById(R.id.listentts);
listenButton.setAlpha((float)1);
listenButton.setClickable(true);
}
#Override
public void onError(String utteranceId) {
Log.d("MainActivity", "Progress on Error " + utteranceId);
}
#Override
public void onStart(String utteranceId) {
Log.d("MainActivity", "Progress on Start " + utteranceId);
}
});
}
else {
ttsOK = false;
}
}
[...]
}
Every View has a Handler associated with it, so there's no need to create a new one.
final View v = findViewById(R.id.listentts); // could be a class member
v.getHandler().post(new Runnable() {
#Override
public void run() {
v.setAlpha(1.0f);
v.setClickable(true);
}
});
Try this:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Button listenButton = (Button) findViewById(R.id.listentts);
listenButton.setAlpha((float)1);
listenButton.setClickable(true);
}
});
private TextToSpeech tts;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
tts = new TextToSpeech(this,(OnInitListener) clickball);
}
OnClickListener clickball=new OnClickListener() {
#Override
public void onClick(View v) {
score=scorenumber.nextInt(8);
ballid=v.getId();
if(score==4)
{
playgame(ballid,Integer.toString(score));
dynamic_image.setBackgroundDrawable(getResources().getDrawable(R.drawable.four_01));
dynamic_image.setVisibility(0x000000);
disablelayout();
timerfunc1(dynamic_image,R.drawable.four_02);
tts.setLanguage(Locale.US);
tts.speak("Four", TextToSpeech.QUEUE_FLUSH, null);
dynamic_image.postDelayed(new Runnable(){
#Override
public void run() {
dynamic_image.setBackgroundDrawable(getResources().getDrawable(R.drawable.score4));
dynamic_image.setVisibility(0x000000);
timerfunc(dynamic_image);
}
}, 2200);
enablelayout4();
}
}
Given above is my source code.but it is throwing classcast exception when it runs..i want to convert the text "Four" to speech when the score is 4.plz anybody help me...i know given below line of code throwing the exception.but i dnt know hot to solve it..
tts = new TextToSpeech(this,(OnInitListener) clickball);
i got the answer...i gave clicklistener name in
tts = new TextToSpeech(this,(OnInitListener) clickball);
actuallly i had to give the OnInitListener name there.i had changed the code like this..
fisrt implement TextToSpeech.OnInitListener
and added its unimplemented method(OnInit).
private TextToSpeech tts;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
tts = new TextToSpeech(this,this);
OnClickListener clickball=new OnClickListener() {
#Override
public void onClick(View v) {
score=scorenumber.nextInt(8);
ballid=v.getId();
if (totalovers==0)
{
gameover();
return;
}
if(score==4)
{
playgame(ballid,Integer.toString(score));
dynamic_image.setBackgroundDrawable(getResources().getDrawable(R.drawable.four_01));
dynamic_image.setVisibility(0x000000);
disablelayout();
timerfunc1(dynamic_image,R.drawable.four_02);
currentScore ="FOUR";
tts.setLanguage(Locale.US);
tts.speak(currentScore, TextToSpeech.QUEUE_FLUSH, null);
dynamic_image.postDelayed(new Runnable(){
#Override
public void run() {
dynamic_image.setBackgroundDrawable(getResources().getDrawable(R.drawable.score4));
dynamic_image.setVisibility(0x000000);
timerfunc(dynamic_image);
}
}, 2000);
enablelayout4();
}
}
#Override
public void onInit(int status) {
// TODO Auto-generated method stub
}
this solved my problem...
I'm really struggling with something... I have a couple of sentences that I want to read, both verbally through tts speek function, and via text on screen, one sentence at a time.
I have the textview area ready, but putting it together is what I'm not getting. Either it will read all the sentences and only show the last one, or it will show and read only the first sentence.
Anyone know i how I can accomplish this goal?
I just ran into this issue, according to the speak method, use an UtteranceProgressListener. I found out this is not executed on the UI thread, so I had to use runOnUiThread() to get back to update the activity.
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
}
#Override
public void onDone(String utteranceId) {
LettersActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// Do something on UI thread
}
});
}
#Override
public void onError(String utteranceId) {
Log.e(TAG, "error on " + utteranceId);
}
});
boolean speakingEnd = tts.isSpeaking();
do{
speakingEnd = tts.isSpeaking();
} while (speakingEnd);
//Continue with code
public void speak(String message){
tts.speak(message, TextToSpeech.QUEUE_FLUSH, null);
while (tts.isSpeaking()){
System.Out.Println("Do something or nothing while speaking..")
}
}
Try this
public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener{
private boolean initialized;
private String queuedText;
private String TAG = "TTS";
private TextToSpeech tts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TextToSpeech(this /* context */, this /* listener */);
tts.setOnUtteranceProgressListener(mProgressListener);
speak("hello world");
}
public void speak(String text) {
if (!initialized) {
queuedText = text;
return;
}
queuedText = null;
setTtsListener(); // no longer creates a new UtteranceProgressListener each time
HashMap<String, String> map = new HashMap<String, String>();
map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "MessageId");
tts.speak(text, TextToSpeech.QUEUE_ADD, map);
}
private void setTtsListener() {
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
initialized = true;
tts.setLanguage(Locale.ENGLISH);
if (queuedText != null) {
speak(queuedText);
}
}
}
private abstract class runnable implements Runnable {
}
private UtteranceProgressListener mProgressListener = new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
} // Do nothing
#Override
public void onError(String utteranceId) {
} // Do nothing.
#Override
public void onDone(String utteranceId) {
new Thread()
{
public void run()
{
MainActivity.this.runOnUiThread(new runnable()
{
public void run()
{
Toast.makeText(getBaseContext(), "TTS Completed", Toast.LENGTH_SHORT).show();
}
});
}
}.start();
}
};
}