Áudio de baixa latência

O áudio de baixa latência deixa os jogos mais realistas e responsivos.

Conclua a checklist a seguir para ativar o áudio de baixa latência no seu jogo no Android:

  1. Usar a Oboe
  2. Solicitar o modo performance "baixa latência"
  3. Solicitar o modo de compartilhamento "exclusivo"
  4. Usar 48.000 Hz ou o conversor de taxa de amostragem da API Oboe
  5. Declarar o uso como AAUDIO_USAGE_GAME
  6. Usar callbacks de dados
  7. Evitar o bloqueio de operações no callback
  8. Ajustar o tamanho para "buffer duplo"

1. Usar a API Oboe

A API Oboe é um wrapper C++ que chama AAudio no Android 8.1 (nível 27 da API) ou versões mais recentes. Em versões anteriores do Android, a Oboe usa o OpenSL ES.

A Oboe está disponível no GitHub ou como um binário pré-criado (links em inglês). Essa API também tem um QuirksManager que corrige problemas em dispositivos específicos, o que torna seu app compatível com mais dispositivos. Se não for possível usar a API Oboe, use a AAudio diretamente.

2. Solicitar o modo de baixa latência

Solicite-o com a Oboe ou a AAudio. Caso contrário, você terá um modo de latência maior por padrão.

Oboe

builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);

AAudio

AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

3. Solicitar o modo exclusivo

Também é possível solicitar acesso exclusivo ao buffer MMAP. Seu app pode não ter acesso exclusivo, mas se isso acontecer, ele vai gravar dados diretamente em um buffer que é lido pelo DSP, o que proporciona a menor latência possível.

Oboe

builder.setSharingMode(oboe::SharingMode::Exclusive);

AAudio

AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);

4. Evitar a conversão da taxa de amostragem

Use a taxa de amostragem normal do dispositivo. Para fazer isso, não especifique uma taxa de amostragem para ter uma taxa de 48.000 Hz. Se você fizer essa especificação, o framework de áudio vai enviar seus dados por um caminho diferente que poderá ter uma latência muito maior.

Se você precisar usar uma taxa, use a Oboe para fazer a conversão dela:

builder->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium);

5. Declarar seu caso de uso corretamente

Especifique o motivo pelo qual seu app reproduz áudio para que o sistema aplique as configurações corretas de roteamento, volume e desempenho. Por exemplo, jogos precisam indicar o uso de AAUDIO_USAGE_GAME para aproveitar as otimizações de latência ao máximo, especialmente quando conectados a fones de ouvido Bluetooth.

Oboe

builder.setUsage(oboe::Usage::Game);

AAudio

AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_GAME);

6. Usar uma função de callback

Use um callback para o stream de saída. Se você usa gravações de bloqueio e está usando um dispositivo que não oferece suporte ao modo MMAP da AAudio, a latência pode ser muito maior.

Oboe

builder.setDataCallback(&myCallbackObject);

AAudio

AAudioStreamBuilder_setDataCallback(builder, &my_callback_proc);

7. Evitar bloquear o callback

Quando você usa streams de baixa latência, o tempo entre os callbacks pode ser de apenas alguns milissegundos. Então, é muito importante que você não faça nada que no callback possa bloqueá-lo por muito tempo. Se o callback for bloqueado, vão ocorrer e falhas do buffer no áudio.

Evite realizar as seguintes ações em um callback:

  • Alocar ou liberar memória
  • E/S de arquivo ou rede
  • Aguardar um mutex ou um bloqueio
  • Suspensão
  • Cálculos complexos de CPU única

Os callbacks precisam fazer cálculos em um ritmo constante para garantir uma reprodução suave sem falhas.

8. Ajustar o tamanho do buffer

Quando seu app abrir o stream de áudio, será necessário ajustar o tamanho do buffer utilizável para ter a latência ideal. A Oboe define o tamanho do buffer como dois bursts automaticamente. No entanto, o padrão é muito mais alto na AAudio. Para usar o buffer duplo, defina o tamanho do buffer como o dobro do burst. O tamanho do burst é igual ao tamanho máximo do callback.

AAudio:

int32_t frames = AAudioStream_getFramesPerBurst() * 2;
AAudioStream_setBufferSizeInFrames(stream, frames);

Se o tamanho do buffer for muito pequeno, você poderá receber falhas causadas por underruns de buffer. Chame AAudioStream_getXRunCount(stream) para receber uma contagem das falhas. Aumente o tamanho do buffer conforme necessário.

Consulte os documentos do GitHub sobre a Oboe (em inglês) para ver uma explicação da terminologia relacionada ao buffer.

OpenSL ES

Se você oferecer suporte a versões do Android anteriores à 8.1, será necessário usar a OpenSL ES. Se você estiver usando a Oboe, poderá configurar seu app para melhorar a latência. Consulte Como conseguir a latência ideal nos documentos do GitHub.

Resultados da checklist

A tabela a seguir contém medidas de latência do OboeTester (app disponível em inglês) de ida e volta (de entrada para saída).

Configuração Latência (ms)
Siga todas as recomendações 20
Modo performance não é baixa latência 205
Não EXCLUSIVO (COMPARTILHADO) 26
44.100 Hz (AAudio) 160
44.100 Hz (Oboe SRC) 23
Não usar um callback de saída (MMAP) 21
Não usar um callback de saída (não MMAP) 62
Tamanho do buffer definido para máximo 53