/ / Non è stato possibile ottenere un loop audio Gapless finora su Android: Android, Android-Audiudiomanager

Non è stato possibile raggiungere il loop audio Gapless finora su Android: android, android-audiomanager

Ho provato quasi tutti i metodi ma non sono riuscito a ottenere una riproduzione audio gapless tra loop di una singola traccia con una durata di 10-15 secondi.

Passaggi che ho provato e fallito:

  1. Diversi formati di file audio .mp3 .wav .ogg utilizzando setLooping(true):

    MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
    mp1.setLooping(true);
    mp1.start();
    
  2. Creare due lettori multimediali e fare il ciclo uno dopo l'altro usando setOnCompletionListenerlo stesso non è riuscito a eseguire il loop senza lacune.

  3. utilizzando setNextMediaPlayer(nextmp) in parte funziona, ma sono possibili solo due loop. Dobbiamo prepararci e ricominciare dopo il completamento dei due precedenti loop.

    mp1.start();
    mp1.setNextMediaPlayer(mp2);
    
  4. Aggiornare: Risultato della risposta @Jeff Mixon: Il looping di Mediaplayer si interrompe con un errore Android. Jeff Mixon funziona bene ma solo per 10 o 20i cicli successivi, a causa di un problema di raccolta dei rifiuti, i Mediaplayer smettono immediatamente di lasciare i registri come indicato di seguito. Sono davvero bloccato qui da 2 anni. Grazie in anticipo.

    E/MediaPlayer(24311): error (1, -38)
    E/MediaPlayer(23256): Error(1,-1007)
    E/MediaPlayer(23546): Error (1,-2147483648)
    

risposte:

28 per risposta № 1

Dal test che ho fatto, questa soluzione funziona bene, oltre 150 loop con un MP3 da 13 secondi a 160 kbps senza alcun problema:

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

Usare LoopMediaPlayer puoi semplicemente chiamare:

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

12 per risposta № 2

Brutto codice di prova del concetto, ma avrai l'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

Questo ha funzionato per me su un dispositivo API 19 e un MP3 da 5 secondi a 128 kbps. Nessun gap nel loop.


3 per risposta № 3

Almeno a partire da KitKat, La risposta di Mattia Maestrini (a questa domanda) è il ho trovato l'unica soluzione che consente il loop gapless di un campione audio di grandi dimensioni (> 1 Mb non compresso). Ho provato:

Includendo semplicemente Maestrini "s LoopMediaPlayer classe nel mio progetto e poi sostituendo la mia MediaPlayer.create() chiama con LoopMediaPlayer.create() chiamate, posso assicurarmi che il mio campione .OGG sia eseguito in loop senza soluzione di continuità. LoopMediaPlayer è quindi una soluzione lodevolmente pratica e trasparente.

Ma questa trasparenza pone la domanda: una volta che cambio la mia MediaPlayer chiede LoopMediaPlayer chiama, come chiama la mia istanza MediaPlayer metodi come.isPlaying, .pause o .setVolume? Di seguito è la mia soluzione per questo problema. Forse può essere migliorato da qualcuno più esperto di Java di me (e accolgo con favore il loro contributo), ma finora ho trovato questa soluzione affidabile.

Le uniche modifiche che apporto alla classe di Maestrini(a parte alcune modifiche consigliate da Lint) sono indicati alla fine del codice seguente; il resto lo includo per il contesto. La mia aggiunta è quella di implementare diversi metodi di MediaPlayer entro LoopMediaPlayer chiamandoli mCurrentPlayer.

Avvertimento: mentre implemento diversi metodi utili di MediaPlayer sotto, Non li implemento tutti. Quindi, se ti aspetti, ad esempio, di chiamare .attachAuxEffect dovrai aggiungerlo tu stesso come metodo a LoopMediaPlayer sulla falsariga di ciò che ho aggiunto. Assicurati di replicare le interfacce originali di questi metodi (ad esempio, parametri, proiezioni e ritorni):

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

2 per risposta № 4

Qualcosa del genere dovrebbe funzionare. Conservare due copie dello stesso file nella directory res.raw. Si noti che questo è solo un POC e non un codice ottimizzato. Ho appena provato questo e funziona come previsto. Fatemi sapere cosa ne pensate.

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 per risposta № 5

Ti suggerisco di usare Soundpool API anziché MediaPlayer.

Dalla documentazione ufficiale:

La classe SoundPool gestisce e riproduce risorse audio per applicazioni.

...

I suoni possono essere riprodotti in loop impostando un valore diverso da zerociclo continuo valore. Un valore -1 fa sì che il suono venga riprodotto in loop per sempre. In questo caso, l'applicazione deve chiamare esplicitamente la funzione stop () per interrompere il suono. Qualsiasi altro valore diverso da zero farà sì che il suono ripeta il numero di volte specificato, ad es. un valore di 3 fa suonare il suono un totale di 4 volte.

...

Guarda Qui per un esempio pratico di come usare SoundPool.


1 per risposta № 6

Per qualche motivo, ho scoperto che il mio "Al termine" L'evento ha sempre sparato una frazione di secondo in ritardo quando si tenta di eseguire il loop di un file OGG di 8 secondi. Per chiunque abbia questo tipo di ritardo, prova quanto segue.

È possibile coda forzata un "NextMediaPlayer" come raccomandato nelle soluzioni precedenti, semplicemente pubblicando a Runnable ritardato a un gestore per i tuoi MediaPlayer e evitando loop in onCompletion Evento del tutto.

Ciò si comporta perfettamente con me con il mio OGG da 8 secondi a 160 kbps, API 16 min.

Da qualche parte nella tua attività / servizio, crea un 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();
}
}`

...inizia la discussione, dichiarare e imposta i tuoi 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;
}

...creare un Metodo di scambio i tuoi MediaPlayer ...

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

...creare Runnables per pubblicare nella tua discussione ...

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

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

OnStartCommand () del tuo servizio o da qualche parte nella tua attività, avvia MediaPlayer ...

...
SongLooperHandler.post(startMP);
...