una studio reproductor pausar musica mediaplayer example detener como cancion android android-audiomanager

reproductor - mediaplayer android studio



No es capaz de lograr un bucle de audio Gapless hasta ahora en Android (6)

He intentado casi todos los métodos, pero no he logrado una reproducción de audio sin interrupciones entre hacer una única pista con una duración de 10-15 segundos.

Pasos que he intentado y he fallado:

  1. Diferentes formatos de archivo de audio .mp3 .wav .ogg usando setLooping(true) :

    MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1); mp1.setLooping(true); mp1.start();

  2. La creación de dos reproductores multimedia y el bucle uno tras otro utilizando setOnCompletionListener no pudo realizar el bucle sin espacios.

  3. Usando setNextMediaPlayer(nextmp) alguna manera funciona, pero solo dos bucles son posibles. Tenemos que prepararnos y comenzar de nuevo después de completar los dos bucles anteriores.

    mp1.start(); mp1.setNextMediaPlayer(mp2);

  4. Actualización: Resultado de la respuesta de @Jeff Mixon: el bucle de Mediaplayer se detiene con un error de Android . Jeff Mixon funciona bien, pero solo para 10 o 20 loops después de eso, debido a algún problema de recolección de basura, los Mediaplayers se detienen inmediatamente dejando los registros como se muestra a continuación. Estoy realmente atascado aquí por 2 años. Gracias por adelantado.

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


Al menos en el caso de KitKat, la respuesta de Mattia Maestrini (a esta pregunta) es la única solución que he encontrado que permite el bucle sin interrupciones de una muestra de audio grande (> 1Mb sin comprimir) . He intentado:

Simplemente incluyendo la clase LoopMediaPlayer de Maestrini en mi proyecto y luego reemplazando mis llamadas de MediaPlayer.create() con llamadas de LoopMediaPlayer.create() , puedo asegurar que mi muestra de .OGG esté perfectamente conectada. LoopMediaPlayer es, por lo tanto, una solución LoopMediaPlayer práctica y transparente.

Pero esta transparencia plantea la pregunta: una vez que cambio mis llamadas de MediaPlayer por llamadas de LoopMediaPlayer , ¿cómo llama mi instancia los métodos de MediaPlayer como. isPlaying , .pause o .setVolume ? A continuación se muestra mi solución para este problema . Es posible que alguien con más conocimientos de Java pueda mejorarlo (y agradezco sus comentarios), pero hasta ahora he encontrado que esta es una solución confiable.

Los únicos cambios que hago en la clase de Maestrini (aparte de algunos ajustes recomendados por Lint) son los que se indican al final del código a continuación; El resto lo incluyo por contexto. Mi adición es implementar varios métodos de MediaPlayer dentro de LoopMediaPlayer llamándolos en mCurrentPlayer .

Advertencia: si bien implemento varios métodos útiles de MediaPlayer continuación, no los implemento todos. Entonces, si espera, por ejemplo, llamar a .attachAuxEffect , deberá agregarlo usted mismo como un método para LoopMediaPlayer a LoopMediaPlayer largo de las líneas de lo que he agregado. Asegúrese de replicar las interfaces originales de estos métodos (es decir, parámetros, lanzamientos y devoluciones):

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


Algo como esto debería funcionar. Mantenga dos copias del mismo archivo en el directorio res.raw. Tenga en cuenta que esto es solo un POC y no un código optimizado. Acabo de probar esto y funciona como es debido. Déjame saber lo que piensas.

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


Código feo de prueba de concepto, pero obtendrás la 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

Esto me funcionó en un dispositivo API 19 y un MP3 de 5 segundos a 128 kbps. No hay huecos en el bucle.


Desde la prueba que he hecho, esta solución funciona bien, más de 150 bucles con un MP3 de 13 segundos a 160 kbps sin ningún 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)); } }; }

Para usar LoopMediaPlayer puedes simplemente llamar:

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


Por alguna razón, encontré que mi Evento de "Finalización" siempre se activaba una fracción de segundo tarde cuando intentaba hacer un bucle en un archivo OGG de 8 segundos. Para cualquier persona que experimente este tipo de retraso, intente lo siguiente.

Es posible poner en cola a la fuerza un "nextMediaPlayer" como se recomienda en soluciones anteriores, simplemente publicando un Runnable retrasado en un Handler para sus MediaPlayers y evitando los bucles en el Evento onCompletion .

Esto funciona perfectamente para mí con mi OGG de 8 segundos a 160 kbps, API mínima 16.

En algún lugar de su actividad / servicio, cree 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(); } }`

... Inicia el tema , declara y configura tus 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; }

... crea un método para intercambiar tus 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."); }

... crea Runnables para publicar en tu hilo ...

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

En onStartCommand () de su Servicio o en algún lugar de su Actividad, inicie MediaPlayer ...

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


Te sugiero que SoundPool API de SoundPool lugar de MediaPlayer .

De la documentación oficial:

La clase SoundPool administra y reproduce recursos de audio para aplicaciones.

...

Los sonidos se pueden enlazar estableciendo un valor de bucle distinto de cero. Un valor de -1 hace que el sonido se reproduzca por siempre. En este caso, la aplicación debe llamar explícitamente a la función stop () para detener el sonido. Cualquier otro valor que no sea cero hará que el sonido se repita la cantidad de veces especificada, por ejemplo, un valor de 3 hace que el sonido se reproduzca un total de 4 veces.

...

Echa un vistazo here para ver un ejemplo práctico de cómo usar SoundPool .