Text to speech stopped speaking but loop to pause does not stop - android

I want to add pause and play functionality in tts. what is have done so far is, when i stop tts speaking and want to get the last spoken word but the loop which is running not getting stop. please resolve my problem.
public void toggleAudioMode() {
isPlayerVisible = !isPlayerVisible;
ivToggleAudioModeAnswer.setImageResource(isPlayerVisible ? R.drawable.ic_audio_off : R.drawable.ic_audio_on);
if (isPlayerVisible) {
splitspeech = result.split(" ");
if (j > 0) {
speakSpeech(j, "run");
} else {
speakSpeech(0, "run");
}
} else {
keepGoing = false;
speakSpeech(0, "stop");
}
}
public void speakSpeech(int m, String type) {
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "done");
while (i < splitspeech.length) {
if (type.equals("run")) {
if (i == 0) { // Use for the first splited text to flush on audio stream
textToSpeech.speak(splitspeech[i].trim(), QUEUE_FLUSH, myHash);
} else { // add the new text on previous then play the TTS
textToSpeech.speak(splitspeech[i].trim(), QUEUE_ADD, myHash);
j = i;
}
}
i++;
}
if (type.equals("stop")) {
textToSpeech.stop();
}
}
i want play and pause functionality.

Related

MediaPlayer is not running the previous sound in android either after released the object and initialized its again

There are two onClickListener one for move forward and the second one is to move backward. I have an array of images, sounds and texts. by pressing forward it moves to the next record and with backward it moves to previous. I faced an issue that if a sound played for a single time at any position. when i tried to move on it by pressing backward then the sound isn't plays. it only plays for a single time if i want to plays it more than one time it isn't plays and break and generates an exception of null pointer to global_media_player. Here global_media_player is the global object.
forward.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (count >= 0 && count < (list.size() - 1)) {
count++;
Toast.makeText(getApplicationContext(),"Before Stop "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
// if(global_media_player.isPlaying()) {
// global_media_player.stop();
// Toast.makeText(getApplicationContext(),"During Stop "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
// }
BookData data = list.get(count);
textView.setText(data.getName());
imageView.setImageDrawable(data.getImage());
Animation slide = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide);
textView.startAnimation(slide);
try {
Toast.makeText(getApplicationContext(),"Before Releasing "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
if (global_media_player != null) {
global_media_player.release();
Toast.makeText(getApplicationContext(),"During Releasing "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
}
Toast.makeText(getApplicationContext(),"Before Initialing "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
global_media_player = data.getAudio();
Toast.makeText(getApplicationContext(),"Before Start "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
global_media_player.start();
Toast.makeText(getApplicationContext(),"After Start "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
backward.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (count > 0 && count < list.size()) {
count--;
Toast.makeText(getApplicationContext(),"Before Stop "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
// if(global_media_player.isPlaying()) {
// global_media_player.stop();
// Toast.makeText(getApplicationContext(),"During Stop "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
// }
BookData data = list.get(count);
textView.setText(data.getName());
imageView.setImageDrawable(data.getImage());
Animation slide = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide);
textView.startAnimation(slide);
try {
Toast.makeText(getApplicationContext(),"Before Releasing "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
if (global_media_player != null) {
global_media_player.release();
global_media_player = null;
Toast.makeText(getApplicationContext(),"During Releasing "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
}
Toast.makeText(getApplicationContext(),"Before Initialing "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
global_media_player = data.getAudio();
Toast.makeText(getApplicationContext(),"Before Start "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
global_media_player.start();
Toast.makeText(getApplicationContext(),"After Start "+global_media_player.toString(),Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});

Cannot close turnbased multiplayer match with Google Play Games

I have a problem closing my game after the last player close the last match. My turn scheme is:
Player A
Player B
Player B
Player A
Player A
Player B
The game works well but in turn "6" when player B try to close the match, player A always see the matsh as "my turn" and not as "completed"
here there is the code thar rules turns and game ended:
#Override
public void onGameEnd(final NMXGameData updatedData) {
super.onGameEnd(updatedData);
if (updatedData.getMatchNumber() == NMXGameConfig.MATCHES) {
boolean iWin = updatedData.getResultPoints()[1] > updatedData.getResultPoints()[0];
boolean tile = updatedData.getResultPoints()[1] == updatedData.getResultPoints()[0];
ParticipantResult opponentParticipantResult;
ParticipantResult myParticipantResult;
if (tile) {
opponentParticipantResult = new ParticipantResult(getOpponentId(), ParticipantResult.MATCH_RESULT_TIE, 1);
myParticipantResult = new ParticipantResult(getCurrentPlayerId(), ParticipantResult.MATCH_RESULT_TIE, 1);
} else {
if (iWin) {
opponentParticipantResult = new ParticipantResult(getOpponentId(), ParticipantResult.MATCH_RESULT_LOSS, 2);
myParticipantResult = new ParticipantResult(getCurrentPlayerId(), ParticipantResult.MATCH_RESULT_WIN, 1);
} else {
opponentParticipantResult = new ParticipantResult(getOpponentId(), ParticipantResult.MATCH_RESULT_WIN, 1);
myParticipantResult = new ParticipantResult(getCurrentPlayerId(), ParticipantResult.MATCH_RESULT_LOSS, 2);
}
}
ArrayList<ParticipantResult> participantResultArrayList = new ArrayList<>();
participantResultArrayList.add(opponentParticipantResult);
participantResultArrayList.add(myParticipantResult);
Games.TurnBasedMultiplayer.finishMatch(getApiClient(), match.getMatchId(), new Gson().toJson(updatedData).getBytes(), opponentParticipantResult, myParticipantResult).setResultCallback(new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(TurnBasedMultiplayer.UpdateMatchResult updateMatchResult) {
finish();
}
});
} else if (updatedData.getMatchNumber() < NMXGameConfig.MATCHES) {
if (getNextPlayerIndex(updatedData.getMatchNumber()) != getNextPlayerIndex(updatedData.getMatchNumber() - 1)) {
Games.TurnBasedMultiplayer.takeTurn(getApiClient(), match.getMatchId(), new Gson().toJson(updatedData).getBytes(), getNextParticipantId());
} else {
Games.TurnBasedMultiplayer.takeTurn(getApiClient(), match.getMatchId(), new Gson().toJson(updatedData).getBytes(), getCurrentPlayerId());
startActivity(startNewOnlineGameIntent(this, updatedData, match.getMatchId()));
}
finish();
}
}
private String getCurrentPlayerId() {
return match.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
}
private String getOpponentId() {
for (String id : match.getParticipantIds()) {
if (!id.equals(getCurrentPlayerId())) {
return id;
}
}
return null;
}
private int getNextPlayerIndex(int nextRoundIndex) {
nextRoundIndex = nextRoundIndex + 1;
return (nextRoundIndex / 2) % 2;
}
I finally figured it out.
I don't know if that is the desired behavior but when in round 6 player_B calls:
Games.TurnBasedMultiplayer.finishMatch(getApiClient(), match.getMatchId(), new Gson().toJson(updatedData).getBytes(), opponentParticipantResult, myParticipantResult).setResultCallback(new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(TurnBasedMultiplayer.UpdateMatchResult updateMatchResult) {
finish();
}
});
The turn goes to player_A that see that match as "my turn". At this point player A must call Games.TurnBasedMultiplayer.finishMatch(getApiClient(), match.getMatchId()) (without playing a real game) and the game is completed for both players

AudioRecord Out of Memory Exception

I created an object that continuously reads the input from the mic but I'm having some troubles with it. I need to be able to pause and start it, so I've implemented the methods, but the problem is that, on an older phone (Galaxy Y) it works very well the first time I start it, but after I pause it and start it again it quickly crashes due to OutOfMemoryException.
#Override
public void run() {
while (mIsRunning) {
synchronized (mThread) {
while (mIsPaused) {
try {
mThread.wait();
} catch (InterruptedException e) {
}
}
}
double sum = 0;
int readSize = mRecorder.read(mBuffer, 0, mBuffer.length);
if (readSize > 0) {
short[] samples = new short[readSize];
for (int i = 0; i < readSize; i++) {
sum += mBuffer[i] * mBuffer[i];
samples[i] = mBuffer[i];
}
final int amplitude = (int) Math.sqrt((sum / readSize));
Message message = new Message();
message.arg1 = amplitude;
message.arg2 = readSize;
message.obj = samples;
message.what = 0;
if (mHandler != null)
mHandler.sendMessage(message);
}
}
}
public void start() {
if (!mIsRunning) {
mIsRunning = true;
mThread.start();
} else synchronized (mThread) {
mIsPaused = false;
mThread.notifyAll();
}
mRecorder.startRecording();
}
public void pause() {
synchronized (mThread) {
mIsPaused = true;
}
mRecorder.stop();
}
So, the first time all works well, but after I call stop() on the AudioRecord and start reading the input again I quickly run out of memory. Any ideas on how to fix this? I was thinking of just not stopping the recording and just not sending the data through the handler but I don't know. Thanks.
The solution was destroying (and releasing) the AudioRecord object on every pause and just recreating it on each start.

continuous sending of data

The android application is bulit in such a way that when data is being sent(on clicking the button) to the usb device ,LED toggles and the data is being displayed. I want data to be sent continuously without any manual intervention. Kindly help
if (mInputStream != null)
{
int Data = 0;
try {
Data = mInputStream.read();
} catch (IOException e) {
}
if (Data == LED_ON)
{
ledStatus.setText("LED is ON");
}
else if (Data == LED_OFF)
{
ledStatus.setText("LED is OFF");
}
else
{
ledStatus.setText("Request failed");
}
}
else
{
ledStatus.setText("mInputStream == null");
}
this time I have tested it myself...
it should definitely work
paste it in your onCreate() good luck :)
Timer t = new Timer();
t.schedule(new TimerTask(){
public void run(){
// write the method name here. which you want to call continuously
Log.d("timer", "timer");
}
},10, 1000);

How to pause android.speech.tts.TextToSpeech?

I'm playing text with android TTS - android.speech.tts.TextToSpeech
I use: TextToSpeech.speak to speak and .stop to stop. Is there a way to pause the text also?
The TTS SDK doesn't have any pause functionality that I know of. But you could use synthesizeToFile() to create an audio file that contains the TTS output. Then, you would use a MediaPlayer object to play, pause, and stop playing the file. Depending on how long the text string is, it might take a little longer for audio to be produced because the synthesizeToFile() function would have to complete the entire file before you could play it, but this delay should be acceptable for most applications.
I used splitting of string and used playsilence() like below:
public void speakSpeech(String speech) {
HashMap<String, String> myHash = new HashMap<String, String>();
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "done");
String[] splitspeech = speech.split("\\.");
for (int i = 0; i < splitspeech.length; i++) {
if (i == 0) { // Use for the first splited text to flush on audio stream
textToSpeech.speak(splitspeech[i].toString().trim(),TextToSpeech.QUEUE_FLUSH, myHash);
} else { // add the new test on previous then play the TTS
textToSpeech.speak(splitspeech[i].toString().trim(), TextToSpeech.QUEUE_ADD,myHash);
}
textToSpeech.playSilence(750, TextToSpeech.QUEUE_ADD, null);
}
}
You can make the TTS pause between sentences, or anywhere you want by adding up to three periods (".") all followed by a single space " ". The example below has a long pause at the beginning, and again before the message body. I'm not sure that is what you are after though.
private final BroadcastReceiver SMScatcher = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(
"android.provider.Telephony.SMS_RECEIVED")) {
// if(message starts with SMStretcher recognize BYTE)
StringBuilder sb = new StringBuilder();
/*
* The SMS-Messages are 'hiding' within the extras of the
* Intent.
*/
Bundle bundle = intent.getExtras();
if (bundle != null) {
/* Get all messages contained in the Intent */
Object[] pdusObj = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdusObj.length];
for (int i = 0; i < pdusObj.length; i++) {
messages[i] = SmsMessage
.createFromPdu((byte[]) pdusObj[i]);
}
/* Feed the StringBuilder with all Messages found. */
for (SmsMessage currentMessage : messages) {
// periods are to pause
sb.append("... Message From: ");
/* Sender-Number */
sb.append(currentMessage.getDisplayOriginatingAddress());
sb.append(".. ");
/* Actual Message-Content */
sb.append(currentMessage.getDisplayMessageBody());
}
// Toast.makeText(application, sb.toString(),
// Toast.LENGTH_LONG).show();
if (mTtsReady) {
try {
mTts.speak(sb.toString(), TextToSpeech.QUEUE_ADD,
null);
} catch (Exception e) {
Toast.makeText(application, "TTS Not ready",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
}
}
};
If you omit the space after the last period it will (or may) not work as expected.
In the absence of a pause option, you can add silence for the duration of when you want to delay the TTS Engine speaking. This of course would have to be a predetermined 'pause' and wouldn't help to include functionality of a pause button, for example.
For API < 21 : public int playSilence (long durationInMs, int queueMode, HashMap params)
For > 21 : public int playSilentUtterance (long durationInMs, int queueMode, String utteranceId)
Remember to use TextToSpeech.QUEUE_ADD rather than TextToSpeech.QUEUE_FLUSH otherwise it will clear the previously started speech.
I used a different approach.
Seperate your text into sentences
Speak every sentence one by one and keep track of the spoken sentence
pause will stop the text instantly
resume will start at the beginning of the last spoken sentence
Kotlin code:
class VoiceService {
private lateinit var textToSpeech: TextToSpeech
var sentenceCounter: Int = 0
var myList: List<String> = ArrayList()
fun resume() {
sentenceCounter -= 1
speakText()
}
fun pause() {
textToSpeech.stop()
}
fun stop() {
sentenceCounter = 0
textToSpeech.stop()
}
fun speakText() {
var myText = "This is some text to speak. This is more text to speak."
myList =myText.split(".")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, null, utteranceId)
sentenceCounter++
} else {
var map: HashMap<String, String> = LinkedHashMap<String, String>()
map[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = utteranceId
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, map)
sentenceCounter++
}
}
override fun onDone(p0: String?) {
if (sentenceCounter < myList.size) {
speakText()
} else {
speakNextText()
}
}
}
I haven't yet tried this, but I need to do the same thing. My thinking is to first split your speech text into an array of words.
Then create a recursive function that plays the next word after the current word is finished, while keeping a counter of the current word.
divide the messages into parts and listen for last utterance by using onutteranceprogress listener
tts.playSilence(1250, TextToSpeech.QUEUE_ADD, null);
It seems that if you put a period after a word AND start the next word with a capital letter, just like a new sentence, like this:
after we came home. We ate dinner.
the "home. We" will then have a pause in it.
This becomes a grammatically strange way of writing it.
So far I have only tested this in my own language, Swedish.
It might be important that the space is there.
Also, an escaped quote (\") seems to have it pause somewhat as well - at least, if you put it around a word it adds space around the word.
This solution is not perfect, but an alternative to #Aaron C's solution may be to create a custom text to speech class like the below. This solution may work well enough if your text is relatively short and spoken words per minute is accurate enough for the language you are using.
private class CustomTextToSpeech extends TextToSpeech {
private static final double WORDS_PER_MS = (double)190/60/1000;
long startTimestamp = 0;
long pauseTimestamp = 0;
private Handler handler;
private Runnable speakRunnable;
StringBuilder textToSpeechBuilder;
private boolean isPaused = false;
public CustomTextToSpeech(Context context, OnInitListener initListener){
super(context, initListener);
setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String arg0) {
Log.d(TAG, "tts done. " + arg0);
startTimestamp = 0;
pauseTimestamp = 0;
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
#Override
public void onError(String arg0) {
Log.e(TAG, "tts error. " + arg0);
}
#Override
public void onStart(String arg0) {
Log.d(TAG, "tts start. " + arg0);
setStartTimestamp(System.currentTimeMillis());
}
});
handler = new Handler();
speakRunnable = new Runnable() {
#Override
public void run() {
speak();
}
};
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
public void setStartTimestamp(long timestamp) {
startTimestamp = timestamp;
}
public void setPauseTimestamp(long timestamp) {
pauseTimestamp = timestamp;
}
public boolean isPaused(){
return (startTimestamp > 0 && pauseTimestamp > 0);
}
public void resume(){
if(handler != null && isPaused){
if(startTimestamp > 0 && pauseTimestamp > 0){
handler.postDelayed(speakRunnable, TTS_SETUP_TIME_MS);
} else {
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
isPaused = false;
}
public void pause(){
isPaused = true;
if (handler != null) {
handler.removeCallbacks(speakRunnable);
handler.removeMessages(1);
}
if(isSpeaking()){
setPauseTimestamp(System.currentTimeMillis());
}
stop();
}
public void utter(){
if(handler != null){
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
public void speak(){
Log.d(TAG, "textToSpeechBuilder: " + textToSpeechBuilder.toString());
if(isPaused()){
String[] words = textToSpeechBuilder.toString().split(" ");
int wordsAlreadySpoken = (int)Math.round((pauseTimestamp - startTimestamp)*WORDS_PER_MS);
words = Arrays.copyOfRange(words, wordsAlreadySpoken-1, words.length);
textToSpeechBuilder = new StringBuilder();
for(String s : words){
textToSpeechBuilder.append(s);
textToSpeechBuilder.append(" ");
}
} else {
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
if (tts != null && languageAvailable)
speak(textToSpeechBuilder.toString(), TextToSpeech.QUEUE_FLUSH, new Bundle(), "utter");
}
}

Categories

Resources