5 SBC/mSBC 编解码
SBC 支持多种采样率、多种帧长。一个 SBC/mSBC 音频帧由 4 部分组成:
struct
{
frame_header;
scale_factors;
audio_samples;
padding;
};其中 frame_header 里的第一字节为 sync_word。对于 SBC,sync_word 固定为 0x9C, 而 mSBC 则为 0xAD。
struct frame_header
{
uint8_t sync_word;
....
};5.1 帧描述参数
帧描述参数见 sbc_frame 结构体:
struct sbc_frame
{
bool msbc; // 是否为 mSBC
enum sbc_freq freq; // 采样频率
enum sbc_mode mode; // 声道模式
enum sbc_bam bam; // 比特分配方式
int nblocks, nsubbands; // 分块数,子带数
int bitpool; // bit 池大小
};nblocks 应为 4、8、12 或 16,nsubbands 可为 4 或 8。
进行编码时,每个声道上的每一帧需要 (nblocks \(\times\) nsubbands) 个采样,该值也可通过 sbc_get_frame_samples() 获取。
bitpool 是一个块(nsubbands 个子带)所能占据的最多比特数。
mSBC 使用一组固定的参数:
const struct sbc_frame msbc_frame = {
.msbc = true,
.mode = SBC_MODE_MONO,
.freq = SBC_FREQ_16K,
.bam = SBC_BAM_LOUDNESS,
.nsubbands = 8, .nblocks = 15,
.bitpool = 26
};5.2 使用方法
5.2.1 编码
确定帧描述参数
确定了帧描述参数后,务必使用
sbc_get_frame_size()等函数检查参数是否合法。检查关键参数
sbc_get_frame_size()获得编码后每个帧的字节长度;sbc_get_frame_bitrate()获得编码后的比特率;sbc_get_frame_samples()为一个声道编码一个帧所需要的采样数
如果帧描述参数不合法,这些函数都将返回 \(0\)。
初始化对象
void sbc_reset( sbc_t *sbc); // SBC 对象进行编码
音频处理库里包含两个编码函数,其区别在于
sbc_encode2的临时内存由外部分配, 而sbc_encode的临时内存则在栈上分配。对于栈空间紧张的应用,应该使用sbc_encode2。 调用一次sbc_encode2或者sbc_encode完成一帧编码,编码成功返回 0 否则返回错误码。sbc_encode2的函数签名如下:int sbc_encode2( sbc_t *sbc, // SBC 对象 const int16_t *pcml, // 左声道 PCM 数据 int pitchl, // 左声道 PCM 相邻数据在 pcml 里的间隔 const int16_t *pcmr, // 右声道 PCM 数据 int pitchr, // 右声道 PCM 相邻数据在 pcmr 里的间隔 const struct sbc_frame *frame, // 帧描述参数 void *data, // 编码输出 unsigned size, // 编码输出的内存长度 void *scratch); // 临时内存当只编码一个声道时,忽略
pcmr和pitchr参数。pitchl和pitchr分别控制如何从pcml和pcmr读取采样:sample[n] = pcm[n * pitch]。举例说明如下:只有一个声道的数据:
scb_encode2(sbc, pcm, 1, ...);要编码两个声道,且两个声道的数据独立存放:
scb_encode2(sbc, pcml, 1, pcmr, 1, ...);要编码两个声道,且两个声道的数据交织存放,即
pcm[] = {左, 右, 左, 右, ...}:scb_encode2(sbc, pcm, 2, pcm + 1, 2, ...);
size参数至少为sbc_get_frame_size(frame)。临时内存
scrach应给按int型对齐,大小至少为SBC_ENCODE_SCRATCH_MEM_SIZE。sbc_encode比sbc_encode2缺少scratch参数,其它参数完全一致,不再赘述。当使用 mSBC 编码时,
frame只需要设置msbc = true,不需要完整填写 mSBC 帧参数:const struct sbc_frame msbc_frame = { .msbc = true, }; sbc_encode2(..., &msbc_frame, ...);
5.2.2 解码
初始化
void sbc_reset( sbc_t *sbc); // SBC 对象进行解码
同编码类似,音频处理库里包含两个解码函数,其区别在于
sbc_decode2的临时内存由外部分配, 而sbc_decode的临时内存则在栈上分配。对于栈空间紧张的应用,应该使用sbc_decode2。 调用一次sbc_decode2或者sbc_decode完成一帧解码,解码成功返回 0 否则返回错误码。sbc_decode2的函数签名如下:int sbc_decode2( sbc_t *sbc, // SBC 对象 const void *data, // 输入数据(即编码后的一帧) unsigned size, // 输入数据的长度,应不小于该帧的长度 struct sbc_frame *frame, // 解出的帧描述参数 int16_t *pcml, // 左声道 PCM 解码输出 int pitchl, // 左声道 PCM 相邻数据在 pcml 里的间隔 int16_t *pcmr, // 右声道 PCM 解码输出 int pitchr, // 右声道 PCM 相邻数据在 pcmr 里的间隔 void *scratch); // 临时内存pitchl和pitchr的含义与sbc_encode2里相同,区别在于后者用于读取 PCM 数据, 而在这里用于写入 PCM 数据。临时内存
scrach应给按int型对齐,大小至少为SBC_DECODE_SCRATCH_MEM_SIZE。调用解码函数时,必须保证
pcml和pcmr空间足够,即每个声道都足够容纳SBC_MAX_SAMPLES个采样。sbc_decode比sbc_decode2缺少scratch参数,其它参数完全一致,不再赘述。