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于杭州
根据基音周期间,激励信号的相关性,来对激励信号做一个增强处理
相应的函数分别是 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于杭州