g729源码分析-5-基音周期搜索(下)

来源:互联网 发布:python mvvm 编辑:程序博客网 时间:2024/05/01 04:36
前面两节讲完了g729基音周期搜索的基本流程与依据
这里结合代码,进一步讲述这一过程


整数基音周期搜索比较简单
分成三段搜索,每段的权值不同,这在之前分析过了,不详述了


分数基音周期搜索是针对每个子帧进行的


每个子帧在开环基音周期附近,搜索闭环基音周期
首先构造出冲激响应 W(z)/A(z) 即感知加权滤器和综合滤波器
串联而成的系统
代码片段:
    /*---------------------------------------------------------------*
     * Find the weighted LPC coefficients for the weighting filter.  *
     *---------------------------------------------------------------*/


    Weight_Az(A, gamma1[i_gamma], M, Ap1);
    Weight_Az(A, gamma2[i_gamma], M, Ap2);
    i_gamma = add(i_gamma,1);


    /*---------------------------------------------------------------*
     * Compute impulse response, h1[], of weighted synthesis filter  *
     *---------------------------------------------------------------*/
//lsc 有单位冲激响应去激励这样的一个系统  (1/Aq(z)) * (Ap1(z)/Ap2(z)), 即,将量化后的预测系数A 与 感知加权滤波器做一个串联,得到一个冲激响应
    for (i = 0; i <= M; i++) {
        ai_zero[i] = Ap1[i];
    }
//lsc 这里得到完整的冲激响应,加入感知加权滤波,用未量化的Az系数生成感知加权,然后用经由量化的Az系数滤波
    Syn_filt(Aq, ai_zero, h1, L_SUBFR, zero, 0);
    Syn_filt(Ap2, h1, h1, L_SUBFR, zero, 0);


得到的冲激响应保存在h1数组当中


根据这个冲激响应,得到残差信号(激励信号),并推算出扣除零输入响应的目标语音信号xn
代码片段:
//lsc 这里计算目标向量,有可能做了去除零输入响应之类的操作,先用包含零输入响应的信号滤波(量化后的Az系数),得到残差信号
    Residu(Aq, &speech[i_subfr], &exc[i_subfr], L_SUBFR);   /* LPC residual */
//lsc 再用残差信号还原,就得到去除零输入响应的语音信息
    Syn_filt(Aq, &exc[i_subfr], error, L_SUBFR, mem_err, 0);
//lsc 对语音信号进行感知加权(采用的是未量化的Az系数)
    Residu(Ap1, error, xn, L_SUBFR);


    Syn_filt(Ap2, xn, xn, L_SUBFR, mem_w0, 0);    /* target signal xn[]*/


接下来是这个函数:
Pitch_fr3
它负责闭环基音周期搜索,如必要再进一步做更精细的分数基音延迟搜索
(当基音周基小于84就需要搜索分数延迟的基音周期,保证在基音周期较短的情况下,得到的自适应激励更精确一些)


调用:
//lsc 进行更精细的分数基音延迟搜索(每个子帧)
    T0 = Pitch_fr3(&exc[i_subfr], xn, h1, L_SUBFR, T0_min, T0_max,
                               i_subfr, &T0_frac);


TO_min TO_max 是开环基音周期TO-3 TO+6得到的,
Pitch_fr3 在这个范围内进一步搜索自相关最大的基音延迟


与开环用信号的自相关最大作为搜索依据不同,
闭环搜索的依据是自适应激励与h1卷积与目标信号的相关最大(这点与g723的五阶闭环基音周期搜索类似)


Norm_Corr就是计算
           39
           Σ   x[n] * yk[n]
          n=0
corr(k)= -----------------------
                39
         开方(  Σ  yk[n]*yk[n])
               n=0
            
其中 x[n]是目标语音信号  yk[n]是自适应激励与h1卷积  自然yk[n]应该有九组
而我们就是使找出corr[k]最大的那个yk[n]  对应的,也就是找到了最佳的自适应激励


Norm_Corr中有一小段代码,与g723的类似,为了节省计算量,
由于每个循环我们都在不断更新yk[n],而这个可以根据卷积的性质做一些化简,代码片段如下:
      k=sub(k,1);//lsc 这里往 t_max方向移动一格,然后再更新卷积,参照723的作法,节省一些运算量
      for (j = L_subfr-(Word16)1; j > 0; j--)
      {
        s = L_mult(exc[k], h[j]);
        s = L_shl(s, h_fac);             /* h is in Q(12-scaling) */
        s_excf[j] = add(extract_h(s), s_excf[j-1]);
      }
      s_excf[0] = shr(exc[k], scaling);
这个类似的技巧在分析g723时分析过了,这里不再详细说明,读者只需要注意一下即可


接下来说是找corr(k)最大的
代码片段如下:
//lsc 在基音周期附近,抽取历史激励源,与h1进行卷积,最后得到的结果与xn进行关联,找出关联最大的那个,认为是闭环基音搜索的结果
  for(i= t0_min+(Word16)1; i<=t0_max; i++)
  {
    if( sub(corr[i], max) >= 0)
    {
      max = corr[i];
      lag = i;
    }
  }
比较简单的代码,不多分析了.


然后就是分数基音延迟搜索了,因为搜索涉及到升抽样,
g729采用了一些取巧的做法,即直接对互相关的结果corr(k)这个序列进行升抽样.
Interpol_3 这个函数就是负责升抽样的,原来在上两节讲过了,就是将序列与sinc函数进行卷积.
应该注意 corr(k)这个序列因为要进行升抽样,而间隔要被拉开,所以对应的inter_3在循环跳变时的间隔为UP_SAMP
而本身sinc函数是关于y轴对称的,以及一些非因果系统的原因,卷积的过程会比较怪异,但它终究只是在计算卷积.
小技巧就不分析了


代码片段如下:


Word16 Interpol_3(      /* (o)  : interpolated value  */
  Word16 *x,            /* (i)  : input vector        */
  Word16 frac           /* (i)  : fraction            */
)
{//lsc 这个是升抽样, 通信信息与sinc函数卷积来达到升抽样的目的
//lsc 抽样的过程,在频域的表现,是将原始信号的频域按抽样频率复制多份,要还原信号,
//lsc 我们可以通过在频域上还原信号,即把抽样后的信号做一个低通滤波,去掉重复的高频成份,就达到还原的目的了
//lsc 这段代码就是在做这些事情,而频域上的矩形滤波器对应时域的信号就是sinc
//lsc 在时域上,只要将原始信号与sinc进行卷积,就完成了升抽样的工作
  Word16 i, k;
  Word16 *x1, *x2, *c1, *c2;
  Word32 s;


  if(frac < 0)
  {
    frac = add(frac, UP_SAMP);
    x--;
  }


  x1 = &x[0];
  x2 = &x[1];
  c1 = &inter_3[frac];
  c2 = &inter_3[sub(UP_SAMP,frac)];


  s = 0;
  for(i=0, k=0; i< L_INTER4; i++, k+=UP_SAMP)
  {
    s = L_mac(s, x1[-i], c1[k]);//lsc sinc是关于y轴对称的,所以 x1[-i] x2[i] 而k的跳跃数为UP_SAMP则是因为升抽样,每个x[i]之间的间隔变宽了3格
    s = L_mac(s, x2[i],  c2[k]);
  }




  return round(s);
}


得到corr(k)的升抽样后,处理就简单了,循环3次,最大的那个corr(k)就是分数基音延迟了
代码片段:


//lsc 为了计算分数延迟,这里可能做了插值,升抽样的处理
  max  = Interpol_3(&corr[lag], -2);
  frac = -2;


  for (i = -1; i <= 2; i++)
  {
    corr_int = Interpol_3(&corr[lag], i);
    if (sub(corr_int, max) > 0)
    {
      max = corr_int;
      frac = i;
    }
  }


扫尾工作,代码片段如下:


//lsc 基音周期参数打包
    index = Enc_lag3(T0, T0_frac, &T0_min, &T0_max,PIT_MIN,PIT_MAX,i_subfr);
//lsc 奇偶校验?
    *ana++ = index;
    if (i_subfr == 0) {
      *ana++ = Parity_Pitch(index);
    }
    
然后获取自适应激励,这里也涉及到升抽样,原理与Interpol_3一样,
代码片段如下:


//lsc 对最佳激励进行升抽样
    Pred_lt_3(&exc[i_subfr], T0, T0_frac, L_SUBFR);
//lsc 升抽样后的历史激励与冲激响应卷积
    Convolve(&exc[i_subfr], h1, y1, L_SUBFR);


Pred_lt_3 就是对历史激励源进行升抽样,然后取出一组激励作为自适应激励源,


可以看出g729对自适应激励作了化简,当基音周期较短时,引入分数基音延迟,这个对应g723的五阶加权闭环基音预测.
当基音周期较大时,则简化了处理,当取自适应激励时,我们仍然看到做了升抽样,这在一定程度上,仍然是对应g723
的五阶加权.


至此,g729编码部分分析得差不多了,剩下的是固定码本搜索,这一点与g723的固定码本搜索算法几乎是一模一样的.
g729与g723另一个不同的是,增益的估值与量化,这些笔者将在后继的章节分析.




                                                           林绍川
                                                           2012.4.17于杭州
                                                           
                                                           
                                                           
                                                           
                                                           
                                                           
                                                           





原创粉丝点击