先聊个实际场景:你戴着耳机开电话会,对面人声很清楚,但窗外车流声、键盘敲击声、自己说话在耳机里的回声全都混在里面,一场会下来脑壳疼。这时候,音频信号处理里的傅里叶变换、语音增强、回声消除、主动降噪这四样东西,就是真正干活的“拆弹专家”。
别看这些名词摆在一起显得硬核,它们并不是孤立的数学玩具。我在音频算法这一行干了快十年,从早期的固话免提到现在的TWS耳机、智能音箱、会议麦克风阵列,几乎所有跟“听清楚”有关的体验问题,最后都会落到这几个算法上。坦白说,很多刚入行的朋友一上来就啃公式,结果被傅里叶变换劝退,或者把回声消除和降噪混为一谈,排错的时候完全找不到北。这篇内容我不打算给你推公式天书,而是从工程落地的角度,把这四个核心算法的“为什么这么做”“实际怎么调”“坑在哪里”讲透,看完你至少能把它们各自负责什么、怎么协作梳理清楚。
我默认你对数字信号处理有一定基础,但不要求你记得住每一个推导。文中所有原理我会用生活化的方式拆开,最后会有一些我实际踩过、填过的坑。基本上,这篇文章可以当作一份“音频算法工程化入门笔记”来读。
1. 音频信号处理:为什么这四件事绕不开
1.1 一段声音从麦克风到扬声器,到底经过了什么
想象一下你对着手机说话,麦克风采集到的信号,绝对不是干净的人声,而是一个大杂烩。
首先是环境噪声,比如空调嗡嗡声、马路上的低频胎噪、隔壁工位的谈话声,这些噪声会直接叠加在你的语音信号上。其次是你自己的声音经过空气传导、颅骨传导产生的“近讲效应”,麦克风离嘴近的时候低频会异常突出。还有一类是设备自身的问题,比如扬声器播放的远端声音被麦克风重新采集到,这就是声学回声。最后,耳机里的主动降噪还要额外处理一个物理问题:噪声通过耳塞的物理隔离不够,需要靠算法产生反向声波去抵消它。
面向不同设备,处理链路也完全不同。
智能音箱的麦克风阵列,核心是去混响和回声消除,因为扬声器离麦克风太近,播放音乐时如果不做回声消除,语音助手根本听不见你说话。TWS耳机的通话降噪,核心是语音增强,因为麦克风在耳朵上,环境噪声突出,而且风噪还容易引起爆音。头戴式降噪耳机,核心是主动降噪,它要处理的是外部噪声通过物理结构泄漏进来的部分,这一块完全靠模拟或数字电路实时生成“反相声波”来抵消。
也就是说,算法不是越堆越好,得看设备形态和声学路径,这也是我一直跟团队强调的:先定场景,再选算法,最后才是调参。
1.2 四种核心算法的分工,别搞混
我把这四种算法用一个很直白的类比来讲:傅里叶变换是“显微镜”,语音增强是“去杂质”,回声消除是“堵漏”,主动降噪是“隔音墙”。
傅里叶变换负责把声音从时间域换到频率域,让我们能看到一段声音里各个频率成分的大小和相位,它是一切频域处理的基础。语音增强处理的是单麦克风或多麦克风采集到的混合信号,目标是提升信噪比,把人声突出、把环境噪声压低。回声消除处理的是设备自身发声反馈到麦克风的“自己人打扰自己人”问题,它依赖自适应滤波实时估计回声路径并减掉。主动降噪是噪声在到达人耳之前,通过对噪声源的估计生成反向声波去物理抵消,跟上面三个算法算出的“结果供人听”不同,它更强调实时和物理空间感知。
实际产品里它们经常会串联使用。一条典型的通话链路是:麦克风采集信号 -> 回声消除(消掉自己扬声器的声音)-> 波束成形(空间指向说话人)-> 语音增强(压低残留噪声)-> 自动增益(稳定音量)-> 扬声器播放。傅里叶变换在中间被用了很多次,每个频域模块都要做一次时频变换。记住这个全景图,后面我们逐个拆。
2. 傅里叶变换:整个音频处理的数学地基
2.1 为什么非要从时域换到频域
任何麦克风采集到的信号,本质上都是一个随时间变化的电压值序列,我们叫它时域信号。时域信号的优点是直观,但它有个致命问题:不同频率成分混叠在一起,很难单独处理。
举个例子,一段录音里同时有50Hz的市电干扰和1000Hz的人声,你在时域波形上根本看不出什么规律,只觉得“有点毛躁”。但如果你对它做傅里叶变换,转到频域看,就能立刻看到在50Hz的位置能量很高,1000Hz附近有一些谐波,这样就有了“分而治之”的可能性。
工程上最常用的是短时傅里叶变换,也叫加窗傅里叶变换。原因很简单,语音是非平稳信号,发音在几十毫秒内会变,所以不能对整个长信号只做一次FFT,而是把信号切成20到32毫秒的帧,每帧内认为信号是平稳的,再逐帧做FFT,最后得到的是频率随时间变化的语谱图。这中间涉及三个关键参数:帧长、帧移、窗函数。
帧长选256点还是512点,对应16kHz采样率下就是16到32毫秒。太短频率分辨率差,太低频分不开;太长则时间分辨率差,瞬态声音(比如爆破音)会被抹掉。一般我建议语音增强用512点(32ms),音乐处理用1024点或更长。帧移一般选帧长的四分之一到一半,比如512点帧长,帧移256点,这样相邻帧有50%重叠,可以避免拼接时出现块效应。窗函数首选汉宁窗,它是汉明窗的兄弟,旁瓣衰减好,频率泄漏少,唯一缺点是主瓣稍宽,实际影响不大。
在真实代码里,很多人喜欢直接拿MATLAB的spectrogram函数出一张图看看,这个没问题,但做实时算法时一定要自己写分帧和加窗,不要依赖库函数,否则后面做DSP移植的时候会掉链子。
2.2 三角脉冲的傅里叶变换记忆方法,别硬背
最近网上在传“三角脉冲的傅里叶变换记忆方法”,很多学生问我是不是要去记一堆公式。我的建议是:不要硬背,要用卷积和图形变换来记,一辈子忘不掉。
三角脉冲可以看作两个宽度相同的矩形脉冲做卷积得到的。你想象一个矩形脉冲是“一块砖”,两个砖头首尾搭在一起,重叠面积的轮廓就是一个三角形。这在时域上叫“矩形脉冲自卷积等于三角脉冲”。
傅里叶变换有一个非常重要的性质:时域卷积对应频域乘积。所以三角脉冲的傅里叶变换,就等于矩形脉冲的傅里叶变换的平方。而单个矩形脉冲的傅里叶变换是sinc函数,那你自动就能推出:三角脉冲的频谱是sinc函数的平方。
好,接下来是记忆法。sinc函数的形状,中间一个高主瓣,两侧各有一串逐渐衰减的旁瓣。sinc的平方有什么变化?主瓣会变得更矮胖、更集中,旁瓣衰减速度会更快。这个特性对应到实际里就是:三角窗比矩形窗在频域的旁瓣泄漏更小,所以做频谱分析的时候,三角窗或者与它类似的窗函数能更好地抑制频谱泄漏。
再深一层,我们从时频对偶性来看:矩形窗在时域是有限长的,对应频域是无限延伸的sinc;三角窗在时域是“更平滑地变到0”,对应频域就是衰减更快的sinc平方。这个概念对选窗函数特别有用:时域越平滑,频域旁瓣越低。巴特利特窗、三角窗、汉宁窗,本质上都在干同一件事。
如果你有一天在面试场上被问到三角脉冲的傅里叶变换,你完全可以这样回答:它等于sinc函数的平方,因为三角脉冲是两个矩形脉冲卷积的结果。面试官往往会对这个回答眼前一亮,因为他问的大多数人只会背公式,不知道背后的卷积关系。
2.3 相位:被大多数人忽略的那一半信息
说到傅里叶变换,很多人只关心幅度谱——也就是哪个频率能量大,哪个频率能量小。但我要强调一个不那么直观却极其重要的概念:相位。如果幅度谱决定你“听见了什么音高和强弱”,那么相位谱决定你“听到的声音形状和空间感”。
有一个经典的实验,我非常推荐大家自己跑一遍:拿一段语音,做FFT到频域,把幅度谱保留,把所有相位随机化,再反变换回来,你会发现声音变成了一种嘈杂的、机器人的、甚至完全听不懂的声音。反过来,如果把相位谱保留、幅度谱全部置为1,反变换回来的语音依然能听懂。这说明什么?对语音可懂度而言,相位的重要性甚至超过了幅度。
在工程上,相位的作用主要体现在几个方面。第一个是波束成形,麦克风阵列就是靠不同麦克风之间的相位差来估计声源方向的。如果相位估计错,波束就会指向垃圾方向。第二个是语音增强,很多降噪算法(比如维纳滤波)只估计幅度增益,处理完的语音会有“音乐噪声”,部分原因就是没有精细处理相位一致性。第三个是主动降噪,反向声波要“完全反相”才能抵消原噪声,这个反相靠的就是相位对齐,差之毫厘谬以千里。
所以我一直跟同事说,做音频算法不能只盯着幅度看。调试的时候要养成习惯:不只画幅度谱,也要看相位差、群延迟、语谱图相位部分。很多听起来“闷闷的”“怪怪的”问题,根源就在相位处理上。
3. 语音增强:把目标语音从噪声里捞出来
3.1 噪声怎么分类,决定了用什么招
语音增强面对的信号模型很简单:x(t) = s(t) + n(t),干净语音加噪声。但噪声五花八门,如果不分类直接套算法,效果会很差。
按统计特性分,有平稳噪声(空调、风扇、电机声)和非平稳噪声(键盘、关门、旁人说话)。平稳噪声的频谱基本不变,可以用噪声估计器持续跟踪;非平稳噪声变化快,只能用更激进的算法去动态估计。按空间分布分,有方向性噪声和扩散噪声。方向性噪声适合用波束成形去“避开”,扩散噪声四面八方都有,波束成形无能为力,只能靠单通道降噪压。
说实话,没有任何一种算法能同时解决所有噪声。实际产品里基本都是组合拳:麦克风阵列先做波束成形,把目标方向增益提高,非目标方向压低,这叫空间滤波;然后单通道语音增强模块接力,把波束输出里残留的噪声再压一遍。很多人喜欢把“降噪深度”当成唯一指标,但深度过头了语音失真也严重,所以必须在信噪比提升和语音失真之间找一个平衡点。
我在调试时一般先看几样客观指标:语音质量评估PESQ、短时客观可懂度STOI、信噪比SNR提升量,再结合主观听音。说实话,客观分数只能筛掉明显差的版本,最终定版主要靠主观试听。
3.2 经典算法路线:谱减法、维纳滤波与MMSE
先从谱减法说起。它是最早的一批语音增强方法,思路极其直白:在频域里,估计出噪声功率谱,然后把带噪信号功率谱减去噪声功率谱,剩下的就是干净语音,再结合带噪信号的相位重建时域信号。
谱减法的实现门槛很低,命令行一两百行就能写出来。但它有个臭名昭著的副作用,就是“音乐噪声”。原因是噪声估计本身有随机波动,减来减去会在某些频点产生小的正负尖峰,听起来就像一串断断续续的“吱吱”声。为了解决它,后来出现了各种改进,比如过减法(多减一点)、谱下限(防止减成负值),但都没法根除。所以现在工业界很少直接纯用谱减法,一般是作为某个大算法的起始估计。
维纳滤波是另一种思路。它不直接做减法,而是给每个频点算一个增益系数,带噪信号乘上这个系数,就是干净语音的估计。这个增益系数和当前频点的先验信噪比直接相关。先验信噪比是一个理论值,实际不可知,所以工程上普遍用“判决引导法”来递推估计。我只能说这个方法很经典,效果比谱减法平稳很多,音乐噪声也弱很多,但缺点是计算量稍大,且对先验信噪比估计的准确性很敏感。
再往后是MMSE—最小均方误差估计。它的核心思想是:在已知带噪信号的前提下,找到一个估计量,使得它与真实干净语音的均方误差最小。这个“最小”不是简单的代数最小,而是对于整个统计分布求期望最小,所以它更“数学”,也更强。Ephraim和Malah在1984年提出的MMSE方法,直到今天还在很多产品里用,尤其是低信噪比环境下,它的语音失真和残留噪声平衡得比维纳滤波更好。
我在实际项目里的“默认起手式”一般是这样:语音存在概率VAD先区分语音段和噪声段,噪声段更新噪声功率谱,语音段用MMSE做一个初始估计,然后用维纳滤波在整个频带上做平滑增益。这个组合拳在20世纪90年代提出的改进版里已经非常成熟,很多开源库(比如WebRTC)用的就是这么一套变体。
3.3 音频处理里的调参心得和坑
关于语音增强的调参,有三件事我觉得值得单独拿出来讲,因为常规文档里几乎不会写。
第一件是“过减法的系数不要拉满”。谱减法里减掉2倍、3倍的噪声,听起来噪声确实没了,但语音也变得“金属感”十足,而且中高频细节全丢。实际调试时我会控制在1.1到1.5倍之间,宁可留一丝丝底噪,也要保住语音的自然度。很多产品经理会说“噪声怎么没除干净”,这时候我会直接放原始音频做AB对比,让他们先感受原始噪声有多大,再感受增强后的可懂度。
第二件是“噪声估计的更新速率”。噪声估计需要在语音和非语音之间快速切换,又不能被语音段污染。WebRTC里的噪声估计器其实很像一个带遗忘因子的递归平均器,语音段不更新,噪声段快速更新。但如果噪声本身也在变(比如你走在路上,路过一辆卡车),更新过慢会导致卡车噪声残留,更新过快又有可能把语音尾巴当噪声吃掉。一般我把噪声估计的适应时间控制在50到200毫秒之间,具体要看目标场景是静态室内还是动态户外。
第三件是“16kHz和48kHz的差异”。很多算法在16kHz下表现良好,一升到48kHz就露馅了,因为高频段(8到24kHz)的信噪比通常很低,而人对高频失真又极其敏感。处理办法是先对带噪信号做下采样到16kHz,增强完再上采样回48kHz,或者在24kHz以下做多频带加权。总之目标采样率不同,策略完全不同,这一点很多人容易踩坑。
4. 回声消除:让设备“听不见自己说话”
4.1 声学回声到底是怎么产生的
你把手机开免提,对方说话的声音从扬声器传出来,有一部分会被麦克风重新收进去,然后这“回声”再通过网络传给对方,对方听到自己的声音,就像是“喂,你那边有回声”。
在专业术语里,扬声器播放的远端信号叫参考信号,麦克风采集到的包括近端语音加回声信号的混合信号叫麦克风信号。回声消除的目标是从麦克风信号里减去经过声学路径衰减、延迟后的参考信号。
麻烦就麻烦在,这个“声学路径”不是一成不变的。你用手挡住扬声器,或者手机换了个位置,声学路径就变了。所以回声消除必须用自适应滤波器,实时跟踪声学路径的变化。它本质上是在解决一个“系统辨识”问题——把房间、扬声器、麦克风之间那套传输特性估计出来。
4.2 自适应滤波与LMS/NLMS
自适应滤波器的经典模型是这样的:参考信号x(n)经过一个未知的声学系统(比如房间回声路径h),产生回声y(n)。然后我们用一个横向FIR滤波器w(n)来模拟这个h,滤波器的输出是y_hat(n),麦克风信号d(n)减去y_hat(n)就是误差信号e(n)。目标是通过某种算法调整w(n),让e(n)的均方差最小。当e(n)最小的时候,w(n)就逼近h,回声也就被减掉了。
最朴素的自适应定则是最小均方算法LMS。它的更新公式简洁到令人发指:w(n+1) = w(n) + μ * e(n) * x(n)。意思是,当前时刻的误差e(n)乘以参考信号x(n),再乘以一个步长μ,去修正滤波器系数。
LMS最大的问题是步长μ不好选。选大了,收敛快但容易发散;选小了,稳但收敛慢。语音信号能量波动大,一个固定的步长很难让滤波器在静音、低语、大声三种情况下都表现良好。
于是有了NLMS—归一化最小均方算法。跟LMS最关键的区别,就是步长会根据参考信号的能量做归一化。也就是说,在输入信号能量大的时候自动把步长缩小,防止发散;能量小的时候自动放大步长,加速收敛。靠着这个改进,NLMS在实际工程中几乎完全取代了LMS。
但在回声路径很长的环境(大房间混响、远距离免提),NLMS的收敛速度还是不够。这时就得引入频域自适应滤波,也就是分块频域自适应滤波器,把信号分成块,在频域里做滤波和权值更新,借此利用FFT把卷积变成点乘,大幅降低计算量。我在智能音箱项目里用的基本就是这种形态的滤波结构,16kHz采样率,滤波器长度65536点(对应约两秒的路径)都可以实时跑,如果只靠时域LMS,DSP早就烧掉了。
4.3 双讲检测和尾长:产品级回声消除的两个命门
就在我以为回声消除“不就是搞个自适应滤波吗”的时候,实际产品给了我两巴掌:双讲和尾长。
双讲就是近端用户和远端用户同时说话。这本来是最常见的通话场景,但对自适应滤波器来说是个灾难。因为误差信号e(n)里既包含残留回声,又包含近端语音。近端语音的出现会误导滤波器,让它的系数发散。所以必须有一个双讲检测模块,检测到“近端有人在说话”时,就冻结自适应滤波器的更新,只保持现有滤波系数去做回声抵消。
DTD的经典方法是用相关性和能量比较:参考信号与麦克风信号的互相关系数高,说明主要是回声,可以更新;相关系数低,说明近端语音占据主导,就冻结更新。也有一些更先进的方法用深度神经网络来做端到端的DTD,但落地复杂度高。工程上我还是建议先从经典相关法做起,稳。
尾长是另一个很容易被忽略的参数。通俗地说,就是自适应滤波器最多能覆盖多长的回声路径。如果尾长设得太短,回声路径比滤波器长,那后面的回声没被建模到,残留回声就会很重。如果尾长设得太长,计算量大,而且收敛会变慢甚至发散。
具体怎么定尾长?一般先测设备从扬声器到麦克风的物理延迟和房间混响时间T60。音乐播放场景回声往往来自扬声器直达和近次反射,尾长50到100毫秒就够。但如果放在客厅,有墙面反射、柜子反射,回声拖到300毫秒都停不下来,那尾长至少得600毫秒。16kHz采样率下,600毫秒就是9600个抽头,所以这也就解释了为什么必须上频域滤波器。
再多说一句:回声消除之后,一般还会跟着一个非线性处理模块,对残余回声做进一步抑制,通常用中心削波或谱减法。但NLP开太狠会损伤近端语音,开太轻又会漏出回声,这个矛盾只能一边听一边调,没有统一答案。
5. 主动降噪:用“反相波”在物理层解决问题
5.1 前馈、反馈、混合式,三种架构怎么选
现在很多人戴的降噪耳机,宣传语动不动就是“-45dB降噪”,猛得不行。但主动降噪不是一个单一算法,它是物理硬件、声学设计与数字信号处理的结合体。核心原理一句话总结:采样环境噪声,做反相处理后通过扬声器播放,与原噪声在耳道口叠加抵消,这叫声学干涉相消。
按麦克风位置和信号流来分,主动降噪大致有三类架构。
前馈式:外部麦克风放在耳机外壳上,采集环境噪声,喂给降噪芯片,芯片处理后驱动扬声器发出反相波。优点是对中高频环境噪声(比如地铁风噪声)有较好的抑制,因为它是“提前一步”处理外部噪声。缺点是不能处理耳塞内部已经存在的噪声,而且必须校准前馈麦克风到扬声器的声学路径,路径一变效果就打折。
反馈式:误差麦克风放在耳道内侧,采集“已经漏进耳道内的噪声”,也就是人耳实际听到的残余噪声。芯片以这个误差信号为参考,把它反向放大并通过扬声器输出。因为有负反馈闭环,相当于一个主动控制系统,对低频噪声效果极好,而且不依赖外部麦克风路径校准。缺点是闭环系统存在稳定性问题,增益拉太高会产生“啸叫”,且对随机中高频噪声处理能力弱。
混合式:外麦克风加内麦克风,前后两个环路一起工作。前馈搞定中高频外噪,反馈搞定低频残噪,两个环路在频域上互补。市面上高端TWS耳机几乎都是混合式。代价是硬件成本翻倍,算法调优难度也成倍增加:两个环路要避免在交叉频段相互打架,需要仔细设计分频和增益控制。
从我试过的产品来看,如果只求成本低、开降噪能压掉空调声,前馈就够了。但真正想把嘈杂地铁里的轰鸣声压下去,还得靠混合式或者至少是高增益反馈式。
5.2 从算法到硬件,延迟是绕不过去的坎
主动降噪最关键的物理限制两个字:延迟。因为抵消噪声本质上要求“反相波”必须在正确的时间点到达人耳。如果主波和反相波时间差太大,非但不能抵消,反而会叠加增强,那就成了增加噪声了。
这里说的延迟不只是芯片算一次滤波的算力延迟,而是包括ADC、DSP处理、DAC、模拟滤波链路、扬声器电声转换、声音在耳道里传播的所有时间相加。整套链路的总延迟,在20到40微秒就会开始产生明显的相位偏差;超过100微秒,降噪效果基本就归零了。
正因为这样,主动降噪往往是模拟域和数字域混合的“邪门功夫”。在模拟域,直接用运算放大器搭一个有源滤波电路,把麦克风信号放大、反相、输出,延迟只有几微秒,效果立竿见影但不灵活。在数字域,可以做自适应降噪,实时修改滤波参数,但延迟抬高到几百微秒甚至一毫秒以上。于是很多产品用了“模拟加数字”混合:先模拟级做宽频反相,再数字级做窄带精修,这样既有低延迟,又有可调性。
这里也想点一个容易被忽视的问题:如果你的降噪耳机在安静环境下开降噪会感到“压耳感”,或者听到低频轰轰声,十有八九是反馈环路的低频增益过高,或者相位裕度不足,系统处在接近不稳定的状态。这种问题用“增益降个3dB”往往就解决了,但需要反复试听。
5.3 开放式与入耳式的差异
最后说说开放式耳机和入耳式耳机的主动降噪差异。很多半入耳、开放式耳机的用户会抱怨“开了降噪跟没开一样”,原因是结构上就不占便宜:耳塞与耳道之间没有形成密闭腔体,外部噪声绕过多条路径直达耳膜,ANC只能针对麦克风采集到的部分进行抵消。
入耳式因为有一个天然的封闭式声学腔,前馈和反馈两个麦克风都能较准确地采集噪声与残余噪声,降噪深度容易做上去。开放式耳机更像是在开放空间里“制造局部安静”,现阶段只能靠多麦克风阵列和自适应算法去扩大空间消除区域,物理上限决定它不可能做到和入耳式一样深的降噪。
如果读者朋友要做产品选型,我的建议是:先把佩戴方式定下来,再决定ANC架构;如果必须做开放式,你就别死磕“-40dB降噪”这种指标,不如把通话降噪、语音增强做扎实,因为开放式耳机的通话场景比降噪场景更常见。
6. 把四种算法串起来:一条真实的音频处理链路
6.1 从麦克风阵列到扬声器:一条完整链路
前面每个算法都单独拎出来讲了一遍,但实际产品里它们都是协作的。我拿一个常见的会议音箱项目举例,给你完整捋一遍。
设备是4麦环形阵列,16kHz采样率、16bit量化。目标场景是3到5米远场免提通话。
原始信号进来先做回声消除,这一步参考信号就是本地扬声器要播出的远端音源。AEC处理的是板载扬声器的直达声和电脑、手机放出来的音频。如果不消除,后面的语音增强会把回声当成“带噪信号里的语音”去增强,或者波束成形时把声源方向指向内放音箱,整个系统就废了。
AEC的输出送给波束成形模块。4个麦克风形成环形阵列,延迟求和波束成形先做一个固定指向,把方向对准人声来源;然后自适应波束成形(比如GSC)动态调整“零点”,把噪声源方向压掉。波束成形的结果再回到单通道语音增强做残噪抑制、音乐噪声控制。
然后是自动增益控制,根据不同距离说话人音量差异,把信号稳定在某个目标电平上。这里要小心,AGC太激进会把背景底噪一起放大,所以一般先降噪再AGC。最后加上一个限幅器,防止偶发大信号削波,再把信号送出去。
6.2 模块之间怎么协作,资源和参数怎么分
多算法串联时,最怕的是模块之间相互“抢资源”或者“互相伤害”。
资源分配上,AEC和波束成形是计算量的大头。如果DSP的主频有限,我会优先保证AEC的实时性,因为回声不消掉,后面所有算法都会错乱。语音增强可以选质量稍差但更省算力的滤波器,毕竟它只是“锦上添花”。
参数分配上,AEC的尾长和步长必须根据房间大小与扬声器音量动态调整,语音增强的降噪强度要看后端是否还有人耳感知模型来兜底。一个常见的原则是:前级能做的不要留给后级。比如回声问题必须在前级压到“几乎听不见”,否则后级语音增强专门去做回声抑制,效果极差,还会牺牲语音。
还有一点就是引入“风噪检测”。我发现很多团队在链路里不加风噪检测模块,结果户外会议场景一刮风,麦克风采集到大量低频风噪,波束成形和语音增强全都被带歪。其实思路也简单:风噪在低频有很强的非平稳特征,可以用多麦克风相关性做一个简单的检测开关,检测到风噪就降低低频增益、切换波束模式。这个模块代码量不大,但对真实场景的舒适度提升非常明显。
6.3 实测中常见的整体性问题与排查思路
这里分享几个我在调试整机时反复遇到过的“链路级”问题。
第一个是“回声没消干净但推给语音增强背锅”。现象是远端用户总说听到自己声音,但AEC模块明明启动了。排查顺序:先看AEC参考信号是不是真的拿到了“扬声器播出去的那一路”而不是“要播出的文件”或者“模拟信号”,很多回声消除问题都出在参考信号没对齐。其次看DTD有没有误判,近端一说话滤波器就冻结,回声就会漏。最后看NLP是否开启,如果完全没有NLP或者NLP强度太低,残余回声就会明显。
第二个是“开启降噪后语音发闷”。这个我见过最多。多数原因不是降噪太强,而是波束成形或者语音增强对高频做了过度压制,尤其是在低信噪比场景下算法为了降噪,把中高频一起削了。解决办法是给高频频段设置一个最大衰减上限,比如10kHz以上最多压6dB,宁可留一点噪声,也要保住语音的“空气感”。
第三个是“DSP算力爆了”。很多新人在PC上跑实时链路用x86测试,各种滤波器随便起,一点都不卡。一旦切到嵌入式平台,发现FFT一帧算不完,下帧就丢数据。这是老生常谈的坑。我的建议是链路设计之初就做好每个模块的“复杂度预算”,比如AEC不可省、波束成形不可省,那语音增强就用计算量小的谱减法变体,而不是一上来就上深度神经网络降噪。毕竟嵌入式的路要一步一步走,功能先通,再谈效果。
第四个是“48kHz听感跟16kHz不一样”。我发现很多团队开发环境用16kHz,最终产品要48kHz,于是简单地对信号做上下采样就把算法串起来。这么做在单频点测试时没事,但到了真人声音上就出现“发干”“发紧”。原因在于上下采样本身会引入频带边缘的失真和群延迟偏差,多个模块串联后误差累积。办法是尽量保持全程统一采样率,如果确实需要跨采样率,至少保证每一个转换环节都使用高质量抗混叠滤波器和合理的群延迟补偿。
结尾:一点个人心得和调试小技巧
最后聊几句真心话。音频信号处理这个领域,入门门槛不高,到处都能找到现成的库和论文代码,但想要在实际产品里达到“听感自然、稳定可靠”,靠的是大量的实际操作和对声学系统的理解。
我自己的调试习惯是:每次只改一个参数,改完必须做主观听音对比,录音文件永远保留原始版本和每个阶段的中间结果。不要相信“感觉好像变好了”这种判断,一定要有A/B对比,最好让不参与开发的人盲听评分。另外我会在电脑上常备几段标准测试音频:一段男声、一段女声、一段嘈杂地铁噪声、一段键盘打字声、一段秒表滴答声,专门用来测试各种算法效果。这些测试材料并不复杂,但能帮你更快定位问题,比临时去网上找素材靠谱得多。
最后再分享一个小技巧:调语音增强和回声消除时,先把输入信号做成一个“扫频信号”,也就是从20Hz到8kHz慢慢扫一遍,输入到扬声器播放,同时观察麦克风信号AEC之后能不能把扫频成分完全消掉。这个方法能在一分钟内定位出回声路径的耦合频点,以及滤波器尾长够不够。我在多个项目里靠这一招省下了大量排查时间,强烈推荐你试试。
算法这条路没有捷径,但每次踩坑后的经验都是实打实的积累。希望这篇内容能帮你在做音频产品时少走一些弯路。