Audio de baja latencia

El audio de baja latencia hace que los juegos se sientan más realistas y responsivos.

Completa la siguiente lista de tareas para habilitar el audio de baja latencia en tu juego para Android:

  1. Usa Oboe.
  2. Solicita el modo de rendimiento de "latencia baja".
  3. Solicita el modo de uso compartido "exclusivo".
  4. Usa el convertidor de tasa de muestreo de Oboe o de 48,000 Hz.
  5. Establece el uso en AAUDIO_USAGE_GAME.
  6. Usa devoluciones de llamada de datos.
  7. Evita bloquear operaciones en la devolución de llamada.
  8. Ajusta el tamaño del búfer a "búfer doble".

1. Usa la API de Oboe

La API de Oboe es un wrapper de C++ que llama a AAudio en Android 8.1 (nivel de API 27) o versiones posteriores. En versiones anteriores, Oboe usa OpenSL ES.

Oboe está disponible en GitHub o como objeto binario precompilado. Oboe también tiene un QuirksManager que corrige problemas en dispositivos específicos, lo que hace que tu app sea compatible con más dispositivos. Si no puedes usar Oboe, usa AAudio directamente.

2. Solicita el modo de baja latencia

Con Oboe o AAudio, solicita el modo de baja latencia. De lo contrario, obtendrás un modo de latencia más alta de forma predeterminada.

Oboe

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

AAudio

AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

3. Solicita el modo exclusivo

También puedes solicitar acceso exclusivo al búfer MMAP. Es posible que tu app no obtenga acceso exclusivo; pero, si lo hace, escribirá directamente en un búfer que lee la DSP, lo que le brinda la latencia más baja posible.

Oboe

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

AAudio

AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);

4. Evita la conversión de la tasa de muestreo

Usa la tasa de muestreo natural del dispositivo. Para hacerlo, no especifiques una tasa de muestreo, y lo más probable es que obtengas 48,000 Hz. Si especificas una tasa de muestreo, el framework de audio enviará tus datos a una ruta diferente que puede tener una latencia mucho mayor.

Si necesitas usar una tasa de muestreo diferente, utiliza Oboe para realizar la conversión de tasa de muestreo:

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

5. Declara correctamente tu caso de uso

Especificar el motivo por el que tu app reproduce audio es fundamental para que el sistema aplique la configuración correcta de enrutamiento, volumen y rendimiento. Por ejemplo, los juegos deben indicar el uso AAUDIO_USAGE_GAME para aprovechar al máximo las optimizaciones de latencia, en especial cuando se conectan auriculares Bluetooth.

Oboe

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

AAudio

AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_GAME);

6. Usa una función de devolución de llamada

Usa una devolución de llamada para la transmisión de salida. Si usas un dispositivo que no admite el modo MMAP de AAudio y usas un dispositivo que bloquea las operaciones de escritura, es posible que la latencia sea mucho mayor.

Oboe

builder.setDataCallback(&myCallbackObject);

AAudio

AAudioStreamBuilder_setDataCallback(builder, &my_callback_proc);

7. Evita bloqueos en la devolución de llamada

Cuando usas una transmisión de latencia baja, el tiempo entre devoluciones de llamada puede ser muy corto, de solo unos milisegundos. Por lo tanto, es muy importante que no hagas nada en la devolución de llamada que pueda causar un bloqueo muy largo. Si se bloquea la devolución de llamada, se producen fallas y desbordamientos del búfer en el audio.

Evita hacer lo siguiente en una devolución de llamada:

  • Asignar o liberar memoria
  • Archivar o conectar E/S
  • Esperar una exclusión mutua o un bloqueo
  • Suspender
  • Realizar cálculos complejos de CPU de única vez

Las devoluciones de llamada deben realizar cálculos matemáticos a un ritmo uniforme para lograr una reproducción fluida sin fallas.

8. Ajusta el tamaño del búfer

Una vez que la app abra la transmisión de audio, deberás ajustar el tamaño del búfer utilizable para lograr una latencia óptima. Oboe establece automáticamente el tamaño del búfer en dos aumentos de actividad. Sin embargo, con AAudio, la configuración predeterminada es mucho más alta. Usa el almacenamiento en búfer doble estableciendo el tamaño del búfer en el doble del tamaño del aumento. El tamaño del aumento es el tamaño máximo de la devolución de llamada.

AAudio:

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

Si el tamaño del búfer es demasiado pequeño, es posible que experimentes fallas ocasionadas por subdesbordamiento. Para obtener un recuento de los errores, llama a AAudioStream_getXRunCount(stream). Aumenta el tamaño del búfer según sea necesario.

Consulta los documentos de Oboe de GitHub para obtener una explicación de la terminología relacionada con los búferes.

OpenSL ES

Si admites versiones de Android anteriores a la 8.1, debes usar OpenSL ES. Si usas Oboe, puedes configurar tu app para mejorar la latencia. Consulta Cómo obtener una latencia óptima en los documentos de GitHub.

Resultados de la lista de tareas

La siguiente tabla contiene las mediciones de OboeTester para latencia de ida y vuelta (entrada a salida).

Configuración Latencia (ms)
Sigue todas las recomendaciones. 20
Modo de rendimiento con latencia baja 205
No EXCLUSIVO (COMPARTIDO) 26
44,100 Hz (AAudio) 160
44,100 Hz (Oboe SRC) 23
No se está usando una devolución de llamada de salida (MMAP) 21
No se está usando una devolución de llamada de salida (no MMAP) 62
Tamaño del búfer establecido al máximo 53