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

相关文章

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

java 恺撒加密/解密实现原理(附带源码)

《java恺撒加密/解密实现原理(附带源码)》本文介绍Java实现恺撒加密与解密,通过固定位移量对字母进行循环替换,保留大小写及非字母字符,由于其实现简单、易于理解,恺撒加密常被用作学习加密算法的入... 目录Java 恺撒加密/解密实现1. 项目背景与介绍2. 相关知识2.1 恺撒加密算法原理2.2 Ja

Nginx屏蔽服务器名称与版本信息方式(源码级修改)

《Nginx屏蔽服务器名称与版本信息方式(源码级修改)》本文详解如何通过源码修改Nginx1.25.4,移除Server响应头中的服务类型和版本信息,以增强安全性,需重新配置、编译、安装,升级时需重复... 目录一、背景与目的二、适用版本三、操作步骤修改源码文件四、后续操作提示五、注意事项六、总结一、背景与

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码