音频算法实战:从傅里叶变换到降噪与回声消除

发布时间:2026/10/2 21:24:28
音频算法实战:从傅里叶变换到降噪与回声消除 有时候你会发现手机通话时对方总说听不清你说话不是因为嗓门小而是背景里空调声、键盘声、马路的低频轰鸣全被一起送了过去戴上降噪耳机坐地铁底噪是没了但一开口说话自己耳朵里却闷得像堵了团棉花开视频会议开到一半扬声器里突然传来自己的声音而且是延迟了半拍的回声整场会议瞬间变成一场混乱的合唱。这些场景背后其实是同一类技术问题——音频信号处理。我做了多年音频算法相关的工作日常打交道最多的就是傅里叶变换、语音增强、回声消除、主动降噪这四样东西。它们名字听着硬核很多刚入门的朋友也容易被满屏的公式劝退但实际上它们之间的关系非常清晰核心思想也不难理解。这篇博客我打算把这些年积累的理解、踩过的坑、调参的经验一次性讲透。不管你是学生、音频工程师、嵌入式开发者还是单纯对降噪耳机和语音通话背后原理感兴趣这篇文章应该都能给你一些有价值的参考。1. 时域与频域傅里叶变换如何撑起整个音频算法栈1.1 为什么音频算法工程师都在和频域打交道麦克风采回来的声音信号本质上是一串随时间变化的电压数值也就是时域波形。你盯着这个波形看能看出声音是大是小、有没有削波但很难看出这声音里到底藏了哪些频率成分——那个让人烦躁的嗡嗡声到底是50Hz的电源干扰还是200Hz的空调共振只看波形根本说不清。傅里叶变换做的事情就是把时域信号拆解成不同频率分量的组合。打个比方你面前有一杯混合果汁时域波形是整杯果汁的颜色和口感傅里叶变换则像一台离心分离机能把橙汁、苹果汁、西瓜汁按比例分开让每一种成分都清晰可见。干过音频算法的人都知道绝大多数的降噪、增强、回声处理都是在频域里操作的。因为在频域里语音和人声、噪声和回声往往分布在不同频段或者具有不同的时变特征分离起来比在时域里容易得多。短时傅里叶变换STFT更是音频算法的事实标准——先加窗分帧把长信号切成一小段一小段通常是20~50ms一帧再对每帧做傅里叶变换得到一个随时间变化的频谱图也就是声谱图。后续所有操作几乎都是在这个声谱图上进行的。1.2 DFT到FFT你其实不需要记住全部公式很多人一听傅里叶变换就头皮发麻觉得公式太长、变量太多。实际上工程上你真正需要理解的只有几个关键点。离散傅里叶变换DFT做的事情是计算信号和一组不同频率复指数信号的相关程度。每个频率分量对应一个复数这个复数的幅度表示该频率成分的强弱相位表示该频率成分的时间偏移。FFT快速傅里叶变换只是DFT的高效算法没有改变数学本质只是把计算量从N²降到了NlogN——这个提升有多大呢一帧1024点的信号DFT大约需要100万次复数乘法FFT只需要大约1万次差了100倍。没有FFT实时音频处理基本无从谈起。实际编码时你几乎不会自己写FFT直接用成熟库就行不同平台有不同选择ARM平台手机、嵌入式设备常用开源库ARM CMSIS-DSP或者更完整的Ne10、KissFFTx86平台服务器、PCFFTW、Intel MKL性能最好跨平台音频框架很多直接内置了FFT实现比如JACK、RtAudio配合自选FFT库不过在理解层面有个概念必须搞清楚频率分辨率。FFT输出的谱线间隔等于采样率除以FFT点数。比如采样率16kHzFFT点数512那么频率分辨率就是16000÷51231.25Hz。这意味着你只能分辨相差超过31.25Hz的频率成分。如果你需要更细的频率分辨就得增大FFT点数但分辨率高了时间分辨就会变差——一段很短的瞬态声音在频谱上会被模糊掉。这就是经典的时频测不准原理。工程上经常需要在时间分辨率和频率分辨率之间找平衡没有万能的参数。1.3 相位比幅度更容易被忽视的信息网上很多讲傅里叶变换的文章花大量篇幅讲幅度谱对相位往往一笔带过。但在音频处理里相位信息的处理失误正是很多复杂算法产生金属声、梳状滤波、回声等问题的根源。相位描述的是各频率成分在时间轴上的偏移关系。人耳对相位的感知不像对幅度那么敏感但在多麦克风阵列、回声消除、波束成形这些场景里相位就是决定性的信息。比如两个麦克风接收到同一个声音信号到达时间会有微小的差异这个差异反映到频域就是相位差利用这个相位差就能估计声源方向。做语音增强时如果直接修改幅度谱而保留原始相位听起来问题不大但如果改动幅度谱太激进再反变换回时域就会出现所谓的音乐噪声——那种听起来像喷泉滴答响的残留噪声。另一个常见问题是时域混叠对频域数据进行非线性修改后再做逆变换和重叠相加如果不做一致性约束就会产生明显的zipper noise阶梯噪声。这些都是我实际调试中反复遇到过的坑。1.4 三角脉冲的傅里叶变换一个实用的记忆锚点关于傅里叶变换网上有个热搜说法叫三角脉冲的傅里叶变换记忆方法。三角脉冲是啥就是幅度从0线性上升到峰值再线性下降到0的这种信号形状像一座山。它的傅里叶变换结果是一个sinc²函数也就是sin(x)/x的平方。这个结论的实用价值不在于三角脉冲本身多常见而在于它是一个记忆锚点矩形脉冲的傅里叶变换是sinc函数第一零点在1/T处三角脉冲是矩形脉冲的卷积所以它的频谱是sinc²函数这两个结果串起来你就能建立一个直觉时域里信号越窄、突变越剧烈频域里能量就越扩散、旁瓣越高。这在设计窗函数时非常有用。我在实际做STFT窗函数选择时经常用汉宁窗、汉明窗而不是矩形窗就是因为矩形窗的频谱旁瓣太高会引入频谱泄漏把强频率成分的能量泄漏到邻近频点干扰后续的降噪判断。选窗就是在时间分辨率和频谱泄漏之间做权衡理解了脉冲函数和sinc的关系这个权衡就变得具体可感了。2. 语音增强降噪的三种路线和效果边界2.1 语音增强到底在解决什么问题语音增强Speech Enhancement的目标是从带噪语音中尽可能恢复出干净的语音信号。看似简单实际很难。因为噪声不是一种东西——空调声是平稳的宽带噪声键盘声是瞬态冲击街道声是非平稳的随机噪声还有可能是来自其他人的说话声也就是干扰语音。不同性质的噪声处理方式完全不同。做语音增强之前先给噪声分类。这是很多初学者容易忽略的第一步但恰恰是最关键的。我个人的经验是花30%的时间搞清楚噪声类型比直接调算法参数重要得多。只有先诊断出噪声类型才能选择合适的处理策略。噪声类型典型来源频域特征适用的处理思路平稳噪声空调、风扇、白噪声频谱随时间变化缓慢谱减法、维纳滤波、噪声估计非平稳噪声街道噪声、音乐、其他语音频谱随时间快速变化深度学习方法、基于掩码的方法瞬态冲击键盘声、关门声、敲击声时域短时高能量瞬态检测抑制、稀疏优化2.2 谱减法简单直接的起点谱减法Spectral Subtraction是语音增强里最经典、最直觉的方法。它基于一个假设在非语音段可以估计出噪声的频谱在语音段把含噪信号的幅度谱减去噪声幅度谱的估计值保留相位不变再反变换回时域。数学上极简效果也立竿见影但实际问题立刻出现如果减去太多残余的负值会变成随机起伏反变换后就是那种滴滴答答的音乐噪声musical noise。早年做语音增强的人都跟音乐噪声搏斗过。为了解决这个问题出现了各种改进方案比如过减法over-subtraction和频谱下限约束spectral floor——给减完的谱设定一个下限不让它减到零以下。这些方法能显著降低音乐噪声但也带来了新的问题噪声抑制得越狠语音失真越大。谱减法适合的场景我总结为噪声相对平稳、信噪比不是特别低、对实时性要求较高的设备。像老式电话、对讲机的降噪链路上谱减法依然有一席之地因为它计算量小、实现简单。2.3 维纳滤波与统计信号模型比谱减法更进一步是维纳滤波Wiener Filter。谱减法相当于一刀切地减噪声而维纳滤波是根据当前帧的信噪比动态计算一个增益系数。信噪比高的频点增益接近1保留语音信噪比低的频点增益很小压制噪声。这个思路在统计信号处理里更体面。维纳滤波器的本质是在均方误差最小意义下的最优线性滤波器它背后有明确的数学模型调参空间也更平滑。实际工程中语音增强通常用的是维纳滤波的变体比如决策引导法Decision-Directed来估计先验信噪比这个方法是Cohen和Gannot等人在经典论文里不断完善的现在大多数单通道语音增强算法里都能看到它的影子。决策引导法是什么意思简单说当前帧的先验信噪比不只看当前帧观测到的信噪比还参考上一帧估计出来的先验信噪比。这个平滑操作极大地减少了音乐噪声代价是算法对语音起始段反应变慢。我调试的时候发现这个平滑系数的取值范围一般在0.95~0.99之间太大了语音起始处会有淹没感太小了音乐噪声又会冒出来。找到合适的值需要对着语谱图反复听、反复看这是纯粹的经验活。2.4 深度学习方案的现状与工程落地注意这些年深度学习在语音增强上的效果确实碾压传统方法尤其是在非平稳噪声和低信噪比场景下。很多手机和耳机里的通话降噪底层已经换成了DNN方案最典型的就是在频域上计算一个理想的比值掩码IRM或复数理想掩码cIRM相当于让神经网络学会一个比维纳滤波更聪明的增益函数。但深度学习方法不是银弹。我在工程落地时遇到过几个很现实的问题模型在训练集上表现很好一到真实场景就露馅泛化能力不足。单麦克风模型的泛化能力尤其吃训练数据的多样性噪声种类、房间混响、麦克风硬件差异都会影响效果。推理延迟和算力开销。手机上的DNN模型要跑在NPU或DSP上模型量化后性能退化多少、延迟是否满足通话要求都是需要实测的。误差是不可解释的。传统方法出了奇怪的问题还能通过观察频谱图判断是哪一步处理引入的DNN一旦出问题你很难定位是数据问题、模型结构问题还是后处理问题。我的建议是如果做低功耗嵌入式产品传统方法依然是可靠的选择如果做旗舰手机或高端耳机可以在传统的链路上加一层轻量级DNN模块。两者结合往往比单独使用任何一种效果都好。现实中很多中高端TWS耳机的通话降噪就是这种混合方案。3. 回声消除自适应滤波器在工程里的真实面目3.1 回声不是声学回声是电学回声先澄清一个概念。很多人以为回声消除处理的是声音在房间里反射产生的回声其实不是。通话回声主要是电学回声是扬声器播放出来的远端信号被本地麦克风重新采集又传回远端远端的人就听到了自己的声音还带延迟。这个过程叫声学回声Acoustic Echo Coupling。声学回声消除AECAcoustic Echo Cancellation要做的事情就是在本地麦克风信号里消除扬声器播放的远端参考信号成分。这个参考信号在算法内部是现成的——不用去猜远端播放什么我们知道。所以回声消除本质上是一个系统辨识问题估计扬声器-麦克风之间的声学路径冲激响应再用这个估计值去预测麦克风里会收到多少回声成分然后减去它。3.2 NLMS自适应滤波的核心逻辑路径冲激响应是随时间变化的——你手一挡在扬声器前或者人站起来走两步声学路径就变了。所以AEC滤波器必须是自适应的最经典的就是NLMS归一化最小均方算法。LMS的更新逻辑很简单每次迭代用当前滤波器的输出和期望信号的误差反向调整滤波器系数让误差的均方值逐渐减小。NLMS在LMS基础上做了归一化把更新步长除以输入信号的能量避免信号太大时一步走过头。实际工程里NLMS几乎是所有商用AEC的基石。但NLMS有几个问题需要处理收敛速度与稳态误差的矛盾。步长大收敛快但稳态回声残留也大步长小收敛慢但稳态效果好。解决方法是变步长回声路径变化大时用大步长快速跟踪路径稳定时用小步长精细收敛。滤波器长度选择。采样率16kHz时一个房间的冲激响应可能长达数百毫秒也就是几千个滤波器抽头。算力有限时滤波器长度不够就无法完全覆盖真实回声路径性能打折。双讲问题。这是AEC里最麻烦的问题。当本地人说话时麦克风信号里既有回声成分也有近端语音。自适应滤波器的误差信号同时包含残差回声和近端语音近端语音会严重干扰系数更新导致滤波器发散。3.3 双讲检测最容易翻车的环节双讲Double-Talk检测就是判断当前时刻本地人有没有在说话。如果检测到双讲就要暂停滤波器系数更新防止近端语音破坏滤波器。这个环节看起来简单做起来却非常容易翻车。为什么难因为判断双讲不能只看麦克风信号能量。远端在放歌、本地人在安静说话时麦克风信号能量可能差不多。如果只靠能量阈值很容易误判。比如远端声音很大时近端语音被不完全遮挡双讲检测器可能漏检滤波器更新还是被污染了。比较可靠的方法是利用远端参考信号做相关性分析。如果麦克风信号里的大部分能量都能被自适应滤波器的输出解释说明主要是回声如果误差信号里包含很多与参考信号不相干的成分说明有近端语音。这个思路是Geigel算法和归一化互相关法的核心。工程实现上我一般用两步策略先做一个粗粒度的能量加互相关判断再结合语音活动检测VAD做平滑双讲状态的切换要加迟滞避免状态频繁跳变。AEC整套链路在配置时还有几个关键参数滤波器长度、参考信号延迟补偿远端参考信号从送到扬声器到被麦克风采回来有一段物理延迟、非线性处理NLP residual echo suppression的强度。其中NLP做过头了会把近端语音的尾音也切掉听感很硬做轻了回声又能听见。这个度只能靠大量主观听测来调。3.4 AEC与AGC、NS的组合顺序一个完整的通话音频链路通常是这样的麦克风进来 → AEC回声消除 → NS噪声抑制 → AGC自动增益控制 → 编码发送。这个顺序是有讲究的。AEC必须放在最前面因为它需要参考信号而且回声如果不先消掉后面NS会把回声当成正常信号的一部分去做增益放大反而让回声更明显。NS的噪声估计如果受到大量回声干扰也会很不稳定。AGC放在最后是为了让发出去的信号有统一稳定的响度但它会把前面处理残留的微弱噪声也放大。所以有些方案在AGC之后还会加一个轻量级的限幅器或背景噪声门。我见过很多项目为了省算力砍掉了AEC或者把NS放前面结果回声问题被放大后面怎么调都救不回来。这个顺序真不能乱。4. 主动降噪耳边的声波反相抵消4.1 主动降噪为什么能消掉噪声主动降噪ANC的原理说出来非常简单一个扬声器播放一个和外界噪声幅度相同、相位相反的声波两个声波在耳朵附近叠加噪声就被抵消了。听起来像科幻其实就是声波叠加原理——两个相位相反的波相遇幅值相互抵消。但落地就没那么简单了。真正的挑战在于噪声不是单一频率是宽带的噪声传播到耳机的位置需要时间数字信号处理本身有延迟耳机戴在每个人耳朵上的声学密封效果不一样所以ANC系统都是一套闭环控制系统前馈麦克风采集外部噪声反馈麦克风采集耳道内的残余噪声控制器实时计算扬声器应该给出什么信号。计算这个信号的过程本质上是设计一个合适的滤波器让误差麦克风处的噪声最小化。4.2 前馈、反馈、混合式怎么选从架构上ANC分成前馈、反馈和混合式三种前馈式Feedforward参考麦克风在耳机外侧采集外部噪声经过固定或自适应滤波器后送到扬声器。优点是不容易产生闭环不稳定的问题对风声、人说话声的泄漏有一定容忍度缺点是只能响应麦克风测到的噪声如果耳机戴歪了外部噪声到耳机内的路径变了降噪效果就会变差。反馈式Feedback只有耳道内的误差麦克风控制目标是让误差麦克风处的噪声最小。好处的对耳内残余噪声能精确抑制对佩戴方式不那么敏感缺点是它是一个闭环系统增益调得太高会啸叫不稳定而且会把近端语音的一部分也当噪声消掉。混合式Hybrid前馈和反馈结合低频段用反馈精确控制中高频段用前馈覆盖是目前中高端TWS耳机的标准做法。我的建议是如果是开发入门级产品前馈式完全够用成本和算法复杂度低且稳定性好如果目标是旗舰级降噪深度和自适应体验就得上混合式。但混合式也意味着两套麦克风阵列、两套滤波器、更多的调校工作项目周期会明显拉长。4.3 稳定性、泄漏和佩戴一致性ANC这块我踩得最多的是稳定性问题。反馈式ANC最怕的是在某个频点出现正反馈——相位差接近360°增益又足够大系统就会自激振荡在高频或低频出现啸叫。调试时我用扫频信号测试整个可听频段一旦发现某处啸叫就得降低反馈增益或者调整相位补偿。还有一个非常实际的问题是泄漏leakage。耳机佩戴不严耳道密封不完美低频降噪效果会大打折扣。很多厂商宣传的-35dB降噪深度通常是在理想密封条件下测出来的。真实佩戴下漏气会导致低频性能大幅下降这就是很多人觉得降噪似乎没宣传的那么神的原因之一。混合式ANC的反馈回路能部分补偿泄漏问题但漏得太狠还是无能为力。另外ANC在人说话时怎么处理也是一个关键痛点。降噪耳机把环境声消了的同时往往也会把你自己的说话声带上耳机里听起来像捂着耳朵说话。高端产品会做通透模式或语音模式通过麦克风把佩戴者自己的声音再采回来经过处理播放出去。解决的问题和ANC类似但需求恰好相反——一个是消除噪声一个是增强特定声音。这种既要消除环境噪声又要保留自己说话声的矛盾就是多麦克风阵列加精准的语音活动检测才能真正解决的。5. 四类算法协同工作的底层逻辑与调参经验5.1 音频处理链路从麦克风到扬声器的算法编排前面四章单独讲了四个算法但真实产品里它们不是孤立工作的而是串联在一条完整的音频处理链路上。以一台支持通话降噪的TWS耳机为例通话时多个麦克风采集信号先经过波束成形做空间滤波指向说话人的方向抑制侧向和后向噪声接着AEC消除扬声器播放的远端信号带来的回声然后NS做单通道或基于波束成形的多通道降噪再经过AGC统一响度限幅器防削波最后编码发送另一边播放音频时ANC系统在后台运行前馈和反馈麦克风协同工作产生反相声波。这个系统里任何一个模块出问题都会牵一发而动全身。比如波束成形指向不准AEC的参考信号和麦克风信号相关性就会下降回声消除效果变差NS估计噪声不准又会干扰AEC的双讲检测。所以算法工程师不能只管自己那一块要有全局思维。我每次接到新项目第一件事不是调参而是先把整条链路跑起来用标准测试信号整体测一遍分清哪个模块对哪个失真负责再针对短板做优化。5.2 参数调试的实测心得音频算法的参数调试不像调Web页面那样所见即所得而是一个听感、数据、波形三方对照的过程。我在实际项目里总结了一套工作流准备一套覆盖各种场景的测试素材安静室内、嘈杂街道、车内高速、多人对话、风噪声、键盘声等每种场景录10分钟以上每个场景同时录制原始信号和处理后信号方便做AB对比看数据计算PESQ、POLQA、STOI等客观指标看频段SNR提升和语音失真度主观听测戴上好耳机反复听原始和处理后的对比重点听有没有音乐噪声、语音是否发闷、有没有金属声、尾音是否被切调参后重复步骤3和4直到听感和指标都满意为止几个具体的踩坑经验双讲检测的迟滞区间我一般设为100ms左右太短容易误判太长会让近端语音的前几个字被削掉NLMS的步长参数我习惯先用离线数据做网格搜索找出一个初始值再在真机上微调。这个值跟硬件相关不同麦克风灵敏度、不同喇叭阻抗都会有影响谱减法的过减因子在信噪比低时不能设太大否则语音失真会非常明显。一个实用技巧是让过减因子随信噪比自适应变小这个思路基于噪声越大越不能下手太重的经验音频处理的帧长通常是16ms或20ms帧移是8ms或10ms50%重叠FFT点数按采样率来定。16kHz采样率时512点FFT比较常见48kHz时1024点更合适。改帧长和FFT点数之后所有后续参数都要重新调不能照搬5.3 如何验证算法效果客观指标与主观听感客观指标不是万能的。PESQ和POLQA对强降噪处理后的语音打分往往偏低因为强降噪必然伴随语音失真而指标不理解牺牲一点语音质量换取可懂度这种权衡。反过来有些算法客观指标很好但就是有一种机械感主观听感并不好。所以我的习惯是客观指标用于回归测试和基线比较主观听感用于最终拍板。单通道降噪的目标是让听感自然而不仅仅是让SNR变高。ANC的降噪深度除了测试设备测出来的插入损耗曲线还要实际戴到头上感受有没有压迫感、有没有啸叫、走路时风噪是不是突然变大。这些机器测不出来的东西恰恰是产品口碑的来源。还有一件事非常关键——算力余量。算法的理论性能和实际部署性能是两回事。我曾经在某款低功耗DSP上移植AEC算法浮点版本在PC上跑得好好的定点化之后性能掉了不少。所以从项目一开始就要确定好目标平台、算力上限和内存上限基于这个约束选算法而不是先选算法再考虑平台。聊到这里回到最开始那几个场景电话那头的背景噪声、会议里的回声、降噪耳机的闷塞感。这些用一句我优化一下算法就能解决吗其实背后是傅里叶变换把信号拆开语音增强把噪声按住回声消除把回声追回来主动降噪把声波倒过来然后它们再串在一条链路上协同工作。每一块单独拿出来都有几十年的学术积累但工程上的难点往往不在单点而在整体。我个人的一个深刻体会是音频算法这行理论水平决定了你的上限但听感和调试经验决定了你的下限。同一个算法新人和老手调出来的效果差距可能非常大。所以如果你刚入这行别急着追求最新最复杂的模型先把傅里叶变换的时频关系吃透把谱减法和NLMS动手实现一遍再用真实场景的数据去折磨自己的算法。这个过程积累下来的直觉比看一百篇论文都管用。最后说一个我自己的小习惯每次调完一版算法我都会用手机录一段真实环境下的语音通过自己产品的处理链路戴上一副好耳机闭上眼睛先听三遍。第一遍听语音的自然度第二遍听噪声的残余和音乐噪声第三遍专门找回声和延迟的痕迹。这三遍听下来算法行不行我心里基本就有数了。数据指标可以参考但最终服务的是人的耳朵这一点永远不要忘。