音量管理包含在CarAudioService
中,它使用固定音量,并期望通过硬件放大器而不是软件在 HAL 下方应用音量。 CarAudioService
将输出设备组织到卷组中,以将相同的增益应用于与卷组关联的所有设备。
固定数量
AAOS 实现使用硬件放大器而不是软件混音器来控制音量。为了避免副作用,请将config_useFixedVolume
标志设置为true
(根据需要进行覆盖):
<resources>
<!-- Car uses hardware amplifier for volume. -->
<bool name="config_useFixedVolume">true</bool>
</resources>
当未设置config_useFixedVolume
标志(或设置为false
)时,应用程序可以调用AudioManager.setStreamVolume()
以按软件混音器中的流类型更改音量。由于对其他应用程序的潜在影响以及软件混音器中的音量衰减可能导致硬件放大器接收时信号中可用的有效位较少,因此这可能并不总是理想的。
卷组
音量组管理音频区域内一组设备的音量。对于每个音量组,可以独立控制音量。产生的增益在相关设备上配置,由车辆放大器应用。音量设置会为用户保留并在用户登录时加载。
定义卷组
CarAudioService 使用car_audio_configuration.xml
中定义的卷组:
<audioZoneConfiguration version="2.0">
<zones>
<zone name="primary zone" isPrimary="true">
<volumeGroups>
<group>
<device address="bus0_media_out">
<context context="music"/>
</device>
</group>
<group>
<device address="bus1_navigation_out">
<context context="navigation"/>
</device>
<device address="bus2_voice_command_out">
<context context="voice_command"/>
</device>
</group>
...
</volumeGroups>
</zone>
</zones>
</audioZoneConfiguration>
每个卷组应包含一个或多个具有关联地址的输出设备。地址应对应于audio_policy_configuration.xml
中定义的输出设备。
配置卷组增益
每个卷组都具有最小、最大和默认增益值以及基于在audio_policy_configuration.xml
中为与卷组关联的设备配置的值的步长。
<devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS" address="bus0_media_out">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<gains>
<gain name="" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
</gains>
</devicePort>
在初始化期间,卷组检查关联设备的增益值并按如下方式配置组:
- 一步的大小。对于卷组控制的所有设备必须相同。
- 最小增益。组中设备中最小的最小增益。
- 最大增益。该组设备中最高的最大增益。
- 默认增益。组中设备中的最高默认增益。
根据这些值的配置方式,可以将卷组的增益设置在与卷组关联的设备支持的范围之外。在这种情况下,对于该设备,根据卷组的值是否低于或高于该范围,将增益设置为设备的最小或最大增益值。
卷组标识符
卷组在运行时按照 XML 文件中定义的顺序进行标识。音频区域内的 ID 范围为0
到N-1
,其中N
是该区域中卷组的数量。这样,卷组 ID 在区域之间就不是唯一的。这些标识符用于与卷组关联的CarAudioManager
API。任何接受不含zoneId
groupId
API 都默认为主音频区域。
多区域卷管理
每个音频区域应具有一个或多个卷组,并且每个卷组仅与单个音频区域相关联。此关系定义为car_audio_configuration.xml
的一部分。要了解更多信息,请参阅上面定义卷组中的示例。
对于与该区域关联的用户,每个区域的当前音量级别都会保留。这些设置是特定于区域的,这意味着如果用户在与主要区域关联的显示器上登录,然后登录与辅助音频区域关联的区域,则第一个区域加载和保留的音量级别与第一个区域的加载和保留的音量级别不同。次要区域。
处理音量键事件
Android 定义了多个用于音量控制的键码,包括:
-
KEYCODE_VOLUME_UP
-
KEYCODE_VOLUME_DOWN
-
KEYCODE_VOLUME_MUTE
默认情况下,Android 将音量键事件路由到应用程序。汽车实现应强制由CarAudioService
处理这些关键事件,然后根据需要调用setGroupVolume
或setMasterMute
。要强制执行此行为,请将config_handleVolumeKeysInWindowManager
标志设置为true
:
<resources>
<bool name="config_handleVolumeKeysInWindowManager">true</bool>
</resources>
音量键事件当前无法区分它们用于哪个区域,并且假定全部与主音频区域相关联。当接收到音量键事件时, CarAudioService
通过获取活动播放器的音频上下文来确定要调整的音量组,然后调整包含与最高优先级音频上下文关联的输出设备的音量组。优先级是根据CarVolume.AUDIO_CONTEXT_VOLUME_PRIORITY
中定义的固定顺序确定的。
淡入淡出和平衡
AudioControl HAL 的两个版本都包含用于设置车辆淡入淡出和平衡的 API。 CarAudioManager 的相应系统 API 将值传递给 AudioControl HAL。这些 API 需要android.car.permission.CAR_CONTROL_AUDIO_VOLUME
。 AudioControl API 是:
setBalanceTowardRight(float value)
将扬声器音量向汽车的右侧 (+) 或左侧 (-) 移动。- 0.0 居中
- +1.0 完全正确
- -1.0 完全离开
- 超出 -1 到 1 范围的值是错误的
setFadeTowardFront(float value)
将扬声器音量向汽车前部 (+) 或后部 (-) 移动。- 0.0 居中
- +1.0 完全前进
- -1.0 完全靠后
- 超出 -1 到 1 范围的值是错误的
您决定如何应用这些值以及如何向用户显示这些值。它们可以严格应用于媒体或全面应用于所有 Android 声音。 Android 11 还引入了对将音频效果应用于输出设备的支持。这样,就可以通过适当的输出设备上的音频效果而不是通过这些 API 来管理淡入淡出和平衡。
音频闪避
当车辆降低一个流的增益以便可以更清楚地听到同时播放的另一流时,就会发生音频闪避。在 AAOS 中,音频闪避是由 HAL 实现的。 Android 无法控制操作系统之外的声音。在 Android 11 中,HAL 可用于做出闪避决策的主要信息是两个输出设备是否都具有活动流。
何时躲避
虽然由各个 OEM 决定 HAL 如何处理闪避,但我们建议遵循以下准则。
当两个应用程序或服务同时持有音频焦点时,通常会在 Android 中播放多个流。要了解 Android 何时可以授予并发焦点,请参阅限制类型中的交互矩阵。随着汽车音响插件的引入,这也取决于你的AudioFocus管理。
Android 混合在一起的任何流都是在应用任何增益之前完成的。因此,与另一个流同时播放时应回避的任何流都应路由到单独的输出设备,以便 HAL 可以在混合它们之前应用回避。
建议的躲避行为
以下是建议的潜在并发交互。
相互作用 | 行动 |
---|---|
EMERGENCY | 回避或静音除SAFETY 以外的所有内容 |
SAFETY | 除EMERGENCY 外,回避一切 |
NAVIGATION | 除SAFETY 和EMERGENCY 外,回避一切 |
CALL | 回避除SAFETY 、 EMERGENCY 和NAVIGATION 之外的所有内容 |
VOICE | 鸭子CALL_RING |
VEHICLE_SOUNDS | 您可以确定活动声音的重要性以及它是否避开其他声音。 |
MUSIC 和ANNOUNCEMENT | 被一切所淹没。作为SYSTEM_SOUND 播放的触摸交互音是例外。 |
躲避时的注意事项
某些应用程序和服务(例如导航或助手)可能会使用多个玩家来执行操作。当数据流停止流经输出设备时,避免过度闪避,以确保媒体在导航或助手应用程序的下一次播放开始之前闪避之前不会恢复到最大音量。
对于具有多个声场且隔离效果足够好的车辆,您可以将音频路由到汽车的不同区域,而不是回避。例如,导航指令可以发送到驾驶员的头枕扬声器,同时继续以正常音量在整个车厢内播放音乐。
安全关键声音
Android 11 引入了HAL 音频焦点 API 。 HAL 确保安全关键声音优先于其他声音。如果 HAL 持有USAGE_EMERGENCY
的音频焦点,则不能保证来自 Android 的应用程序和服务不会播放声音。 HAL 确定应混合或静音来自 Android 的哪些流以播放安全关键的声音。
配置音量设置 UI
AAOS 将卷设置 UI 与卷组配置分离。这些可以按照配置卷组增益中的描述进行覆盖。这种分离可确保卷组配置发生更改时无需进行任何更改。
在汽车设置 UI 中, packages/apps/Car/Settings/res/xml/car_volume_items.xml
包含与每个定义的AudioAttributes.USAGE
关联的 UI 元素(标题和图标资源)。该文件通过使用与每个VolumeGroup
中包含的第一个识别的用法关联的资源来合理呈现所定义的VolumeGroups
。
例如,以下示例将VolumeGroup
定义为包括voice_communication
和voice_communication_signalling
。汽车设置 UI 的默认实现使用与voice_communication
关联的资源呈现VolumeGroup
,因为这是文件中的第一个匹配项。
<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
<item car:usage="voice_communication"
car:title="@*android:string/volume_call"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="voice_communication_signalling"
car:title="@*android:string/volume_call"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="media"
car:title="@*android:string/volume_music"
car:icon="@*android:drawable/ic_audio_media"/>
<item car:usage="game"
car:title="@*android:string/volume_music"
car:icon="@*android:drawable/ic_audio_media"/>
<item car:usage="alarm"
car:title="@*android:string/volume_alarm"
car:icon="@*android:drawable/ic_audio_alarm"/>
<item car:usage="assistance_navigation_guidance"
car:title="@string/navi_volume_title"
car:icon="@drawable/ic_audio_navi"/>
<item car:usage="notification_ringtone"
car:title="@*android:string/volume_ringtone"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="assistant"
car:title="@*android:string/volume_unknown"
car:icon="@*android:drawable/ic_audio_vol"/>
<item car:usage="notification"
car:title="@*android:string/volume_notification"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="notification_communication_request"
car:title="@*android:string/volume_notification"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="notification_communication_instant"
car:title="@*android:string/volume_notification"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="notification_communication_delayed"
car:title="@*android:string/volume_notification"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="notification_event"
car:title="@*android:string/volume_notification"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="assistance_accessibility"
car:title="@*android:string/volume_notification"
car:icon="@*android:drawable/ic_audio_ring_notif"/>
<item car:usage="assistance_sonification"
car:title="@*android:string/volume_unknown"
car:icon="@*android:drawable/ic_audio_vol"/>
<item car:usage="unknown"
car:title="@*android:string/volume_unknown"
car:icon="@*android:drawable/ic_audio_vol"/>
</carVolumeItems>
上述配置中使用的属性和值在packages/apps/Car/Settings/res/values/attrs.xml
中声明。音量设置 UI 使用以下基于VolumeGroup
的 CarAudioManager API:
-
getVolumeGroupCount()
了解应绘制多少个控件。 -
getGroupMinVolume()
和getGroupMaxVolume()
获取下限和上限。 -
getGroupVolume()
获取当前音量。 -
registerVolumeChangeObserver()
以获取音量变化通知。
汽车卷组活动
音量更新和静音切换的汽车用例具有上下文基础,可以定义某些应用程序的操作,例如音量设置。来自汽车音频堆栈的当前音量和静音回调提供有限的上下文信息。为了更好地服务于汽车用例和未来的可扩展性,Android 14 中添加了 CarVolumeGroupEvent。每个事件都包含三种关键类型的信息:
-
CarVolumeGroupInfo
列表 EventTypes
(位图)-
ExtraInfos
列表
汽车卷组信息
事件回调的接收者可以随时访问受影响的汽车卷组信息列表。这意味着应用程序不需要对汽车音频框架进行任何额外的调用来获取最新状态。它可以简单地使用接收到的CarVolumeGroupInfos
to update the UI or internal states. To make it easier for apps, the aspects that changed in a car volume group are also provided as part of
,如下所述。
事件类型
定义 CarVolumeGroupInfo 的哪个方面已更改。应用程序可以使用它来识别更改并采取所需的操作。例如, EVENT_TYPE_VOLUME_MAX_INDEX_CHANGED
表示各个CarVolumeGroups' maximum volume gain index has changed and can be queried by
。
下表显示了EventType
和CarVolumeGroupInfo
之间的关系。
事件类型 | 汽车卷组信息 |
---|---|
EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED | CarVolumeGroupInfo.getVolumeGainIndex() |
EVENT_TYPE_VOLUME_MIN_INDEX_CHANGED | CarVolumeGroupInfo.getMinVolumeGainIndex() |
EVENT_TYPE_VOLUME_MAX_INDEX_CHANGED | CarVolumeGroupInfo.getMaxVolumeGainIndex() |
EVENT_TYPE_MUTE_CHANGED | CarVolumeGroupInfo.isMulated() |
EVENT_TYPE_VOLUME_BLOCKED_CHANGED | CarVolumeGroupInfo.isBlocked() |
EVENT_TYPE_ATTENUATION_CHANGED | CarVolumeGroupInfo.isAttenuated() |
EVENT_TYPE_ZONE_CONFIGURATION_CHANGED | CarVolumeGroupInfo.getAudioAttributes() |
额外信息
提供有关CarVolumeGroup
更改原因的附加信息。应用程序可以使用此信息提供额外的上下文来提醒用户采取行动或发出通知。例如, EXTRA_INFO_TRANSIENT_ATTENUATION_THERMAL
表示由于热过载而导致的主动瞬态衰减。如果用户尝试增加音量,该应用程序可以通知用户。
我们不会对ExtraInfos
强制实施任何流程。您可以自行决定根据ExtraInfos
确定流程。例如,如果由于EXTRA_INFO_TRANSIENT_ATTENUATION_DUCKED
导致衰减处于活动状态,您还可以选择最初淡出音量栏 UI 以防止用户更改音量。其他人可能会选择显示一个提示,表明闪避处于活动状态,并允许用户更改音量。
汽车音频框架依赖于 AudioControl HAL IAudioGainCallback
来提供建议的ExtraInfos
。要了解更多信息,请参阅音频增益回调。
CarVolumeGroupEvent
可扩展以满足汽车音频框架的未来需求。我们打算仅通过CarVolumeGroupEvent
支持新功能。我们强烈建议应用程序开发人员使用CarVolumeGroupEvent
来处理组音量和静音更改。
车卷组事件回调
Android 14 为特权和平台应用程序提供了新的回调,以注册CarVolumeGroupEvents
并获得通知。
要注册回调,请使用
CarAudioManager#registerCarVolumeGroupEventCallback()
要取消注册回调,请使用
CarAudioManager#unregisterCarVolumeGroupEventCallback()
如果应用程序注册了新的CarVolumeGroupEventCallback
和旧的CarVolumeCallback
,则事件CarVolumeGroupEventCallbacks
优先。汽车音频堆栈不再触发CarVolumeCallback
。这可以防止同一事件的同一应用程序重复触发。
我们强烈建议您使用CarVolumeGroupEventCallback
来管理组音量和静音更改。
音频增益回调
自 Android 13 起,AudioControl HAL 可以触发异步回调来管理因汽车音响系统更改而导致的音量级别更新。
哈尔API
音频控制@2.0 AIDL
AudioControl AIDL HAL 2.0 版本添加了以下 API:
应用程序编程接口 | 目的 |
---|---|
IAudioControl#registerGainCallback | 向 AudioControl HAL 注册IAudioGainCallback 的实例。 |
IAudioGainCallback#onAudioDeviceGainsChanged | 用于通知音频增益配置更改的异步回调。 |
AudioControl HAL 回调包括原因列表和相应的AudioGainConfigInfo
,其中包括:
- 区域 ID
- 设备端口地址
- 卷索引 > 索引可以是受限索引或更新索引。
原因大致可分为以下几类:
- 限制原因。音量和静音行为的瞬时变化。
- 更新原因。音量行为的永久改变。
限制类型
从AudioControl
HAL AIDL
V3
开始,以下是支持的限制类型:
- 沉默的
- 阻塞
- 局限性
- 衰减
主动限制 | 用户触发的音量变化 | 用户触发的静音切换 |
---|---|---|
沉默的 | ❌ | ❌(取消静音) ✔(静音) |
阻塞 | ❌ | ✔ |
局限性 | ❌(超过限制) ✔(低于限制) | ✔ |
衰减 | ✔ | ✔ |
限制之间的优先级为静音 > 阻止 > 限制 > 衰减。
静音限制
静音限制为:
-
Reasons.TCU_MUTE
-
Reasons.REMOTE_MUTE
汽车音响框架内部维护这两种静音状态:
用户静音。根据用户的请求(通过
CarAudioManager
或按键事件)进行切换。哈尔静音。根据通过
AudioGain
回调收到的静音限制进行切换。
对于“设置”应用程序等侦听器,卷组整体静音 ( CarVolumeGroupInfo.isMuted()
) 状态将基于是否启用上述任一静音。
启用 HAL 静音后,在限制期间,所有传入的音量更改和组取消静音请求都将被忽略。
交互案例:HAL 静音处于活动状态并且用户请求静音切换
当启用 HAL 静音且禁用用户静音时:
- 卷组整体静音状态更改为
true
。 - 将处理用户启用静音的请求。
- 原因:应始终尊重用户的静音请求,以保护用户的隐私。
当启用 HAL 静音且启用用户静音时:
卷组整体静音状态更改为
true
。用户提出的禁用静音的请求将
NOT
被处理。缓存用户静音状态保持启用状态。原因:只有在没有活动限制的情况下才会满足用户取消静音请求。
原因:取消静音缓存 用户静音可能会导致意外的声音爆炸,危及用户安全。如果在整个点火周期启用静音状态,这会降低用户对声级感知的意识,则尤其如此。
交互案例:HAL 静音启用和禁用,而用户静音没有变化
切换 HAL 静音将更改卷组的整体静音状态。但是,它不会直接更新用户静音状态。当禁用用户静音并收到启用 HAL 静音回调时:
- 卷组整体静音状态更改为
true
。 启用 HAL 静音时,
NOT
处理用户更改音量的请求。原因:静音时用户无法感知声音。允许音量改变可能会导致声音爆炸并危及用户安全。
原因:音量应用程序可以注册回调并自动触发取消静音 (CarAudioManager.setVolumeGroupMute(...,/* mute=*/ true,..)),无需用户干预(如果这是 OEM 的预期行为)。
当 HAL 静音被禁用且用户静音被禁用时:
卷组静音状态更改为
false
。原因:当静音状态频繁切换时,将静音状态设为粘性并请求用户取消静音可能会不必要地中断用户。
用户更改音量的请求将正常处理。
阻塞
阻止限制是:
-
Reasons.FORCED_MASTER_MUTE
-
Reasons.REMOTE_MUTE
-
Reasons.TCU_MUTE
。
当阻止限制处于活动状态时,用户请求:
- 不处理更改量。
- 切换静音已处理。
局限性
限制条件有:
-
Reasons.THERMAL_LIMITATION
-
Reasons.SUSPEND_EXIT_VOL_LIMITATION
当限制处于活动状态时,用户请求:
改变音量:
- 在限制内进行处理
- 以上限制不予处理
切换静音已处理。
衰减
衰减限制为:
-
Reasons.ADAS_DUCKING
-
Reasons.NAV_DUCKING
-
Reasons.PROJECTION_DUCKING
当衰减限制处于活动状态时,用户请求:
更改体积已处理。新的当前音量级别设置为衰减音量(而不是以前的音量)。未来的成交量变化都是从这个水平开始的。
切换静音已处理。
更新索引
以下内容被视为异步卷索引更新: Reasons.EXTERNAL_AMP_VOL_FEEDBACK
。
因此,AudioControl HAL 可以将卷组当前索引更新为指定索引。这主要用作音频系统对汽车音频框架的音量更改请求的反馈。索引更新还作为CarVolumeGroupEvent
回调与应用程序进行通信,以同步索引。
例子
用例:用户将卷索引更新为 30
用户使用 Volume 应用程序将音量指数更改为 30。
该索引被转换为音量增益并发送到音频 HAL。
Audio HAL
的供应商实现接收新的音量增益并更新音频系统(如外部放大器)。音频系统响应音量级别仅更新为索引 15(出于 Android 未知的原因)。
AudioControl HAL
触发器的供应商实现:IAudioGainCallback.onAudioDeviceGainsChanged(EXTERNAL_AMP_VOL_FEEDBACK, {..., 15 /* New index */})
汽车音频服务使用来自回调的新索引,该索引用于持久性和对音量应用程序的回调。用户请求的索引是30。然而,音频系统异步反馈将索引更新为15。
用例:退出挂起后首次播放音频
挂起前的音量指数设置为高水平 95(范围:[0-99])。
Android 进入挂起状态。
一旦 Android 存在暂停(例如恢复):
供应商
Audio HAL/AudioControl HAL
将安全索引 30 应用于本地音频系统。供应商
AudioControl HAL
还会触发安全索引的回调:
IAudioGainCallback.onAudioDeviceGainsChanged(SUSPEND_EXIT_VOL_LIMITATION, {..., 30 /* safe index */})
汽车音频服务使用来自用于持久性的回调的新索引以及它自己对同步索引的音量应用程序的回调。挂起前的音量索引为 95。但是,恢复后,该索引由
AudioControl HAL
实现者设置为安全音量级别 30。
动态卷配置
对于此功能,我们考虑以下主要用例:
车辆下线 (EOL) 配置。
汽车制造商更愿意在停产时根据车辆音频系统设置更新音量配置。通常,这是一种旁加载,无需更新 Android SW 映像。
汽车制造商可能需要在服务计划期间更新卷配置。
运行时配置。汽车音频系统支持外部放大器配置,这些 ECU 可以托管在启动期间查询的音量范围配置。
按需配置。旨在支持对基于需求的音频功能不断增长的需求,其中用户在一段时间内订阅增强的信号处理。新的卷范围配置在订阅期间有效。
设计
动态卷配置分三个阶段实现:
发现。供应商 AudioControl HAL 实现通过供应商拥有的自定义 IPC 机制发现新的音量范围更新。
一旦发现,就会通过
AudioControl::IModuleChangeCallback
生成回调。更新。汽车音频堆栈使用新的音量范围更新音量组状态。
交易量范围更新后,我们会努力保持相同的交易量水平。然而,如果该指数超出范围,则当前成交量指数将被设置为安全值。例如,回调期间供应商提供的默认级别。
打回来。
音量组范围更新后,汽车音频堆栈会触发对通过
CarVolumeGroupEventCallback
注册的应用程序的回调。CarVolumeGroupEvent
携带更新的CarVolumeGroupInfo
、事件类型(更改内容)和额外信息(更改原因)。
图 1.动态卷配置。
哈尔API
音频控制@3.0 AIDL
AudioControl AIDL HAL 3.0 版本引入了以下 API:
应用程序编程接口 | |
---|---|
IAudioControl#setModuleChangeCallback | 使用 AudioControl HAL 设置 IModuleChangeCallback 的实例。 |
IAudioControl#clearModuleChangeCallback | 清除先前使用 AudioControl HAL 设置的 IModuleChangeCallback 实例。 |
IModuleChangeCallback#onAudioPortsChanged | 用于通知 AudioPort 更改的回调 |
顺序
动态卷配置的时序图如下所示。
图 2.动态卷配置的序列图。
关键方面
要优化此功能,请考虑以下事项。
作为回调的一部分提供的音频端口必须与汽车总线定义匹配:
- 设备端口。
IN_DEVICE
、OUT_DEVICE
- 联系。
BUS
- 地址。在 Audio HAL 定义中定义
- 增益模式。
JOINT
- 设备端口。
供应商必须在音频 HAL 策略中定义音量范围定义的超集,并使用回调针对车辆变体对其进行自定义。有关详细信息,请参阅
IModuleChangeCallbac
AIDL 定义。当多个音频总线属于同一音量组时,每个音频总线必须具有相同的音量范围定义。如果不这样做,则会导致汽车音频框架拒绝新的音量范围定义。