3. 分帧与加窗
任务目标
对一段语音信号执行分帧(帧长25ms、帧移10ms),并对每帧应用汉明窗,对比加窗前后单帧波形,验证分帧参数与窗函数效果。
环境依赖
需要 numpy、matplotlib。若需读取真实语音文件,另需 scipy.io.wavfile(本案例使用合成信号,不依赖外部文件)。
核心实现
import numpy as np
import matplotlib.pyplot as plt
# ---------- 参数设置 ----------
sr = 16000 # 采样率 16kHz
frame_len_ms = 25 # 帧长 25ms
frame_shift_ms = 10 # 帧移 10ms
frame_len = int(sr * frame_len_ms / 1000) # 400 点
frame_shift = int(sr * frame_shift_ms / 1000) # 160 点
# ---------- 生成测试信号(含两个频率分量,便于观察加窗效果) ----------
t = np.arange(sr * 0.1) / sr # 100ms 信号
signal = 0.6 * np.sin(2 * np.pi * 440 * t) + 0.4 * np.sin(2 * np.pi * 1800 * t)
# ---------- 分帧 ----------
n_frames = 1 + (len(signal) - frame_len) // frame_shift
frames = np.stack([signal[i * frame_shift : i * frame_shift + frame_len]
for i in range(n_frames)])
# ---------- 汉明窗 ----------
hamming_win = np.hamming(frame_len)
frames_win = frames * hamming_win
# ---------- 取第 3 帧(索引2)对比加窗前后 ----------
idx = 2
frame_raw = frames[idx]
frame_win = frames_win[idx]
# ---------- 可视化 ----------
time_axis = np.arange(frame_len) / sr * 1000 # 毫秒
plt.figure(figsize=(10, 6))
plt.subplot(2, 1, 1)
plt.plot(time_axis, frame_raw, color='#4C72B0')
plt.title(f'Frame {idx} — Before Windowing')
plt.xlabel('Time (ms)')
plt.ylabel('Amplitude')
plt.grid(alpha=0.3)
plt.subplot(2, 1, 2)
plt.plot(time_axis, frame_win, color='#C44E52')
plt.title(f'Frame {idx} — After Hamming Window')
plt.xlabel('Time (ms)')
plt.ylabel('Amplitude')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('frame_windowing.png', dpi=120)
plt.show()
# ---------- 验证输出 ----------
print(f'采样率: {sr} Hz')
print(f'帧长: {frame_len} 点 ({frame_len_ms}ms)')
print(f'帧移: {frame_shift} 点 ({frame_shift_ms}ms)')
print(f'信号总长度: {len(signal)} 点 ({len(signal)/sr*1000:.1f}ms)')
print(f'总帧数: {n_frames}')
print(f'第{idx}帧加窗前能量: {np.sum(frame_raw**2):.4f}')
print(f'第{idx}帧加窗后能量: {np.sum(frame_win**2):.4f}')
运行方式
直接运行脚本即可。程序会生成 frame_windowing.png 图像,并在控制台打印分帧参数与能量统计。
验证方法
- 分帧正确性:控制台输出帧长为400点、帧移160点、总帧数符合公式
1 + (len(signal)-frame_len)//frame_shift。信号长度1600点,计算得总帧数 = 1 + (1600-400)//160 = 8,输出应为8。 - 加窗效果:观察图像——加窗前帧两端幅值不为零,加窗后帧两端平滑衰减至接近0,中间部分保持原波形形状。第3帧(索引2)起始采样点索引为
2×160=320,该帧原始信号两端有明显非零值,加窗后两端被抑制。 - 能量变化:汉明窗系数平方和约为0.397(对400点),因此加窗后能量约为加窗前的0.397倍。控制台打印的两帧能量比值应接近此值(因信号非平稳,存在小幅偏差)。
常见问题
- 帧数计算:当信号长度不是帧移整数倍时,末尾不足一帧的余量被丢弃,这是标准做法。
- 窗函数选择:汉明窗主瓣较宽但旁瓣衰减大,适合语音分析;若需更高频率分辨率可换用矩形窗,但频谱泄漏更明显。
- 信号边界:若使用真实语音文件,需先读取并转为单声道float数组,再替换
signal变量即可。