a-law原理及算法实现

最近因为项目的原因需要将16bits的pcm信号编码成8bits样本进行传输,在网上查了下,有基于G.711标准的两种算法(A-law, u-law),现将原理及代码介绍如下:

介绍:
G711
也称为PCM(脉冲编码调制),是国际电信联盟订定出来的一套语音压缩标准,主要用于电话。它主要用脉冲编码调制对音频采样,采样率为8k每秒。它利用一个64Kbps未压缩通道传输语音讯号。起压缩率为1:2,即把16位数据压缩成8位。G.711是主流的波形声音编解码器。
G.711 标准下主要有两种压缩算法。一种是µ-law algorithm (又称often u-law, ulaw, mu-law),主要运用于北美和日本;另一种是A-law algorithm,主要运用于欧洲和世界其他地区。其中,后者是特别设计用来方便计算机处理的。这两种算法都使用一个采样率为8kHz的输入来创建64Kbps的数字输出。a-law也叫g711a,输入的是13位(其实是S16的高13位).

压缩过程
(1)取符号位并取反得到s,
(2)获取强度位eee,获取方法如图所示
(3)获取高位样本位wxyz
(4)组合为seeewxyz,将seeewxyz逢偶数为取补数,编码完毕

a-law原理及算法实现_第1张图片

示例:
输入pcm数据为3210,二进制对应为(0000 1100 1000 1010)
二进制变换下排列组合方式(0 0001 1001 0001010)
(1)获取符号位最高位为0,取反,s=1
(2)获取强度位0001,查表,编码制应该是eee=100
(3)获取高位样本wxyz=1001
(4)组合为11001001,逢偶数为取反为10011100

编码代码如下

#define MAX  (32635)   
void encode(unsigned char *dst, short *src, size_t len)
{ 
   for(int i = 0; i < len ; i++)
    {

 //      *dst++ =  *src++;
        short pcm  = *src++;
        int sign = (pcm & 0x8000) >> 8;
        if(sign != 0)
            pcm = -pcm;
        if(pcm > MAX)   pcm = MAX;
        int exponent = 7;
        int expMask;
        for(expMask = 0x4000; (pcm & expMask) == 0 && exponent >0; exponent--,expMask >>= 1){}
        int mantissa = (pcm >> ((exponent == 0) ? 4 : (exponent + 3))) & 0x0f;
        unsigned char alaw = (unsigned char)(sign | exponent << 4 | mantissa);
        *dst++ = (unsigned char)(alaw ^0xD5);   
    }     
} 

译码代码如下


void decode(short *dst, unsigned char *src, size_t len)
{
    for(size_t i=0; i < len  ; i++)
    {
        unsigned char alaw = *src++;
        alaw ^= 0xD5;
        int sign = alaw & 0x80;
        int exponent = (alaw & 0x70) >> 4;
        int data = alaw & 0x0f;
        data <<= 4;
        data += 8;   //丢失的a 写1
        if(exponent != 0)  //将wxyz前面的1补上
            data += 0x100;
        if(exponent > 1)
            data <<= (exponent - 1);

        *dst++ = (short)(sign == 0 ? data : -data);

    }
} 

测试函数

    std::string filename = "3.pcm";
    FILE* file = fopen(filename.c_str(), "rb");

    ScheduleServer::CPCMPlayer player;    
    short* frame = (short*) malloc(480);
    unsigned char * data = (unsigned char *) malloc(480);


    while (true)
    {
            size_t read_len = fread(frame, sizeof(short), 480, file);
            if(480 != read_len)
            {
                fseek(file, 0, SEEK_SET);
                //break;
            }

           encode(data,frame,read_len);
           decode(frame,data,read_len);

            player.play(frame);

    }

    free(frame);

你可能感兴趣的:(C++,primer,plus)