低遅延オーディオにより、ゲームの臨場感と応答性が向上します。
Android のゲームで低遅延オーディオを有効にするには、次のチェックリストの項目をすべて実施する必要があります。
- Oboe を使用する
- パフォーマンス モード「低遅延」をリクエストする
- 共有モード「排他」をリクエストする
- 48,000 Hz を使用するか、Oboe のサンプルレート コンバータを使用する
- 用途を AAUDIO_USAGE_GAME に設定する
- データ コールバックを使用する
- コールバック内でブロッキングしない
- バッファサイズを「ダブルバッファ」になるように調整する
1. Oboe API を使用する
Oboe API は、Android 8.1(API レベル 27)以降で AAudio を呼び出す C++ ラッパーです。それより前のバージョンの Android の場合、Oboe は OpenSL ES を使用します。
GitHub で入手するか、ビルド済みバイナリとして利用できます。Oboe は、特定のデバイスの問題を修正する QuirksManager も備えているため、アプリが多くのデバイスに対応できます。Oboe を使用できない場合は、AAudio を直接使用してください。
2. 低遅延モードをリクエストする
Oboe または AAudio で低遅延モードをリクエストします。リクエストしなかった場合は、デフォルトで遅延の大きなモードになります。
Oboe
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
AAudio
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
3. 排他モードをリクエストする
MMAP バッファへの排他アクセスをリクエストすることもできます。排他アクセスを取得しないことも可能ですが、取得すれば DSP が読み取るバッファに直接書き込むことになり、遅延を可能な限り少なくできます。
Oboe
builder.setSharingMode(oboe::SharingMode::Exclusive);
AAudio
AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
4. サンプルレート変換を回避する
デバイスにとって自然なサンプルレートを使用します。そのため、サンプリング レートは指定しません。そうすることで、ほぼ確実に 48,000 Hz になります。サンプルレートを指定すると、オーディオ フレームワークがデータを別のパスで送信するため、遅延が大幅に大きくなる可能性があります。
これ以外のサンプルレートを使用する必要がある場合は、次のように Oboe を使用してサンプルレート変換を行います。
builder->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium);
5. 用途を適切に宣言する
オーディオを再生する理由を指定することは、適切なルーティング、音量、パフォーマンスの設定を適用するうえで非常に重要です。たとえば、遅延最適化機能を最大限に活用できるように、用途として AAUDIO_USAGE_GAME
を宣言します。Bluetooth ヘッドセットに接続している場合は特に必要です。
Oboe
builder.setUsage(oboe::Usage::Game);
AAudio
AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_GAME);
6. コールバック関数を使用する
出力ストリームにコールバックを使用します。AAudio MMAP モードをサポートしていないデバイスでブロッキング書き込みを使用すると、遅延が大幅に大きくなる可能性があります。
Oboe
builder.setDataCallback(&myCallbackObject);
AAudio
AAudioStreamBuilder_setDataCallback(builder, &my_callback_proc);
7. コールバック内でブロッキングしない
低遅延ストリームを使用すると、コールバック同士の間隔が非常に短くなります(数ミリ秒程度)。そのため、長時間ブロックされる可能性のあるオペレーションをコールバック内で行わないことが非常に重要になります。コールバックがブロックされると、バッファ アンダーフローとグリッチが発生します。
コールバック内で避けるべきことは、次のとおりです。
- メモリの割り当てと解放
- ファイル I/O とネットワーク I/O
- ミューテックスまたはロックでの待機
- スリープ
- CPU による一回での負荷の高い計算
グリッチなしでスムーズに再生するために、コールバックでの計算は一定のペースで行う必要があります。
8. バッファサイズを調整する
音声ストリームを開いたら、遅延を最適化するために使用可能なバッファサイズを調整する必要があります。Oboe では、自動的に 2 バーストに設定されますが、AAudio のデフォルトは、はるかに大きなサイズです。バッファサイズをバーストサイズの 2 倍に設定して、ダブル バッファリングを使用してください。バーストサイズが最大コールバック サイズとなります。
AAudio:
int32_t frames = AAudioStream_getFramesPerBurst() * 2;
AAudioStream_setBufferSizeInFrames(stream, frames);
バッファサイズが小さすぎると、バッファ アンダーランによってグリッチが発生する可能性があります。グリッチ数は AAudioStream_getXRunCount(stream)
を呼び出すことで取得できます。必要に応じて、バッファサイズを増やしてください。
詳しくは、 GitHub の Oboe ドキュメント を参照してください。
OpenSL ES
8.1 より前のバージョンの Android をサポートする場合は、OpenSL ES を使用する必要があります。Oboe を使用する場合は、遅延を改善するようにアプリを設定できます。詳しくは、 最適なレイテンシの取得 ご覧ください。
チェックリストの結果
次の表に、OboeTester によるラウンドトリップ(入力から出力まで)の遅延の測定結果を示します。
設定 | 遅延(ミリ秒) |
---|---|
推奨事項をすべて実施 | 20 |
パフォーマンス モードが低遅延でない | 205 |
排他でない(共有) | 26 |
44,100 Hz(AAudio) | 160 |
44,100 Hz(Oboe SRC) | 23 |
出力コールバックを使用しない(MMAP) | 21 |
出力コールバックを使用しない(非 MMAP) | 62 |
バッファサイズを最大に設定 | 53 |