2. 车载音频系统概述

车载音频与手机音频最大的差别,不在于“能不能出声”,而在于多音源并发、多区域独立、多优先级抢占。一辆车在同一时刻可能同时存在导航播报、媒体播放、蓝牙电话、倒车雷达提示音、ADAS告警音、语音助手唤醒音等多种声音需求,它们共享有限的扬声器与功放通道,必须由一套明确的策略决定“谁出声、谁静音、谁压低音量”。理解这套策略的载体,就是车载音频场景、音频链路与Android音频架构分层。

一、车载音频场景:从“一路声音”到“多区多源”

车载音频场景可以按“使用情境”和“声音区域”两个维度理解。使用情境决定了声音的优先级与打断关系,声音区域决定了声音从哪个扬声器输出。

  • 媒体场景:音乐、视频、有声书,通常优先级最低,可被其他声音压低(Ducking)或暂停。
  • 导航场景:TTS播报,优先级中等,通常要求压低媒体而不是完全打断。
  • 电话场景:蓝牙通话、紧急呼叫,优先级高,要求媒体静音、麦克风上行。
  • 安全告警场景:倒车雷达、车道偏离、碰撞预警,优先级最高,必须强制输出且不可被静音。
  • 语音交互场景:语音助手唤醒与识别,需要与媒体、导航协调麦克风与扬声器资源。

声音区域则把车内划分为主驾区、副驾区、后排区、全车区等。Android从10开始引入AudioZone与AudioDeviceGroup概念,允许不同区域播放不同内容,例如副驾看视频、主驾听导航,互不干扰。这正是车载音频HAL需要重点支撑的能力。

车载音频的核心矛盾:有限的物理通道(功放路数、DSP算力、总线带宽)与无限并发的逻辑需求(多App、多场景、多区域)之间的冲突。HAL就是解决这一冲突的“翻译层”与“执行层”。

二、音频链路:从App到扬声器的完整通路

一条完整的车载音频链路,通常包含以下环节:

  1. 应用层:MediaPlayer、ExoPlayer、AudioTrack、TTS引擎等产生PCM数据。
  2. 框架层:AudioService、AudioPolicyService、AudioFlinger负责策略决策、混音、重采样、音量管理。
  3. HAL层:Audio HAL接收框架下发的流与设备配置,转换为硬件可识别的操作。
  4. 驱动与DSP层:ALSA SoC驱动、DSP固件完成通道路由、均衡、延时、分频。
  5. 功放与扬声器:Class-D功放驱动车门扬声器、中置、低音炮等。

在Android 8.0之后,Audio HAL从传统的audio_hw_device逐步演进为HIDL/AIDL接口,典型接口包括IDevice、IStreamOut、IStreamIn。车载场景还会扩展IAudioControl用于车辆专用控制,例如getBusForContext()把Android音频上下文映射到具体的物理总线。

// 典型车载 AudioControl HAL 接口片段
interface IAudioControl {
  getBusForContext(Context ctx) generates (BusAddress bus);
  setBalance(float balance);
  setFade(float fade);
  // ctx: MEDIA / NAVIGATION / PHONE / ALARM / NOTIFICATION ...
};

三、Android音频架构分层与HAL定位

Android音频架构自上而下可分为四层,HAL位于框架与内核之间,是承上启下的关键层。

层级主要组件职责
应用层MediaPlayer、AudioTrack、TTS、语音助手产生音频数据,声明使用场景
框架层AudioService、AudioPolicyService、AudioFlinger策略决策、混音、音量、设备管理
HAL层Audio HAL、AudioControl HAL流管理、设备路由、总线映射、参数下发
内核与硬件层ALSA SoC、DSP、功放、扬声器实际音频传输、处理与发声

HAL的定位可以概括为三点:第一,屏蔽硬件差异,让上层不关心具体是AKM还是TI的DSP;第二,执行音频策略,把框架的“导航压低媒体”翻译为具体的总线增益与通道切换;第三,管理并发与路由,决定多路流如何混合、送到哪个区域、走哪条物理链路。

判断一个车载音频问题是否属于HAL范畴,可以问三个问题:是否涉及物理通道选择?是否涉及多流并发路由?是否涉及DSP/功放参数下发?只要命中一个,大概率需要从HAL层排查。

四、案例与常见误区

案例:导航播报时媒体不压低。现象是导航TTS响起,媒体音量不变。排查路径:先确认AudioPolicy中NAVIGATION与MEDIA的交互规则是否配置了Ducking;再检查AudioControl HAL的getBusForContext()是否把两者映射到了同一条总线;最后确认DSP是否收到了压低增益参数。若三者中任一环节缺失,都会表现为“不压低”。

误区一:把HAL当成“驱动”。HAL不是驱动,它不直接操作寄存器,而是通过标准接口向驱动/DSP下发配置。把HAL当驱动写,会导致与框架接口不兼容。

误区二:忽略区域与总线的映射关系。很多“声音从错误扬声器出来”的问题,根源是AudioDeviceGroup与物理总线映射错位,而不是混音错误。

误区三:认为优先级由App决定。App只能声明Usage,真正的优先级与打断关系由AudioPolicy与HAL共同执行,App无法绕过安全告警的强制输出。

车载音频调试中,务必先画清楚“场景—流—总线—区域—扬声器”的映射表,再动手改代码。缺少这张表,任何参数调整都可能是盲调。
车载音频链路与 Android 音频架构分层 应用层 MediaPlayer AudioTrack TTS / 导航 语音助手 蓝牙电话 框架层 AudioService 音量 / 焦点 AudioPolicyService 策略 / 路由决策 AudioFlinger 混音 / 重采样 AudioDeviceGroup 区域 / 设备组 HAL 层(承上启下) Audio HAL IDevice / IStreamOut AudioControl HAL getBusForContext() 流管理 并发 / 路由 参数下发 增益 / 通道 内核与硬件层 ALSA SoC PCM / I2S DSP EQ / 延时 / 分频 Class-D 功放 通道驱动 扬声器 / 区域 主驾 / 副驾 / 后排 / 低音 典型并发场景 导航播报 + 媒体播放 → 媒体 Ducking 蓝牙电话 + 媒体 → 媒体静音,麦克风上行 倒车雷达 + 任意 → 强制输出,不可静音 副驾视频 + 主驾导航 → 区域隔离输出 HAL 定位

把这张图记在脑子里,后续无论是分析AudioPolicy配置、调试DSP路由,还是编写AudioControl HAL,都能快速定位问题所在的层级。车载音频工程的难点,从来不是单点技术,而是场景、链路、分层三者之间的映射关系是否清晰、一致、可验证。