天天看點

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

    //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個幀會被認為非靜音,這個處理是避免元音段被不正确地截了

代碼片段

    if ( (Tm2 == 4) || (CodStat.SinDet < 0) )//lsc 濁音,要添加尾響

        VadStat.Aen += 2 ;

    else

        VadStat.Aen -- ;

    if ( VadStat.Aen > 6 )//lsc 尾響限制為6幀

        VadStat.Aen = 6 ;

    if ( VadStat.Aen < 0 )

        VadStat.Aen = 0 ;

與網絡上流行的算法不同的是,g723做了濾波,對靜音的判斷是基于激勵的能量

逆向濾波代碼片段如下:

    //lsc 逆向濾波

    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系數的生成,這裡,隻需要知道它是一個濾波器,

可以得到殘差信号即可

噪聲能量估值

    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的計算,繞得很

對噪聲能量做個限制

    //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的編碼模型裡,就認為是由一個随機信号激勵形成的

    if ( !VadStat.Aen ) {//lsc 如果是清音

        VadStat.Nlev = L_add( VadStat.Nlev, L_shr( VadStat.Nlev, 5 ) ) ;

    }

    else {//lsc 如果是濁音,或者處于尾響階段

        VadStat.Nlev = L_sub( VadStat.Nlev, L_shr( VadStat.Nlev,11 ) ) ;

    }

近一步限制噪聲能量的取值範圍 最小128 最大16383

    VadStat.Penr = Acc1 ;

    if ( VadStat.Nlev < 0x00000080L )

        VadStat.Nlev = 0x00000080L ;

    if ( VadStat.Nlev > 0x0001ffffL )

        VadStat.Nlev = 0x0001ffffL ;

接來計算門限系數,這裡涉及到用折線段來模拟指數函數,

知道是這麼處理的就可以了.

最後将噪聲能量估值與門限系數相乘,然後與激勵能量比較,來判斷是否為靜音幀

代碼片段如下:

    //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分之一的因子了

    //lsc 0表示為靜音 筆者認為文檔中的描述有誤,應該是thr*nlev與能量比較,至少從代碼看,是這樣,糾結啊

    if ( Acc0 > Acc1 )

        VadState = 0 ;

尾響處理,如果是元音,後面的6幀認為非靜音,比較簡單,筆者就不列出代碼了

總結:

g723的能量檢測仍然基于能量檢測的

                                                      林紹川

                                                      2012.1.5 于杭州