1. 音频文件读取与波形显示

音频文件读取与波形显示 学习卡片

任务说明

本案例实现一个最小可用的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头信息一致,波形形状与输入音频内容相符,即视为通过。

WAV文件二进制布局 RIFF头 12字节 fmt子块 24字节 data头 8字节 音频样本数据 N字节(样本数×声道数×位深/8) 解析提取的关键参数 采样率 (Hz) 如: 16000, 44100 位深 (bit) 8位无符号 / 16位有符号 声道数 1=单声道, 2=立体声 样本总数 决定波形时间轴长度 程序处理流程 打开WAV文件 解析头部参数 读取样本数据 归一化到[-1,1] 绘图