I've created an app that uses MediaPlayer to play a random (short) sound when a button is clicked. The sounds are played correctly on android devices < 2.2. This is the code responsible for playing sounds.
r = new Random();
sounds = new ArrayList<MediaPlayer>();
sounds.add(MediaPlayer.create(this, R.raw.sound1));
sounds.add(MediaPlayer.create(this, R.raw.sound2));
sounds.add(MediaPlayer.create(this, R.raw.sound3));
sounds.add(MediaPlayer.create(this, R.raw.sound4));
sounds.add(MediaPlayer.create(this, R.raw.sound5));
theButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
playSound();
}
});
private void playSound() {
Thread thread = new Thread() {
public void run() {
MediaPlayer soundPlayer = sounds.get(r.nextInt(sounds.size()));
while (soundPlayer.isPlaying())
{
soundPlayer = sounds.get(r.nextInt(sounds.size()));
}
soundPlayer.seekTo(0);
soundPlayer.start();
}
};
thread.start();
}
The sounds are all .wav files. I tried converting them to .mp3, but then they wouldn't play at all. Am I doing something extremely wrong, or is the MediaPlayer in 2.2 buggy? Anyone else had this problem and know of a fix? Keep in mind that the sounds are played normally on all other devices with an android version below 2.2.
I think you shouldn't create a ArrayList for MediaPlayer. Instead that, you use only a MediaPlayer object and a ArrayList to contain all music resources.
When you next other song, you update only the info of MediaPlayer. For example,
Release the previous MediaPlayer object.
Create other MediaPlayer object
Finally, start this song
Seems there was a problem with the sampling rate of the mp3's that the 2.2 Framework frowned upon. I fixed it by opening up the sounds in a sound editor, resampling them and adding silence to the first and last second of the sounds.
Related
Recently i have an audio project in Audacity with multiple tracks in it. It has vocals separate and instruments separate.
I have exported each track as a WAV(16bit) file. I have 5 files for 5 tracks. All the files are more than 300MB and lengths of 25 minutes.
I was trying to play them simultaneously using mediaplayer:
I have created Mediaplayer instances mp1, mp2, mp3, mp4 and mp5 for each file
I try to play them simultaneously by:
Default Way to start all media players:
mp1.start()
mp2.start()
mp3.start()
mp4.start()
mp5.start()
What i observed is there is some gaps in between.
I could have mixed them in audacity and played as a single file. the reason i want them to be played seperately is i want to control the volume of each track in android.
I reasearched a lot on net regarding this:
I thought the problem is in .start() commands not starting simultaneously.
Methods 1: Asyntask Method to start all mp simultaneously:
SO i have tried using Asyntask and created 5 separate threads like below:
class PlayThread extends AsyncTask<MediaPlayer, Void, Void>
{
#Override
protected Void doInBackground(MediaPlayer... player) {
player[0].start();
return null;
}
}
then did
PlayThread[] playThreads = new PlayThread[5];
for (int i = 0; i < 5; i++)
playThreads[i] = new PlayThread();
playThreads[0].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp1);
playThreads[1].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp2);
playThreads[2].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp3);
playThreads[3].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp4);
playThreads[4].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp5);
Method 2: using CyclicBarrier
public void syncedCommand(
MediaPlayer player1,
MediaPlayer player2,
MediaPlayer player3,
MediaPlayer player4,
MediaPlayer player5
) {
final CyclicBarrier commandBarrier = new CyclicBarrier(5, new Runnable() {
#Override
public void run() {
L.m("ALL threads done");
}
});
new Thread(new SyncedCommandService(commandBarrier, player1)).start();
new Thread(new SyncedCommandService(commandBarrier, player2)).start();
new Thread(new SyncedCommandService(commandBarrier, player3)).start();
new Thread(new SyncedCommandService(commandBarrier, player4)).start();
new Thread(new SyncedCommandService(commandBarrier, player5)).start();
}
private class SyncedCommandService implements Runnable {
private final CyclicBarrier mCommandBarrier;
private MediaPlayer mMediaPlayer;
private int lengthseek;
public SyncedCommandService(CyclicBarrier barrier, MediaPlayer player) {
mCommandBarrier = barrier;
mMediaPlayer = player;
}
#Override public void run() {
try {
L.m("Waiting for thread");
mCommandBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
mMediaPlayer.start();
}
}
then play them using
syncedCommand(mp1,mp2,mp3,mp4,mp5);
In both the above methods stil there is some latency among the tracks, there is some gaps in between.
Later i have read in another post that its not possible:
syncing multiple audio tracks on android
The problem is that each MediaPlayer represents a separate stream,
which may start at different times. Even if you call start on all of
them at the same time (which is impossible), there's no way to
guarantee that the OS will load and actually start each one at the
same time.
As far as I know, Android offers no way to synchronize multiple
streams. Even with operating systems that do offer this capability,
it's extremely cumbersome, and usually not 100% accurate.
The correct solution is to open a single stream, eg using the audio
track interface, and do the MP3 decoding and mixing yourself before
sending the data to the stream.
The solution its mentioning is to decode, mix and encode. I am new to do this method using Audio Track. I can still figure out how to do it. BUt will it allow me
1. to change volume of tracks on fly like media player changes volume of each track. Or i have to fix the parameters first and then mix, play and check whether its suitable to my needs
2. Can i seek to particular time and start playing.
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 am trying to play two sound items, one after the other
MediaPlayer mp = null;
protected void produceErrorSound(int index) {
if (mp != null) {
mp.reset();
mp.release();
}
mp = MediaPlayer.create(this, index);
mp.start();
}
public void correctAnswerAndNext(){
produceErrorSound(R.raw.right1) ;
produceErrorSound(R.raw.right1) ;
}
but only second sound is produced.
is there any alternative approach?
I can't see any wait mechanism in your code.
You can use an onCompletionListener to receive a callback when your first MediaPlayer has finished playback. At that point you can start the second MediaPlayer.
An alternative way in Jellybean (and later versions) is to use the audio chaining functionality (i.e. setNextMediaPlayer) to automatically start another MediaPlayer as soon as the current one has finished playing. Something like this (I've omitted the calls to setDataSource etc for brevity):
mediaPlayer1.prepare();
mediaPlayer2.prepare();
mediaPlayer1.start();
mediaPlayer1.setNextMediaPlayer(mediaPlayer2);
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();
}
Hey,
I'm using MediaPlayer to play a regular ShoutCast stream. The code is straightforward with prepareAsync() and a handler to start the playback. While it works flawlessly with some streams like DI.FM or ETN.FM (http://u10.di.fm:80/di_progressive), with others (http://mp3.wpsu.org:8000/) it won't go past the prepare state. No other listeners are called either.
//Uri streamUri = Uri.parse("http://u10.di.fm:80/di_progressive"); /* works */
Uri streamUri = Uri.parse("http://mp3.wpsu.org:8000/"); /* stuck on prepare state */
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(this.getBaseContext(), streamUri);
mediaPlayer.prepareAsync();
Any feedback is appreciated!
I think that there are some compatibility problems with the server end.
This is rather strange since the emulator handles it ok in my case - just not on my Froyo Galaxy S, even though it is the same API version.
It could be a codec issue, http streaming issue, I do not know.
But all the servers that fail tend to be old ones, with "Copyright 1998 - 2004" at the bottom... Not exactly recent or up to date you would think.
One potential workaround (which I have not tried yet) would be to use the StreamProxy, which would also make your code compatible with 2.1 and possibly earlier versions too. At the cost of extra work, extra code, and without doubt extra bugs...
In case you are not aware of it, there is another player bug report for 2.2 which may be relevant too:
Basic streaming audio works in 2.1 but not in 2.2
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:
private void actionCancel(){
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.
Additionally i have a following counter:
#Override
public void onBufferingUpdate(final MediaPlayer mp, final int percent) {
if (!mp.isPlaying()){
// android.util.Log.d(TAG,"onBufferingUpdate(): onBufferingUpdateCount = "+onBufferingUpdateCount);
if (onBufferingUpdateCount>MAX_BUFFERING_UPDATES_AT_PREPARING_STATE)
restartMP();
onBufferingUpdateCount++;
return;
}
}
i'd discover this listener always triggers at preparing state. So if it triggers more than 10 times and MP is still not playing i'm just restarting it:
private void restartMP(){
if (mp!=null)
if (mpState==MediaPlayerState.Preparing)
actionCancel();
else
mp.reset();
else
mp = new MediaPlayer();
mpState = MediaPlayerState.Idle;
onBufferingUpdateCount=0;
//isRequestCancelled=false;
requestTrackInfoStartedAt=0;
requestPlay();
}
note MediaPlayerState is my custom enum which has "Preparing" value. Also mpState is a class property/field which holds current MediaPlayerState state. Before starting prepareAsync() im setting mpState to MediaPlayerState.Preparing after it completes im setting it to MediaPlayerState.Started or other corresponding value.