Blog technique sur mes expériences de développeur.
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 :
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 :
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
.
Quelle est la différence entre ces deux classes ?
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 :
SoundPool
permet de jouer uniquement des sons courts tandis que la classe MediaPlayer
permet de jouer des sons longs ;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) ;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.
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()
Bien évidemment le SDK Android offre bien plus de possibilités de personnalisation. N’hésitez pas à vous tourner vers la documentation officielle pour en prendre connaissance.
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 :
Context
;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 :
Currently has no effect. Use a value of 1 for future compatibility.
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)
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 :
Dans notre cas, obtient :
soundPool.play(soundAlert, 1f, 1f, 1, 2, 1f)
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