前面两节讲完了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另一个不同的是,增益的估值与量化,这些笔者将在后继的章节分析.

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

  1. android6.0源码分析之Camera API2.0下的初始化流程分析

    1.Camera2初始化的应用层流程分析 Camera2的初始化流程与Camera1.0有所区别,本文将就Camera2的内置应用来分析Camera2.0的初始化过程.Camera2.0首先启动的是C ...

  2. android6.0源码分析之Camera API2.0下的Preview(预览)流程分析

    1.Camera2 preview的应用层流程分析 preview流程都是从startPreview开始的,所以来看startPreview方法的代码: <code class="hl ...

  3. 云客Drupal源码分析之插件系统(下)

    以下内容仅是一个预览,完整内容请见文尾: 至此本系列对插件的介绍全部完成,涵盖了系统插件的所有知识 全文目录(全文10476字): 实例化插件 插件映射Plugin mapping 插件上下文   具 ...

  4. react-redux源码分析及实现原型(下)

    上一次我们讲解了Provider.connect.selectorFactory.这次主要分析 connectAdvanced 这个核心API. react-redux源码分析及实现原型_上 conn ...

  5. spring源码分析02-spring生命周期源码解析

    spring生命周期流程图: 1.spring扫描 Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象 做准备,所以我们先明白Spring到底是怎 ...

  6. 手机自动化测试:appium源码分析之bootstrap六 (下)

    代码的步骤和swipe类似,而且最终调用的也是UiDevice.swipe方法,那么我们来看看到底区别在什么地方.首先它也分控件和坐标,分别分析: 控件 首先将控件的中心点作为起始坐标,然后根据提供的 ...

  7. android6.0源码分析之Camera API2.0下的Capture流程分析

    前面分析preview的时候,当预览成功后,会使能ShutterButton,即可以进行拍照,定位到ShutterButton的监听事件为onShutterButtonClick方法: <cod ...

  8. FFmpeg源码分析:视频滤镜介绍(下)

    FFmpeg在libavfilter模块提供音视频滤镜.所有的视频滤镜都注册在libavfilter/allfilters.c.我们也可以使用ffmpeg -filters命令行来查看当前支持的所有滤 ...

  9. Swoole 源码分析——Server模块之OpenSSL(下)

    前言 上一篇文章我们讲了 OpenSSL 的原理,接下来,我们来说说如何利用 openssl 第三方库进行开发,来为 tcp 层进行 SSL 隧道加密 OpenSSL 初始化 在 swoole 中,如 ...

最新文章

  1. 网络工程师中级2018 2019 2004-2017年真题+答案【结构介绍】
  2. 豆瓣评分 9.3 的 Java 巨著 | Bruce Eckel 重讲 Java 的编程思想
  3. zephyr 测试框架
  4. Python Django模板页面过滤器使用示例
  5. SQL server 2008 中的五个系统数据库详解
  6. Linux之find学习--详解
  7. 以正常使用来进行测试
  8. 电力-104规约实际测试3
  9. 《管理者必读12篇》:如何做好人员管理?
  10. 原理图端口符号_200 一步步开始学习制作PCBamp;PCBA--绘制原理图
  11. 卖服务器销售打广告语句子,卖灯销售广告语
  12. 使用键鼠网络共享用windows控制ubuntu,实现跨屏跨系统操作
  13. Python字符串、数列、字典及if、for语句
  14. 一个屌丝程序猿的人生(一百二十六)
  15. java水费管理系统课程设计_java水电费管理系统
  16. Elasticsearch之拼音搜索(十五)
  17. 谁不曾辜负或被辜负?
  18. springboot问题排解
  19. 微服务启动读取jar包失败:java: 读取D:\repository\org\aspectj\aspectjweaver\1.9.6\aspectjweaver-1.9.6.jar时出错; erro
  20. Revit二次开发(Revit2019+VS2017)-【NO.01】开发流程简述

热门文章

  1. Git 到底是个什么东西?
  2. python中fact()是什么意思_python中fact函数是什么及如何使用?
  3. 简易超声波雷达的arduino实现
  4. Codeforces Round #768 (Div. 1)(A-C)
  5. APP - 小米/红米电视开机广告屏蔽 hosts 列表分享,轻松解决开机广告
  6. ASP.NET动态网站开发学习实录(一)
  7. AR增强现实与AI人工智能融合的未来
  8. 五、数据CRUD操作
  9. APP (UniAPP) 微信支付回调的 HTTP_RAW_POST_DATA 报错情况 (wxpayv3)
  10. 你选择双休还是单休?