3、频域降噪方法:STFT、谱减法、维纳滤波与MMSE

频域降噪,说白了就是把声音从时域搬到频域去处理。为什么要这么折腾?因为噪声和语音在频域里更容易分开。我刚开始做这行时,总觉得时域滤波更直观,直到碰到一个非平稳噪声的案例——时域怎么调都压不住,换到频域,几行代码就解决了。

这一章我们聊四个核心方法:短时傅里叶变换(STFT)是频域处理的基础;谱减法是最直观的降噪思路;维纳滤波在频域里有个漂亮的闭合解;MMSE估计器则是统计最优的频域方法。嗯,一个一个来。

3.1 短时傅里叶变换(STFT)

STFT 是频域降噪的基石。你想想看,语音信号是非平稳的,但如果我们只取一小段(比如 20-40ms),这段信号可以近似看成平稳的。STFT 做的就是这件事——加窗、分帧、做 FFT。

核心公式:

X(m, k) = Σ x[n] · w[n - mR] · e^(-j2πkn/N)

其中 m 是帧索引,k 是频率索引,w[n] 是窗函数,R 是帧移。

我在项目中遇到过一个问题:窗函数选错了,降噪效果直接打对折。常用的窗函数有这些:

窗函数 主瓣宽度 旁瓣衰减 推荐场景
汉宁窗 较宽 -31dB 通用语音处理
汉明窗 较宽 -43dB 语音增强
布莱克曼窗 最宽 -58dB 高动态范围
矩形窗 最窄 -13dB 不推荐用于降噪

我的经验:帧长选 20-30ms,帧移 50% 重叠。汉宁窗是默认选择,频谱泄漏和频率分辨率之间平衡得最好。如果你做实时系统,帧移可以调到 75%,但计算量会翻倍。

STFT 的流程其实不复杂:

  1. 分帧:把信号切成重叠的帧
  2. 加窗:每帧乘以窗函数,减少频谱泄漏
  3. FFT:对每帧做 N 点 FFT,得到复数频谱
  4. 处理:在频域做降噪(谱减、维纳、MMSE 等)
  5. IFFT:逆变换回时域
  6. 重叠相加:把处理后的帧拼接成完整信号

这里有个坑——重叠相加处理不好,会有明显的块状噪声。我曾经调试一个降噪算法,降噪效果很好,但听起来像有人在"打嗝"。查了两天才发现是重叠相加的增益补偿没做对。

注意:STFT 的时频分辨率受海森堡不确定性原理限制。时间分辨率高,频率分辨率就低,反之亦然。没有完美的窗函数,只有适合你场景的窗函数。

3.2 谱减法

谱减法是最直观的频域降噪方法。它的思路很简单:带噪语音的幅度谱 - 噪声幅度谱 = 干净语音幅度谱。配合带噪语音的相位,就能重建出干净语音。

基本公式:

|Ŝ(k)| = |X(k)| - |N̂(k)|  如果结果大于 0
|Ŝ(k)| = 0                  如果结果小于 0

这里 |X(k)| 是带噪语音的幅度谱,|N̂(k)| 是估计的噪声幅度谱。相位直接用带噪语音的相位——人耳对相位不敏感,这个近似在大多数场景下都够用。

但谱减法有个致命问题:音乐噪声。你想想看,当 |X(k)| 接近 |N̂(k)| 时,相减后会出现随机的小峰值,听起来像背景里有人在弹钢琴。嗯,这就是音乐噪声。

改进方案:

  • 过减因子 α:|Ŝ(k)| = |X(k)| - α · |N̂(k)|,α > 1 可以压得更狠,但音乐噪声更明显
  • 谱底限 β:|Ŝ(k)| = max(|X(k)| - α · |N̂(k)|, β · |X(k)|),β 通常取 0.01-0.1
  • 平滑处理:对估计的幅度谱做时域或频域平滑

我在项目中用过一种"自适应谱减法":根据当前帧的信噪比动态调整 α 和 β。信噪比高时 α 小一点,保留更多语音细节;信噪比低时 α 大一点,压得更狠。效果比固定参数好不少。

3.3 维纳滤波的频域实现

维纳滤波在时域里推导起来挺复杂,但在频域里,它有一个漂亮的闭合解。说白了,维纳滤波就是找一个最优的滤波器 H(k),使得估计误差的均方值最小。

频域维纳滤波器的形式:

H(k) = ξ(k) / (1 + ξ(k))

其中 ξ(k) = Ps(k) / Pn(k) 是先验信噪比,Ps(k) 是语音功率谱,Pn(k) 是噪声功率谱。

你看这个公式,当信噪比很高时(ξ(k) >> 1),H(k) ≈ 1,滤波器几乎不衰减;当信噪比很低时(ξ(k) << 1),H(k) ≈ ξ(k),滤波器大幅衰减。这个特性很符合直觉——信噪比高的频段保留,信噪比低的频段压制。

实际实现的关键:先验信噪比 ξ(k) 需要估计。常用的方法是"判决引导法"(Decision-Directed):

ξ(m, k) = α · |Ŝ(m-1, k)|² / Pn(m, k) + (1-α) · max(γ(m, k) - 1, 0)

其中 α 通常取 0.98,γ(m, k) 是后验信噪比。这个方法能有效减少音乐噪声。

维纳滤波比谱减法好在哪里?我个人的体会是:维纳滤波的过渡更平滑。谱减法在信噪比临界点附近会有"硬切"的感觉,而维纳滤波是连续衰减的,听起来更自然。

但维纳滤波也有缺点:它假设语音和噪声都是平稳的,而且需要准确的噪声功率谱估计。如果噪声估计不准,维纳滤波的效果还不如谱减法。

3.4 MMSE 估计器

MMSE(最小均方误差)估计器,是频域降噪的"天花板"方法之一。它从统计最优的角度出发,直接估计干净语音的幅度谱。

Ephraim 和 Malah 在 1984 年提出的 MMSE-STSA 估计器,公式长这样:

|Ŝ(k)| = G(ξ(k), γ(k)) · |X(k)|

其中增益函数 G 是:

G(ξ, γ) = √(ξ/(1+ξ)) · Γ(1.5) · M(-0.5; 1; -ν) / γ

这里 Γ 是伽马函数,M 是合流超几何函数,ν = ξ·γ/(1+ξ)。

说实话,这个公式看着挺吓人的。我第一次实现时,查了半天合流超几何函数的数值计算方法。后来发现,可以用查表法或者多项式近似来加速。

MMSE 的核心优势:

  • 理论上最优——在均方误差准则下,没有比它更好的估计器
  • 音乐噪声最少——因为它是统计意义上的最优估计,不会出现谱减法那种随机峰值
  • 对低信噪比场景特别有效——-5dB 甚至 -10dB 下仍有可用的降噪效果

MMSE 的变体也很多:

变体名称 估计目标 特点
MMSE-STSA 短时幅度谱 原始版本,计算量大
MMSE-LSA 对数幅度谱 对语音失真更不敏感,听感更好
MMSE-SP 语音存在概率 结合 VAD,减少噪声过估计

我在项目中用过 MMSE-LSA,听感确实比谱减法好很多。但代价是计算量——在嵌入式平台上,MMSE 的实时实现需要做不少优化。比如用多项式近似代替合流超几何函数,或者用查表法预计算增益值。

注意:MMSE 估计器对噪声功率谱的估计误差非常敏感。如果噪声估计偏差超过 3dB,MMSE 的效果可能还不如一个调参调好的谱减法。所以,噪声跟踪算法(比如最小值跟踪、MCRA)是 MMSE 的前置必修课。

3.5 四种方法的对比与选择

说了这么多,到底该用哪个?我画了一张对比图,帮你快速决策:

频域降噪方法对比与选择 STFT 频域处理基础 谱减法 简单直观 维纳滤波 最优线性估计 MMSE 统计最优 评价维度:计算复杂度 · 降噪效果 · 音乐噪声 · 语音失真 谱减法 维纳滤波 MMSE 推荐场景 计算量低 音乐噪声多 计算量中等 过渡平滑 计算量高 音乐噪声少 实时系统 → 谱减法 高质量 → MMSE 我的建议 原型阶段用谱减法快速验证 → 产品阶段升级到维纳滤波或 MMSE

选型建议:

  • 资源受限的嵌入式平台:谱减法 + 过减因子 + 谱底限,调参到位效果够用
  • 实时通信系统:维纳滤波,计算量和效果平衡得最好
  • 高保真语音处理:MMSE-LSA,听感最好,但要做好计算优化
  • 科研或离线处理:直接上 MMSE,或者它的改进版(如 MMSE-SP)

最后说一句:没有银弹。我在项目中见过有人花三个月调 MMSE 的参数,效果还不如一个调了两周的谱减法。算法选型要结合你的硬件平台、实时性要求和听感目标来定。先跑通一个简单的版本,再逐步优化,这是最稳妥的路。