系列音视频指南
文章目录
前言
本文重点讲解PCM与PCMA之间的转换,PCM与PCMU的转换感兴趣的话可以自己实现!!
一、PCM是什么?
PCM:又称脉冲编码调制。人耳听到的是模拟信号,PCM是把声音从模拟信号转化为数字信号的技术。原理是用一个固定的频率对模拟信号进行采样,采样后的信号在波形上看就像一串连续的幅值不一的脉冲(脉搏短暂起伏的电冲击),把这些脉冲的幅值按一定的精度进行量化,这些量化后的数值被连续的输出、传输、处理或记录到存储介质中,所有这些组成了数字音频的产生过程(抽样、量化、编码三个过程)。
描述PCM数据的6个参数:
Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。
Sample Size : 量化位数。通常该值为16-bit。
Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两种类型,立体声包含左声道和右声道。还有环绕立体声等其它不太常用的类型。
Sign : 表示样本数据是否是有符号位,比如有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255。
Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。
Integer Or Floating Point : 整形或浮点型。大多数格式的PCM样本数据使用整形表示。
PCM数据格式

计算音频文件大小:
[时长] s * [采样率]Hz * [采样位数]bit * [声道数] / 8 = [文件大小]byte
某音频信号是采样率为8kHz、声道数、位宽为16bit,时长为1s,则音频数据的大小为:
1 * 8000 * 16 *2 = 256000 bit / 8 = 32000 byte / 1024 = 31.25 KB(双声道)
二、G711是什么?
g711是一种由国际电信联盟制定的一套语音压缩标准,主要用于电话语音通信,而人声最大频率一般在3.4kHz,所以只要以8k的采样频率对人声进行采样,就可以保证完全还原原始声音。G711-U和G711-A都能提供较好的语音质量,但是它们占用的带宽较高,需要64kbps=8KByte,打包周期是20ms,因此8000/(1000/20ms)= 160Bytes, G.729也是20ms的打包频率。
g711的内容是将一个13bit或14bit的样本编码成一个8bit的样本。g711标准主要分两种压缩方法:a-law和mu-law,
a-law:将一个13bit的pcm样本压缩成一个8bit的pcm样本。
mu-law:将一个14bit的pcm样本压缩成一个8bit的pcm样本。
压缩比固定为:
8/14 = 57% (uLaw)
8/13 = 62% (aLaw)
G711 A-LAW
a-law采用13折线法编码,13折线是从“非均匀量化”基点出发,非均匀量化是精分低信号,粗分高信号。alaw编码的目标是将一个13bit的pcm样本压缩成一个8bit样本.
把x轴划分为不均匀的8份,第一点取1/2处,第二点取1/4处,第三点取1/8处……第七点取1/128.
把y轴划分为均匀的8分段。
即:将x/y轴各段交点连接起来
分成7个点:1/128,1/64,1/32,1/16,1/8,1/4,1/2
分成8个段:1/128,1/128,1/64,1/32,1/16,1/8,1/4,1/2(x轴每个点之间的间隔)
为什么较十三折线:
0-1/128段和1/128-1/64的距离相等(是1/64平分来的),所以这两段的斜率相同,可以将第一第二段看作一大段,所以实际上是分了7段不同的斜率。
因为取值是±1,所以反向还有一段一样的图,因为±1±2斜率相同,所以将其合成一大段,这时整个图总共有13段斜率不同的折线,这就是13折线法的由来。

在13折线法中采用的折叠码有8位,其中第一位c1表示量化极性的正负,后面的7位分为段落码和段内码两部分,用于表示量化值得绝对值。其中个第2-4位(c2c3c4)为段落码,可以表示8种斜率的段落,其他4位c5~c8为段内码,可以表示每一段落内的16种量化电平。段内码代表的16个量化电平是均匀划分的。所以,这7位码总能表示2^7=128种量化值

G711 U-LAW
略
三、源码分析
1.PCM->G711A
代码如下(示例):
int SIGN_BIT = 0x80;
int QUANT_MASK = 0xf;
int SEG_SHIFT = 4;
int SEG_MASK = 0x70;
int BIAS = 0x84;
int CLIP = 0x1FDF; //8159
short seg_end[] = {0xFF, 0x1FF, 0x3FF, 0x7FF,0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
short searchSegNum(short val,short *table,short size){
for (short i = 0; i < size; i++) {
if(val <= table[i]){
return i;
}
}
return size;
}
char linear2alaw(short pcm_val){
short mask;
short seg;
unsigned short aval;
if(pcm_val >= 0){
mask = 0xD5;
}else{
mask = 0x55;
pcm_val = (short) (-pcm_val - 1);
if(pcm_val < 0){
pcm_val = 0x7FFF ; //32767
}
}
/* Convert the scaled magnitude to segment number. */
seg = searchSegNum(pcm_val, seg_end, (short)8);
/* Combine the sign, segment, and quantization bits. */
if (seg >= 8)
return (char) (0x7F ^ mask);
else {
aval = (unsigned short) (seg << SEG_SHIFT);
if (seg < 2)
aval |= (pcm_val >> 4) & QUANT_MASK;
else
aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
return (char) (aval ^ mask);
}
}
bool convertPcmToG711A(char* pcmBuffer, int pcmLength, char* g711Buffer)
{
if(!pcmBuffer || !g711Buffer || pcmLength==0){
return false;
}
for(int i=0; i<pcmLength; i++){
g711Buffer[i] = linear2alaw(pcmBuffer[i]);
}
return true;
}
播放g711a
ffplay.exe -i audiodebase64.pcma -f alaw -ac 1 -ar 8000
如果播放pcmu的话,改为-f ulaw

2.G711A->PCM
代码如下(示例):
bool convertG711ToPcm(char* g711Buffer, int g711length, char* pcmBuffer)
{
if (!pcmBuffer || g711length == 0){
return false;
}
for (int i=0; i<g711length; i++){
char alaw = g711Buffer[i];
alaw ^= 0xD5; //1101 0101
int sign = alaw & 0x80; //1000 0000
int exponent = (alaw & 0x70)>>4; //0111 0000
int pcm = (alaw & 0x0F)>>4 | 8; //0000 1111
if (exponent > 1){
pcm += 0x0100;
pcm <<= (exponent - 1);
}
pcm = (short)((sign == 0 ? pcm : -pcm) & 0xFFFF);
pcmBuffer[i*2+0] = (char) (pcm & 0xFF);
pcmBuffer[i*2+1] = (char) (pcm>>8 & 0xFF);
}
return true;
}
ffplay.exe -i audioOriginal.pcm -f s16le -ac 1 -ar 8000

3.PCM->G711U
略
4.G711U->PCM
略
总结
本文主要介绍了pcm与pcma的转换原理以及代码实现,感兴趣的 可以自己实现pcm与pcmu以及pcma与pcmu之间的转换。
提供一个开源的代码,可以当作参考资料 https://gitee.com/asalmc/pcm-g711.git