12、音频处理:回声消除、降噪算法、自动增益控制
各位同学,今天我们来聊聊移动端 WebRTC 里最让人头疼,也最见功力的三个模块:回声消除(AEC)、降噪(NS) 和 自动增益控制(AGC)。
说实话,这三个东西在 PC 端可能还好,一放到移动端,各种奇葩问题就全冒出来了。我自己在项目里踩过的坑,十个有八个跟音频处理有关。你想想看,用户那边环境嘈杂、手机喇叭贴着麦克风、网络一卡一卡的……这些场景下,如果音频处理没做好,通话体验直接归零。
好,我们一个一个来拆解。
12.1 回声消除(AEC)—— 最核心的硬骨头
回声是怎么产生的?说白了就是扬声器放出来的声音,又被麦克风给收进去了。然后远端的人就听到了自己的回声,体验极差。
WebRTC 里内置了一套非常成熟的 AEC 算法,叫 AEC3(Acoustic Echo Canceller 3)。它的核心思路是:
- 自适应滤波:估计扬声器到麦克风的回声路径。
- 双讲检测:判断当前是单讲(只有远端说话)还是双讲(双方都在说)。
- 非线性处理:对残余回声做最后的压制。
嗯,这里要注意:移动端的 AEC 和 PC 端有个很大的不同——延迟抖动。移动端的音频采集和播放路径更长,延迟不稳定,AEC 很容易失效。
核心经验: 在移动端,AEC 的参考信号延迟必须精确控制在 ±5ms 以内。否则回声消除效果会大打折扣。
我曾经在一个项目中,发现回声总是消不干净。排查了两天,最后发现是音频采集线程和播放线程的时钟不同步导致的。解决办法很简单:统一使用同一个音频时钟源。
12.1.1 AEC 的启用与配置
在 WebRTC 中,AEC 默认是开启的。但移动端我们通常需要手动调整一些参数:
// 获取音频处理模块
auto audio_processing = webrtc::AudioProcessingBuilder().Create();
// 配置 AEC
webrtc::AudioProcessing::Config config;
config.echo_canceller.enabled = true;
config.echo_canceller.mobile_mode = true; // 移动端模式
audio_processing->ApplyConfig(config);
// 处理音频数据
audio_processing->ProcessStream(audio_frame);
注意那个 mobile_mode 参数。我建议你在移动端一定要开启它。这个模式针对移动端的短延迟、高抖动做了优化。
小技巧: 如果发现回声消除过度,导致远端声音失真,可以尝试降低 echo_canceller.effectiveness 的强度值。默认是 1.0,可以调到 0.8 试试。
12.2 降噪算法(NS)—— 让声音更干净
降噪,说白了就是把背景噪音去掉,只保留人声。WebRTC 里内置了两种降噪模式:频谱减法 和 神经网络降噪。
我个人习惯在移动端使用 频谱减法,因为它的计算量小,功耗低。但如果你对降噪效果要求很高,比如在嘈杂的商场或地铁里,那还是得上神经网络降噪。
12.2.1 降噪等级的选择
WebRTC 提供了四个降噪等级:
| 等级 | 枚举值 | 适用场景 |
|---|---|---|
| 低 | kLow | 安静环境,如办公室 |
| 中 | kModerate | 一般室内,有轻微噪音 |
| 高 | kHigh | 户外、街道等中等噪音 |
| 极高 | kVeryHigh | 极端嘈杂环境 |
我建议你根据当前设备的麦克风信噪比动态调整。比如:
// 动态调整降噪等级
if (snr > 20) {
config.noise_suppression.level = kModerate;
} else if (snr > 10) {
config.noise_suppression.level = kHigh;
} else {
config.noise_suppression.level = kVeryHigh;
}
警告: 降噪等级不是越高越好。等级太高会导致人声失真,听起来像「罐子里的声音」。我曾经在项目里把降噪开到最高,结果用户反馈说「声音像机器人」。后来调低了一档,效果反而更好。
12.3 自动增益控制(AGC)—— 让音量刚刚好
AGC 的作用很简单:自动调整麦克风的输入音量。用户离麦克风远了,自动放大;离近了,自动减小。
WebRTC 的 AGC 有两种模式:
- 模拟 AGC:直接控制硬件麦克风的增益。
- 数字 AGC:在软件层面调整音频信号的幅度。
在移动端,我强烈建议使用 数字 AGC。为什么?因为模拟 AGC 需要硬件支持,不同手机的麦克风驱动差异很大,很容易出问题。
12.3.1 AGC 的配置要点
// 配置 AGC
config.gain_controller1.enabled = true;
config.gain_controller1.mode = kAdaptiveDigital; // 数字自适应模式
config.gain_controller1.target_level_dbfs = -3; // 目标电平 -3dBFS
config.gain_controller1.compression_gain_db = 9; // 压缩增益 9dB
这里有个坑:target_level_dbfs 不要设得太高。我见过有人设成 0dBFS,结果稍微大点声就削波了。建议保持在 -3dBFS 到 -6dBFS 之间。
避坑指南: 我曾经在适配某款国产手机时,发现 AGC 完全不起作用。排查后发现是手机的音频 HAL 层对增益控制做了特殊处理。解决办法是:在应用层先做一次音量归一化,再交给 WebRTC 的 AGC 处理。
12.4 三者的协同工作
这三个模块不是独立工作的。它们之间有严格的顺序:
- 先做 AEC(回声消除)
- 再做 NS(降噪)
- 最后做 AGC(自动增益控制)
为什么是这个顺序?因为如果先做 AGC,会把回声也放大,导致 AEC 处理难度增加。同样,如果先做 NS,可能会把一些有用的回声参考信号给滤掉,影响 AEC 的效果。
下面这张图展示了完整的音频处理流程:
12.5 移动端特有的优化技巧
最后,分享几个我在移动端项目里积累的经验:
- 采样率统一:确保采集、处理、播放的采样率一致。我见过有人把采集设成 16kHz,处理设成 48kHz,结果 AEC 完全失效。
- 缓冲区大小:移动端建议使用 10ms 或 20ms 的帧长。太大会增加延迟,太小会增加 CPU 开销。
- 硬件加速:如果设备支持,尽量使用硬件 AEC。比如 iOS 的 AVAudioSession 就提供了硬件回声消除。
- 降噪与 AGC 的平衡:降噪太强会导致音量变小,这时候 AGC 会拼命放大,反而引入更多噪声。我建议降噪和 AGC 的强度不要同时开到最高。
个人经验: 我在做一款社交 App 时,发现用户在嘈杂环境中通话质量很差。后来我加了一个「环境自适应」功能:根据麦克风采集到的背景噪声水平,自动调整降噪等级和 AGC 目标电平。效果非常好,用户满意度提升了 30%。
好了,关于音频处理的三个核心模块就讲到这里。记住,音频处理没有银弹,每个场景都需要你根据实际情况去调优。多测试、多听、多对比,慢慢就能找到最适合你产品的参数组合。