Low latency audio makes games feel more realistic and responsive.
Complete the following checklist to enable low latency audio in your game on Android:
- Use Oboe
- Request performance mode "low latency"
- Request sharing mode "exclusive"
- Use 48000 Hz or the Oboe sample rate converter
- Set usage to AAUDIO_USAGE_GAME
- Use data callbacks
- Avoid blocking operations in the callback
- 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.
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.
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:
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
AAUDIO_USAGE_GAME to take full advantage of latency
optimizations, especially when connected to Bluetooth headsets.
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.
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
- 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.
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.
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.
The following table contains OboeTester measurements of round-trip (input to output) latency.
|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|