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)。

实用技巧: 实际工程中,我们通常只计算 k 在合理基频范围内的自相关值。比如语音信号,基频最低50Hz,最高500Hz,对应延迟范围就是 采样率/500 到 采样率/50。只算这个区间,能省一大半计算量。

4.2 基频(F0)估计——从自相关到精确频率

有了自相关函数,找基频就变成了找峰值。但这里有个坑——自相关函数在 k=0 处永远是最大值(因为信号和自己完全对齐)。所以我们要跳过 k=0,在合理延迟范围内找第一个显著峰值。

具体步骤:

  1. 预处理:对信号加窗(汉明窗),去除直流分量。
  2. 计算自相关:用快速自相关或直接计算。
  3. 峰值检测:在预设延迟范围内找最大值。
  4. 频率换算: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。

避坑指南: 我曾经在噪声环境下做基频估计,结果惨不忍睹。自相关法对噪声敏感,尤其是低频噪声。建议先做带通滤波(比如80-400Hz),把基频范围以外的能量滤掉。另外,倍频错误也很常见——算法可能把2倍基频或1/2倍基频当成真实值。可以用倒谱法或YIN算法做二次校验。

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

你想想看,这个包络有什么用?动态范围压缩、自动增益控制、语音端点检测,都离不开它。我在做音频剪辑软件时,就用包络来做“淡入淡出”——检测到包络低于阈值就自动切掉静音段。

不过希尔伯特变换有个问题:计算量大,而且对非平稳信号效果一般。实际工程中,我更喜欢用低通滤波法:

  1. 对信号取绝对值(全波整流)。
  2. 通过一个低通滤波器(截止频率通常设为10-50Hz)。
  3. 得到平滑的包络曲线。

这个方法简单粗暴,但效果足够好。我做过对比,在语音信号上,低通滤波法和希尔伯特法的包络形状几乎一致,但前者速度快了10倍。

核心要点: 音量包络提取的关键参数是低通截止频率。设得太高,包络会跟着波形细节抖动;设得太低,包络反应太慢,跟不上声音变化。我一般设20Hz左右,对语音和音乐都适用。

4.4 本章知识体系

下面这张图,把本章的核心逻辑串起来了。从原始信号出发,经过自相关函数得到基频估计,经过包络提取得到音量轮廓。两条线并行,互不干扰。

时域特征提取知识体系 时域特征提取(下)知识体系 原始音频信号 自相关函数 (ACF) 音量包络提取 峰值检测 频率换算 F0 = fs / lag 希尔伯特变换 / 低通滤波 平滑包络曲线 F0 + 包络 联合特征 两条路径独立计算,最终融合为联合特征

4.5 工程实践中的常见问题

问题 原因 解决方案
基频跳变(抖动) 噪声干扰、倍频/半频错误 中值滤波平滑、加VAD判决、限制F0变化率
包络过冲 滤波器阶数太低、截止频率过高 提高滤波器阶数、降低截止频率
实时性不足 自相关计算量太大 用FFT加速、只计算有效延迟范围
清音段误判 自相关峰值阈值设置不当 动态阈值、结合能量特征做二次判决