Skip to content

Implement the sbc encoder and decoder based on Android Bluetooth SBC #88865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions MAINTAINERS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5266,6 +5266,15 @@ West:
labels:
- "area: Audio"

"West project: libsbc":
status: maintained
maintainers:
- MarkWangChinese
files:
- modules/libsbc/
labels:
- "area: Audio"

"West project: littlefs":
status: odd fixes
files:
Expand Down
113 changes: 113 additions & 0 deletions include/zephyr/libsbc/sbc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#if defined(CONFIG_LIBSBC_ENCODER)
#include "sbc_encoder.h"
#endif
#if defined(CONFIG_LIBSBC_DECODER)
#include "oi_codec_sbc.h"
#include "oi_status.h"
#endif

enum __packed sbc_ch_mode {
SBC_CH_MODE_MONO,
SBC_CH_MODE_DUAL_CHANNEL,
SBC_CH_MODE_STEREO,
SBC_CH_MODE_JOINT_STEREO,
};

enum __packed sbc_alloc_mthd {
SBC_ALLOC_MTHD_LOUDNESS,
SBC_ALLOC_MTHD_SNR,
};

#if defined(CONFIG_LIBSBC_ENCODER)

struct sbc_encoder {
SBC_ENC_PARAMS sbc_encoder_params;
};

struct sbc_encoder_init_param {
uint32_t bit_rate;
uint32_t samp_freq;
uint8_t blk_len;
uint8_t subband;
enum sbc_alloc_mthd alloc_mthd;
enum sbc_ch_mode ch_mode;
uint8_t ch_num;
uint8_t min_bitpool;
uint8_t max_bitpool;
};

/**
* Setup encoder
* param The init parameters.
* return Zero on success or (negative) error code otherwise.
*/
int sbc_setup_encoder(struct sbc_encoder *encoder, struct sbc_encoder_init_param *param);

/**
* Encode a frame
* encoder Handle of the encoder
* in_data Input PCM samples
* nbytes Target size, in bytes, of the frame
* out_data Output buffer of `nbytes` size
* return Return number of bytes output
*/
uint32_t sbc_encode(struct sbc_encoder *encoder, const void *in_data, void *out_data);

/**
* Return the number of PCM samples in a frame
* encoder Handle of the encoder.
* return Number of PCM samples or (negative) error code otherwise
*/
int sbc_frame_samples(struct sbc_encoder *encoder);

/**
* Return the number of PCM bytes in a frame
* encoder Handle of the encoder.
* return Number of PCM bytes or (negative) error code otherwise
*/
int sbc_frame_bytes(struct sbc_encoder *encoder);

/**
* Return the encoded size of one frame
* encoder Handle of the encoder.
* return The encoded size of one frame in bytes or (negative) error code otherwise
*/
int sbc_frame_encoded_bytes(struct sbc_encoder *encoder);
#endif

#if defined(CONFIG_LIBSBC_DECODER)

struct sbc_decoder {
OI_CODEC_SBC_DECODER_CONTEXT context;
uint32_t context_data[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)];
};

/**
* Setup the SBC decoder.
* decoder Handle of the decoder
*
* return Zero on success or (negative) error code otherwise.
*/
int sbc_setup_decoder(struct sbc_decoder *decoder);

/**
* Decode a frame
* decoder Handle of the decoder
* in_data Input bitstream, it is increased after decode one frame
* in_size Input data size in bytes, it is decreased after decode one frame
* out_data Output PCM samples
* out_size Output data size in bytes
* return Zero on success or (negative) error code otherwise.
*/
int sbc_decode(struct sbc_decoder *decoder, const void **in_data, uint32_t *in_size,
void *out_data, uint32_t *out_size);
#endif
3 changes: 3 additions & 0 deletions modules/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ comment "hal_nxp module not available."
comment "liblc3 module not available."
depends on !ZEPHYR_LIBLC3_MODULE

comment "libsbc module not available."
depends on !ZEPHYR_LIBSBC_MODULE

comment "LittleFS module not available."
depends on !ZEPHYR_LITTLEFS_MODULE

Expand Down
49 changes: 49 additions & 0 deletions modules/libsbc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
if(CONFIG_LIBSBC_ENCODER OR CONFIG_LIBSBC_DECODER)

zephyr_library_named(libsbc)
zephyr_library_compile_options(-O3 -std=c11 -ffast-math -Wno-array-bounds)

zephyr_compile_definitions(SBC_FOR_EMBEDDED_LINUX)
zephyr_compile_definitions(SBC_NO_PCM_CPY_OPTION)
zephyr_library_sources(sbc.c)

if(CONFIG_LIBSBC_ENCODER)
zephyr_library_sources(
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_analysis.c
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_dct.c
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_dct_coeffs.c
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_enc_bit_alloc_mono.c
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_enc_bit_alloc_ste.c
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_enc_coeffs.c
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_encoder.c
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/srce/sbc_packing.c
)
zephyr_include_directories(
${ZEPHYR_LIBSBC_MODULE_DIR}/encoder/include
)
endif()

if(CONFIG_LIBSBC_DECODER)
zephyr_library_sources(
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/alloc.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/bitalloc.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/bitalloc-sbc.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/bitstream-decode.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/decoder-oina.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/decoder-private.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/decoder-sbc.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/dequant.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/framing.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/framing-sbc.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/oi_codec_version.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/readsamplesjoint.inc
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/synthesis-8-generated.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/synthesis-dct8.c
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce/synthesis-sbc.c
)
zephyr_include_directories(
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/include
${ZEPHYR_LIBSBC_MODULE_DIR}/decoder/srce
)
endif()
endif()
15 changes: 15 additions & 0 deletions modules/libsbc/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2024 NXP
# SPDX-License-Identifier: Apache-2.0

config ZEPHYR_LIBSBC_MODULE
bool

config LIBSBC_ENCODER
bool "libsbc encoder Support"
help
This option enables the Android SBC encoder library for Bluetooth A2DP

config LIBSBC_DECODER
bool "libsbc decoder Support"
help
This option enables the Android SBC decoder library for Bluetooth A2DP
191 changes: 191 additions & 0 deletions modules/libsbc/sbc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <zephyr/libsbc/sbc.h>

#if defined(CONFIG_LIBSBC_ENCODER)

int sbc_setup_encoder(struct sbc_encoder *encoder, struct sbc_encoder_init_param *param)
{
SBC_ENC_PARAMS *encoder_params;

if (encoder == NULL) {
return -EINVAL;
}

memset(encoder, 0, sizeof(struct sbc_encoder));

encoder_params = &encoder->sbc_encoder_params;

encoder_params->s16ChannelMode = (int16_t)param->ch_mode;
encoder_params->s16NumOfSubBands = (int16_t)param->subband;
if (!encoder_params->s16NumOfSubBands) {
return -EINVAL;
}
encoder_params->s16NumOfBlocks = (int16_t)param->blk_len;
if (!encoder_params->s16NumOfBlocks) {
return -EINVAL;
}
encoder_params->s16AllocationMethod = (int16_t)param->alloc_mthd;
encoder_params->s16NumOfChannels = param->ch_num;
if (!encoder_params->s16NumOfChannels) {
return -EINVAL;
}

switch (param->samp_freq) {
case 16000u:
encoder_params->s16SamplingFreq = 0;
break;
case 32000u:
encoder_params->s16SamplingFreq = 1;
break;
case 44100u:
encoder_params->s16SamplingFreq = 2;
break;
case 48000u:
encoder_params->s16SamplingFreq = 3;
break;
default:
return -EINVAL;
}

encoder_params->u16BitRate = param->bit_rate;

SBC_Encoder_Init(encoder_params);

if (encoder_params->s16BitPool < param->min_bitpool) {
/* need to increase the `param->bit_rate` */
return -EINVAL;
} else if (encoder_params->s16BitPool > param->max_bitpool) {
/* need to decrease the `param->bit_rate` */
return -EOVERFLOW;
}

return 0;
}

/**
* Encode a SBC frame
*/
uint32_t sbc_encode(struct sbc_encoder *encoder, const void *in_data, void *out_data)
{
uint32_t ret;

if ((encoder == NULL) || (in_data == NULL) || (out_data == NULL)) {
return 0;
}

ret = SBC_Encode(&encoder->sbc_encoder_params, (int16_t *)in_data, out_data);

return ret;
}

int sbc_frame_samples(struct sbc_encoder *encoder)
{
if (encoder == NULL) {
return -EINVAL;
}

return encoder->sbc_encoder_params.s16NumOfSubBands *
encoder->sbc_encoder_params.s16NumOfBlocks;
}

int sbc_frame_bytes(struct sbc_encoder *encoder)
{
if (encoder == NULL) {
return -EINVAL;
}

return sbc_frame_samples(encoder) * 2 *
(encoder->sbc_encoder_params.s16ChannelMode == SBC_CH_MODE_MONO ? 1 : 2);
}

int sbc_frame_encoded_bytes(struct sbc_encoder *encoder)
{
int size = 4;
int channel_num = 2;
SBC_ENC_PARAMS *encoder_params;

if (encoder == NULL) {
return -EINVAL;
}

encoder_params = &encoder->sbc_encoder_params;

if (encoder_params->s16ChannelMode == SBC_CH_MODE_MONO) {
channel_num = 1;
}

size += (4 * encoder_params->s16NumOfSubBands * channel_num) / 8;
if ((encoder_params->s16ChannelMode == SBC_CH_MODE_MONO) ||
(encoder_params->s16ChannelMode == SBC_CH_MODE_DUAL_CHANNEL)) {
size += ((encoder_params->s16NumOfBlocks * channel_num *
encoder_params->s16BitPool + 7) / 8);
} else if (encoder_params->s16ChannelMode == SBC_CH_MODE_STEREO) {
size += ((encoder_params->s16NumOfBlocks *
encoder_params->s16BitPool + 7) / 8);
} else {
size += ((encoder_params->s16NumOfSubBands +
encoder_params->s16NumOfBlocks *
encoder_params->s16BitPool + 7) / 8);
}

return size;
}
#endif

#if defined(CONFIG_LIBSBC_DECODER)
/**
* Setup decoder
*/
int sbc_setup_decoder(struct sbc_decoder *decoder)
{
OI_STATUS status;

if (decoder == NULL) {
return -EINVAL;
}

memset(decoder, 0, sizeof(struct sbc_decoder));

status = OI_CODEC_SBC_DecoderReset(
&decoder->context,
&decoder->context_data[0],
sizeof(decoder->context_data),
2, 2, FALSE);
if (!OI_SUCCESS(status)) {
return -EIO;
}

return 0;
}

/**
* Decode a frame
*/
int sbc_decode(struct sbc_decoder *decoder, const void **in_data, uint32_t *in_size,
void *out_data, uint32_t *out_size)
{
OI_STATUS status;

if (decoder == NULL || in_data == NULL || in_size == NULL ||
out_data == NULL || out_size == NULL) {
return -EINVAL;
}

status = OI_CODEC_SBC_DecodeFrame(&decoder->context,
(const OI_BYTE**)in_data,
in_size,
out_data,
out_size);
if (!OI_SUCCESS(status)) {
return -EIO;
} else {
return 0;
}
}
#endif
Loading
Loading