汽車音響

Android Automotive OS (AAOS) 構建在核心 Android 音頻堆棧之上,以支持在車輛中作為信息娛樂系統運行的用例。 AAOS 負責信息娛樂聲音(即媒體、導航和通信),但不直接負責具有嚴格可用性和時間要求的鈴聲和警告。雖然 AAOS 提供了信號和機制來幫助車輛管理音頻,但最終由車輛決定應該為駕駛員和乘客播放哪些聲音,確保在沒有安全問題的情況下正確聽到安全關鍵聲音和監管聲音中斷。

由於 Android 管理車輛的媒體體驗,諸如收音機調諧器之類的外部媒體源應由應用程序表示,這些應用程序可以處理源的音頻焦點和媒體鍵事件。

Android 11 包括對汽車相關音頻支持的以下更改:

Android 聲音和流

汽車音頻系統處理以下聲音和流:

以流為中心的架構圖

圖 1.以流為中心的架構圖

Android 管理來自 Android 應用程序的聲音,控制這些應用程序並根據聲音類型將它們的聲音路由到 HAL 的輸出設備:

  • 邏輯流,在核心音頻命名法中稱為源,用Audio Attributes標記。
  • 物理流,在核心音頻術語中稱為設備,在混合後沒有上下文信息。

為了可靠性,外部聲音(來自獨立來源,如安全帶警告鈴聲)在 Android 之外、在 HAL 之下甚至在單獨的硬件中進行管理。系統實現者必須提供一個混音器,以接受來自 Android 的一個或多個聲音輸入流,然後以合適的方式將這些流與車輛所需的外部聲源組合。

HAL 實現和外部混音器負責確保聽到對安全至關重要的外部聲音,並在 Android 提供的流中進行混音並將它們路由到合適的揚聲器。

安卓聲音

應用程序可能有一個或多個播放器,它們通過標準 Android API(例如,用於焦點控制的AudioManager或用於流式傳輸的MediaPlayer )進行交互,以發出一個或多個邏輯音頻數據流。該數據可以是單聲道單聲道或 7.1 環繞聲,但會被路由並作為單個源處理。應用程序流與AudioAttributes相關聯,它為系統提供有關如何表達音頻的提示。

邏輯流通過 AudioService 發送並路由到一個(並且只有一個)可用的物理輸出流,每個物理輸出流都是 AudioFlinger 中混音器的輸出。在音頻屬性混合到物理流之後,它們不再可用。

然後將每個物理流傳送到音頻 HAL 以在硬件上呈現。在汽車應用程序中,渲染硬件可以是本地編解碼器(類似於移動設備)或跨車輛物理網絡的遠程處理器。無論哪種方式,音頻 HAL 實現的工作是提供實際的樣本數據並使其變得可聽。

外部流

不應通過 Android 路由的聲音流(出於認證或時間原因)可能會直接發送到外部混音器。從 Android 11 開始,HAL 現在能夠為這些外部聲音請求焦點以通知 Android,以便它可以採取適當的操作,例如暫停媒體或阻止其他人獲得焦點。

如果外部流是應該與 Android 正在生成的聲音環境交互的媒體源(例如,在打開外部調諧器時停止 MP3 播放),那麼這些外部流應該由 Android 應用程序表示。這樣的應用程序將代表媒體源而不是 HAL 請求音頻焦點,並會根據需要通過啟動/停止外部源來響應焦點通知以適應 Android 焦點策略。該應用程序還負責處理媒體鍵事件,例如播放/暫停。控制此類外部設備的一種建議機制是HwAudioSource

輸出設備

在音頻 HAL 級別,設備類型AUDIO_DEVICE_OUT_BUS提供用於車輛音頻系統的通用輸出設備。總線設備支持可尋址端口(其中每個端口都是物理流的端點),並且預計是車輛中唯一受支持的輸出設備類型。

系統實現可以為所有 Android 聲音使用一個總線端口,在這種情況下,Android 將所有內容混合在一起並將其作為一個流提供。或者,HAL 可以為每個CarAudioContext提供一個總線端口,以允許同時傳遞任何聲音類型。這使得 HAL 實現可以根據需要混合和閃避不同的聲音。

通過car_audio_configuration.xml將音頻上下文分配給輸出設備。

麥克風輸入

捕獲音頻時,Audio HAL 會收到一個openInputStream調用,其中包含一個AudioSource參數,該參數指示應如何處理麥克風輸入。

VOICE_RECOGNITION源(特別是 Google 助理)需要一個立體聲麥克風流,該流具有迴聲消除效果(如果可用)但沒有對其應用其他處理。波束成形預計將由助手完成。

多通道麥克風輸入

要從具有兩個以上通道(立體聲)的設備捕獲音頻,請使用通道索引掩碼而不是位置索引掩碼(例如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設置的值(最多兩個通道)。

並發捕獲

從 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 中,引入了系統用法。這些用法的行為類似於之前建立的用法,除了它們需要係統 API 以及android.permission.MODIFY_AUDIO_ROUTING來使用。新的系統用法是:

  • USAGE_EMERGENCY
  • USAGE_SAFETY
  • USAGE_VEHICLE_STATUS
  • USAGE_ANNOUNCEMENT

要構造具有系統用法的AudioAttributes ,請使用AudioAttributes.Builder#setSystemUsage而不是setUsage 。使用非系統用法調用此方法將導致拋出IllegalArgumentException 。此外,如果在構建器上同時設置了系統使用和使用,它將在構建時拋出IllegalArgumentException

要檢查與AudioAttributes實例關聯的用法,請調用AudioAttributes#getSystemUsage 。這將返回關聯的使用情況或系統使用情況。

音頻上下文

為了簡化 AAOS 音頻的配置,類似的用法已分組到CarAudioContext中。這些音頻上下文在CarAudioService中用於定義路由、音量組和音頻焦點管理。

Android 11 中的音頻上下文是:

CarAudioContext相關屬性用法
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 能夠將音頻配置到單獨的區域中。每個區域都是車輛內的設備集合,具有自己的捲組、上下文的路由配置和焦點管理。通過這種方式,主艙可以配置為一個音頻區域,而後顯示器的耳機插孔可以配置為第二個區域。

這些區域被定義為car_audio_configuration.xml的一部分。 CarAudioService然後讀取配置並幫助 AudioService 根據其關聯區域路由音頻流。每個區域仍然根據上下文和應用程序 uid 定義路由規則。創建播放器時, CarAudioService確定播放器與哪個區域關聯,然後根據使用情況,AudioFlinger 應將音頻路由到哪個設備。

每個音頻區域的焦點也獨立保持。這使得不同區域中的應用程序能夠獨立地產生音頻而不會相互干擾,同時讓應用程序仍然尊重其區域內的焦點變化。 CarZonesAudioFocus中的CarAudioService負責管理每個區域的焦點。

配置多區域音頻

圖 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通過藍牙接收的音樂。
AUDIO_DEVICE_IN_TELEPHONY_RX用於從與電話相關的蜂窩無線電接收的音頻。

配置音頻設備

Android 可見的音頻設備必須在/audio_policy_configuration.xml中定義,其中包括以下組件:

  • 模塊名稱。支持“primary”(用於汽車用例)、“A2DP”、“remote_submix”和“USB”。模塊名稱和相應的音頻驅動程序應編譯為audio.primary.$(variant).so
  • 設備端口。包含可以從此模塊訪問的所有輸入和輸出設備(包括永久連接的設備和可移動設備)的設備描述符列表。
    • 對於每個輸出設備,您可以定義由 min/max/default/step 值組成的增益控制,單位為毫貝(1 毫貝 = 1/100 dB = 1/1000 貝)。
    • devicePort 實例上的地址屬性可用於查找設備,即使有多個設備具有與AUDIO_DEVICE_OUT_BUS相同的設備類型。
  • 混合端口。包含由音頻 HAL 公開的所有輸出和輸入流的列表。每個 mixPort 實例都可以視為 Android AudioService 的物理流。
  • 路線。定義輸入和輸出設備之間或流和設備之間的可能連接列表。

以下示例定義了一個輸出設備 bus0_phone_out,其中所有 Android 音頻流都由 mixer_bus0_phone_out 混合。該路由將mixer_bus0_phone_out的輸出流帶到設備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>