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](https://developer.android.com/games/sdk/oboe/low-latency-audio#oboe_api)
2. [Request performance mode "low latency"](https://developer.android.com/games/sdk/oboe/low-latency-audio#low_latency_mode)
3. [Request sharing mode "exclusive"](https://developer.android.com/games/sdk/oboe/low-latency-audio#exclusive_mode)
4. [Use 48000 Hz or the Oboe sample rate converter](https://developer.android.com/games/sdk/oboe/low-latency-audio#sample_rate_conversion)
5. [Set usage to AAUDIO_USAGE_GAME](https://developer.android.com/games/sdk/oboe/low-latency-audio#use_case)
6. [Use data callbacks](https://developer.android.com/games/sdk/oboe/low-latency-audio#callback_function)
7. [Avoid blocking operations in the callback](https://developer.android.com/games/sdk/oboe/low-latency-audio#avoid_blocking)
8. [Tune buffer size to "double buffer"](https://developer.android.com/games/sdk/oboe/low-latency-audio#tune_buffer_size)

## 1. Use the Oboe API

The [Oboe](https://github.com/google/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](https://github.com/google/oboe) or as
a [prebuilt binary](https://github.com/google/oboe/blob/main/docs/GettingStarted.md#option-1-using-pre-built-binaries-and-headers).
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

```css+lasso
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
```

### AAudio

```scdoc
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

```css+lasso
builder.setSharingMode(oboe::SharingMode::Exclusive);
```

### AAudio

```scdoc
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

```css+lasso
builder.setUsage(oboe::Usage::Game);
```

### AAudio

```scdoc
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

```text
builder.setDataCallback(&myCallbackObject);
```

### AAudio

```scdoc
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](https://github.com/google/oboe/wiki/TechNote_BufferTerminology)
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](https://github.com/google/oboe/blob/main/docs/GettingStarted.md#obtaining-optimal-latency)
in the GitHub docs.

## Checklist results

The following table contains
[OboeTester](https://play.google.com/store/apps/details?id=com.mobileer.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           |

| **Note:** Results can vary greatly between different devices.