到目前为止,无法在Android上实现Gapless音频循环

Sai*_*Sai 31 android android-audiomanager

我已经尝试了几乎所有方法,但是我无法在循环单个轨道之间实现无间隙音频播放,持续时间为10-15秒.

我尝试过的步骤失败了:

  1. 不同的音频文件格式.mp3 .wav .ogg使用 setLooping(true):

    MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
    mp1.setLooping(true);
    mp1.start();
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建两个媒体播放器并使用setOnCompletionListener相同的循环逐个循环 无法循环无间隙.

  3. 使用setNextMediaPlayer(nextmp)它的工作方式,但只有两个循环是可能的.在完成前两个循环后,我们必须准备并重新开始.

    mp1.start();
    mp1.setNextMediaPlayer(mp2);
    
    Run Code Online (Sandbox Code Playgroud)
  4. 更新: @Jeff Mixon的结果回答: Mediaplayer循环因Android错误而停止.Jeff Mixon工作正常,但之后只有10或20个循环,由于一些垃圾收集问题,Mediaplayers会立即停止离开日志,如下所示.我真的有点困在这里2年了.提前致谢.

    E/MediaPlayer(24311): error (1, -38)
    E/MediaPlayer(23256): Error(1,-1007)
    E/MediaPlayer(23546): Error (1,-2147483648)
    
    Run Code Online (Sandbox Code Playgroud)

Mat*_*ini 32

从我所做的测试来看,这个解决方案运行良好,150个循环和13秒160 kbps MP3没有任何问题:

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));
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

要使用LoopMediaPlayer你可以打电话:

LoopMediaPlayer.create(context, R.raw.sample);
Run Code Online (Sandbox Code Playgroud)


Jef*_*xon 13

丑陋的概念证明代码,但你会明白:

// 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
Run Code Online (Sandbox Code Playgroud)

这适用于API 19设备和5秒128 kbps MP3.循环中没有间隙.


COD*_*EaD 6

至少就KitKat而言,Mattia Maestrini的答案(对此问题)是我发现唯一解决方案,解决方案可以对大型(> 1Mb未压缩)音频样本进行无间隙循环。我试过了:

通过简单地将Maestrini的LoopMediaPlayer类包括在我的项目中,然后用MediaPlayer.create()调用替换LoopMediaPlayer.create()调用,我可以确保.OGG示例被无缝循环。 LoopMediaPlayer因此,这是一个值得称赞的实用且透明的解决方案。

但是这种透明性引出了一个问题:一旦我将MediaPlayer调用交换为LoopMediaPlayer调用,实例调用MediaPlayer方法(如)将如何进行。isPlaying.pause还是.setVolume以下是我针对此问题的解决方案。也许可以由比我更精通Java的人来改进它(我欢迎他们的投入),但是到目前为止,我发现这是一个可靠的解决方案。

我对Maestrini班级所做的唯一更改(除了Lint建议的一些调整之外)在下面的代码结尾处进行了标记;其余内容包括在内。我除了是落实的几种方法MediaPlayerLoopMediaPlayer调用它们mCurrentPlayer

警告:虽然我实现了MediaPlayer以下几种有用的方法,但并未实现所有这些方法。 因此,如果您希望例如打电话给.attachAuxEffect您,则需要按照我添加的内容自行添加此方法LoopMediaPlayer。确保复制这些方法的原始接口(即,Parameters,Throws和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();
    }
}
Run Code Online (Sandbox Code Playgroud)