3. 噪声类型与特性
做语音降噪,说白了就是跟时域信号打交道。你连信号长什么样都不清楚,后面那些滤波器、神经网络再花哨也白搭。这一节我把最核心的几个概念串起来讲,都是我在项目里天天用的东西。
3.1 采样率与量化位数
模拟信号是连续的,计算机只能处理离散的点。怎么把连续信号变成离散点?两个步骤:采样和量化。
采样,就是每隔一段时间取一个值。这个间隔的倒数就是采样率。比如 16kHz 采样率,意味着每秒取 16000 个点。我最早做语音增强时,客户给的数据是 8kHz 采样的,听起来糊得不行,高频信息全丢了。后来我学乖了,拿到数据先看采样率。
量化,是把采样得到的电压值用有限个二进制位表示。16 位量化,就是每个采样点用 16 个 bit 存,能表示 65536 个不同的幅度等级。位数越高,动态范围越大,底噪越低。
| 参数 | 常见值 | 说明 |
|---|---|---|
| 采样率 | 8kHz / 16kHz / 44.1kHz | 语音常用 16kHz,音乐用 44.1kHz |
| 量化位数 | 16 bit / 24 bit / 32 bit float | 16 bit 是语音标配,32 bit float 用于中间处理 |
| 比特率 | 采样率 × 量化位数 × 声道数 | 16kHz × 16bit × 1 = 256kbps |
3.2 奈奎斯特定理
这个定理很简单:采样率必须大于信号最高频率的两倍,否则就会发生混叠。混叠是什么?就是高频信号伪装成低频信号混进来,你根本分不清。
举个例子。语音信号最高频率通常认为是 8kHz(实际上大部分能量在 4kHz 以下),所以 16kHz 采样率刚好满足奈奎斯特条件。如果你用 8kHz 采样率去采一个 5kHz 的信号,采出来的结果看起来像 3kHz 的信号——这就是混叠。
所以,部署时要注意三点:
- 采样率必须大于信号最高频率的两倍
- ADC 前必须有抗混叠低通滤波器(硬件或软件)
- 降采样前必须先做低通滤波,再抽取
3.3 分帧
语音信号是非平稳的,但它在很短的时间内(10-30ms)可以看作是平稳的。所以我们把信号切成一段一段的帧来处理。
分帧有两个参数:帧长和帧移。帧长一般取 20-30ms,帧移通常取帧长的一半,这样相邻帧之间有 50% 的重叠。为什么要重叠?因为加窗后帧两端的信号会被削弱,重叠可以保证信息不丢失。
以 16kHz 采样率、25ms 帧长为例:
- 帧长 = 0.025 × 16000 = 400 个采样点
- 帧移 = 200 个采样点(50% 重叠)
- 每秒帧数 = 16000 / 200 = 80 帧
3.4 加窗
分帧之后,每一帧的两端是截断的。直接做 FFT 会产生频谱泄漏——就是能量从主瓣泄漏到旁瓣,看起来像多了很多虚假的频率成分。加窗就是为了缓解这个问题。
常用的窗函数有三种:
| 窗函数 | 主瓣宽度 | 旁瓣衰减 | 适用场景 |
|---|---|---|---|
| 矩形窗 | 最窄 | -13dB | 几乎不用,频谱泄漏严重 |
| 汉明窗 | 较宽 | -43dB | 语音处理最常用 |
| 汉宁窗 | 较宽 | -32dB | 噪声估计、谱减法常用 |
汉明窗和汉宁窗长得像,但公式差一点:
汉明窗: w[n] = 0.54 - 0.46 × cos(2πn/N)
汉宁窗: w[n] = 0.50 - 0.50 × cos(2πn/N)
我个人习惯在降噪算法里用汉明窗。它的旁瓣衰减更大,能更好地抑制频谱泄漏。但如果你做的是谱减法,汉宁窗的旁瓣衰减虽然小一点,但主瓣更平滑,反而更适合。
3.5 短时能量与过零率
这两个特征在语音端点检测(VAD)里用得最多。虽然现在深度学习 VAD 很流行,但传统方法在低资源设备上依然有优势。
短时能量,就是一帧内所有采样点的平方和。公式很简单:
E = Σ x[n]² (n = 0, 1, ..., N-1)
能量高的帧大概率是语音,能量低的帧大概率是静音或背景噪声。但要注意,噪声的能量也可能很高,比如有人在旁边敲键盘。所以单靠能量做 VAD 不够可靠。
过零率,就是一帧内信号穿过零轴的次数。语音的过零率比噪声低,因为语音主要集中在低频,而噪声(尤其是白噪声)高频成分多,过零率自然高。
我做过一个项目,在 Cortex-M4 上跑实时 VAD,只用能量和过零率两个特征,配合一个简单的阈值判断,就能在安静环境下达到 95% 以上的准确率。代码量不到 50 行,延迟只有一帧。
// 短时能量计算
float compute_energy(float *frame, int frame_len) {
float energy = 0.0f;
for (int i = 0; i < frame_len; i++) {
energy += frame[i] * frame[i];
}
return energy;
}
// 过零率计算
int compute_zcr(float *frame, int frame_len) {
int zcr = 0;
for (int i = 1; i < frame_len; i++) {
if ((frame[i] > 0 && frame[i-1] < 0) ||
(frame[i] < 0 && frame[i-1] > 0)) {
zcr++;
}
}
return zcr;
}
3.6 本章知识体系
下面这张图把本章的核心概念串起来了。从模拟信号到数字信号,再到分帧加窗和特征提取,每一步都有对应的部署要点。
这张图从左到右展示了信号处理的完整流程。左边是信号数字化,中间是分帧加窗和特征提取,右边是部署时要注意的工程细节。你想想看,每一步都有坑,但每一步也都有成熟的解决方案。
好了,时域信号基础就讲到这里。这些概念在后面的章节里会反复用到,尤其是分帧加窗和特征提取,几乎是每个降噪算法的起点。你先把这些基础打牢,后面学滤波器设计和神经网络部署时就会轻松很多。