Gappless sound loop on Android.

And what to do about it.

Have you ever tried to loop sounds in Android? And how did it go?

Easy, right? Just call setLooping(true) on your instance of MediaPlayer and it’s done. There’s one small detail, though. When you set the .setLooping value to true and hear the result, if you don’t mind the silence gap between the loop, then you’re done. If, on the other hand, you need to do perfect loop with no gaps, well this is the blog post for you.

Faced with this challenge, I couldn’t believe that this issue is still persistent on Android in 2000 and seventeen. And yes, it’s official bug reported in 2011.

There are couple workarounds proposed which I’ve tried out and doesn’t (seem to) work.

1. Using ExoPlayer 2 — with the latest version of the player it seems that gapless loop has been achieved. Yet, when you implement the ExoPlayer 2 and tryout all the possible implementations of the looping behavior, it doesn’t work. Video is gapless though.

2. Using SoundPool — this is only applicable if you use sounds weighting less than 1MB, which is rarely the case. This was a total no go for me.

3. Using .ogg sounds — the sound format seems to have no impact on the gap even though some developers have reported this as possible fix.

4. Fooling the MediaPlayer with seekTo — create Handler object that will .seekTo(0) milliseconds before the sound ends — in theory this sounded like something that might work, but the end result is no different than .setLoopin(true).

new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mMediaPlayer.seekTo(0);
}
}
,mMediaPlayer.getDuration()-100);

Finally I’ve found solution that (kinnda) works — it’s based on this stackoverflow question and this answers (one and two). Here’s my modification of the looping sound classes:

Player is implemented by calling:

PerfectLoopMediaPlayer.create(context, R.raw.someResource)

Or:

PerfectLoopMediaPlayer.create(context, "SD/path_to_sound")

Basically, what it comes down to is having two players that are simultaneously prepared and concatenating their lifecycles so it seems that is one continuous stream. Things to consider:

1. possibilities of OOM exception

2. additional handling for MediaPlayer properties change (that might not be passed through when one object is killed and another one is created).

3. when instance is created, is the same moment when the player starts reproducing sound

IMPORTANT: It seems that Android 6.0.1 version still, even with this implementation has problems with the perfect loop. There is still noticeable gap present on some devices that are running on this OS version.

However, this is the best possible solution and the one you should go with, until it’s officially fixed.