web-dev-qa-db-ja.com

Android)では、これまでギャップレスオーディオループを実現できませんでした

私はほぼすべての方法を試しましたが、10〜15秒の持続時間で単一のトラックをループする間にギャップレスオーディオ再生を実現できませんでした。

私が試したが失敗した手順:

  1. さまざまなオーディオファイル形式_.mp3_ _.wav_ _.ogg_ setLooping(true)を使用:

    _MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
    mp1.setLooping(true);
    mp1.start();
    _
  2. 2つのメディアプレーヤーを作成し、setOnCompletionListenersameを使用して次々にループすると、ギャップなしでループできませんでした。

  3. setNextMediaPlayer(nextmp)を使用すると、どのように機能しますが、2つのループしか使用できません。前の2つのループが完了した後、準備して再開する必要があります。

    _mp1.start();
    mp1.setNextMediaPlayer(mp2);
    _
  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)
    _
32
Sai

私が行ったテストから、このソリューションは問題なく動作し、13秒160 kbpsMP3で150ループを超えます。

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

LoopMediaPlayerを使用するには、次のように呼び出すことができます。

LoopMediaPlayer.create(context, R.raw.sample);
31

醜い概念実証コードですが、次のようなアイデアが得られます。

// 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

これは、API19デバイスと5秒の128kbpsMP3で機能しました。ループにギャップはありません。

13
Jeff Mixon

少なくともキットカットの時点では、 Mattia Maestriniの回答 (この質問に対する)は、ギャップレスループを可能にする唯一の解決策です。大きな(> 1Mb非圧縮)オーディオサンプル。私はもう試した:

MaestriniのLoopMediaPlayerクラスをプロジェクトに含め、MediaPlayer.create()呼び出しをLoopMediaPlayer.create()呼び出しに置き換えるだけで、.OGGサンプルがシームレスにループされるようになります。したがって、LoopMediaPlayerは、称賛に値する実用的で透過的なソリューションです。

しかし、この透明性は疑問を投げかけます。MediaPlayer呼び出しをLoopMediaPlayer呼び出しと交換すると、インスタンスはどのように.MediaPlayer.pauseなどのisPlayingメソッドを呼び出しますか。または.setVolume以下はこの問題の私の解決策です。おそらく、私よりもJavaに精通している人が改善できる可能性があります(そして私は彼らの意見を歓迎します)が、これまでのところ、これは信頼できる解決策であることがわかりました。

Maestriniのクラスに加えた唯一の変更(Lintが推奨するいくつかの調整を除く)は、以下のコードの最後にマークされているとおりです。残りは文脈のために含めます。私の追加は、MediaPlayerLoopMediaPlayerのいくつかのメソッドを呼び出すことにより、それらをmCurrentPlayer内に実装することです。

警告:以下のMediaPlayerのいくつかの便利なメソッドを実装している間すべてを実装しているわけではありません。たとえば、.attachAuxEffectを呼び出す場合は、これを追加する必要があります。私が追加したものに沿ってLoopMediaPlayerする方法としてあなた自身。これらのメソッドの元のインターフェース(つまり、パラメーター、スロー、リターン)を必ず複製してください。

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

このようなものが機能するはずです。同じファイルの2つのコピーをres.rawディレクトリに保存します。これは単なるPOCであり、最適化されたコードではないことに注意してください。これをテストしたところ、意図したとおりに機能しています。どう考えているか教えてください。

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

MediaPlayerの代わりに SoundPool APIを使用することをお勧めします。

公式ドキュメントから:

SoundPoolクラスは、アプリケーションのオーディオリソースを管理および再生します。

.。

ゼロ以外のループ値を設定することで、サウンドをループさせることができます。値が-1の場合、サウンドは永久にループします。この場合、アプリケーションはstop()関数を明示的に呼び出してサウンドを停止する必要があります。その他のゼロ以外の値を指定すると、サウンドが指定された回数繰り返されます。値が3の場合、サウンドは合計4回再生されます。

.。

SoundPoolの使用方法の実際的な例については、 ここ をご覧ください。

2
bonnyz

何らかの理由で、8秒のOGGファイルをループしようとすると、"OnCompletion"イベントが常に数分の1秒lateを起動していることがわかりました。このタイプの遅延が発生している場合は、次のことを試してください。

強制的にキューに入れるa "nextMediaPlayer"以前のソリューションで推奨されているように、遅延実行可能をMediaPlayerのハンドラーに投稿し、onCompletionでのループを回避するイベントを実行するだけで可能です。完全に。

これは、160kbpsの8秒OGG、最小API16で問題なく動作します。

アクティビティ/サービスのどこかに、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();
    }
}`

...スレッドを開始します、宣言し、MediaPlayerを設定します...

@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;
}

...作成スワッピングのメソッド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.");
}

... createRunnablesスレッドに投稿します...

private Runnable startMP = new Runnable(){
    public void run(){
        activeMP.start();
        SongLooperHandler.postDelayed(timedQueue,activeSongMilliseconds);
    }
};

private Runnable timedQueue = new Runnable(){
    public void run(){
        swapActivePlayers();
    }
};

サービスのonStartCommand()またはアクティビティのどこかで、MediaPlayerを起動します...

...
SongLooperHandler.post(startMP);
...
1
Donald Tan

Mattia Maestriniの答え を使用すると、オーディオを希望どおりにループさせることができましたが、これをAndroid Autoに使用していたため、オーディオのみが車のスピーカーではなく電話のスピーカーで再生しました。最終的には この回答 が見つかりました。これは、このコンテキストでnew MediaPlayer()コンストラクターをsetDataSourceメソッドで使用することが重要になるバグを示しています。 。私はすでにコードでUrisを使用していたので、そのバリアントを使用しました。そのため、それがどれほど重要かは100%わかりません。コードにとって重要な場合は、他のsetDataSourceバリアントのいずれかで十分だと思います。

最終的に私のために働いたのは次のとおりです。

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

私はここや他の場所で提案されたすべてを試しましたが、うまくいったのはMusicクラスの代わりに ExoPlayer だけです。 libgdxファイルには次の方法でアクセスできます。

Uri.parse("file:///Android_asset/" + path)

プラットフォーム固有のコード も必要です。

0
Arunex

CODE-REadのLoopMediaPlayerの例は素晴らしいですが、新しいMediaPlayer()を使用する場合 MediaPlayerを作成する方法(私が行うようにFileまたはAssetFileDescriptorデータソースを使用)ではなくMediaPlayer.Create()メソッドの場合は、注意が必要です。

  1. .start()の後にsetOnCompletionListenerメソッドを呼び出すと、起動しません。
  2. MCurrentPlayerで.setNextMediaPlayerを呼び出す前に、mNextPlayerを完全に.prepare()または.prepareAsync()してください。そうしないと、mNextPlayerの再生に失敗します。これは、以下に示すように、onPreparedListenerで.start、setOnCompletionListener、および.setNextMediaPlayerを呼び出すことを意味します。

新しいMediaPlayer()メソッドを使用してプレーヤーを作成するように彼のコードを変更し、AssetFileDescriptorとファイルからデータソースを設定する機能も追加しました。これで誰かの時間を節約できることを願っています。

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