12、音频策略定制实战:修改默认路由策略、添加自定义音频设备、适配特殊硬件场景。

好,咱们终于到了动手环节。前面讲了那么多策略文件的结构、路由规则、设备枚举,说实话,光看不练容易忘。今天这一章,我就带大家走一遍真正的定制流程。我会用三个实战场景,把修改默认路由、添加自定义设备、适配特殊硬件这几个核心操作串起来。

嗯,先说说我个人的习惯。每次拿到一个新平台,我第一件事不是看代码,而是先拉一份完整的 audio_policy_configuration.xml 出来,然后用我写的一个小脚本把它可视化。你想想看,几百行的 XML,光靠肉眼找设备映射关系,太容易漏了。

12.1 修改默认路由策略:让系统优先走你的设备

默认路由策略,说白了就是系统在不知道选谁的时候,会选谁。比如你插上耳机,系统应该切到耳机;拔掉耳机,系统应该切回扬声器。这个逻辑在 audio_policy_configuration.xml 里是通过 route 标签和 mixPortprofile 来控制的。

我遇到过一个问题:某款平板电脑,外接了一个 USB-C 转 3.5mm 的转接头,系统死活不认。插上去之后,声音还是从扬声器出来。后来一查,是默认路由里 USB 设备的优先级比扬声器低。

怎么改?看这个例子:

<!-- 修改前:USB 设备排在扬声器后面 -->
<route type="mix" sink="speaker">
    <source address="0">usb_device</source>
</route>

<!-- 修改后:把 USB 设备提到前面,并增加一个专属路由 -->
<route type="mix" sink="usb_headset">
    <source address="0">usb_device</source>
    <source address="1">speaker</source>
</route>

这里的关键是 sinksource 的对应关系。系统在路由选择时,会按照 route 列表的顺序去匹配。你把 USB 设备的路由写在前面,系统就会优先尝试它。

小技巧: 如果你想让某个设备成为“默认输出”,可以在 audio_policy_configuration.xmlglobalConfiguration 里设置 defaultOutputDevice。比如 <globalConfiguration defaultOutputDevice="speaker"/>

12.2 添加自定义音频设备:让系统认识你的硬件

有时候,硬件团队会搞一些“非标”设备。比如一个通过 SPI 接口连接的麦克风阵列,或者一个定制的 DSP 音频处理模块。这些设备在标准的 ALSA 驱动里可能没有对应的 carddevice 号,所以系统根本不知道它们的存在。

这时候,你就得手动在策略文件里“注册”它们。我建议分三步走:

  1. 定义设备描述:在 audio_policy_configuration.xmldevicePorts 里添加一个新设备。
  2. 定义混音端口:在 mixPorts 里添加对应的 HAL 接口。
  3. 建立路由:把设备和混音端口用 route 连起来。

举个例子,假设我们要添加一个名为 custom_mic_array 的输入设备:

<!-- 第一步:定义设备端口 -->
<devicePort tagName="Custom Mic Array" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
             samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</devicePort>

<!-- 第二步:定义混音端口 -->
<mixPort name="custom_mic input" role="sink">
    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
             samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>

<!-- 第三步:建立路由 -->
<route type="mix" sink="custom_mic input">
    <source address="0">Custom Mic Array</source>
</route>

嗯,这里要注意一点。设备端口的 type 字段,必须用系统已经定义的 AUDIO_DEVICE_* 常量。你不能自己发明一个 AUDIO_DEVICE_IN_CUSTOM,因为 HAL 层不认识它。我一般会找一个功能最接近的现有类型,比如麦克风就用 AUDIO_DEVICE_IN_BUILTIN_MIC

警告: 添加自定义设备后,一定要同步修改 HAL 层的 adev_get_device_address 函数。否则系统虽然知道有这个设备,但不知道去哪里找它的驱动。我曾经因为这个疏忽,浪费了整整两天调试时间。

12.3 适配特殊硬件场景:一个真实的案例

我记得有一次,我们做一款带 AI 降噪功能的会议音箱。硬件上有一个独立的 DSP 芯片,专门处理麦克风阵列的波束成形。这个 DSP 在 Linux 下被识别为一个单独的声卡,但它的采样率是 16kHz,而主声卡是 48kHz。

问题来了:系统默认会把所有音频流都路由到主声卡,但降噪功能必须走 DSP 声卡。而且,两个声卡之间还要做重采样。

我的解决方案是这样的:

  1. 创建两个独立的音频策略模块:一个给主声卡,一个给 DSP 声卡。
  2. 在策略文件里定义两个 audioPolicyConfig,分别对应不同的 mixPorts
  3. globalConfiguration 里的 deviceCategory 来区分场景:当检测到“会议模式”时,强制路由到 DSP 模块。

核心代码片段如下:

<!-- 定义两个策略模块 -->
<audioPolicyConfig version="1.0">
    <!-- 主声卡模块 -->
    <module name="primary" halVersion="2.0">
        <attachedDevices>
            <item>Speaker</item>
            <item>Built-In Mic</item>
        </attachedDevices>
        <!-- ... 其他配置 ... -->
    </module>

    <!-- DSP 声卡模块 -->
    <module name="dsp" halVersion="2.0">
        <attachedDevices>
            <item>DSP Mic Array</item>
        </attachedDevices>
        <!-- 注意:这里设置采样率为 16kHz -->
        <mixPort name="dsp input" role="sink">
            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                     samplingRates="16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
        </mixPort>
    </module>
</audioPolicyConfig>

然后,在 Java 层的 AudioManager 里,我写了一个监听器,当应用设置音频属性为 AudioAttributes.USAGE_VOICE_COMMUNICATION 时,动态调用 setPreferredDeviceForStrategy 来切换路由。

核心思路: 特殊硬件场景的适配,本质上就是“策略 + 动态路由”。策略文件定义好静态的拓扑结构,而运行时通过 API 来切换。不要把所有的逻辑都硬编码在 XML 里,那样太死板了。

12.4 知识体系图:音频策略定制的核心逻辑

下面这张图,是我自己总结的音频策略定制流程。每次做新项目,我都会对着它过一遍,确保没有遗漏。

音频策略定制核心流程 1. 需求分析 硬件特性 / 使用场景 2. 策略文件修改 devicePorts / mixPorts / routes 3. HAL 层适配 设备地址 / 采样率匹配 4. 动态路由逻辑 AudioManager API / 策略切换 5. 验证与调试 dumpsys / logcat / 音频测试 迭代优化 每个步骤都可能需要多次迭代,尤其是 HAL 层适配和动态路由逻辑

12.5 避坑指南:我踩过的几个坑

最后,分享几个实战中容易出问题的地方。这些都是我亲身经历过的,希望能帮你少走弯路。

  • 设备地址写错devicePorts 里的 address 字段,必须和 HAL 层 adev_get_device_address 返回的值完全一致。大小写、空格都不能错。我曾经因为一个下划线写成了连字符,查了三个小时。
  • 采样率不匹配:如果自定义设备的采样率和系统默认的不一样,一定要在 mixPortprofile 里明确指定。否则系统会用默认的 48kHz 去驱动你的 16kHz 设备,出来的声音全是噪声。
  • 忘记重启 audioserver:修改策略文件后,只重启应用是不够的。必须 adb shell restart audioserver 或者直接重启设备。嗯,这个我犯过不止一次。
  • 路由冲突:如果你定义了多个 route 指向同一个 sink,系统会按照文件里的顺序选择第一个。但如果你在运行时又通过 API 设置了 setPreferredDevice,那就会产生冲突。我的建议是:要么全用静态路由,要么全用动态 API,不要混用。
调试利器:adb shell dumpsys media.audio_policy 可以实时查看当前的路由状态和设备列表。我每次改完策略文件,第一件事就是跑这个命令,确认设备已经被正确加载。

好了,这一章的内容就到这里。音频策略定制,说白了就是“定义设备、建立路由、动态切换”这三板斧。但每一板斧里都有很多细节,需要你在实际项目中慢慢体会。记住,不要怕改错,改错了大不了重启 audioserver 嘛。

公众号:蓝海资料掘金营,微信 deep3321