Not able to achieve Gapless audio looping so far on Android
Asked Answered
T

9

42

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 :

  1. Different audio file formats .mp3 .wav .ogg using setLooping(true):

    MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
    mp1.setLooping(true);
    mp1.start();
    
  2. Creating two mediaplayers and looping one after another using setOnCompletionListenersame failed to loop without gaps.

  3. 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);
    
  4. 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)
    
Teacake answered 9/10, 2014 at 8:51 Comment(7)
You could try android.media.AudioTrack to feed the raw audio data to the hardware again and againHallah
When you tried 2 media players, did you set one to time 0 before mp1 finished?Niobium
Yea I've tried it. some loops works without gaps others doesn't but mostly it dose have a gap. I've achieved this using timer and time tasker. @NiobiumTeacake
How big are the files you are trying to loop?Niobium
it is about 30-40 seconds @NiobiumTeacake
"till date" == "so far" or is it some Android specific vocabulary?Rafaelrafaela
@CiroSantilli六四事件法轮功包卓轩 Grammar mistake, thanks for pointing out. I will change the title accordingly.Teacake
Q
43

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);
Quadruple answered 26/4, 2015 at 21:42 Comment(10)
@SaiKiran Have you checked if the solution works for you? Let me know. ThanksQuadruple
The audio is playing fine without any gaps. but when it reaches 300+ loops and when the phone is in sleep, I'm getting a error like MediaPlayer finalized without being released. I think this is because of GC. please have a look at #15023537 . and correct me if you can. thanks .Teacake
@SaiKiran Unfortunately i can't reproduce the issue, i tested over 400+ loops with two different phones without any problem. I improved the code and made some changes, i hope this solves your issue. Let me knowQuadruple
@SaiKiran Also i forgot to mention that MediaPlayer finalized without being released is a warning, not an errorQuadruple
Anyway, the MediaPlayer stops playing the audio after that warning. Please give me 3 more hours. I'm going to loop the a 15sec audio for 3 hours. if everything goes fine, I will accept your answer. Thank you for your quick responses..Teacake
See my answer below; at least as of KitKat, Mattia Maestrini's Answer is the only solution I've found that allows gapless looping of a large (> 1Mb uncompressed) audio sample.Retrogression
After 1minut it stops playing. It does not work for me.Surf
Worth noting that the Lightning Bug app took 6 months to implement a background solution for gapless with the NDK that still doesn't work on all devices (lightningbug.me/2012/02/07/im-using-2-9-but-still-hear-skipping), and that Google never responded on the issue (issuetracker.google.com/issues/36931073).Thorstein
@MattiaMaestrini The sound will start to play immediately once create() got called. How can you keep it silent before e.g. starting it with a button click? I removed mCurrentPlayer.start(); from onPrepared() but this will result into error "Start called in state 4: error(-38,0)" Basically the player stays in prepared mode once I remove mCurrentPlayer.start(); from onPrepared()Dire
Works on some sounds but not ALL , so not 100% solution.Faubion
R
14

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:

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();
    }
}
Retrogression answered 20/7, 2016 at 17:33 Comment(11)
What happens when mNextPlayer is currently playing and you call pause() method? I'm pretty sure the audio wont be stopped because in pause() method mCurrentPlayer is being paused. I had written a better solution for this will update the answer soon.Teacake
Solution: Before doing any operation to mediaplayer, you need to check which mediaplayer is being played and do (pause,stop etc) operations to currently playing mediaplayer.Teacake
@SaiKiran, as I say above, so far I've found this a reliable solution. I think the reason is that LoopMediaPlayer's OnCompletionListener callback ensures that mCurrentPlayer points to mNextPlayer as soon as setNextMediaPlayer activates mNextPlayer (mCurrentPlayer = mNextPlayer above).Retrogression
@Code-Read good additions, but you should also call mNextPlayer.onStop() and mNextPlayer.release() on corresponding methods, to avoid the warning MediaPlayer finalized without being releasedPoleyn
@MateusGondim, I'm guessing you mean .stop() rather than .onStop(). Are you actually experiencing this error with LoopMediaPlayer? If you are, with what Android release and vendor? I can understand adding mNextPlayer.release() as you suggest, because technically mNextPlayer.create() puts it in the Prepared state (see notes under State Diagram in docs), but not mNextPlayer.stop(), because mNextPlayer is never started, only mCurrentPlayer. Do you agree?Retrogression
@Code-Read Yes, I meant .stop(), sorry. I thought you would get an exception upon calling release() without calling stop(), but it turns out it's not a problem, so yeah, I guess you'd just need to call mNextPlayer.release().Poleyn
@MateusGondim, I've added mNextPlayer.release() as you recommend. Thanks for your comments.Retrogression
If anyone is having trouble with the sound stopping after awhile: change your stream type MUSIC (Or Set the MUSIC attribute in AudioAttributes).Jealousy
how do I stop..??Palaeography
If anyone is getting any GC error or getting audio cut after a while also check that you have a reference to your LoopMediaPlayer as a member variable in your calling class. After that it works perfectly! :)Angloindian
I converted this code to Kotlin : LoopMediaPlayer.ktAlgeciras
S
13

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.

Showing answered 4/11, 2014 at 0:44 Comment(5)
Finally I got the solution but I have to check the code with lower API's and I'll let you know. Thanks @zaventhTeacake
Any news on behavior on lower APIs?Kwa
I just tried this with KitKat and got a noticable gap when looping a 21 second stereo 160 kb/s, 44.1KHz Vorbis Audio sound sample. The gap was worse than I got by using MediaPlayer.setLooping.Retrogression
P.S.: I think the reason for the gap is that this method does not initialize player2 until after player1 has completed. @Mattia's code above at https://mcmap.net/q/383646/-not-able-to-achieve-gapless-audio-looping-so-far-on-android overcomes this problem and I find it plays gaplessly.Retrogression
Still noticeable gapLowrey
C
2

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();
}
}
Cummine answered 5/11, 2014 at 4:7 Comment(1)
Thanks you for your reply, your code has still some gap or overlap. anyway a very thank you for your response.Teacake
K
2

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.

Kex answered 25/4, 2015 at 11:36 Comment(3)
For some reason I can't use sound pool.Teacake
I tried using SoundPool to avoid gaps in my looped playback. I discovered that SoundPool apparently buffers its media to the system heap, so is impractical (at least with my KitKat platform) for larger sound samples (my sample 44.1KHz * 32 bits * 21 seconds). See https://mcmap.net/q/391727/-audiocache-heap-size-overflow-issue-req-size-1053184-max-size-1048576.Retrogression
sound pool can play at most 5 seconds long audio filesSurf
L
2

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();
    }
}
Libertarian answered 5/8, 2019 at 1:24 Comment(0)
T
1

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);
...
Tannenberg answered 21/5, 2017 at 19:32 Comment(0)
J
0

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.

Jezreel answered 7/7, 2019 at 19:14 Comment(0)
B
0

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

  1. Call the setOnCompletionListener method AFTER .start() or it will not fire.
  2. 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();
    }
}
Benjy answered 8/7, 2019 at 22:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.