I'm writing an audio player using MediaPlayer that allows the user to skip the actual tune. A skip request might occur at any time, including between a call to MediaPlayer.prepareAsync and the upcoming onPrepared callback. The doc says:
It is important to note that the Preparing state is a transient state, and the behavior of calling any method with side effect while a MediaPlayer object is in the Preparing state is undefined.
Does that include calls to reset, or even to release? Because if it so, then I would either have to wait for the onPrepared callback to reuse the MediaPlayer or allocate a brand new MediaPlayer if I don't want to wait and release the obsolete one on the onPrepared callback, right?
I also built a stream music player and struggled with the preparing state. The worse part of it was that there were some streams where prepare() hung forever downloading (buffering) data without ever calling onBufferUpdate. Calling release did nothing. So, the way I did it was calling reset() on the stuck MediaPlayer from anotherthread after 15 seconds despite the recommendations in the docs. This caused it to throw an exception and brought it to error state. After catching the exception I called release(). This seems to have solved the problem. I hope this will be useful to someone.
In my opinion i will follow the advice in the docs, i found several issues with player in different devices (in some devices is not stable at all reusing the same player).
I think a good option is to have to players allocated, and switch between them when user skips a tune, then you wait for the original player to arrive to prepared state and then you reset it safely.
I'm facing an issue when MP "hangs" at preparing state too long (stream) and i'm trying to stop it using reset(). This causes MP to hang and thus my whole app freezes. Seems like there is no way to stop MP at preparing state. Im thinking on use prepare() wrapped in thread instead of prepareAsync(). Then i'll be able to kill that thread. As for now i did it in following way:
try {
mp.setDataSource(new String());
} catch (Exception e) {
e.printStackTrace();
android.util.Log.d(TAG,"actionCancel(): mp.setDataSource() exception");
mp.reset();
}
and it works 4me.
Related
I've created a game which is nearly complete, but I've still got one problem:
Once I press a button I want a sound to play, I did this with this code:
MediaPlayer planeSound = MediaPlayer.create(ObjectCanvas.getDrawContext(), R.raw.plane);
planeSound.start();
It works perfectly, but the only problem is when I press it a lot of times, then the MediaPlayer gives a error which is error (-19,0). I searched but I can't find a solution for this. If the error happens the MediaPlayer won't play any sounds anymore untill the actvity is recreated. The game doesn't get a FC, but just the error (-19,0).
I don't know how to fix it, I hope someone can give me some suggestions or explanation.
Go for sound pool, or reuse one instance of media player, or release each instance once you have finished with it.
When done with the MediaPlayer, you should call release(), to free the
resources. If not released, too many MediaPlayer instances will result
in an exception.
etc
It is also recommended that once a MediaPlayer object is no longer
being used, call release() immediately so that resources used by the
internal player engine associated with the MediaPlayer object can be
released immediately. Resource may include singleton resources such as
hardware acceleration components and failure to call release() may
cause subsequent instances of MediaPlayer objects to fallback to
software implementations or fail altogether. Once the MediaPlayer
object is in the End state, it can no longer be used and there is no
way to bring it back to any other state.
See the documentation: http://developer.android.com/reference/android/media/MediaPlayer.html
I've written some code for Android 2.2 that plays an audio file using the Android MediaPlayer. Without getting into the details of the code, I noticed that there exists a function called
isPlaying()
that allows you to check if an audio file is currently being played by the MediaPlayer. So, for example, when the following snippet of code runs
Toast.makeText(getApplicationContext(), "Sound playing is: " +
mediaPlayer.isPlaying(), Toast.LENGTH_SHORT).show();
it displays the following message
Sound playing is: true / false
depending on whether there's sound playing or not.
When I wrote some code to record sound from the microphone using the Android MediaRecorder, however, I noticed that there did not look like there exists a function called
isRecording()
that checks to see whether a recording is in progress.
So, I was wondering if the onus is on the programmer then to figure out if a recording is in progress by embedding some logic into their code - or if perhaps there indeed exists a way to do this (check if a recording is in progress) by using another in-built function offered by the Android API.
Doesn't look like there is such a function after all. I think it makes sense to try to embed some logic in the code to do this cleanly.
Important
I found a nice workaround to handle this issue, and you can read the workaround right away, but I strongly suggest that you read the entire explanation first.
The scenario
I was trying to find a way to figure out if the mediarecorder had started recording or not, I assumed that calling the start() method on the recorder after the prepare() method was enough, but it turns out, it isn't.
Before you get offended by what I just said, Let me explain the scenario...
I was building a simple audio recording app from scratch, no libraries, copy paste, all hardwork. So I knew exactly what each part of my code is doing. Or at least I thought I did.
Until I decided to try and break my application by clicking on the buttons to start and stop recording like I was playing a piano. And yes, my stop button wasn't even appearing until the mediarecorder's start() method was called.
So I was greeted with a crash and logcat welcomed me with
java.lang.RuntimeException: stop failed.
at android.media.MediaRecorder.stop(Native Method)
along with
E/MediaRecorder(15709): stop failed: -1007
So I read online and found out that calling stop() right after start() on MediaRecorder causes this problem.
So the biggest question was, how do I detect if it is now SAFE for me to enable the STOP button on my recorder?
The Workaround (Not at all perfect, but it works)
MediaRecorder.getMaxAmplitude() // The maximum absolute amplitude measured since the last call, or 0 when called for the first time
As you can see, the MediaRecorder.getMaxAmplitude() method or the MediaRecorder.maxAmplitude property return 0 when called for the first time and the amplitude after that.
So, instead of allowing the user to Stop the recording right after calling MediaRecorder.start() I am now waiting until the MediaRecorder.maxAmplitude value is greater than zero, at which point I can be sure that the MediaRecorder is initialized, started, and recording, and is in a state where calling stop() is allowed. You can accomplish this by using a runnable that keeps checking until the amplitude is greater than 0. I am already using a runnable for the timer, so I perform the check in that.
Please Note
When working on an emulator, the value returned by MediaRecorder.maxAmplitude is always 0. So, you should use an Android Device to check if everything works as expected.
Now my buttons stay disabled for less than a second when I first start recording. But if I stop and start too quickly, they remain disabled for a bit longer, and I show a "Please wait while the recording starts" message for the user.
I hope this answer helps someone.
Regards!
I am trying to play multiple audio files, one after the other and am currently using AsyncTasks to prepare and start the mediaPlayer but have failed to find a good way to move on the to next track at the end of the current one. Not every audio file will be played every time, and it's playing is decided by a boolean value.
Any help is much apprecieated.
I guess you have read android-sdk/docs/reference/android/media/MediaPlayer.html , it says:
When the playback reaches the end of stream, the playback completes.
If the looping mode was being set to truewith setLooping(boolean), the
MediaPlayer object shall remain in the Started state. If the looping
mode was set to false , the player engine calls a user supplied
callback method, OnCompletion.onCompletion(), if a
OnCompletionListener is registered beforehand via
setOnCompletionListener(OnCompletionListener). The invoke of the
callback signals that the object is now in the PlaybackCompleted
state. While in the PlaybackCompleted state, calling start() can
restart the playback from the beginning of the audio/video source.
So you may set a new source, prepareAsync then start in completion callback. In this way , you get continuous playback, but it is not seamless.
Doubtful using MediaPlayer for this will work like you want it to. Try this tutorial:
http://www.droidnova.com/creating-sound-effects-in-android-part-1,570.html
If that doesn't work you'll probably have to mix the sounds together yourself them stream that result directly to the hardware using AudioTrack. That's more low level, but it will give you the most control. It just depends on what you are doing if the AudioManager solution will work for you or not. It's definitely the simpler route. But, if you're trying to line up two samples so that when one finishes the next begins, like in a music app, you probably will have to mix and stream that audio yourself.
http://developer.android.com/reference/android/media/AudioTrack.html
Algorithm to mix sound
I've got an interesting situation with the android MediaPlayer. I've got about 30 sounds that I need to play in an activity. I'm getting strange results that I'm not sure are coming from memory issues or not...
The reason why I need to use media player is that I've got a timed flow to the sounds where one sound needs to play after the other in a certain order. I also use the callbacks to see which sound is played next. Thus, soundpool is out...
I've tried loading each sound in to it's own media player using MediaPlayer.create(). Now, this works fine on my device, a Samsung Galaxy S Vibrant. My client with a Samsung Ace, however is getting null pointer exceptions when I try to set the onCompletionListener, meaning .create() returned null on his device but not mine.
So, I switched from loading them all at once to loading them on the fly in the onCompletionListener of the previous sound using this method
This kind of works, the fist 3-4 sounds play and then it hangs on my client's device (still works fine on my device). Which I'm assuming it's hanging on the .prepare()
This is incredibly hard to sort out since my client has no real knowledge of logcat and no SDK to debug, and it works fine on my device. It even hangs on his second device which is the same model as mine.
Anyone have any idea what might be causing this or how to debug it?
I'm thinking my next step will be to simply just use the one MediaPlayer and use the mp parameter of OnCompletionListener to load up the next sound on the same media player. I'll post my findings.
Providing you are releasing the media player correctly, it shouldn't be a memory problem. You can properly ensure this by doing:
if(mediaplayer!=null){
if(mediaplayer.isPlaying()){
mediaplayer.stop();
}
mediaplayer.release();
mediaplayer = null;
}
I believe there is a 10MB limit (minimum, some phones are higher) to apk memory allocation, so you should be able to check this fits within your limits. Market will allow up to 50MB however.
For debugging, I suggest you ensure that all the assests are there (sounds silly, but it rules it out) since if you're missing the sound files it will of course fail to create. Creating Toast notifications if an exception is thrown e.g. IllegalStateException will ensure that you management of media player states is not a problem.
It's also worth mentioning that you should not ever mix prepare and create. If you are using create, it will already call prepare and subsequent calls to prepare may cause odd behaviour; not sure if it will nullify the player though!
In general, you don't want to really be doing it the way you propose (creating a new mediaplayer and changing the sound in onComplete). The reason for this is simple, when you call create or prepare, it will load the sound into memory - this will take a variable amount of time dependant on phone (it may be a really long time for some phones!). It's far better to load them all up at once.
So, it must have been some kind of memory issue. I had 20+ media players loaded up initially.
Now I combined them all in to one media player and am loading the sounds dynamically. The sounds are small so I'm not worried about the loading time.
Here is some helper code in case anyone wants it.
private MediaPlayer getMediaPlayer(MediaPlayer mp, int resID, OnCompletionListener listener){
mp.release();
mp = null;
mp = new MediaPlayer();
AssetFileDescriptor afd = getResources().openRawResourceFd(resID);
try {
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
mp.prepare();
mp.setOnCompletionListener(listener);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return mp;
}
I use this to load a new sound. Its got a fair bit of overhead but it works and that's what's important.
Here is how I use it.
mSoundPlayer = getMediaPlayer(mSoundPlayer, R.raw.a123switch, endSwitch123Listener);
mSoundPlayer.start();
Can we stream an audio file via async task in android. give me an example if u have done.
Alright I've struggled with this myself for quite a while, since everyone always told me "just use prepareAsync() and it will work.
However, you will still need to wait until enough has been buffered before trying to start() otherwise you'll get errors and nothing will happen.
First of, let your music-streamer class implement OnPreparedListener. This will be used to check if enough has been buffered before starting to play.
Next, use this piece of code to start the buffering and set the listener:
mediaPlayer.setDataSource(URL here);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(this);
You have now set your listener to check whether or not enough has been buffered. Only one more step to go
public void onPrepared(MediaPlayer mediaplayer) {
// We now have buffered enough to be able to play
mediaPlayer.start();
}
The song will now be able to start playing without giving errors and all that.
Good luck!
As for the fact you want to play the music and be able to stop it at any time, you will need to use a Service if you plan on being able to stop your music from all activities. There are a few tutorials about that to be found on the internet.
You can use mediaPlayer object.
mediaPlayer.setDataSource("Your datasource");
mediaPlayer.prepareAsync();
The method prepareAsyc: Prepares the player for playback, asynchronously.
For more information see: http://developer.android.com/reference/android/media/MediaPlayer.html
Hope this helps...