12、非线性处理:中心削波、频谱减法、舒适噪声生成(CNG)

好,我们继续往下走。前面聊了线性滤波和自适应滤波,那些手段对付的是比较「规矩」的回声路径。但实际场景里,回声路径往往没那么听话——非线性失真、突发噪声、残留回声,这些才是真正让人头疼的东西。

我个人习惯把这一章的内容称为「收尾三件套」。为什么这么说?因为中心削波、频谱减法、舒适噪声生成,它们通常不是单独作战,而是跟在AEC(声学回声消除)主算法后面,处理那些残余的、非线性的回声成分。说白了,就是给回声处理做最后的「打扫战场」。

12.1 中心削波:简单粗暴但有效

中心削波(Center Clipping)是我最早接触的非线性处理手段。它的思路非常直接:把信号幅度小于某个阈值的部分直接砍掉。

你想想看,经过线性AEC处理后,残留的回声通常幅度比较小。如果我们设定一个合适的阈值,把低于这个阈值的信号全部置零,那残留回声就被干掉了。当然,近端语音如果也很小,也会被误伤——这就是它的局限性。

我在项目中遇到过这样一个场景:某款低端手机,扬声器失真严重,线性AEC怎么调都压不干净。后来我在后处理阶段加了一个中心削波,阈值设成近端语音平均幅度的30%,效果立竿见影。虽然牺牲了一点弱语音的清晰度,但整体通话质量提升了一大截。

核心公式:

y(n) = sign(x(n)) * max(0, |x(n)| - T)

其中 T 是削波阈值,通常根据背景噪声水平动态调整。

我的经验:阈值不要固定死。我建议用近端信号的短时能量做自适应,比如取最近50ms信号平均幅度的0.2~0.4倍。这样在安静环境下削得狠一点,嘈杂环境下保留更多细节。

12.2 频谱减法:从频域下手

中心削波是在时域干活,频谱减法(Spectral Subtraction)则是在频域做文章。它的核心思想是:估计出噪声(这里指残留回声)的频谱,然后从原始信号频谱中减去它。

为什么需要频谱减法?因为中心削波对宽带噪声效果不错,但对音乐、铃声这类窄带残留回声,削波会引入明显的「音乐噪声」——就是那种断断续续的、像水泡一样的声音。频谱减法能更好地保留语音的谐波结构。

具体做法分三步:

  1. 估计噪声谱:利用AEC输出的误差信号,或者远端参考信号,估算残留回声的功率谱密度。
  2. 计算增益函数:根据信噪比(这里其实是信号与残留回声的比值)计算每个频点的衰减增益。
  3. 应用增益:将增益函数乘到原始信号频谱上,再反变换回时域。

注意:频谱减法最怕两件事:一是噪声估计不准,导致语音失真;二是减过头了,产生负功率。我通常会在增益函数上加一个下限,比如最小增益设为0.1,避免完全切断某个频段。

我记得有一次调试车载免提系统,后排乘客说话时回声总是消不干净。线性AEC已经尽力了,但车内混响和扬声器非线性导致残留回声集中在低频段。我用频谱减法专门处理200Hz以下的频段,效果比全频段处理要好得多。

12.3 舒适噪声生成:别让通话「死寂」

好,现在中心削波和频谱减法把残留回声干掉了。但问题来了——当远端不说话、近端也不说话时,信号被削得干干净净,对方听到的就是一片死寂。这种「断崖式」的静音,比有噪声还难受。

舒适噪声生成(Comfort Noise Generation, CNG)就是解决这个问题的。它的任务是:在检测到双端静音时,生成一段与背景噪声特性相似的随机信号,填充到输出中。

说白了,就是「造假」——造一段听起来像真实环境噪声的信号,让通话保持自然。

我见过一些初学者直接加白噪声,结果对方一听就知道「这噪声不对劲」。真正的舒适噪声要匹配背景噪声的频谱形状和幅度波动。常用的方法是:

  • 用高斯随机数作为激励源
  • 通过一个AR(自回归)模型或滤波器,将白噪声塑造成目标频谱
  • 控制输出幅度,使其与背景噪声的短时能量一致

CNG的典型流程:

1. 在非静音段,持续更新背景噪声的频谱模板
2. 检测到静音时,生成高斯白噪声
3. 用频谱模板对白噪声进行滤波
4. 调整增益,使输出能量与背景噪声匹配
5. 平滑过渡,避免突变

嗯,这里要注意:CNG的启动和停止一定要做淡入淡出。我曾经因为没做平滑,导致每次静音切换时都有一个「噗」的冲击声,被测试团队追着改了好几天。

12.4 三者的协同工作

在实际系统中,这三个模块通常是串联使用的。我画了一张流程图,帮你理清它们的关系:

非线性处理三件套协同流程 AEC输出信号 中心削波(时域) 阈值自适应:基于短时能量 频谱减法(频域) 残留回声谱估计 + 增益计算 双端静音检测 舒适噪声生成(CNG) AR模型滤波 + 能量匹配 最终输出信号 注:双端静音时 CNG替换削波/减法输出

从图上你能看到:AEC输出先经过中心削波,把明显的残留回声砍掉;然后进入频谱减法,处理那些削不干净的窄带残留;最后,如果检测到双端都静音,就启动CNG填充舒适噪声。

这里有个细节:CNG和前面两个模块是互斥的。当CNG工作时,中心削波和频谱减法的输出会被替换掉。为什么?因为静音段不需要保留任何语音信息,直接生成噪声反而更自然。

避坑指南:我曾经在某个项目中,把CNG的噪声模板更新得太快。结果每次说话停顿,噪声特性都变一下,听起来像有人在调节音量旋钮。后来我把模板更新周期设为1~2秒,用指数平滑做慢速跟踪,问题就解决了。

12.5 参数调优建议

这三个模块的参数调优,我总结了一张表,方便你参考:

模块 关键参数 我的调优建议
中心削波 阈值T、攻击/释放时间 T设为近端语音平均幅度的0.25~0.35倍;攻击时间5ms,释放时间20ms,避免语音起始段被误削
频谱减法 过减因子α、噪声谱更新速度β α取1.2~1.5(减多一点),β取0.1~0.3(慢更新);低频段α可以更大
舒适噪声 AR模型阶数、模板更新周期 阶数8~12足够;更新周期1~2秒,用0.95的遗忘因子做指数平滑

嗯,这些参数不是绝对的。不同硬件、不同场景下,你需要根据实际听感做微调。我个人的习惯是:先拿一段典型的通话录音,离线调好参数,再上板验证。这样能省很多现场调试的时间。

好了,非线性处理这块就聊到这儿。中心削波、频谱减法、舒适噪声生成,这三板斧用好了,你的AEC系统就能从「能用」变成「好用」。下一章我们聊聊更高级的话题——双麦克风波束成形与回声消除的联合优化。


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