2. 语音降噪基础回顾
做语音算法部署这些年,我见过不少同事在滤波器参数调优时翻车。翻车的原因往往不是算法本身多复杂,而是对音频信号的基础概念理解不够扎实。说白了,采样率、量化、Nyquist 定理这些东西,就是咱们吃饭的碗。碗要是漏了,菜做得再香也白搭。
这一章咱们就把这些基础再过一遍。我会结合项目里踩过的坑来讲,争取让你看完就能用上。
2.1 采样率:时间轴上的离散化
模拟信号是连续的,计算机只能处理离散的点。采样,就是每隔一个固定的时间间隔,抓一个信号值。
这个间隔的倒数,就是采样率。单位是 Hz(赫兹)。
常见采样率速查表
| 采样率 | 典型用途 | 最高可表示频率 |
|---|---|---|
| 8 kHz | 电话语音、对讲机 | 4 kHz |
| 16 kHz | 语音识别、语音增强 | 8 kHz |
| 44.1 kHz | CD 音质、音乐 | 22.05 kHz |
| 48 kHz | 影视、专业音频 | 24 kHz |
我个人习惯在做降噪滤波器时,先用 16 kHz。为什么?因为人声的主要能量集中在 4 kHz 以下,16 kHz 采样率既能保留足够的信息,又能减少计算量。你想想看,在嵌入式设备上,每降低一倍的采样率,计算量可能减少 40% 以上。
经验之谈: 我曾经在一个项目里,为了追求所谓的"高保真",把采样率设成了 48 kHz。结果算法在开发板上跑不动,帧长调短了又导致频率分辨率不够。最后老老实实降到 16 kHz,问题全解决了。选采样率,够用就好。
2.2 量化:幅度轴上的离散化
采样解决了时间轴的问题,量化解决的是幅度轴的问题。每个采样点的幅度值,需要用有限个比特来表示。
最常见的量化位深是 16 位,也就是用 2^16 = 65536 个等级来表示一个采样点的幅度。听起来很多对吧?但在实际项目中,量化误差带来的噪声是真实存在的。
量化误差的公式很简单:
量化步长 Δ = 满量程范围 / 2^B
其中 B 是位深
量化噪声的功率大约是 Δ²/12。这个噪声是白噪声,均匀分布在 0 到 fs/2 的频带内。
注意: 量化噪声是加性的。如果你的信号本身动态范围很小,比如只有 -30 dBFS,而量化噪声在 -96 dBFS(16 位),那信噪比还有 66 dB,问题不大。但如果你做增益放大,把信号抬高了 40 dB,量化噪声也会被一起放大。我曾经在调试一个 ANC(主动降噪)算法时,就因为没注意量化噪声的放大,导致低频段出现了可闻的底噪。排查了两天才找到原因。
2.3 Nyquist 定理:采样率的底线
Nyquist 定理是音频信号处理里最硬的约束之一。它的核心就一句话:
采样率必须大于信号最高频率的两倍。
数学表达:fs > 2 * f_max
如果违反了这个定理,就会发生混叠(Aliasing)。混叠的意思是,高频信号会伪装成低频信号混进来,而且一旦混进来,就再也分不开了。
举个例子:
- 信号频率:7 kHz
- 采样率:8 kHz
- Nyquist 频率:4 kHz
- 结果:7 kHz 的信号会被混叠成 1 kHz 的信号(因为 8 - 7 = 1)
你想想看,本来想滤掉 7 kHz 的噪声,结果它变成了 1 kHz 的信号,跟人声混在一起。这滤波器还怎么设计?
避坑指南: 我在做语音采集前端时,经常发现 ADC 芯片自带的抗混叠滤波器不够陡峭。尤其是低成本方案,截止频率之后还有不少能量残留。我的做法是:在数字域再加一个低通滤波器,把 Nyquist 频率以上的能量再压一压。虽然多了一步计算,但能省掉后面无数的调试时间。
2.4 dB 与信噪比:衡量信号质量的标尺
dB(分贝)是个对数单位。为什么要用对数?因为人耳对声音的感知是对数的,而且音频信号的动态范围太大了,从 0 dB SPL(听阈)到 120 dB SPL(痛阈),差了 100 万倍。用线性坐标根本没法看。
常用的 dB 定义:
功率比:dB = 10 * log10(P1 / P2)
幅度比:dB = 20 * log10(A1 / A2)
信噪比(SNR)就是信号功率与噪声功率的比值,也用 dB 表示。
SNR 计算公式:
SNR(dB) = 10 * log10( P_signal / P_noise )
或者用幅度:SNR(dB) = 20 * log10( A_signal / A_noise )
在实际项目中,我经常用 SNR 来评估降噪滤波器的效果。比如:
- 输入 SNR:5 dB(噪声比信号还大)
- 输出 SNR:15 dB(提升了 10 dB)
- 这个滤波器就算合格了
但要注意,SNR 提升 10 dB 听起来不多,实际上噪声功率已经降到了原来的 1/10。人耳能明显感觉到区别。
注意: 计算 SNR 时,一定要保证信号和噪声是同步对齐的。我曾经犯过一个低级错误:用不同时间段的信号和噪声来计算 SNR,结果算出来 30 dB,实际听起来噪声大得离谱。后来才发现是时间窗没对齐。做评估时,时间对齐是第一步。
2.5 短时傅里叶变换(STFT):时频分析的基石
傅里叶变换告诉我们信号里有哪些频率成分,但它丢失了时间信息。对于语音这种非平稳信号,我们需要知道"在什么时间出现了什么频率"。STFT 就是干这个的。
STFT 的核心思想很简单:把信号切成一小段一小段,对每一段做傅里叶变换。
STFT 的步骤:
1. 选择一个窗函数(汉宁窗、海明窗等)
2. 将窗函数与信号相乘,截取一段
3. 对这一段做 FFT
4. 滑动窗函数,重复步骤 2-3
这里有两个关键参数:
- 帧长(window size): 每段信号的长度,决定了频率分辨率
- 帧移(hop size): 窗函数滑动的步长,决定了时间分辨率
帧长和帧移是一对矛盾:
| 参数 | 频率分辨率 | 时间分辨率 |
|---|---|---|
| 帧长大 | 高(能区分相近频率) | 低(时间定位模糊) |
| 帧长小 | 低(频率模糊) | 高(能捕捉瞬变) |
在降噪滤波器里,我一般这样选:
- 语音降噪:帧长 512 点(16 kHz 下约 32 ms),帧移 128 点(50% 重叠)
- 音乐降噪:帧长 1024 点(44.1 kHz 下约 23 ms),帧移 256 点
经验之谈: 窗函数的选择也很重要。汉宁窗(Hann)的旁瓣衰减好,适合做降噪;海明窗(Hamming)的主瓣窄一点,适合做频谱分析。我习惯用汉宁窗,因为它的旁瓣衰减更快,能减少频谱泄漏。有一次我在做噪声估计时,用了矩形窗,结果频谱泄漏导致噪声谱估计严重偏差,滤波器把语音也削掉了。换成汉宁窗后,问题立刻解决。
下面这张图展示了 STFT 的完整流程:
STFT 的输出是一个复数矩阵 X[m, k],其中 m 是帧索引,k 是频率索引。这个矩阵就是我们在频域做降噪处理的基础。
在后续章节中,我们会基于 STFT 的频谱来设计各种降噪滤波器。比如谱减法、维纳滤波、MMSE 估计等,都是在 STFT 的框架下工作的。
本章核心要点:
- 采样率决定时间分辨率,必须满足 Nyquist 定理
- 量化位深决定动态范围,16 位是语音处理的底线
- dB 是对数单位,SNR 是评估降噪效果的核心指标
- STFT 是时频分析的基础,帧长和帧移的选择直接影响算法性能