在车辆环境中,可能存在多个应用或者服务需要同时或者交替播放音频,如导航、音乐、语音助手等。音频焦点机制允许系统协调这些音频流,确保在某一时刻只有一个或几个应用能够播放音频,从而避免声音混杂和冲突。
1.2.1 当某个应用或者服务需要播放声音的时候,它首先需要向系统请求音频焦点。系统会根据当前焦点的持有情况和应用请求的焦点类型,来决定是否授予音频焦点。如果应用获取到焦点,就可以开始播放音频内容;如果请求焦点被拒绝,则不能播放。
1.2.2 有音频播放需求的应用或者服务,在发起音频请求焦点前,应该去注册监听系统的音频焦点变化。在收到系统的系统监听变化的回调后,做出相应的动作,如开始播放、暂停播放或者释放焦点。
1.2.3 当音频播放完成后,应用需要去释放之前已经申请到的音频焦点,并注销音频变化监听器。
1.2.4 音频焦点机制是一套协调各个声源应用发声的规则,但是并不能强制去控制各个应用是否发声。因此,如果某些声源应用不遵守焦点规则,就会出现播放混乱的情况。比如在播放音乐的时候触发语音,正常情况需要暂停或者压低媒体音量,如果语音服务或者多媒体应用没有遵守焦点机制,就会出现两个声音混合听不清楚。
AUDIOFOCUS_GAIN:请求独占的长时间的音频焦点,例如音乐播放的焦点请求。
AUDIOFOCUS_GAIN_TRANSIENT:请求短暂的独占的音频焦点,比如短暂的语音交互。
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:请求短暂的播报,但是允许其他音源压低并继续播放,比如播放视频的时候有新消息提示或者地图播报。
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:请求短暂的独占的音频焦点,且不允许其他音源有任何声音,与 GAIN_TRANSIENT 类似,不常用。
焦点变化回调的类型包括:
音频路由指的是系统如何将音频流从一个或多个音频源(如音乐播放器、视频播放器、导航应用等)发送到哪些音频输出设备(如扬声器、耳机、蓝牙设备等)的过程。
大致的流程如下:
首先是各种音源应用产生逻辑音频流,然后在Audio Flinger中根据音频路由策略去做声音的混合,得到物理音频流,并通过HAL层输出到Android系统外部。在Android系统之外,会有一个外部混音器,用于接收Android的物理音频流,并以合适的方式将这些音频流和车辆外部的声音做混合,并将混音结果路由到合适的音响设备。
总体的结构图如下:
Android系统中去设置音量值的时候,会传入一个音频流类型的参数,以便对不同的类型做的音量的控制和记忆。音频流类型定义如下:
public static final String[] STREAM_NAMES = new String[] {
"STREAM_VOICE_CALL", //电话
"STREAM_SYSTEM", //系统
"STREAM_RING", //铃声
"STREAM_MUSIC", //音乐
"STREAM_ALARM", //闹钟
"STREAM_NOTIFICATION",//通知
"STREAM_BLUETOOTH_SCO",//蓝牙
"STREAM_SYSTEM_ENFORCED",//系统强制
"STREAM_DTMF", //双音多频(拨号音)
"STREAM_TTS", //语音播报
};
但在底层的音量控制中,会对以上的音频类型做一次映射,根据系统指定的需求,把这些音频类型做一次分类,有些音频流类型复用同一个存储值。
通常的映射定义如下:
private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC // STREAM_TTS
};
可以看到系统声音、铃声、提示音、系统强制音、拨号音都共用一个类型 STREAM_RING 的音量值 ;音乐媒体和语音公用一个类型 STREAM_MUSIC 的音量值 。比如当调节了音乐媒体的音量值之后,语音播报的声音也会变大。当然,这种映射关系可以根据项目的需求修改,如果语音播报的声音不想跟着媒体音一起调节,那么也可以做单独的定义。
AAOS提供了CarAudioService,它主要提供了两个能力,一是增加了多音区分组的概念,另一个是支持硬件放大器调节音量,而非使用传统的AudioFlinger软件调节。
由于座舱的环境变更越来越复杂,已经出现了中控主音区、后排音区、头枕音区等多个音区,所以最新的AAOS是通过zone(音区)+group(分组)的方式去组织,如下:
// device/generic/car/emulator/audio/car_audio_configuration.xml
<zone name="primary zone" isPrimary="true" occupantZoneId="0">
<zoneConfigs>
<zoneConfig name="primary zone config 0" isDefault="true">
<volumeGroups>
<group maxActivationVolumePercentage="90" activationConfig="activation_volume_on_boot_config">
<device address="bus0_media_out">
<context context="music"/>
<context context="announcement"/>
</device>
<device address="bus6_notification_out">
<context context="notification"/>
</device>
</group>
<group minActivationVolumePercentage="20" activationConfig="activation_volume_on_source_changed_config">
<device address="bus1_navigation_out">
<context context="navigation"/>
</device>
<device address="bus2_voice_command_out">
<context context="voice_command"/>
</device>
</group>
<group minActivationVolumePercentage="20" activationConfig="activation_volume_on_playback_changed_config">
<device address="bus4_call_out">
<context context="call"/>
</device>
<device address="bus3_call_ring_out">
<context context="call_ring"/>
</device>
</group>
</volumeGroups>
</zoneConfig>
</zoneConfigs>
</zone>
<zone name="rear seat zone 1" audioZoneId="1">
<zoneConfigs>
<zoneConfig name="rear seat zone 1 config 0" isDefault="true">
<volumeGroups>
<group>
<device address="bus100_audio_zone_1">
<context context="music"/>
</device>
</group>
<group>
<device address="bus101_audio_zone_1">
<context context="navigation"/>
<context context="voice_command"/>
<context context="call_ring"/>
<context context="call"/>
<context context="alarm"/>
<context context="notification"/>
<context context="system_sound"/>
<context context="emergency"/>
<context context="safety"/>
<context context="vehicle_status"/>
<context context="announcement"/>
</device>
</group>
</volumeGroups>
</zoneConfig>
</zoneConfigs>
</zone>
在同一个组里面的的音频上下文,音量设置会同步,比如上面的music、announcement和notification是一组,改变了其中一种类型的音量,两外的也会跟着改变。当然,OEM厂商也可以去自定义自己的分组。
以上分组配置文件中还有一个device address的字段,这是代表音频动态路由的指定设备。在AAOS中,推荐使用硬件混音器去做音量的调节,即不使用AudioFlinger的软件调节方式。想要达到这种目的,需要在源码frameworks/base/core/res/res/values/config.xml的配置文件中,去修改config_useFixedVolume为ture,表示音量的调节在软件层面是固定的,此配置默认是false。
<!-- Flag indicating that the media framework should not allow changes or mute on any
stream or global volumes. -->
<bool name="config_useFixedVolume">false</bool>
同时,禁止了软件的音量调节后,还需要开启动态路由的配置,位置在packages/services/Car/service/res/values/config.xml,修改audioUseDynamicRouting字段为true,开启动态路由。此字段的默认配置是false,需要在源代码中打开:
<!-- Configuration to enable usage of dynamic audio routing. If this is set to false,
dynamic audio routing is disabled and audio works in legacy mode. It may be useful
during initial development where audio hal does not support bus based addressing yet. -->
<bool name="audioUseDynamicRouting">false</bool>