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

2024-03-16 00:58

本文主要是介绍g729源码分析-5-基音周期搜索(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前面两节讲完了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-基音周期搜索(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/813827

相关文章

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

慢sql提前分析预警和动态sql替换-Mybatis-SQL

《慢sql提前分析预警和动态sql替换-Mybatis-SQL》为防止慢SQL问题而开发的MyBatis组件,该组件能够在开发、测试阶段自动分析SQL语句,并在出现慢SQL问题时通过Ducc配置实现动... 目录背景解决思路开源方案调研设计方案详细设计使用方法1、引入依赖jar包2、配置组件XML3、核心配

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序