g723源码详细分析-15-静音检测

现在来分析静音检测
语音通话,基本上是一方听,一方说,采用静音检测可以起到节省一半带宽的作用

网络上有很多静音检测的代码,基本的思路,都是构造一个自适应的能量探测试,
低于阀值时,就认为出现静音

g723的思路基本与此相同
Comp_Vad 这个函数负责静音栽决,看代码吧,
这里笔者只分析算法,不再纠结定点数运算引起的数值缩放的问题了

g723的静音检测,检测了当前帧的180个样点(后60样点由于没有在当前帧里处理)
首先看到ScfTab这个静态数组
因为门限值的计算涉及到log,采用了类似711里的算法,用一系列折线段来近似指数函数
底为0.89 参考门限值公式10^(-0.05)约等于0.89

    static  Word16  ScfTab[11] = {//lsc 这里是一系列折线的斜率,用于计算近似的指数函数 注意:因为采用的是归一化的位移作自变量,所以这个表是递增的,把0.89的指数函数的第一象限"反"过来看(这里显得很绕,笔者也纠结了很久)
         9170 ,
         9170 ,
         9170 ,
         9170 ,//lsc 0.277 * 32768 = 9093 这个点基本吻合  0.277 为 0.89^11
        10289 ,//lsc 0.313995361328125=10,
        11544 ,//lsc 0.352294921875=9?
        12953 ,//lsc 0.395=8?
        14533 ,//lsc 0.4435=0.89^7 ?
        16306 ,//lsc 6 0.49761962890625=0.89^6
        18296 ,//lsc 5 0.558349609375=0.89^5
        20529 ,//lsc 0.626=0.89^4
    } ;
这个数组里的值为0.89^(n)扩大32768倍, n取值范围(4~11)

从四个子帧里找出基音周基最小的一个,存入Minp
    /* Find Minimum pitch period */ //lsc VadStat.Polp 是从Line.Olp中取来的,每一帧保存两个(因为基音周期是每120采样计算一次)
    Minp = PitchMax ;
    for ( i = 0 ; i < 4 ; i ++ ) {
        if ( Minp > VadStat.Polp[i] )
            Minp = VadStat.Polp[i] ;
    }

判断当前帧是否处理浊音段,依据为,如果所有子帧的基音周期都约为Minp的整数倍,
即认为当前帧是浊音段,代码片段
    //lsc itu的中文翻译有误,当tm2=4时,应认为是浊音,见itu的英文版,是voice
    Tm2 = 0 ;
    for ( i = 0 ; i < 4 ; i ++ ) {
        Tm1 = Minp ;
        for ( j = 0 ; j < 8 ; j ++ ) {
            Tm0 = sub( Tm1, VadStat.Polp[i] ) ;
            Tm0 = abs_s( Tm0 ) ;
            if ( Tm0 <= 3 )//lsc 如果在倍数附近,差值不大于3,为浊音加分,都在倍数附近,就是浊音了
                Tm2 ++ ;
            Tm1 = add( Tm1, Minp ) ;//lsc 用减法和加法,循环8次,代替除法
        }
    }

尾响处理,如果是浊音,之后的6个帧会被认为非静音,这个处理是避免元音段被不正确地截了
代码片段
    /* Update adaptation enable counter if not periodic and not sine */
    if ( (Tm2 == 4) || (CodStat.SinDet < 0) )//lsc 浊音,要添加尾响
        VadStat.Aen += 2 ;
    else
        VadStat.Aen -- ;

    /* Clip it */
    if ( VadStat.Aen > 6 )//lsc 尾响限制为6帧
        VadStat.Aen = 6 ;
    if ( VadStat.Aen < 0 )
        VadStat.Aen = 0 ;
        
与网络上流行的算法不同的是,g723做了滤波,对静音的判断是基于激励的能量
逆向滤波代码片段如下:
    //lsc 逆向滤波
    /* Inverse filter the data */
    Acc1 = 0L ;
    for ( i = SubFrLen ; i < Frame ; i ++ ) {

        Acc0 = L_mult( Dpnt[i], 0x2000 ) ;
        for ( j = 0 ; j < LpcOrder ; j ++ )
            Acc0 = L_msu( Acc0, Dpnt[i-j-1], VadStat.NLpc[j] ) ;
        Tm0 = round ( Acc0 ) ;
        Acc1 = L_mac( Acc1, Tm0, Tm0 ) ;//lsc 计算出能量
    }
其中的VadStat.NLpc这个数组,是在计算舒适背景音时,形成的一个平均滤波器,笔者将
在介绍舒适背景音时详细介绍这个lpc系数的生成,这里,只需要知道它是一个滤波器,
可以得到残差信号即可

噪声能量估值
    /* Scale the rezidual energy */
    Acc1 = L_mls( Acc1, (Word16) 2913 ) ;//lsc 这可能是除11.22(32768/2913=11.24),噪声能量估值  2913 * 11.22 = 32684(32767?)  10^1.05 = 11.220184543019634355910389464779
这里要注意,观察itu g723文档的vad章节的 A-5公式,它把10^1.05这个因子挪过来了,而把80这个因子移给了thrd的计算,绕得很

对噪声能量做个限制
    /* Clip noise level in any case */ //lsc 如果噪声能量太大,更新噪声的能量,加前一帧噪声能量估值的3/4,再取1/4,形成当前噪声能量
    if ( VadStat.Nlev > VadStat.Penr ) {
        Acc0 = L_sub( VadStat.Penr, L_shr( VadStat.Penr, 2 ) ) ;
        VadStat.Nlev = L_add( Acc0, L_shr( VadStat.Nlev, 2 ) ) ;
    }
    
根据当前帧是元音还是辅音,做一个适当放大处理,盖因辅音段,在g723的编码模型里,就认为是由一个随机信号激励形成的
    /* Update the noise level, if adaptation is enabled */
    if ( !VadStat.Aen ) {//lsc 如果是清音
        VadStat.Nlev = L_add( VadStat.Nlev, L_shr( VadStat.Nlev, 5 ) ) ;
    }
    /* Decay Nlev by small amount */
    else {//lsc 如果是浊音,或者处于尾响阶段
        VadStat.Nlev = L_sub( VadStat.Nlev, L_shr( VadStat.Nlev,11 ) ) ;
    }

近一步限制噪声能量的取值范围 最小128 最大16383
    /* Update previous energy */
    VadStat.Penr = Acc1 ;

    /* CLip Noise Level */
    if ( VadStat.Nlev < 0x00000080L )
        VadStat.Nlev = 0x00000080L ;
    if ( VadStat.Nlev > 0x0001ffffL )
        VadStat.Nlev = 0x0001ffffL ;

接来计算门限系数,这里涉及到用折线段来模拟指数函数,
知道是这么处理的就可以了.
最后将噪声能量估值与门限系数相乘,然后与激励能量比较,来判断是否为静音帧
代码片段如下:
    /* Compute the threshold */ //lsc 这里计算门限,本质而言,还是一个能量探测器
    Acc0 = L_shl( VadStat.Nlev, 13 ) ;
    Tm0 = norm_l( Acc0 ) ;
    Acc0 = L_shl( Acc0, Tm0 ) ;
    Acc0 &= 0x3f000000L ;//lsc 笔者认为,itu可能把10这个因子塞在这里了,把10拆成(10^0.005)^20,塞进指数里,做了缩放,纠结的运算啊
    Acc0 <<= 1 ;//lsc
    Tm1 = extract_h( Acc0 ) ;
    Acc0 = L_deposit_h( ScfTab[Tm0] ) ;
    Acc0 = L_mac( Acc0, Tm1, ScfTab[Tm0-1] ) ;//lsc 这两行计算斜率,插值
    Acc0 = L_msu( Acc0, Tm1, ScfTab[Tm0] ) ;
    Tm1 = extract_h( Acc0 ) ;
    Tm0 = extract_l( L_shr( VadStat.Nlev, 2 ) ) ;
    Acc0 = L_mult( Tm0, Tm1 ) ;
    Acc0 >>= 11 ;//lsc 这里隐含着扩大8位, 15+1-2=14 而只右移11位,则扩大了8倍,这样基本算是找到了文档的1/80分之一的因子了

    /* Compare with the threshold */ //lsc 0表示为静音 笔者认为文档中的描述有误,应该是thr*nlev与能量比较,至少从代码看,是这样,纠结啊
    if ( Acc0 > Acc1 )
        VadState = 0 ;

尾响处理,如果是元音,后面的6帧认为非静音,比较简单,笔者就不列出代码了

总结:
g723的能量检测仍然基于能量检测的

                                                      林绍川
                                                      2012.1.5 于杭州






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值