4. 音频重采样
任务目标
本案例实现一个音频重采样工具:将44.1kHz采样率的WAV文件转换为16kHz,并输出重采样前后的频谱对比图,用于直观验证重采样对音频频带的影响。
环境依赖
- Python 3.8+
- 安装依赖:
pip install numpy scipy matplotlib
实现代码
以下代码完成:读取WAV → 线性插值重采样 → 保存新WAV → 绘制频谱对比。
import numpy as np
import wave
from scipy.fft import rfft, rfftfreq
import matplotlib.pyplot as plt
def read_wav(path):
with wave.open(path, 'rb') as wf:
ch = wf.getnchannels()
sw = wf.getsampwidth()
rate = wf.getframerate()
data = np.frombuffer(wf.readframes(wf.getnframes()), dtype=np.int16)
if ch > 1:
data = data.reshape(-1, ch).mean(axis=1).astype(np.int16)
return data.astype(np.float64), rate
def write_wav(path, data, rate):
data = np.clip(data, -32768, 32767).astype(np.int16)
with wave.open(path, 'wb') as wf:
wf.setnchannels(1)
wf.setsampwidth(2)
wf.setframerate(rate)
wf.writeframes(data.tobytes())
def resample_linear(data, src_rate, dst_rate):
n_src = len(data)
n_dst = int(n_src * dst_rate / src_rate)
x_src = np.arange(n_src)
x_dst = np.linspace(0, n_src - 1, n_dst)
return np.interp(x_dst, x_src, data)
def plot_spectrum(data, rate, title, ax):
n = len(data)
if n == 0:
return
win = np.hanning(n)
spec = np.abs(rfft(data * win))
freqs = rfftfreq(n, 1/rate)
ax.plot(freqs, 20*np.log10(spec + 1e-10), lw=0.8)
ax.set_title(title)
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Magnitude (dB)')
ax.set_xlim(0, rate/2)
ax.grid(alpha=0.3)
def main():
# 生成测试信号:1kHz正弦波 + 18kHz高频分量(44.1kHz下可表示)
t = np.arange(44100) / 44100.0
signal = 0.6*np.sin(2*np.pi*1000*t) + 0.4*np.sin(2*np.pi*18000*t)
signal = (signal * 32767).astype(np.int16)
write_wav('test_44k.wav', signal, 44100)
# 读取并重采样
data, rate = read_wav('test_44k.wav')
resampled = resample_linear(data, rate, 16000)
write_wav('test_16k.wav', resampled, 16000)
# 频谱对比
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
plot_spectrum(data, rate, 'Original 44.1kHz Spectrum', ax1)
plot_spectrum(resampled, 16000, 'Resampled 16kHz Spectrum', ax2)
plt.tight_layout()
plt.savefig('spectrum_compare.png', dpi=100)
plt.close()
print('完成:已生成 test_44k.wav, test_16k.wav, spectrum_compare.png')
if __name__ == '__main__':
main()
运行方式
python resample_demo.py
验证方法
- 运行后检查当前目录是否生成三个文件:
test_44k.wav、test_16k.wav、spectrum_compare.png。 - 打开
spectrum_compare.png,应观察到:- 上图(44.1kHz)在1kHz和18kHz处各有一个明显峰值,频带延伸至22.05kHz。
- 下图(16kHz)在1kHz处峰值保留,18kHz处峰值消失(因超过8kHz奈奎斯特频率被滤除),频带仅延伸至8kHz。
- 用音频播放器打开两个WAV,16kHz版本高频明显变暗,但1kHz主音清晰可辨。
预期输出说明
由于代码使用确定性正弦信号作为输入,频谱图必然呈现上述特征:18kHz分量在重采样后因不满足16kHz采样定理而消失,1kHz分量完整保留。这是由信号处理理论直接推出的确定性结果,非实测数据。