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 於杭州
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章