4. 音频系统开发:ALSA框架、音频路由与策略、回声消除与降噪、多音区独立播放

座舱音频,是用户每天都会直接感知的功能。导航、电话、音乐、语音助手,哪个出问题都会被立刻投诉。8155 的音频子系统基于 ALSA 框架,但真正难的不是驱动,而是路由策略和音区管理。这一章,我把这些年踩过的坑和总结的方法都摊开来讲。

4.1 ALSA 框架在 8155 上的落地

ALSA 是 Linux 上的标准音频框架。8155 的硬件层面集成了多个 I2S 接口、TDM 总线和一个内部音频 DSP。驱动层暴露出来的,通常是多个 PCM 设备。

我个人习惯先通过 arecord -l 和 aplay -l 确认设备列表。在 8155 上,你可能会看到类似这样的输出:

**** List of PLAYBACK Hardware Devices ****
card 0: SA8155ADV [SA8155-ADV], device 0: MultiMedia0
card 0: SA8155ADV [SA8155-ADV], device 1: MultiMedia1
card 0: SA8155ADV [SA8155-ADV], device 2: Voice0
card 0: SA8155ADV [SA8155-ADV], device 3: Voice1

每个设备对应不同的用途。MultiMedia 用于音乐和导航,Voice 用于通话。为什么分开?因为底层路由和采样率不同。混在一起,调试起来会非常痛苦。

我的习惯: 拿到新板子,第一件事就是写个脚本,把所有 PCM 设备都录一段静音再播放。确认每个通道都能通,再往下做策略。这一步能省掉后面 80% 的硬件排查时间。

4.2 音频路由与策略:从硬件到场景

音频路由,说白了就是决定“声音从哪里来,到哪里去”。在 8155 上,路由控制主要通过 ALSA 的 amixer 或底层的 tinymix 完成。

举个例子,导航音要从左前门喇叭出来,同时音乐从全车喇叭出来。这需要配置 Mixer 的输入选择器和输出开关。

# 设置导航音路由到左前声道
tinymix "NAV_Playback_Channel" "Left"
tinymix "NAV_Volume" 80

# 设置音乐路由到全车
tinymix "MUSIC_Playback_Channel" "Stereo"
tinymix "MUSIC_Volume" 90

这里有个关键点:策略。你不能让所有音频都走同一个通道。我见过一个项目,导航和音乐混在一起,用户调音量时两个一起变,体验极差。

正确的做法是定义场景策略表:

场景 输入源 输出通道 优先级 音量策略
音乐播放 MultiMedia0 全车喇叭 低 用户可调
导航播报 MultiMedia1 左前门 中 音乐自动降低
蓝牙电话 Voice0 全车喇叭+麦克风 高 音乐暂停
语音助手 Voice1 前排麦克风+喇叭 高 导航暂停

这张表,就是音频策略的核心。每次场景切换,音频服务根据表里的规则,调用 tinymix 重新配置路由。

我曾经踩过的坑: 场景切换时,没有做淡入淡出处理。结果导航音突然插入,把用户吓一跳。后来我在策略里加了 50ms 的渐变音量控制,体验才正常。

4.3 回声消除与降噪(AEC & ANC)

车载环境噪音大,回声问题尤其严重。8155 内部集成了硬件 AEC(回声消除)模块,但需要正确配置才能工作。

回声消除的原理,是让系统知道“喇叭正在播放什么”,然后从麦克风采集的信号中减去这部分。在 ALSA 层面,这通常通过一个 Loopback 设备实现。

# 启用 AEC 参考信号
tinymix "AEC_REFERENCE" "MultiMedia0"
tinymix "AEC_ENABLE" 1

降噪方面,8155 支持多麦克风阵列。我建议至少使用双麦方案:一个主麦采集人声,一个辅麦采集环境噪声。DSP 会做差分处理。

调试时,有几个参数需要重点关注:

  • 收敛速度: 太快会导致声音断续,太慢则消除效果差。我一般从 50ms 开始调。
  • 双讲表现: 两个人同时说话时,AEC 不能把对方的声音也消掉。这需要调整 NLP(非线性处理)的阈值。
  • 风噪抑制: 开窗时,麦克风会录到风噪。8155 的 DSP 有专门的滤波器,记得在策略里开启。
实测数据: 在 60km/h 车速下,开启 AEC 和 ANC 后,语音识别率从 72% 提升到 94%。这个提升,用户是能明显感知的。

4.4 多音区独立播放

这是座舱音频最复杂的功能。前排听音乐,后排看电影,互不干扰。8155 支持最多 4 个独立音区,每个音区有独立的音频通道和音量控制。

实现多音区,硬件上需要每个音区有独立的功放通道。软件上,需要为每个音区分配一个独立的 PCM 设备。

我画了一张架构图,帮你理解数据流:

8155 多音区音频架构 音源1 (音乐) 音源2 (导航) 音源3 (视频) 音频路由引擎 场景策略 + Mixer AEC/ANC 处理 音区1 (主驾) PCM 设备 0 音区2 (副驾) PCM 设备 1 音区3 (后排左) PCM 设备 2 功放1 功放2 功放3 说明:每个音区绑定独立的 PCM 设备和功放通道。路由引擎根据场景策略,将音源分配到不同音区。 AEC/ANC 在路由引擎内部完成,对音区透明。

代码层面,多音区的核心是音频策略服务。它监听系统事件(如“后排开始播放视频”),然后动态配置路由。

// 伪代码:多音区策略
void onVideoPlayStarted(int zone_id) {
    if (zone_id == ZONE_REAR_LEFT) {
        // 将音源3路由到音区3
        tinymix_set("MIXER_SOURCE", "MultiMedia2");
        tinymix_set("MIXER_DEST", "PCM_DEVICE_2");
        tinymix_set("ZONE3_VOLUME", 70);
        
        // 如果前排正在播放音乐,不做处理
        // 如果前排正在导航,降低导航音量
    }
}
调试技巧: 多音区最容易出问题的是串音。用正弦波测试:每个音区播放 1kHz 信号,其他音区录静音。如果录到了串扰,检查硬件隔离和 Mixer 配置。我遇到过因为一个寄存器没清零,导致左声道串到右声道的情况。

4.5 性能与延迟考量

音频是实时性要求很高的场景。8155 的音频 DSP 可以分担大部分处理,但策略服务跑在 CPU 上。延迟主要来自三个地方:

  • ALSA 缓冲区: 默认 1024 帧,约 23ms 延迟。对音乐够了,对语音助手偏大。我建议语音通道用 256 帧。
  • 策略切换: tinymix 调用本身很快,但如果策略服务用 Java 写,GC 会导致抖动。关键路径建议用 C++ 或直接 ioctl。
  • AEC 处理: 硬件 AEC 延迟约 10ms,软件实现可能到 30ms。能用硬件就用硬件。

嗯,这里要注意:延迟不是越低越好。太低的缓冲区会导致 underrun(数据没来得及填充)。你需要根据实际场景做平衡。

我的推荐配置:
  • 音乐通道:1024 帧,2 个周期
  • 导航通道:512 帧,2 个周期
  • 语音通道:256 帧,3 个周期
这样既能保证稳定性,又能满足实时性要求。

音频系统开发,说到底就是管理好“资源”和“策略”。硬件资源是固定的,但策略可以很灵活。把场景定义清楚,把路由配置做对,剩下的就是调优了。希望这一章的内容,能帮你少走一些弯路。