Áudio espacial

O áudio espacial é uma experiência de áudio imersiva que coloca os usuários no centro da ação, tornando o som do conteúdo mais realista. O som é "espacializado" para criar um efeito de vários alto-falantes, semelhante a uma configuração de som surround, mas com fones de ouvido.

Por exemplo, em um filme, o som de um carro pode começar atrás do usuário, avançar e desaparecer ao longe. Em um chat por vídeo, as vozes podem ser separadas e posicionadas ao redor do usuário, facilitando a identificação de quem está falando.

Se o conteúdo usa um formato de áudio compatível, é possível adicionar áudio espacial ao app a partir do Android 13 (nível 33 da API).

Consultar recursos

Use a classe Spatializer para consultar os recursos e o comportamento de espacialização do dispositivo. Comece recuperando uma instância do Spatializer do AudioManager:

Kotlin

val spatializer = audioManager.spatializer

Java

Spatializer spatializer = AudioManager.getSpatializer();

Depois de receber o Spatializer, verifique as quatro condições que precisam ser verdadeiras para que o dispositivo emita áudio espacializado:

Critérios Cheque
O dispositivo é compatível com espacialização? getImmersiveAudioLevel() não é SPATIALIZER_IMMERSIVE_LEVEL_NONE
A espacialização está disponível?
A disponibilidade depende da compatibilidade com o roteamento de saída de áudio atual.
isAvailable() é true
A espacialização está ativada? isEnabled() é true
Uma faixa de áudio com os parâmetros fornecidos pode ser espacializada? canBeSpatialized() é true

Essas condições podem não ser atendidas, por exemplo, se a espacialização estiver indisponível para a faixa de áudio atual ou desativada no dispositivo de saída de áudio.

Rastreamento da Cabeça

Com fones de ouvido compatíveis, a plataforma pode ajustar a espacialização do áudio com base na posição da cabeça do usuário. Para verificar se um rastreamento da cabeça está disponível para o roteamento de saída de áudio atual, chame isHeadTrackerAvailable().

Conteúdo compatível

Spatializer.canBeSpatialized() indica se o áudio com as propriedades fornecidas pode ser espacializado com o roteamento atual do dispositivo de saída. Esse método usa um AudioAttributes e um AudioFormat, ambos descritos em mais detalhes abaixo.

AudioAttributes

Um objeto AudioAttributes descreve o uso de um stream de áudio (por exemplo, áudio de jogo ou mídia padrão), além dos comportamentos de reprodução e do tipo de conteúdo.

Ao chamar canBeSpatialized(), use a mesma instância de AudioAttributes definida para o Player. Por exemplo, se você estiver usando a biblioteca Jetpack Media3 e não tiver personalizado o AudioAttributes, use AudioAttributes.DEFAULT.

Desativar o Áudio Espacial

Para indicar que seu conteúdo já foi espacializado, chame setIsContentSpatialized(true) para que o áudio não seja processado duas vezes. Como alternativa, ajuste o comportamento de espacialização para desativar totalmente a espacialização chamando setSpatializationBehavior(AudioAttributes.SPATIALIZATION_BEHAVIOR_NEVER).

AudioFormat

Um objeto AudioFormat descreve detalhes sobre o formato e a configuração de canal de uma faixa de áudio.

Ao instanciar o AudioFormat para transmitir para canBeSpatialized(), defina a codificação como o mesmo formato de saída esperado do decodificador. Você também precisa definir uma máscara de canal que corresponda à configuração de canal do seu conteúdo. Consulte a seção Comportamento de espacialização padrão para orientações sobre valores específicos a serem usados.

Detectar mudanças no Spatializer

Para detectar mudanças no estado do Spatializer, adicione um listener com Spatializer.addOnSpatializerStateChangedListener(). Da mesma forma, para detectar mudanças na disponibilidade de um rastreamento da cabeça, chame Spatializer.addOnHeadTrackerAvailableListener().

Isso pode ser útil se você quiser ajustar a seleção de faixas durante a reprodução usando os callbacks do listener. Por exemplo, quando um usuário conecta ou desconecta o fone de ouvido do dispositivo, o callback onSpatializerAvailableChanged indica se o efeito do espacializador está disponível para o novo roteamento de saída de áudio. Nesse ponto, você pode atualizar a lógica de seleção de faixas do player para corresponder aos novos recursos do dispositivo. Para mais detalhes sobre o comportamento de seleção de faixas do ExoPlayer, consulte a seção ExoPlayer e áudio espacial.

ExoPlayer e áudio espacial

As versões recentes do ExoPlayer facilitam a adoção do áudio espacial. Se você usa a biblioteca independente do ExoPlayer (nome do pacote com.google.android.exoplayer2), a versão 2.17 configura a plataforma para emitir áudio espacializado, e a versão 2.18 apresenta restrições de contagem de canais de áudio. Se você usa o módulo ExoPlayer da biblioteca Media3 (nome do pacote androidx.media3), as versões 1.0.0-beta01 e mais recentes incluem essas mesmas atualizações.

Depois de atualizar a dependência do ExoPlayer para a versão mais recente, o app só precisa incluir conteúdo que possa ser espacializado.

Restrições de contagem de canais de áudio

Quando as quatro condições para o áudio espacial são atendidas, o ExoPlayer escolhe uma faixa de áudio multicanal. Caso contrário, o ExoPlayer escolhe uma faixa estéreo. Se as propriedades de Spatializer mudarem, o ExoPlayer vai acionar uma nova seleção de faixa para escolher uma faixa de áudio que corresponda às propriedades atuais. Essa nova seleção de faixa pode causar um curto período de armazenamento em buffer.

Para desativar as restrições de contagem de canais de áudio, defina os parâmetros de seleção de faixa no player, conforme mostrado abaixo:

Kotlin

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

Java

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

Da mesma forma, é possível atualizar os parâmetros de um seletor de faixa para desativar as restrições de contagem de canais de áudio da seguinte maneira:

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()
);

Com as restrições de contagem de canais de áudio desativadas, se o conteúdo tiver várias faixas de áudio, o ExoPlayer vai selecionar inicialmente a faixa com o maior número de canais e que pode ser reproduzida no dispositivo. Por exemplo, se o conteúdo tiver uma faixa de áudio multicanal e uma estéreo, e o dispositivo for compatível com a reprodução de ambas, o ExoPlayer vai selecionar a faixa multicanal. Consulte a Seleção de faixa de áudio para saber como personalizar esse comportamento.

Seleção de faixa de áudio

Quando o comportamento de restrições de contagem de canais de áudio do ExoPlayer está desativado, ele não seleciona automaticamente uma faixa de áudio que corresponda às propriedades do espacializador do dispositivo. Em vez disso, é possível personalizar a lógica de seleção de faixas do ExoPlayer definindo parâmetros de seleção antes ou durante a reprodução. Por padrão, o ExoPlayer seleciona faixas de áudio iguais à faixa inicial em relação ao tipo MIME (codificação), à contagem de canais e à taxa de amostragem.

Como mudar os parâmetros de seleção de faixa

Para mudar os parâmetros de seleção de faixa do ExoPlayer, use Player.setTrackSelectionParameters(). Da mesma forma, é possível acessar os parâmetros atuais do ExoPlayer com Player.getTrackSelectionParameters(). Por exemplo, para selecionar uma faixa de áudio estéreo durante a reprodução:

Kotlin

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

Java

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

Mudar os parâmetros de seleção de faixa durante a reprodução pode causar uma interrupção. Para mais informações sobre como ajustar os parâmetros de seleção de faixa do player, consulte a seção seleção de faixa da documentação do ExoPlayer.

Comportamento de espacialização padrão

O comportamento de espacialização padrão no Android inclui os seguintes comportamentos, que podem ser personalizados pelos OEMs:

  • Somente o conteúdo multicanal é espacializado, não o conteúdo estéreo. Se você não usa o ExoPlayer, dependendo do formato do conteúdo de áudio multicanal, talvez seja necessário configurar o número máximo de canais que podem ser emitidos por um decodificador de áudio para um número grande. Isso garante que o decodificador de áudio gere PCM multicanal para a plataforma espacializar.

    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);

    Para ver um exemplo em ação, consulte o MediaCodecAudioRenderer.java do ExoPlayer. Para desativar a espacialização por conta própria, independentemente da personalização do OEM, consulte Desativar o áudio espacial.

  • AudioAttributes: o áudio será qualificado para espacialização se o usage estiver definido como USAGE_MEDIA ou USAGE_GAME.

  • AudioFormat: use uma máscara de canal que contenha pelo menos os canais AudioFormat.CHANNEL_OUT_QUAD (frontal esquerdo, frontal direito, traseiro esquerdo e traseiro direito) para que o áudio seja qualificado para espacialização. No exemplo abaixo, usamos AudioFormat.CHANNEL_OUT_5POINT1 para uma faixa de áudio 5.1. Para uma faixa de áudio estéreo, use AudioFormat.CHANNEL_OUT_STEREO.

    Se você estiver usando o Media3, use Util.getAudioTrackChannelConfig(int channelCount) para converter uma contagem de canais em uma máscara de canal.

    Além disso, defina a codificação como AudioFormat.ENCODING_PCM_16BIT se você tiver configurado o decodificador para gerar 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();

Testar o Áudio Espacial

Verifique se o Áudio Espacial está ativado no dispositivo de teste:

  • Para fones de ouvido com fio, acesse Configurações do sistema > Som e vibração > Áudio espacial.
  • Para fones de ouvido sem fio, acesse Configurações do sistema > Dispositivos conectados > ícone de engrenagem do dispositivo sem fio > Áudio espacial.

Para verificar a disponibilidade do Áudio Espacial para o roteamento atual, execute o comando adb shell dumpsys audio no dispositivo. Os seguintes parâmetros vão aparecer na saída enquanto a reprodução estiver ativa:

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