引言

WAV(Waveform Audio File Format)作为一种无损音频格式,虽然保证了音质,但其巨大的文件体积给存储和传输带来了挑战。例如,一首3分钟的CD音质WAV歌曲(44.1kHz,16bit,立体声)体积可达31.7MB,而同样的内容转为MP3后仅需3-4MB,压缩率高达90%

本文将系统介绍使用Python进行WAV音频压缩的各种方案,从基础的格式转换到专业的动态范围压缩,再到前沿的神经网络编解码器,满足不同场景下的压缩需求。

一、理解Python标准库的局限

在开始之前,需要澄清一个重要问题:Python内置的wave模块不支持任何压缩格式

python

import wave

# ❌ 错误示例 - wave模块不支持真正的压缩
with wave.open('output.wav', 'wb') as wf:
    wf.setnchannels(1)
    wf.setsampwidth(2)
    wf.setframerate(44100)
    # 以下代码不会产生压缩效果
    wf.setcomptype('NONE', 'not compressed')  # 仅支持'NONE'

wave模块的setcomptype方法虽然存在,但仅接受'NONE'作为压缩类型。这意味着要实现对WAV音频的有效压缩,我们必须借助第三方库。

二、方案一:格式转换压缩(最常用)

2.1 使用pydub进行有损压缩

pydub是最简单易用的音频处理库,底层调用ffmpeg实现各种格式转换。

python

from pydub import AudioSegment
import os

def compress_to_mp3(input_wav: str, output_mp3: str, bitrate: str = "128k"):
    """
    将WAV压缩为MP3格式
    
    Args:
        input_wav: 输入的WAV文件路径
        output_mp3: 输出的MP3文件路径
        bitrate: 比特率,可选 "64k", "128k", "192k", "256k", "320k"
    """
    # 加载WAV文件
    audio = AudioSegment.from_wav(input_wav)
    
    # 导出为MP3(有损压缩)
    audio.export(output_mp3, format="mp3", bitrate=bitrate)
    
    # 计算压缩比
    original_size = os.path.getsize(input_wav)
    compressed_size = os.path.getsize(output_mp3)
    ratio = (1 - compressed_size / original_size) * 100
    
    print(f"原始大小: {original_size / 1024:.1f} KB")
    print(f"压缩后: {compressed_size / 1024:.1f} KB")
    print(f"压缩率: {ratio:.1f}%")
    return ratio

def compress_to_aac(input_wav: str, output_aac: str, bitrate: str = "128k"):
    """压缩为AAC格式(比MP3更高效)"""
    audio = AudioSegment.from_wav(input_wav)
    audio.export(output_aac, format="adts", bitrate=bitrate)  # adts是AAC容器格式

def compress_to_ogg(input_wav: str, output_ogg: str, bitrate: str = "128k"):
    """压缩为OGG格式(开源,免费)"""
    audio = AudioSegment.from_wav(input_wav)
    audio.export(output_ogg, format="ogg", bitrate=bitrate)

2.2 批量压缩工具

python

import glob
from pathlib import Path
from tqdm import tqdm

def batch_compress(input_dir: str, output_dir: str, bitrate: str = "128k", format: str = "mp3"):
    """批量压缩目录下所有WAV文件"""
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    
    wav_files = glob.glob(f"{input_dir}/**/*.wav", recursive=True)
    
    results = []
    for wav_path in tqdm(wav_files, desc="压缩进度"):
        rel_path = Path(wav_path).relative_to(input_dir)
        output_path = Path(output_dir) / rel_path.with_suffix(f".{format}")
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        audio = AudioSegment.from_wav(wav_path)
        audio.export(str(output_path), format=format, bitrate=bitrate)
        
        results.append({
            "input": wav_path,
            "input_size": Path(wav_path).stat().st_size,
            "output_size": output_path.stat().st_size
        })
    
    # 统计
    total_input = sum(r["input_size"] for r in results)
    total_output = sum(r["output_size"] for r in results)
    print(f"总压缩率: {(1 - total_output/total_input)*100:.1f}%")
    
    return results

三、方案二:无损压缩(FLAC)

对于需要保持完美音质的场景,FLAC(Free Lossless Audio Codec)是最佳选择。它可以将WAV压缩至原大小的40%-60%,同时不损失任何音频信息。

3.1 使用pyFLAC

python

import pyflac
import soundfile as sf
import numpy as np

def wav_to_flac_pyflac(input_wav: str, output_flac: str, compression_level: int = 5):
    """
    使用pyFLAC将WAV转换为FLAC格式
    
    Args:
        input_wav: WAV文件路径
        output_flac: 输出FLAC文件路径
        compression_level: 0-8,0最快/体积大,8最慢/体积小,默认5
    """
    # pyFLAC的FileEncoder直接接受WAV文件
    encoder = pyflac.FileEncoder(
        input_file=input_wav,
        output_file=output_flac,
        compression_level=compression_level,
        verify=True  # 验证编码正确性
    )
    encoder.process()
    print(f"FLAC压缩完成: {output_flac}")

def flac_to_wav(input_flac: str, output_wav: str):
    """将FLAC解压回WAV"""
    decoder = pyflac.FileDecoder(
        input_file=input_flac,
        output_file=output_wav
    )
    audio_array, sample_rate = decoder.process()
    return audio_array, sample_rate

def stream_encode_example():
    """流式编码示例(适用于实时音频)"""
    import sounddevice as sd
    
    # 设置回调函数
    def write_callback(buffer, num_bytes, num_samples, current_frame):
        print(f"编码了 {num_bytes} 字节数据")
    
    # 创建流编码器
    encoder = pyflac.StreamEncoder(
        sample_rate=44100,
        write_callback=write_callback,
        compression_level=5
    )
    
    # 模拟处理音频数据
    # audio_data = np.random.randn(44100 * 2).astype(np.float32)  # 2秒音频
    # encoder.process(audio_data)
    # encoder.finish()

3.2 使用soundfile(更简单的选择)

soundfile库底层使用libsndfile,天然支持FLAC格式:

python

import soundfile as sf

def wav_to_flac_soundfile(input_wav: str, output_flac: str):
    """使用soundfile进行FLAC压缩(最简单)"""
    # 读取WAV
    data, samplerate = sf.read(input_wav)
    
    # 写入FLAC(soundfile根据扩展名自动选择格式)
    sf.write(output_flac, data, samplerate)
    
    # 验证无损性
    data_reloaded, sr_reloaded = sf.read(output_flac)
    assert np.allclose(data, data_reloaded), "FLAC压缩损失了数据!"
    print("✅ 无损压缩验证通过")

# 也支持其他无损格式
def wav_to_wv(input_wav: str, output_wv: str):
    """压缩为WavPack格式(无损且压缩比更高)"""
    data, samplerate = sf.read(input_wav)
    sf.write(output_wv, data, samplerate)  # 扩展名为.wv即可

四、方案三:A-law/μ-law压缩(通信专用)

在电话通信系统中,A-law和μ-law压缩是国际标准。它们使用对数量化技术,将16位线性PCM压缩为8位,压缩比达50%,同时保持可接受的语音质量。

4.1 使用a-law-lib库

python

import a_law_lib as al
import numpy as np
import soundfile as sf

def compress_alaw(input_wav: str, output_wav: str, num_levels: int = 16):
    """
    应用A-law压扩压缩
    
    A-law是欧洲电话系统标准,A=87.6
    """
    # 读取音频
    data, samplerate = sf.read(input_wav)
    
    # 归一化到[-1, 1]范围(如果需要)
    if data.dtype == np.int16:
        data = data / 32768.0
    
    # A-law编码
    encoded = al.a_law_encode(data, A=87.6)
    
    # 量化(减少量化级别)
    quantized = al.quantize(encoded, num_levels=num_levels)
    
    # 解码还原
    decoded = al.a_law_decode(quantized, A=87.6)
    
    # 保存结果
    sf.write(output_wav, decoded, samplerate)
    print(f"A-law压缩完成,量化级别: {num_levels}")

def compress_mulaw(input_wav: str, output_wav: str):
    """μ-law压缩(北美/日本标准)"""
    # μ-law编码(标准库支持)
    import audioop
    
    with open(input_wav, 'rb') as f:
        # 假设是16位PCM
        pcm_data = f.read()
        ulaw_data = audioop.lin2ulaw(pcm_data, 2)  # 2表示16位
    
    # 保存为8位μ-law WAV
    with wave.open(output_wav, 'wb') as wf:
        wf.setnchannels(1)
        wf.setsampwidth(1)  # 8位
        wf.setframerate(8000)  # 电话音质
        wf.writeframes(ulaw_data)

# 完整处理管道
def process_wave_file_demo():
    """一行代码完成完整A-law处理流程"""
    # a_law_lib内置的处理函数
    al.process_wave_file(
        file_path="input.wav",
        output_path="output_alaw.wav",
        A=87.6,
        num_levels=16
    )
    print("完整处理完成:读取 → A-law编码 → 量化 → 解码 → 保存")

五、方案四:动态范围压缩(音频制作专用)

动态范围压缩不是用于文件存储,而是用于调整音频的响度平衡——将大声部分压低、小声部分提升,使整体音量更均匀。

5.1 使用dynamic-range-compression库

python

from dynamic_range_compression import compress_audio

def apply_dynamic_compression(input_wav: str, output_wav: str):
    """
    应用动态范围压缩
    
    适用场景:
    - 播客音量均衡
    - 音乐母带处理
    - 语音清晰度提升
    """
    compress_audio(
        filePath=input_wav,
        destPath=output_wav,
        threshold=-20,      # dB,超过此音量的部分被压缩
        makeupGain=3,       # dB,补偿增益
        kneeWidth=6,        # dB,压缩拐点平滑度
        compressionRatio=4,  # 4:1压缩比
        lookAhead=5,        # 毫秒,提前检测
        attack=10,          # 毫秒,起效时间
        release=100         # 毫秒,释放时间
    )
    print("动态范围压缩完成")

def batch_compress_podcast(input_dir: str, output_dir: str):
    """批量处理播客音频(标准化音量)"""
    import glob
    from pathlib import Path
    
    standard_settings = {
        "threshold": -24,
        "makeupGain": 4,
        "kneeWidth": 6,
        "compressionRatio": 3,
        "lookAhead": 5,
        "attack": 15,
        "release": 120
    }
    
    for wav_file in glob.glob(f"{input_dir}/*.wav"):
        output_path = Path(output_dir) / Path(wav_file).name
        compress_audio(wav_file, str(output_path), **standard_settings)
        print(f"处理完成: {output_path}")

六、方案五:神经网络音频编解码器(前沿技术)

近年来,基于神经网络的音频编解码器实现了前所未有的压缩比。如Descript Audio Codec (DAC) 宣称在44.1kHz立体声音频上可实现90倍压缩比

6.1 使用Descript Audio Codec

python

# 安装: pip install descript-audio-codec

def neural_compress(input_wav: str, output_dir: str, target_bandwidth: int = 6):
    """
    使用神经网络编解码器压缩
    
    Args:
        target_bandwidth: 目标带宽 kbps,可选 1, 2, 4, 6, 12, 24, 32, 64
    """
    from dac import DACEncoder, DACDecoder
    import torchaudio
    
    # 加载模型
    encoder = DACEncoder()
    decoder = DACDecoder()
    
    # 读取音频
    audio, sr = torchaudio.load(input_wav)
    
    # 编码
    codes = encoder.encode(audio, sr, target_bandwidth=target_bandwidth)
    
    # 解码重建
    reconstructed = decoder.decode(codes)
    
    # 保存
    torchaudio.save(f"{output_dir}/reconstructed.wav", reconstructed, sr)
    print(f"神经网络压缩完成,带宽: {target_bandwidth} kbps")

# 其他可用模型
# - EnCodec: Meta开源,支持多种带宽
# - SoundStream: Google的端到端神经音频编解码器
# - AudioCodec-Hub: 统一接口支持多种模型

七、压缩方案对比与选择指南

方案 压缩类型 典型压缩比 音质 适用场景 推荐库
MP3/AAC 有损 90% 音乐分发、流媒体 pydub
FLAC 无损 40-60% 完美 归档、后期制作 soundfile, pyFLAC
A-law/μ-law 有损(语音优化) 50% 一般(电话音质) 通信系统 a-law-lib
动态范围压缩 效果处理 不减少文件大小 改变动态 播客、母带 dynamic-range-compression
神经编解码器 有损(智能) 90-98% 好-优秀 前沿应用 descript-audio-codec

八、完整工具类实现

python

import os
import numpy as np
from pathlib import Path
from typing import Optional, Literal
from dataclasses import dataclass

@dataclass
class CompressionResult:
    """压缩结果"""
    input_path: str
    output_path: str
    input_size_mb: float
    output_size_mb: float
    compression_ratio: float
    method: str

class WAVCompressor:
    """统一的WAV压缩器"""
    
    def __init__(self):
        self._check_dependencies()
    
    def _check_dependencies(self):
        """检查依赖"""
        self.has_pydub = self._try_import('pydub')
        self.has_soundfile = self._try_import('soundfile')
        self.has_pyflac = self._try_import('pyflac')
        self.has_alaw = self._try_import('a_law_lib')
    
    def _try_import(self, name):
        try:
            __import__(name)
            return True
        except ImportError:
            return False
    
    def to_mp3(self, input_wav: str, output_mp3: str = None, 
               bitrate: str = "128k") -> CompressionResult:
        """转换为MP3"""
        if not self.has_pydub:
            raise ImportError("请安装 pydub: pip install pydub")
        
        from pydub import AudioSegment
        
        if output_mp3 is None:
            output_mp3 = str(Path(input_wav).with_suffix('.mp3'))
        
        audio = AudioSegment.from_wav(input_wav)
        audio.export(output_mp3, format="mp3", bitrate=bitrate)
        
        return self._calculate_result(input_wav, output_mp3, "MP3")
    
    def to_flac(self, input_wav: str, output_flac: str = None,
                compression_level: int = 5) -> CompressionResult:
        """转换为FLAC(无损)"""
        if not self.has_soundfile:
            raise ImportError("请安装 soundfile: pip install soundfile")
        
        import soundfile as sf
        
        if output_flac is None:
            output_flac = str(Path(input_wav).with_suffix('.flac'))
        
        data, sr = sf.read(input_wav)
        sf.write(output_flac, data, sr)
        
        return self._calculate_result(input_wav, output_flac, "FLAC")
    
    def to_ogg(self, input_wav: str, output_ogg: str = None,
               bitrate: str = "128k") -> CompressionResult:
        """转换为OGG"""
        if not self.has_pydub:
            raise ImportError("请安装 pydub: pip install pydub")
        
        from pydub import AudioSegment
        
        if output_ogg is None:
            output_ogg = str(Path(input_wav).with_suffix('.ogg'))
        
        audio = AudioSegment.from_wav(input_wav)
        audio.export(output_ogg, format="ogg", bitrate=bitrate)
        
        return self._calculate_result(input_wav, output_ogg, "OGG")
    
    def _calculate_result(self, input_path: str, output_path: str, 
                          method: str) -> CompressionResult:
        """计算压缩结果统计"""
        in_size = os.path.getsize(input_path) / (1024 * 1024)
        out_size = os.path.getsize(output_path) / (1024 * 1024)
        ratio = (1 - out_size / in_size) * 100
        
        return CompressionResult(
            input_path=input_path,
            output_path=output_path,
            input_size_mb=in_size,
            output_size_mb=out_size,
            compression_ratio=ratio,
            method=method
        )
    
    def smart_compress(self, input_wav: str, 
                       quality: Literal['high', 'medium', 'low'] = 'medium',
                       preserve_lossless: bool = False) -> CompressionResult:
        """
        智能压缩决策
        
        Args:
            quality: 'high' (高音质), 'medium' (平衡), 'low' (小体积)
            preserve_lossless: 是否保持无损
        """
        if preserve_lossless:
            return self.to_flac(input_wav)
        
        configs = {
            'high': {'format': 'mp3', 'bitrate': '320k'},
            'medium': {'format': 'mp3', 'bitrate': '128k'},
            'low': {'format': 'ogg', 'bitrate': '64k'}
        }
        
        cfg = configs[quality]
        
        if cfg['format'] == 'mp3':
            return self.to_mp3(input_wav, bitrate=cfg['bitrate'])
        else:
            return self.to_ogg(input_wav, bitrate=cfg['bitrate'])

# 使用示例
if __name__ == "__main__":
    compressor = WAVCompressor()
    
    # 智能压缩
    result = compressor.smart_compress("song.wav", quality='medium')
    
    print(f"\n📊 压缩报告")
    print(f"   输入: {result.input_path} ({result.input_size_mb:.2f} MB)")
    print(f"   输出: {result.output_path} ({result.output_size_mb:.2f} MB)")
    print(f"   方法: {result.method}")
    print(f"   🎯 压缩率: {result.compression_ratio:.1f}%")

九、总结与建议

根据你的具体需求选择合适的方案:

  1. 日常音乐压缩:使用pydub + MP3,128kbps是音质与体积的最佳平衡

  2. 无损归档:使用soundfile + FLAC,完整保留原始音质

  3. 语音/电话应用:使用A-law/μ-law压缩,节省带宽同时保持语音可懂度

  4. 播客制作:配合动态范围压缩,让听感更舒适

  5. 极致压缩比:尝试神经网络编解码器(DAC/EnCodec)

选择合适的技术方案,可以在存储空间和音频质量之间找到最佳平衡点。

更多推荐