4、时域特征提取(下):自相关函数、基频(F0)估计、音量包络提取
上一章我们聊了短时能量、过零率和短时自相关。说实话,那些都是热身。这一章才是时域特征里真正硬核的东西——基频估计和音量包络。
基频是什么?说白了就是声音的基本振动频率。人说话、唱歌、乐器发声,都有个最低的重复频率,那就是F0。你想想看,为什么男声低沉、女声尖细?就是因为基频不同。男声大概在85-180Hz,女声在165-255Hz,小孩能到300Hz以上。
音量包络呢?它描述的是声音响度随时间的变化曲线。没有包络,声音就是平的、死的。有了包络,你才能听出“哒哒哒”的打击感,或者“呜——”的持续感。
好,我们一个一个来拆。
4.1 自相关函数(ACF)——基频估计的基石
基频估计的方法很多,但最经典、最直观的,就是自相关函数法。我在项目里做语音性别识别时,用的就是它。
自相关的核心思想很简单:把一个信号和它自己延迟后的版本做相关。如果延迟量刚好等于基频周期,两个波形就会高度重合,相关值达到峰值。
数学上,对于离散信号 x[n],自相关函数定义为:
R[k] = Σ x[n] · x[n + k] (n从0到N-1-k)
其中 k 是延迟量(lag)。R[k] 越大,说明延迟 k 个样本后信号和原信号越像。
嗯,这里要注意:直接计算自相关,计算量很大。N个点,k从0到N-1,复杂度是O(N²)。我早期做嵌入式开发时,芯片算力有限,根本跑不动。后来改用快速自相关——先做FFT,频域相乘,再IFFT回来,复杂度降到O(N log N)。
4.2 基频(F0)估计——从自相关到精确频率
有了自相关函数,找基频就变成了找峰值。但这里有个坑——自相关函数在 k=0 处永远是最大值(因为信号和自己完全对齐)。所以我们要跳过 k=0,在合理延迟范围内找第一个显著峰值。
具体步骤:
- 预处理:对信号加窗(汉明窗),去除直流分量。
- 计算自相关:用快速自相关或直接计算。
- 峰值检测:在预设延迟范围内找最大值。
- 频率换算:F0 = 采样率 / 峰值对应的延迟。
代码实现大概是这样:
def estimate_f0(x, fs, f0_min=50, f0_max=500):
# 计算自相关
N = len(x)
lag_min = int(fs / f0_max)
lag_max = int(fs / f0_min)
# 只计算需要的延迟范围
R = np.zeros(lag_max - lag_min)
for k in range(lag_min, lag_max):
R[k - lag_min] = np.sum(x[:N-k] * x[k:])
# 找峰值
peak_idx = np.argmax(R) + lag_min
f0 = fs / peak_idx
return f0
我曾经在一个语音合成项目里用这个算法,结果发现清音段(辅音)也会算出基频,而且值乱跳。后来加了 voiced/unvoiced 判决——如果自相关峰值低于某个阈值,就判定为清音,F0置为0或NaN。
4.3 音量包络提取——让声音“活”起来
音量包络,说白了就是声音响度随时间变化的轮廓。它不像瞬时能量那样毛刺多,而是平滑地勾勒出声音的“形状”。
提取包络最常用的方法是希尔伯特变换。希尔伯特变换能构造出信号的解析信号,取模就得到瞬时包络。
数学上:
解析信号: x_a(t) = x(t) + j · H{x(t)}
瞬时包络: e(t) = |x_a(t)| = sqrt(x(t)² + H{x(t)}²)
其中 H{x(t)} 是 x(t) 的希尔伯特变换。
代码实现:
def hilbert_envelope(x):
# 希尔伯特变换
analytic = scipy.signal.hilbert(x)
envelope = np.abs(analytic)
return envelope
你想想看,这个包络有什么用?动态范围压缩、自动增益控制、语音端点检测,都离不开它。我在做音频剪辑软件时,就用包络来做“淡入淡出”——检测到包络低于阈值就自动切掉静音段。
不过希尔伯特变换有个问题:计算量大,而且对非平稳信号效果一般。实际工程中,我更喜欢用低通滤波法:
- 对信号取绝对值(全波整流)。
- 通过一个低通滤波器(截止频率通常设为10-50Hz)。
- 得到平滑的包络曲线。
这个方法简单粗暴,但效果足够好。我做过对比,在语音信号上,低通滤波法和希尔伯特法的包络形状几乎一致,但前者速度快了10倍。
4.4 本章知识体系
下面这张图,把本章的核心逻辑串起来了。从原始信号出发,经过自相关函数得到基频估计,经过包络提取得到音量轮廓。两条线并行,互不干扰。
4.5 工程实践中的常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 基频跳变(抖动) | 噪声干扰、倍频/半频错误 | 中值滤波平滑、加VAD判决、限制F0变化率 |
| 包络过冲 | 滤波器阶数太低、截止频率过高 | 提高滤波器阶数、降低截止频率 |
| 实时性不足 | 自相关计算量太大 | 用FFT加速、只计算有效延迟范围 |
| 清音段误判 | 自相关峰值阈值设置不当 | 动态阈值、结合能量特征做二次判决 |