2019-03-17 16:35:43 qq_43103848 阅读数 3311

一、通信系统信源
通信系统的信源有两大类:模拟信号和数字信号。例如:话筒输出的语音信号属于模拟信号; 而文字、计算机数据属于数字信号。 数字信号相比于模拟信号有抗干扰能力强、无噪声积累的优点。因此,若输入是模拟信号,则在数字通信系统的信源编码部分需对输入模拟信号进行数字化。
数字化需要三个步骤:抽样、量化和编码。抽样是指用每隔一定时间的信号样值序列来代替原来在时间上连续的信号,也就是在时间上将模拟信号离散化。量化是用有限个幅度值近似原来连续变化的幅度值, 把模拟信号的连续幅度变为有限数量的有一定间隔的离散值。 编码则是按照一定的规律, 把量化后的值用二进制数字表示,然后转换成二值或多值的数字信号流。
三个步骤如下图所示:

在这里插入图片描述
二、抽样信号的非均匀量化编码( 13 折线法)
(1)脉冲编码调制简介
模拟信号抽样后变成时间离散的信号,经过量化后,此抽样信号才能成为数字信号。分析可知:最简单的均匀量化器对于小输入信号很不利。为了改善小信号时的信号量噪比,在实际应用中常采用非均匀量化。非均匀量化时,量化间隔随信号抽样值的不同而变化。信号样值小时,量化间隔 v也小;信号抽样值大时,量化间隔 v也变大。
实际应用中,用 13 折线法近似 A压缩律,来进行非均匀量化。图中横坐标 x 在 0 至 1 区间中分为不均匀的 8 段。1/2 至 1 间的线段称为第8 段;1/4 至 1/2 间的线段称为第 7 段;1/8 至 1/4 间的线段称为第 6 段;依此类推。图中纵坐标 y 则均匀地划分作 8 段。将与这 8 段相应的座标点 (x, y) 相连,就得到了一条折线。
在这里插入图片描述
在语音通信中,通常采用 8 位的 PCM编码就能够保证满意的通信质量。在 13 折线法中采用的折叠码有 8 位。第一位 c1 表示量化值的极性正负。后面的 7 位分为段落码和段内码两部分,用于表示量化值的绝对值。其中第 2 至 4位(c2 c3 c4) 是段落码,共计 3 位,可以表示 8 种斜率的段落;其他 4 位(c5 --c8)为段内码,可以表示每一段落内的 16 种量化电平。段内码代表的 16 个量化电平是均匀划分的。所以,这 7 位码总共能表示 7 2 =128 种量化值。

在下面的表中给出了段落码和段内码的编码规则。

在这里插入图片描述
在上述编码方法中,段内码是按量化间隔均匀编码的,但是因为各个段落的斜率不等,长度不等,故不同段落的量化间隔是不同的。其中第 1 和 2 段最短,斜率最大,其横坐标 x 的归一化动态范围只有 1/128。再将其等分为 16 小段后,每一小段的动态范围只有 (1/128)/(1/16) = 1/2048 。第 8 段最长,其横坐标 x的动态范围为 1/2 。将其 16等分后,每段长度为 1/32。假若采用均匀量化而仍希望对于小电压保持有同样的动态范围 1/2048,则需要用 11 位的码组才行。现在采用非均匀量化,只需要 7 位就够了。

2012-06-23 16:18:00 weixin_34326558 阅读数 61

摘要:本文介绍了G.711标准的A律压缩算法的基本原理,设计出了A律编解码的软件流程框图,在以TMS320VC5416为处理器的硬件开发平台上实现了语音信号的A律压缩解压算法,并给出了C54x的汇编语言程序。

0 引言

语言压缩技术既节省了存储时所占用的存储空间,又减少了传输时所占用的带宽,而快速发展的数字信号处理(DSP)技术使得实时实现各种复杂的语音压缩算法成为可能。

国际电信联盟(ITU)1972年制定出来的一套语音压缩标准称为G.7ll,主要用于公用电话网。它用脉冲编码调制(PCM)对语音信号采样,采样率为8kHz,比特率为64kb/s。该标准下主要有两种压缩算法,一种是μ律,另一种是A律。其中欧洲和中国等国家采用A律压缩算法,美国和日本等国家采用μ律压缩算法。

1 A律压缩算法简介

A律编码的数据对象是12位精度的二进制数,它保证了压缩后的数据有5位的精度并存储到一个字节(8位)中。A律符合下式的对数压缩方程:

式中,x为归一化的输入语音信号,y为压扩后的归一化输出信号,A为压缩参数取值。由于该方程是一条连续的光滑曲线,物理上实现比较复杂。在实用中一般用13段折线近似该曲线,此时选择AA=87.6。对PCM信号,一般地,输入的模拟语音信号经过低通滤波、放大和限幅后,然后进入A/D转换器编码,常用的编码方法有2的补码和折叠二进制码两种。表l是A律压缩编码规律表,其中只给出了在抽样值为正值时,A/D转换器输出的12位二进制数(补码)是如何压缩成8位二进制数的。

2 A律算法的硬件构成

采用合众达电子的SEED-DEC5416嵌入式DSP开发板作为实现A律算法的硬件开发平台,其系统结构原理如图1所示。主处理器选择的是1 6 bit定点DSP芯片TMS320vC5416,语音信号经过差分运算放大后送入ADC进行模数转换,采样率为8kHz,量化位数为12bit,转换后的数据送入McBSP,然后对McBSP中的数据进行压缩编码,压缩后的数据送入'C5416内部的DARAM保存,还可以直接解压后经McBSP送DAC,数模转换后再送入功放模块驱动扬声器发声。其中,McBSP与ADC/DAC之间的数据交换是靠ADC/DAC提供的移位信号和帧同步信号实现的。

3 A律算法的软件设计

在TI提供的开发工具CCS中进行源程序的编辑和调试,源程序采用'C54x系列的汇编语言编写。首先完成对DSP芯片必要的初始化、McBSP的初始化及开放McBSP中断,然后执行A律压缩解压算法。

一般地,用程序进行A律编码解码有两种方法:一种是查表法,这种方法程序代码小,执行速度快,但需要占用较多的内存以存储查找表(表的大小为212=4096个字),另一种方法是直接计算法,这种程序代码较多,执行速度慢一些,但可以节省内存空间。对照表l,可以画出A律压缩编码直接计算方法的流程如图2所示。待压缩的数据来自voidin变量空间(见附录1),该变量空间保存着McBSP中的数据,保存符号位就是将待压缩数据的位11放入压缩数据的位7,并通过测试位11决定是否对该数求补即对负数求绝对值(A/D转换后的数据用二进制补码表示),求绝对值的目的是为了减小直接对负值压缩时带来的量化误差。另外,还将两个8位的压缩数据组成一个16位的字保存至compress变量空间。

解压一般是压缩的逆过程,但有时为了使得量化误差不超过△i/2,可在解压后的数据位增加一个最低有效位,该最低有效位为1时经过D/A转换后对应△min/2电压,即将8位的压缩数据解压成13位的数据。本文中解压仍然按照解压成12位的数据,其解压流程如图3所示。图3中压缩数据暂存单元com的位*的段码值若大于l时,需将这4位段内码的前后相邻位都置1,然后将这6位送至解压数据暂存单元dcom的位(段码值+3)~位(段码值-2)即可,代码(见附录2)中采用的方法是对这6位逻辑左移(段码值-2)位。

4 实验分析及结论

由于语音信号可以分解成许多正弦频率的和,实验中采用200Hz的正弦信号代替实际的语音信号。将汇编语言编写的源程序和命令连接文件都添加到新建立的工程文件夹中,并编译、链接及装载程序运行,然后执行集成开发环境ccs的菜单命令View|Graph|Time Frenquency,打开Graph Property Dialog对话框,在此分两次输入标题名sine和decompress sine、起始地址0x0082和0x065E(命令连接文件.cmd中将.bss段定位到DARAM的org=0080h上),并选择数据的长度为200及数据类型为16-bit signed integer,然后点击ok按钮即可生成图4和图5的曲线。

与图4相比,图5只有在幅值比较大时两者的误差才略为明显,这是A律解压算法特性造成的,对语音信号的影响并不大,因为语音信号出现大幅度值的概率小。

本文完成的语音信号的A律编解码的DSP实现,经过实验表明,可以达到实时的语音压缩及解压效果

附录1压缩代码

转载于:https://www.cnblogs.com/alfredzzj/archive/2012/06/23/2559360.html

2019-03-17 17:22:11 qq_43103848 阅读数 2699

(一)13 折线法编码的 Matlab 实现
(a)编程思想
“模拟信号的抽样”已经得到了时间离散、幅度连续的抽样信号。若将对抽样信号非均匀量化编码编出的 8 位码组用 C1 C2 C3 C4 C5 C6 C7 C8 表示。
(1)抽样信号是 1×n 的矩阵,例如采样周期 T=0.0015; n=0:1:1/T; 则有667个抽样值, 13 折现法编码后得到的则是 667×8 的矩阵,一行对应一个抽样值的 8 位 PCM码组。每个抽样值的编码思想都是一样的, 若要进行多个抽样值的编码,进行 for 循环即可。下面阐述单个抽样值的编码思路。
(2)确定极性码 C1 :利用 matlab 的符号函数 sign(x) :x<0 时,sign(x)=-1;x=0 时,sign(x)=0;x>0 时,sign(x)=1。所以,若 sign (x)大于等于 0, C1 =1;否则 C1=0。
例如: +1000,sign (1000)=1,所以C1 =1。
(3)确定段落码 C2 C3 C4 :PCM13折线编码的动态范围为 -2048–2048 ,而上一步骤中已经求出了各抽样信号极性,于是只要对抽样信号的绝对值分析即可。故对抽样值依次进行取模、归一、乘以 2048、取整的操作,可以将抽样值转化为 0–2048 之间的整数。根据段落码与段落范围的关系,使用 if 语句即可确定C2 C3 C4 。
例如: +1000,因为 1000≥128,故 C2 =1;又 1000≥512,故 C3=1;又 1000
≤1024,故 C4 =0。对于其他取值情况,判断方法与此类似。
(4)确定段内码 C5 C6 C7 C8 :每一段落均被均匀地划分为 16 个量化间隔,不过不同段落的量化间隔是不同的。 (3)中确定了段落编码, 可以确定每段的起始值,再根据待编码值、 所在段的起始值、 所在段量化间隔的大小即可确定段内码。
例如:+1000,C2 C3 C4 =110,故 1000处于第 7 段(二进制 110 转化为十进制得到 6,6+1=7)

sp=[0,16,32,64,128,256,512,1024]; %每段起始值
spmin=[1,1,2,4,8,16,32,64]; %每段的最小量化间隔

再得到段起始值 sp(7)=512,段最小量化间隔 spmin(7)=32。(1000-512)/32=15.15, 向负无穷方向取整得到 15,再将15十进制转化为二进制, 得到1111,故 C5C6C7C8=1111。
(5)若码组矩阵初始化为全 0阵,则只有当某个码为 1时,才需要进行赋值。
例如: -100,sign (-100)=-1, C1 =0。而 C1 初始化值就为 0,故可以不采取任何操作,保持初始值即可。

(二)8 位 PCM 码的译码实现
(a)编程思想
(1)由 C1 可以确定抽样值的正负, C1 =1,抽样值为正;否则为负。
(2) C2 C3 C4确定抽样值的段落序号, C5 C6 C7 C8确定抽样值在段内第几个量化间隔。
(3)译码时,通常是将码组转化为此量化间隔的中间值。
例如: +1000编码后的 8位码组为 1110111,对1110111进行解码。
C1 =1,故抽样值为正。C2 C3 C4=110,二进制转化为十进制为 6,6+1=7,故抽样值在第 7段。

sp=[0,16,32,64,128,256,512,1024];       %段落起点值 
spmin=[1,1,2,4,8,16,32,64];             %最小量化间隔

C5 C6 C7 C8 =1111,二进制转为十进制为 15,故抽样值在段内第 15段。
sp(7)=512,spmin(7)=32。
11101111译码值为( 992+1024)/2=1008。也就是 sp(7)+spmin(7)×( 15+0.5)=512+32×15.5=1008
故利用段落码和段内码即可编程实现译码。

完整代码如下:

%pcm depcm comp.m 对抽样信号进行量化编码译码,将译码输出值与原抽样值比较 %13折线法编码源代码

T=0.0015; 

n=0:1:1/T; 

xn=2.5+3.*sin(200*2*pi*n*T)+2.*cos(70*2*pi*n*T);
%采样产生的抽样信号

figure(1) 

subplot(2,1,1) 

stem(n,xn) 

title('抽样得到的序列 ') 

 

z=sign(xn); %判断 S的正负 

xnnor=abs(xn)/max(abs(xn)); %xn取模并且归一化 

S=2048*xnnor; 

S=floor(S); %向负无穷方向取整

figure(2)

subplot(2,1,2) 

stem(n,S) 

axis([0,800,-2020,2020]) 

title('取模、归一、乘 2048、取整后的序列 ') 

 

code=zeros(length(S),8); %初始化码组矩阵为全零阵

%极性码第一位和段落码第二三四位

for i=1:length(S) 

if z(i)>=0

   
code(i,1)=1; %不改变依旧为 0

end

if (S(i)>=128) 

code(i,2)=1; 

if (S(i)>=512) 

code(i,3)=1; 

if (S(i)>=1024) 

code(i,4)=1; 

end

else

if (S(i)>=256) 

code(i,4)=1; 

end

end

else

if (S(i)>=32) 

code(i,3)=1; 

if (S(i)>=64) 

code(i,4)=1; 

end

else

if (S(i)>=16) 

code(i,4)=1; 

end

end

end

end

%段内码,第五六七八位

N=zeros(1,length(S)); 

for i=1:length(S)

   
N(i)=bin2dec(num2str(code(i,(2:4))))+1; %找到code位于第几段

end

sp=[0,16,32,64,128,256,512,1024]; %每段起始值 

spmin=[1,1,2,4,8,16,32,64]; %每段的最小量化间隔

 for
i=1:length(S)

   
loc=floor((S(i)-sp(N(i)))/spmin(N(i))); %向负无穷方向取整,段内第几段

   
if (loc==16) 

       
loc=loc-1; 

   
end %正负 2048时, loc=16,当做 15处理 

   
for k=1:4 

       
code(i,9-k)=mod(loc,2); 

       
loc=floor(loc/2);

   
end %十进制数转化为 4位二进制

end

fprintf( '抽样信号进行 13折线编码后的码组为(每一行代表一个抽样值,共 %d个值) ',length(S))

code %code为13折线译码后的码组,
是length(S)*8 的矩阵

 

 

%十三折线解码源代码

dcode=code; %将上面产生的
667个码值作为输入 ,decode是length(S)*8 的矩阵

sp=[0,16,32,64,128,256,512,1024]; %段落起点值 

spmin=[1,1,2,4,8,16,32,64]; %最小量化间隔

dS=zeros(1,size(dcode,1)); 

for i=1:size(dcode,1) 

   
par=bin2dec(num2str(dcode(i,(2:4))))+1; %段落位置 

   
parmid=bin2dec(num2str(dcode(i,(5:8)))); % 段间位置 

   
dS(i)=sp(par)+spmin(par)*(parmid+0.5);

   
if dcode(i,1)==0 %符号位

       
dS(i)=-dS(i); 

   
end

end

dS=dS/2048; %归一化

fprintf( '8位PCM 码译码后的归一化数值为(共 %d个值) ',size(dcode,1))

dS %dS为将8位PCM码解码后的归一化值

结果图:
在这里插入图片描述
在这里插入图片描述

%作图比较原抽样值与量化编码译码后的值(简便起见,比较 0至0.05)

T=0.0015; 

nbuf=0:1:0.05/T; 

xnbuf=2.5+3.*sin(200*2*pi*nbuf*T)+2.*cos(70*2*pi*nbuf*T);
%0至0.05的抽样值

figure(3)

plot(nbuf,xnbuf/max(abs(xnbuf)), 'p',nbuf,dS(1:(length(nbuf))),
'ro') %原模拟信号幅度归一化

legend('原抽样值 ','编码解码后的值 ') 

title('原抽样值与解码后的值比较图(均归一化) ')

在这里插入图片描述
由上图知,除个别数值外,解码所得值与原抽样信号基本相同。而且,误差较大的点,其幅度也较大,这也与预期相符。因为 13 折线法不同段落的量化间隔是不同的,幅度越大,量化间隔也就越大,误差越大;幅度较小值时,误差较小。这也是 13 折线法的优点:对于不同的信号强度可以保持信号量躁比恒定。

2019-01-12 12:32:53 weixin_44041884 阅读数 8807

基于 MATLAB 的 PCM 编码解码实现

关于 PCM 的原理在此不过多解释,代码使用的是 A 律 13 折线法。

抽样信号

抽样信号的 MATLAB 代码如下:

clear;
clc;
T=0.0005;
t=-0.01:T:0.01;
fs=2000;
sdt=1/fs;
t1=-0.01:sdt:0.01;
xt=cos(2*pi*30*t)+sin(2*pi*120*t);
st=cos(2*pi*30*t1)+sin(2*pi*120*t1);
max = max(abs(st));

% 原始信号
figure;
subplot(2,1,1);plot(t,xt);title('原始信号');grid on;
subplot(2,1,2);stem(t1,st,'.');title('抽样信号');grid on;

绘制出来的图像如下图:

抽样信号图片

选择这个抽样频率是因为绘制出来的图像好看一点。可以看到上述代码中有一个 max 变量,这个后面要用到。

PCM 编码

分别得到符号位,段位码,段内码即可。代码如下:

function code=PCMcoding(S)
    z=sign(S);                                %判断S的正负
    MaxS=max(abs(S));                         %求S的最大值 
    S=abs(S/MaxS);                            %归一化
    Q=2048*S;                                 %量化
    code=zeros(length(S),8);                  %代码存储矩阵(全零)
    
    % 段落码判断程序
    for i=1:length(S)
        if (Q(i)>=128)&&(Q(i)<=2048)
            code(i,2)=1;            %在第五段与第八段之间,段位码第一位都为"1"
        end
        if (Q(i)>32)&&(Q(i)<128)||(Q(i)>=512)&&(Q(i)<=2048)
            code(i,3)=1;            %在第三四七八段内,段位码第二位为"1"
        end
        if (Q(i)>=16)&&(Q(i)<32)||(Q(i)>=64)&&(Q(i)<128)||(Q(i)>=256)&&(Q(i)<512)||(Q(i)>=1024)&&(Q(i)<=2048)
            code(i,4)=1;            %在二四六八段内,段位码第三位为"1"
        end
    end
    
    N=zeros(length(S));                              %段内码判断程序
    
    for i=1:length(S)
        N(i)=bin2dec(num2str(code(i,2:4)))+1;        %找到code位于第几段
    end
    
    a=[0,16,32,64,128,256,512,1024];                 %量化间隔
    b=[1,1,2,4,8,16,32,64];                          %除以16,得到每段的最小量化间隔
    for i=1:length(S)  
        q=ceil((Q(i)-a(N(i)))/b(N(i)));              %求出在段内的位置
        if q==0
            code(i,(5:8))=[0,0,0,0];                 %如果输入为零则输出"0"
        else k=num2str(dec2bin(q-1,4));              %编码段内码为二进制
            code(i,5)=str2num(k(1));
            code(i,6)=str2num(k(2));
            code(i,7)=str2num(k(3));
            code(i,8)=str2num(k(4));
        end
        if z(i)>0
            code(i,1)=1;
        elseif z(i)<0
            code(i,1)=0;
        end                                           %符号位的判断
    end
    code = reshape(code', 1, []);
end

将 PCM 编码抽象成了一个函数,这样方便在后来的过程中调用。注意到编码阶数后有一个重整矩阵维度的操作,这是因为刚编码完的数据是 a*8 的矩阵,为了后续操作方便要把它变成 1*8a 的矩阵。完成函数后绘图:

% PCM 编码
pcm_encode = PCMcoding(xt);

figure;
stairs(pcm_encode);
axis([0 20 -0.1 1.1]);
title('PCM 编码');
grid on;

这里选择 20 个码元来看看效果,因此将坐标设为 0 20 。绘出的图如下:
PCM编码信号

PCM 解码

注意到编码时有一个归一化的操作,因此信号幅度已经丢失,在解码时要将上述提到的 max 参数传入,以便解码恢复到原来的幅度。代码如下:

function s=PCMdecoding(encode, max)
    encode=(reshape(encode',8,length(encode)/8))';
    l=size(encode,1);
    a=[0,16,32,64,128,256,512,1024];
    b=[1 1 2 4 8 16 32 64];
    c=[0 1.5:15.5];
    for i=1:l
        x=encode(i,1);
        T=bin2dec(num2str(encode(i,(2:4))))+1;
        Y=bin2dec(num2str(encode(i,(5:8))));
        if Y==0
            k(i)=a(T)/2048;
        else
            k(i)=(a(T)+b(T)*c(Y))/2048;
        end
        if x==0
            s(i)=-k(i);
        else
            s(i)=k(i);
        end
    end
    s = s*max;
end

解码的时候有一个重塑矩阵形状的操作,同样为了解码方便。通常编码后还会使用信道编码等,因此编码时有一个重塑的操作,解码时又一个重塑操作。解码完成可视化一下:

% PCM 译码
pcm_decode = PCMdecoding(pcm_encode, max);

figure;
subplot(2,1,1);plot(t, pcm_decode);
title('PCM 译码');grid on;

subplot(2,1,2);plot(t,xt);
title('原始信号');grid on;

解码后的信号如下:

在这里插入图片描述

效果还行。

失真度分析

失真度分析代码如下:

% 计算失真度
da=0; 
for i=1:length(t)
    dc=(st(i)-pcm_decode(i))^2/length(t);
    da=da+dc;
end
fprintf('失真度是:%.6f\n',da);

在此保留了六位小数,输出为 失真度是:0.000174

2017-08-10 21:05:36 TS____4 阅读数 2539
最近做音频传输用到A律μ律编解码,记录一下遇到的问题。采用ALSA做音频输入输出,将音频数据编码后便于传输。
原始数据:8000hz  双声道  S16_LE(16位)  256kbps
编码数据:8000hz  单声道  8位 64kbps
主要任务是将16位(小端存储)数据编码为8位,然后再解码回去。这里编解码用的是网上找的函数,遇到的问题是编解码之后的音频播放效果惨不忍睹,最后找到问题是在“小端存储”上。
小端存储是将高字节数据放在高位地址上。例如:10 00转换为十进制是16。地址从左往右递增,10所在是低位地址,00所在是高位地址。
大端存储是将高字节数据放在低位地址上。例如:10 00转换为十进制是4096。

我在将小端存储的16位数据提取出来的时候,直接把它原封不动的赋给了一个short变量,结果导致高低位混乱,最终结果就是音频混乱。下面是具体程序:

 1.A律μ律编解码

static unsigned char ALawCompressTable[] =
{
    1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};
static short ALawDecompressTable[] =
{
    0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80,
    0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
    0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0,
    0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
    0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600,
    0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
    0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00,
    0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
    0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8,
    0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
    0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8,
    0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
    0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60,
    0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
    0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0,
    0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
    0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280,
    0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
    0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940,
    0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
    0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00,
    0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
    0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500,
    0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
    0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128,
    0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
    0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028,
    0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
    0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0,
    0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
    0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250,
    0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350,
};

static unsigned char MuLawCompressTable[] =
{
    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};

static short MuLawDecompressTable[] =
{
    0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84,
    0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
    0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84,
    0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
    0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804,
    0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
    0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444,
    0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
    0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64,
    0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
    0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74,
    0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
    0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC,
    0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
    0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0,
    0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
    0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C,
    0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
    0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C,
    0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
    0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC,
    0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
    0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC,
    0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
    0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C,
    0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
    0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C,
    0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
    0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
    0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
    0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
    0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
};

/* Alaw
* Linear Input Code Compressed Code
* ------------------------ ---------------
* 0000000wxyza   000wxyz
* 0000001wxyza   001wxyz
* 000001wxyzab   010wxyz
* 00001wxyzabc   011wxyz
* 0001wxyzabcd   100wxyz
* 001wxyzabcde   101wxyz
* 01wxyzabcdef   110wxyz
* 1wxyzabcdefg   111wxyz
*/

unsigned char LinearToAlawSample(short sample)
{
    int sign   = 0;
    int exponent = 0;
    int mantissa = 0;
    unsigned char compressedByte = 0;

    sign = ((~sample) >> 8) & 0x80;
    if (sign == 0)
    {
        sample = (short)-sample;
    }
    if (sample > 0x7F7B)
    {
        sample = 0x7F7B;
    }
    if (sample >= 0x100)
    {
        exponent = (int)ALawCompressTable[(sample >> 8) & 0x7F];
        mantissa = (sample >> (exponent + 3)) & 0x0F;
        compressedByte = (unsigned char)((exponent << 4) | mantissa);
    }
    else
    {
        compressedByte = (unsigned char)(sample >> 4);
    }
    compressedByte ^= (unsigned char)(sign ^ 0x55);
    return compressedByte;
}

short AlawToLinearSample(unsigned char sample)
{
    return ALawDecompressTable[sample];
}

/* ulaw
* Biased Linear Input Code Compressed Code
* ------------------------ ---------------
* 00000001wxyza   000wxyz
* 0000001wxyzab   001wxyz
* 000001wxyzabc   010wxyz
* 00001wxyzabcd   011wxyz
* 0001wxyzabcde   100wxyz
* 001wxyzabcdef   101wxyz
* 01wxyzabcdefg   110wxyz
* 1wxyzabcdefgh   111wxyz
*/


unsigned char LinearToMuLawSample(short sample)
{
    int cBias = 0x84;
    int cClip = 0x7F7B;
    int sign = (sample >> 8) & 0x80;

    if (sign != 0)
    {
        sample = (short)-sample;
    }
    if (sample > cClip)
    {
        sample = (short)cClip;
    }
    sample = (short)(sample + cBias);

    int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF];
    int mantissa = (sample >> (exponent + 3)) & 0x0F;
    int compressedByte = ~(sign | (exponent << 4) | mantissa);
    return (unsigned char)compressedByte;
}

short MuLawToLinearSample(unsigned char sample)
{
    return MuLawDecompressTable[sample];
}

2.编解码映射表初始化

//alaw_table[256][3]映射表 
//第一列:原始数据 -32767 ~ 32767
//第二列:编码数据 0 ~ 255
//第三列:解码数据 -32767 ~ 32767中的256个
short alaw_table[256][3]; 


//alaw_table_init生成A律坐标映射表
void alaw_table_init()
{
	short i, count, sample16;
	unsigned char sample8;
	
	count = 0;
	for(i=-32767; i<32767; i++)
	{
		sample8 = LinearToAlawSample(i);           //编码数据
		sample16 = AlawToLinearSample(sample8);	   //解码数据
		if(i==-32767)
		{
			alaw_table[0][0] = i;
			alaw_table[0][1] = sample8;
			alaw_table[0][2] = sample16;
		}
		else
		{
			if(sample8 != alaw_table[count][1])
			{
				count++;
				alaw_table[count][0] = i;
				alaw_table[count][1] = sample8;
				alaw_table[count][2] = sample16;
			}
		}
	}
}

3.用查表法实现编解码

//linear->alaw
unsigned char l2a(short val)
{
    int max=255;
    int i;
    for(i=0; i<max; i++)
    {
        if(val<alaw_table[0][0])
            return alaw_table[0][1];
        if(val>alaw_table[i][0] && val<=alaw_table[i+1][0])
            return alaw_table[i+1][1];
        if(val>alaw_table[255][0])
            return alaw_table[255][1];
    }
}
//alaw->linear
short a2l(unsigned char val)
{
    short i;
    for(i=0; i<256; i++)
        if(val == alaw_table[i][1])
            return alaw_table[i][2];
    return 0;
}

原始音频数据存储在*sendbuffer,16位小端存储,双声道交叉存储。提取出单声道数据进行转换,然后存入编码数据指针*sendbuffer_alaw用于传输。
short sample;
unsigned char result;

for(i=0; i<frames; i++)
{
	sample = *(sendbuffer+i*4+1);     //注意原始数据是小端存储
	sample <<= 8;
	sample +=  *(sendbuffer+i*4)  ;
	result = l2a(sample);
	*(sendbuffer_alaw+i) = result;
}

接收到的编码数据存于*recvbuffer_alaw,解码获得16位双声道数据存放于*recvbuffer。
short sample;
unsigned char result;
 for(i=0; i<frames; i++)
 {
	 result = *(recvbuffer_alaw+i);   
	 sample = a2l(result);
	 *(recvbuffer+i*4) = sample;
	 *(recvbuffer+i*4+1) = sample >> 8;
	 *(recvbuffer+i*4+2) = sample;
	 *(recvbuffer+i*4+3) = sample >> 8;
 }




G.711编解码原理

阅读数 8336

音频编解码原理

阅读数 21147

A率PCM编码

阅读数 3695