Android 拡張機能

OpenSL ES for Android は、OpenSL ES リファレンス仕様を拡張することで、Android と互換性を持たせ、Android プラットフォームの機能と柔軟性を活用できるようにします。

Android 拡張機能の API の定義は、OpenSLES_Android.h と、このヘッダーに含まれているヘッダー ファイル内にあります。これらの拡張機能の詳細については、OpenSLES_Android.h をご覧ください。このファイルは、インストール ルートの sysroot/usr/include/SLES ディレクトリにあります。特に記載のない限り、すべてのインターフェースは明示的です。

これらの拡張機能は Android 固有のものなので、他の OpenSL ES 実装へのアプリの移植性は制限されます。この問題を緩和するには、拡張機能を使用しないようにするか、#ifdef を使用してコンパイル時に拡張機能を除外します。

次の表は、Android OpenSL ES がオブジェクト タイプごとにサポートしている Android 固有のインターフェースとデータロケータを示しています。セル内の「〇」は、各オブジェクト タイプでインターフェースとデータロケータを使用できることを示します。

機能 オーディオ プレーヤー オーディオ レコーダー エンジン 出力ミックス
Android バッファキュー 〇: ソース(デコード) × × ×
Android 構成 × ×
Android エフェクト × ×
Android エフェクト機能 × × ×
Android エフェクト センド × × ×
Android シンプル バッファキュー 〇: ソース(再生)またはシンク(デコード) × ×
Android バッファキュー データロケータ 〇: ソース(デコード) × × ×
Android ファイル ディスクリプタ データロケータ 〇: ソース × × ×
Android シンプル バッファキュー データロケータ 〇: ソース(再生)またはシンク(デコード) 〇: シンク × ×

Android 構成インターフェース

Android 構成インターフェースは、オブジェクトに対してプラットフォーム固有のパラメータを設定できるようにします。このインターフェースは、対応するオブジェクトをインスタンス化する前にアプリで使用できるという点で、他の OpenSL ES 1.0.1 インターフェースとは異なります。したがって、オブジェクトの設定をインスタンス化の前に行うことができます。/sysroot/usr/include/SLES にある OpenSLES_AndroidConfiguration.h ヘッダー ファイルで、次に示す利用可能な構成キーと値が説明されています。

  • オーディオ プレーヤーのストリーム タイプ(デフォルト: SL_ANDROID_STREAM_MEDIA)。
  • オーディオ レコーダーの録音プロファイル(デフォルト: SL_ANDROID_RECORDING_PRESET_GENERIC)。

次のコード スニペットは、オーディオ プレーヤーで Android オーディオ ストリーム タイプを設定する方法の例を示しています。

    // CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION
    // in the required interface ID array. Do not realize player yet.
    // ...
    SLAndroidConfigurationItf playerConfig;
    result = (*playerObject)->GetInterface(playerObject,
        SL_IID_ANDROIDCONFIGURATION, &playerConfig);
    assert(SL_RESULT_SUCCESS == result);
    SLint32 streamType = SL_ANDROID_STREAM_ALARM;
    result = (*playerConfig)->SetConfiguration(playerConfig,
        SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
    assert(SL_RESULT_SUCCESS == result);
    // ...
    // Now realize the player here.
    

次のような同様のコードを使用して、オーディオ レコーダーのプリセットを設定することができます。

    // ... obtain the configuration interface as the first four lines above, then:
    SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
    result = (*playerConfig)->SetConfiguration(playerConfig,
        RECORDING_PRESET, &presetValue, sizeof(SLuint32));
    

Android エフェクト インターフェース

Android エフェクト、エフェクト センド、エフェクト機能の各インターフェースは、アプリでデバイス固有のオーディオ エフェクトを照会して使用するための汎用メカニズムを提供します。デバイス メーカーは、自社が提供するデバイス固有のオーディオ エフェクトをすべて文書化する必要があります。

ポータブル アプリでは、オーディオ エフェクトに Android エフェクトの拡張機能ではなく OpenSL ES 1.0.1 API を使用してください。

Android ファイル ディスクリプタ データロケータ

Android ファイル ディスクリプタ データロケータは、オーディオ プレーヤーのソースを、読み取りアクセス権限が付与されたオープン ファイル ディスクリプタとして指定できるようにします。データ形式は MIME にする必要があります。

アプリはファイル ディスクリプタを通じて APK からアセットを読み取るので、この拡張機能はネイティブのアセット マネージャーと併用すると特に役立ちます。

Android シンプル バッファキュー データロケータとインターフェース

OpenSL ES 1.0.1 リファレンス仕様では、バッファキューはオーディオ プレーヤーのみに使用でき、PCM や他のデータ形式と互換性があります。Android シンプル バッファキュー データロケータとインターフェースの仕様は、次の 2 つの例外を除けばリファレンス仕様と同じです。

  • Android シンプル バッファキューは、オーディオ レコーダーとオーディオ プレーヤーで使用できます。
  • これらのキューでは PCM データ形式のみを使用できます。

録音を行う場合、アプリで空のバッファをキューに登録する必要があります。システムがバッファへのデータ書き込みを完了したことについて、登録済みのコールバックから通知が送信されたとき、アプリはそのバッファから読み取りを行うことが可能です。

再生は同じ方法で動作します。ただし、ソースコードの互換性を将来にわたって維持するために、アプリでは OpenSL ES 1.0.1 バッファキューではなく Android シンプル バッファキューを使用することをおすすめします。

バッファキューの動作

Android 実装には、再生の状態が SL_PLAYSTATE_STOPPED になると再生カーソルが現在再生中のバッファの先頭に戻るという、リファレンス仕様の要件が含まれていません。Android 実装は、その動作に準拠することも、再生カーソルの位置を変更しないままにすることもできます。その結果、アプリはどちらの動作が行われるかを推測できないため、SL_PLAYSTATE_STOPPED に遷移した後に BufferQueue::Clear() メソッドを明示的に呼び出す必要があります。そうすることで、バッファキューが既知の状態に設定されます。

同様に、バッファキュー コールバックのトリガーを SL_PLAYSTATE_STOPPED への遷移にするべきか、それとも BufferQueue::Clear() の実行にするべきかを管理する仕様はありません。そのため、いずれかに対する依存関係を作成するのではなく、アプリが両方を処理できるようにすることをおすすめします。

オブジェクト作成時の動的インターフェース

便宜上、OpenSL ES 1.0.1 の Android 実装では、アプリはオブジェクトをインスタンス化するときに動的インターフェースを指定できます。これは、DynamicInterfaceManagement::AddInterface() を使用してインスタンス化後に動的インターフェースを追加する方法の代替策になります。

拡張機能のレポート

プラットフォームが Android 拡張機能をサポートしているかどうかを照会するためのメソッドには次の 3 つがあります。

  • Engine::QueryNumSupportedExtensions()
  • Engine::QuerySupportedExtension()
  • Engine::IsExtensionSupported()

どのメソッドも ANDROID_SDK_LEVEL_<API-level> を返します。ここで、API-level はプラットフォームの API レベルです(ANDROID_SDK_LEVEL_23 など)。プラットフォームの API レベルが 9 以上の場合、プラットフォームは拡張機能をサポートしています。

オーディオを PCM にデコードする

このセクションでは、エンコードされたストリームを即座に再生することなく PCM にデコードするための、OpenSL ES 1.0.1 の Android 固有の拡張機能(サポート終了済み)について説明します。次の表は、この拡張機能と代替手段を使用する際の推奨事項を示しています。

API レベル 代替手段
15 以下 適切なライセンスが付与されたオープンソースのコーデック
16~20 MediaCodec クラス、または適切なライセンスが付与されたオープンソースのコーデック
21 以上 <media/NdkMedia*.h> ヘッダー ファイル内の NDK MediaCodec、MediaCodec クラス、または適切なライセンスが付与されたオープンソースのコーデック

注: 現在、MediaCodec API の NDK バージョンに関するドキュメントはありません。ただし、例として native-codec サンプルコードを参照することができます。

標準のオーディオ プレーヤーでは、データシンクとして出力ミックスを指定して、オーディオ機器への再生を行います。一方、Android 拡張機能では、アプリがデータソースを指定する際に、URI として指定するか、MIME データ形式を使用して記述される Android ファイル ディスクリプタ データロケータとして指定すると、オーディオ プレーヤーがデコーダとして機能します。そのような場合、データシンクは PCM データ形式を使用する Android シンプル バッファキュー データロケータになります。

この機能は主に、ゲームが新しいゲームレベルに変わるときにオーディオ アセットをプリロードするために使用します。これは、SoundPool クラスが提供する機能に似ています。

アプリはまず、Android シンプル バッファキューに空のバッファセットを登録する必要があります。その後、アプリがバッファに PCM データを格納します。各バッファがいっぱいになると、Android シンプル バッファキュー コールバックが発生します。コールバック ハンドラは、PCM データを処理し、現時点で空のバッファをキューに再登録してから戻ります。アプリはデコードされたバッファを追跡する必要がありますが、コールバック パラメータ リストには、データが含まれているバッファや次にキューに登録されるべきバッファを示す十分な情報が含まれていません。

データソースは、ストリームの終了時に SL_PLAYEVENT_HEADATEND イベントを配信することで、ストリームの終了(EOS)を暗黙的に報告します。受信したすべてのデータをアプリがデコードした後は、Android シンプル バッファキュー コールバックへの呼び出しはそれ以上行われません。

通常、シンクの PCM データ形式は、サンプルレート、チャンネル数、ビット深度に関しては、エンコードされたデータソースの PCM データ形式と一致します。ただし、別のサンプルレート、チャンネル数、ビット深度にデコードすることが可能です。実際の PCM 形式を検出するための準備については、デコードされた PCM データの形式をメタデータによって特定するをご覧ください。

OpenSL ES for Android の PCM デコード機能では、一時停止と最初のシークがサポートされています。音量調節、エフェクト、ループ処理、再生レートはサポートされていません。

プラットフォームの実装によっては、アイドル状態のままにできないリソースがデコードで必要になる場合があります。そのため、空の PCM バッファを十分に提供することをおすすめします。そうしないと、デコーダが飢餓状態になります。この現象は、アプリが Android シンプル バッファキュー コールバックから、別の空のバッファをキューに登録せずに戻った場合などに発生する可能性があります。デコーダの飢餓状態の結果は明示されませんが、たとえば「デコードされた PCM データの削除」、「デコード処理の一時停止」、「デコーダの完全終了」などになります。

注: Android 4.x(API レベル 16~20)で実行されているアプリの場合、エンコードされたストリームを PCM にデコードするが即座に再生しないようにするには、MediaCodec クラスを使用することをおすすめします。Android 5.0(API レベル 21)以降で実行されている新しいアプリでは、NDK と同等の <NdkMedia*.h> を使用することをおすすめします。これらのヘッダー ファイルは、インストール ルートの media/ ディレクトリにあります。

ストリーミング ADTS AAC を PCM にデコードする

データソースが MIME データ形式を使用する Android バッファキュー データロケータで、データシンクが PCM データ形式を使用する Android シンプル バッファキュー データロケータの場合、オーディオ プレーヤーがストリーミング デコーダとして機能します。MIME データ形式を次のように設定してください。

  • コンテナ: SL_CONTAINERTYPE_RAW
  • MIME タイプの文字列: SL_ANDROID_MIME_AACADTS

この機能は主に、AAC オーディオを処理するストリーミング メディア アプリのうち、再生前にカスタム オーディオ処理を実行する必要があるアプリを対象としています。オーディオを PCM にデコードする必要があるアプリのほとんどでは、オーディオを PCM にデコードするで説明されている方法を使用してください。これは、その方法がよりシンプルで、より多くのオーディオ形式に対応しているためです。ここで説明する手法は特殊で、次に示す両方の条件に該当する場合にのみ使用します。

  • 圧縮されたオーディオ ソースが、ADTS ヘッダーに含まれている AAC フレームのストリームである。
  • アプリがこのストリームを管理する。データは、識別子が URI であるネットワーク リソースや、識別子がファイル ディスクリプタであるローカル ファイル内にはありません。

アプリは最初に、いっぱいになったバッファのセットを Android バッファキューに登録する必要があります。各バッファには 1 つ以上の完全な ADTS AAC フレームが含まれています。各バッファが空になると、Android バッファキュー コールバックが発生します。コールバック ハンドラは、バッファにデータを補充してキューに再登録してから戻る必要があります。アプリはエンコードされたバッファを追跡し続ける必要はありません。コールバック パラメータ リストには、次にキューに登録する必要があるバッファを示す十分な情報が含まれています。ストリームの最後は、EOS アイテムをキューに登録することによって明示的に示されます。EOS 以降はキューに登録できません。

デコーダが飢餓状態にならないようにするには、いっぱいになった ADTS AAC バッファを提供することをおすすめします。デコーダの飢餓状態は、アプリが Android バッファキュー コールバックから、別のいっぱいになったバッファをキューに登録せずに戻った場合などに発生する可能性があります。デコーダの飢餓状態の結果は明示されません。

データソースを除けば、ストリーミング デコードの方法はオーディオを PCM にデコードするで説明されている方法と同じです。

名前は似ていますが、Android バッファキューは Android シンプル バッファキューとは異なります。ストリーミング デコーダは、ADTS AAC データソース用の Android バッファキューと、PCM データシンク用の Android シンプル バッファキューの両方の種類のバッファキューを使用します。Android シンプル バッファキューの API の詳細については、Android シンプル バッファキュー データロケータとインターフェースをご覧ください。Android バッファキューの API の詳細については、インストール ルートの docs/Additional_library_docs/openmaxal/ ディレクトリにある index.html ファイルをご覧ください。

デコードされた PCM データの形式をメタデータによって特定する

SLMetadataExtractionItf インターフェースはリファレンス仕様に含まれています。ただし、デコードされた PCM データの実際の形式を示すメタデータキーは Android に固有のものです。これらのメタデータキーは OpenSLES_AndroidMetadata.h ヘッダー ファイルで定義されています。このヘッダー ファイルはインストール ルートの /sysroot/usr/include/SLES ディレクトリにあります。

メタデータキーのインデックスは、Object::Realize() メソッドの実行が完了した直後に利用できるようになります。ただし、関連付けられた値は、最初にエンコードされたデータをアプリがデコードするまで利用できません。おすすめの方法は、Object::Realize メソッドを呼び出した後でメインスレッドのキー インデックスをクエリすることです。また、Android シンプル バッファキュー コールバック ハンドラを最初に呼び出す際、そこに含まれている PCM 形式のメタデータ値を読み取る方法もおすすめです。このインターフェースの使用例については、NDK パッケージのサンプルコードをご覧ください。

メタデータキーの名前は不変ですが、キー インデックスは文書化されておらず、変更される可能性があります。アプリでは、インデックスがそれぞれの実行間で不変であると想定しないようにする必要があります。また、複数のオブジェクト インスタンスが同じ実行内のインデックスを共有するという想定もしてはなりません。

浮動小数点データ

Android 5.0(API レベル 21)以降で実行されているアプリでは、単精度浮動小数点形式で AudioPlayer にデータを提供することができます。

次のサンプルコードでは、Engine::CreateAudioPlayer() メソッドが浮動小数点データを使用するオーディオ プレーヤーを作成します。

    #include <SLES/OpenSLES_Android.h>
    ...
    SLAndroidDataFormat_PCM_EX pcm;
    pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
    pcm.numChannels = 2;
    pcm.sampleRate = SL_SAMPLINGRATE_44_1;
    pcm.bitsPerSample = 32;
    pcm.containerSize = 32;
    pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
    pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
    pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
    ...
    SLDataSource audiosrc;
    audiosrc.pLocator = ...
    audiosrc.pFormat = &pcm;
    
詳細については、「オーディオをサンプリングする」の浮動小数点オーディオをご覧ください。