不扯量子力学的话,现实中的音频是模拟信号,比如一些单一乐器发出的正弦波,经过采样存储在电子设备上的就是数字信号了。PCM(Pulse Code Modulation) 脉冲编码调制,就是一种对现实音频采样并数字化的过程。 采样一般有这三个参数:

采样率
每秒内采样的次数。采样率越高越接近真实的模拟信号。常见采样率有8kHz,20.05kHz,44.1kHz,48kHz,96kHz。 采样率描述的是频率,是针对正弦波的纵轴而言的。

采样位数
是每个采样点的解析度,其值是最接近每个采样点的振幅。数值越大解析度越高,录制和播放出来的声音越接真实。 一般有8,16,24,32位(bits),对应1,2,3,4 bytes. 采样位数描述的是振幅,是针对正弦波的横轴而言的。

声道
声道数是指录制声音时不同空间位置独立的音频信号,也可以说是录制声音时麦克风的数量。 比如单声道,双声道。还有5.1声道,一般家庭影院是这样的,它指的是:

  • C(Central)中置
  • FL(Front Left)左前置
  • FR(Front Right)右前置
  • SL(Surround Left)左环绕
  • SR(Surround Right)右环绕
  • SW(Subwoofer)低音炮

PCM文件本身是最原始的采样数据,它并没有指明上面说的采样率等参数,所以一般的播放器是没法播放PCM文件的。

所以我要使用 Python 播放 PCM 文件之前也是需要知道这些参数的,巧的是我知道这个 PCM 文件的来源,当然也就知道这些参数了。

Python 本身没有播放音频的库,需要一个第三方的库,网上常说的 PyAudio 早已不维护,而我现在使用的是 Python3.10,于是找了好久找到了 simpleaudio 这个库。

在安装好simpleaudio后发现依然无法使用,提示 No module named simpleaudio,最后发现我的 vscode 使用的还是我没有卸载的 Python3.9,而我的 simpleaudio 是安装在 Python3.10 下的,切换一下就好,这里需要注意下。

另外官方给的例子使用了 numpy 的 linspace 函数,在传递第三个参数时给的是 float 类型,而那个参数需要的是 int,于是报了 ‘float’ object cannot be interpreted as an integer 的错, 这里也要注意下。下面是一个简单的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import simpleaudio as sa

pcmfile = open(
    r"audio.pcm", "rb"
)
# 可播放,音量需要调很大
pcm_data = pcmfile.read()
play_obj = sa.play_buffer(pcm_data, 1, 2, 48000)

# wait for playback to finish before exiting
play_obj.wait_done()

后来也想到了 Gstreamer 和 FFmpeg,使用 FFmpeg 的 ffplay 也可以播放,比如:

ffplay -ar 48000 -ac 1 -f s16le -i audio.pcm

该命令的作用是使用 ffplay 播放频率为48000Hz,单通道,16位、小端的音频文件 audio.pcm

  • -i 表示指定的输入文件
  • -f 表示强制使用的格式
  • -ar 表示播放的音频数据的采样率
  • -ac 表示播放的音频数据的通道数