qq_41944943 2022-07-18 17:21 采纳率: 0%
浏览 39
已结题

SBC编解码原理详解问题

有没有同学了解SBC编解码原理,比如参数设置中 block length中的具体含义和作用,以及APCM算法原理等,万分感谢!

  • 写回答

1条回答 默认 最新

  • m0_72929209 2022-07-23 16:43
    关注
    1. SBC算法是sub band code的缩写,也可称为子带编码
      在A2DP协议中,SBC算法是默认支持的
      蓝牙SBC算法是特率传递高质量音频数据的低计算复杂度的音频编码算法
    2. 1 算法基本框图
      SBC系统使用一个余弦调制的滤波器组,用来解析和同步。滤波器组可设定成4或8个子带
      子带信号的量化采用比特分配器和自适应脉冲编码器组调制
      可用的比特位数和编码器的块数都是可配置的
      编码后的数据由比特流打包器打包,以有线或无线方式传输
      解码是编码的逆过程
      这里写图片描述
    3. 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算法实现
    4. 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中给出的例子中,压缩前的数据有1202=240Byte,压缩后的数据有54Byte,压缩比接近4.4:1,压缩比是可调的,其与编码参数有关
    压缩后的帧数据长度能够由编码参数计算出来。此例中,
    帧长度=4+(4 * 子带数量 * 通道数量)/8+(块数量 * 通道数量 * bitpool)/8
    =4+(4
    81)/8+(151*26)/8=8+48.75=57(加上CRC正好58字节)
    若是在传输过程当中丢失了个别帧,解压后回放时会出现一小段音频的总体丢失,对总体的解压没有影响

    评论

报告相同问题?

问题事件

  • 系统已结题 7月26日
  • 修改了问题 7月19日
  • 修改了问题 7月19日
  • 创建了问题 7月18日

悬赏问题

  • ¥15 使用C#,asp.net读取Excel文件并保存到Oracle数据库
  • ¥15 C# datagridview 单元格显示进度及值
  • ¥15 thinkphp6配合social login单点登录问题
  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配