c++之PCM G711A G711U源码实现

发布于:2022-12-11 ⋅ 阅读:(1910) ⋅ 点赞:(1)

系列音视频指南


前言

本文重点讲解PCM与PCMA之间的转换,PCM与PCMU的转换感兴趣的话可以自己实现!!

一、PCM是什么?

PCM:又称脉冲编码调制。人耳听到的是模拟信号,PCM是把声音从模拟信号转化为数字信号的技术。原理是用一个固定的频率对模拟信号进行采样,采样后的信号在波形上看就像一串连续的幅值不一的脉冲(脉搏短暂起伏的电冲击),把这些脉冲的幅值按一定的精度进行量化,这些量化后的数值被连续的输出、传输、处理或记录到存储介质中,所有这些组成了数字音频的产生过程(抽样、量化、编码三个过程)。
描述PCM数据的6个参数:
Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。
Sample Size : 量化位数。通常该值为16-bit。
Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两种类型,立体声包含左声道和右声道。还有环绕立体声等其它不太常用的类型。
Sign : 表示样本数据是否是有符号位,比如有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255。
Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。
Integer Or Floating Point : 整形或浮点型。大多数格式的PCM样本数据使用整形表示。

PCM数据格式
在这里插入图片描述

计算音频文件大小:
[时长] s * [采样率]Hz * [采样位数]bit * [声道数] / 8 = [文件大小]byte
某音频信号是采样率为8kHz、声道数、位宽为16bit,时长为1s,则音频数据的大小为:
1 * 8000 * 16 *2 = 256000 bit / 8 = 32000 byte / 1024 = 31.25 KB(双声道)

二、G711是什么?

g711是一种由国际电信联盟制定的一套语音压缩标准,主要用于电话语音通信,而人声最大频率一般在3.4kHz,所以只要以8k的采样频率对人声进行采样,就可以保证完全还原原始声音。G711-U和G711-A都能提供较好的语音质量,但是它们占用的带宽较高,需要64kbps=8KByte,打包周期是20ms,因此8000/(1000/20ms)= 160Bytes, G.729也是20ms的打包频率。

g711的内容是将一个13bit或14bit的样本编码成一个8bit的样本。g711标准主要分两种压缩方法:a-law和mu-law,
a-law:将一个13bit的pcm样本压缩成一个8bit的pcm样本。
mu-law:将一个14bit的pcm样本压缩成一个8bit的pcm样本。
压缩比固定为:
8/14 = 57% (uLaw)
8/13 = 62% (aLaw)

G711 A-LAW

a-law采用13折线法编码,13折线是从“非均匀量化”基点出发,非均匀量化是精分低信号,粗分高信号。alaw编码的目标是将一个13bit的pcm样本压缩成一个8bit样本.
把x轴划分为不均匀的8份,第一点取1/2处,第二点取1/4处,第三点取1/8处……第七点取1/128.
把y轴划分为均匀的8分段。
即:将x/y轴各段交点连接起来
分成7个点:1/128,1/64,1/32,1/16,1/8,1/4,1/2
分成8个段:1/128,1/128,1/64,1/32,1/16,1/8,1/4,1/2(x轴每个点之间的间隔)

为什么较十三折线:
0-1/128段和1/128-1/64的距离相等(是1/64平分来的),所以这两段的斜率相同,可以将第一第二段看作一大段,所以实际上是分了7段不同的斜率。
因为取值是±1,所以反向还有一段一样的图,因为±1±2斜率相同,所以将其合成一大段,这时整个图总共有13段斜率不同的折线,这就是13折线法的由来。
在这里插入图片描述
在13折线法中采用的折叠码有8位,其中第一位c1表示量化极性的正负,后面的7位分为段落码和段内码两部分,用于表示量化值得绝对值。其中个第2-4位(c2c3c4)为段落码,可以表示8种斜率的段落,其他4位c5~c8为段内码,可以表示每一段落内的16种量化电平。段内码代表的16个量化电平是均匀划分的。所以,这7位码总能表示2^7=128种量化值
在这里插入图片描述

G711 U-LAW

三、源码分析

1.PCM->G711A

代码如下(示例):


int SIGN_BIT = 0x80;
int QUANT_MASK = 0xf;
int SEG_SHIFT = 4;
int SEG_MASK = 0x70;
int BIAS = 0x84;
int CLIP = 0x1FDF;       //8159 
short seg_end[] = {0xFF, 0x1FF, 0x3FF, 0x7FF,0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
short searchSegNum(short val,short *table,short size){
    for (short i = 0; i < size; i++) {
        if(val <= table[i]){
            return i;
        }
    }
    return size;
}
char linear2alaw(short pcm_val){
    short mask;
    short seg;
    unsigned short aval;
    if(pcm_val >= 0){
        mask = 0xD5;
    }else{
        mask = 0x55;
        pcm_val = (short) (-pcm_val - 1);
        if(pcm_val < 0){
            pcm_val = 0x7FFF ;      //32767
        }
    }
    /* Convert the scaled magnitude to segment number. */
    seg = searchSegNum(pcm_val, seg_end, (short)8);
    /* Combine the sign, segment, and quantization bits. */
    if (seg >= 8)       
        return (char) (0x7F ^ mask);
    else {
        aval = (unsigned short) (seg << SEG_SHIFT);
        if (seg < 2)
            aval |= (pcm_val >> 4) & QUANT_MASK;
        else
            aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
        return (char) (aval ^ mask);
    }
}

bool convertPcmToG711A(char* pcmBuffer, int pcmLength, char* g711Buffer)
{
    if(!pcmBuffer || !g711Buffer || pcmLength==0){
        return false;
    }
    for(int i=0; i<pcmLength; i++){
        g711Buffer[i] = linear2alaw(pcmBuffer[i]);
    }
    return true;
}

播放g711a
ffplay.exe -i audiodebase64.pcma -f alaw -ac 1 -ar 8000
如果播放pcmu的话,改为-f ulaw
在这里插入图片描述

2.G711A->PCM

代码如下(示例):

bool convertG711ToPcm(char* g711Buffer, int g711length, char* pcmBuffer)
{
    if (!pcmBuffer || g711length == 0){
        return false;
    }

    for (int i=0; i<g711length; i++){
        char alaw = g711Buffer[i];
        alaw ^= 0xD5;                           //1101 0101
        
        int sign     =  alaw & 0x80;            //1000 0000
        int exponent = (alaw & 0x70)>>4;        //0111 0000
        int pcm    = (alaw & 0x0F)>>4 | 8;      //0000 1111
        if (exponent > 1){
            pcm += 0x0100;                   
            pcm <<= (exponent - 1);
        }
        pcm = (short)((sign == 0 ? pcm : -pcm) & 0xFFFF);
        pcmBuffer[i*2+0] = (char) (pcm & 0xFF);
        pcmBuffer[i*2+1] = (char) (pcm>>8 & 0xFF);
    }
    return true;
}

ffplay.exe -i audioOriginal.pcm -f s16le -ac 1 -ar 8000
在这里插入图片描述

3.PCM->G711U

4.G711U->PCM

总结

本文主要介绍了pcm与pcma的转换原理以及代码实现,感兴趣的 可以自己实现pcm与pcmu以及pcma与pcmu之间的转换。
提供一个开源的代码,可以当作参考资料 https://gitee.com/asalmc/pcm-g711.git

本文含有隐藏内容,请 开通VIP 后查看

微信公众号

今日签到

点亮在社区的每一天
去签到