2. 音频文件读取与播放
任务目标
读取一个WAV音频文件,将其解码为PCM样本数据,通过系统音频设备播放,并验证读取的音频数据与原始文件一致。本案例使用Python标准库wave完成WAV解析,使用sounddevice库实现音频播放。
环境依赖
- Python 3.8+
- 安装依赖:
pip install sounddevice numpy - 操作系统需具备可用的音频输出设备
完整实现
import wave
import numpy as np
import sounddevice as sd
import hashlib
import sys
def read_wav(filepath):
"""读取WAV文件,返回PCM数据、采样率、声道数、采样宽度"""
with wave.open(filepath, 'rb') as wf:
channels = wf.getnchannels()
sampwidth = wf.getsampwidth()
framerate = wf.getframerate()
nframes = wf.getnframes()
pcm_bytes = wf.readframes(nframes)
return pcm_bytes, framerate, channels, sampwidth
def bytes_to_int16(pcm_bytes, channels):
"""将字节串转为int16数组,按帧交错排列"""
data = np.frombuffer(pcm_bytes, dtype=np.int16)
# 确保帧数为整数
valid_len = (len(data) // channels) * channels
data = data[:valid_len]
return data.reshape(-1, channels)
def play_audio(data, framerate):
"""播放音频数据(阻塞直到播放完成)"""
sd.play(data, samplerate=framerate)
sd.wait()
def verify_integrity(original_bytes, filepath):
"""重新读取文件,对比哈希验证读取正确性"""
with open(filepath, 'rb') as f:
file_bytes = f.read()
# 跳过WAV文件头(44字节标准头),比较PCM数据部分
pcm_start = 44
original_pcm = original_bytes
file_pcm = file_bytes[pcm_start:pcm_start + len(original_pcm)]
return hashlib.sha256(original_pcm).hexdigest() == hashlib.sha256(file_pcm).hexdigest()
def main():
if len(sys.argv) < 2:
print("用法: python wav_player.py <音频文件.wav>")
sys.exit(1)
wav_path = sys.argv[1]
# 1. 读取WAV
pcm_data, framerate, channels, sampwidth = read_wav(wav_path)
print(f"采样率: {framerate} Hz, 声道数: {channels}, 采样宽度: {sampwidth*8} bit")
print(f"PCM数据长度: {len(pcm_data)} 字节")
# 2. 转换为numpy数组用于播放
if sampwidth == 2: # 16-bit PCM
audio_array = bytes_to_int16(pcm_data, channels)
else:
print(f"仅支持16-bit PCM,当前为{sampwidth*8} bit")
sys.exit(1)
# 3. 播放
print("开始播放...")
play_audio(audio_array, framerate)
print("播放完成")
# 4. 验证数据完整性
if verify_integrity(pcm_data, wav_path):
print("✓ 数据完整性验证通过:读取的PCM数据与文件一致")
else:
print("✗ 数据完整性验证失败")
if __name__ == "__main__":
main()
运行方式
# 生成测试WAV文件(可选,用于验证)
python -c "import wave, struct; wf=wave.open('test.wav','wb'); wf.setnchannels(1); wf.setsampwidth(2); wf.setframerate(16000); [wf.writeframes(struct.pack('<h', int(10000*__import__('math').sin(2*__import__('math').pi*440*i/16000)))) for i in range(16000)]; wf.close()"
# 运行播放器
python wav_player.py test.wav
验证方法
- 播放验证:程序执行后应能听到440Hz正弦音持续1秒,控制台输出播放完成无报错。
- 数据完整性验证:程序自动计算读取的PCM数据与源文件PCM段的SHA256哈希并比对,输出“验证通过”即表示读取正确。
- 参数输出验证:控制台打印的采样率、声道数、采样宽度应与生成文件时设置的参数一致(16000Hz、单声道、16bit)。
常见问题
- 播放无声音:检查系统音量、默认音频输出设备是否可用;尝试将
sd.play改为sd.play(audio_array, framerate, blocking=True)。 - 非16-bit WAV:本案例仅处理16-bit PCM格式,其他格式需扩展
bytes_to_int16函数。 - 文件头非标准:部分WAV文件头超过44字节(包含扩展信息),此时
verify_integrity中的偏移量需调整,可改用wave模块重新读取对比。