I'm building an app in which there is tts part, in the main activity i'm creating an instance of tts like
public class Translator extends Activity implements OnClickListener{
TextToSpeech tts;
ArrayList<TTS.resultData> textsToBeSpoken = new ArrayList<TTS.resultData>();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_translator);
initTTS();//calling to initialise tts;
}
}
and i'm calling the initTTS() method from onCreate() method;
public void initTTS(){
tts = new TextToSpeech(Translator.this, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
toast("TTS ready to use");
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
log("Started speaking");
}
#Override
public void onError(String utteranceId) {
log("Error in processing Text to speech");
}
#Override
public void onDone(String utteranceId) {
log("Text to speech finished previewing");
}
});
}
});
}
and there is one more function called
public void speakUpSon(){
HashMap<String, String> params = new HashMap<String, String>();
if(textsToBeSpoken.size() > 0){
for(int i = 0; i < textsToBeSpoken.size(); i++){
if(getCanProceedSpeaking()){
int index = i;
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "finished Speaking of index : " + i);
System.out.println(textsToBeSpoken.get(i));
tts.speak(textsToBeSpoken.get(i)._originalTxt, TextToSpeech.QUEUE_ADD, params);
textsToBeSpoken.remove(index);
}
}
}
}
in other thread somewere in the app i'll be inserting the objects to speak in textsToBeSpoken, and in other thread there'll be loop were it'll check for the size of the textsToBeSpoken if the size > 0, it'll call the speakUpSon() method.
until here everything works fine, but i get the following error messages
08-14 11:42:04.370: E/ActivityThread(4945): Activity com.PI.prototype.translator.Translator has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection#b4669088 that was originally bound here
You have to call tts.shutdown() somewhere in your code, it is best in onStop() and call initTTS(); in onStart()
Related
I created a splash screen in Android Studio. Now I want a text to speech function to say:
Done by Me
This should happen when the splash screen opens. How do I go about this?
Here is my program so far:
public class CinemaList extends Activity {
private static int SPLASH_TIME_OUT = 4000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent homeIntent = new Intent(CinemaList.this, MovieList.class);
startActivity(homeIntent);
finish();
}
}, SPLASH_TIME_OUT);
}
}
I hope that you're enjoying learning Android development. It can be done as follows:
public class SplashActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {
private TextToSpeech mTts;
private static final String TAG = MainActivity.class.getName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTts = new TextToSpeech(this, this);
}
#Override
protected void onPause() {
if(mTts != null){
mTts.stop();
mTts.shutdown();
}
super.onPause();
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String utteranceId) {
Log.d(TAG, "Done: " + utteranceId);
Intent homeIntent = new Intent(CinemaList.this, MovieList.class);
startActivity(homeIntent);
}
#Override
public void onError(String utteranceId) {
Log.e(TAG, "Error: " + utteranceId);
}
#Override
public void onStart(String utteranceId) {
Log.i(TAG, "Started: " + utteranceId);
}
});
mTts.speak("Done by ME!", TextToSpeech.QUEUE_ADD, null);
} else {
Log.e(TAG, "Failed");
}
}
}
I took the liberty of starting the 'Cinema List' activity after the speech completes. However, you can continue to use your Handler if you wish.
Also, rather than calling finish() explicitly, I'd advise adding the noHistory flag to the SplashActivity in the manifest:
<activity
android:name=".SplashActivity"
android:noHistory="true"/>
The noHistory flag ensures that the Activity will not be there when back is pressed.
noHistory vs finish() - Which is preferred?
I have a FrameLayout with two ImageButtons (Play, Stop). By default
Play button is VISIBLE, Stop button is GONE
Clicking the Play starts the TTS Engine which reads the text. On Completion of reading the text, I want to set the Visibility of
Play to GONE, Stop to VISIBLE
Should I use the UtteranceProgressListener to serve the purpose? If not,
How can I perform the above action?
What is the purpose of UtteranceProgressListener?
Did you perhaps mean that:
reading starts -> Play is gone and Stop is visible
reading ends -> Play is visible, Stop is gone
Anyway, the purpose of UtteranceProgressListener is exactly what you are describing. It's used to monitor the progress of the speech synthesis.
You can add an "utterance id" (here "helloText") to any text that is spoken out:
tts.speak("Hello Stack Overflow!", TextToSpeech.QUEUE_ADD, "helloText");
But that's not really necessary in your case, so the last parameter can be null:
tts.speak("Hello Stack Overflow!", TextToSpeech.QUEUE_ADD, null);
The UtteranceProgressListener should be added before calling speak(). You could do that for example in the TTS initialization callback onInit() if the TTS status is TextToSpeech.SUCCESS.
It can be a separate class or just an anonymous inner class:
speech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
// Speaking started.
}
#Override
public void onDone(String utteranceId) {
// Speaking stopped.
}
}
#Override
public void onError(String utteranceId) {
// There was an error.
}
});
The onStart() method is triggered when speaking starts (soon after calling speak()) so that's one possible place to switch the visible button. For example the Play button could be switched to a Stop button.
The onDone() method is triggered when speaking is finished and it's another possible place to switch the visible button. For example the Stop button could be switched to a Play button.
And as you can see the "utterance id" is available in both methods if you provided a one in the speak() method call. It would be useful if you needed to know exactly which text is being spoken/finished being spoken/failed with an error.
UtteranceProgressListener can be used to identify when the TTS is completed. Try this following code which shows a toast after TTS completed.
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();
}
};
}
Here is my code, I have a series of questions that will be asked by TTS and after every question speech recognizer will be invoked.My utterance listener is never being called.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speech_recognizer);
tts = new TextToSpeech(this /* context */, this /* listener */);
}
//This is called after first time user clicks a button
private void processEnquiry() {
// TODO Auto-generated method stub
for(int i=0;i<EnquiryList.size();i++)
{
speak(EnquiryList.get(i).toString());
}
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
initialized = true;
tts.setLanguage(Locale.ENGLISH);
if (queuedText != null) {
speak(queuedText);
}
}
}
public void speak(String text) {
// If not yet initialized, queue up the text.
if (!initialized) {
queuedText = text;
return;
}
queuedText = null;
// Before speaking the current text, stop any ongoing speech.
//tts.stop();
// Speak the text.
setTtsListener();
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()
{
final SpeechRecognizer callWithResult = this;
int listenerResult = tts.setOnUtteranceProgressListener(new UtteranceProgressListener()
{
#Override
public void onDone(String utteranceId)
{
callWithResult.onDone(utteranceId);
}
#Override
public void onError(String utteranceId)
{
callWithResult.onError(utteranceId);
}
#Override
public void onStart(String utteranceId)
{
callWithResult.onStart(utteranceId);
}
});
if (listenerResult != TextToSpeech.SUCCESS)
{
Log.e(TAG, "failed to add utterance progress listener");
}
}
public void onDone(String utteranceId)
{
callSpeechRecognition();
}
public void onError(String utteranceId)
{
}
public void onStart(String utteranceId)
{
}
TextToSpeech.SUCCESS returns 0.
Here's a modified version of your script, where the onDone() method in the Utterance listener is called. I have eliminated many features that are not directly connected to the TTS playback, to create a barebones sample. If this works for you, then you can add back the other features one by one, testing that nothing breaks on the way.
import android.app.Activity;
import android.os.Bundle;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;
import android.view.View;
import java.util.HashMap;
import java.util.Locale;
public class MainActivity extends Activity implements TextToSpeech.OnInitListener {
private TextToSpeech tts;
private SpeechRecognizer sr;
private boolean initialized;
private String queuedText;
private String TAG = "TTS";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speech_recognizer);
tts = new TextToSpeech(this /* context */, this /* listener */);
tts.setOnUtteranceProgressListener(mProgressListener);
sr = SpeechRecognizer.createSpeechRecognizer(this); // added
}
// Modified for testing purposes
//This is called after first time user clicks a button
/*
private void processEnquiry() {
for (int i = 0; i < EnquiryList.size(); i++) {
speak(EnquiryList.get(i).toString());
}
}
*/
public void processEnquiry(View v) {
speak("Process enquiry");
}
// End of modification
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
initialized = true;
tts.setLanguage(Locale.ENGLISH);
if (queuedText != null) {
speak(queuedText);
}
}
}
public void speak(String text) {
// If not yet initialized, queue up the text.
if (!initialized) {
queuedText = text;
return;
}
queuedText = null;
// Before speaking the current text, stop any ongoing speech.
//tts.stop();
// Speak the text.
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() {
// Method radically simplified; callWithResult is retained but not used here
final SpeechRecognizer callWithResult = sr; // was `this`
}
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) {
callSpeechRecognition();
}
};
private void callSpeechRecognition() {
Log.d(TAG, "callSpeechRecognition() called");
}
}
I'm not sure exactly and not sure if this answer will help you, but I think you should not set UtteranceProgressListener every time you request TTS to speak, instead you should set the listener at onInit() once.
And note that the empty text will not be spoken so no callback will be invoked as well.
Though basically setting the listener after TTS initialization looks fine for me and works without problems on my Nexus5 and GalaxyS4, even if setting the listener every time I request TTS to speak, so there might be some device specific problems or some TTS engine specific problems.
Oops I forgot to mention that UtteranceProgressListener is available on API level 15 and above, so the listener will no be invoked on API level 14 and below.
I wrote the following code:
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private TextToSpeech mTTS;
#Override
protected void onPause() {
super.onPause();
if (mTTS != null) {
mTTS.stop();
mTTS.shutdown();
}
}
#Override
protected void onResume() {
super.onResume();
mTTS = new TextToSpeech(getApplicationContext(),
new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR){
mTTS.setLanguage(Locale.ENGLISH);
mTTS.speak("Hello!", TextToSpeech.QUEUE_FLUSH, null);
}
}
});
}
public void onButtonClick(View view) {
mTTS.speak("Hello!", TextToSpeech.QUEUE_FLUSH, null);
}
}
But this code: mTTS = new TextToSpeech(... freezes the UI thread for 5–8 seconds.
I noticed that the delay happens on this line in logcat (first line):
07-13 11:51:11.304 5296-5296/com.example.TextToSpeachTest I/TextToSpeech﹕ Connected to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
07-13 11:51:17.317 5296-5296/com.example.TextToSpeachTest I/Choreographer﹕ Skipped 391 frames! The application may be doing too much work on its main thread.
I tried to put it inside AsyncTask:
#Override
protected void onResume() {
super.onResume();
MyAsyncTask newTask = new MyAsyncTask() {
protected void onPostExecute(Boolean result) {
}
};
newTask.context = getApplicationContext();
newTask.execute();
}
...
class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private TextToSpeech mTTS;
public Context context;
#Override
protected Boolean doInBackground(Void... arg0) {
mTTS = new TextToSpeech(context,
new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR){
mTTS.setLanguage(Locale.ENGLISH);
mTTS.speak("Hello!", TextToSpeech.QUEUE_FLUSH, null);
}
}
});
return true;
}
}
But nothing changed. Could you please advise a proper solution/idea?
This delay manifests on my Phone LG L4 II (E440). On Nexus 10 – no delays.
I tried different talking apps from Play Store on my LG L4. On some apps there is also UI blocking, but some work without the blocking. This means – it is possible to implement. But how?
Your code is OK, you don't need to call TextToSpeech constructor from a different thread.
Actually, the problem is in the implementation of some TextToSpeech libraries that use main UI thread to do the speech processing.
The solution is making the TextToSpeech processing to run in a separated process, different from the process of the UI thread. In that way, UI interaction and speech processing is done in the main thread of two different process. The only way that I found to do it is creating the TextToSpeech object and control code in an Android Service with its own process.
In order to create a Service with its own process, configuration in AndroidManifest.xml must include the property: android:process:
<service
android:name=".TtsService"
android:enabled="true"
android:exported="false"
android:process="com.example.ttsservice">
</service>
This introduce complexity because now the Activity must bind the Service using a Messenger or AIDL (http://developer.android.com/guide/components/bound-services.html) to manage the TextToSpeech features.
You dont have to do the speech on a background thread.
IMO - you need to implement (TextToSpeech.OnInitListener, UtteranceProgressListener)
tts.setOnUtteranceProgressListener(new UtteranceProgressListener()
{
#Override
public void onDone(String utteranceId)
{
onDoneSpeaking(utteranceId);
}
#Override
public void onError(String utteranceId)
{
}
#Override
public void onStart(String utteranceId)
{
}
});
...
public void onInit(int status) {
if ( status == TextToSpeech.SUCCESS ) {
int result = tts.setLanguage( Locale.US );
if ( result == TextToSpeech.LANG_MISSING_DATA ||
result == TextToSpeech.LANG_NOT_SUPPORTED ) {
Log.e("MainActivity#onInit", "set lang error with "+result);
}
else {
tts.speak(mcomment.substring(left, right), TextToSpeech.QUEUE_FLUSH, null);
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();
}
};
}