Not able to achieve Gapless audio looping so far on Android

后端 未结 9 1908
走了就别回头了
走了就别回头了 2020-11-28 06:05

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 trie

9条回答
  •  囚心锁ツ
    2020-11-28 06:51

    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();
        }
    }
    

提交回复
热议问题