音频解码SBC_立体声音频编解码芯片

音频解码SBC_立体声音频编解码芯片SBC音频编解码算法浅析1.SBC算法简介SBC是subbandcode的缩写,也可称为子带编码在A2DP协议中,SBC算法是默认支持的蓝牙SBC算法是一种以中等比特率传递高质量音频数据的低计算复杂度的音频编码算法1.1算法基本框图SBC系统使用一个余弦调制的滤波器组,用来解析和同步。滤波器组可设定成4或8个子带子带信号的量化采用比特分配器和自适应脉冲编码器组调制可用的比特位数

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

SBC音频编解码算法浅析

1. SBC算法简介

  • SBC是sub band code的缩写,也可称为子带编码
  • 在A2DP协议中,SBC算法是默认支持的
  • 蓝牙SBC算法是一种以中等比特率传递高质量音频数据的低计算复杂度的音频编码算法

1.1 算法基本框图

  • SBC系统使用一个余弦调制的滤波器组,用来解析和同步。滤波器组可设定成4或8个子带
  • 子带信号的量化采用比特分配器和自适应脉冲编码器组调制
  • 可用的比特位数和编码器的块数都是可配置的
  • 编码后的数据由比特流打包器打包,以有线或无线方式传输
  • 解码是编码的逆过程
    这里写图片描述

1.1 算法所用参数

  • sampling_frequency:采样频率。常用的是16KHz,32KHz,44.1KHz和48KHz
  • channel mode:通道模式。可以是单声道,双声道,立体声和联合立体声
  • block length:块长度。可以是4,8,12,16
  • subbands:子带数量。可以是4或8
  • allocation method:分配方式。可以是SNR或loudness
  • bitpool:比特池。范围是2-250。此值越大,编码产生的数据越长

2 SBC算法实现

2.1 SBC编码算法实现

这里写图片描述

  • PCM格式的左右声道进入多相解析器,输出尺度因子和子带采样数据
  • 每个尺度因子分别对应一个子带
  • 量化后的子带采样数据需要进行打包,打包方式可以是分段或不分段
    这里写图片描述
  • 多相解析器的代码实现较为复杂,流程图如下[具体请看参考文献2的Appendix B]:
    这里写图片描述

2.2 SBC解码算法实现

这里写图片描述

  • 解码过程是编码过程的逆
  • 多相综合器的代码实现较为复杂,流程图如下[具体请看参考文献2的Appendix B]:
    这里写图片描述

3 SBC解码算法在某蓝牙主设备上的应用

3.1 帧格式

这里写图片描述

3.2 参数选择

  • sampling frequency:16
  • channel mode:单声道
  • number of subbands:8
  • number of channels:1
  • number of blocks:15
  • allocation method:SNR
  • bitpool:26

3.3 代码示例

void SBC_Decode(uint8_t * DataIn, FILE * fOutput)
{
#define SBC_SAMPLING_FREQ 16
#define SBC_CHANNEL_MODE 0
#define SBC_NUM_OF_SUBBANDS 8
#define SBC_NUM_OF_CHANNELS 1
#define SBC_NUM_OF_BLOCKS 15
#define SBC_ALLOC_METHOD 0
#define SBC_BITPOOL 26 
#define SBC_DECODED_BUFFER_SIZE (16*8)

    uint8_t blocks_per_packet = SBC_NUM_OF_BLOCKS;
    uint8_t num_bits = SBC_BITPOOL;
    const uint8_t * buf = (DataIn+1);//ignore CRC byte
    uint16_t len = SBC_GROUP_SIZE;
    uint16_t usDecodedBuffer[SBC_DECODED_BUFFER_SIZE];

    /* convenience */
    const uint8_t * end = buf + len;
#define left (end - buf)
    uint16_t * outBufPtr = usDecodedBuffer;

    /* workspace */
    static INSAMPLE samples[16][8]; /* We blow the stack if this is not static. */             
    ITER i, j, k;
    uint32_t scaleFactors[8]; //= {0x0f, 0x0c, 0x0b, 0x0b, 0x0a, 0x0a, 0x09, 0x09};
    int32_t bitneed[8];
    uint32_t bits[8];
    int32_t bitcount, slicecount, bitslice;
    uint8_t samplingRate, blocks, snr, numSubbands, bitpoolSz, bitpos = 0x80;
    int8_t max_bitneed = 0; 
#ifndef SPEED_OVER_ACCURACY
    int32_t levels[8];
#endif

#if (DEBUG_DECODING == 1)
    const uint8_t *start_buf = buf;
    pr_info("%s: blocks_per_packet = %d, num_bits = %d, buf = %p, len = %d\n",
        __func__, blocks_per_packet, num_bits, buf, len);
    for (i = 0; i < len; i++) {
        pr_info("buf[%d] = 0x%02x\n", i, buf[i]);
    }
#endif

    /* look into the frame header */
    if (left < SBC_GROUP_SIZE) goto out;/* too short a frame header */

    /* use Bemote specific constants */
    samplingRate = 0; /* always 16000 Hz */
    blocks = blocks_per_packet;
    snr = 0;
    numSubbands = SBC_NUM_OF_SUBBANDS;
    bitpoolSz = num_bits;               

    /* read scale factors */
    /* pr_info("sbc_decode: read scale factors, numSubbands = %d\n", numSubbands); */
    /**/
    for(i = 0; i < numSubbands; i++){

        if(bitpos == 0x80){

            scaleFactors[i] = (*buf) >> 4;
            bitpos = 0x08;
        }
        else{

            scaleFactors[i] = (*buf++) & 0x0F;
            bitpos = 0x80;
        }
    }

    /* calculate bitneed table and max_bitneed value (A2DP 12.6.3.1) */
    if(snr){

        for(i = 0; i < numSubbands; i++){

            bitneed[i] = scaleFactors[i];
            if(bitneed[i] > max_bitneed) max_bitneed = bitneed[i];
        }
    }
    else{

        const signed char* tbl;

        if(numSubbands == 4) tbl = (const signed char*)loudness_4[samplingRate];
        else tbl = (const signed char*)loudness_8[samplingRate];

        for(i = 0; i < numSubbands; i++){

            if(scaleFactors[i]){

                int loudness = scaleFactors[i] - tbl[i];

                if(loudness > 0) loudness /= 2;
                bitneed[i] = loudness;
            }
            else bitneed[i] = -5;
            if(bitneed[i] > max_bitneed) max_bitneed = bitneed[i];
        }
    }               

    /* fit bitslices into the bitpool */
    bitcount = 0;
    slicecount = 0;
    bitslice = max_bitneed + 1;
    /* pr_info("sbc_decode: fit bitslices into the bitpool, bitslice = %d\n", bitslice ); */
    do{
        bitslice--;
        bitcount += slicecount;
        slicecount = 0;
        for(i = 0; i < numSubbands; i++){

            if(bitneed[i] > bitslice + 1 && bitneed[i] < bitslice + 16) slicecount++;
            else if(bitneed[i] == bitslice + 1) slicecount += 2;
        }

    }while(bitcount + slicecount < bitpoolSz);              

    /* distribute bits */
    for(i = 0; i < numSubbands; i++){

        if(bitneed[i] < bitslice + 2) bits[i] = 0;
        else{

            int8_t v = bitneed[i] - bitslice;
            if(v > 16) v = 16;
            bits[i] = v;
        }
    }       

    /* allocate remaining bits */
    for(i = 0; i < numSubbands && bitcount < bitpoolSz; i++){

        if(bits[i] >= 2 && bits[i] < 16){

            bits[i]++;
            bitcount++;
        }
        else if(bitneed[i] == bitslice + 1 && bitpoolSz > bitcount + 1){

            bits[i] = 2;
            bitcount += 2;
        }
    }
    for(i = 0; i < numSubbands && bitcount < bitpoolSz; i++){

        if(bits[i] < 16){

            bits[i]++;
            bitcount++;
        }
    }               

    /* reconstruct subband samples (A2DP 12.6.4) */
#ifndef SPEED_OVER_ACCURACY
        for(i = 0; i < numSubbands; i++) levels[i] = (1 << bits[i]) - 1;
#endif

    /* pr_info("sbc_decode: reconstruct subband samples, blocks = %d\n", blocks ); */
    for(j = 0; j < blocks; j++){

        for(i = 0; i < numSubbands; i++){

            if(bits[i]){

                uint32_t val = 0;
                k = bits[i];
                do{

                    val <<= 1;
#if (DEBUG_DECODING == 1)
                    pr_info("%s: buf = %p, offset %d\n",
                        __func__, buf, buf-start_buf);
#endif
                    if(*buf & bitpos) val++;
                    if(!(bitpos >>= 1)){
                        bitpos = 0x80;
                        buf++;
                    }
                }while(--k);

                val = (val << 1) | 1;
                val <<= scaleFactors[i];

                #ifdef SPEED_OVER_ACCURACY
                    val = mulshift(val, bits[i]);
                #else
                    val /= levels[i];
                #endif

                val -= (1 << scaleFactors[i]);

                samples[j][i] = SAMPLE_CVT(val);
            }
            else samples[j][i] = SAMPLE_CVT(0);
        }
    }       

    //sbc_decoder_reset();

    for(j = 0; j < blocks; j++){
        synth(outBufPtr, samples[j], numSubbands, gV);
        outBufPtr += numSubbands;
    }

    /* if we used a byte partially, skip the rest of it, it is "padding" */
    if(bitpos != 0x80) buf++;

    out:
#if (DEBUG_DECODING == 1)
        if(left < 0)
            pr_err("SBC: buffer over-read by %d bytes.\n", -left);
        if(left > 0)
            pr_err("SBC: buffer under-read by %d bytes.\n", left);
#endif 

    fwrite(usDecodedBuffer, sizeof(uint16_t), 120, fOutput);
    fflush(fOutput);
    memset(usDecodedBuffer, 0, sizeof(usDecodedBuffer));

}

4 总结

  • 在章节3中给出的例子中,压缩前的数据有120*2=240Byte,压缩后的数据有54Byte,压缩比接近4.4:1,压缩比是可调的,其与编码参数有关
  • 压缩后的帧数据长度可以由编码参数计算出来。此例中,
    帧长度=4+(4 * 子带数量 * 通道数量)/8+(块数量 * 通道数量 * bitpool)/8
    =4+(4*8*1)/8+(15*1*26)/8=8+48.75=57(加上CRC正好58字节)
  • 如果在传输过程中丢失了个别帧,解压后回放时会出现一小段音频的整体丢失,对整体的解压没有影响

参考文献

1. LOW-POWER IMPLEMENTATION OF THE BLUETOOTH SUBBAND AUDIO CODEC.pdf
2. A2DP_SPEC_V12.pdf
3. 音频数据SBC算法编码方法及蓝牙立体声子系统.pdf

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194905.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • sql定义变量的语法是什么_sql 定义变量

    sql定义变量的语法是什么_sql 定义变量有的时候在查询更新数据库的时候,需要多条语句的查询,因此需要多次修改declare可以满足多次执行,但数据只修改一次。declare @local_variabledata_typeDECLARE:定义变量,变量第一个字母是“@”声明时需要指定变量的类型,可以使用set和select对变量进行赋值,在sql语句中就可以使用@local

    2022年8月20日
    13
  • 光纤交换机配置zone[通俗易懂]

    光纤交换机配置zone[通俗易懂]B系列SAN交换机-Zoning配置信息型本文档提供了BrocadeSAN交换机zoning配置的分步骤指南。详细信息新建zone配置:a. 新建一个zoning配置,用cfgcreate命令。格式:cfgcreate“cfgname”,“zonename1;zonename2;…”举例:cfgcreate“cfg1”,“zone1;zone2”b. 新建一个zone,用zonecreate命令。格式:zonecreate“zone

    2022年5月22日
    36
  • 黑盒测试的等价类划分法_黑盒测试等价类输出

    黑盒测试的等价类划分法_黑盒测试等价类输出1、等价类划分2、边界值分析

    2022年10月3日
    4
  • 【Matlab】如何规范地编写一个MATLAB函数文件

    【Matlab】如何规范地编写一个MATLAB函数文件在matlab中,M文件分为脚本文件和函数文件。如果M文件的第一个可执行语句以function开头,那这个M文件就是函数文件。函数文件内定义的变量为局部变量,只在函数文件内部起作用,当函数文件执行完后,这些内部变量将被清除。本文介绍如何规范地编写一个函数文件。通常,函数文件由函数声明行、H1行、在线帮助文本区、编写和修改记录、函数主体等几个部分组成。格式如下:function输出形参…

    2022年7月17日
    12
  • SSM图书管理系统(源码分享)「建议收藏」

    SSM图书管理系统(源码分享)「建议收藏」SSM图书管理系统开发环境:eclipse+maven+maven+mysql+tomcat8.5开发技术:后端ssm+前端layui(JSP)代码下载:https://gitee.com/kai_w/bookms说明:压缩包类包含了完整的项目配置文件和数据库文件该项目已实现功能:管理员的登录与退出,登录错误提示 图书的新增,修改,下架,…

    2022年9月23日
    6
  • TCP三次握手四次挥手(通俗易懂版)

    TCP三次握手四次挥手(通俗易懂版)TCP连接的建立(三次握手)最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。三次握手通俗理解:TCP客户端是男孩子,TCP服务器是女孩子,男孩子要追女孩子,首先要建立恋爱关系。需要一个建立恋爱关系的过程。男孩:我们可以恋爱吗?(客户端请求服务器建立连接)女孩:你想追我?想清楚了!(服务器要求客户端确认连接)男孩:没错,你就是我的梦…

    2022年6月22日
    30

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号