你真的了解PCM吗?

> 实现音频PCM录制的Github地址:https://github.com/crazydog-ki/MMSesssion

# 什么是PCM?

PCM全称Pulse-Code Modulation,即脉冲编码调制。简单来说就是一种用数字信号表示采样模拟信号的方法。

# 获取PCM原始数据的步骤

## 采样

通常自然界的声音可通过一条曲线在坐标中显示连续的模拟信号表示:

![声音模拟信号](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d4640da7edd49bc8401996a84273710~tplv-k3u1fbpfcp-zoom-1.image)

为了更加容易理解PCM,截取PCM部分波形,假设该波形表示1s的音频模拟信号。则采样可如下图所示:

![声音模拟信号采样](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fbf9202fe19a4b5888dde30f269b0566~tplv-k3u1fbpfcp-zoom-1.image)

其中红色的曲线表示**原始模拟信号**;蓝色垂直线段表示当前时间点对原始模拟信号的**一次采样**。采样是一系列**基于振幅**的样本,故采样过程又被称为PAM(Pulse Amplitude Modulation)。

**采样率**(Sample Rate)表示**每秒采样次数**,单位为Hz。根据场景不同,采样率也会有所差异,采样率越高,采样的声音就会越接近原始声音,声音的还原度就越好,质量越高,同时占用空间也会越大。例如,通话时的采样率为8kHz,常用的媒体文件采样率是44.1kHz。

## 量化

原始信号采样后需要通过**量化**来描述采样数据的大小。

![音频模拟信号量化](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6bac603602744c0782e5caf0a2a693bd~tplv-k3u1fbpfcp-zoom-1.image)

**量化处理过程**:就是将**时间连续的模拟信号**转为**离散的数字信号**,并将数字信号转换为二进制数,用于存储和传输。

在图例中,如果采样是画垂直线段,那么量化就是画水平线段,用于衡量每次采样的数字指标:

![音频模拟信号量化](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9aca8701649f4dce9375ef9db2a25101~tplv-k3u1fbpfcp-zoom-1.image)

每条横线代表一个等级,为更好地描述量化过程,引入**位深**的概念:

>位深:用于描述采用多少二进制位表示一个音频模拟信号采样,常见的位深有8bit、16bit、32bit、64bit,其中16bit最常见。显而易见,位深度越大对模拟信号的描述越真实,对声音的描述更加准确。

![音频模拟信号量化](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b1d63245701e4cab8891feb4a2246fb9~tplv-k3u1fbpfcp-zoom-1.image)

量化的过程就是将水平高度的样本四舍五入到一个可用的并且最近的Level描述的过程。

## 编码

![音频模拟信号编码](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/676d5b3373ad413294d16c32f099c24e~tplv-k3u1fbpfcp-zoom-1.image)

这里的编码是指将每个sample数据转换为二进制数据,该数据就是PCM数据。PCM数据可直接存储在介质上,也可以经过编码压缩后进行存储和传输。

PCM数据常用量化指标:**采样率**、**位深度**、**声道数**、**采样数据是否有符号**(有符号的采样数据不能使用无符号的方式播放)、**字节序**(表示PCM数据采样是大端存储还是小端存储,通常为小端存储)。例如:FFmpeg常见的PCM数据格式为s16le,表示有符号的16bit小端PCM数据。

## PCM数据存储

例如一段有符号的8bit的PCM数据:

```
binary | 0010 0000 | 1010 0000 | ...
decimal| 32 | -96 | ...
```

其表示的采样范围是-128~127,当含有多个通道时,PCM数据通常会交织排列,以双声道为例:

```

FL | FR | FL | FR | FL | FR

```

对于8bit有符号的PCM数据而言,上图表示第一个字节存放第一个左声道数据、第二个字节存放第一个右声道数据。不同的驱动程序对于多声道数据的排列方式可能稍有区别,下面是常用的声道排列图:

```

2: FL FR (stereo)

3: FL FR LFE (2.1 surround)

4: FL FR BL BR (quad)

5: FL FR FC BL BR (quad + center)

6: FL FR FC LFE SL SR (5.1 surround - last two can also be BL BR)

7: FL FR FC LFE BC SL SR (6.1 surround)

8: FL FR FC LFE BL BR SL SR (7.1 surround)

```

## 音量调整

音量的表示实际上就是量化过程中每个采样数据的Level值,只要适当地增大或者缩小采样的Level就可以达到更改音量的目的。但是需要说明的是,并不是将Level乘以2就能得到两倍于原声的音量。

**数据溢出**:每个采样数据的取值范围是有限制的,例如一个signed 8bit的样本,取值范围为-128~127,值为125时,放大两倍后的值为250,超过了可描述的范围,此时发生了数据溢出。这个时候就需要我们做策略性的裁剪处理,使放大后的值符合当前格式的取值区间。

**对数描述**:平时表示声音强度都是用分贝(db)作单位的,声学领域中,分贝的定义是声源功率与基准声功率比值的对数乘以10的数值。**根据人耳的心理声学模型,人耳对声音感知程度是对数关系,而不是线性关系**。人类的听觉反应是基于声音的相对变化而非绝对的变化。对数标度正好能模仿人类耳朵对声音的反应。所以用分贝作单位描述声音强度更符合人类对声音强度的感知。前面我们直接将声音乘以某个值,也就是线性调节,调节音量时会感觉到刚开始音量变化很快,后面调的话好像都没啥变化,使用对数关系调节音量的话声音听起来就会均匀增大.

如下图,横轴表示音量调节滑块,纵坐标表示人耳感知到的音量,图中取了两块横轴变化相同的区域,音量滑块滑动变化一样, 但是人耳感觉到的音量变化是不一样的,在左侧也就是较安静的地方,感觉到音量变化大,在右侧声音较大区域人耳感觉到的音量变化较小:

![声音音量](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e10f607cacc84267b476549a6c0090bd~tplv-k3u1fbpfcp-zoom-1.image)

上一篇:C语言实现 IFFT 运算


下一篇:使用开源poi 实现 Word转Pdf