Low latency audio

Low latency audio makes games feel more realistic and responsive.

Complete the following checklist to enable low latency audio in your game on Android:

  1. Use Oboe
  2. Request performance mode "low latency"
  3. Request sharing mode "exclusive"
  4. Use 48000 Hz or the Oboe sample rate converter
  5. Set usage to AAUDIO_USAGE_GAME
  6. Use data callbacks
  7. Avoid blocking operations in the callback
  8. Tune buffer size to "double buffer"

1. Use the Oboe API

The Oboe API is a C++ wrapper that calls AAudio on Android 8.1 (API level 27) or higher. On earlier Android versions, Oboe uses OpenSL ES.

Oboe is available on GitHub or as a prebuilt binary. Oboe also has a QuirksManager that corrects for problems on specific devices, which makes your app compatible with more devices. If you cannot use Oboe, use AAudio directly.

2. Request low latency mode

With Oboe or AAudio, request low latency mode. Otherwise, you get a higher latency mode by default.

Oboe

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

AAudio

AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

3. Request exclusive mode

You can also request exclusive access to the MMAP buffer. Your app may not get exclusive access, but if it does, your app writes directly into a buffer that is read by the DSP, which gives your app the lowest possible latency.

Oboe

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

AAudio

AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);

4. Avoid sample rate conversion

Use the natural sample rate of the device. You can do this by not specifying a sample rate, and you almost certainly get 48000 Hz. If you do specify a sample rate, the audio framework sends your data on a different path that can have much higher latency.

If you do need to use a different sample rate, use Oboe to do the sample rate conversion:

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

5. Properly declare your use case

Specifying the reason your app plays audio is critical for the system to apply the right routing, volume, and performance settings. For example, games should indicate usage AAUDIO_USAGE_GAME to take full advantage of latency optimizations, especially when connected to Bluetooth headsets.

Oboe

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

AAudio

AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_GAME);

6. Use a callback function

Use a callback for the output stream. If you use blocking writes and you are on a device that does not support the AAudio MMAP mode, latency might be much higher.

Oboe

builder.setDataCallback(&myCallbackObject);

AAudio

AAudioStreamBuilder_setDataCallback(builder, &my_callback_proc);

7. Avoid blocking in the callback

When you use a low latency stream, the time between callbacks can be very short, just a few milliseconds. So it is very important that you don't do anything in the callback that could block for a long time. If the callback is blocked, the buffer underflows and glitches occur in the audio.

Avoid doing the following in a callback:

  • Allocating or freeing memory
  • File or network I/O
  • Waiting on a mutex or lock
  • Sleep
  • Heavy one-time CPU calculations

The callbacks should do math at an even pace for smooth playback without glitches.

8. Tune the buffer size

Once your app opens the audio stream, you need to tune the usable buffer size for optimal latency. Oboe automatically sets the buffer size to two bursts. But with AAudio, the default is much higher. Use double buffering by setting the buffer size to twice the burst size. The burst size is the maximum callback size.

AAudio:

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

If the buffer size is too small, you might get glitches caused by buffer underruns. You can get a count of the glitches by calling AAudioStream_getXRunCount(stream). Increase the buffer size as needed.

See the GitHub Oboe docs for an explanation of buffer‑related terminology.

OpenSL ES

If you are supporting versions of Android before 8.1, you have to use OpenSL ES. If you are using Oboe, you can configure your app to improve the latency. See Obtaining optimal latency in the GitHub docs.

Checklist results

The following table contains OboeTester measurements of round-trip (input to output) latency.

Configuration Latency (ms)
Follow all recommendations 20
Performance mode not low latency 205
Not EXCLUSIVE (SHARED) 26
44100 Hz (AAudio) 160
44100 Hz (Oboe SRC) 23
Not using an output callback (MMAP) 21
Not using an output callback (not MMAP) 62
Buffer size set to maximum 53