Lecture gapless et maux de tête avec Android et son MediaPlayer

Meh…

Je viens probablement de tomber sur le bout de code le plus étrange de l’histoire des bouts de codes. Et c’est d’autant plus étrange que ça nous vient de Google.

Incipit

Dans un lecteur de musique bien conçu, le passage d’une musique à l’autre se fait sans temps mort. On appelle ça la lecture gapless. La manière de gérer une transition fluide est généralement la même : on a un premier buffer qui lit la musique en cours et un second, qui commence à temporiser lorsque le premier arrive vers la fin. Ça, c’est la théorie.

Dans l’API Android, la classe qui permet de lire des médias audios ou vidéos s’appelle MediaPlayer  et s’utilise comme ça :

Jusque là, rien de bien compliqué. Sauf que ça fonctionne pour une seule musique. Dès que la lecture arrive à la fin, il faut charger l’audio suivant et ça se fait comme ça :

Sauf que vous voyez où je veux en venir : avec une telle méthode, ça ne fait pas de lecture gapless. Lorsque la musique arrive à la fin, le MediaPlayer  doit être réinitialisé et redémarré pour lire une nouvelle musique.

Depuis Android 4.1, cependant (soit l’API niveau 16), il est possible de faire un enchaînement fluide grâce à la méthode void setNextMediaPlayer (MediaPlayer next) de la classe. Il nous faut donc un second MediaPlayer pour assurer le fondu.

Le mal de tête

Voilà comment je m’y suis pris au départ : ma classe de gestion de la lecture possède deux MediaPlayer . Le premier devait me servir à la musique courante et le second, à la musique suivante. Lorsque la musique suivante arrivait à sa fin, j’avais prévu d’échanger les deux MediaPlayer , de réinitialiser celui qui gère la musique suivante, puis de le réaffecter avec la méthode setNextMediaPlayer() . Voici mon code originel (je précise que c’est du Groovy et pas du Java) :

Bon, je ne vais pas vous faire languir, ça ne fonctionne pas. J’ai erré un certain temps, voire un temps certain avant de trouver la solution. Le problème est que la documentation d’Android n’explique absolument pas l’effet de bord généré par setNextMediaPlayer() . Et il est gros : dès que la musique change, notre précédent MediaPlayer  est automatiquement relâché. Plus moyen, donc, d’invoquer des méthodes pour obtenir le durée de la chanson ou le temps de lecture. Mais en plus, le premier et le second MediaPlayer sont étroitement liés : si vous réinitialisez le premier, le second le sera aussi.

La solution

Je l’ai trouvée ici et elle est totalement contre-intuitive : il faut réinitialiser les deux MediaPlayer. Et, contrairement à ce que je pensais, ça ne stoppe absolument pas la lecture. Voici le code complet :

En espérant que ça aide quelqu’un.

Edit

En testant ma solution ce matin avec la tête un peu moins dans le cul, je me suis rendu compte que ma solution laisse quand-même un léger blanc. J’ai donc revu un peu le code de la fonction onCompletion()  :

La différence principale c’est qu’au lieu de réinitialiser les deux MediaPlayer  j’affecte nextMediaPlayer  à mediaPlayer  puis je créé un nouveau MediaPlayer dans nextMediaPlayer. J’espère que le colecteur de déchets saura gérer ça.

Aucun avis pertinent dans Lecture gapless et maux de tête avec Android et son MediaPlayer

Laisser un commentaire

indique des champs obligatoire.