開源編解碼器 SOLO 源碼解讀(一):帶寬擴展

聲網Aogra 開源了自研的 SOLO 編解碼器,面向全部音視頻、WebRTC 開發者。本系列源碼解讀將講解底層核心技術,並分享如何集成到本身的 WebRTC 應用中。本文爲第一篇。

做者:趙曉涵,聲網Agora 音頻算法工程師算法

SOLO 在 Silk 的基礎上擴展了帶寬擴展模塊,用來分別處理低頻信息(0-8kHz 採樣部分)和高頻信息(8-16kHz 採樣部分),在編碼端,二者使用兩套耦合的分析編碼系統進行碼流生成。在解碼端,利用低頻信號和高頻信息,SOLO 能夠解碼出寬帶信號。SOLO 使用帶寬擴展主要有兩個緣由,首先,帶寬擴展可讓更多的碼率分配到更重要的低頻部分,提高編碼效率;第二個緣由是帶寬擴展能夠減小進入到信號分析模塊的採樣點數,從而減小信號分析部分的複雜度(以前須要分析所有的信號,如今只須要分析低頻部分)。在減小了原有複雜度的前提下,SOLO 纔可以在低頻部分額外增長較多計算以選取最佳的多描述編碼狀態(詳細內容可見下一篇源碼解析),讓編解碼音質達到預期。函數

編碼端

SOLO 編碼端的大部分操做都是在下述函數中完成的:ui

SKP_int32 AGR_Sate_encode_process(
    SATEEncCtl *sateCtl,                 /* I/O SATE Encoder state       */
    const SKP_int16  *vin,               /* I   input signal             */
    NovaBits   *bits,                    /* I   bitstream operator       */
    void       *skctrl, 
    void       *hbctrl,
    SKP_int16 *nBytesOut                 /* I   encoded bits             */
)

首先,輸入的16kHz 採樣率的語音幀會先進入到一個正交鏡像濾波器組(QMF)裏進行頻帶的劃分:編碼

void AGR_Sate_qmf_decomp(
    const spx_word16_t *xx,                       /* I   Input signal              */
    const spx_word16_t *aa,                       /* I   Qmf coefficients          */
    spx_word16_t *y1,                             /* O   Output low band signal    */
    spx_word16_t *y2,                             /* O   Output high band signal   */
    SKP_int32     N,                              /* I   frame size                */
    SKP_int32     M,                              /* I   Qmf order                 */
    spx_word16_t *mem,                            /* I/O Qmf state                 */
    SKP_int8     *stack
)

該函數的輸出的是兩個時域幀,分別包含低頻信息和高頻信息。低頻信息和高頻信息會在後續分別進行處理,其中,低頻信息會經過函數SKP_Silk_SDK_Encode進行分析和編碼,這部份內容咱們會在下一期 SOLO 代碼解析裏進行詳細解讀。code

SKP_int SKP_Silk_SDK_Encode( 
    void                              *encState,      /* I/O: State                    */
    const SKP_SILK_SDK_EncControlStruct  *encControl, /* I:   Control structure        */
    const SKP_int16                   *samplesIn,     /* I:   Input samples            */
    SKP_int                           nSamplesIn,     /* I:   Number of samples        */
    SKP_uint8                         *outData,       /* O:   Encoded output           */
    SKP_int16                         *nBytesOut      /* I/O: I: Max bytes O:out bytes */
)

高頻信息的編碼以線性濾波分析爲基礎,同時爲了減小碼率,部分依賴於低頻信號的殘差信息,所以在進行高頻信息編碼以前,須要經過下述函數提取低頻編碼信息中的殘差信息:視頻

SKP_int SKP_Silk_SDK_Get_Encoder_Residue( void *encState,SKP_int32 *r )

高頻信息的分析和編碼在函數 AGR_Bwe_encode_frame_FLP中進行:ip

SKP_int32 AGR_Bwe_encode_frame_FLP(
    AGR_Sate_HB_encoder_control_FLP *hbEncCtrl,
    AGR_Sate_encoder_hb_state_FLP *psHBEnc,
    NovaBits    *bits,                                 /* I   bitstream operator       */
    SKP_float   *high,
    SKP_int32   *residue,
    SKP_int16   *nBytesOut      /* I/O: Number of bytes in outData (input: Max bytes)  */
)

首先高頻信息經過 AGR_Sate_find_HB_LPC_FLP進行分析獲得自身的 8 階 LPC 係數,並將其轉化爲編碼偏差較小的 LSP 係數:ci

SKP_int32 AGR_Sate_find_HB_LPC_FLP(
    AGR_Sate_encoder_hb_state_FLP      *psEnc,         /* I/O  Encoder state FLP       */
    AGR_Sate_HB_encoder_control_FLP    *hbEncCtrl,     /* I/O  HB Encoder control FLP  */
    SKP_int32                       hb_subfr_length,   /* I    subframe length         */
    SKP_int32                       hb_lpc_order,      /* I    high band lpc order     */
    SKP_int32                       first              /* I                            */
)

隨後經過 AGR_Sate_lsp_quant_highband進行雙碼本量化開發

SKP_int32 AGR_Sate_lsp_quant_highband(
    SKP_float *lsp,                                     /* I/O  lsp coefficients       */
    SKP_int32 order                                     /* I    lpc order              */
)

量化後,編碼器會將 LSP 係數轉化爲 1 個 index 來表示:get

idx1 = lsp_weight_quant(qlsp, quant_weight1, AGR_Sate_highband_lsp_cdbk1, HB_LSP_CB1, order);
idx2 = lsp_weight_quant(qlsp, quant_weight2, AGR_Sate_highband_lsp_cdbk2, HB_LSP_CB2, order);
idx = (idx2<<8)+idx1;

隨後,該幀被分爲 4 個子幀,計算各個子幀的殘差信號,並計算其對應窄帶殘差信號子幀的增益,共計4個,使用單碼本量化。量化後的 LSP index 和 gain 使用下述函數寫入獨立碼流。

void AGR_Sate_bits_pack(NovaBits *bits, int data, int nbBits)

其中,LSP index 使用 12 bits 編碼,每一個子幀 gain 使用 5 bits 編碼,因此高頻信息的碼流共計 12+4*5=32 bits,即 4 bytes,該段碼流位於窄帶碼流以後,和窄帶碼流中的第二組多描述碼流綁定在一塊兒組包。

解碼端

解碼器能夠看作編碼器的鏡像,解碼器收到碼流後,首先會經過下述函數解碼獲得 0-8kHz 採樣率的低頻信息,這部分也會在下一期 SOLO 代碼解析裏進行詳細解讀。

SKP_int SKP_Silk_SDK_Decode(
    void*                               decState,       /* I/O: State                  */
    SKP_SILK_SDK_DecControlStruct*      decControl,     /* I/O: Control structure      */
    SKP_int                             lostFlag,       /* I:   0: no loss, 1 loss     */
    const SKP_uint8                     *inData,        /* I:   Encoded input vector   */
    const SKP_int16                     nBytesIn[],     /* I:   Number of input Bytes  */
    SKP_int16                           *samplesOut,    /* O:   Decoded output         */
    SKP_int16                           *nSamplesOut    /* I/O: Number of samples      */
)

隨後,解碼器經過下述函數獲得低頻殘差信息以用來解碼 8-16kHz 的高頻信息。

SKP_int SKP_Silk_SDK_Get_Decoder_Residue(void *decState, SKP_int32 *r)

同時,解碼器會使用如下函數來進行高頻信息的解碼。

SKP_int32 AGR_Bwe_decode_frame_FLP(
    AGR_Sate_HB_decoder_control_FLP *hbDecCtrl,
    AGR_Sate_decoder_hb_state_FLP *psHBDec,
    NovaBits *bits,                                     /* I    bitstream operator     */
    SKP_float *OutHigh,
    SKP_int32 *residue_Q10,
    SKP_int32 lostflag                                  /* I    lost falg              */
    )

該函數內的處理總體上能夠分紅兩種 case,第一種是沒有正常接收到包含高頻信息的多描述碼流,這種狀況下會複用上一幀解碼出的 LSP index 和子幀增益;若是正常接收到了包含高頻信息的多描述碼流,則會從 4 bytes 的高頻信息中解碼、反量化出所需的 LPC 濾波器係數和 4 個子幀增益。

恢復高頻信號使用的殘差信號是乘上子幀增益後的低頻殘差信號。使用高頻殘差再加上高頻 LPC 係數,經過如下函數就能夠解碼獲得高頻信號。

void AGR_Sate_LPC_synthesizer(
    SKP_float    *output,            /* O    output signal           */
    SKP_float    *ipexc,             /* I    excitation signal       */
    SKP_float    *sLPC,              /* I/O  state vector            */
    SKP_float    *a_tmp,             /* I    filter coefficients     */
    SKP_int32    LPC_order,          /* I    filter order            */
    SKP_int32    subfr_length        /* I    signal length           */
)

隨後,低頻信息和高頻信息會進入到如下函數中,進行高低頻的合成,函數輸出的是 16kHz 採樣率的寬帶信號。

void AGR_Sate_qmf_synth(
    const spx_word16_t *x1,                       /* I   Low band signal           */
    const spx_word16_t *x2,                       /* I   High band signal          */
    const spx_word16_t *a,                        /* I   Qmf coefficients          */
    spx_word16_t *y,                              /* O   Synthesised signal        */
    SKP_int32     N,                              /* I   Signal size               */
    SKP_int32     M,                              /* I   Qmf order                 */
    spx_word16_t *mem1,                           /* I/O Qmf low band state        */
    spx_word16_t *mem2,                           /* I/O Qmf high band state       */
    SKP_int8     *stack
)

至此,解碼端就完成了將窄帶信號擴展成寬帶信號的操做。

若有疑問,能夠 點擊這裏,與咱們的工程師交流

相關文章
相關標籤/搜索