diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 240b34c9a0d0e..be867069ed981 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -5417,6 +5417,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: diff --git a/include/zephyr/libsbc/sbc.h b/include/zephyr/libsbc/sbc.h new file mode 100644 index 0000000000000..3b8ff336c66ba --- /dev/null +++ b/include/zephyr/libsbc/sbc.h @@ -0,0 +1,113 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#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 diff --git a/modules/Kconfig b/modules/Kconfig index eda6daa16095e..3d5d834d92e11 100644 --- a/modules/Kconfig +++ b/modules/Kconfig @@ -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 diff --git a/modules/libsbc/CMakeLists.txt b/modules/libsbc/CMakeLists.txt new file mode 100644 index 0000000000000..1249e9632aeae --- /dev/null +++ b/modules/libsbc/CMakeLists.txt @@ -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() diff --git a/modules/libsbc/Kconfig b/modules/libsbc/Kconfig new file mode 100644 index 0000000000000..af81496226793 --- /dev/null +++ b/modules/libsbc/Kconfig @@ -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 diff --git a/modules/libsbc/sbc.c b/modules/libsbc/sbc.c new file mode 100644 index 0000000000000..9be90dd3cffac --- /dev/null +++ b/modules/libsbc/sbc.c @@ -0,0 +1,191 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#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 diff --git a/west.yml b/west.yml index 3634554334c3a..88abb73847c7a 100644 --- a/west.yml +++ b/west.yml @@ -293,6 +293,9 @@ manifest: path: modules/hal/libmetal groups: - hal + - name: libsbc + revision: 8e1beda02acb8972e29e6edbb423f7cafe16e445 + path: modules/lib/libsbc - name: littlefs path: modules/fs/littlefs groups: