Son spatial

Le son spatial est une expérience audio immersive qui permet de plonger au cœur de l'action, pour un son toujours plus réaliste. Le son est "spatialisé" pour créer un effet multi-enceintes, semblable à une configuration de son surround, mais avec des écouteurs.

Par exemple, dans un film, le son d'une voiture peut commencer derrière l'utilisateur, se déplacer vers l'avant et s'éloigner au loin. Lors d'un chat vidéo, les voix peuvent être séparées et placées autour de l'utilisateur, ce qui permet d'identifier plus facilement les interlocuteurs.

Si votre contenu utilise un format audio compatible, vous pouvez ajouter l'audio spatial à votre application à partir d'Android 13 (niveau d'API 33).

Requête de fonctionnalités

Utilisez la classe Spatializer pour interroger les capacités et le comportement de spatialisation de l'appareil. Commencez par récupérer une instance de Spatializer à partir de AudioManager :

Kotlin

val spatializer = audioManager.spatializer

Java

Spatializer spatializer = AudioManager.getSpatializer();

Une fois que vous avez obtenu Spatializer, vérifiez les quatre conditions qui doivent être remplies pour que l'appareil produise un son spatialisé :

Critères Vérifier
L'appareil est-il compatible avec la spatialisation ? getImmersiveAudioLevel() n'est pas SPATIALIZER_IMMERSIVE_LEVEL_NONE
La spatialisation est-elle disponible ? La disponibilité de
dépend de la compatibilité avec le routage de sortie audio actuel.
isAvailable() est true
La spatialisation est-elle activée ? isEnabled() est true
Une piste audio avec les paramètres indiqués peut-elle être spatialisée ? canBeSpatialized() est true

Ces conditions peuvent ne pas être remplies, par exemple si la spatialisation n'est pas disponible pour la piste audio actuelle ou si elle est complètement désactivée sur l'appareil de sortie audio.

Suivi de la tête

Avec les casques compatibles, la plate-forme peut ajuster la spatialisation de l'audio en fonction de la position de la tête de l'utilisateur. Pour vérifier si un capteur de mouvements de la tête est disponible pour le routage de la sortie audio actuelle, appelez isHeadTrackerAvailable().

Contenu compatible

Spatializer.canBeSpatialized() indique si l'audio avec les propriétés données peut être spatialisé avec le routage de l'appareil de sortie actuel. Cette méthode utilise un AudioAttributes et un AudioFormat, qui sont tous deux décrits plus en détail ci-dessous.

AudioAttributes

Un objet AudioAttributes décrit l'utilisation d'un flux audio (par exemple, audio de jeu ou média standard), ainsi que ses comportements de lecture et son type de contenu.

Lorsque vous appelez canBeSpatialized(), utilisez la même instance AudioAttributes que celle définie pour votre Player. Par exemple, si vous utilisez la bibliothèque Jetpack Media3 et que vous n'avez pas personnalisé AudioAttributes, utilisez AudioAttributes.DEFAULT.

Désactiver le son spatial

Pour indiquer que votre contenu a déjà été spatialisé, appelez setIsContentSpatialized(true) afin que l'audio ne soit pas traité deux fois. Vous pouvez également ajuster le comportement de spatialisation pour la désactiver complètement en appelant setSpatializationBehavior(AudioAttributes.SPATIALIZATION_BEHAVIOR_NEVER).

AudioFormat

Un objet AudioFormat décrit les détails du format et de la configuration des canaux d'une piste audio.

Lorsque vous instanciez AudioFormat pour le transmettre à canBeSpatialized(), définissez l'encodage sur le même que le format de sortie attendu du décodeur. Vous devez également définir un masque de canal qui correspond à la configuration des canaux de votre contenu. Pour obtenir des conseils sur les valeurs spécifiques à utiliser, consultez la section Comportement de spatialisation par défaut.

Écouter les modifications apportées à Spatializer

Pour écouter les modifications de l'état de Spatializer, vous pouvez ajouter un écouteur avec Spatializer.addOnSpatializerStateChangedListener(). De même, pour écouter les changements de disponibilité d'un détecteur de mouvements de la tête, appelez Spatializer.addOnHeadTrackerAvailableListener().

Cela peut être utile si vous souhaitez ajuster la sélection de pistes pendant la lecture à l'aide des rappels de l'auditeur. Par exemple, lorsqu'un utilisateur connecte ou déconnecte son casque de l'appareil, le rappel onSpatializerAvailableChanged indique si l'effet spatialiseur est disponible pour le nouveau routage de sortie audio. À ce stade, vous pouvez envisager de mettre à jour la logique de sélection des pistes de votre lecteur pour qu'elle corresponde aux nouvelles capacités de l'appareil. Pour en savoir plus sur le comportement de sélection des pistes d'ExoPlayer, consultez la section ExoPlayer et l'audio spatial.

ExoPlayer et le son spatial

Les dernières versions d'ExoPlayer facilitent l'adoption du son spatialisé. Si vous utilisez la bibliothèque ExoPlayer autonome (nom de package com.google.android.exoplayer2), la version 2.17 configure la plate-forme pour qu'elle génère un son spatialisé, et la version 2.18 introduit des contraintes sur le nombre de canaux audio. Si vous utilisez le module ExoPlayer de la bibliothèque Media3 (nom du package androidx.media3), les versions 1.0.0-beta01 et ultérieures incluent ces mêmes mises à jour.

Une fois que vous avez mis à jour votre dépendance ExoPlayer vers la dernière version, votre application n'a plus qu'à inclure du contenu pouvant être spatialisé.

Contraintes liées au nombre de canaux audio

Lorsque les quatre conditions du son spatial sont remplies, ExoPlayer sélectionne une piste audio multicanal. Sinon, ExoPlayer choisit une piste stéréo. Si les propriétés Spatializer changent, ExoPlayer déclenche une nouvelle sélection de pistes pour sélectionner une piste audio correspondant aux propriétés actuelles. Notez que cette nouvelle sélection de pistes peut entraîner une courte période de rechargement.

Pour désactiver les contraintes sur le nombre de canaux audio, définissez les paramètres de sélection de piste sur le lecteur, comme indiqué ci-dessous :

Kotlin

exoPlayer.trackSelectionParameters = DefaultTrackSelector.Parameters.Builder(context)
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()

Java

exoPlayer.setTrackSelectionParameters(
  new DefaultTrackSelector.Parameters.Builder(context)
    .setConstrainAudioChannelCountToDeviceCapabilities(false)
    .build()
);

De même, vous pouvez mettre à jour les paramètres d'un sélecteur de piste existant pour désactiver les contraintes de nombre de canaux audio comme suit :

Kotlin

val trackSelector = DefaultTrackSelector(context)
...
trackSelector.parameters = trackSelector.buildUponParameters()
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()

Java

DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
...
trackSelector.setParameters(
  trackSelector
    .buildUponParameters()
    .setConstrainAudioChannelCountToDeviceCapabilities(false)
    .build()
);

Lorsque les contraintes sur le nombre de canaux audio sont désactivées, si le contenu comporte plusieurs pistes audio, ExoPlayer sélectionne initialement la piste qui comporte le plus grand nombre de canaux et qui peut être lue sur l'appareil. Par exemple, si le contenu contient une piste audio multicanal et une piste audio stéréo, et que l'appareil est compatible avec la lecture des deux, ExoPlayer sélectionne la piste multicanal. Pour savoir comment personnaliser ce comportement, consultez la section Sélection de la piste audio.

Sélection de la piste audio

Lorsque le comportement des contraintes de nombre de canaux audio d'ExoPlayer est désactivé, ExoPlayer ne sélectionne pas automatiquement une piste audio correspondant aux propriétés du spatialiseur de l'appareil. À la place, vous pouvez personnaliser la logique de sélection des pistes d'ExoPlayer en définissant des paramètres de sélection des pistes avant ou pendant la lecture. Par défaut, ExoPlayer sélectionne les pistes audio qui sont identiques à la piste initiale en termes de type MIME (encodage), de nombre de canaux et de fréquence d'échantillonnage.

Modifier les paramètres de sélection des pistes

Pour modifier les paramètres de sélection de piste d'ExoPlayer, utilisez Player.setTrackSelectionParameters(). De même, vous pouvez obtenir les paramètres actuels d'ExoPlayer avec Player.getTrackSelectionParameters(). Par exemple, pour sélectionner une piste audio stéréo en cours de lecture :

Kotlin

exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters
  .buildUpon()
  .setMaxAudioChannelCount(2)
  .build()

Java

exoPlayer.setTrackSelectionParameters(
  exoPlayer.getTrackSelectionParameters()
    .buildUpon()
    .setMaxAudioChannelCount(2)
    .build()
);

Notez que la modification des paramètres de sélection de piste en cours de lecture peut entraîner une interruption de la lecture. Pour en savoir plus sur l'ajustement des paramètres de sélection des pistes du lecteur, consultez la section Sélection des pistes de la documentation ExoPlayer.

Comportement de spatialisation par défaut

Le comportement de spatialisation par défaut dans Android inclut les comportements suivants, qui peuvent être personnalisés par les OEM :

  • Seul le contenu multicanal est spatialisé, et non le contenu stéréo. Si vous n'utilisez pas ExoPlayer, vous devrez peut-être configurer le nombre maximal de canaux pouvant être générés par un décodeur audio sur un grand nombre, en fonction du format de votre contenu audio multicanal. Cela garantit que le décodeur audio génère un PCM multicanal pour que la plate-forme puisse le spatialiser.

    Kotlin

    val mediaFormat = MediaFormat()
    mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99)

    Java

    MediaFormat mediaFormat = new MediaFormat();
    mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99);

    Pour voir un exemple en action, consultez MediaCodecAudioRenderer.java d'ExoPlayer. Pour désactiver vous-même la spatialisation, quelle que soit la personnalisation de l'OEM, consultez Désactiver le son spatial.

  • AudioAttributes : l'audio est éligible à la spatialisation si usage est défini sur USAGE_MEDIA ou USAGE_GAME.

  • AudioFormat : utilisez un masque de canal qui contient au moins les canaux AudioFormat.CHANNEL_OUT_QUAD (avant gauche, avant droit, arrière gauche et arrière droit) pour que l'audio puisse être spatialisé. Dans l'exemple ci-dessous, nous utilisons AudioFormat.CHANNEL_OUT_5POINT1 pour une piste audio 5.1. Pour une piste audio stéréo, utilisez AudioFormat.CHANNEL_OUT_STEREO.

    Si vous utilisez Media3, vous pouvez utiliser Util.getAudioTrackChannelConfig(int channelCount) pour convertir un nombre de canaux en masque de canal.

    De plus, définissez l'encodage sur AudioFormat.ENCODING_PCM_16BIT si vous avez configuré le décodeur pour qu'il génère un PCM multicanal.

    Kotlin

    val audioFormat = AudioFormat.Builder()
      .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
      .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
      .build()

    Java

    AudioFormat audioFormat = new AudioFormat.Builder()
      .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
      .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
      .build();

Tester le son spatial

Assurez-vous que le son spatial est activé sur votre appareil de test :

  • Pour les casques filaires, accédez à Paramètres système > Son et vibreur > Audio spatial.
  • Pour les casques sans fil, accédez à Paramètres système > Appareils connectés > Icône en forme d'engrenage pour votre appareil sans fil > Audio spatial.

Pour vérifier si le son spatial est disponible pour le routage actuel, exécutez la commande adb shell dumpsys audio sur votre appareil. Les paramètres suivants devraient s'afficher dans le résultat lorsque la lecture est active :

Spatial audio:
mHasSpatializerEffect:true (effect present)
isSpatializerEnabled:true (routing dependent)