Pas capable de réaliser des boucles audio Gapless jusqu'ici sur Android

J'ai essayé presque toutes les méthodes, mais j'ai échoué à obtenir une lecture audio sans boucle entre la boucle d'une seule piste avec une durée de 10-15 secondes.

Étapes sur lesquelles j'ai essayé et échoué:

  • Quelle est la différence entre DataOutputStream et ObjectOutputStream?
  • Existe-t-il un moyen d'obtenir une sortie de débogage copie-et-pastable dans l'Emulateur SDK Android?
  • Rendre les fichiers epub dans Android
  • Analysez le projet Android avec Lint et SonarQube
  • Paramètre Android LinearLayout background programmatic
  • Qu'est ce que "android: key" utilisé dans <PreferenceScreen>?
    1. Différents formats de fichiers audio .mp3 .wav .ogg aide de setLooping(true) :

       MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1); mp1.setLooping(true); mp1.start(); 
    2. La création de deux mediaplayers et la mise en boucle l'une après l'autre à l'aide de setOnCompletionListener échoué à boucler sans lacunes.

    3. En utilisant setNextMediaPlayer(nextmp) certains comment cela fonctionne, mais seulement deux boucles sont possibles. Nous devons nous préparer et recommencer après l'achèvement des deux dernières boucles.

       mp1.start(); mp1.setNextMediaPlayer(mp2); 
    4. Mise à jour: Résultat de la réponse @Jeff Mixon: la mise en boucle du Mediaplayer s'arrête avec une erreur Android . Jeff Mixon fonctionne bien, mais seulement pour 10 ou 20 boucles après cela, en raison d'un problème de collecte de déchets, les Mediaplayers s'arrêtent immédiatement en quittant les journaux, comme indiqué ci-dessous. Je suis vraiment gentille de rester ici depuis 2 ans. Merci d'avance.

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

  • Tirage de la toile Android tirezTextrez-vous la taille de la police de la largeur?
  • Href = "tel:" sur Android tente d'ajouter un contact au lieu de déclencher un appel
  • Changer l'onglet d'onglet de couleur sur V4 ViewPager
  • Erreur d'installation: INSTALL_FAILED_MEDIA_UNAVAILABLE?
  • Android - sauvegarder l'image dans la galerie
  • Problème d'imprimante Bluetooth dans Android
  • 6 Solutions collect form web for “Pas capable de réaliser des boucles audio Gapless jusqu'ici sur Android”

    À partir du test que j'ai fait, cette solution fonctionne bien, plus de 150 boucles avec un MP3 de 13 secondes 160 kbps sans problème:

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

    Pour utiliser LoopMediaPlayer vous pouvez simplement appeler:

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

    Code de preuve de concept laid, mais vous allez avoir l'idée:

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

    Cela a fonctionné pour moi sur un périphérique API 19 et un MP3 de 5 secondes 128 kbps. Pas de lacunes dans la boucle.

    Une telle chose devrait fonctionner. Conservez deux copies du même fichier dans le répertoire res.raw. Notez que ce n'est qu'un code POC et non un code optimisé. Je viens de tester cela et ça marche comme prévu. Laissez-moi savoir ce que vous pensez.

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

    Je vous suggère d'utiliser SoundPool API au lieu de MediaPlayer .

    De la documentation officielle:

    La classe SoundPool gère et joue des ressources audio pour les applications.

    Les sons peuvent être bouclés en définissant une valeur de boucle non nulle. Une valeur de -1 amène le son à boucler pour toujours. Dans ce cas, l'application doit appeler explicitement la fonction stop () pour arrêter le son. Toute autre valeur non nulle entraînera le répétition du son du nombre de fois spécifié, par exemple, une valeur de 3 fait que le son joue un total de 4 fois.

    SoundPool ici un exemple pratique de l'utilisation de SoundPool .

    Au moins à partir de KitKat, la réponse de Mattia Maestrini (à cette question) est la seule solution que j'ai trouvée qui permet une boucle sans arrêt d'un grand échantillon audio (> 1Mb non compressé) . J'ai essayé:

    • .setLooping (true): donne un bruit interloop ou pause même avec l'exemple .WAV parfaitement équilibré ( bogue publié dans Android );
    • Format OGG : format sans cadre, donc mieux que MP3, mais MediaPlayer émet toujours des artefacts interloop; et
    • SoundPool : peut fonctionner pour de petits échantillons de sons, mais de gros échantillons provoquent des débordements de taille de tas .

    En incluant simplement la classe LoopMediaPlayer de Maestrini dans mon projet, puis en remplaçant mes appels LoopMediaPlayer.create() appels LoopMediaPlayer.create() , je peux vous assurer que mon exemple .OGG est bouclé de façon transparente. LoopMediaPlayer est donc une solution pratique et transparente.

    Mais cette transparence implique la question: une fois que je permets mes appels LoopMediaPlayer pour les appels LoopMediaPlayer , comment mon instance appelle-t-elle des méthodes MediaPlayer telles que. isPlaying , .pause ou .setVolume ? Voici ma solution pour ce problème . Peut-être peut-être amélioré par quelqu'un de plus Java-Savvy que moi-même (et j'apprécie leur contribution), mais jusqu'à présent, j'ai trouvé cette solution fiable.

    Les seuls changements que je fais au cours de Maestrini (à part quelques ajustements recommandés par Lint) sont aussi marqués à la fin du code ci-dessous; Le reste que j'inclue pour le contexte. Mon ajout consiste à implémenter plusieurs méthodes de LoopMediaPlayer dans LoopMediaPlayer en les appelant sur mCurrentPlayer .

    Mise en garde: alors que je met en œuvre plusieurs méthodes utiles de MediaPlayer ci-dessous, je ne les met pas en œuvre. Donc, si vous vous attendez par exemple à appeler .attachAuxEffect vous devrez l'ajouter comme une méthode à LoopMediaPlayer selon les lignes que j'ai ajoutées. Assurez-vous de répliquer les interfaces d'origine de ces méthodes (c.-à-d., Paramètres, Tirs et Retours):

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

    Pour une raison quelconque, j'ai constaté que mon événement "OnCompletion" tirait toujours une fraction de seconde en retard lors de la tentative de boucle d'un fichier OGG de 8 secondes. Pour ceux qui connaissent ce type de délai, essayez ce qui suit.

    Il est possible de mettre en file d'attente un "nextMediaPlayer" comme recommandé dans les solutions précédentes, en affichant simplement un Runnable retardé à un gestionnaire pour vos MediaPlayers et en évitant de boucler complètement l' événement OnCompletion .

    Cela fonctionne parfaitement pour moi avec mon OGG de 8 bits de 160kbps, min API 16.

    Quelque part dans votre activité / service, créez 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(); } }` 

    lancez le fil , déclarez et configurez vos 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; } 

    … créez une méthode pour échanger vos 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."); } 

    … crée Runnables pour publier sur votre thread …

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

    Dans l'OnStartCommand de votre service () ou quelque part dans votre activité, démarrez MediaPlayer …

     ... SongLooperHandler.post(startMP); ... 
    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.