So I have a simple soundboard type application that operates via a ListView. Tap an item, it plays the sound. I have a public static int[] named soundResourceList that has entries such as R.raw.foo, R.raw.bar in the same order of the entries associated with the text in the ListView.
This code has worked for many users including myself, however one recently posted a NullPointerException, in particularly from the setOnCompletionListener line. This to me means that MediaPlayer.create returned null, but I can't figure out why it would fail on their device but not everybody else's.
public void onListItemClick(ListView parent, View v, int position, long id) {
MediaPlayer mp = MediaPlayer.create(getBaseContext(), soundResourceList[position]);
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
mp.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
Normally a NPE with MediaPlayer is due to something like not releasing its previous instance, but I already have that issue handled as shown in the code. So what am I missing that might cause the MediaPlayer.create() to fail?
The create method doc says:
Returns
a MediaPlayer object, or null if creation failed
Since a number of low-level resources might be allocated for each MediaPlayer, it could be that if the GC is busy for a while or doesn't run you'll run out of sound buffers (or whatever) and the MediaPlayer allocation will fail, returning null.
If these are all relatively-short-lived audio snippets, you might look into android.media.SoundPool.
Related
I'm creating a simple soundboard to play sounds when a user clicks a button. Problem is, if the button is pressed enough ( usually around 10 times ) it will eventually stop playing and show the error E/MediaPlayer: error (1, -19)
what am I doing wrong? My code that plays the sound:
private void playSound(int soundID){
final MediaPlayer mp = MediaPlayer.create(this,soundID);
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mp.start();
}
});
}
You should use Soundpool. It's created exactly for playing short sound effects. And it's much simpler to use than MediaPlayer. MediaPlayer should only be used for playing regular/large music.
See here for example:
http://www.vogella.com/tutorials/AndroidMedia/article.html#tutorial-play-sounds-via-soundpool
Note: No need to use onTouch as in the example, you can just use onClick for simplicity.
Reason: "W/Choreographer: (let say) Frame time is 0.239384 ms in the future! Check that graphics HAL is generating vsync timestamps using the correct timebase."
i.e, there is a much gap between timestamps.
This problem can be removed by using setOnCompletionListener() within your OnClick() method like: `
#Overrid
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
// Get the {#link Word} object at the given position the user clicked on
Word word = words.get(position);
Log.v("NumbersActivity","Current word: "+word);
// Create and setup the {#link MediaPlayer} for the audio resource associated with the current word
MediaPlayer mMediaPlayer = MediaPlayer.create(NumbersActivity.this, word.getAudioResourceId());
// Start the audio file
mMediaPlayer.start();
// Keep timeStamp sync
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mMediaPlayer.release();
}
});
}
`
private void playSound(int soundID){
final MediaPlayer mp = MediaPlayer.create(this,soundID);
mp.start();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mp.release();
}
});
}
I'm trying to build a game which plays some sounds effects on click & at the same time music in the background.
I tried implementing this with two MediaPlayer objects.
The first one, which served for the effects on click works great.
The second one however sometimes logs error 100, sometimes error 38. No sound at all.
Variables
private MediaPlayer mEffects;
private MediaPlayer mpSoundBackground;
Implementation of the sound media player:
mpSoundBackground = MediaPlayer.create(MainActivity.this, R.raw.soundbackground1small);
mpSoundBackground.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
Logger.d("prepared");
musicPrepared = true;
}
});
mpSoundBackground.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Logger.d("error "+what);
return false;
}
});
if (musicPrepared) {
mpSoundBackground.start();
Logger.d("music is prepared");
} else {
Logger.d("music is not prepared");
}
Implementation of the effects Media Player:
stopPlaying();
mEffects= MediaPlayer.create(MainActivity.this, R.raw.soundhit);
mEffects.start();
private void stopPlaying() {
if (mEffects!= null) {
mEffects.stop();
mEffects.release();
mEffects= null;
}
}
Update
To add to the confusion: It does seem to work in emulator
(Genymotion), but does not work on my OnePlus One, running Lollipop
You need to use the setOnPreparedListener method for both players. also if you want to play a sound on clicks consider using SoundPool.
Also in the public void onPrepared(MediaPlayer mp) method, you can use mp.start there is no need for that flag, since you can not know for sure that it is prepared once you reach that prepared flag
I couldn't make the errors go away, until I reconverted my soundfile to MP3.
Now it plays both on device & simulator without any problems.
Moral of this story: if you are running into errors, try a few encodings of the same file (possibly a few file sizes too!), it might be the solution.
I'm trying to write a function to play a short sound (in /res/raw) in my program, called at effectively random times throughout the program. So far I have this function:
public void playSound() {
MediaPlayer mp = new MediaPlayer();
mp = MediaPlayer.create(this, R.raw.ShortBeep);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setLooping(false);
mp.start();
}
It works fine for awhile, but after exactly 30 plays of the sound, it stops making sound.
According to the Docs
... failure to call release() may cause subsequent instances of MediaPlayer objects to fallback to software implementations or fail altogether.
When you are done with it call mp.release() so that it can release the resources. I don't know what the limit is and I'm sure it depends on many factors. Either way you should be calling this function on your MediaPlayer object, especially if it will be used more than once.
I've just solved the exact same problem, but I'm using Xamarin. I ended up changing from holding on to a MediaPlayer instance for the lifetime of the activity to creating an instance each time I want to play a sound. I also implemented the IOnPreparedListener and IOnCompletionListener.
Hopefully you can get the idea despite it being C# code
public class ScanBarcodeView :
MvxActivity,
MediaPlayer.IOnPreparedListener,
MediaPlayer.IOnCompletionListener
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.ScanBarcodeView);
((ScanBarcodeViewModel) ViewModel).BarcodeScanFailed += (sender, args) => PlaySound(Resource.Raw.fail);
((ScanBarcodeViewModel) ViewModel).DuplicateScan += (sender, args) => PlaySound(Resource.Raw.tryagain);
}
private void PlaySound(int resource)
{
var mp = new MediaPlayer();
mp.SetDataSource(ApplicationContext, Android.Net.Uri.Parse($"android.resource://com.company.appname/{resource}"));
mp.SetOnPreparedListener(this);
mp.SetOnCompletionListener(this);
mp.PrepareAsync();
}
public void OnPrepared(MediaPlayer mp)
{
mp.Start();
}
public void OnCompletion(MediaPlayer mp)
{
mp.Release();
}
}
So, each time I want a sound to be played I create a MediaPlayer instance, so the data source, tell it that my Activity is the listener to Prepared and Completion events and prepare it. Since I'm using PrepareAsync I don't block the UI thread. When the media player is prepared the Start method on the MediaPlayer is called, and when the sound has finished playing the MediaPlayer object is released.
Before I made these changes I would get to 30 sounds played and it would all stop working. Now I've gone way past 30, also multiple sounds can be played simultaneously.
Hope that helps.
I have a game in which a sound plays when a level is completed. Everything works fine to start with but after repeating a level 10 or 20 times the logcat suddenly reports:
"MediaPlayer error (-19,0)" and/or "MediaPlayer start called in state 0" and the sounds are no longer made.
I originally had the all sounds in mp3 format but, after reading that ogg may be more reliable, I converted them all to ogg, but the errors appeared just the same.
Any idea how I can fix this problem?
I was getting the same problem, I solved it by adding the following code to release the player:
mp1 = MediaPlayer.create(sound.this, R.raw.pan1);
mp1.start();
mp1.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
};
});
I think you are not releasing the mediaplayers you are using to play the sound..
You need to release() the media players otherwise the resources are not released , and you soon get out of memory (since you allocate them again next time). so,I think you can play twice or even thrice... but not many times without releasing the resources
MediaPlayer is not a good option when you are playing small sound effects as the user can click on multiple buttons very soon and you will have to create a MP object for all of them which doesnt happen synchronously. That is why you are not hearing sounds for every click. Go for the SoundPool Class which allows you to keep smaller sounds loaded in memory and you can play them any time you want without any lag which you would feel in a mediaplayer. http://developer.android.com/reference/android/media/SoundPool.html Here is a nice tutorial : http://www.anddev.org/using_soundpool_instead_of_mediaplayer-t3115.html
I solved both the errors (-19,0) and (-38,0) , by creating a new object of MediaPlayer every time before playing and releasing it after that.
Before :
void play(int resourceID) {
if (getActivity() != null) {
//Using the same object - Problem persists
player = MediaPlayer.create(getActivity(), resourceID);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
player.release();
}
});
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
}
After:
void play(int resourceID) {
if (getActivity() != null) {
//Problem Solved
//Creating new MediaPlayer object every time and releasing it after completion
final MediaPlayer player = MediaPlayer.create(getActivity(), resourceID);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
player.release();
}
});
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
}
This is a very old question, But this came up first in my search results So other people with the same issue will probably come upon this page eventually.
Unlike what some others have said, you can in fact use MediaPlayer for small sounds without using a lot of memory. I'll put in a little modified snippit from my soundboard app to show you what I'm getting at.
private MediaPlayer mp;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
mp = new MediaPlayer();
}
private void playSound(int soundID){
mp.reset();
AssetFileDescriptor sound = getResources().openRawResourceFd(soundID);
try {
mp.setDataSource(sound.getFileDescriptor(),sound.getStartOffset(),sound.getLength());
mp.prepare();
} catch (IOException e) {
e.printStackTrace();
}
mp.start();
}
with the way I set it up, you create on MediaPlayer object that you reuse everytime you play a sound so that you don't use up too much space.
You call .reset() instead of .release() because .release() is only used if you are disposing of an object, however you want to keep your MediaPlayer Object.
You use an assetfiledescriptor to set a new soundfile for your mediaplayer to play instead of setting a new object to your mediaplayer address because that way you are creating new objects within the method that aren't being handled properly and you will eventually run into the same error as you described.
This is only one of many ways to use MediaPlayer but I personally think it is the most efficient if you are only using it for small sound applications. The only issue with it is that it is relatively restrictive in what you can accomplish, but that shouldn't be much of an issue if you are indeed using it for small sound applications.
i try delete emulator and new create emulator for remove error of (-19,0) media player.
I'm trying to create a simple Sound-board Android app, using ListView items as buttons. (Btw, I'm a novice programmer)
The idea is that I press a button, and a specific sound file plays. If I press any button while a sound is playing, it should first stop that sound and then start to play the new one.
Currently the sounds play without stopping any currently playing sounds, so that if I spam the buttons I get multiple sounds playing at the same time (and if I press too many at once, the app force closes).
I have tried using a few variations of:
if (mp.isPlaying()) {
mp.stop();
}
But according to what I read on a few other sources, I am creating multiple instances of the MediaPlayer class, and even though they have the same name the stop() method tries to stop on the latest instance of mp (in some cases it isn't even created yet).
I'm guessing my general implementation of the MediaPlayer class is wrong, but it's the best I could figure out to do.
Anyways, here's the relevant block of code:
public class soundTest extends Activity {
private ListView lv1;
private String lv_arr[]={"test 1","test 2","test 3","test 4","test 5"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv1=(ListView)findViewById(R.id.ListView01);
lv1.setAdapter(new ArrayAdapter<String>(this,R.layout.list_item, lv_arr));
lv1.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
if (lv1.getItemAtPosition(position)=="test 1") {
MediaPlayer mp = MediaPlayer.create(getApplicationContext(),R.raw.sound1);
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
if (lv1.getItemAtPosition(position)=="test 2") {
MediaPlayer mp = MediaPlayer.create(getApplicationContext(),R.raw.sound2);
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
//And the rest of the sounds 3,4,5.
}
});
}
}
Any help would be appreciated, thanks.
Edit (22nd March):
I've found the following piece of code that should work:
mp.setDataSource(context, Uri.parse("android.resource://" + Config.PACKAGE + "/" + resId));
But, I can't figure out how the "Config.PACKAGE" part works. I just get an error "PACKAGE cannot be resolved, or is not a field".
I tried replacing "PACKAGE" with the package name, same error. I also tried:
try {
mp.setDataSource(getApplicationContext(),Uri.parse("android.resource://com.mptest/" + R.raw.test2));
} catch (IOException e) {
e.printStackTrace();
}
But I can't work what exactly to put in place of "//com.mptest/".
The global variable MediaPlayer needs to be set private static. This has caught me several times.
Don't use a global. Use the singleton pattern. That is what it is for, so that you can have exactly one instance w/o using a global variable.
There are lots of good examples of Java code for this pattern, start with http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpatterns.html and you can't go wrong.
Consider keeping your MediaPlayer as a global variable instead of having multiple instances of it.
You might find a good reference in the VideoView Source, since it handles one MediaPlayer object and you may change content during playback.