Agrega audio espacial a tu app de XR

Dispositivos de realidad extendida correspondientes
Esta guía te ayuda a crear experiencias para estos tipos de dispositivos de realidad extendida.
Visores de realidad extendida
Lentes de realidad extendida con cable

Las funciones de audio espacial en Jetpack SceneCore te permiten crear experiencias de audio envolventes en tus aplicaciones de Android XR.

El audio espacial simula cómo los usuarios perciben el sonido en un entorno 3D. Crea la sensación de que el sonido emana de todas las direcciones, incluso por encima y por debajo del usuario. El sistema lo hace simulando una o más "bocinas virtuales" en ubicaciones específicas en el espacio 3D.

Las apps existentes que no se diseñaron ni modificaron para Android XR tienen su audio espacializado automáticamente en Android XR. A medida que el usuario se mueve por su espacio, todo el audio de la app se emitirá desde el panel en el que se renderiza la IU de la app. Por ejemplo, si suena un temporizador de una app de reloj, el audio sonará como si proviniera de la posición del panel de la app. Android XR modificará automáticamente el sonido para lograr un realismo posicional. Por ejemplo, la distancia percibida entre el panel de la app y el usuario afectará sutilmente el volumen de audio para una mayor sensación de realismo.

Para obtener más información sobre cómo las apps existentes renderizan el audio espacial, lee Cómo agregar sonido estéreo y envolvente a tu app en esta página.

Si estás optimizando tu app para XR, Jetpack SceneCore proporciona herramientas para la personalización avanzada del audio espacial. Puedes posicionar sonidos con precisión en el entorno 3D, usar audio ambisónico para campos de sonido realistas y aprovechar la integración de sonido envolvente integrada.

Tipos de audio espacial disponibles en Android XR

Android XR admite audio posicional, estéreo, envolvente y ambisónico.

Audio posicional

Se puede posicionar un audio posicional para que se reproduzca desde un punto específico en el espacio 3D. Por ejemplo, puedes tener un modelo 3D de un perro ladrando en la esquina de tu entorno virtual. Puedes tener varias entidades que emitan sonido desde cada una de sus posiciones respectivas. Para renderizar audio posicional, los archivos deben ser mono o estéreo.

Sonido estéreo y envolvente espacializado

Se admiten todos los formatos multimedia de Android para audio posicional, estéreo y envolvente. Además de estos formatos, los dispositivos Android XR pueden admitir formatos de audio Dolby Atmos, Dolby Digital y Dolby Digital+.

El audio estéreo se refiere a formatos de audio con dos canales, y el sonido envolvente se refiere a formatos de audio con más de dos canales, como configuraciones de sonido envolvente 5.1 o 7.1. Los datos de sonido de cada canal están asociados con una bocina. Por ejemplo, cuando se reproduce música en estéreo, el canal de la bocina izquierda puede emitir pistas de instrumentos diferentes a las de la derecha.

El sonido envolvente se usa con frecuencia en películas y programas de televisión para mejorar el realismo y la inmersión mediante el uso de varios canales de bocinas. Por ejemplo, el diálogo suele reproducirse desde un canal de bocina central, mientras que el sonido de un helicóptero que vuela puede usar diferentes canales en secuencia para dar la sensación de que el helicóptero vuela alrededor de tu espacio 3D.

Audio ambisónico

El audioambisónico (o ambisonics) es como un skybox para el audio, que proporciona un paisaje sonoro envolvente para tus usuarios. Usa ambisonics para sonidos ambientales de fondo o en otras situaciones en las que quieras replicar un campo de sonido esférico completo que rodee al oyente. Android XR admite el formato de audio ambisónico AmbiX en ambisonics de primer, segundo y tercer orden. Recomendamos los tipos de archivo Opus (.ogg) y PCM/Wave (.wav).

Usa audio espacial con Jetpack SceneCore

La implementación de audio espacial con Jetpack SceneCore implica verificar las capacidades espaciales y elegir una API para cargar audio espacial.

Verifica las capacidades espaciales

Antes de usar las funciones de audio espacial, verifica que el Session admita audio espacial. En todos los fragmentos de código de las siguientes secciones, se verifican las capacidades antes de intentar reproducir audio espacializado.

Carga audio espacial

Puedes usar cualquiera de las siguientes APIs para cargar audio espacial para usar en Jetpack SceneCore.

  • SoundPool: Es ideal para efectos de sonido cortos que tienen un tamaño inferior a 1 MB. Se cargan con anticipación y los sonidos se pueden usar de forma repetida. Esta es una excelente manera de cargar audio para audio posicional.
  • ExoPlayer: Es ideal para cargar contenido estéreo y de sonido envolvente, como música y video. También permite la reproducción de contenido multimedia en segundo plano.
  • MediaPlayer: Proporciona la forma más sencilla de cargar audio ambisónico.
  • AudioTrack: Proporciona el mayor control sobre cómo cargar datos de audio. Permite escribir directamente búferes de audio o si sintetizaste o decodificaste tus propios archivos de audio.

Verifica la compatibilidad con formatos multimedia

La plataforma de Android admite algunos formatos multimedia. Sin embargo, un dispositivo Android XR en particular podría admitir formatos adicionales, como Dolby Atmos. Para consultar la compatibilidad con formatos multimedia, usa AudioCapabilities de ExoPlayer:

val audioCapabilities = AudioCapabilities.getCapabilities(
    context,
    androidx.media3.common.AudioAttributes.DEFAULT,
    null,
    emptyList()
)

if (audioCapabilities.supportsEncoding(C.ENCODING_AC3)) {
    // Device supports playback of the Dolby Digital media format.
}
if (audioCapabilities.supportsEncoding(C.ENCODING_E_AC3)) {
    // Device supports playback of the Dolby Digital Plus media format.
}
if (audioCapabilities.supportsEncoding(C.ENCODING_E_AC3_JOC)) {
    // Device supports playback of the Dolby Digital Plus with Dolby Atmos media format.
}

La verificación de estas capacidades puede implicar el bloqueo de llamadas y no debe llamarse en el subproceso principal.

Agrega audio posicional a tu app

Las fuentes de sonido posicionales se definen mediante PointSourceParams y una asociada Entity. La posición y la orientación de la Entity determinan dónde se renderiza PointSourceParams en el espacio 3D. Llama setPointSourceParams en el SpatialMediaPlayer para configurar PointSourceParams y su entity asociada en una instancia de MediaPlayer.

Ejemplo de audio posicional

En el siguiente ejemplo, se carga un archivo de audio de efecto de sonido en un grupo de sonidos y se reproduce en la posición de la Entity.

// Check spatial capabilities before using spatial audio
if (session.scene.spatialCapabilities.contains(SpatialCapability.SPATIAL_AUDIO)
) { // The session has spatial audio capabilities
    val maxVolume = 1F
    val lowPriority = 0
    val infiniteLoop = -1
    val normalSpeed = 1F

    val soundPool = SoundPool.Builder()
        .setAudioAttributes(
            AudioAttributes.Builder()
                .setContentType(CONTENT_TYPE_SONIFICATION)
                .setUsage(USAGE_ASSISTANCE_SONIFICATION)
                .build()
        )
        .build()

    val pointSource = PointSourceParams()

    val soundEffect = appContext.assets.openFd("sounds/tiger_16db.mp3")
    val pointSoundId = soundPool.load(soundEffect, lowPriority)

    soundPool.setOnLoadCompleteListener { soundPool, sampleId, status ->
        // wait for the sound file to be loaded into the soundPool
        if (status == 0) {
            SpatialSoundPool.play(
                session = session,
                entity = entity,
                soundPool = soundPool,
                soundID = pointSoundId,
                params = pointSource,
                volume = maxVolume,
                priority = lowPriority,
                loop = infiniteLoop,
                rate = normalSpeed
            )
        }
    }
} else {
    // The session does not have spatial audio capabilities
}

Puntos clave sobre el código

  • El primer paso es verificar si las capacidades de audio espacial están disponibles con spatialCapabilities.
  • Si configuras contentType en CONTENT_TYPE_SONIFICATION y el uso en USAGE_ASSISTANCE_SONIFICATION, el sistema tratará este archivo de audio como un efecto de sonido.
  • En el ejemplo anterior, se carga el archivo de audio en el grupo inmediatamente antes de usarlo para mantener el código unido por simplicidad. Lo ideal es que cargues todos los efectos de sonido de forma asíncrona a medida que cargas la app para que todos los archivos de audio estén disponibles en el grupo cuando los necesites.

Agrega sonido estéreo y envolvente a tu app

La forma recomendada de agregar sonido estéreo y envolvente a tu app es usar Exoplayer. Para obtener más información sobre cómo usar el audio espacial con Exoplayer, consulta la guía de audio espacial.

Posicionamiento de bocinas estéreo y de sonido envolvente

Con el posicionamiento de bocinas de sonido envolvente, las bocinas virtuales de sonido envolvente se posicionan y orientan en relación con una bocina central, alrededor del usuario en una configuración ITU estándar.

De forma predeterminada, la bocina del canal central se coloca en la app's mainPanelEntity. Esto incluye apps para dispositivos móviles que tienen su audio espacializado automáticamente por Android XR.

En el caso del sonido estéreo, la ubicación de las bocinas es similar al sonido envolvente, excepto que solo los canales izquierdo y derecho se posicionan en los lados izquierdo y derecho del panel, respectivamente.

Si tienes varios paneles y quieres elegir qué panel emite audio, o si quieres que el audio estéreo o envolvente se renderice en relación con otra Entity, puedes usar PointSourceAttributes para definir la ubicación del canal central. Los canales restantes se colocarán como se mencionó anteriormente. En estas situaciones, también debes usar MediaPlayer.

A medida que el usuario se mueve por su espacio, las bocinas virtuales estéreo y de sonido envolvente se moverán y ajustarán para garantizar que las bocinas estén siempre en una posición óptima.

Si configuraste el MediaPlayer o el ExoPlayer para que continúe reproduciendo sonido estéreo o envolvente en segundo plano, se modificará el posicionamiento de la bocina virtual cuando la app se ejecute en segundo plano. Como no hay ningún panel ni otro punto en el espacio para anclar el sonido, el audio espacial se mueve con el usuario (en otras palabras, está "bloqueado en la cabeza").

Ejemplo de sonido envolvente

En el siguiente ejemplo, se carga un archivo de audio 5.1 con MediaPlayer y se configura el canal central del archivo para que sea una Entity.

// Check spatial capabilities before using spatial audio
if (session.scene.spatialCapabilities.contains(SpatialCapability.SPATIAL_AUDIO)) {
    // The session has spatial audio capabilities

    val pointSourceAttributes = PointSourceParams()

    val mediaPlayer = MediaPlayer()

    val fivePointOneAudio = appContext.assets.openFd("sounds/aac_51.ogg")
    mediaPlayer.reset()
    mediaPlayer.setDataSource(fivePointOneAudio)

    val audioAttributes =
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()

    SpatialMediaPlayer.setPointSourceParams(
        session,
        mediaPlayer,
        pointSourceAttributes,
        entity = session.scene.mainPanelEntity,
    )

    mediaPlayer.setAudioAttributes(audioAttributes)
    mediaPlayer.prepare()
    mediaPlayer.start()
} else {
    // The session does not have spatial audio capabilities
}

Puntos clave sobre el código

Agrega campos de sonido ambisónico a tu app

La forma más sencilla de reproducir campos de sonido ambisónico es cargar el archivo con un MediaPlayer. Dado que el sonido ambisónico se aplica a todo el paisaje sonoro, no es necesario especificar una Entity para proporcionar una posición. En su lugar, crea una instancia de SoundFieldAttributes con el orden ambisónico adecuado que especifique la cantidad de canales.

Ejemplo de ambisonics

En el siguiente ejemplo, se reproduce un campo de sonido ambisónico con MediaPlayer.

// Check spatial capabilities before using spatial audio
if (session.scene.spatialCapabilities.contains(SpatialCapability.SPATIAL_AUDIO)) {
    // The session has spatial audio capabilities

    val soundFieldAttributes =
        SoundFieldAttributes(SpatializerConstants.AmbisonicsOrder.FIRST_ORDER)

    val mediaPlayer = MediaPlayer()

    val soundFieldAudio = appContext.assets.openFd("sounds/foa_basketball_16bit.wav")

    mediaPlayer.reset()
    mediaPlayer.setDataSource(soundFieldAudio)

    val audioAttributes =
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()

    SpatialMediaPlayer.setSoundFieldAttributes(
        session,
        mediaPlayer,
        soundFieldAttributes
    )

    mediaPlayer.setAudioAttributes(audioAttributes)
    mediaPlayer.prepare()
    mediaPlayer.start()
} else {
    // The session does not have spatial audio capabilities
}

Puntos clave sobre el código

  • Al igual que con los fragmentos anteriores, el primer paso es verificar si las capacidades de audio espacial están disponibles con hasCapability().
  • contentType y el uso son puramente informativos.
  • El AMBISONICS_ORDER_FIRST_ORDER indica a SceneCore que el archivo de campo de sonido define cuatro canales.