Android sound file not playing fully, gets brutally interupted - android

Scenario
I have a problem that starts to drive me nuts. In one activity I have the following scenario. On create:
- create a GPS Listner
- create a Location Listener
- Start GPS listner and location listener
- Start a asyncTask that downloads a map from the web / or load it from cache
- play a wav file which is a raw resource, very small (around 63 Kilo) which is short, basically it says: "Let's head to the start point" and it makes visible a textview for 10 seconds.
Problem
Rarely the sound gets play fully. Sometimes I hear only "Let's head", sometimes "Let's head to the star", sometimes no play at all. It's like something else is blocking the sound. I moved the sound in its own thread but still no success:
Code
private void ShowTextPlayVoice(String message, int soundResId)
{
txtProceed.setText(message);
txtProceed.setVisibility(View.VISIBLE);
final int soundId = soundResId;
Handler splashHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
txtProceed.setVisibility(View.INVISIBLE);
break;
}
super.handleMessage(msg);
}
};
Message msg = new Message();
msg.what = 0;
splashHandler.sendMessageDelayed(msg, 10000);
Thread thread = new Thread() {
public void run() {
MediaPlayer mp = MediaPlayer.create(ctx, soundId);
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
};
thread.start();
}
Thank you all for help.

After some tries, here is a working solution
private MediaPlayer mediaPlayer = new MediaPlayer();
private void ShowTextPlayVoice(String message, int soundResId)
{
txtProceed.setText(message);
txtProceed.setVisibility(View.VISIBLE);
Handler splashHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
txtProceed.setVisibility(View.INVISIBLE);
break;
}
super.handleMessage(msg);
}
};
Message msg = new Message();
msg.what = 0;
splashHandler.sendMessageDelayed(msg, 10000);
new SoundTask().execute(soundResId);
}
protected class SoundTask extends AsyncTask<Integer, Void, Void>
{
#Override
protected Void doInBackground(Integer... params)
{
AssetFileDescriptor afd = ctx.getResources().openRawResourceFd(params[0]);
try
{
//final MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.reset();
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
mediaPlayer.prepare();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer arg0) {
mediaPlayer.seekTo(0);
mediaPlayer.start();
});
}
});
afd.close();
}
catch (Exception e) {
}
return null;
}
}

Related

How to play/stop default sound automatically in Android?

I want to play and stop the default sound with following rules:
If the sound is not playing, let play it in 10 seconds.
If the sound is playing, let stop it and play at the first position.
Based on these above rules, I design a function as follows:
public MediaPlayer mp =null;
public void playDefaultSound(){
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
mp = MediaPlayer.create(getApplicationContext(), notification);
try {
if (mp.isPlaying()) {
mp.stop();
mp.release();
mp = MediaPlayer.create(getApplicationContext(), notification);
}
mp.start();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
mp.stop();
mp.release();
}
}, 10000);
} catch (Exception e) {
e.printStackTrace();
}
}
But sometime I still listen two sound are playing (in case of the first sound play and I call the playDefaultSound() function again). Do you think is it correct to delete the mp = MediaPlayer.create(getApplicationContext(), notification); bellow mp.release()? How could I correct the function to satisfy these rules? Thanks all
final MediaPlayer mp = new MediaPlayer();
public void playDefaultSound(){
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
try {
if (mp != null && mp.isPlaying()) {
mp.seekTo(0);
} else {
mp.reset();
mp.setDataSource(getApplicationNotification(), notification);
mp.start();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
mp.stop();
mp.release();
}
}, 10000);
} catch (Exception e) {
e.printStackTrace();
}
}
P.S. - Always see the state diagram or lifecycle of things whenever stuck.
Ref : [Android Media Player State Diagram][1]
[1]: https://developer.android.com/reference/android/media/MediaPlayer.html#StateDiagram "MediaPlayer State Diagram"

Play mp3 files one after the other with little delay with more elegant code?

I need to play multiple (correct :2 ) mp3 files loaded from my assets when a button is clicked. If the button is again clicked 2 new songs should be played and the old ones should be stopped immidiatelly.The process is on going...
At the moment I have achieved this by stupidly creating 2 mediaPlayer instances in 2 separate methods that basically have the same body .
The caller is an onTouch method and ,within it, first I call method 1 ,then sleep for 2 secs ,then call method 2. I m sure there must be a way for this to be more elegant code? For example I have implemented some listeners that are just sitting there (Actually tried to make this work but totally screwed up with Illegal States all over the place ). Ideally I would like to use 1 MediaPlayer and one method for playing every sound in my app.
int carouzelIndex = 0
#Override
public boolean onTouch(MotionEvent e, int scaledX, int scaledY) {
...
if (e.getAction() == MotionEvent.ACTION_UP) {
carouzelIndex++;
Assets.playMusic1("music1.ogg",false);
Thread thread = new Thread(){
public void run(){
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Assets.playMusic2("music2.ogg",false);
}
}
}
}
Now my player methods 1 and 2 (playMusic1() and playMusic2() )are the same. Both are instansiating different MediaPlayers and for the shake of simplicity I write just one copy in the post
Public class Assets
MediaPlayer mediaPlayer;
public static void playMusic(String filename, boolean looping) {
AssetFileDescriptor afd = null;
Log.d("Assets", "playing music");
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(looping);
}
try {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(looping);
}
afd = GameMainActivity.assets.openFd(filename);
mediaPlayer.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getLength());
afd.close();
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if (!mp.isPlaying())
mp.start();
}
});
mediaPlayer.setOnErrorListener(new OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
}
});
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mediaPlayerIsFinished = true;
mp.reset();
}
});
mediaPlayer.setOnInfoListener(new OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
// TODO Auto-generated method stub
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
For immediate playback you need two MediaPlayer set up and chained together via OnCompletionListener
You can do it like this:
Note that I also deleted some unnecessary overrides, you might add them again if you need them.
Public class Assets{
MediaPlayer mediaPlayer;
MediaPlayer mediaPlayer2;
boolean mediaplayer2prepared = false;
boolean mediaplayer1finished = false;
public static void playMusic(String filename, boolean looping, String filename2, boolean looping2) {
AssetFileDescriptor afd = null;
Log.d("Assets", "playing music");
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(looping);
mediaplayer1finished = false;
}
if (mediaPlayer2 == null) {
mediaPlayer2 = new MediaPlayer();
mediaPlayer2.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer2.setLooping(looping);
mediaplayer2prepared = false;
}
try {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(looping);
mediaplayer1finished = false;
}
if (mediaPlayer2 != null) {
mediaPlayer2.stop();
mediaPlayer2.release();
mediaPlayer2 = new MediaPlayer();
mediaplayer2prepared = false;
mediaPlayer2.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer2.setLooping(looping);
}
afd = GameMainActivity.assets.openFd(filename);
mediaPlayer.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getLength());
afd.close();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if (!mp.isPlaying())
mp.start();
}
});
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mediaPlayerIsFinished = true;
mp.reset();
if(mediaplayer2prepared)
mediaplayer2.start();
mediaplayer1finished = true;
}
});
mediaPlayer.prepareAsync();
afd2 = GameMainActivity.assets.openFd(filename2);
mediaPlayer2.setDataSource(afd2.getFileDescriptor(),
afd2.getStartOffset(), afd2.getLength());
afd2.close();
mediaPlayer2.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayerIsFinished = true;
mp.reset();
mediaplayer2prepared = true;
if(mediaplayer1finished && !mp.isPlaying()){
mp.start();
}
}
});
mediaPlayer2.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mediaPlayerIsFinished = true;
mp.reset();
}
});
mediaPlayer2.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
}
}
}
To start both mediaplayers in succession simply do Assets.playMusic("music1.ogg",false, "music2.ogg",false);
You can try something like this. Use a single mediaplayer in a single thread. It is an asynchronous operation so it will go on also if the thread is paused. I think it would work.
private Thread t = null;
private MediaPlayer mediaPlayer;
...
Button b=findViewById(R.id.yourButtonId);
b.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
//if button had previously clicked stop audio reproduction
if(mediaPlayer.isPlaying()) mediaPlayer.stop();
if(t.isAlive()) t.stop();
String[] files;
//Here you have to set your filenames in the array
...
...
//Now play that audio
playMusic(files, false);
}
});
...
protected static void playMusic(String[] files, boolean looping){
AssetFileDescriptor afd = null;
Log.e("Assets", "Playing music");
t=new Thread(){
#Override
public void run(){
//Initialize mediaPlayer
for(String filename:files){
mediaPlayer=new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(looping);
afd = GameMainAcivity.assets.openFd(filename);
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),afd.getLength());
afd.close();
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new OnPreparedListener(){
#Override
public void onPrepared(MediaPlayer mp){
if(!mp.isPlaying()) mp.start();
}
});
//while audio is playing wait in this position
while(mediaPlayer.isPlaying()){
this.wait(100);
}
//now loop can restart with the other filename that has
//to be reproduced
}
}
};
t.start();
}

Not able to achieve Gapless audio looping so far on Android

I have tried almost every method but I've failed to achieve gapless audio playback between looping a single track with a duration of 10-15 seconds.
Steps I've tried and failed :
Different audio file formats .mp3 .wav .ogg using
setLooping(true):
MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
mp1.setLooping(true);
mp1.start();
Creating two mediaplayers and looping one after another using
setOnCompletionListenersame failed to loop without gaps.
Using setNextMediaPlayer(nextmp) some how it works but only two loops is possible. We have to prepare and start again after the completion of previous two loops.
mp1.start();
mp1.setNextMediaPlayer(mp2);
Update:
Result of #Jeff Mixon answer:
Mediaplayer looping stops with an error Android.
Jeff Mixon works fine but only for 10 or 20 loops after that, due to some garbage collection issue the Mediaplayers stops immediately leaving the logs as posted below. I'm really kind of stuck here for 2 years. Thanks in advance.
E/MediaPlayer(24311): error (1, -38)
E/MediaPlayer(23256): Error(1,-1007)
E/MediaPlayer(23546): Error (1,-2147483648)
From the test that I have done, this solution works fine, over 150 loops with a 13 seconds 160 kbps MP3 without any problem:
public class LoopMediaPlayer {
public static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
}
To use LoopMediaPlayer you can just call:
LoopMediaPlayer.create(context, R.raw.sample);
Ugly proof-of-concept code, but you'll get the idea:
// Will need this in the callbacks
final AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.sample);
// Build and start first player
final MediaPlayer player1 = MediaPlayer.create(this, R.raw.sample);
player1.start();
// Ready second player
final MediaPlayer player2 = MediaPlayer.create(this, R.raw.sample);
player1.setNextMediaPlayer(player2);
player1.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
// When player1 completes, we reset it, and set up player2 to go back to player1 when it's done
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
player2.setNextMediaPlayer(player1);
}
});
player2.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
// Likewise, when player2 completes, we reset it and tell it player1 to user player2 after it's finished again
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
player1.setNextMediaPlayer(player2);
}
});
// This loop repeats itself endlessly in this fashion without gaps
This worked for me on an API 19 device and a 5-second 128 kbps MP3. No gaps in the loop.
At least as of KitKat, Mattia Maestrini's Answer (to this question) is the only solution I've found that allows gapless looping of a large (> 1Mb uncompressed) audio sample. I've tried:
.setLooping(true): gives interloop noise or pause even with perfectly trimmed .WAV sample (published bug in Android);
OGG format: frameless format, so better than MP3, but MediaPlayer still emits interloop artifacts; and
SoundPool: may work for small sound samples but large samples cause heap size overflow.
By simply including Maestrini's LoopMediaPlayer class in my project and then replacing my MediaPlayer.create() calls with LoopMediaPlayer.create() calls, I can ensure my .OGG sample is looped seamlessly. LoopMediaPlayer is therefore a commendably practical and transparent solution.
But this transparency begs the question: once I swap my MediaPlayer calls for LoopMediaPlayer calls, how does my instance call MediaPlayer methods such as .isPlaying, .pause or .setVolume? Below is my solution for this issue. Possibly it can be improved upon by someone more Java-savvy than myself (and I welcome their input), but so far I've found this a reliable solution.
The only changes I make to Maestrini's class (aside from some tweaks recommended by Lint) are as marked at the end of the code below; the rest I include for context. My addition is to implement several methods of MediaPlayer within LoopMediaPlayer by calling them on mCurrentPlayer.
Caveat: while I implement several useful methods of MediaPlayer below, I do not implement all of them. So if you expect for example to call .attachAuxEffect you will need to add this yourself as a method to LoopMediaPlayer along the lines of what I have added. Be sure to replicate the original interfaces of these methods (i.e., Parameters, Throws, and Returns):
public class LoopMediaPlayer {
private static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
private final MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
// code-read additions:
public boolean isPlaying() throws IllegalStateException {
return mCurrentPlayer.isPlaying();
}
public void setVolume(float leftVolume, float rightVolume) {
mCurrentPlayer.setVolume(leftVolume, rightVolume);
}
public void start() throws IllegalStateException {
mCurrentPlayer.start();
}
public void stop() throws IllegalStateException {
mCurrentPlayer.stop();
}
public void pause() throws IllegalStateException {
mCurrentPlayer.pause();
}
public void release() {
mCurrentPlayer.release();
mNextPlayer.release();
}
public void reset() {
mCurrentPlayer.reset();
}
}
Something like this should work. Keep two copies of the same file in the res.raw directory. Please note that this is just a POC and not an optimized code. I just tested this out and it is working as intended. Let me know what you think.
public class MainActivity extends Activity {
MediaPlayer mp1;
MediaPlayer mp2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp1 = MediaPlayer.create(MainActivity.this, R.raw.demo);
mp2 = MediaPlayer.create(MainActivity.this, R.raw.demo2);
mp1.start();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
int duration = mp1.getDuration();
while (mp1.isPlaying() || mp2.isPlaying()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
duration = duration - 100;
if (duration < 1000) {
if (mp1.isPlaying()) {
mp2.start();
mp1.reset();
mp1 = MediaPlayer.create(MainActivity.this,
R.raw.demo);
duration = mp2.getDuration();
} else {
mp1.start();
mp2.reset();
mp2 = MediaPlayer.create(MainActivity.this,
R.raw.demo2);
duration = mp1.getDuration();
}
}
}
}
});
thread.start();
}
}
I suggest you to use SoundPool API instead of MediaPlayer.
From the official documentation:
The SoundPool class manages and plays audio resources for
applications.
...
Sounds can be looped by setting a non-zero loop
value. A value of -1 causes the sound to loop forever. In this case,
the application must explicitly call the stop() function to stop the
sound. Any other non-zero value will cause the sound to repeat the
specified number of times, e.g. a value of 3 causes the sound to play
a total of 4 times.
...
Take a look here for a practical example of how to use SoundPool.
In using Mattia Maestrini's answer, I was able to get the audio looping the way I wanted but, since I was using this for Android Auto, discovered that the audio only played over my phones speakers instead of my car speakers. I eventually found this answer which points out a bug which makes it important in this context to use the new MediaPlayer() constructor with the setDataSource method. I was already using Uris in my code so I used that variant, so I'm not 100% sure how important that is, I would assume any of the other setDataSource variants would be sufficient if it matters for your code.
Here's what ultimately ended up working for me:
public class LoopMediaPlayer extends MediaPlayer {
private static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private Uri mMediaUri = null;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
private Float mLeftVolume;
private Float mRightVolume;
public static LoopMediaPlayer create(Context context, Uri mediaUri) {
try {
return new LoopMediaPlayer(context, mediaUri);
}
catch (Exception e) {
throw new RuntimeException("Unable to create media player", e);
}
}
private LoopMediaPlayer(Context context, Uri mediaUri) throws IOException {
mContext = context;
mMediaUri = mediaUri;
mCurrentPlayer = new MediaPlayer();
mCurrentPlayer.setDataSource(mContext, mMediaUri);
mCurrentPlayer.prepare();
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
try {
mNextPlayer = new MediaPlayer();
mNextPlayer.setDataSource(mContext, mMediaUri);
if (mLeftVolume != null && mRightVolume != null) {
mNextPlayer.setVolume(mLeftVolume, mRightVolume);
}
mNextPlayer.prepare();
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
catch (Exception e) {
Log.e(TAG, "Problem creating next media player", e);
}
}
private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
#Override
public void prepare() throws IllegalStateException {
// no-op, internal media-players are prepared when they are created.
}
#Override
public boolean isPlaying() throws IllegalStateException {
return mCurrentPlayer.isPlaying();
}
#Override
public void setVolume(float leftVolume, float rightVolume) {
mCurrentPlayer.setVolume(leftVolume, rightVolume);
mNextPlayer.setVolume(leftVolume, rightVolume);
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
}
#Override
public void start() throws IllegalStateException {
mCurrentPlayer.start();
}
#Override
public void stop() throws IllegalStateException {
mCurrentPlayer.stop();
}
#Override
public void pause() throws IllegalStateException {
mCurrentPlayer.pause();
}
#Override
public void release() {
mCurrentPlayer.release();
mNextPlayer.release();
}
#Override
public void reset() {
mCurrentPlayer.reset();
}
}
For some reason, I found that my "OnCompletion" Event was always firing a fraction of second late when attempting to loop an 8-second OGG file. For anyone experiencing this type of delay, try the following.
It is possible to forcibly queue a "nextMediaPlayer" as recommend in previous solutions, by simply posting a delayed Runnable to a Handler for your MediaPlayers and avoiding looping in onCompletion Event altogether.
This performs flawlessly for me with my 160kbps 8-second OGG, min API 16.
Somewhere in your Activity/Service, create a HandlerThread & Handler...
private HandlerThread SongLooperThread = new HandlerThread("SongLooperThread");
private Handler SongLooperHandler;
public void startSongLooperThread(){
SongLooperThread.start();
Looper looper = SongLooperThread.getLooper();
SongLooperHandler = new Handler(looper){
#Override
public void handleMessage(Message msg){
//do whatever...
}
}
}
public void stopSongLooperThread(){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){
SongLooperThread.quit();
} else {
SongLooperThread.quitSafely();
}
}`
...start the Thread, declare and set up your MediaPlayers...
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
startSongLooperThread();
activeSongResID = R.raw.some_loop;
activeMP = MediaPlayer.create(getApplicationContext(), activeSongResID);
activeSongMilliseconds = activeMP.getDuration();
queuedMP = MediaPlayer.create(getApplicationContext(),activeSongResID);
}
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
stopSongLooperThread();
activeMP.release();
queuedMP.release();
activeMP = null;
queuedMP = null;
}
...create a Method for swapping your MediaPlayers...
private void swapActivePlayers(){
Log.v("SongLooperService","MediaPlayer swap started....");
queuedMP.start();
//Immediately get the Duration of the current track, then queue the next swap.
activeSongMilliseconds = queuedMP.getDuration();
SongLooperHandler.postDelayed(timedQueue,activeSongMilliseconds);
Log.v("SongLooperService","Next call queued...");
activeMP.release();
//Swap your active and queued MPs...
Log.v("SongLooperService","MediaPlayers swapping....");
MediaPlayer temp = activeMP;
activeMP = queuedMP;
queuedMP = temp;
//Prepare your now invalid queuedMP...
queuedMP = MediaPlayer.create(getApplicationContext(),activeSongResID);
Log.v("SongLooperService","MediaPlayer swapped.");
}
...create Runnables to post to your thread...
private Runnable startMP = new Runnable(){
public void run(){
activeMP.start();
SongLooperHandler.postDelayed(timedQueue,activeSongMilliseconds);
}
};
private Runnable timedQueue = new Runnable(){
public void run(){
swapActivePlayers();
}
};
In your Service's onStartCommand() or somewhere in your Activity, start the MediaPlayer...
...
SongLooperHandler.post(startMP);
...
I have tried everything suggested here and elsewhere and the only thing that worked is ExoPlayer instead of the Music class. You can access your libgdx files with:
Uri.parse("file:///android_asset/" + path)
You'll also need platform specific code.
CODE-REad's LoopMediaPlayer example is great, but if you use the new MediaPlayer() method of creating the MediaPlayer (like I do for using File or AssetFileDescriptor datasources) rather than the MediaPlayer.Create() method then you must be careful to
Call the setOnCompletionListener method AFTER .start() or it will
not fire.
Fully .prepare() or .prepareAsync() the mNextPlayer before
calling .setNextMediaPlayer on the mCurrentPlayer or it will fail to
play the mNextPlayer. This means calling .start, setOnCompletionListener, and .setNextMediaPlayer in the onPreparedListener as shown below.
I have modified his code to use the new MediaPlayer() method to create the player and also added the ability to set datasource from AssetFileDescriptor and a File. I hope this saves someone some time.
public class LoopMediaPlayer {
private static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private AssetFileDescriptor mAfd = null;
private File mFile = null;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
public LoopMediaPlayer(Context context, File file){
mContext = context;
mFile = file;
try {
mCurrentPlayer = new MediaPlayer();
mCurrentPlayer.setLooping(false);
mCurrentPlayer.setDataSource(file.getAbsolutePath());
mCurrentPlayer.prepareAsync();
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
}
});
} catch (Exception e) {
Log.e("media", e.getLocalizedMessage());
}
}
public LoopMediaPlayer(Context context, AssetFileDescriptor afd){
mAfd = afd;
mContext = context;
try {
mCurrentPlayer = new MediaPlayer();
mCurrentPlayer.setLooping(false);
mCurrentPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mCurrentPlayer.prepareAsync();
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
}
});
} catch (Exception e) {
Log.e("media", e.getLocalizedMessage());
}
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setLooping(false);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
}
});
mCurrentPlayer.prepareAsync();
}
private void createNextMediaPlayer() {
try{
if(mAfd != null){
mNextPlayer = new MediaPlayer();
mNextPlayer.setDataSource(mAfd.getFileDescriptor(), mAfd.getStartOffset(), mAfd.getLength());
mNextPlayer.prepareAsync();
mNextPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
}
});
}
else if(mFile!=null){
mNextPlayer = new MediaPlayer();
mNextPlayer.setDataSource(mFile.getAbsolutePath());
mNextPlayer.prepareAsync();
mNextPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
}
});
}
else {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mNextPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
}
});
}
} catch (Exception e) {
}
}
private final MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
createNextMediaPlayer();
Log.d("LoopMediaPlayer", String.format("Loop #%d", ++mCounter));
}
};
// code-read additions:
public boolean isPlaying() throws IllegalStateException {
return mCurrentPlayer.isPlaying();
}
public void setVolume(float leftVolume, float rightVolume) {
mCurrentPlayer.setVolume(leftVolume, rightVolume);
}
public void start() throws IllegalStateException {
mCurrentPlayer.start();
}
public void stop() throws IllegalStateException {
mCurrentPlayer.stop();
}
public void pause() throws IllegalStateException {
mCurrentPlayer.pause();
}
public void release() {
mCurrentPlayer.release();
mNextPlayer.release();
}
public void reset() {
mCurrentPlayer.reset();
}
}

How to correctly change MediaPlayer audio stream type?

I have a simple mp service to play, pause, resume audio. All works fine.
But, last night I have decided to add a feature for user to route audio to ear-piece or speaker and have been battling with mp.setAudioStreamType().
Problem is that I can't change it while service connected and mp created. I don't want to terminate service and/or unbind and rebind as it would require a lot of refactoring
How do I supposed to change AudioStreamType while playing an audio?
Here is my code:
Player service:
public class PService extends Service {
private MediaPlayer mp = new MediaPlayer();
public static final String PLAYING_FINISHED_MSG = "1";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
mp.stop();
mp.release();
}
private void playSong(String file) {
try {
mp.reset();
mp.setDataSource(file);
mp.setAudioStreamType(MYAPP.getAudioStreamType());
mp.prepare();
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer arg0) {
Intent i = new Intent();
i.setAction(MDService.PLAYING_FINISHED_MSG);
sendBroadcast(i);
}
});
toggle route button onclick
currentlyPlayingFile = file;
currentlyPlayingPhone = phone;
lastDurationBeforePause = mpInterface.getCurrentPosition();
if(MYAPP.getAudioStreamType() == AudioManager.STREAM_MUSIC)
{
MYAPP.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
recording_player_route_button.setImageResource(R.drawable.route_off);
}
else{
MYAPP.setAudioStreamType(AudioManager.STREAM_MUSIC);
recording_player_route_button.setImageResource(R.drawable.route_on);
}
try {
mpInterface.playFile(file);
player_seekbar.setProgress(0);
player_seekbar.setMax(mpInterface.getDuration());
//seekto last millisecond after switching from/to sepaker
if(seekTo>0)
{
mpInterface.seekTo(seekTo);
}
isPauseButtonPressed = false;
handleSeekBarUpdate.postDelayed(handleSeekBarUpdateJob, 1);
} catch (RemoteException e) {
e.printStackTrace();
}
The MODIFY_AUDIO_SETTINGS permission is needed in the Manifest for this to work.
AudioManager am=(AudioManager)getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_NORMAL);
MediaPlayer mp=new MediaPlayer();
Uri ringtoneUri=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
try
{
mp.setDataSource(getApplicationContext(), ringtoneUri);
mp.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
mp.prepare();
mp.start();
}
catch(Exception e)
{
//exception caught in the end zone
}

Repeating notification sound with delay

I am attempting to play a notification sound once every two seconds. My code is as follows:
final Handler myHandler = new Handler();
mMediaPlayer = new MediaPlayer();
final Runnable mMyRunnable = new Runnable()
{
#Override
public void run()
{
try
{
mMediaPlayer.setDataSource(getBaseContext(), getAlarmUri(alarm_number));
final AudioManager audioManager = (AudioManager) getBaseContext().getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0)
{
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mMediaPlayer.prepare();
mMediaPlayer.start();
}
}
catch (IOException e)
{
}
}
};
mMediaPlayer.setOnCompletionListener(new OnCompletionListener()
{
#Override
public void onCompletion(MediaPlayer mp)
{
myHandler.postDelayed(mMyRunnable, 2000);
}
});
myHandler.post(mMyRunnable);
When the code executes, the notification sound plays once and then I get an IllegalStateException at the line mMediaPlayer.setDataSource(...
I have no idea why.
NO! You should use a Timer which will execute a TimerTask for a repeat rate that you choose:
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
// Do your work here
}
};
timer.schedule(task, 'DELAY_FOR_EXECUTION', 'TIME_TO_WAIT');
example:
`//timer.schedule(task, 0, 5000);`
This will run immediatly, every 5 secs

Categories

Resources