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 于杭州


你可能感兴趣的:(g723源码详细分析-12-更新内存与打包等)