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']} 样本")

运行方式

  1. 准备一个16-bit PCM立体声WAV文件,命名为 stereo_input.wav,放在与脚本相同目录。
  2. 运行:python channel_split_merge.py
  3. 脚本生成三个文件: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)建议改为分块处理。