3. 语音特征提取
任务目标
本案例实现语音特征提取中的MFCC(Mel频率倒谱系数)计算流程。输入为一帧时域语音信号(采样率16kHz,帧长25ms,即400个采样点),输出为该帧对应的13维MFCC特征向量(不含第0维能量)。代码使用Python和NumPy实现,不依赖第三方音频处理库,便于理解算法内部机制。
核心实现
import numpy as np
def pre_emphasis(signal, alpha=0.97):
"""预加重:提升高频分量"""
return np.append(signal[0], signal[1:] - alpha * signal[:-1])
def framing(signal, sample_rate=16000, frame_len=0.025, frame_shift=0.010):
"""分帧:返回 (num_frames, frame_len_samples) 数组"""
frame_len_samples = int(sample_rate * frame_len)
frame_shift_samples = int(sample_rate * frame_shift)
num_frames = 1 + (len(signal) - frame_len_samples) // frame_shift_samples
frames = np.stack([signal[i * frame_shift_samples: i * frame_shift_samples + frame_len_samples]
for i in range(num_frames)])
return frames
def hz_to_mel(hz):
return 2595 * np.log10(1 + hz / 700)
def mel_to_hz(mel):
return 700 * (10 ** (mel / 2595) - 1)
def mel_filterbank(sample_rate=16000, n_fft=512, n_mels=26):
"""构造Mel滤波器组,返回 (n_mels, n_fft//2+1) 矩阵"""
low_mel = hz_to_mel(0)
high_mel = hz_to_mel(sample_rate / 2)
mel_points = np.linspace(low_mel, high_mel, n_mels + 2)
hz_points = mel_to_hz(mel_points)
bin_points = np.floor((n_fft + 1) * hz_points / sample_rate).astype(int)
filterbank = np.zeros((n_mels, n_fft // 2 + 1))
for m in range(1, n_mels + 1):
left, center, right = bin_points[m - 1], bin_points[m], bin_points[m + 1]
for k in range(left, center):
filterbank[m - 1, k] = (k - left) / (center - left)
for k in range(center, right):
filterbank[m - 1, k] = (right - k) / (right - center)
return filterbank
def mfcc(frame, sample_rate=16000, n_mels=26, n_mfcc=13):
"""
计算单帧MFCC特征
输入: frame - 一帧时域信号 (400,)
输出: (13,) 维MFCC向量
"""
# 1. 预加重
emphasized = pre_emphasis(frame)
# 2. 加窗(汉明窗)
windowed = emphasized * np.hamming(len(emphasized))
# 3. FFT(512点,不足补零)
n_fft = 512
spectrum = np.fft.rfft(windowed, n=n_fft)
power = np.abs(spectrum) ** 2
# 4. Mel滤波
filterbank = mel_filterbank(sample_rate, n_fft, n_mels)
mel_energy = np.dot(filterbank, power)
# 5. 取对数
log_mel = np.log(mel_energy + 1e-10)
# 6. DCT-II(取前n_mfcc个系数)
n_frames = len(log_mel)
dct_coeffs = np.zeros(n_mfcc)
for k in range(n_mfcc):
dct_coeffs[k] = np.sum(log_mel * np.cos(np.pi * k * (2 * np.arange(n_frames) + 1) / (2 * n_frames)))
dct_coeffs *= np.sqrt(2 / n_frames)
return dct_coeffs
# ===== 最小验证闭环 =====
if __name__ == "__main__":
# 构造测试信号:400点、16kHz的1kHz正弦波(模拟一帧语音)
sample_rate = 16000
t = np.arange(400) / sample_rate
test_frame = 0.5 * np.sin(2 * np.pi * 1000 * t)
# 计算MFCC
mfcc_vec = mfcc(test_frame)
# 验证输出维度
assert mfcc_vec.shape == (13,), f"输出维度错误: {mfcc_vec.shape}"
# 验证数值有限性
assert np.all(np.isfinite(mfcc_vec)), "MFCC包含非有限值"
# 验证DCT系数能量集中性(前几阶系数应显著大于高阶系数)
energy_ratio = np.sum(np.abs(mfcc_vec[:3])) / (np.sum(np.abs(mfcc_vec)) + 1e-12)
assert energy_ratio > 0.5, f"前3阶系数能量占比异常: {energy_ratio:.3f}"
print("MFCC维度: 13")
print("前5个系数:", np.round(mfcc_vec[:5], 4))
print("验证通过: 输出维度正确,数值有限,能量集中性合理")
运行与验证
环境依赖:仅需Python 3.6+和NumPy(pip install numpy)。
运行方式:将上述代码保存为 mfcc_extract.py,在终端执行 python mfcc_extract.py。
验证判定标准:
- 程序无异常退出,输出
MFCC维度: 13; - 输出
前5个系数为有限数值; - 显示
验证通过,说明三个断言全部满足:维度正确、数值有限、前3阶系数能量占比大于50%(符合语音信号DCT能量集中的特性)。
关键说明
本实现覆盖MFCC完整计算链:预加重→分帧(单帧输入已隐含)→加窗→FFT→Mel滤波→对数→DCT。代码中framing函数已提供但未在mfcc()中调用,因为本案例输入为单帧;若处理连续语音,需先调用framing对整段信号分帧,再逐帧调用mfcc()。Mel滤波器组采用26个滤波器,输出取前13维DCT系数(不含第0维能量项),这是语音唤醒任务中常用的配置。