音频编码PCM介绍

了解音频编码之前,先了解一下常用的音频格式:

PCM参数

        PCM(Pulse Code Modulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字音频数据。  

描述PCM数据的6个参数:

(1).Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。

(2).Sample Size : 量化位数。通常该值为16-bit。

(3).Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两种类型,立体声包含左声道和右声道。另外还有环绕立体声等其它不太常用的类型。

(4).Sign : 表示样本数据是否是有符号位,比如用一字节表示的样本数据,有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255。

(5).Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。

(6).Integer Or Floating Point : 整形或浮点型。大多数格式的PCM样本数据使用整形表示,而在一些对精度要求高的应用方面,使用浮点类型表示PCM样本数据。

 

采样频率、量化深度数越高,声音质量也越高,保存这段声音所用的空间也就越大;立体声(双声道)存储大小是单声道文件的两倍。即:

文件大小(B)=采样频率(Hz)×录音时间(S)×(量化深度/8)×声道数(单声道为1,立体声为2)
如:录制1分钟采样频率为44.1KHz,量化深度为16位,立体声的声音(CD音质),文件大小为:
   44.1×1000×60×(16/8)×2=10584000B≈10.09M

PCM数据格式结构

        如果是单声道的音频文件,采样数据按时间的先后顺序依次存入(有的时候也会采用LRLRLR方式存储,只是另一个声道的数据为0),如果是双声道的话就按照LRLRLR的方式存储,存储的时候与字节序有关。big-endian模式如下图所示:

FFmpeg支持的PCM数据格式

使用ffmpeg -formats命令,获取ffmpeg支持的音视频格式,其中我们可以找到支持的PCM格式。

 DE alaw            PCM A-law
 DE f32be           PCM 32-bit floating-point big-endian
 DE f32le           PCM 32-bit floating-point little-endian
 DE f64be           PCM 64-bit floating-point big-endian
 DE f64le           PCM 64-bit floating-point little-endian
 DE mulaw           PCM mu-law
 DE s16be           PCM signed 16-bit big-endian
 DE s16le           PCM signed 16-bit little-endian
 DE s24be           PCM signed 24-bit big-endian
 DE s24le           PCM signed 24-bit little-endian
 DE s32be           PCM signed 32-bit big-endian
 DE s32le           PCM signed 32-bit little-endian
 DE s8              PCM signed 8-bit
 DE u16be           PCM unsigned 16-bit big-endian
 DE u16le           PCM unsigned 16-bit little-endian
 DE u24be           PCM unsigned 24-bit big-endian
 DE u24le           PCM unsigned 24-bit little-endian
 DE u32be           PCM unsigned 32-bit big-endian
 DE u32le           PCM unsigned 32-bit little-endian
 DE u8              PCM unsigned 8-bit

s是有符号,u是无符号,f是浮点数。
be是大端,le是小端。

FFmpeg中Packed和Planar的PCM数据区别

FFmpeg中音视频数据基本上都有Packed和Planar两种存储方式,对于双声道音频来说,Packed方式为两个声道的数据交错存储;Planar方式为两个声道分开存储。假设一个L/R为一个采样点,数据存储的方式如下所示:

  • Packed: L R L R L R L R
  • Planar: L L L L R R R R

FFmpeg音频解码后的数据是存放在AVFrame结构中的。

  • Packed格式,frame.data[0]或frame.extended_data[0]包含所有的音频数据中。
  • Planar格式,frame.data[i]或者frame.extended_data[i]表示第i个声道的数据(假设声道0是第一个), AVFrame.data数组大小固定为8,如果声道数超过8,需要从frame.extended_data获取声道数据。

下面为FFmpeg内部存储音频使用的采样格式,所有的Planar格式后面都有字母P标识。

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
    AV_SAMPLE_FMT_FLT,         ///< float
    AV_SAMPLE_FMT_DBL,         ///< double
 
    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    AV_SAMPLE_FMT_FLTP,        ///< float, planar
    AV_SAMPLE_FMT_DBLP,        ///< double, planar
    AV_SAMPLE_FMT_S64,         ///< signed 64 bits
    AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar
 
    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

说明:

  • Planar模式是ffmpeg内部存储模式,我们实际使用的音频文件都是Packed模式的。
  • FFmpeg解码不同格式的音频输出的音频采样格式不是一样。测试发现,其中AAC解码输出的数据为浮点型的 AV_SAMPLE_FMT_FLTP 格式,MP3解码输出的数据为 AV_SAMPLE_FMT_S16P 格式(使用的mp3文件为16位深)。具体采样格式可以查看解码后的AVFrame中的format成员或解码器的AVCodecContext中的sample_fmt成员。
  • Planar或者Packed模式直接影响到保存文件时写文件的操作,操作数据的时候一定要先检测音频采样格式。

字节序

谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian?

big endian是指低地址存放最高有效字节(MSB,Most Significant Bit),而little endian则是低地址存放最低有效字节(LSB,Least Significant Bit)。

下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:


Big Endian

低地址 高地址

----------------------------------------------------------------------------->

| 12 | 34 | 56 | 78 |


Little Endian

低地址 高地址

----------------------------------------------------------------------------->

| 78 | 56 | 34 | 12 |


所有网络协议都是采用big endian的方式来传输数据的。所以也把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。

PCM音量控制

下面我们先看一个真正的音频样本波形:

如果我们放大5倍波形,也就是振幅乘以5,此时我们听到了更大的声音,此时样本波形如下:

   

假如我们有2048bytesPCM数据,样本大小两个字节,共有1024个样本,我们要放大两倍声音,代码可以按如下写:

int pcm[1024] =data;
int i;
for (i = 0; i < 1024; i++) {
    pcm[i] *= 2;
}
这是不是很简单,但是接下来我们还需要考虑两个方面的问题

       因为每个样本取值范围是有限制的,调节音量时不可能随便增大,比如一个signed 16 bits的样本,值为5000,我们放大10倍,由于有符号位16bits数据取值范围为-32768~32767,5000乘以10得到的50000超过了32767,数据溢出了,最后值可能变为-15536,不是我们期望的。此时我们就需要裁剪了,确保数值在正确范围内。

对数描述

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

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

                                  

 

PCM音频数据的处理

分离双声道PCM音频数据左右声道的数据,按照双声道的LRLRLR的PCM音频数据可以通过将它们交叉的读出来的方式来分离左右声道的数据。

int pcm_s16le_split(const char* file, const char* out_lfile, const char* out_rfile) {
     FILE *fp = fopen(file, "rb+");
     if (fp == NULL) {
         printf("open %s failed\n", file);
         return -1;
     }
     FILE *fp1 = fopen(out_lfile, "wb+");
     if (fp1 == NULL) {
         printf("open %s failed\n", out_lfile);
         return -1;
     }
     FILE *fp2 = fopen(out_rfile, "wb+");
     if (fp2 == NULL) {
         printf("open %s failed\n", out_rfile);
         return -1;
     }
     char * sample = (char *)malloc(4);
     while(!feof(fp)) {
         fread(sample, 1, 4, fp);
         //L
         fwrite(sample, 1, 2, fp1);
         //R
         fwrite(sample + 2, 1, 2, fp2);
     }
     free(sample);
     fclose(fp);
     fclose(fp1);
     fclose(fp2);
     return 0;
 }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值