MediaPlayer or SoundPool for multiple short sounds? - java

The app basically allows the user to play an animal sound, voice or other sound when clicking various items. I'm trying to figure out if I'm doing this in the right way because I'm seeing some issues like this error, when the user mutes>unmutes>plays a sound:
java.lang.IllegalStateException at
android.media.MediaPlayer.isPlaying(Native Method)
public class GuessActivity extends Activity implements PopupMenu.OnMenuItemClickListener {
public static int[] ssSoundsArray = {R.raw.sbuffalo, R.raw.scamel, R.raw.scat, R.raw.schicken};
public static int[] ssVoicesArray = {R.raw.buffalo, R.raw.camel, R.raw.cat, R.raw.chicken};
MediaPlayer mMediaPlayer;
Context context;
...
}
Here's an example of how I am using MediaPlayer:
if(audio_all) {
if(mMediaPlayer != null) {
if(mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
mMediaPlayer.release();
}
}
mMediaPlayer = MediaPlayer.create(this, R.raw.whatever);
mMediaPlayer.start();
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mediaPlayer) {
mMediaPlayer.release();
mMediaPlayer = null;
}
});
}
I'm doing this multiple times for different buttons etc, making sure to release() after they are done since I ran into mem issues before. So the exception is telling above is telling me isPlaying() isn't valid since MediaPlayer does not exist but for some reason it ignores if(mMediaPlayer != null)
It is necessary to stop if anything is playing since the user is free to click randomly and of course I do not want any sounds overlaying eachother.
Here's how the user mutes:
case R.id.action_toggle_sounds:
if(audio_all) {
if(mMediaPlayer != null) {
if(mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
mMediaPlayer.release();
}
}
Toast.makeText(this,"All sounds disabled",Toast.LENGTH_LONG).show();
audio_all = false;
} else {
Toast.makeText(this,"All sounds enabled",Toast.LENGTH_LONG).show();
audio_all = true;
}
return true;
I very much appreciate any help with this, thanks a lot!

If you has not so much tracks to play (not more than 32) you can try to initialize multiple MediaPlayer instances and release() them only when app onPause()/onStop() called.
Then you will have pool of ready to use MediaPlayer's. You just have to remember which one is in use right now.
Also if you use MediaPlayer in multiple threads (not only UI thread) then you MUST use mutex (thread blocking) to avoid issues.
MediaPlayer based state machine - IllegalStateException basicly means player instance is in state when calling isPlaying() not allowed. For isPlaying() documented not allowed state is only Error state, but assume state after calling release() may be also not appropriate for this.

Related

Putting a sound effect in a java program [duplicate]

I'm looking to do a very simple piece of code that plays a sound effect. So far I have this code:
SoundManager snd;
int combo;
private void soundSetup() {
// Create an instance of the sound manger
snd = new SoundManager(getApplicationContext());
// Set volume rocker mode to media volume
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
// Load the samples from res/raw
combo = snd.load(R.raw.combo);
}
private void playSound() {
soundSetup();
snd.play(combo);
}
However, for some reason when I use the playSound() method, nothing happens. The audio file is in the correct location.
Is there a specific reason you are using SoundManager? I would use MediaPlayer instead, here is a link to the Android Docs
http://developer.android.com/reference/android/media/MediaPlayer.html
then it's as simple as
MediaPlayer mp = MediaPlayer.create(getApplicationContext(), R.raw.combo);
mp.start();
Make a directory called "raw/" under the "res/" directory. Drag wav or mp3 files into the raw/ directory. Play them from anywhere as above.
i have also attempted using the top answer, yet it resulted in NullPointerExceptions from the MediaPlayer when i tried playing a sound many times in a row, so I extended the code a bit.
FXPlayer is my global MediaPlayer.
public void playSound(int _id)
{
if(FXPlayer != null)
{
FXPlayer.stop();
FXPlayer.release();
}
FXPlayer = MediaPlayer.create(this, _id);
if(FXPlayer != null)
FXPlayer.start();
}

Destroy Nuance session

I have an activity that creates an "Audio" class and tries to use android Text to Speech API to read some text. If the language is not supported, it tries to use MediaPlayer to play a custom mp3 file from the server. Finally if MediaPlayer fails, it uses Nuance SpeechKit to read the text:
My problem is when I destroy the activity, I want to destroy/stop the Nuance audio too and I'm not sure how to shutdown Nuance audio.
Activity class
private Audio audio;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
audio = new Audio(this).play("my text to read");
}
#Override
protected void onPause() {
audio.pause();
super.onPause();
}
#Override
protected void onDestroy() {
audio.destroy();
super.onDestroy();
}
Audio class
private TextToSpeech tts;
private MediaPlayer player;
private Session session;
public void play(String text) {
// check if supported
if (supported) tts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
else mediaPlayer(text);
}
private void mediaPlayer(String text) {
// make some queries on server to find the file url
if (queryFoundFile) {
player = new MediaPlayer();
player.setDataSource(myFileUrl);
player.setAudioStreamType(3);
player.prepare();
player.start();
} else nuancePlayer(text);
}
private void nuancePlayer(String text) {
Transaction.Options options = new Transaction.Options();
options.setLanguage(new Language("eng-USA"));
session = Session.Factory.session(activity, myServer, appKey);
session.speakString(text, options, new Transaction.Listener() {
#Override
public void onError(Transaction transaction, String s, TransactionException e) {
e.printStackTrace()
}
});
// it reaches here and nuance plays the audio
}
// these are the methods I call when the activity is paused or destroyed
public void pause() {
if (tts != null) tts.stop();
if (player != null) player.stop();
if (nuance != null) nuance.getAudioPlayer().stop(); // don't work
}
public void destroy() {
if (tts != null) tts.shutdown();
if (player != null) player.release();
if (nuance != null) nuance.getAudioPlayer().stop(); // don't work
}
If I'm using Text to Speech or MediaPlayer and if I destroy my Activity, the audio is immediately destroyed. But I can't seem to destroy the audio if is Nuance playing. It just keeps talking.
I did some debugging and the pause() and destroy() methods are being called. Also nuance.getAudioPlayer is not null and is the AudioPlayer playing. I can't find the reason why he is not stopping when I call the method stop() on him.
What is Nuance?
This is my first time using Nuance so I am not that experienced with this. Basically I see it like an alternative to the Android Text to Speech.
Nuance Developers
Why I have this on my project?
My project has 4 main languages, and I need to have a text to speech function to read some text. The problem is, android Text to Speech don't support some of these languages which Nuance support.
Why is Nuance my last option?
Because Nuance has costs. I try to use android TTS or MediaPlayer. Only if those two fail, I use Nuance. It is a last resort to read my text!
As per change log, this issue is known since a year ago and no fixed yet (as per changelog).
For temporary solution till they gave you fixed release you can do the following:
Break your text in small chunks and instead of playing complete text(as audio) at once, Queued these small text chunks into the audio player so that you audio will stop after finish playing the current chunk instead of complete text.
because as per known issue in the change log given below:
Please note this line:
However, if multiple Audios are queued for playback and stop() is called, then the next Audio will not begin playing and the queue will be cleared.
I hope this will help you.
A quick look on the nuance API shows that session.speakString(...) returns a Transaction, and a Transaction has 2 methods that might be of interest for you:
cancel() - Cancel the Transaction.
stopRecording() - Stop recording audio and complete the Transaction, if there is an ongoing recording.
Looks like cancel() is what you need.
Try setting session to null to see if the garbage collector solves the problem.

Android MediaPlayer reset freezes UI

I have a problem with the Android MediaPlayer when changing the dataSource of the player. According the specification of the MediaPlayer (http://developer.android.com/reference/android/media/MediaPlayer.html) I have to reset the player when changing the dataSource. This works fine, but as soon as the channelChanged method is called twice in quick succession the MediaPlayer.reset freezes the UI. I profile the code as seen here:
public void channelChanged(String streamingUrl)
{
long m1 = System.currentTimeMillis();
mMediaPlayer.reset();
long m2 = System.currentTimeMillis();
try
{
mMediaPlayer.setDataSource(streamingUrl);
}
catch (IOException e)
{
e.printStackTrace();
}
long m3 = System.currentTimeMillis();
mMediaPlayer.prepareAsync();
long m4 = System.currentTimeMillis();
Log.d("MEDIAPLAYER", "reset: " + (m2 - m1));
Log.d("MEDIAPLAYER", "setDataSource: " + (m3 - m2));
Log.d("MEDIAPLAYER", "preparing: " + (m4 - m3));
}
reset: 3
setDataSource: 1
preparing: 0
reset: 3119
setDataSource: 2
preparing: 1
So apparently the reset is blocked by the asynchronous preparing of the first call (when I wait until the first stream starts and then call channelChanged() again, everything is fine).
Any ideas how to solve the problems? Should I execute the whole method in a separate thread? Basically I want to avoid that, because it seems not to be a good coding style and can possibly cause some further issues, e.g. when the user tries to start the player again, but the player is still in the reset method, which on the other hand seems to wait for the asyncPrepare method. It is not clear how the player would behave...
Is there any other good solution?
MediaPlayer is a tricky bastard. I recommend you take a look at the sample app where the MediaPlayer bad design is made evident by looking at the mess of code you have to write around it to have a consistent media playback experience.
If anything, after looking at the sample, you see that when they want to skip a track, they essentially reset and release…
mPlayer.reset();
mPlayer.release();
…and later when they are ready to load a new track…
try {
mPlayer.reset();
mPlayer.setDataSource(someUrl);
mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
//bam!
}
});
mPlayer.prepareAsync();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
I have added the try/catch because on some devices/OS versions, the MediaPlayer is worse than others and sometimes it just does weird stuff. You should have an Interface/Listener that is capable of reacting to these situations
UPDATE:
This is a method I use when I stop (or pause) my Music Playback (mostly taken from the sample app, this is running in a service and it has been modified to suit my own app but still).
The first method is used by both stop and pause, the former passes true, the later false
/**
* Releases resources used by the service for playback. This includes the "foreground service"
* status and notification, the wake locks and possibly the MediaPlayer.
*
* #param releaseMediaPlayer Indicates whether the Media Player should also be released or not
*/
void relaxResources(boolean releaseMediaPlayer) {
stopForeground(true);
stopMonitoringPlaybackProgress();
// stop and release the Media Player, if it's available
if (releaseMediaPlayer && mPlayer != null) {
mPlayer.reset();
mPlayer.release();
mPlayer = null;
}
// we can also release the Wifi lock, if we're holding it
if (mWifiLock.isHeld()) {
mWifiLock.release();
}
}
This is part of the processPauseRequest():
if (mState == State.Playing) {
// Pause media player and cancel the 'foreground service' state.
mState = State.Paused;
mPlayer.pause();
dispatchBroadcastEvent(ServiceConstants.EVENT_AUDIO_PAUSE);//notify broadcast receivers
relaxResources(false); // while paused, we always retain the mp and notification
And this is part of the processStopRequest() (simplified):
void processStopRequest(boolean force, final boolean stopSelf) {
if (mState == State.Playing || mState == State.Paused || force) {
mState = State.Stopped;
// let go of all resources...
relaxResources(true);
currentTrackNotification = null;
giveUpAudioFocus();
}
}
Now the core part is the next/skip…
This is what I do…
void processNextRequest(final boolean isSkipping) {
processStopRequest(true, false); // THIS IS IMPORTANT, WE RELEASE THE MP HERE
mState = State.Retrieving;
dispatchBroadcastEvent(ServiceConstants.EVENT_TRACK_INFO_LOAD_START);
// snipped but here you retrieve your next track and when it's ready…
// you just processPlayRequest() and "start from scratch"
This is how the MediaPlayer sample does it (found in the samples folder) and I haven't had problems with it.
That being said, i know what you mean when you say you get the whole thing blocked, I've seen it and it's the MP buggyness. If you get an ANR I'd like to see the log for it.
For the record here's how I "begin playing" (a lot of custom code has been omited but you get to see the MP stuff):"
/**
* Starts playing the next song.
*/
void beginPlaying(Track track) {
mState = State.Stopped;
relaxResources(false); // release everything except MediaPlayer
try {
if (track != null) {
createMediaPlayerIfNeeded();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(track.audioUrl);
} else {
processStopRequest(true, false); // stop everything!
return;
}
mState = State.Preparing;
setUpAsForeground(); //service
/* STRIPPED ALL CODE FROM REMOTECONTROLCLIENT, AS IT ADDS A LOT OF NOISE :) */
// starts preparing the media player in the background. When it's done, it will call
// our OnPreparedListener (that is, the onPrepared() method on this class, since we set
// the listener to 'this').
// Until the media player is prepared, we *cannot* call start() on it!
mPlayer.prepareAsync();
// We are streaming from the internet, we want to hold a Wifi lock, which prevents
// the Wifi radio from going to sleep while the song is playing.
if (!mWifiLock.isHeld()) {
mWifiLock.acquire();
}
} catch (IOException ex) {
Log.e("MusicService", "IOException playing next song: " + ex.getMessage());
ex.printStackTrace();
}
}
As a final note, I've noticed that the "media player blocking everything" happens when the audio stream or source is unavailable or unreliable.
Good luck! Let me know if there's anything specific you'd like to see.
The newest phones and Android API works much butter, reset method takes only 5-20 ms when fast switching between songs (next or prev)
So there is no solution for older phones, it just how it works

Android : youtube player has been released

I'm getting this error Fatal Exception: java.lang.IllegalStateException
This YouTubePlayer has been released , but release() wasn't called explicitly.Here is the piece of code where crash occurs :
if(youtubePlayer != null){
time = youtubePlayer.getCurrentTimeMillis();//exception may occur
}
is it possible to check that youtubePlayer was released? Any callback ? Thanks.
Most of the code in the Youtube SDK is obfuscated which makes it really hard to debug. And the fact that there isn't any direct method to check if the YoutubePlayer has been released or not doesn't help either.
Having said that I think making YoutubePlayer null (in onStop()) seems more of a hack than a proper solution to me. You should release the YoutubePlayer in onDestroy() and don't manually assign it as null anywhere. One simple approach to check if the YoutubePlayer has been released or not is put your calls (like youtubePlayer.loadVideo(), cueVideo(), getCurrentTimeMillis() etc.) in a try catch block and catch the IllegalStateException exception.
According to the Youtube SDK documentation on errors:
public static final YouTubePlayer.ErrorReason
UNEXPECTED_SERVICE_DISCONNECTION
Playback has been canceled and the player has been released due to an
unexpected disconnection from the YouTube API service. Any further
calls to this player instance will result in errors, a new player
instance must be created to re-enable playback.
So to create a new instance of the YoutubePlayer just call the initialize() method in the catch block.
Example:
public void setVideoId(final String videoId) {
if (videoId != null && !videoId.equals(this.videoId)) {
this.videoId = videoId;
if (youtubePlayer != null) {
try {
youtubePlayer.loadVideo(videoId);
} catch (IllegalStateException e) {
initialize(API_KEY, this);
}
}
}
}
#Override
public void onDestroy() {
if (youtubePlayer != null) {
youtubePlayer.release();
}
super.onDestroy();
}
I fixed this issue by making videoPlayer null in onDestroy of Activity.
Edit:
Tried in onStop and it works fine.
#MickeyTin, Thanks..
The type of variable returned by youtubePlayer.getCurrentTimeMillis(); is a int.
This should work:
if(youtubePlayer != null){
int millis = youtubePlayer.getCurrentTimeMillis();//exception may occur
}
This is how I managed to get rid of this exception. In my case it was thrown on onSaveInstanceState where I tried to save current player position using the same piece of code:
if(youtubePlayer != null){ time = youtubePlayer.getCurrentTimeMillis(); }
and upon successfull player initialization in onInitializationSuccess I continued playing video using the time value assigned in onCreate from Bundle.
But it turned out that such approach is unnecessary. To avoid the exception throw I just added android:configChanges="orientation|screenSize" attribute to my player activity in manifest. This makes the system handle orientation changes by itself, and adding time parameter to cueing the video becomes redundant. This is what checking the wasRestored flag is made for in onInitializationSuccess.
Here's the changes summary:
AndroidManifest.xml:
<activity android:name=".VideoPlayerActivity"
android:configChanges="orientation|screenSize"/>
VideoPlayerActivity.java:
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player, boolean wasRestored) {
this.mPlayer = player;
if (!wasRestored) {
mPlayer.loadVideo(mVideoId);
}
}

How to check if mediaplayer is playing or stopped?

How can I check whether the mediaplayer is playing or stopped, using Java Media Framework?
You can call getState and check against Controller.Started:
if (mediaPlayer.getState() == Controller.Started)
// Register ControllerListener :
public class myPlayer implements ControllerListener {
// ....
Player player = Manager.createRealizedPlayer(url);
player.addControllerListener(this);
// ....
// And check for EndOfMedia event in the controllerUpdate method:
public void controllerUpdate(ControllerEvent event) {
if (event instanceof EndOfMediaEvent) {
// Take appropriate action
}
}
} // End of class
By checking the state and by listening to EndOfMedia event, one could detect if media is being played or stopped.
It seems like a bit changed since the accepted answer.
The following works for me:
if(this.player.getStatus() == MediaPlayer.Status.STOPPED){
// Do something
}

Categories

Resources