Ludovic ROLAND

Blog technique sur mes expériences de développeur.

Android : Jouer des sons d'alerte

12 octobre 2022

Sur Android, il est parfois nécessaire de jouer des sons au sein d’une application. Ces sons peuvent être de diverses natures :

  • Lecture d’un fichier audio long ;
  • Lecture d’un son bref.

Les deux utilisations sont radicalement opposées. On peut par exemple lire un fichier audio long pour par exemple jouer une musique, un podcast ou alors un contenu en streaming au sein de l’application. Alors que le son bref est plutôt utilisé pour alerter l’utilisateur.

Ces alertes peuvent être de diverses natures :

  • Informer l’utilisateur via une confirmation qu’une action s’est déroulée correctement au sein de l’application ;
  • Informer l’utilisateur via une alerte qu’une action s’est mal déroulée au sein de l’application.

SounPool et MediaPlayer

Si l’on suit les recommandations de la documentation Android, en fonction du cas d’usage, il convient d’utiliser ses classes différentes du SDK Android. Aussi, lorsque l’on souhaite lire un fichier audio long, il convient d’utiliser la classe MediaPlayer, tandis que pour lire des fichiers audios brefs, il convient de se tourner vers la classe SoundPool.

Il existe pas mal de littérature sur le sujet, y compris la documentation officielle. Mais si l’on doit résumer, voici ce que l’on pourrait dire :

  • La classe SoundPool permet de jouer uniquement des sons courts tandis que la classe MediaPlayer permet de jouer des sons longs ;
  • La classe SoundPool permet de décoder les sons avant la lecture ce qui permet de limiter l’usage du CPU (la classe MediaPlayer décode les sons au moment de la lecture) ;
  • La classe SoundPool permet de jouer plusieurs sons à la fois, ce qui n’est pas la cas de la classe MediaPlayer.

Dans la suite de cet article, je vous propose de voir comment manipuler la classe SoundPool afin de jouer un son d’alerte dans une application Android.

Utiliser la classe SoundPool

Quand on utilise la classe SoundPool pour lire une alerte, la première étape consiste à déclarer une instance de la classe. Cette instance peut tout à fait être portée par une Activity, un Fragment, un ViewModel ou encore un AndroidViewModel en fonction de l’architecture de votre application.

Pour créer une nouvelle instance de la classe SoundPool, il convient d’utiliser la classe SoundPool.Builder :

private val soundPool = SoundPool.Builder().build()

Il est bien évidemment possible de personnaliser l’instance en précisant par exemple le nombre de sons qu’il est possible de jouer en parallèle grâce à la méthode setMaxStreams. Dans le cadre de ce tutoriel, un seul son pourra être jouer :

private val soundPool = SoundPool.Builder().apply {
  setMaxStreams(1)
}.build()

Il est également possible de préciser l’usage qui est fait du ou des sons de notre instance de SoundPool en renseignant un AudioAttribute à l’aide de la méthode setAudioAttributes. Cette méthode prend en paramètre une instance de la classe AudioAttributes qu’il convient de créer à l’aide de la classe AudioAttributes.Builder :

private val soundPool = SoundPool.Builder().apply {
  setMaxStreams(1)
  setAudioAttributes(AudioAttributes.Builder().build())
}.build()

Depuis ce Builder, il est alors possible de préciser l’usage du son via la méthode setUsage et le type de son via la méthode setContentType. Ces deux méthodes prennent en paramètre un entier qui correspond à une constante du SDK Android. Dans notre cas, nous allons utiliser la constante AudioAttributes.USAGE_ALARM pour la méthode setUsage et la constante AudioAttributes.CONTENT_TYPE_SONIFICATION pour la méthode setContentType :

private val soundPool = SoundPool.Builder().apply {
  setMaxStreams(1)
  setAudioAttributes(AudioAttributes.Builder().apply {
    setUsage(AudioAttributes.USAGE_ALARM)
    setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
  }.build())
}.build()

Charger le son

Maintenant que notre instance de SoundPool est configurée, il convient de charger le son que nous souhaitons jouer. Pour ce faire, il convient d’appeler la méthode SoundPool#load. Il existe plusieurs signatures pour cette méthode mais dans le cadre de ce tutoriel, nous allons utiliser la signature qui accepte trois paramètres :

  • un Context ;
  • un identifiant de fichier ;
  • un entier permettant de définir une priorité.

Si vous développez sur Android, vous devriez être à l’aise avec le premier paramètre qui est un Context. Si vous êtes dans une Activity vous pouvez la passer en tant que paramètre grâce au mot clef this@Activity, si vous êtes dans un Fragment vous pouvez récupérer un Context à l’aide de la méthode requireContext, si vous êtes dans un AndroidViewModel vous pouvez utiliser la classe Application grâce à la méthode getApplication et si vous êtes dans un Composable, vous pouvez récupérer un Context grâce à l’instruction LocalContext.current. Bref, les possibilités ne manquent pas.

Le second paramètre correspond à l’identifiant du fichier audio à lire. Il doit être préalablement placé dans le dossier res/raw du projet.

Finalement pour le dernier paramètre, vous pouvez définir une priorité de 1. Ce paramètre n’est de toute façon pas pris en compte actuellement comme le précise la documentation :

La méthode renvoie alors un entier qui correspond alors à un identifiant interne.

Dans le cadre de ce tutoriel, le fichier à lire est le fichier alert.wav. On obtient alors :

private val soundAlert = soundPool.load(getApplication(), R.raw.alert, 1)

Jouer le son

Maintenant que le son est chargé, il convient de le jouer. Pour ce faire, il est nécessaire d’appeler la méthode SoundPool#play. Cette méthode accepte plusieurs paramètres :

  • l’identifiant du son à jouer ;
  • le volume du son à jouer dans l’oreille gauche ;
  • Le volume du son à jouer dans l’oreille droite ;
  • La priorité du son à jouer ;
  • Le nombre de fois que le son doit être joué ;
  • La vitesse de lecture du son.

Dans notre cas, obtient :

soundPool.play(soundAlert, 1f, 1f, 1, 2, 1f)

Libérer la mémoire

Si vous souhaitez optimiser un peu l’utilisation que fait votre application de la mémoire, vous pouvez forcer la libération de celle-ci par l’instance de la classe SoundPool. Pour se faire, il convient d’appeler la méthode release et d’affecter la valeur null à l’instance :

soundPool.release()
soundPool = null

Commentaires