Adicionar áudio espacial ao seu app de XR

Dispositivos XR relevantes
Estas orientações ajudam você a criar experiências para esses tipos de dispositivos XR.
Headsets XR
Óculos XR com fio

Os recursos de áudio espacial no Jetpack SceneCore permitem criar experiências de áudio imersivas nos aplicativos Android XR.

O áudio espacial simula como os usuários percebem o som em um ambiente 3D. Ele cria a sensação de que o som emana de todas as direções, incluindo acima e abaixo do usuário. O sistema faz isso simulando um ou mais "alto-falantes virtuais" em locais específicos no espaço 3D.

Os apps atuais que não foram projetados ou modificados para o Android XR têm o áudio espacializado automaticamente no Android XR. À medida que o usuário se move pelo espaço, todo o áudio do app será emitido pelo painel em que a interface do app é renderizada. Por exemplo, se um timer disparar em um app de relógio, o áudio vai soar como se estivesse vindo da posição do painel do app. O Android XR vai alterar automaticamente o som para realismo posicional. Por exemplo, a distância percebida entre o painel do app e o usuário vai afetar sutilmente o volume do áudio para uma maior sensação de realismo.

Para mais informações sobre como os apps atuais renderizam o áudio espacial, leia Adicionar som estéreo e surround ao app nesta página.

Se você estiver otimizando o app para XR, o Jetpack SceneCore vai fornecer ferramentas para personalização avançada de áudio espacial. É possível posicionar sons com precisão no ambiente 3D, usar áudio ambissônico para campos de som realistas e aproveitar a integração de som surround integrada.

Tipos de áudio espacial disponíveis no Android XR

O Android XR oferece suporte a áudio posicional, estéreo, surround e ambissônico.

Áudio posicional

Um áudio posicional pode ser posicionado para reprodução em um ponto específico no espaço 3D. Por exemplo, é possível ter um modelo 3D de um cachorro latindo no canto do seu ambiente virtual. Você pode ter várias entidades emitindo som de cada uma das posições. Para renderizar áudio posicional, os arquivos precisam ser mono ou estéreo.

Som estéreo e surround espacializado

Todos os formatos de mídia do Android são compatíveis com som posicional, estéreo e surround. Além desses formatos, os dispositivos Android XR podem oferecer suporte a formatos de áudio Dolby Atmos, Dolby Digital e Dolby Digital+.

O áudio estéreo se refere a formatos de áudio com dois canais, e o som surround se refere a formatos de áudio com mais de dois canais, como som surround 5.1 ou som surround 7.1 configurações. Os dados de som de cada canal estão associados a um alto-falante. Por exemplo, ao reproduzir música em estéreo, o canal do alto-falante esquerdo pode emitir faixas de instrumentos diferentes do direito.

O som surround é usado com frequência em filmes e programas de televisão para melhorar o realismo e a imersão usando vários canais de alto-falantes. Por exemplo, o diálogo geralmente é reproduzido em um canal de alto-falante central, enquanto o som de um helicóptero voando pode usar canais diferentes em sequência para dar a sensação de que o helicóptero está voando pelo espaço 3D.

Áudio ambissônico

O áudio ambissônico (ou ambissônicos) é como um skybox para áudio, oferecendo uma paisagem sonora imersiva para os usuários. Use ambissônicos para sons ambientais de fundo ou outros cenários em que você queira replicar um campo de som esférico completo que envolva o ouvinte. O Android XR oferece suporte ao AmbiX formato de áudio ambissônico em ambissônicos de primeira, segunda e terceira ordem. Recomendamos os tipos de arquivo Opus (.ogg) e PCM/Wave (.wav).

Usar áudio espacial com o Jetpack SceneCore

A implementação de áudio espacial com o Jetpack SceneCore envolve a verificação de recursos espaciais e a escolha de uma API para carregar áudio espacial.

Verificar recursos espaciais

Antes de usar os recursos de áudio espacial, verifique se a Session oferece suporte a áudio espacial. Em todos os snippets de código nas seções a seguir, os recursos são verificados antes de tentar reproduzir áudio espacializado.

Carregar áudio espacial

É possível usar qualquer uma das APIs a seguir para carregar áudio espacial para uso no Jetpack SceneCore.

  • SoundPool: ideal para efeitos sonoros curtos com menos de 1 MB de tamanho. Eles são carregados com antecedência e os sons podem ser usados repetidamente. Essa é uma ótima maneira de carregar áudio para áudio posicional.
  • ExoPlayer: ideal para carregar conteúdo estéreo e som surround, como música e vídeo. Também permite a reprodução de mídia em segundo plano.
  • MediaPlayer: oferece a maneira mais simples de carregar áudio ambissônico.
  • AudioTrack: oferece mais controle sobre como carregar dados de áudio. Permite gravar buffers de áudio diretamente ou se você sintetizou ou decodificou seus próprios arquivos de áudio.

Verificar o suporte ao formato de mídia

Alguns formatos de mídia são compatíveis com a plataforma Android. No entanto, um dispositivo Android XR específico pode oferecer suporte a outros formatos, como Dolby Atmos. Para consultar o suporte ao formato de mídia, use AudioCapabilities do 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.
}

A verificação desses recursos pode envolver o bloqueio de chamadas e não deve ser chamada na linha de execução principal.

Adicionar áudio posicional ao app

As fontes de som posicionais são definidas por PointSourceParams e uma associada Entity. A posição e a orientação da Entity determinam onde o PointSourceParams é renderizado no espaço 3D. Chame setPointSourceParams no SpatialMediaPlayer para definir o PointSourceParams e a entity associada em uma instância MediaPlayer.

Exemplo de áudio posicional

O exemplo a seguir carrega um arquivo de áudio de efeito sonoro em um pool de som e o reproduz na posição da 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
}

Principais pontos sobre o código

  • A primeira etapa é verificar se os recursos de áudio espacial estão disponíveis usando spatialCapabilities.
  • Definir o contentType como CONTENT_TYPE_SONIFICATION e o uso como USAGE_ASSISTANCE_SONIFICATION permite que o sistema trate esse arquivo de áudio como um efeito sonoro.
  • O exemplo anterior carrega o arquivo de áudio no pool imediatamente antes de usá-lo para manter o código junto para simplificar. O ideal é carregar todos os efeitos sonoros de forma assíncrona ao carregar o app para que todos os arquivos de áudio estejam disponíveis no pool quando necessário.

Adicionar som estéreo e surround ao app

A maneira recomendada de adicionar som estéreo e surround ao app é usando Exoplayer. Para mais informações sobre como usar o áudio espacial com Exoplayer, consulte o guia de áudio espacial.

Posicionamento de alto-falantes estéreo e som surround

Com o posicionamento de alto-falantes de som surround, os alto-falantes virtuais de som surround são posicionados e orientados em relação a um alto-falante central, ao redor do usuário em uma configuração ITU padrão.

Por padrão, o alto-falante do canal central é colocado na do app's mainPanelEntity. Isso inclui apps para dispositivos móveis que têm o áudio espacializado automaticamente pelo Android XR.

Para estéreo, o posicionamento do alto-falante é semelhante ao som surround, exceto que apenas os canais esquerdo e direito são posicionados nos lados esquerdo e direito do painel, respectivamente.

Se você tiver vários painéis e quiser escolher qual deles emite áudio ou se quiser que o áudio estéreo ou surround seja renderizado em relação a outra Entity, use o PointSourceAttributes para definir o local do canal central. Os canais restantes serão colocados conforme mencionado anteriormente. Nessas situações, você também precisa usar MediaPlayer.

À medida que o usuário se move pelo espaço, os alto-falantes virtuais estéreo e som surround vão se mover e se ajustar para garantir que os alto-falantes estejam sempre em uma posição ideal.

Se você tiver configurado o MediaPlayer ou ExoPlayer para continuar reproduzindo som estéreo ou som surround em segundo plano, o posicionamento do alto-falante virtual será alterado quando o app estiver em segundo plano. Como não há painel ou outro ponto no espaço para ancorar o som, o áudio espacial se move com o usuário (ou seja, ele é "bloqueado na cabeça").

Exemplo de som surround

O exemplo a seguir carrega um arquivo de áudio 5.1 usando MediaPlayer e define o canal central do arquivo como uma 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
}

Principais pontos sobre o código

Adicionar campos de som ambissônicos ao app

A maneira mais simples de reproduzir campos de som ambissônicos é carregar o arquivo com um MediaPlayer. Como o som ambissônico se aplica a toda a paisagem sonora, não é necessário especificar uma Entity para fornecer uma posição. Em vez disso, crie uma instância de SoundFieldAttributes com a ordem ambissônica apropriada especificando o número de canais.

Exemplo de ambissônicos

O exemplo a seguir reproduz um campo de som ambissônico usando 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
}

Principais pontos sobre o código

  • Assim como nos snippets anteriores, a primeira etapa é verificar se os recursos de áudio espacial estão disponíveis usando hasCapability().
  • O contentType e o uso são puramente informativos.
  • O AMBISONICS_ORDER_FIRST_ORDER sinaliza para o SceneCore que o arquivo de campo de som define quatro canais.