1. 麦克风阵列原始PCM数据读取与格式解析
任务说明
本案例实现一个最小可用的WAV文件读取与波形显示工具:解析44字节标准PCM WAV头,提取采样率、位深、声道数等关键参数,读取音频样本数据,并绘制时域波形图。通过该工具,可以直观理解WAV文件的存储格式与音频数据的数值范围。
WAV文件格式要点
标准PCM WAV文件由RIFF头(12字节)、fmt子块(24字节)和data子块(8字节头+音频数据)组成。本案例仅处理未压缩的PCM格式,支持8位和16位两种常见位深。8位样本为无符号数(中心值128),16位样本为有符号小端整数(范围-32768~32767)。
代码实现
import wave
import numpy as np
import matplotlib.pyplot as plt
import sys
def read_wav(filepath):
"""读取WAV文件,返回采样率、样本数据和声道数"""
with wave.open(filepath, 'rb') as wf:
n_channels = wf.getnchannels()
samp_width = wf.getsampwidth()
framerate = wf.getframerate()
n_frames = wf.getnframes()
# 读取原始字节
raw_data = wf.readframes(n_frames)
# 根据位深转换数据类型
if samp_width == 1:
dtype = np.uint8
center = 128.0
elif samp_width == 2:
dtype = np.int16
center = 0.0
else:
raise ValueError(f"不支持的位深: {samp_width*8} bit,仅支持8/16位")
# 转换为numpy数组并归一化到[-1, 1]
audio = np.frombuffer(raw_data, dtype=dtype).astype(np.float32)
audio = (audio - center) / (128.0 if samp_width == 1 else 32768.0)
# 多声道时取第一声道
if n_channels > 1:
audio = audio[::n_channels]
return framerate, audio, n_channels
def plot_waveform(sr, audio, title="时域波形"):
"""绘制时域波形图"""
duration = len(audio) / sr
time = np.linspace(0, duration, len(audio))
plt.figure(figsize=(12, 4))
plt.plot(time, audio, linewidth=0.5, color='#1f77b4')
plt.xlabel("时间 (s)")
plt.ylabel("幅度")
plt.title(f"{title} - 采样率: {sr} Hz")
plt.grid(True, alpha=0.3)
plt.xlim(0, duration)
plt.ylim(-1.1, 1.1)
plt.tight_layout()
plt.show()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("用法: python wav_plot.py <音频文件.wav>")
sys.exit(1)
wav_path = sys.argv[1]
# 读取并显示基本信息
sr, audio, ch = read_wav(wav_path)
print(f"采样率: {sr} Hz")
print(f"声道数: {ch}")
print(f"样本数: {len(audio)}")
print(f"时长: {len(audio)/sr:.2f} s")
print(f"幅度范围: [{audio.min():.3f}, {audio.max():.3f}]")
# 绘制波形
plot_waveform(sr, audio)
运行方式
依赖环境:Python 3.6+,numpy,matplotlib。运行前先准备一个PCM WAV文件(可用Audacity导出或使用以下命令生成测试文件):
# 生成3秒440Hz正弦波测试文件(需安装numpy)
python -c "
import numpy as np, wave
sr = 16000
t = np.linspace(0, 3, sr*3, endpoint=False)
data = (np.sin(2*np.pi*440*t) * 32767).astype(np.int16)
with wave.open('test.wav', 'wb') as wf:
wf.setnchannels(1)
wf.setsampwidth(2)
wf.setframerate(sr)
wf.writeframes(data.tobytes())
print('已生成 test.wav')
"
然后执行:
python wav_plot.py test.wav
验证方法
运行程序后,观察终端输出与波形图:
- 终端应显示采样率16000 Hz、声道数1、样本数48000、时长3.00s,幅度范围接近[-1.0, 1.0];
- 波形图应呈现频率440Hz的正弦波,3秒内可见约1320个完整周期;
- 将代码中
audio[::n_channels]改为audio.reshape(-1, n_channels)[:, 0]可验证多声道文件读取逻辑。
判定标准:程序无报错运行,输出参数与WAV头信息一致,波形形状与输入音频内容相符,即视为通过。