写点什么

开源编解码器 SOLO 源码解读(一):带宽扩展

  • 2020-02-26
  • 本文字数:3965 字

    阅读完需:约 13 分钟

开源编解码器 SOLO 源码解读(一):带宽扩展

声网 Aogra 自研的编解码器 SOLO,已向所有音视频、WebRTC 开发者开源。本系列源码解读将讲解底层核心技术,并分享如何集成到自己的 WebRTC 应用中。本文为此系列的第一篇。本文作者:赵晓涵,声网 Agora 音频算法工程师。


SOLO 在 Silk 的基础上扩展了带宽扩展模块,用来分别处理低频信息(0-8kHz 采样部分)和高频信息(8-16kHz 采样部分),在编码端,两者使用两套耦合的分析编码系统进行码流生成。在解码端,利用低频信号和高频信息,SOLO 可以解码出宽带信号。SOLO 使用带宽扩展主要有两个原因,首先,带宽扩展可以让更多的码率分配到更重要的低频部分,提升编码效率;第二个原因是带宽扩展可以减少进入到信号分析模块的采样点数,从而减少信号分析部分的复杂度(之前需要分析全部的信号,现在只需要分析低频部分)。在减少了原有复杂度的前提下,SOLO 才能够在低频部分额外增加较多计算以选取最佳的多描述编码状态(详细内容可见下一篇源码解析),让编解码音质达到预期。

编码端

SOLO 编码端的大部分操作都是在下述函数中完成的:


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(constspx_word16_t*xx,                       /* I   Input signal              */constspx_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 代码解析里进行详细解读。


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 中进行:


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 系数:


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 来表示:


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(constspx_word16_t*x1,                       /* I   Low band signal           */constspx_word16_t*x2,                       /* I   High band signal          */constspx_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)
复制代码


至此,解码端就完成了将窄带信号扩展成宽带信号的操作。


本文转载自 声网 Agora 公众号。


原文链接:https://mp.weixin.qq.com/s/maJjXYCzmcqyXWToIB1N_g


2020-02-26 15:46555

评论

发布
暂无评论
发现更多内容

成都大运会“数智竞技邀请赛”启动 开悟平台为全球青年提供竞技舞台

科技热闻

教你两分钟做出一个精美好用的404页面

源字节1号

前端开发 后端开发 网页开发

在线JSON转HTML,TABLE表格工具

入门小站

工具

中国企业的测试之道被美国学习了?

博文视点Broadview

即学即会 Serverless 系列:初识 Serverless 架构

Serverless Devs

云计算 阿里云 Serverless 架构

API接口知识小结

源字节1号

程序员 有趣的技术知识

Redis面试题:基本数据类型与底层存储结构

Linux服务器开发

redis 面试题 Linux服务器开发 Linux后台开发 BAT面试题

聊聊数仓中TPCD-DS&TPC-H与查询性能的那些事儿

华为云开发者联盟

编辑器 GaussDB(DWS) TPCD-DS TPC-H 查询性能

深度揭秘阿里云 Serverless Kubernetes

Serverless Devs

Linux之uniq命令

入门小站

Linux

运维审计系统是堡垒机么?跟堡垒机有啥区别?

行云管家

运维 堡垒机 运维审计系统

安全大讲堂 | 孙朝晖:全量数据是一切网络安全分析的起点

腾讯安全云鼎实验室

数据分析 网络安全 数据安全 安全大讲堂

信创云管理平台,头部券商解决异构云资源管理的关键

BoCloud博云

信创 云管平台

OceanBase 源码解读(八):事务日志的提交和回放

OceanBase 数据库

oceanbase 源码解读

企业知识管理的措施

小炮

知识管理

ironSource 推出全球首个跨渠道应用营销平台 ironSource Luna

科技热闻

大咖说|试衣到家 CEO:我们卖的不是衣服,是服务

大咖说

阿里巴巴 科技 时尚产业 试衣到家

3. 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识bombardierdate

MASA技术团队

C# .net 测试 压测 测试工具

如何进行高效的版本管理,版本管理的方法

阿里云云效

云计算 阿里云 项目管理 云原生 版本管理

如何使用Java AWT 创建一个简易计算器

华为云开发者联盟

Java 计算器 GUI AWT 图形

集结创新力量,2022航天宏图&华为云杯PIE软件开发者大赛正式开启

科技热闻

隐私安全的必答题,网易云信如何解?

网易云信

隐私安全

如何实现24小时客户服务

小炮

客户服务

《Mybatis 手撸专栏》第1章:开篇介绍,我要带你撸 Mybatis 啦!

小傅哥

小傅哥 mybatis 手写Mybatis

小程序多端引流新思路:App公域流量挖掘

Speedoooo

APP开发 智慧终端 引流获客工具 引流获客系统

小程序容器轻松打造轻应用生态平台

Speedoooo

APP开发 小程序容器 轻应用 快应用 超级app

VuePress 博客之 SEO 优化(五)添加 JSON-LD 数据

冴羽

Vue 前端 vuepress SEO 博客搭建

开源编解码器 SOLO 源码解读(一):带宽扩展_开源_声网_InfoQ精选文章