自動車オーディオ

Android Automotive OS(AAOS)は、コア Android オーディオ スタックに基づき、自動車のインフォテインメント システムとして動作するユースケースをサポートします。AAOS は、インフォテインメント サウンド(メディア、ナビゲーション、通信など)を担いますが、可用性とタイミングの要件が厳しい通知音や警告については直接対応しません。AAOS は車両でオーディオを管理できるようにシグナルとメカニズムを提供しますが、ドライバーや乗客のためにどのサウンドを再生するかを判断し、安全上重要なサウンドや規制音が途切れることなく適切に聞こえるようにすることは、最終的には車両次第です。

Android が車両のメディア エクスペリエンスを管理するため、ラジオ チューナーなどの外部メディアソースには、ソースの音声フォーカスやメディア キーイベントを処理できるアプリを使用する必要があります。

Android 11 では、自動車関連のオーディオ サポートに次の変更が加えられました。

Android のサウンドとストリーム

自動車用オーディオ システムは、次のサウンドとストリームを処理します。

ストリーム中心のアーキテクチャ図

図 1. ストリーム中心のアーキテクチャ図

Android は Android アプリからのサウンドを管理してこれらのアプリを制御し、サウンドタイプに応じて HAL でサウンドを出力デバイスにルーティングします。

  • 論理ストリーム(Core Audio の命名法ではソース)は、オーディオ属性でタグ付けされます。
  • 物理ストリーム(Core Audio の命名法ではデバイス)には、ミキシング後のコンテキスト情報がありません。

信頼性の確保のため、外部サウンド(シートベルトの警告音など、独立したソースからのサウンド)は、Android 外、HAL の下、または別のハードウェアで管理されます。システム実装者は、Android から 1 つ以上のサウンド入力ストリームを受け入れ、こうしたストリームを適切な方法で車両に必要な外部サウンドのソースと組み合わせるミキサーを用意する必要があります。

HAL の実装と外部ミキサーにより、安全性が重視される外部サウンドが確実に聞こえるようになるとともに、Android が提供するストリームでミキシングが行われ、適切なスピーカーにルーティングされます。

Android のサウンド

アプリには、標準の Android API(フォーカス制御用の AudioManager、ストリーミング用の MediaPlayer など)を介してやり取りし、音声データの 1 つ以上の論理ストリームを出力するプレーヤーが 1 つ以上含まれる場合があります。このデータは、単一チャンネルのモノラルにも 7.1 サラウンドにもなり得ますが、単一のソースとしてルーティングされ、処理されます。アプリ ストリームは、音声の表現方法に関する情報をシステムに与える AudioAttributes に関連付けられます。

論理ストリームは AudioService を介して送信され、利用可能な物理出力ストリームのいずれか 1 つにルーティングされます。各ストリームは、AudioFlinger 内のミキサーの出力です。物理ストリームにミックスダウンされたオーディオ属性は使用できなくなります。

その後、各物理ストリームは、ハードウェアでのレンダリングのために Audio HAL に配信されます。自動車アプリでは、レンダリング ハードウェアは、ローカル コーデック(モバイル デバイスに類似)または車両の物理ネットワーク全体のリモート プロセッサになります。いずれの場合でも、Audio HAL の実装を通じて実際のサンプルデータが配信され、可聴化されます。

外部ストリーム

(認証またはタイミングの理由で)Android 経由でルーティングすることを回避すべきサウンド ストリームは、外部ミキサーに直接送信される場合があります。Android 11 では、HAL がこうした外部サウンドのフォーカスをリクエストして Android に通知し、メディアを一時停止したり、他の音声がフォーカスを得ないようにしたりなど、適切なアクションを行えるようになりました。

外部ストリームが Android によって生成されるサウンド環境とやり取りする必要があるメディアソースの場合(たとえば、外部チューナーがオンになっている場合に MP3 の再生を停止する)、こうした外部ストリームには Android アプリを使用する必要があります。このようなアプリは、Android のフォーカス ポリシーに適合するように必要に応じて外部ソースを起動 / 停止することにより、HAL ではなくメディアソースに代わって音声フォーカスをリクエストし、フォーカス通知に応答します。また、再生や一時停止など、メディア キーイベントの処理も行います。このような外部デバイスを制御するためのメカニズムとしては HwAudioSource などが推奨されます。

出力デバイス

Audio HAL レベルでは、デバイスタイプ AUDIO_DEVICE_OUT_BUS は、車両のオーディオ システムで使用する汎用出力デバイスを提供します。このバスデバイスは、アドレス指定可能なポート(各ポートは物理ストリームのエンドポイント)をサポートし、車両で唯一サポートされる出力デバイスタイプであると想定されます。

システムの実装では、Android のすべてのサウンドに 1 つのバスポートを使用できます。この場合、Android はすべてをミックスして 1 つのストリームとして配信します。または、HAL は CarAudioContext ごとに 1 つのバスポートを提供し、任意のサウンドタイプの同時配信を可能にします。これにより、HAL の実装で、必要に応じて異なるサウンドのミックスやダッキングを行えるようになります。

出力デバイスに対するオーディオ コンテキストの割り当ては、car_audio_configuration.xml で行います。

マイク入力

Audio HAL は、音声キャプチャ時に、マイク入力の処理方法を示す AudioSource 引数を含む openInputStream 呼び出しを受け取ります。

VOICE_RECOGNITION ソース(具体的には Google アシスタント)は、エコー キャンセル効果(可能な場合)以外には処理が一切適用されていないステレオ マイク ストリームを想定しています。ビームフォーミングはアシスタントで行うことが想定されています。

マルチチャンネル マイク入力

チャンネル数が 2 つ(ステレオ)を超えるデバイスから音声をキャプチャするには、位置インデックス マスク(CHANNEL_IN_LEFT など)でなくチャンネル インデックス マスクを使用します。以下に例を示します。

final AudioFormat audioFormat = new AudioFormat.Builder()
    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
    .setSampleRate(44100)
    .setChannelIndexMask(0xf /* 4 channels, 0..3 */)
    .build();
final AudioRecord audioRecord = new AudioRecord.Builder()
    .setAudioFormat(audioFormat)
    .build();
audioRecord.setPreferredDevice(someAudioDeviceInfo);

setChannelMasksetChannelIndexMask の両方を設定した場合、AudioRecord は、setChannelMask によって設定された値(最大 2 チャンネル)のみを使用します。

同時キャプチャ

Android 10 では、Android フレームワークは入力の同時キャプチャをサポートしていますが、ユーザーのプライバシーを保護するための制限が設けられています。たとえば AUDIO_SOURCE_FM_TUNER などの仮想ソースは無視されますが、通常の入力(マイクなど)とは同時キャプチャできます。また、同時キャプチャの制限により、HwAudioSources も考慮されません。`

AUDIO_DEVICE_IN_BUS デバイスまたはセカンダリ AUDIO_DEVICE_IN_FM_TUNER デバイスで動作するように設計されているアプリは、明示的にこれらのデバイスを特定し、AudioRecord.setPreferredDevice() を使って Android のデフォルト ソース選択ロジックをバイパスする必要があります。

オーディオ用途

AAOS は、ルーティング、ボリューム調整、フォーカス管理に、主に AudioAttributes.AttributeUsages を使用します。用途とは、ストリームが再生されている理由のことです。したがって、ストリームと音声フォーカスのリクエストでは、必ずオーディオ再生の用途を指定する必要があります。AudioAttributes オブジェクトの作成時に特に設定しなかった場合、用途はデフォルトで USAGE_UNKOWN に設定されます。これは現在のところ USAGE_MEDIA と同様に扱われますが、メディアの再生ではこの動作に依存しないようにしてください。

システム用途

Android 11 では、システム用途が導入されています。今回新たに導入された用途はこれまでの用途と同様に機能しますが、android.permission.MODIFY_AUDIO_ROUTING とシステム API を使用する必要があります。新しいシステム用途は次のとおりです。

  • USAGE_EMERGENCY
  • USAGE_SAFETY
  • USAGE_VEHICLE_STATUS
  • USAGE_ANNOUNCEMENT

システム用途を使って AudioAttributes を作成するには、setUsage ではなく AudioAttributes.Builder#setSystemUsage を使用します。システム用途以外の用途を使ってこのメソッドを呼び出すと、IllegalArgumentException がスローされます。また、システム用途とそれ以外の用途がともにビルダーに設定されている場合、ビルダーはビルド時に IllegalArgumentException をスローします。

AudioAttributes インスタンスに関連付けられている用途を確認するには、AudioAttributes#getSystemUsage を呼び出します。関連付けられているシステム用途またはそれ以外の用途が返されます。

オーディオ コンテキスト

AAOS オーディオの構成を簡素化するために、類似した用途は CarAudioContext にグループ化されました。こうしたオーディオ コンテキストは、ルーティング、音量グループ、音声フォーカス管理を定義するために CarAudioService 全体で使用されます。

Android 11 のオーディオ コンテキストは次のとおりです。

CarAudioContext 関連付けられた AttributeUsage
MUSIC UNKNOWN, GAME, MEDIA
NAVIGATION ASSISTANCE_NAVIGATION_GUIDANCE
VOICE_COMMAND ASSISTANT, ASSISTANCE_ACCESSIBILITY
CALL_RING NOTIFICATION_RINGTONE
CALL VOICE_COMMUNICATION, VOICE_COMMUNICATION_SIGNALING
ALARM ALARM
NOTIFICATION NOTIFICATION, NOTIFICATION_*
SYSTEM_SOUND ASSISTANCE_SONIFICATION
EMERGENCY EMERGENCY
SAFETY SAFETY
VEHICLE_STATUS VEHICLE_STATUS
ANNOUNCEMENT ANNOUNCEMENT

オーディオ コンテキストと用途をマッピングしています。ハイライト表示されている行は、新しいシステム用途向けです。

マルチゾーン オーディオ

自動車については、プラットフォームを操作する複数のユーザーが個別のメディアを同時に利用するという、新しい一連のユースケースが考えられます。たとえば、運転手はキャビンで音楽を再生し、バックシートの同乗者は後部座席のディスプレイで YouTube 動画を視聴できます。マルチゾーン オーディオでは、異なる音声ソースを車両の異なるエリアで同時に再生できるようにすることで、これを可能にしています。

Android 10 以降、マルチゾーン オーディオにより、OEM はオーディオを個別のゾーンに構成できます。各ゾーンは、車両内のデバイスの集合であり、独自の音量グループ、コンテキストのルーティング構成、フォーカス管理を備えています。このようにして、メインのキャビンを 1 つのオーディオ ゾーンとして、後部座席のディスプレイのヘッドフォン端子を 2 つ目のゾーンとして構成できます。

ゾーンは car_audio_configuration.xml の一部として定義されます。次に、CarAudioService が構成を読み取り、関連付けられているゾーンに基づいて AudioService でオーディオ ストリームをルーティングできるようにします。各ゾーンは引き続き、コンテキストとアプリ UID に基づいてルーティングのルールを定義します。プレーヤーが作成されると、CarAudioService はプレーヤーが関連付けられているゾーンを判別し、その用途に基づいて、AudioFlinger でオーディオのルーティング対象のデバイスを決定します。

また、フォーカスはオーディオ ゾーンごとに独立して管理されます。これにより、異なるゾーンのアプリは、相互に干渉することなく、オーディオを独立して生成しながら、各ゾーン内のフォーカスの変化に対応できます。CarAudioService 内の CarZonesAudioFocus は、各ゾーンのフォーカスを管理します。

マルチゾーン オーディオを構成する

図 2. マルチゾーン オーディオを構成する

オーディオ HAL

自動車用オーディオの実装では、標準の Android Audio HAL に依存します。これには以下が含まれます。

  • IDevice.hal: 入出力ストリームの作成、マスター ボリュームとミュートの処理を行います。また、次のものを使用します。
    • createAudioPatch: デバイス間に外部間パッチを作成します。
    • IDevice.setAudioPortConfig(): 各物理ストリームの音量を提供します。
  • IStream.hal: 入力と出力のバリアントとともに、オーディオ サンプルとハードウェアの間のストリーミングを管理します。

車載デバイスのタイプ

次のデバイスタイプは自動車プラットフォームに関連しています。

デバイスタイプ 説明
AUDIO_DEVICE_OUT_BUS Android からのプライマリ出力(これは Android からのすべてのオーディオが車両に配信される方法です)。各コンテキストのストリームを明確にするためのアドレスとして使用されます。
AUDIO_DEVICE_OUT_TELEPHONY_TX 伝送のためにセル無線通信にルーティングされるオーディオに使用されます。
AUDIO_DEVICE_IN_BUS 特に分類されていない入力に使用されます。
AUDIO_DEVICE_IN_FM_TUNER ブロードキャスト ラジオ入力にのみ使用されます。
AUDIO_DEVICE_IN_TV_TUNER テレビデバイスに使用されます(存在する場合)。
AUDIO_DEVICE_IN_LINE AUX 入力ジャックに使用されます。
AUDIO_DEVICE_IN_BLUETOOTH_A2DP Bluetooth 経由で受信した音楽。
AUDIO_DEVICE_IN_TELEPHONY_RX 通話に関連付けられたセル無線通信から受信したオーディオに使用されます。

オーディオ デバイスの構成

Android に表示されるオーディオ デバイスは /audio_policy_configuration.xml で定義する必要があります。これには以下のコンポーネントが含まれます。

  • module name: 「primary」(自動車のユースケースに使用)、「A2DP」、「remote_submix」、「USB」をサポートします。モジュール名と対応するオーディオ ドライバは、audio.primary.$(variant).so にコンパイルする必要があります。
  • devicePorts: このモジュールからアクセス可能なすべての入出力デバイス(永続的に接続されているデバイスやリムーバブル デバイスを含む)のデバイス記述子のリストを含みます。
    • 出力デバイスごとに、最小値 / 最大値 / デフォルト値 / ステップ値(ミリベル単位)で構成されるゲイン コントロールを定義できます(1 ミリベル = 1/100 デシベル = 1/1,000 ベル)。
    • devicePort インスタンスのアドレス属性はデバイスの検出に使用できます(AUDIO_DEVICE_OUT_BUS と同じデバイスタイプのデバイスが複数ある場合でも可能)。
  • mixPorts: オーディオ HAL によって公開されるすべての入出力ストリームのリストを含みます。各 mixPort インスタンスは、Android AudioService への物理ストリームと見なされます。
  • routes: 入力デバイスと出力デバイスの間、またはストリームとデバイスの間の可能な接続のリストを定義します。

次の例では、mixer_bus0_phone_out によって、すべての Android オーディオ ストリームがミックスされる出力デバイス bus0_phone_out を定義しています。ルートは、デバイス bus0_phone_out への mixer_bus0_phone_out の出力ストリームを取得します。

<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
    <modules>
        <module name="primary" halVersion="3.0">
            <attachedDevices>
                <item>bus0_phone_out</item>
<defaultOutputDevice>bus0_phone_out</defaultOutputDevice>
            <mixPorts>
                <mixPort name="mixport_bus0_phone_out"
                         role="source"
                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                            samplingRates="48000"
                            channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                </mixPort>
            </mixPorts>
            <devicePorts>
                <devicePort tagName="bus0_phone_out"
                            role="sink"
                            type="AUDIO_DEVICE_OUT_BUS"
                            address="BUS00_PHONE">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                            samplingRates="48000"
                            channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                    <gains>
                        <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
                                minValueMB="-8400"
                                maxValueMB="4000"
                                defaultValueMB="0"
                                stepValueMB="100"/>
                    </gains>
                </devicePort>
            </devicePorts>
            <routes>
                <route type="mix" sink="bus0_phone_out"
                       sources="mixport_bus0_phone_out"/>
            </routes>
        </module>
    </modules>
</audioPolicyConfiguration>