用FFmpeg解码PCM数据
🔊 PCM在音频创作中的基础地位
理解PCM数据是进阶音频创作的基石:
- 无损内核:PCM是未经压缩的音频原始数据,所有音频格式(MP3、AAC、FLAC)最终都源于PCM数据。掌握PCM就是掌握了音频的底层语言。
- 解码流程:从压缩音频到PCM的转换是音频编辑的必经之路——任何滤镜处理(音量、均衡、混响)都是在PCM数据上进行的数学运算。
- 音频编辑软件:Audacity、Adobe Audition等软件内部都是将音频转为PCM后进行编辑,再压缩为指定格式输出。理解PCM有助于排查音频问题。
- 采样参数:采样率(如44.1kHz、48kHz)决定频率范围,位深度(16bit、24bit、32bit float)决定动态范围和信噪比。选择合适参数在音质与文件大小之间取得平衡。
什么是PCM数据?
PCM(Pulse Code Modulation,脉冲编码调制)是未经任何压缩的音频原始数据格式。它直接以数字采样值表示模拟音频信号的瞬时幅度,因此数据量最大,但也是所有音频处理的基础。
FFmpeg解码PCM的关键参数
使用 -f 选项指定PCM的封装格式(实质上是采样格式):
f32le— 32位浮点型,小端字节序,每个样本占4字节s16le— 16位有符号整数,小端字节序,每个样本占2字节s32le— 32位有符号整数,小端字节序,每个样本占4字节
-ac 指定声道数(如1为单声道,2为立体声),-ar 指定采样率(如44100Hz)。
PCM二进制结构
PCM文件没有文件头,直接按顺序存放每个采样点的数值。对于单声道文件,数据排列为:
[样本0][样本1][样本2]...
对于立体声(双声道),数据按帧交错排列,每帧包含左右声道各一个样本:
[左声道0][右声道0][左声道1][右声道1]...
每个样本所占的字节数由采样格式决定:f32le占4字节,s16le占2字节,s32le占4字节。
用Python读取PCM数据
Python的 struct.unpack 模块可以高效地将二进制数据解析为数值:
'f'— 32位浮点数(对应f32le)'h'— 16位有符号整数(对应s16le)'i'— 32位有符号整数(对应s32le)'<'— 小端字节序(little-endian),Intel/AMD架构的标准
示例:struct.unpack('<' + 'f' * n, data) 将二进制数据解析为n个32位浮点数。
广告
{{v.name}}
从 input.mp3 的第 1 分 30 秒开始,截取 10 秒钟的音频,并将其转换为单声道、44.1kHz 采样率、32 位浮点型的 PCM 数据,代码如下
$ ffmpeg -ss 00:01:30 -t 10 -i input.mp3 -f f32le -ac 1 -ar 44100 -acodec pcm_f32le output.pcm
生成的 output.pcm 文件是二进制文件,需要编写程序来读取这些数据并进行处理。
读取 f32le 格式的 PCM 文件并将前 1000 个样本的值打印出来,代码如下
import struct
# 指定 PCM 文件路径
pcm_file = "output.pcm"
# 打开并读取 PCM 二进制数据
with open(pcm_file, 'rb') as f:
pcm_data = f.read()
# 计算样本总数(每个 32 位浮点数占 4 个字节)
sample_count = len(pcm_data) // 4
print(f"Total samples: {sample_count}")
# 使用 struct 模块解包二进制数据为浮点数列表
# '<' 表示小端字节序,'f' 表示浮点数
samples = struct.unpack('<' + 'f' * sample_count, pcm_data)
# 打印前 1000 个样本的值(或其他处理)
for i in range(1000):
print(f"Sample {i}: {samples[i]}")
读取pcm_s16le(16 位有符号整数)格式的 PCM 文件并将前 1000 个样本的值打印出来,代码如下
import struct
with open("output_s16.pcm", 'rb') as f:
pcm_data = f.read()
# 每个 16 位整数占 2 个字节
sample_count = len(pcm_data) // 2
# 'h' 表示 16 位有符号整数
samples = struct.unpack('<' + 'h' * sample_count, pcm_data)