I'm implementing an Interactive Voice Response application on Android. I would like to know how to determine when the tts.speak() function has done talking so I can call my speech recognizer function.
public class TTSActivity extends Activity implements OnInitListener, OnUtteranceCompletedListener, ... {
private TextToSpeech mTts;
...........
private void speak(String text) {
if(text != null) {
HashMap<String, String> myHashAlarm = new HashMap<String, String>();
myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_ALARM));
myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "SOME MESSAGE");
mTts.speak(text, TextToSpeech.QUEUE_FLUSH, myHashAlarm);
}
}
// Fired after TTS initialization
public void onInit(int status) {
if(status == TextToSpeech.SUCCESS) {
mTts.setOnUtteranceCompletedListener(this);
}
}
// It's callback
public void onUtteranceCompleted(String utteranceId) {
Log.i(TAG, utteranceId); //utteranceId == "SOME MESSAGE"
}
...........
}
Read A good tutorial
The setOnUtteranceCompletedListener is deprecated since API level 15. Instead, use setOnUtteranceProgressListener.
I found a code snippet (here) that made it really easy for me to know when text to speech is finished:
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
myTTS.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String utteranceId) {
// Log.d("MainActivity", "TTS finished");
}
#Override
public void onError(String utteranceId) {
}
#Override
public void onStart(String utteranceId) {
}
});
} else {
Log.e("MainActivity", "Initilization Failed!");
}
}
http://www.codota.com/android/scenarios/52fcbd34da0ae25e0f855408/android.speech.tts.TextToSpeech?tag=dragonfly
To know when TTS is finished you have to call the setOnUtteranceProgressListener which has 3 call back methods onStart,onDone and onError
then include a Utterance Id to the speak method
Code Snippet
textToSpeech=new TextToSpeech(this, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if (status==TextToSpeech.SUCCESS){
int result=textToSpeech.setLanguage(Locale.ENGLISH);
if (result==TextToSpeech.LANG_MISSING_DATA||result==TextToSpeech.LANG_NOT_SUPPORTED){
Log.i("TextToSpeech","Language Not Supported");
}
textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
Log.i("TextToSpeech","On Start");
}
#Override
public void onDone(String utteranceId) {
Log.i("TextToSpeech","On Done");
}
#Override
public void onError(String utteranceId) {
Log.i("TextToSpeech","On Error");
}
});
}else {
Log.i("TextToSpeech","Initialization Failed");
}
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak(text,TextToSpeech.QUEUE_FLUSH,null,TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
}
I noticed that there are people having problems in the use of TextToSpeech to ask that the solution to you
How to know when TTS is finished?
without use setOnUtteranceCompletedListener
public void isTTSSpeaking(){
final Handler h =new Handler();
Runnable r = new Runnable() {
public void run() {
if (!tts.isSpeaking()) {
onTTSSpeechFinished();
}
h.postDelayed(this, 1000);
}
};
h.postDelayed(r, 1000);
}
Try this following code which shows a toast after TTS completed. Replace toast with your own action.
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();
}
}; }
With this code I solved the problem. I wanted the start button to stay lit as long as someone was speaking. Maybe this will help someone.
public void _tts_speak_lenght() {
t2 = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (TTS.isSpeaking()) {
isSpeaking = true;
}
else {
if (!TTS.isSpeaking()) {
t2.cancel();
isSpeaking = false;
imgBtn_startReader.setImageResource(R.drawable.ic_start_off);
}
}
}
});
}
};
_timer.scheduleAtFixedRate(t2, (int)(0), (int)(100));
}
use a SpeakableListener when do text to speak.
private volatile boolean finished;
private SpeakableListener listener = new SpeakableListener() {
#Override
public void markerReached(SpeakableEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void speakableCancelled(SpeakableEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void speakableEnded(SpeakableEvent arg0) {
TestSpeaker.this.finished = true;
}
#Override
public void speakablePaused(SpeakableEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void speakableResumed(SpeakableEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void speakableStarted(SpeakableEvent arg0) {
TestSpeaker.this.finished = false;
}
#Override
public void topOfQueue(SpeakableEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void wordStarted(SpeakableEvent arg0) {
// TODO Auto-generated method stub
}
};
/** Speak the following string. */
public void sayIt(String str) {
System.out.print(" " + str + " ");
/* The the synthesizer to speak. */
synthesizer.speakPlainText(str, listener);
Related
beacon manager.addMonitorNotifier(new MonitorNotifier()
in this method, there are two methods and both are not running .... it is a small app of beacon I want to show the alert .... and that alert in my method in if condition ... but it is not running I did debugger but it is not working
import java.util.Collection;
public class WebViewScreen extends AppCompatActivity implements BeaconConsumer {
public static final String TAG="MainActivity";
private Button startbtn,stopbtn;
private BeaconManager beaconManager=null;
private static final String ALTBEACON_LAYOUT="m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25";
private Region beaconRegion=null;
private void ShowAlert(final String title, final String message){
runOnUiThread (new Thread(new Runnable() {
public void run() {
AlertDialog alertDialog = new AlertDialog.Builder(WebViewScreen.this).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
}));
}
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
Log.d(TAG,"on create called");
startbtn=findViewById(R.id.startButton);
stopbtn=findViewById(R.id.stopButton);
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},1234);
beaconManager=BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(ALTBEACON_LAYOUT));
beaconManager.bind(this);
startbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startBiconMonitoring();
}
});
stopbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopBiconMonitoring();
}
});
}
private boolean entryMessageRaised=false;
private boolean exitMessageRaised=false;
private boolean rangingMessageRaised=false;
private void stopBiconMonitoring() {
Log.d(TAG,"Stop Beacon Monitering");
try {
beaconManager.stopMonitoringBeaconsInRegion(beaconRegion);
beaconManager.stopRangingBeaconsInRegion(beaconRegion);
}catch (RemoteException e){
e.printStackTrace();
}
}
private void startBiconMonitoring() {
Log.d(TAG,"Start Beacon Monitering");
try {
beaconRegion = new Region("My Bicons",Identifier.parse("B9407F30-F5F8-466E-AFF9-25556B57FE6D"),Identifier.parse("4"),Identifier.parse("200"));
beaconManager.startMonitoringBeaconsInRegion(beaconRegion);
beaconManager.startRangingBeaconsInRegion(beaconRegion);
}catch (RemoteException e){
e.printStackTrace();
}
}
private void findID() {
}
#Override
public void onBeaconServiceConnect() {
beaconManager.addMonitorNotifier(new MonitorNotifier() {
#Override
public void didEnterRegion(Region region) {
if (!entryMessageRaised){
showAlert("didEnterRegion","Entering Region"+region.getUniqueId()+"Beacon Detected UUID/Major/Minor:"+region.getId1()+"/"+region.getId2()+"/"+region.getId3());
entryMessageRaised=true;
}
else {
Log.d(TAG,"somrething go wrong");
}
}
#Override
public void didExitRegion(Region region) {
if (!exitMessageRaised){
showAlert("didExitRegion","Exiting Region"+region.getUniqueId()+"Beacon Detected UUID/Major/Minor:"+region.getId1()+"/"+region.getId2()+"/"+region.getId3());
exitMessageRaised=true;
}
}
#Override
public void didDetermineStateForRegion(int i, Region region) {
}
});
beaconManager.addRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> collection, Region region) {
if (rangingMessageRaised && collection != null && !collection.isEmpty()){
for (Beacon beacon:collection){
showAlert("didExitRegion","Ranging Region"+region.getUniqueId()+"Beacon Detected UUID/Major/Minor:"+beacon.getId1()+"/"+beacon.getId2()+"/"+beacon.getId3());
}
rangingMessageRaised=true;
}
}
});
}
}
I want to show the alert if the device will found bacon
Here's my code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_route);
SetupButton();
}
private void SetupButton()
{
Button createNewMessage = (Button) findViewById(R.id.button);
createNewMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ListenForNewMessage();
}
});
}
private void ListenForNewMessage()
{
final SpeechRecognizer newDeliverySpeech = SpeechRecognizer.createSpeechRecognizer(this);
RecognitionListener newDeliveryRecognitionListener = new RecognitionListener() {
#Override
public void onReadyForSpeech(Bundle params) {
Log.d("SpeechListening","onReadyForSpeech");
}
#Override
public void onBeginningOfSpeech() {
Log.d("SpeechListening","onBeginningOfSpeech");
}
#Override
public void onRmsChanged(float rmsdB) {
//do nothing
}
#Override
public void onBufferReceived(byte[] buffer) {
//do nothing
}
#Override
public void onEndOfSpeech() {
Log.d("SpeechListening","onEndOfSpeech");
}
#Override
public void onError(int error) {
//do nothing
}
#Override
public void onResults(Bundle results) {
ArrayList<String> userMessage;
userMessage = results.getStringArrayList(RESULTS_RECOGNITION);
PushNewDelivery(userMessage);
}
#Override
public void onPartialResults(Bundle partialResults) {
//do nothing
}
#Override
public void onEvent(int eventType, Bundle params) {
//do nothing
}
};
newDeliverySpeech.setRecognitionListener(newDeliveryRecognitionListener);
if (newDeliverySpeech.isRecognitionAvailable(getApplicationContext()))
{
Log.d("SpeechListening","started listening hopefully");
newDeliverySpeech.startListening(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH));
}
}
}
The problem is, only the started listening hopefully is logged, the RecognitionListener never has onReadyForSpeech() or any of its methods called.
details
details
details
details
Can someone please tell me what I'm doing wrong here?
You are basically creating a new SpeechRecognizer object and register a new listener each time you click on the button. On top of that you create the SpeechRecognizer using the current Activity Context but you are actually using the Application context when calling: isRecognitionAvailable();
Try to create the SpeechRecognizer as a member object and register your listener when onCreate() is called. Also try to avoid using the Application context to avoid memory leaks.
Here is an example of how you should do it.
private SpeechRecognizer mDeliverySpeech;
private Intent mSpeechIntent;
private boolean mListening = false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_route);
SetupButton();
SetupSpeechRecognizer();
mSpeechIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mSpeechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "en");
mSpeechIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, this.getPackageName());
mSpeechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
mSpeechIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);
}
private void SetupButton()
{
Button createNewMessage = (Button) findViewById(R.id.button);
createNewMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ListenForNewMessage();
}
});
}
private void SetupSpeechRecognizer()
{
mDeliverySpeech = SpeechRecognizer.createSpeechRecognizer(this);
RecognitionListener newDeliveryRecognitionListener = new RecognitionListener() {
#Override
public void onReadyForSpeech(Bundle params) {
Log.d("SpeechListening","onReadyForSpeech");
}
#Override
public void onBeginningOfSpeech() {
Log.d("SpeechListening","onBeginningOfSpeech");
}
#Override
public void onRmsChanged(float rmsdB) {
//do nothing
}
#Override
public void onBufferReceived(byte[] buffer) {
//do nothing
}
#Override
public void onEndOfSpeech() {
Log.d("SpeechListening","onEndOfSpeech");
}
#Override
public void onError(int error) {
//do nothing
}
#Override
public void onResults(Bundle results) {
ArrayList<String> userMessage;
userMessage = results.getStringArrayList(RESULTS_RECOGNITION);
PushNewDelivery(userMessage);
}
#Override
public void onPartialResults(Bundle partialResults) {
//do nothing
}
#Override
public void onEvent(int eventType, Bundle params) {
//do nothing
}
};
mDeliverySpeech.setRecognitionListener(newDeliveryRecognitionListener);
}
private void ListenForNewMessage()
{
if (mDeliverySpeech.isRecognitionAvailable(this) && !mListening)
{
Log.d("SpeechListening","started listening hopefully");
mListening = true;
mDeliverySpeech.startListening(mSpeechIntent);
new CountDownTimer(5000, 5000) {
public void onTick(long millisUntilFinished) {}
public void onFinish() {
mDeliverySpeech.stopListening();
mListening = false;
}
}.start();
}
}
Do not forget to properly handle the activity life cycle when working with listener to avoid memory leak.
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);
}
});
I've a viewpager in my application. When the user swipes to the right/left I use TTS engine to speak the text and MediaPlayer to play a sound.
The problem is both plays simultaneously.. How do I play the sound once TTS engine speaks the text is over?
P.S: I don't want to use sleep or wait.
Update:
Here is my code:
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
if (result == TextToSpeech.LANG_MISSING_DATA
|| result == TextToSpeech.LANG_NOT_SUPPORTED) {
} else {
//Do Something here
}
if(Build.VERSION.SDK_INT >= 15 ){
UtteranceProgressListener listener = new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
// TODO Auto-generated method stub
}
#Override
public void onError(String utteranceId) {
// TODO Auto-generated method stub
}
#Override
public void onDone(String utteranceId) {
// TODO Auto-generated method stub
//start MediaPlayer
playMedia(viewPager.getCurrentItem());
}
};
tts.setOnUtteranceProgressListener(listener);
}
else{
tts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener(){
#Override
public void onUtteranceCompleted(String arg0) {
playMedia(viewPager.getCurrentItem());
}
});
}
} else {
Intent installIntent = new Intent();
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
}
You have to give it an id param. Otherwise it doesn't call the listeners:
HashMap<String, String> params = new HashMap<String, String>();
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,"stringId");
textToSpeech.speak(string,TextToSpeech.QUEUE_ADD, params);
In android their are two APIs to detect if the TTS engine finished speaking:
> Android 4 (ICS)
UtteranceProgressListener listener = new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
// TODO Auto-generated method stub
}
#Override
public void onError(String utteranceId) {
// TODO Auto-generated method stub
}
#Override
public void onDone(String utteranceId) {
// TODO Auto-generated method stub
//start MediaPlayer
}
};
yourTTSObject.setOnUtteranceProgressListener(listener);
Perior to ICS Android 4.0 you can use :
yourTTSObject.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener(){
#Override
public void onUtteranceCompleted(String arg0) {
// start your mediaplayer here
}
});
take a look at the documentations here
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();
}
};
}