原理
PCM是對模擬的連續信號進行抽樣。
G711則是對PCM數據進行再一次的抽樣。
G711主要是對16bit的PCM進行抽樣,取到PCM的高位數據,去掉低位的數據,并且只保留8位。這樣壓縮的比率就達到了2:1。可知是有損壓縮。
這里主要討論G711a.
編碼過程
bit: 0--1--2--3--4--5--6--7--8--9--10--11--12--13--14--15
0----符號位s
* Linear Input Code Compressed Code
* ---------------------------------------
* 0000000wxyza 000wxyz
* 0000001wxyza 001wxyz
* 000001wxyzab 010wxyz
* 00001wxyzabc 011wxyz
* 0001wxyzabcd 100wxyz
* 001wxyzabcde 101wxyz
* 01wxyzabcdef 110wxyz
* 1wxyzabcdefg 111wxyz
接下來的部分就是尋找聲音的強度位。
按上表,共有8個級別。
標記為seg。
然后取強度位之后的4位作為樣本位wxyz
最終經過編碼以后
就變成了:s(取反)+3位強度位+4位樣本位總共8位數據。
代碼
//強度表
static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF};
//尋找當前的強度位,其實就是前八位的匹配
/*前八位可能性
0x0000 0000
0x0000 0001
0x0000 0010
0x0000 0100
0x0000 1000
0x0001 0000
0x0010 0000
0x0100 0000
*/
static int search(int val, short *table, int size)
{
int i;
for (i = 0; i < size; i++) {
if (val <= *table++)
return (i);
}
return (size);
}
//編碼
unsigned char pcm2g711a(int pcm_val)
{
int mask;
int seg;
unsigned char aval;
if (pcm_val >= 0) { //正數
mask = 0xD5; //0x 1101 0101
} else { //負數
mask = 0x55; //0x 0101 0101
pcm_val = -pcm_val - 1; //取反減1 0x0000 0001
}
//mask作用,符號位取反,偶數位取反
//找到高位對應的數值表所在位置
seg = search(pcm_val, seg_end, 8);
//組合符號位,高位,和高位樣本位
if (seg >= 8) //尋找表里最大,直接返回最大值
return (0x7F ^ mask);
else {
aval = seg << SEG_SHIFT; //左移4位 取得高位,并且最低四位為0000
if (seg < 2) //原值seg為0或者1;
aval |= (pcm_val >> 1) & QUANT_MASK; //右移四位,取樣本位
else //原值seg為2,3,4,5,6,7
aval |= (pcm_val >> seg) & QUANT_MASK;//右移seg+3位取樣本位
return (aval ^ mask); //高位組合樣本為,并且符號位取反
}
}
解碼過程
拿到8位的g711編碼數據后
與0x55亦或,還原偶數位,并取強度位。
取得樣本位.
根據強度位+還原樣本,放大還原。
通過符號位的值取反得到pcm數據的正負。
代碼
//解碼
short alaw2pcm(
unsigned char a_val)
{
short t;
short seg;
a_val ^= 0x55;
//后四位a_val即強度樣本位
t = (a_val & QUANT_MASK) << 4;//左移放大,對其強度樣本位
//強度位
seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
switch (seg) {
case 0: //強度位為0,即0x0000 0000
t += 8; //即0x0000 0000 0000 1000+強度樣本位
break;
case 1: //強度位為1,即0x0000 0001
t += 0x108;//即0x0000 0001 0000 1000+強度樣本位
break;
default: //強度位為2-7
t += 0x108;//即0x0000 0001 0000 1000+強度樣本位
t <<= seg - 1; //左移恢復原本數量級
}
//符號位
return ((a_val & SIGN_BIT) ? t : -t);
}