speex源码分析-4-固定码本激励

完成自适应激励后,激励的剩余成分可以认为是随机信号了
所以各种celpc编码器的第二级码本都是一个伪随机的码本


speex的也不例外.


speex首先会做一个类似归一化的操作.
具体地说,先把real_exc减去自适应解码激励,得到随机激励
      /* FIXME: Make sure this is save from overflows (so far so good) */
      for (i=0;i<st->subframeSize;i++)
         real_exc[i] = EXTRACT16(SUB32(EXTEND32(real_exc[i]), PSHR32(exc32[i],SIG_SHIFT-1)));//lsc 扣减自适应激励


ps:real_exc前面提到了,是语音信号通过量化的lpc滤波得到的残差信号(可以认为是最佳激励)

计算扣减自适应解码激励后的激励(我们以下称之为固定码本激励)
      ener = SHL32(EXTEND32(compute_rms16(real_exc, st->subframeSize)),SIG_SHIFT);//lsc 计算real_exc扣减自适应激励后的残差的平均能量开方


对ener进行量化
            qe = scal_quant(fine_gain, exc_gain_quant_scal3_bound, 8);//lsc fine_gain进行量化  ener/ol_gain
            speex_bits_pack(bits, qe, 3);
            ener=MULT16_32_Q14(exc_gain_quant_scal3[qe],ol_gain);//lsc q(ener/ol_gain) * olgain


搜索向量(扣除自适应激励贡献成份的语音信号)"归一化"
      /* Normalize innovation */
      signal_div(target, target, ener, st->subframeSize);//lsc 将target 相应地增小 1/ener(量化后的)


然后就进入了固定码本搜索函数
split_cb_search_shape_sign 而默认情况下会调用split_cb_search_shape_sign_N1.
(speex另一条固定码本激励编码应该是抽取多个码字的
这里只分析量化成一个码字的情况,多个码字的原理应该是类似的)


split_cb_search_shape_sign_N1函数主要参数的含义
target:搜索的目标向量 ak:lpc awk1:感知加权 awk2:感知加权 par:固定码本搜索的配置,如码本表等 p:阶 nsf:子帧长 r:单位冲激响应


compute_weighted_codebook:
这个函数,它实际上在计算固定码本中每个码字(5个伪随机数)与综合滤波器r的卷积
并且只计算5个点,因为 5个点的码字与20个点的冲激响应,卷积之后,实际贡献应该是25个点,但此处不需计算后面的20个
因为之后的搜索是每五个语音信号为一组,与卷积的结果进行匹配.
后面20个点的影响,会在每次循环之后再次更新,这样就极大的减少计算量.
此函数还会把卷积结果的五个点的能量计算出来,因为在后面进行搜索时,欧式距离的计算会用得着

完成了所有码字与冲激响应的卷积运算,接下来40个语音信号,分成8组,每组5个,根据欧式距离搜索最佳匹配的码字
总共循环8次,会就得到40个固定码本激励


根据欧式距离搜索最佳码字
         vq_nbest(x, resp2, subvect_size, shape_cb_size, E, 1, &best_index, &best_dist, stack);//lsc 计算欧式距离最小的那一组码字,目标向量初分成8组,每组5个,所以需要有8个循环


前面提到的卷积实际有25个样点,在这里才计算最佳码字剩余的20个样点,从之后的搜索向量中扣减         
         target_update(t+subvect_size*(i+1), g, r+q, nsf-subvect_size*(i+1));//lsc 这里相当于在做卷积,要扣减掉当前所查找出的码字对后继搜索向量的影响


此至,完成了固定码本激励的搜索


这一步为下一子帧的计算做准备,就是将当前解码的激励保存下来
         for (i=0;i<st->subframeSize;i++)
            exc[i] = EXTRACT16(SATURATE32(PSHR32(ADD32(SHL32(exc32[i],1),innov[i]),SIG_SHIFT),32767));//lsc 合成激励,更新历史激励码本,为之后的自适应码本搜索做准备


这是前面提到的更新零输入响应所需要的内存,为下一子帧的零输入响应计算做准备
      /* Final signal synthesis from excitation */
      iir_mem16(exc, interp_qlpc, sw, st->subframeSize, st->lpcSize, st->mem_sp, stack);//lsc  更新st->mem_sp 用于下一帧计算零输入响应


      /* Compute weighted signal again, from synthesized speech (not sure it's the right thing) */
      if (st->complexity!=0)
         filter_mem16(sw, bw_lpc1, bw_lpc2, sw, st->subframeSize, st->lpcSize, st->mem_sw, stack);//lsc 更新st->mem_sw 用于下一帧计算零输入响应


整个激励编码完成


保存当前的lsp,作为下一帧插值的依据
   /* Store the LSPs for interpolation in the next frame */
   if (st->submodeID>=1)//lsc 保存当前的lsp与量化的lsp,做为下一帧lsp插值依据
   {
      for (i=0;i<st->lpcSize;i++)
         st->old_lsp[i] = lsp[i];
      for (i=0;i<st->lpcSize;i++)
         st->old_qlsp[i] = qlsp[i];
   }


这一行就简单多了,40个样点,留给下一帧编码时处理
   SPEEX_COPY(st->winBuf, in+2*st->frameSize-st->windowSize, st->windowSize-st->frameSize);
   
至此,speex窄带编码部分分析完毕
之后笔者将会简单地分析解码,其实知道编码,读者基本可以推断出解码的过程了






                                                  林绍川 2012-11-02 于杭州
posted @ 2012-11-02 16:51  飞天大蟾蜍  阅读(48)  评论(0编辑  收藏  举报