您的位置:首页 > 其它

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

2012-06-05 21:02 169 查看
后置滤波处理

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

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
, ptr_sig_past
);//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
= 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
,ptr_y_up
);//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于杭州
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: