g723源码详细分析-12-更新内存与打包等

完成声道参数与激励编码后,需要将这些信息打包成帧,
并且要更新相应的内存,如保存当前激励成为之后编码的自适应激励码本等

在Find_Fcbk之后,即完成了所有编码,接下来更新内存状态
Decod_Acbk里解码自适应激励,这个函数在编码自适应激励时描述过了,即根据基音周期
从历史激励里,相邻的五个激励加权形成自适应激励

自适应激励与修正激励(低速率下为固定码本激励,高速率下为多脉冲激励)相加,形成历史激励
代码片段如下:

//固定与自适应相加,形成历史码本激励
for ( j = 0 ; j < SubFrLen ; j ++ ) {
Dpnt[j] = shl( Dpnt[j], (Word16) 1 ) ;//lsc 固定码本/多脉冲激励是在原始信号缩小两倍的基础上计算出来的,这时要还原回去,
Dpnt[j] = add( Dpnt[j], ImpResp[j] ) ;//lsc 从这里看出,自适应激励是原值
CodStat.PrevExc[PitchMax-SubFrLen+j] = Dpnt[j] ;
}

Update_Err:
这个函数是用于调节自适应激励的增益使用的,这里回顾一样在搜索自适应激励时被调用的
Test_Err,它用于决定搜索增益码本的范围.
总的来说,当增益处于低水平时(即搜索范围较小时),它的作用是将搜索范围调大.
在增益处于高水平时(即搜索范围较小时),它的作用是将搜索范围调小

Update_Err首先是根据自适应码本增益,计算出误差,
误差总共有5个,每30个采样值块,对应至一个误差(由于基音周期最长为147,所有有5个误差)
误差被记录在全局变量:CodStat.Err,它是根据时间降序排列的,即CodStat.Err[0]记录的误差
离当前最近, CodStat.Err[4]则最远

举个例子:如果基音周期为60,会根据CodStat.Err[2] CodStat.Err[1]的值来生成新的误差估值
生成的依据是历史误差值,乘以误差增益码本里的值(取值范围大约从0.125到2.2不等),再加4
可以看出,增益小,误差也会越来越小,
而增益大,误差也越来越大

代码片段如下
void Update_Err(
Word16 Olp, Word16 AcLg, Word16 AcGn
)
{//lsc 对这段的理解,这段是为了让自适应码本的搜索次数限制在一定的次数之内
//通过对固定码本的增益,形成一个历史误差数组(5段),根据基音周期,分别落在这五段之内,
//5段的数据是按时间排序的,0表示最近的时间,4表示最远的时间,计算的依据则是根据当前的基音周期,从相应的段中(除30),取数据进行估值
//在进行自适应码本搜索的时候,则根据基音周期,从相应的段中取出最大的误差增益,来决定自适应码本增益搜索的次数
//(次数越多,根据表,我们知道增益会越大,这样就起到降低固定码本的目标向量的作用)
Word16 *ptr_tab;
Word16 i, iz, temp1, temp2;
Word16 Lag;
Word32 Worst1, Worst0, L_temp;
Word16 beta;

Lag = Olp - (Word16)Pstep + AcLg;

/* Select Quantization tables */
i = 0 ;
ptr_tab = tabgain85;
if ( WrkRate == Rate63 ) {
if ( Olp >= (Word16) (SubFrLen-2) ) ptr_tab = tabgain170;
}
else {
ptr_tab = tabgain170;
}
beta = ptr_tab[(int)AcGn]; /* beta = gain * 8192 */


if(Lag <= (SubFrLen/2)) {
Worst0 = L_mls(CodStat.Err[0], beta);
Worst0 = L_shl(Worst0, 2);
Worst0 = L_add(Err0, Worst0);
Worst1 = Worst0;
}

else {
iz = mult(Lag, 1092); /* Lag / 30 */ //lsc 32767/30 约等于1092 --- mult乘法的结果是缩小2*15次方的,所以iz还是一个很小的值
temp1 = add(iz, 1);
temp2 = sub(shl(temp1, 5), shl(temp1, 1)); /* 30 (iz+1) */ //lsc这里相当于判断 120/30这种情况
if(temp2 != Lag) {

if(iz == 1) {//lsc这里零的情况
Worst0 = L_mls(CodStat.Err[0], beta);
Worst0 = L_shl(Worst0, 2);
Worst0 = L_add(Err0, Worst0);//lsc Err0 shr2之后直接是0
Worst1 = L_mls(CodStat.Err[1], beta);
Worst1 = L_shl(Worst1, 2);
Worst1 = L_add(Err0, Worst1);
if(Worst0 > Worst1) Worst1 = Worst0;
else Worst0 = Worst1;
}

else {//lsc这是非零的情况
Worst0 = L_mls(CodStat.Err[iz-2], beta);
Worst0 = L_shl(Worst0, 2);
Worst0 = L_add(Err0, Worst0);
L_temp = L_mls(CodStat.Err[iz-1], beta);
L_temp = L_shl(L_temp, 2);
L_temp = L_add(Err0, L_temp);
if(L_temp > Worst0) Worst0 = L_temp;
Worst1 = L_mls(CodStat.Err[iz], beta);
Worst1 = L_shl(Worst1, 2);
Worst1 = L_add(Err0, Worst1);
if(L_temp > Worst1) Worst1 = L_temp;
}
}
else { /* Lag % SubFrLen = 0 */ //lsc这大概就120/30这种情况
Worst0 = L_mls(CodStat.Err[iz-1], beta);
Worst0 = L_shl(Worst0, 2);
Worst0 = L_add(Err0, Worst0);
Worst1 = L_mls(CodStat.Err[iz], beta);
Worst1 = L_shl(Worst1, 2);
Worst1 = L_add(Err0, Worst1);
}
}

for(i=4; i>=2; i--) {
CodStat.Err[i] = CodStat.Err[i-2];
}
CodStat.Err[0] = Worst0;
CodStat.Err[1] = Worst1;

return;
}

Word16 Test_Err(
Word16 Lag1, Word16 Lag2
)
{

int i, i1, i2;
Word16 zone1, zone2;
Word32 Acc, Err_max;
Word16 iTest;

i2 = Lag2 + ClPitchOrd/2;
zone2 = mult( (Word16) i2, (Word16) 1092);//lsc 这里的意思就是除30 i2倒退一个基音周期,与i1为限,正好包含了"前一个波形"帧

i1 = - SubFrLen + 1 + Lag1 - ClPitchOrd/2;
if(i1 <= 0) i1 = 1;
zone1 = mult( (Word16) i1, (Word16) 1092);

//lsc 起始点,在历史的误差数组中取出误差最大的那个
Err_max = -1L;
for(i=zone2; i>=zone1; i--) {
Acc = L_sub(CodStat.Err[i], Err_max);
if(Acc > 0L) {
Err_max = CodStat.Err[i];
}
}
Acc = L_sub(Err_max, ThreshErr);
if((Acc > 0L) || (CodStat.SinDet < 0 ) ) {//lsc 高于某个阀值,调整为0,说明增益一直大于1,误差一直维持在较大的水平
iTest = 0;
}
else {//lsc 误差越大(增益大于1),搜索的范围越小,误差越小(增益小于1),搜索的范围越大,这样会起到调节增益的作用
Acc = L_negate(Acc);
Acc = L_shr(Acc, DEC);
iTest = extract_l(Acc);
}

return(iTest);
}

最后是更新振铃减法里相应的值,用于下一个子帧做扣减零输入响应时使用,
代码基本是把公式翻译成c代码,不详细分析了

最后打包操作,将量化的lsp系数(笔者记忆中这应该是一个索引),自适应码本以及增益

两种速率下面的修正码本以及增益打成一个包,相应帧格式itu文档里的描述,这里不重复了

有个细节稍微说一下,即多脉冲位置的打包问题,
笔者简要地说明如下:
注意表格CombinatorialTable的特点
即 98280 + 20475 恰好等于118755
意味着118755等于第二排从20475 ... 1所有的值的
98280等于第二排 17550 ... 1所有值的和,
可以自行验证,编码时做加法,解码时做减法,可以根据所得结果是否大于0来判断对应的位置有无脉冲
118755 , 23751 3654 406 29 1
98280 , 20475 3276 378 28 1
80730 , 17550 2925 351 27 1
65780 , 14950 2600 325 26 1
53130 , 12650 2300 300 25 1
42504 , 10626 2024 276 24 1
33649 , 8855 1771 253 23 1
26334 , 7315 1540 231 22 1
20349 , 5985 1330 210 21 1
15504 , 4845 1140 190 20 1
11628 , 3876 969 171 19 1
8568 , 3060 816 153 18 1
6188 , 2380 680 136 17 1
4368 , 1820 560 120 16 1
3003 , 1365 455 105 15 1
2002 , 1001 364 91 14 1
1287 , 715 286 78 13 1
792 , 495 220 66 12 1
462 , 330 165 55 11 1
252 , 210 120 45 10 1
126 , 126 84 36 9 1
56 , 70 56 28 8 1
21 , 35 35 21 7 1
6 , 15 20 15 6 1
1 , 5 10 10 5 1
1 4 6 4 1
1 3 3 1
1 2 1
1 1
1



关于解码,静音压缩以及舒适背景音生成问题,笔者将在之后的章节继续分析.


林绍川
2011.10.09 于杭州


你可能感兴趣的:(内存)