2. 数字音频基础
说实话,刚入行做音频处理那会儿,我总觉得时域信号看着就挺直观的——波形上下跳,振幅大小一目了然。但直到第一次调试降噪算法,我才发现光看时域波形根本看不出噪声藏在哪个频率段。那次折腾了两周,最后用频谱图一看,问题瞬间就清楚了。
所以这一章,咱们就把傅里叶变换、短时傅里叶变换和频谱图这些东西彻底聊透。你想想看,音频DSP里百分之八十的问题,最后都要回到频域去解决。
为什么非要去频域?
时域信号描述的是「什么时间发生了什么」,频域描述的是「什么频率发生了什么」。说白了,就是换了个角度看问题。
举个例子,你录了一段吉他声,时域波形看起来就是一堆乱糟糟的振动。但转到频域,你能清楚看到基频在哪个位置,泛音怎么分布,甚至能分辨出是哪根弦在响。
核心观点:时域和频域是同一信号的两个投影,没有谁更「真实」,只有谁更适合当前任务。
傅里叶变换:从时域到频域的桥梁
傅里叶变换的数学公式长这样:
X(f) = ∫ x(t) · e^(-j2πft) dt
别被积分符号吓到。我习惯这么理解:把信号x(t)和不同频率的正弦波做内积,如果某个频率分量存在,内积结果就大;不存在,结果就接近零。
离散版本(DFT)才是我们实际用的:
X[k] = Σ x[n] · e^(-j2πkn/N), n=0..N-1
快速傅里叶变换(FFT)只是DFT的高效算法实现。我在项目中几乎从不手写FFT,直接用成熟库就好,但必须理解它的几个关键参数:
| 参数 | 含义 | 我的经验值 |
|---|---|---|
| FFT点数(N) | 频率分辨率 = 采样率 / N | 通常1024或2048 |
| 采样率(fs) | 决定可分析的最高频率 = fs/2 | 音频常用44100或48000 |
| 频率分辨率 | 相邻频点间隔 | 1024点@44100Hz ≈ 43Hz |
注意:FFT点数越大,频率分辨率越高,但时间分辨率会下降。这是傅里叶变换的固有矛盾——你不可能同时获得无限精确的时间和频率信息。
短时傅里叶变换(STFT):给信号加窗
标准的傅里叶变换假设信号是平稳的。但音频信号呢?语音、音乐都是非平稳的——频率成分随时间变化。怎么办?
STFT的思路很简单:把信号切成一小段一小段,假设每段内信号是平稳的,然后对每段做FFT。
STFT{x[n]}(m, ω) = Σ x[n] · w[n-m] · e^(-jωn)
其中w[n]是窗函数。我常用的窗函数有这些:
| 窗类型 | 主瓣宽度 | 旁瓣衰减 | 我什么时候用 |
|---|---|---|---|
| 汉宁窗 | 较宽 | -31dB | 通用场景,默认选择 |
| 汉明窗 | 较宽 | -43dB | 语音处理,旁瓣抑制更好 |
| 布莱克曼窗 | 最宽 | -58dB | 需要极低频谱泄漏时 |
| 矩形窗 | 最窄 | -13dB | 几乎不用,泄漏太严重 |
我曾经在一个语音增强项目里,默认用了矩形窗,结果频谱图上全是假峰,折腾了两天才发现是窗函数选错了。嗯,从那以后我再也不敢随便用矩形窗了。
频谱图:把STFT结果画出来
频谱图就是把STFT的结果用图像展示出来:
- 横轴:时间
- 纵轴:频率
- 颜色/亮度:能量强度
我习惯用Python的librosa库来画频谱图,代码大概这样:
import librosa
import librosa.display
import matplotlib.pyplot as plt
# 加载音频
y, sr = librosa.load('guitar.wav', sr=44100)
# 计算STFT
D = librosa.stft(y, n_fft=2048, hop_length=512, win_length=2048)
# 转换为dB刻度
D_db = librosa.amplitude_to_db(abs(D), ref=np.max)
# 显示频谱图
plt.figure(figsize=(12, 6))
librosa.display.specshow(D_db, sr=sr, hop_length=512, x_axis='time', y_axis='hz')
plt.colorbar(format='%+2.0f dB')
plt.title('频谱图')
plt.tight_layout()
读频谱图有几个要点:
- 看横条纹:代表某个频率持续存在,比如电源噪声的50Hz/60Hz
- 看竖条纹:代表某个时刻的瞬态冲击,比如鼓点、拍手声
- 看谐波结构:等间距的水平亮线,代表乐器的泛音列
- 看能量分布:颜色越亮,能量越强
小技巧:调试降噪算法时,我习惯先看频谱图找到噪声的「频率指纹」,再针对性设计滤波器。比盲目调参数高效十倍。
STFT参数怎么选?
这是实际项目中最常遇到的问题。我一般按这个流程来:
- 先定帧长:语音用20-30ms,音乐用40-60ms
- 再定帧移:通常取帧长的25%-50%
- 最后选窗函数:默认汉宁窗,特殊需求再换
举个例子,采样率44100Hz,帧长取2048点(约46ms),帧移512点(约11.6ms),汉宁窗。这个配置在大多数音频任务里表现都不错。
避坑:帧移太大(比如超过75%帧长)会导致频谱图时间分辨率太差,瞬态事件被模糊掉。帧移太小(比如小于12.5%)则计算量暴增,而且相邻帧之间信息冗余严重。
知识体系总览
下面这张图把本章的核心逻辑串起来了:
从这张图能清楚看到:时域信号通过傅里叶变换到频域,但非平稳信号需要STFT这个「升级版」。STFT的三个参数(帧长、帧移、窗函数)直接决定了频谱图的质量,而频谱图又是我们做音频分析、降噪、特征提取的基础工具。
我个人习惯在项目初期先用默认参数跑一遍频谱图,看看整体情况,再根据具体任务微调参数。比如做语音端点检测,我会用短帧(20ms)提高时间精度;做音高分析,我会用长帧(60ms)提高频率精度。
嗯,这一章的内容就到这里。记住一句话:频谱图是音频DSP工程师的「听诊器」——不会看频谱图,很多问题你根本找不到病因。