g729源码分析-10-后置滤波处理(一)

后置滤波处理


后置滤波处理,大致有以下几个步骤


step 1: 长时预测
这段代码的主要目的是利用语音的长时相关来加强当前的语音信号
做法是在基音延迟附近搜索最佳基音延迟,利用历史解码出来的激励信号
对当前的激励信号做一个加权


step 2:共振峰感知加权,这个不用说了,加强共振峰处的能量


step 3:倾斜被偿,修正因为共振峰感知加权引入的频谱倾斜


step 4:增益处理


长时预测的代码比较繁琐,独立了出来分析


进入Post函数,前面的解码阶段,已经得到了重构的语音信号与lpc系数
以及基音周期


所以可以立刻再得到残差信号


    /* Compute weighted LPC coefficients */
    Weight_Az(coeff, GAMMA1_PST, M, apond1);
    Weight_Az(coeff, GAMMA2_PST, M, apond2);


    /* Compute A(gamma2) residual */
    Residu(apond2, signal_ptr, res2_ptr, L_SUBFR);//lsc 通过感知加权的分母部分子系统,就可以得到残差信号


得到了残差信号,注意res2_ptr这个指针的位置,前面有一段是用来保存之前帧的残差的,用来作当前帧的最佳基音延迟依据


pst_ltp
后置长时预测滤波器
先做归一化处理,
然后是搜索最佳基音延迟


函数声明:


static void search_del(
 Word16 t0,                /* input : pitch delay given by coder */
 Word16 *ptr_sig_in,       /* input : input signal (with delay line) */
 Word16 *ltpdel,           /* output: delay = *ltpdel - *phase / f_up */
 Word16 *phase,            /* output: phase */
 Word16 *num_gltp,         /* output: 16 bits numerator of LTP gain */
 Word16 *den_gltp,         /* output: 16 bits denominator of LTP gain */
 Word16 *sh_num_gltp,      /* output: justification for num_gltp */
 Word16 *sh_den_gltp,      /* output: justification for den_gltp */
 Word16 *y_up,             /* output: LT delayed signal if fract. delay */
 Word16 *off_yup           /* output: offset in y_up */
)
{//lsc 输出 长时预测增益 (num_gltp den_gltp sh_num_gltp sh_den_gltp:增益的分子,分母,以及归一化的位置位数)
//lsc ltpdel:最佳延迟 phase:最佳分数延迟 y_up:升抽样序列 off_yup:最佳延迟对应的升抽样在y_up里的偏移,注意最佳延迟是分两截的,前后各8个,off_yup=0:表示取前8个中的对应位置,反之则后8个(拗口的表述,但是这个意思)
//lsc 真是纠结的一系列参数啊...


参数的解释大致如上


这个函数的描述的过程大概是这样:
先搜索整数基音延迟
也就是在解码出来的基音周期附近搜索(大约搜索3个位置)
然后在最佳整数延迟附近再搜索分数延迟(这里又要处理升抽样,不如g723直接n阶加权来得干脆)


搜索的依据,就是自相关最大的那个
笔者对升抽样的过程作一个简要的注释说明,
整个过程中比较简单,显得比较晦涩的是升抽样滤波器的冲激响应数组:tab_hup_s,它就是sinc函数的抽样
这个数组中每个样点的顺序是经过调整的.笔者会在代码注释中说明,这个调整是如何进行的,


代码片段如下:




   /* initialization used only to suppress Microsoft Visual C++ warnings */
    i_max = (Word16)0;


    for(i=0; i<3; i++) {
        L_acc = 0L;
        for(n=0; n<L_SUBFR; n++) {
            L_acc = L_mac( L_acc, ptr_sig_in[n] , ptr_sig_past[n]);//lsc 计算自相关
        }
        if(L_acc < 0) {
            L_acc = 0L;
        }
        L_temp =L_sub(L_acc ,L_num_int);//lsc 在基音周基附近,寻找自相关最大的那个基音延迟
        if(L_temp > 0L) {
            L_num_int = L_acc;
            i_max = (Word16)i;
        }
        ptr_sig_past--;
    }


    if(L_num_int == 0) {
        *num_gltp = 0;
        *den_gltp = 1;
        *ltpdel = 0;
        *phase = 0;
        return;
    }


    /* Compute den for i_max */
    lambda = add(lambda, (Word16)i_max);//lsc 得到最佳基音延迟
    ptr_sig_past = ptr_sig_in - lambda;
    L_acc = 0L;
    for(i=0; i<L_SUBFR; i++) {
        temp = *ptr_sig_past++;
        L_acc = L_mac( L_acc, temp, temp);//lsc 该基音延迟对应的能量
    }
    if(L_acc == 0L) {
        *num_gltp = 0;
        *den_gltp = 1;
        *ltpdel = 0;
        *phase = 0;
        return;
    }
    L_den_int = L_acc;//lsc 保存最佳整数基音延迟的能量




    /***********************************/
    /* Select best phase around lambda */
    /***********************************/


    /* Compute y_up & denominators */
    /*******************************/


    ptr_y_up = y_up;
    L_den_max = L_den_int;
    ptr_L_den0 = L_den0;
    ptr_L_den1 = L_den1;
    ptr_h = tab_hup_s;//lsc 指向插值序列数组,是一个排列顺序经过调整的矩形滤波器的冲激响应
    temp = sub(lambda, LH_UP_SM1);
    ptr_sig_past0 = ptr_sig_in - temp;//lsc ptr_sig_past0位于最佳基音延迟后的一个位置


/*


//lsc 这个表格的数据组织顺序是经过调整的,笔者按每4格一跳,重新排列,发现这其实是一个sinc函数的抽样.
Word16 tab_hup_s[SIZ_TAB_HUP_S] = {
-188,  2873, 31650, -1597,  -484,  7041, 28469, -2147,  -933, 12266,
23705, -1992, -1492, 18050, 18050, -1492, -1992, 23705, 12266,  -933,
-2147, 28469,  7041,  -484, -1597, 31650,  2873,  -188 };


重新组织如下:


-188  -484  -933  -1492 -1992 -2147 -1597 (从-188开始,每跳4格,抽取,就可以得到这个序列)
2873  7041  12266 18050 23705 28469 31650 (从2873,开始,每跳4格抽取,以此类推,得到后面两行)
31650 28469 23705 18050 12266 7041  2873
-1597 -2147 -1992 -1492 -933  -484  -188


画一下这个序列,就可以知道,它实际上是一个sinc函数的抽样,
调整tab_hup_s之后,对照着读下面的循环代码,理解起来就方便了,下面的代码原理是与Pred_lt_3一样的,就是在计算升抽样
把子帧的40个样点,升抽样成320个样点,320个样点按分数延迟,组织在一个数组里头y_up
y_up里每连续的40个值,就表示一组分数延迟的采样


之后的计算就是照本宣科了,比较相关性,就可以得到最佳分数延迟


*/


    /* Loop on phase */
    for(phi=1; phi<F_UP_PST; phi++) {//lsc 计算最佳分数延迟


        /* Compute y_up for lambda+1 - phi/F_UP_PST */
        /* and lambda - phi/F_UP_PST                */


        ptr_sig_past = ptr_sig_past0;
        for(n = 0; n<=L_SUBFR; n++) {//lsc 这里处理插值,照理应循环40次,每次循环计算一个点(做一次残差与插值序列卷积),这里做了优化,减少了计算量
            ptr1 = ptr_sig_past++;
            L_acc = 0L;
            for(i=0; i<LH2_S; i++) {
                L_acc = L_mac(L_acc, ptr_h[i], ptr1[-i]);//lsc 计算卷积,ptr_h指向每8格一跳的插 按当前两点,过去两点(插值滤波器是非因果的)
            }
            ptr_y_up[n] = round(L_acc);//lsc 这里得到了残差的插值,总共320
        }


        /* compute den0 (lambda+1) and den1 (lambda) */


        /* part common to den0 and den1 */
        L_acc = 0L;
        for(n=1; n<L_SUBFR; n++) {
            L_acc = L_mac(L_acc, ptr_y_up[n] ,ptr_y_up[n]);//lsc 算出中间39个取样的总能量
        }
        L_temp0 = L_acc;        /* saved for den1 */


        /* den0 */
        L_acc = L_mac(L_acc, ptr_y_up[0] ,ptr_y_up[0]);//lsc 加前面一个点,就是 lambda - phi/F_UP_PST 的能量
        *ptr_L_den0 = L_acc;


        /* den1 */
        L_acc = L_mac(L_temp0, ptr_y_up[L_SUBFR] ,ptr_y_up[L_SUBFR]);//lsc 加后面一个点,就是 lambda+1 - phi/F_UP_PST 的能量
        *ptr_L_den1 = L_acc;


        if(sub(abs_s(ptr_y_up[0]),abs_s(ptr_y_up[L_SUBFR])) >0) {//lsc L_den_max 这段代码,哪个序列能量大,记录哪个,为了后继的计算归一化
            L_temp =L_sub(*ptr_L_den0 ,L_den_max );
            if(L_temp> 0L) {
                L_den_max = *ptr_L_den0;
            }
        }
        else {
            L_temp =L_sub(*ptr_L_den1 ,L_den_max );
            if(L_temp> 0L) {
                L_den_max = *ptr_L_den1;
            }
        }
        ptr_L_den0++;
        ptr_L_den1++;
        ptr_y_up += L_SUBFRP1;
        ptr_h += LH2_S;
    }


    if(L_den_max == 0) {
        *num_gltp = 0;
        *den_gltp = 1;
        *ltpdel = 0;
        *phase = 0;
        return;
    }




经过这一过程的处理,就得到了升抽样,
接来说就是对各个备选的升抽样序列进行相关性的大小比较
取相关性最大的那个


这些跟基音周期搜索是极为相似的,过程也比较简单,代码不多说明了


结束了search_del.
接下来的过程也就简单了,按照search_del输出参数,对残差信号进行相应的滤波(或者说是加权)


have fun!
                                       林绍川
                                       2012.6.5于杭州

你可能感兴趣的:(UP,lambda,Signal,output,initialization,delay)