/ / Não é possível obter loops de áudio sem intervalos até o momento no Android - Android, android-audiomanager

Não é possível obter loops de áudio sem intervalos até o momento no Android - Android, android-audiomanager

Eu tentei quase todos os métodos, mas eu não consegui alcançar a reprodução de áudio sem intervalos entre looping de uma única faixa com uma duração de 10-15 segundos.

Passos que eu tentei e falhei:

  1. Diferentes formatos de arquivos de áudio .mp3 .wav .ogg usando setLooping(true):

    MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
    mp1.setLooping(true);
    mp1.start();
    
  2. Criando dois mediaplayers e dando um loop após o outro usando setOnCompletionListenermesmo não conseguiu loop sem lacunas.

  3. Usando setNextMediaPlayer(nextmp) alguma coisa funciona, mas apenas dois loops são possíveis. Temos que nos preparar e começar de novo após a conclusão dos dois ciclos anteriores.

    mp1.start();
    mp1.setNextMediaPlayer(mp2);
    
  4. Atualizar: Resultado da resposta do @Jeff Mixon: Mediaplayer looping pára com um erro Android. Jeff Mixon funciona bem, mas apenas por 10 ou 20Depois de alguns loops, devido a algum problema de coleta de lixo, o Mediaplayers pára imediatamente de deixar os logs conforme abaixo. Eu estou realmente meio preso aqui por 2 anos. Obrigado antecipadamente.

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

Respostas:

28 para resposta № 1

Do teste que eu fiz, esta solução funciona bem, mais de 150 loops com 13 segundos de 160 kbps MP3 sem qualquer 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));
}
};
}

Usar LoopMediaPlayer você pode simplesmente ligar:

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

12 para resposta № 2

Código de prova de conceito feio, mas você vai ter a idéia:

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

Isso funcionou para mim em um dispositivo API 19 e em um MP3 de 5 segundos com 128 kbps. Não há lacunas no loop.


3 para resposta № 3

Pelo menos a partir de KitKat, Resposta de Mattia Maestrini (para esta questão) é a A única solução que eu encontrei que permite looping sem intervalos de uma amostra de áudio grande (> 1Mb não-comprimido). Eu tentei:

Simplesmente incluindo Maestrini "s LoopMediaPlayer classe no meu projeto e, em seguida, substituindo o meu MediaPlayer.create() chama com LoopMediaPlayer.create() chamadas, posso garantir que minha amostra .OGG esteja conectada perfeitamente. LoopMediaPlayer é, portanto, uma solução louvável, prática e transparente.

Mas essa transparência levanta a questão: uma vez que eu troque minha MediaPlayer apela para LoopMediaPlayer chama, como minha instância chama MediaPlayer métodos como.isPlaying, .pause ou .setVolume? Abaixo está a minha solução para este problema. Possivelmente ele pode ser melhorado por alguém mais experiente em Java do que eu (e eu agradeço sua contribuição), mas até agora eu encontrei esta uma solução confiável.

As únicas mudanças que faço para a aula de Maestrini(além de alguns ajustes recomendados pela Lint) são tão marcados no final do código abaixo; o resto incluo para o contexto. Minha adição é implementar vários métodos de MediaPlayer dentro LoopMediaPlayer chamando-os mCurrentPlayer.

Embargo: enquanto eu implementar vários métodos úteis de MediaPlayer abaixo, Eu não implemento todos eles. Então, se você espera, por exemplo, chamar .attachAuxEffect você precisará adicionar isso sozinho como um método para LoopMediaPlayer ao longo das linhas do que eu adicionei. Certifique-se de replicar as interfaces originais desses métodos (por exemplo, parâmetros, lances e retornos):

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 para resposta № 4

Algo como isso deve funcionar. Mantenha duas cópias do mesmo arquivo no diretório res.raw. Por favor, note que este é apenas um POC e não um código otimizado. Acabei de testar isso e está funcionando como pretendido. Diz-me o que pensas.

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 para resposta № 5

Eu sugiro que você use SoundPool API em vez de MediaPlayer.

Da documentação oficial:

A classe SoundPool gerencia e reproduz recursos de áudio para aplicações.

...

Os sons podem ser colocados em loop, definindo um valor diferente de zerolaço valor. Um valor de -1 faz com que o som seja repetido para sempre. Nesse caso, o aplicativo deve chamar explicitamente a função stop () para parar o som. Qualquer outro valor diferente de zero fará com que o som repita a número especificado de vezes, por ex. um valor de 3 faz com que o som seja reproduzido um total de 4 vezes.

...

Dê uma olhada Aqui para um exemplo prático de como usar SoundPool.


1 para resposta № 6

Por alguma razão, descobri que meu "Ao concluir" O evento estava sempre disparando uma fração de segundo tarde ao tentar fazer um loop em um arquivo OGG de 8 segundos. Para qualquer pessoa com esse tipo de atraso, tente o seguinte.

É possível fila à força uma "nextMediaPlayer" conforme recomendado em soluções anteriores, simplesmente postando um Runnable atrasado um manipulador para seus MediaPlayers e evitando loop em onCompletion Evento completamente.

Isso funciona perfeitamente para mim com meu OGG de 8 segundos e 160kbps, API 16 min.

Em algum lugar da sua Atividade / Serviço, crie um Manipulador de Rosca e Manipulador...

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

...inicie o thread, declarar e configure seus MediaPlayers...

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

...Crie um Método para troca seus 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.");
}

...crio Runnables para postar no seu tópico ...

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

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

No onStartCommand () do seu Serviço ou em algum lugar da sua Atividade, inicie o MediaPlayer ...

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