后置滤波处理


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


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