介绍
g711常用在嵌入式方面,采样率为8k。g711 标准下主要有两种压缩算法。一种是u-law,另一种是A-law。两者都是对一个采样进行压缩,区别在于g711-a将13位转换为8位压缩数据,g711-u将14位转换为8位压缩数据。
压缩比固定为:
8/14 = 57% (uLaw)
8/13 = 62% (aLaw)
但事实上写代码的时候,常常将16位转换位8位。压缩比为50%
测试一下
模拟201个16位音频采样数据 区间在[-10000–10000]
转换为8位的g711a压缩数据再转换回来
将数据用matlab上显示出来
蓝色为pcm采样数据
红色为g711a压缩数据(相对16位变化不明显)
橙色为压缩后再解压缩的pcm数据
可以很明显看到在低频段的数据是最精准的,频率越高声音的失真是越大的。这是由于g711采用非线性量化–对数量化
对数量化
由于语声的小信号出现概率大,大信号出现概率小,所以需要对小信号更加精准,大信号相对粗略点。
对数量化的实现
如G711A使用压缩十三折线法
g711a输入的是13位(S16的高13位),这种格式是经过特别设计的,便于数字设备进行快速运算。
1、取符号位并取反得到s
2、获取强度位eee
3、获取高位样本位wxyz
4、组合为seeewxyz,将seeewxyz逢偶数为取补数,编码完毕
将红线(g711a压缩数据)放大看看
代码
经过验证可行,方便以后使用
#define SIGN_BIT (0x80)
#define QUANT_MASK (0xf)
#define NSEGS (8)
#define SEG_SHIFT (4)
#define SEG_MASK (0x70)
#define BIAS (0x84)
#define int16_t short
#define uint8_t unsigned char
#define uint32_t unsigned long
#define LENGTH 400
int ulaw2linear(unsigned char u_val)
{
int t;
u_val = ~u_val;
t = ((u_val & QUANT_MASK) << 3) + BIAS;
t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}
int alaw2linear(unsigned char a_val)
{
int t;
int seg;
a_val ^= 0x55;
t = (a_val & QUANT_MASK) << 4;
seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
switch (seg) {
case 0:
t += 8;
break;
case 1:
t += 0x108;
break;
default:
t += 0x108;
t <<= seg - 1;
}
return ((a_val & SIGN_BIT) ? t : -t);
}
int g711_decode(void *pout_buf, int *pout_len, const void *pin_buf, const int in_len, int type)
{
int16_t *dst = (int16_t *)pout_buf;
uint8_t *src = (uint8_t *)pin_buf;
uint32_t i = 0;
int Ret = 0;
if ((NULL == pout_buf) || \
(NULL == pout_len) || \
(NULL == pin_buf) || \
(0 == in_len)) {
return -1;
}
if (*pout_len < 2 * in_len) {
return -2;
}
if (TP_ALAW == type) {
for (i = 0; i < in_len; i++) {
*(dst++) = (int16_t)alaw2linear(*(src++));
}
}
else {
for (i = 0; i < in_len; i++) {
*(dst++) = (int16_t)ulaw2linear(*(src++));
}
}
*pout_len = 2 * in_len;
Ret = 2 * in_len;
return Ret;
}
编码
static short seg_aend[8] = {
0x1F, 0x3F, 0x7F, 0xFF,
0x1FF, 0x3FF, 0x7FF, 0xFFF
};
static short search(int val, short *table, int size)
{
int i;
for (i = 0; i < size; i++) {
if (val <= *table++)
return (i);
}
return (size);
}
unsigned char linear2alaw(static int pcm_val)/* 2's complement (16-bit range) */
{
int mask;
int seg;
unsigned char aval;
pcm_val = pcm_val >> 3;
if (pcm_val >= 0) {
mask = 0xD5;/* sign (7th) bit = 1 */
}
else {
mask = 0x55;/* sign bit = 0 */
pcm_val = -pcm_val - 1;
}
/* Convert the scaled magnitude to segment number.
将缩放的幅度转换为段数
*/
seg = search(pcm_val, seg_aend, 8);
/* Combine the sign, segment, and quantization bits.
组合符号,分段和量化位
*/
if (seg >= 8)/* out of range, return maximum value. */
return (unsigned char)(0x7F ^ mask);
else {
aval = (unsigned char)seg << SEG_SHIFT;
if (seg < 2)
aval |= (pcm_val >> 1) & QUANT_MASK;
else
aval |= (pcm_val >> seg) & QUANT_MASK;
return (aval ^ mask);
}
}
int main()
{
FILE * fp_in = fopen("./x64/g711.pcm","rb");
FILE * fp_out = fopen("./x64/out_audio.pcm", "wb");
char in_buf[LENGTH];
char out_buf[LENGTH*2];//输出为输入2倍
int in_len = LENGTH;
int out_len = LENGTH * 2;
if (!fp_in||!fp_out)
{
printf("error open file\n");
return -1;
}
while (LENGTH == fread(&in_buf, 1, LENGTH, fp_in))
{
g711_decode(out_buf, &out_len,&in_buf, LENGTH, TP_ALAW);
fwrite(out_buf, 1, out_len, fp_out);
printf("send %d\n",out_len);
}
fclose(fp_in);
fclose(fp_out);
return 1;
}