g723源码详细分析-19-基音后置滤波器

现在来分析g723基音后置滤波器

根据基音周期间,激励信号的相关性,来对激励信号做一个增强处理
相应的函数分别是 Comp_Lpf Filt_Lpf

Comp_Lpf 负责计算最佳的基音延后,
具体地说,是在基音周期延迟附近,找出最匹配当前子帧激励的60个连继激励e[i].
使用的方法自然找自相关最大的.找到之后,对e[i]进行加权,与当前子帧的激励相加,
这样就能达到增加音效的作用.查找分两个方向进行,前向与后向.
公式见itu 723文档3.6节 式42

现在来看代码,
一开始,就查找前向后向自相关最大的两个延迟
    /* Find both indices */ //lsc 根据基音周期,计算前向与后向相关,在基音周期附近找出最匹配的位置,可能会用它们来加强语音数据
    Bindx = Find_B( Buff, Olp, Sfc ) ;//lsc 这个返回负值
    Findx = Find_F( Buff, Olp, Sfc ) ;//lsc 这个返回正值

Find_B Find_F 这两个函数十分类似,只是查找的方向不同,一个前向,一个后向,
即,找出基音周期附近,自相关最大的那个延迟索引,如果最大的自相关为负值,忽略这个关联,
前向后向都为负值,则基音后置滤波就不进行了

Find_B函数:
Word16   Find_B( Word16 *Buff, Word16 Olp, Word16 Sfc )
{
    int   i,j   ;

    Word16   Indx = 0 ;

    Word32   Acc0,Acc1 ;

    if ( Olp > (Word16) (PitchMax-3) )
        Olp = (Word16) (PitchMax-3) ;

    Acc1 = (Word32) 0 ;

    for ( i = (int)Olp-3 ; i <= (int)Olp+3 ; i ++ ) {

        Acc0 = (Word32) 0 ;
        for ( j = 0 ; j < SubFrLen ; j ++ )
            Acc0 = L_mac( Acc0, Buff[PitchMax+(int)Sfc*SubFrLen+j],
                                    Buff[PitchMax+(int)Sfc*SubFrLen-i+j] ) ;//lsc 这里就是在计算自相关了
        if ( Acc0 > Acc1 ) {//lsc 只有大零的情况下,才会找出最大的自相关取引的索引Indx
            Acc1 = Acc0 ;
            Indx = -(Word16) i ;
        }
    }
    return Indx ;
}

接下来计算能量,用于计算增益
    /* Compute target energy */
    Acc0 = (Word32) 0 ;
    for ( j = 0 ; j < SubFrLen ; j ++ )
        Acc0 = L_mac( Acc0, Buff[PitchMax+(int)Sfc*SubFrLen+j],
                                    Buff[PitchMax+(int)Sfc*SubFrLen+j] ) ;
    Lcr[0] = Acc0 ;//lsc 当前子帧的能量

然后是计算基音延后自相关,以及基音延后的能量
前向和后向延后当中,选择的依据是以下两式的大小
   Cb^2/Eb  Cf^2/Ef
Cb:后向自相关 Eb:后向的能量
Cf:前向自相关 Ef:前向的能量

计算后向的自相关与能量的代码片段如下:
    if ( Bindx != (Word16) 0 ) {//lsc 计算backword与当前子帧互相关,以及backword的能量
        Acc0 = (Word32) 0 ;
        Acc1 = (Word32) 0 ;
        for ( j = 0 ; j < SubFrLen ; j ++ ) {
            Acc0 = L_mac( Acc0, Buff[PitchMax+(int)Sfc*SubFrLen+j],
                        Buff[PitchMax+(int)Sfc*SubFrLen+(int)Bindx+j] ) ;
            Acc1 = L_mac( Acc1, Buff[PitchMax+(int)Sfc*SubFrLen+(int)Bindx+j],
                        Buff[PitchMax+(int)Sfc*SubFrLen+(int)Bindx+j] ) ;
        }
        Lcr[1] = Acc0 ;//lsc 互相关
        Lcr[2] = Acc1 ;//lsc 能量
    }
    else {
        Lcr[1] = (Word32) 0 ;
        Lcr[2] = (Word32) 0 ;
    }
前向的计算与以类似,就不例举了
计算结果 当前子帧能量,前后向相关以及能量会被归一化存入Scr数组当中

比较,并计算基音后置滤波的参数,比较仍然是用交叉乘绕开除法
    if ( (Bindx != (Word16) 0) && ( Findx != (Word16) 0) ) {
        Exp = mult_r( Scr[1], Scr[1] ) ;
        Acc0 = L_mult( Exp, Scr[4] ) ;//lsc 这里利用交叉乘绕开除法
        Exp = mult_r( Scr[3], Scr[3] ) ;
        Acc1 = L_mult( Exp, Scr[2] ) ;//lsc 这里利用交叉乘绕开除法
        if ( Acc0 > Acc1 )//lsc 哪个的相关性大,用哪个,因为Ten肯定是一样的,所以这一项被忽略不计算了
            Pf = Get_Ind( Bindx, Scr[0], Scr[1], Scr[2] ) ;
        else
            Pf = Get_Ind( Findx, Scr[0], Scr[3], Scr[4] ) ;
    }

Get_Ind 这个函数负责计算基音后置滤波器参数
这里有个基音后置滤波器是否有效的判断,
依据基音延迟激励与当前子帧激励的相关性来判断的,itu里的定义比较绕,
直接看代码反而简单:

    /* Check valid gain */
    Acc0 = L_mult( Ten, Enr ) ;
    Acc0 = L_shr( Acc0, (Word16) 2 ) ;
    Acc1 = L_mult( Ccr, Ccr ) ;

    if ( Acc1 > Acc0 ) {//lsc 关联的平方,比能量的1/4乘积大,实际上就是类似相关系数的定义,说明两个序列有一点的相关性
    ......

即相关系数大于1/4即可认为两个序列是相关的
然后是计算基音延后的加权gb,以及gp(笔者认为gp是对加权后的一个调整值,防止因为加权,反而出现激励能量变小的情况)

代码片段如下:
        if ( Ccr >= Enr )//lsc 基音延后的加权值
            Pf.Gain = LpfConstTable[(int)WrkRate] ;
        else {
            Pf.Gain = div_s( Ccr, Enr ) ;
            Pf.Gain = mult( Pf.Gain, LpfConstTable[(int)WrkRate] ) ;
        }
        /* Compute scaling gain */ //lsc 这里是计算ppf` 即itu文档的 式 42
        Acc0 = L_deposit_h( Ten ) ;
        Acc0 = L_shr( Acc0, (Word16) 1 ) ;
        Acc0 = L_mac( Acc0, Ccr, Pf.Gain ) ;//lsc 平方展开,会有一个自相关的项,这里被加上
        Exp  = mult( Pf.Gain, Pf.Gain ) ;
        Acc1 = L_mult( Enr, Exp ) ;
        Acc1 = L_shr( Acc1, (Word16) 1 ) ;
        Acc0 = L_add( Acc0, Acc1 ) ;
        Exp = round( Acc0 ) ;

        Acc1 = L_deposit_h( Ten ) ;
        Acc0 = L_deposit_h( Exp ) ;
        Acc1 = L_shr( Acc1, (Word16) 1 ) ;

        if ( Acc1 >= Acc0 )
            Exp = (Word16) 0x7fff ;
        else
            Exp = div_l( Acc1, Exp ) ;

        Acc0 = L_deposit_h( Exp ) ;
        Pf.ScGn = Sqrt_lbc( Acc0 ) ;//这里得到了gp
    }
    else {//lsc 小于能量的1/4则忽略
        Pf.Gain = (Word16) 0 ;
        Pf.ScGn = (Word16) 0x7fff ;
    }

    Pf.Gain = mult( Pf.Gain, Pf.ScGn ) ;//gp需要单独保留,而gf/gb则是不需要的 见式42

将滤波器应用与激励信号,这个比较简单,基本是把itu文档3.6节的式42翻译成c代码
void  Filt_Lpf( Word16 *Tv, Word16 *Buff, PFDEF Pf, Word16 Sfc )
{
    int   i  ;

    Word32   Acc0 ;

    for ( i = 0 ; i < SubFrLen ; i ++ ) {//lsc 见文档的 式42,利用基音周期的相关性,来加强当前帧的激励
        Acc0 = L_mult( Buff[PitchMax+(int)Sfc*SubFrLen+i], Pf.ScGn ) ;
        Acc0 = L_mac( Acc0, Buff[PitchMax+(int)Sfc*SubFrLen+(int)Pf.Indx+i],
                                                                Pf.Gain ) ;
        Tv[(int)Sfc*SubFrLen+i] = round( Acc0 ) ;
    }

    return;
}


                                                    林绍川
                                                    2012.01.18于杭州

你可能感兴趣的:(c,文档,div)