A律十三折线法G711编解码介绍

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. 获取符号位
  2. 确定序号位的第一个高位1的位置,并确定序号编码
  3. 获取强度为abcd,也就是序号位后紧跟的4bit
  4. 最后还需要进行异或处理

最后,贴源码,网上有很多,我也是参考别人的,因为已经写得很好了,没必要自己在去重新实现:

#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是强度位保留下来了,就补偿加88刚好是一个字节低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原理差不多,感兴趣的可以自行去研究,这里就不再阐述了!


参考文献

关于二进制的基础知识
PCM的A律13折线编码介绍
通信原理之13折线

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/108140976
今日推荐