A律十三折线法G711编解码介绍
简介
G711国际电信联盟ITU-T定制出来的一套语音压缩标准,主要用于对PCM音频数据编码,将PCM16bit数据压缩为为8Bit,它是主流的波形音频编码器,相当于只在帧内压缩,不会从帧间之间来考虑压缩,输出码率通常大于32kbps,所以推荐输入信号码率为64kbps,8Khz和S16的采样格式,压缩比为1:2;
G711有两套编码标准A-law用于13bit压缩为8bit,u-law则是用于15bit压缩为8bit;前者主要用于北美和中国地区,后者则用于欧美和日本地区;
数学原理
简单来说,G711可以将16Bit数据压缩为8Bit数据。编码有两种A-law和u-law,A-law编码针对计算机做了设计处理,将13Bit压缩为8Bit,而u-law则是15Bit数据压缩;国内通用前者,所以我们以A-law为例讲解,它的函数公式:
A=87.6时,函数图像接近原点,函数图像如下:
该函数图中,X表示输入信号,Y表示输出信号,且均归一化到(-1, +1)区间;将x轴(0,1)非均匀量化为为8段1/2、1/4、… 1/64、1/128最终到原点0,共8段;同理每段对应到Y轴上也有8段,并且x负轴也分为这相同的8段,在原点(0,0)处,-1/64到+1/64是实质是相同的斜率,可归纳一个段,加上剩余的12段,也就是十三段,经过十三折线后的函数图像不再像上图的曲线,而是一段段的直线连接,折线图逼近A-law曲线图,故称十三折线法;并且十三段折线对应不同的斜率,在值较小时,曲线斜率近似于折线,精度较高,而值较大时,则曲线斜率与折线相差较大,精度较低
在以上基础上,我们将每一个(如1-1/2、1/2-1/4)大段称为“段落”,在每个段落内部实现均分16分,这样在X轴上(-1,+1)就有16个段落 * 16个均分 = 256个量级;对应到Y轴上也是256个量级,这样我们就将输入信号编码离散化到256个量级范围,刚好是8Bit,也就是编码后的值范围在256量级,也就是8Bit;而输入端X,我们段落之间非均匀化划分,段内16分均匀划分,这种均匀-非均匀量化范围可以0~2048(12bit)的范围,加上还有负数的情况(1bit极性),所以我们输入信号可以对13bit的数据进行编码,按照这种原理进行量化的表格如下:
量化范围 | 归一化 | 段落码(3bit) | 权重值 |
---|---|---|---|
0~16 | 0~1/128 | 0 0 0 | 8 4 2 1 |
16~32 | 1/128~1/64 | 0 0 1 | 8 4 2 1 |
32-64 | 1/64~1/32 | 0 1 0 | 16 8 4 2 |
64~128 | 1/32~1/16 | 0 1 1 | 32 16 8 4 |
128~256 | 1/16~1/8 | 1 0 0 | 64 32 16 8 |
256~512 | 1/8~1/4 | 1 0 1 | 128 64 32 16 |
512~1024 | 1/4~1/2 | 1 1 0 | 256 128 64 32 |
1024~2048 | 1/2~1 | 1 1 1 | 512 256 128 64 |
总段落实质是16个段落,8个正8个负,负数情况下转为正进行查表,同时记录正负极性
而段内码是均匀划分,表格如何:
量化范围 | 段内码(4bit) |
---|---|
0 | 0x0 |
1 | 0x1 |
2 | 0x2 |
… | … |
15 | 0xf |
拥有上面两张表后,我们就可以实现对输入信号进行编码压缩了,上面段落码+段内码一共才7bit,还差一位bit,这个就是信号的极性,正负了;
例如,输入信号2000,请问输出编码是多少?
- 确定段落码
2000在1024~2048段,所以段落码是1 1 1 - 确定段内码
2000 - 1024 = 976,1024~2048段内权重值为512 256 128 64,也就是512x + 256y+128z + 64w趋近于976,最终x=1,y=1,z=0,w=1; - 极性取反2000的符号位是0,所以极性为1
最终2000的编码值为1 1 1 1 1 1 0 1 = 0xfd
计算机实现
以上是十三折线法原理,但是转化到计算机上来还是有区别的,例如我们一般存储数据类型都是8的倍数类型存储,如short类型就是16bit,但是十三折线是针对13bit的数据压缩,这个时候就需要自行处理,移除低位3bit或者高位3bit,根据自己的需求处理;并且计算机处理做了特殊的改动,以下表为准:
- Linear Input code 代表输入的十三bit信号值,s是符号位0…1代表序号位,abcd代表强度位,x是会移除的bit
- Compressed code是经过压缩编码后的数据,s取反,0…1表示输入的序号位,高位第一个1的位置,abcd则是输入保留下来的
- Linear output code是逆过程,解码后的数据,s恢复,abcd恢复,输入x则只会添1做补偿,其他为0,这块也就是误差值存在
所以,按照上述表格,进行编码的步骤:
- 获取符号位
- 确定序号位的第一个高位1的位置,并确定序号编码
- 获取强度为abcd,也就是序号位后紧跟的4bit
- 最后还需要进行异或处理
最后,贴源码,网上有很多,我也是参考别人的,因为已经写得很好了,没必要自己在去重新实现:
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
#define QUANT_MASK (0xf) /* Quantization field mask. */
#define NSEGS (8) /* Number of A-law segments. */
#define SEG_SHIFT (4) /* Left shift for segment number. */
#define SEG_MASK (0x70) /* Segment field mask. */
#define BIAS (0x84) /* Bias for linear code. */
#define CLIP 8159
#define G711_A_LAW (0)
#define G711_MU_LAW (1)
#define DATA_LEN (16)
确定序号位的顺序index
static short seg_aend[8] = {
0x1F, 0x3F, 0x7F, 0xFF,
0x1FF, 0x3FF, 0x7FF, 0xFFF
};
编码函数
unsigned char linear2alaw(int pcm_val)
{
int mask;
int seg;
unsigned char aval;
移除低位3bit
pcm_val = pcm_val >> 3;
根据值正负确定mask掩码
if (pcm_val >= 0) {
mask = 0xD5;/* sign (7th) bit = 1 */
} else {
mask = 0x55;/* sign bit = 0 */
因为十三折线输入信号正负范围都是一样的,但是有符号的类型如char是-128~+127
负数是多一位,这里减一刚好和正数对应,并且pcm_val变为正值了,对应去编码
pcm_val = -pcm_val - 1;
}
查找序号位index
seg = search(pcm_val, seg_aend, 8);
编码超过最大值,去范围内最大值0x7f
if (seg >= 8)/* out of range, return maximum value. */
return (unsigned char) (0x7F ^ mask);
else {
aval = (unsigned char) seg << SEG_SHIFT;
为什么要分两种情况?seg为2说明序号位位于第一位,再他后面只有5个bit了,只能移动一位
if (seg < 2)
aval |= (pcm_val >> 1) & QUANT_MASK;
seg大于2情况右移seg个bit,只保留其后4位强度位即可
else
aval |= (pcm_val >> seg) & QUANT_MASK;
异或处理返回
return (aval ^ mask);
}
}
解码过程
int alaw2linear(unsigned char a_val)
{
int t;
int seg;
得到偶数位异或后的值
a_val ^= 0x55;
获取强度位4bit,并且左移动4位,强度位也已经在编码前的位置了
t = (a_val & QUANT_MASK) << 4;
获取序号位,也就是符号位后面第几位是1
seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
switch (seg) {
符号位全为0的情况,说明编码之前的值很小,而t是强度位保留下来了,就补偿加8,8刚好是一个字节低4位的最大值,当做补偿
case 0:
t += 8;
break;
符号位为1,说明7位序号位刚好最后一位是1 也就是0x0100 中间强度位刚好4个字节为t 低4位补偿8
case 1:
printf("i'm here");
t += 0x108;
break;
default:
其他符号位则以序号位为1的情况进行左移动,将序号位移动到正确的位置上去
t += 0x108;
t <<= seg - 1;
}
根据符号位确定返回正负值
return ((a_val & SIGN_BIT) ? t : -t);
}
以上就是A-law的算法原理以及实现过程,u-law是15折线法,和A-law原理差不多,感兴趣的可以自行去研究,这里就不再阐述了!