4. 音频基础:声音的数字化、采样率与位深度、音频文件格式(WAV、MP3、FLAC)、Python音频读写操作

好,咱们进入第四讲。这一章是硬核基础,说白了就是搞清楚「声音在计算机里到底长什么样」。我当年刚接触语音交互时,以为音频就是播放个MP3那么简单,结果一上手就踩了无数坑。嗯,今天咱们把这些坑一个个填平。

4.1 声音的数字化:从模拟到数字

声音本质上是空气的振动。麦克风把这种振动转成电信号,也就是连续的模拟波形。但计算机只认0和1,所以必须把连续的波形「切」成离散的点。这个过程就叫数字化。

数字化包含两步:采样和量化。采样是每隔一段时间取一个值,量化是把取到的值用整数表示。你想想看,就像用尺子量一段曲线,尺子越密、刻度越细,量出来的结果就越接近原样。

核心公式:

数字音频 = 采样(时间离散化) + 量化(幅度离散化)

我在项目中遇到过一个问题:用低端麦克风录制的语音,背景噪声特别大。后来发现是量化精度不够,导致微弱信号被「吃掉」了。所以,数字化这一步的质量,直接决定了后续所有处理的上限。

4.2 采样率与位深度

这两个参数是音频数字化的灵魂。咱们一个一个说。

4.2.1 采样率(Sample Rate)

采样率就是每秒采样的次数,单位是Hz。常见的采样率有:

采样率 常见用途 我的评价
8 kHz 电话语音 够用,但音质像「罐头」
16 kHz 语音识别、语音交互 我个人最常用,性价比之王
44.1 kHz CD音质音乐 音乐制作标配
48 kHz 影视、专业音频 视频项目里常见

这里有个关键定理——奈奎斯特采样定理:采样率必须大于信号最高频率的两倍,才能无失真地还原。说白了,人耳能听到20 kHz,所以CD用44.1 kHz(略大于40 kHz)。语音交互用16 kHz就够了,因为人声主要能量在4 kHz以下。

我的经验:做语音识别项目时,我建议直接用16 kHz采样率。8 kHz虽然省带宽,但识别率会明显下降。别问我怎么知道的——我曾经为了省存储空间用8 kHz,结果模型准确率掉了5个点,得不偿失。

4.2.2 位深度(Bit Depth)

位深度决定了每个采样点的精度。常见的有:

  • 8 bit:256个等级,音质粗糙,有量化噪声
  • 16 bit:65536个等级,CD标准,动态范围96 dB
  • 24 bit:1677万个等级,专业录音室标准,动态范围144 dB
  • 32 bit float:浮点表示,处理时不会溢出,常用于后期

为什么会这样?位深度越大,能记录的声音细节就越丰富。16 bit已经能覆盖人耳从最轻到最响的听觉范围。但如果你做音频处理(比如降噪、增益),建议用32 bit float,因为中间计算不会丢失精度。

避坑指南:我曾经在批量处理音频时,直接把32 bit float保存成16 bit,结果所有音频都出现了「削波」失真。后来才意识到,浮点转定点时必须做归一化处理。切记:保存前检查动态范围。

4.3 音频文件格式:WAV、MP3、FLAC

格式这东西,说白了就是「怎么把数字音频存成文件」。不同格式有不同的压缩方式和适用场景。

4.3.1 WAV(无损、未压缩)

WAV是微软和IBM联合开发的格式。它直接把采样数据存下来,不压缩。优点是质量无损,读写快;缺点是文件大。一分钟16 bit/44.1 kHz的立体声WAV,大约10 MB。

我个人的习惯是:原始录音和中间处理结果都用WAV。因为每次压缩都会损失信息,等最终定稿再转成其他格式。

4.3.2 MP3(有损压缩)

MP3利用心理声学模型,去掉人耳不敏感的频率成分。压缩比能达到10:1以上。128 kbps的MP3听起来和CD差别不大,但文件小得多。

但注意:MP3不适合做语音识别。我在项目中测试过,同一段语音,WAV格式的识别准确率比MP3高3-5%。因为压缩过程会丢失高频细节,而这些细节对某些声学模型很重要。

4.3.3 FLAC(无损压缩)

FLAC是自由无损音频编解码器。它像zip一样压缩WAV,但解压后和原数据完全一致。压缩率大约2:1到3:1。既节省空间,又保留全部信息。

如果你做音频存档或分发,FLAC是很好的选择。我自己的音乐库全部是FLAC格式,既省硬盘又不怕质量损失。

格式 压缩方式 质量 文件大小 推荐场景
WAV 无压缩 无损 大 原始录音、中间处理
MP3 有损压缩 有损 小 音乐播放、网络传输
FLAC 无损压缩 无损 中等 音频存档、高质量分发

4.4 Python音频读写操作

理论说完了,咱们上手实操。Python里读写音频,我推荐两个库:wave(标准库,轻量)和 soundfile(第三方,功能强)。

4.4.1 使用 wave 库读写 WAV

wave 是Python自带的,不用安装。适合处理简单的WAV文件。

import wave
import numpy as np

# 读取WAV文件
with wave.open('input.wav', 'rb') as wf:
    # 获取参数
    n_channels = wf.getnchannels()      # 声道数
    sampwidth = wf.getsampwidth()       # 采样字节数(1=8bit, 2=16bit)
    framerate = wf.getframerate()       # 采样率
    n_frames = wf.getnframes()          # 总帧数
    
    # 读取音频数据(bytes)
    data_bytes = wf.readframes(n_frames)
    
    # 转成numpy数组(16bit时用int16)
    data = np.frombuffer(data_bytes, dtype=np.int16)
    data = data.reshape(-1, n_channels)  # 多声道时reshape

print(f'采样率: {framerate} Hz')
print(f'声道数: {n_channels}')
print(f'时长: {n_frames / framerate:.2f} 秒')

# 写入WAV文件
with wave.open('output.wav', 'wb') as wf:
    wf.setnchannels(1)          # 单声道
    wf.setsampwidth(2)          # 16 bit = 2字节
    wf.setframerate(16000)      # 16 kHz
    
    # 生成一个1秒的正弦波(440 Hz)
    t = np.linspace(0, 1, 16000, endpoint=False)
    sine_wave = np.sin(2 * np.pi * 440 * t) * 32767  # 16bit最大值
    sine_wave = sine_wave.astype(np.int16)
    
    wf.writeframes(sine_wave.tobytes())

小技巧:用 numpy.frombuffer 转数据时,dtype一定要和采样字节数匹配。16 bit用 np.int16,24 bit用 np.int32 然后右移8位。我曾经因为类型不匹配,读出来的数据全是噪声,排查了半天。

4.4.2 使用 soundfile 库读写多种格式

soundfile 支持WAV、FLAC、OGG等多种格式,而且直接返回numpy数组,非常方便。先安装:

pip install soundfile

然后看代码:

import soundfile as sf

# 读取音频(自动识别格式)
data, samplerate = sf.read('input.flac')
# data 是 numpy 数组,shape = (帧数, 声道数)
# 值范围:-1.0 到 1.0(浮点)

print(f'采样率: {samplerate}')
print(f'数据形状: {data.shape}')
print(f'数据类型: {data.dtype}')

# 写入音频
sf.write('output.wav', data, samplerate, subtype='PCM_16')
# subtype 可选:'PCM_16', 'PCM_24', 'FLOAT' 等

# 写入FLAC
sf.write('output.flac', data, samplerate, subtype='PCM_16')

注意 soundfile 读出来的数据是浮点型(-1.0 ~ 1.0),而 wave 读出来是整型。这个区别很重要,做信号处理时建议用浮点,避免溢出。

4.4.3 实战:批量转换音频格式

最后分享一个我常用的脚本,批量把MP3转成16 kHz/16 bit的WAV:

import os
import soundfile as sf
import librosa  # 需要安装:pip install librosa

input_dir = 'mp3_files/'
output_dir = 'wav_files/'
os.makedirs(output_dir, exist_ok=True)

for filename in os.listdir(input_dir):
    if not filename.endswith('.mp3'):
        continue
    
    filepath = os.path.join(input_dir, filename)
    
    # 用librosa读取MP3(自动解码)
    data, sr = librosa.load(filepath, sr=16000, mono=True)
    
    # 保存为16 kHz/16 bit WAV
    out_path = os.path.join(output_dir, filename.replace('.mp3', '.wav'))
    sf.write(out_path, data, 16000, subtype='PCM_16')
    
    print(f'已转换: {filename} -> {out_path}')

注意:librosa.load 默认会做两件事:重采样到22.05 kHz,并转成单声道。如果你需要原始采样率,记得传 sr=None。我刚开始用的时候没注意,所有音频都被重采样了,导致后续分析全错。

知识体系总览

下面这张图总结了本章的核心逻辑,从模拟声音到数字文件的全流程:

音频数字化全流程 模拟声音 连续的空气振动 麦克风 采样 时间离散化 采样率 (Hz) 量化 幅度离散化 位深度 (bit) 数字 音频 文件格式 WAV 无压缩 · 无损 MP3 有损压缩 · 小体积 FLAC 无损压缩 · 中等体积 Python 音频读写 wave 库 · soundfile 库 · librosa 库 读取 → 处理 → 保存

这张图把整个流程串起来了:模拟声音经过采样和量化变成数字音频,然后以不同格式存成文件,最后用Python工具进行读写和处理。每个环节的参数选择都会影响最终效果。

可可精品资料库