Bonjour à tous! Dans ce tutoriel Ionic 5 (Ionic 4 compatible) nous allons voir comment créer un lecteur audio MP3 avec Ionic.

Nous allons utiliser le plugin Media d’Ionic qui propose une vaste sélection de fonctions qui nous permettrons de créer un lecteur complet.

Nous prendrons l’exemple de l’application Audible et de son lecteur de livre audio.

IONIC AUDIO: Comment créer un lecteur audio MP3 avec Ionic (Ionic Media)
IONIC AUDIO: Comment créer un lecteur audio MP3 avec Ionic (Ionic Media)

Si vous pouvez créer une application de lecture de fichier audio plus avancée, je vous conseille mon cours Ionic JS: https://drissas.com/ionic-javascript/

Voilà donc les étapes que nous allons suivre pour créer une lecteur audio MP3 avec Ionic:

  1. Créer un lecteur audio avec Ionic
  2. Lire un fichier audio MP3 dans Ionic
  3. Comment mettre sur pause le lecteur audio
  4. Récupérer et afficher le temps de lecture restant
  5. Avancer et reculer de 30 secondes

Comment créer un lecteur audio avec Ionic ?

On commence ce tutoriel Ionic 5 par la création de l’application et son design rapide.

Vous pouvez créer votre application Ionic de lecteur audio avec la commande suivante:

ionic start audio blank --type="angular"

Pour nous rapprocher du thème sombre de l’application Audible d’Amazon, nous donnons une couleur noir à notre header:

<ion-header>
  <ion-toolbar color="dark">
    <ion-title>
      Ionic Audio
    </ion-title>
  </ion-toolbar>
</ion-header>

Ensuite je vous propose d’afficher la couverture de votre titre, ou de votre livre audio par exemple.

Pour vos tests vous pouvez comme moi télécharger la couverture du livre audio suivant (à placer dans le dossier assets/):

Puis vous pouvez afficher cette couverture avec les balises suivante:

<ion-content>
  <div class="img_box">
    <img src="../../assets/libre_audio.jpg">
  </div>

Puis donner les propriétés suivantes pour afficher le fond de la page en noir et celui de l’image en gris foncé:

ion-content{
  --ion-background-color: #333333;
}
.img_box {
  padding: 20px;
  background-color: #1d1d1d;
}

On complète l’affichage de notre page avec l’ajout du titre, e, cours, ainsi que de la progression de notre fichier MP3:

<div id="container">
  <h2>Chapitre 3</h2>
  <ion-range min="0" max="100" value="40" mode="md" color="warning"></ion-range>
  <p>Temps restant: 13m 12s</p>
</div>

On utilise le composant Ionic Range pour créer notre barre de progression, dont je vous retrouvez la documentation avancée ici: https://ionicframework.com/docs/api/range

Nous verrons dans les parties suivantes comment dynamiser l’affichage de la barre de progression et du temps restant en temps réel.

Nous utiliserons pour cela un certain nombre de variables et de fonctions.

Vous pouvez ainsi pour peaufiner l’affichage du titre et de la barre de progression ajouter les propriétés CSS suivantes:

#container {
  text-align: center;
  padding: 20px;
}
h2 {
  color: white;
  font-size: 16px;
  margin: 2px 0px;
}
ion-range {
  padding: 0px 10px;
  --bar-height: 3px;
}
p {
  color: #b7b6b6;
  font-size: 13px;
  margin: 0px;
  font-weight: 500;
}

On termine le contenu de notre page avec la création des trois boutons d’actions et mettre sur pause, revenir en arrière ou avancer la progression du ficher audio.

Pour cela on créer trois boutons avec le composant Ionic ion-button, et on les place dans une boîte flex_box:

<div class="flex_box">
  <ion-button fill="clear" color="light">
    <ion-icon slot="icon-only" name="play-back"></ion-icon>
  </ion-button>
  <ion-button shape="round" color="light">
    <ion-icon slot="icon-only" name="pause"></ion-icon>
  </ion-button>
  <ion-button fill="clear" color="light">
    <ion-icon slot="icon-only" name="play-forward"></ion-icon>
  </ion-button>
</div>

On donne ainsi simplement les propriétés CSS flex-box à notre boîte div, et on modifie légèrement l’apparence de nos boutons:

.flex_box {
  height: 250px;
  display: flex;
  justify-content: center;
  align-items: center;
}
ion-button {
  height: 86px;
  min-height: max-content;
  margin: 0px 7px;
  font-size: 21px;
}

Voilà donc pour la partie visuelle de notre application. À noter aussi que j’ai modifié la couleur warning pour notre barre de progression dans le fichier:

src/theme/variables.scss

Voilà les nouvelles valeurs de couleurs de ma variable warning (orange donc pour ressembler à l’application Audible):

--ion-color-warning: #ffa500;
--ion-color-warning-rgb: 255,165,0;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0,0,0;
--ion-color-warning-shade: #e09100;
--ion-color-warning-tint: #ffae1a;

On passe dès maintenant à la lecture de notre fichier audio MP3 dans Ionic.

Comment lire un fichier audio MP3 dans Ionic ?

Pour lire des fichiers audio dans Ionic, nous allons utiliser le plugin Ionic Media: https://ionicframework.com/docs/native/media

Il existe un autre plugin natif de lecture audio, Ionic Audio Natif, mais il ne permet pas de gérer de manière avancé les fonctions de lecture, pause, avancement, etc..

Cet autre plugin Native Audio est plus adapté à la lecture de musique de fond ou à la création de bruit d’interaction.

Je vous laisse donc aussi le lien de la documentation Cordova de plugin Media que nous allons utiliser aujourd’hui: https://github.com/apache/cordova-plugin-media

Pour installer ce plugin dans votre application, vous pouvez entrer les deux commandes suivantes tout simplement:

ionic cordova plugin add cordova-plugin-media
npm install @ionic-native/media

Ensuite rendez-vous dans le fichier suivant pour importer et déclarer ce nouveau plugin:

src/app/app.module.ts

La procédure d’importation est ensuite classique:

import { Media } from '@ionic-native/media/ngx';

@NgModule({
  [...],
  providers: [
    [...],
    Media, // <- plugin Media
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

On peut ensuite se rendre dans le fichier TypeScript de notre page d’accueil:

src/app/home/home.page.ts

Pour ensuite importer et déclarer localement le plugin et ces deux modules dans notre page Home:

import { Platform } from '@ionic/angular';
import { Media, MediaObject } from '@ionic-native/media/ngx';

[...]
constructor(
    public media: Media,
    public platform: Platform
  ) {}

On se sert en effet du module MediaObject pour déclarer notre première variable file, qui représentera notre fichier audio:

file: MediaObject;

On créé ensuite notre première fonction readAudio() pour créer le média audio à lire, puis pour lancer sa lecture:

readAudio() {
  this.file = this.media.create('../../assets/003.mp3');
  // to listen to plugin events:
  this.file.onStatusUpdate.subscribe(status => console.log(status)); // fires when file status changes
  this.file.onSuccess.subscribe(() => console.log('Action is successful'));
  this.file.onError.subscribe(error => console.log('Error!', error));
  // play the file
  this.file.play();
}

Cette fonction permet aussi de renvoyer dans la console le résultat positif ou non de notre fonction de lecture audio avec Ionic.

Lorsque vous créez le média audio à lire, indiquez l’adresse de votre fichier MP3 dans votre dossier assets/, dans mon cas le fichier « 003.mp3 ».

On exécute alors notre fonction dans le constructor() mais seulement lorsque la page a bien fini de se charger.

Pour cela no utilise la fonction ready() du plugin Platform que nous avons importé et déclaré précédemment. On exécute alors notre fonction readAudio() une fois la page chargée:

constructor(
  public media: Media,
  public platform: Platform
) {
  this.platform.ready().then(() => {
    this.readAudio();
  });
}

Attention, pour tester le bon fonctionnement de votre lecteur audio avec Ionic, vous devez lancer votre application sur une plateforme mobile ou browser.

Le plus simple pour nous est de lancer notre application sur la plateforme browser (équivalent d’un appareil mobile sur un navigateur):

ionic cordova run browser

La première fois, cette commande devrait vous proposer de créer cette plateforme et ensuite de lancer votre application dans votre navigateur.

Votre application devrait donc s’ouvrir à l’adresse http://localhost:8100/ et vous lire votre fichier audio.

Nous allons maintenant voir comment interagir avec ce fichier audio et nos boutons Ionic.

Comment mettre sur pause le lecteur audio dans Ionic ?

Si vous avez regardé la documentation, vous avez surement remarqué que les principales fonctions d’interaction sont déjà conçues.

Il existe notamment une fonction pause() et play() pour ce plugin qui permet de mettre sur pause et de lire le fichier audio.

Nous allons donc surtout travailler sur l’interface du bouton dans cette partie.

On commence par créer la variable et chaîne de caractère playIcon pour stocker le nom de notre icône:

playIcon = 'pause';

Vous retrouvez toutes les icônes d’Ionic sur le site suivant: https://ionicons.com/. SI vous travailler avec Ionic 4 vous pouvez toujours accéder et choisir vos icônes sur le site de la V4: https://ionicons.com/v4/.

On affecte ensuite notre variable playIcon à l’attribut name de l’icône de notre bouton play ou pause:

<ion-button (click)="playPause()" shape="round" color="light">
  <ion-icon slot="icon-only" [name]="playIcon"></ion-icon>
</ion-button>

Ce bouton est en plus associé à la fonction playPause() qui comme son nom l’indique va nous permettre de jouer ou de mettre sur pause la lecture.

Dans notre fonction playPause() on effectue une condition sur la valeur de noter variable playIcon, pour savoir sur quel mode nous étions précédemment.

Ensuite on change la valeur de notre icône et on joue ou met sur sur pause le fichier audio en fonction du cas:

playPause() {
  if(this.playIcon == 'pause') {
    this.playIcon = 'play';
    this.file.pause();
  } else {
    this.playIcon = 'pause';
    this.file.play();
  }
}

Cette fonction nous permet ainsi de mettre à jour la valeur de notre icône à chaque clique mais également de mettre sur pause ou de jouer le fichier audio.

L’utilisateur est désormais capable d’interagir avec le fichier est de le mettre sur pause à sa guise.

Comment récupérer et afficher le temps de lecture restant ?

On souhaite maintenant proposer plus d’informations à l’utilisateur, et lui permettre de visualiser l’avancement de la lecture.

Nous allons en effet afficher l’avancement de notre audio dans une barre de progression Ionic, avec un ensemble de fonction et de variable.

On commence tout d’abord par créer trois variables qui nous servirons pour cette fonctionnalité:

audioDuration: number;
currentPosition: number;
restTime: string;

Analysons chaque variable:

  • audioDuration: la durée totale de notre fichier audio en secondes (ex: 2230)
  • currentPosition: la position actuelle de la lecture (ex: 1490)
  • restTime: le temps restant en caractère (ex: 12m 56s)

Bref on utilisera deux variables de type nombre pour notre barre de progression, avec la valeur maximal (audioDuration) et la position actuelle de notre curseur (currentPosition).

On convertira ensuite la différence entre ces deux variables en un affichage en minutes et secondes.

Pour commencer, on peut récupérer la durée totale de notre fichier audio MP3 avec la fonction du plugin getDuration().

Ici par exemple on renvoie la valeur absolue de la durée de noter fichier MP3 en secondes:

this.audioDuration = Math.floor(this.file.getDuration());

Il existe existe également une deuxième fonction qui renvoie en direct la position de lecture de notre fichier audio avec Ionic: getCurrentPosition().

On utilise donc cette fonction et on renvoie la valeur de la position:

this.file.getCurrentPosition().then((position) => {};

Après avoir effectué quelques tests, j’ai constaté en consultant les rapports d’erreurs du plugin qu’on ne pouvait récupérer la durée totale du fichier MP3 qu’après exécution de la fonction getCurrentPosition().

On récupèrera donc en même temps la durée totale du fichier et sa position actuelle de lecture, quitte à récupérer plusieurs fois la durée totale qui ne change pas.

On utilise ensuite la fonction JavaScript setInterval() qui nous permet d’exécuter du code de manière régulière dans le temps, ici à chaque seconde:

setInterval(() => {
  this.file.getCurrentPosition().then((position) => {
    this.audioDuration = Math.floor(this.file.getDuration());
    this.currentPosition = Math.floor(position);
  });
}, 1000 );

Grâce à ces lignes de codes, on récupère chaque seconde la position de la lecture de notre fichier audio, ainsi que sa durée totale qui elle ne change pas.

On créé ensuite une fonction pour convertir un temps en secondes en un chaîne de caractère qui sépare minutes et secondes:

convertSec(secondes: number) {
  const min = Math.floor((secondes/60/60)*60);
  const sec = Math.floor(((secondes/60/60)*60 - min)*60);
  this.restTime = min + 'm ' + sec + 's';
}

On peut alors ensuite calculer la différence entre la position actuelle et la durée totale de notre fichier pour calculer le temps restant:

this.convertSec(this.audioDuration - this.currentPosition);

Avec ces nouvelles lignes de codes, on peut maintenant compléter notre fonction readAudio() pour la faire à al fois lire notre audio mais aussi récupérer ces données de lecture:

readAudio() {
  this.file = this.media.create('../../assets/003.mp3');
  // to listen to plugin events:
  this.file.onStatusUpdate.subscribe(status => console.log(status)); // fires when file status changes
  this.file.onSuccess.subscribe(() => console.log('Action is successful'));
  this.file.onError.subscribe(error => console.log('Error!', error));
  // play the file
  this.file.play();
  // get current position
  setInterval(() => {D
    this.file.getCurrentPosition().then((position) => {
      this.audioDuration = Math.floor(this.file.getDuration());
      this.currentPosition = Math.floor(position);
      this.convertSec(this.audioDuration - this.currentPosition);
    });
  }, 1000 );
}

Il ne nous reste plus qu’à afficher ces données dans notre HTML, avec les différents attributs de notre barre de progression:

<div id="container">
  <h2>Chapitre 3</h2>
  <ion-range (click)="changePosition()" min="0" [max]="audioDuration" [(ngModel)]="currentPosition" mode="md" color="warning"></ion-range>
  <p>Temps restant: {{ restTime }}</p>

On ajoute en plus de cela l’évènement (click) pour exécuter la fonction changePosition() à chaque fois que l’utilisateur clique sur la barre de progression et change sa position.

La fonction changePosition() est simple, elle récupère la valeur de notre barre de progression, et utilise la fonction de déplacement seekTo() pour déplacer la lecture de notre fichier audio:

changePosition() {
  console.log('changePosition: ' + this.currentPosition);
  this.file.seekTo(this.currentPosition*1000);
  this.convertSec(this.audioDuration - this.currentPosition);
}

Ensuite il ne nous reste plus qu’à convertir à nouveau la position actuelle en temps restant et l’afficher en dessous de notre barre de progression.

Comment avancer et reculer de 30 secondes ?

Nous venons de voir rapidement comment déplacer le curseur de notre barre de progression et en conséquence avancer ou reculer la lecture.

Il existe aussi dans de nombreux lecteur audio (comme Audible) la possibilité d’avancer et de reculer de par exemple 30 secondes en cliquant sur un bouton.

Nous allons donc créer la fonction seekTo() qui prendra en paramètre le temps en millisecondes que nous voulons ajouter ou soustraire à notre lecture.

On reprend donc nos deux boutons et on les associe à cette même fonction, mais avec des paramètres différents: -30000 (-30 secondes) et 30000 (+30 s):

<ion-button (click)="seekTo(-30000)" fill="clear" color="light">
  <ion-icon slot="icon-only" name="play-back"></ion-icon>
</ion-button>
[...]
<ion-button (click)="seekTo(30000)" fill="clear" color="light">
  <ion-icon slot="icon-only" name="play-forward"></ion-icon>
</ion-button>

On utilisera ensuite la fonction seekTo() du plugin Media qui prend en paramètre des millisecondes, et nous permet de déplacer notre fichier audio à cet emplacement exact:

this.file.seekTo(10000); // déplacement à l'emplacement 10 secondes

Attention, cette fonction nous déplace à l’emplacement 10 secondes, mais nous ne décale pas de 10 secondes.

Pour faire un décalage de plus ou moins 30 secondes, nous devons récupérer la position actuelle du curseur et lui ajouter ou soustraire nos 30 secondes.

Ensuite déplacer la lecture du fichier audio à ce nouvel emplacement:

seekTo(duration: number) {
  console.log('décalage de ' + duration / 1000 + ' secondes');
  this.file.seekTo(this.currentPosition*1000 + duration);
  this.currentPosition = this.currentPosition + Math.floor(duration/1000);
  this.convertSec(this.audioDuration - this.currentPosition);
}