4. 音频通道分离与合并
任务目标
读取一段立体声WAV音频文件,将其左右声道数据分离,并分别输出为:左声道单声道WAV、右声道单声道WAV,以及将左右声道混合后的单声道WAV(取平均值)。最终通过播放或波形对比验证分离与合并的正确性。
环境依赖
Python 3.8+,仅需标准库 wave、struct、math,无需第三方库。
核心实现
import wave
import struct
import math
def process_stereo(input_path, out_left, out_right, out_mono):
# 读取立体声WAV
with wave.open(input_path, 'rb') as wav_in:
n_channels = wav_in.getnchannels()
samp_width = wav_in.getsampwidth()
framerate = wav_in.getframerate()
n_frames = wav_in.getnframes()
if n_channels != 2:
raise ValueError("输入文件必须是立体声(2通道)")
if samp_width != 2:
raise ValueError("仅支持16-bit PCM WAV")
raw = wav_in.readframes(n_frames)
# 解析左右声道样本
fmt = '<' + 'h' * (n_frames * 2)
samples = struct.unpack(fmt, raw)
left = samples[0::2]
right = samples[1::2]
# 合并为单声道(取平均)
mono = [(l + r) // 2 for l, r in zip(left, right)]
# 写入单声道WAV(左右分离结果)
for path, data in [(out_left, left), (out_right, right), (out_mono, mono)]:
with wave.open(path, 'wb') as wav_out:
wav_out.setnchannels(1)
wav_out.setsampwidth(samp_width)
wav_out.setframerate(framerate)
frames = struct.pack('<' + 'h' * len(data), *data)
wav_out.writeframes(frames)
# 返回基本信息供验证
return {
'channels': n_channels,
'samp_width': samp_width,
'framerate': framerate,
'frames': n_frames,
'left_samples': len(left),
'right_samples': len(right),
'mono_samples': len(mono)
}
if __name__ == '__main__':
info = process_stereo('stereo_input.wav', 'left.wav', 'right.wav', 'mono.wav')
print(f"处理完成:{info['frames']} 帧,左声道 {info['left_samples']} 样本,右声道 {info['right_samples']} 样本,单声道 {info['mono_samples']} 样本")
运行方式
- 准备一个16-bit PCM立体声WAV文件,命名为
stereo_input.wav,放在与脚本相同目录。 - 运行:
python channel_split_merge.py - 脚本生成三个文件:
left.wav、right.wav、mono.wav。
验证方法
- 文件属性检查:用任意音频工具(如Audacity)打开三个输出文件,确认均为单声道、采样率与原始文件一致。
- 波形对比:将
left.wav与原始立体声文件的左声道波形叠加,应完全重合;right.wav同理。 - 数值验证:运行以下命令检查左右声道是否确实分离(若输出全0表示两声道完全相同,属正常情况):
python -c "
import wave, struct
for f in ['left.wav','right.wav']:
w = wave.open(f,'rb')
data = w.readframes(w.getnframes())
samples = struct.unpack('<'+'h'*(w.getnframes()), data)
print(f, '峰值:', max(samples), '最小值:', min(samples))
w.close()
"
通过条件:三个输出文件均能正常打开播放;left.wav 与原始左声道内容一致,right.wav 与原始右声道内容一致;mono.wav 每个样本值等于左右样本平均值(允许整数除法截断误差)。
常见问题
- 输入不是立体声:脚本会抛出
ValueError,需确认WAV文件为2通道。 - 采样位深不是16-bit:当前实现仅支持16-bit PCM,若为8-bit或24-bit需调整
struct格式。 - 大文件内存占用:本实现一次性读取全部样本,超大文件(>500MB)建议改为分块处理。