Text to Speech Crashes - android

I've got a problem with android TTS.
I've built a game which announces a number for every cycle of game play. After a number of cycles the game crashes. Looking at the logcat it appear that the AudioPlayer is running out of room "AudioFlinger? no more track names available".
So after a little searching I've found this question.
AudioFlinger could not create track. status: -12
Which helpfully points out that Android has a hard limit of 32 active AudioTrack objects per device. Checking back to the logcat I found that it had announced 32 times. So seems that I am running out of objects.
So my initial idea was to shut down the TTS engine and re-initialise it after 30 announcements, there would be a short pause it game play but not too bad. This unfortunately didn’t do the trick. It seems that shutting down the TTS engine doesn’t reset the AudioTrack objects.
I've played around with the game and found that you can play up to before the crash, close the program, restart it and keep on playing with no crashes (until it reaches 32 continuous plays.) So there is a way to release the AudioTrack objects, in the logcat it talks about “AwesomePlayer reset” searching online I don't think there's is a way to control the AwesomePlayer.
So my question is, how do I clear the AudioTrack objects? There is a function “release()” and There is only one set of AudioTracks, I'm just not sure on how to get that to work.
Any other ideas on how to clear the AudoTrack objects would be welcome.
Thank TC
P.S. I'm pretty sure I've implemented the TTS correctly here's my code
private void playNumber() {
if (inPlay) {
numbersPlayed += 1;
Log.d("TOM", "playNumber");
Locale loc = new Locale("spa", "ESP");
mTts.setLanguage(loc);
String genNum = String.valueOf(generatedNumber);
Log.d("TOM", "genNum to String");
HashMap<String, String> map = new HashMap<String, String>();
Log.d("TOM", "toHashMap");
map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "TTS Stopped");
Log.d("TOM", "mapPut");
mTts.speak(genNum, TextToSpeech.QUEUE_FLUSH, map);
Log.d("TOM", "TTSSpeak");
mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
enterLock = true;
Log.d("TOM", "uttStart");
}
#Override
public void onDone(String utteranceId) {
enterLock = false;
timerCount.start();
Log.d("TOM", "uttDone");
}
#Override
public void onError(String utteranceId) {
Log.d("TOM", "uttError");
}
});
}
}
public void onInit(int i) {
if (generatedNumber == -2) {
enterLock = false;
Toast.makeText(this, getString(R.string.ttsReady_toast), Toast.LENGTH_SHORT).show();
Log.d("TOM", "onInit: " + Boolean.toString(inPlay));
} else {
playNumber();
Log.d("TOM", "New Start Play");
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_DATA_CHECK_CODE) {
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
mTts = new TextToSpeech(this, this);
Log.d("TOM", "ttsEngineOK");
} else {
Intent installIntent = new Intent();
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
Log.d("TOM", "ttsEngineError");
}
}
}

I figured out my problem.
I feel a bit silly really, the problem had nothing to do with the TTS engine. The actual problem lied in the mediaplayer clip that I was playing when the player got a correct answer. The way I had it set up, every time the answer was correct a new mediaplayer object was created and so this hit the 32 AudioTrack limit not the TTS. I've just put the mediaPlayer.create function in onCreate where it should have been and everything is fine now!!!
That teaches me for not putting all my code on, I'm sure someone with more experience would have noticed that straight away.

Related

Android media recording and unhandled events

Hi I am using android with java. I have set up a very simple button which when held down records audio and when released stops recording. I have two questions:
When I run the following implementation of my idea, I get runtime a warning mediarecorder went away with unhandled events every time the button is released. I can't find what is causing this! I see that this has been answered previously on this forum many years ago with the suggestion to add mediaRecorder.update(), but this does not address why the warning is occurring. What does it mean by unhandled events and what could be causing it? I have done nothing different I can see than in the documentation, other than using an onTouchListener...
Second, should I be wary of user's being able to switch on and off the button very rapidly - could this cause runtime problems and should I take steps to guard against this?
The relevant code I use is more-or-less this:
public void set() {
View.OnTouchListener recordOnTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
if (requestMultiplePermissions(Permissions).granted) {
audioSetup();
recordAudio();
}
return true;
case MotionEvent.ACTION_UP:
stopAudio();
return true;
default:
return false;
}
}
binding.addnewvocabRecordVocab.setOnTouchListener(recordOnTouchListener)
}
where
private void audioSetup() {
File filedir = new File(filepath);
if (!filedir.exists()) {filedir.mkdirs();
file = new File(filepath,filename);
if (file.exists()) { file.delete();}
}
public void recordAudio () {
isRecording = true;
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
}
try {
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setOutputFile(file);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaRecorder.start();
}
public void stopAudio () {
if (isRecording) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
isRecording = false;
}
}
This isn't a full answer to my own question, and the question still stands. But after playing with this a while, I have learnt a few lessons.
If the user taps the record button for a second, then mediarecorder.stop() will produce an error if there is not sufficient data recorded. See this. So if one wants to prevent the app from crashing, one needs to wrap mediarecorder.stop() in some catch - as the discussion in the link advises us. In fact, on some shortish taps, the stop method seems to take rather a long time (well over a second), so it might be worth considering disabling the button just before and after the stop method is called.
Another problem with fast repeated tapping is that it seems to keep adding to the main thread queue, which is probably inadvisable. I have found that using an executor thread with submit is a nice way of dealing with this. Schematically we can
public class audioLibrary() {
//or could go directly in main code
ExecutorService executor;
public void onStartRecord() {
if (executor == null) {
this.executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
//check isRecording and record stuff
});
}
}
public void onStopRecord() {
if (executor != null) {
executor.submit(() -> {
//check isRecording and shut down mediarecorder
//together with catch for onStop.
});
executor.shutdown();
}
}
}
Clearly, one must be a little careful to make sure the thread really does always get shut down.
Curiously, I do not get the `unhandled events' when I do this now...
Maybe someone could expand on this and comment on my code above to see whether they agree with the gist of it, whether it is necessary and on possible improvements.

Why is my sound script interfering with my advertises

I added sound effects to android game after I set up the rewarded advertises with admob on Unity. Here is my script for rewarded ads:
string adUnitId = "ca-app-pub-5920324855307233/4458481507";
RewardBasedVideoAd rewardBasedVideo = null;
void Start () {
managerScript = gameObject.GetComponent<GameManager>();
isCalled = false;
rewardBasedVideo = RewardBasedVideoAd.Instance;
}
public void adButton(){
isCalled = true;
AdRequest request = new AdRequest.Builder().Build();
rewardBasedVideo.OnAdRewarded += HandleRewardBasedVideoRewarded;
rewardBasedVideo.LoadAd(request, adUnitId);
}
void Update(){
if(isCalled == true){
adButton();
showAd();
}
}
public void showAd(){
if (rewardBasedVideo.IsLoaded()){
rewardBasedVideo.Show();
}
}
public void HandleRewardBasedVideoRewarded(object sender, Reward args){
isCalled = false;
managerScript.revival();
managerScript.Loading.SetActive(false);
rewardBasedVideo.OnAdRewarded -= HandleRewardBasedVideoRewarded;
}
Here is my script for sound effects (sound script):
public AudioClip Death;
public static bool toggled;
public static Sounds Instance;
public void DeathSound(){
if(toggled == true){
GetComponent<AudioSource>().PlayOneShot(Death);
}
}
public void SoundToggle(){
if(toggled == false){
toggled = true;
}else if(toggled == true){
toggled = false;
}
}
Finally how I call for a sound in another script:
Sounds sound;
GameObject soundManager;
public void Start(){
soundManager = GameObject.Find("Sound Manager");
sound = soundManager.GetComponent<Sounds>();
}
public void Death(){
sound.DeathSound();
}
The problem is for some reason when the boolean "toggled" is true in the Sound script, the HandleRewardBasedVideoRewarded method does not occur after the rewarded video advertise finishes. When sound is not toggled the method is called after the advertise and works fine. How is the sound effects being on affecting the method that happens after an advertise finishes? This problem is bothering me. Can someone help?
Update:
I tried disabling sound before the ad is loaded and then re enabling it after the ad. The problem still happens. Im not 100% certain but maybe the toggle bool has some effect, but I dont know how.
Either your code is not complete in the question or you made a mistake because GetComponent<AudioSource>().PlayOneShot(Death) is not being called anywhere in the script. You are calling DeathSound() from Death() but nothing is calling Death() to call DeathSound() which actually plays the sound. Take a closer look at your code. Call Death() function from somewhere and your sound should play.
EDIT:
Did not read your question well first time.
Before you call the showAd() function, Check if the audio is playing then stop playing it. Also change your Death from AudioClip to AudioSource. If possible don't use static in your code. It will introduce lots of other problems. If you want to share variables between scenes, you can do that with the PlayerPrefs class instead of using static variables.
public void showAd(){
if (Death.isPlaying)
{
Death.Stop();
}
if (rewardBasedVideo.IsLoaded()){
rewardBasedVideo.Show();
}
}

Time Difference Check- Android video capture

I am working on a video recording camera APP. App crashes if camera is stopped right after starting it maybe because of video size very less. I want to activate stop button only if video size is greater than 1 sec. But problem is I cannot find Current Time and Start time correctly. Finding the difference of two time factors will help in implementing 2 sec Check. Need Help please.
private void onClickActions(View v)
{
float tt = start_time /10000000000000f;
float ct = ((System.currentTimeMillis() ) /10000000000000f);
Log.d("Before stoping S-Time ",tt+"");
Log.d("Before stoping C-Time ",ct+"");
if (recording && tt>=2.0f)
{
Log.d("After Stopping = ",tt+"");
// stop recording and release camera
mediaRecorder.stop(); // stop the recording
recording = false;
rec.setVisibility(View.INVISIBLE);
start_time = 0;
}
//remove time code to initial revert
if(v.getId()== start.getId() && ((CameraPreview.recordHappy || CameraPreview.recordSad))) {
prepareMediaRecorder();
recording = true;
mediaRecorder.start();
consent = true;
happyRecorded=true;
stop.setClickable(true);
start.setClickable(false);
if (AndroidVideoCaptureExample.iV.getVisibility()==View.VISIBLE)
AndroidVideoCaptureExample.iV.setVisibility(View.INVISIBLE);
//AndroidVideoCaptureExample.capture.setText("RECORDING STARTED!");
rec.setVisibility(View.VISIBLE);
start_time = (int)(System.currentTimeMillis());
//Toast.makeText(myContext, "You are being recorded now!", Toast.LENGTH_LONG);
}
if(v.getId()== stop.getId() && consent==true && recording==false) {
if((!CameraPreview.recordHappy && CameraPreview.recordSad))
{
releaseMediaRecorder(); // release the MediaRecorder object
Intent intent = new Intent();
intent.setClass(AndroidVideoCaptureExample.this, consentActivity.class);
startActivity(intent);
finish();
}
else {
CameraPreview.recordHappy = false;
CameraPreview.recordSad = true;
stop.setClickable(false);
start.setClickable(true);
recording = false;
AndroidVideoCaptureExample.capture.setText("Record Neutral Moment");
rec.setVisibility(View.INVISIBLE);
}
}
}
I think you might be overengineering a simple thing. You don't really need to count record time unless you are showing it on the UI. If you want to disable the button, simply disable it just before starting the recording, then use Handler to re-enable after 2 seconds:
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
// enable stop button
}
},2000);
However, I would argue that's not a very good user experience. If you look at cameras like Google Camera, you can stop it immediately after starting, it just won't record anything. To achieve this, you need to catch the RuntimeException when calling mediaRecorder.stop(), then check and clean up the generated file. If it's empty then delete it and don't throw an error to the UI.

Play music synchronous using 3 MediaPlayer Objects on Android/Eclipse

What i have:
I have implemented three MediaPlayer.Objects in my App.
All Three are created using a thread:
protected void onResume() {
// Threads
mTT1 = new TrackThread(this, R.raw.audiofile1, 1, mHandler);
mTT2 = new TrackThread(this, R.raw.audiofile2, 2, mHandler);
mTT3 = new TrackThread(this, R.raw.audiofile3, 3, mHandler);
// start thread
mTT1.start();
mTT2.start();
mTT3.start();
super.onResume();
}
"simplified" Code in the Thread for creating:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
public void run() {
super.run();
try {
mMp.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getDeclaredLength());
mMp.prepare();
} catch (IllegalArgumentException | IllegalStateException
| IOException e) {
Log.e(TAG, "Unable to play audio queue do to exception: "
+ e.getMessage(), e);
}
}
As I read in several Tutorials the "prepare()" methode takes a little bit of time to finish. Therefore i implemented a "Waiting loop" which waits until all MPs are prepared and created.
When "prepare and create" are done i enable the Start button and i want to start all 3 Mediaplayers SIMULTANEOUSLY.
I again use a Thread for dooing so:
public void onClick(View v) {
// Button 1
if (mBtn.getId() == v.getId()) {
mTT1.startMusic();
mTT2.startMusic();
mTT3.startMusic();
}
Code in the thread:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
// start
public void startMusic() {
if (mMp == null)
return;
mMp.start();
}
Please note that the code above is not the full code, but it should be enough to define my problem.
What i want, My problem:
All MPs should play their Music in Sync, unfortunately sometimes when i start the music, there is a time delay between them.
The MPs must start at the exact same time as the 3Audio-files must be played simultaneously (and exactly in sync)
What i have already tried:
+) using SoundPool: My Audio-files are to big(5Megabyte and larger) for SoundPool
+) seekTo(msec): i wanted to seek every MP to a Specific time: eg.: 0, but this did not solve the problem.
+) to reach more Programmers i also asked this question on: coderanch.com
I hope somebody can help me!
Thanks in advance
The bottleneck here will certainly be preparing the mediaplayers to play. The Android framework provides an asynchronous method to perform this loading, and so with a bit of synchronization code you should be able to get these audio sources to play at roughly the same time. To keep from sound artifacting, you'll want less than 10ms of latency.
Initialize an atomic counter, C, to the number of things to load.
Use the prepareAsync() functions within MediaPlayer to prepare all three. Immediately after calling prepareAsync, supply a listener using setOnPreparedListener(listener).
Inside this listener, decrement C and check the value. If the value is greater than 0, wait on an object using the java object .wait() function. If the value is equal to 0, call notifyAll() on the object to wake up all of the other mediaplayer prepared-listener callback threads.
public void startMediaPlayers(List<MediaPlayer> mediaPlayers) {
private AtomicInteger counter = new AtomicInteger(mediaPlayers.size());
Object barrier = new Object();
/* start off all media players */
for (MediaPlayer player : mediaPlayers) {
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mediaPlayer) {
int value = counter.decrementAndGet();
if (value == 0) {
// all media players are done loading.
// wake up all the ones that are asleep
barrier.notifyAll();
} else {
while (value > 0) {
try {
// wait for everyone else to load
barrier.wait();
} catch (InterruptedException e) {
// ignore
}
}
}
mediaPlayer.start();
callback.success(true);
}
player.prepareAsync();
}
}
As nobody could help me I found a solution on my own. MediaPlayer did not fulfill my requirements but Android JETPlayer in combination with JETCreator did.
CAUTION: Installing Python for using JETCreator is very tricky, therfore
follow this tutorial. And be careful with the versions of python and wxpython, not all versions support the JETCreator.
I used:
Python Version 2.5.4 (python-2.5.4.msi)
wxPython 2.8 (wxPython2.8-win32-unicode-2.8.7.1-py25.exe)
For those who do not know how to implement the Jetplayer watch this video
(at min.5 he starts with programming the Jetplayer).
Unfortunately I do not speak French so I just followed the code which worked for me.
Using Android JETCreator you can create your own JET Files and use them as your resource.
Useful links:
Demo data
Manual
Code/class

How to make my TTS not overlap with Google Map voice (for driving direction)?

I am writing an app with TTS, I am able to use UtteranceProgressListener (don't worry about older version on this topic) to gain Audio focus from Music players, and give focus back to Music players after the speech is done. But the app TTS still overlaps with Google Maps (navigator) voices.
Is there anyway I can tell when Map is speaking, and my voice can be queued, or even flushed (because right now, I cannot discern/understand either of them when both of them are talking).
Or someone can point me to the source of Google Map (or hidden APIs that I can use), I understand that Google map is not part of the Android open source.
Below is snippet of my code:
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
private class utteranceListener extends UtteranceProgressListener {
private OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Pause playback
audioManager.abandonAudioFocus(afChangeListener);
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK){
// we don't duck, just abandon focus
audioManager.abandonAudioFocus(afChangeListener);
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
audioManager.abandonAudioFocus(afChangeListener);
}
}
};
#Override
public void onDone(String utteranceId)
{
audioManager.abandonAudioFocus(afChangeListener);
}
#Override
public synchronized void onError(String utteranceId)
{
audioManager.abandonAudioFocus(afChangeListener);
}
#Override
public void onStart(String utteranceId)
{
int result = audioManager.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
}
By the way, on music and TTS, I like old-fashion AUDIOFOCUS_LOSS_TRANSIENT better than AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK, a 'ducked' music still makes (TTS) speech hard to listen, especially when playing Rap music, which sometimes is just 'a speech/talk'.
I've got it mostly resolved, with one exception, when Google Map is talking, my code (below) still returns AUDIOFOCUS_REQUEST_GRANTED, so I still have overlapping here.
audioManager.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
On the things that work, two different concepts, UtterancePrograssListener is for events triggered by your own app, like whenever your speech is done or your speech is about to start; and OnAudioFocusChangeListener is for events triggered by other apps, like google maps grabbed audio focus from you.
So in case that I want to have my own app stop talking when Google Map is about to announce driving directions, below code would stop my talking:
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Someone else has taken over the audio focus,
// abandonAudioFocus() would not do anything.
// you can only stop(), or lower your voice.
tts.stop();
audioManager.abandonAudioFocus(afChangeListener);
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK){
// this is what Google Map actually triggers
tts.stop();
audioManager.abandonAudioFocus(afChangeListener);
}
.....

Categories

Resources