From 639fe4f4306b55de7675441c194bde015fd11cd7 Mon Sep 17 00:00:00 2001 From: Graham Wacey Date: Mon, 1 Jul 2024 16:42:07 +0100 Subject: [PATCH] applications: nrf5340_audio: LC3 Decoder Audio Module Implement the LC3 T2 decoder audio module. Signed-off-by: Graham Wacey --- include/audio_modules/lc3_decoder.h | 83 +++ subsys/CMakeLists.txt | 1 + subsys/Kconfig | 1 + .../lc3/t2/decoder/CMakeLists.txt | 16 + subsys/audio_modules/lc3/t2/decoder/Kconfig | 146 +++++ .../lc3/t2/decoder/lc3_decoder.c | 435 +++++++++++++++ .../audio_modules/lc3_module/CMakeLists.txt | 30 + .../subsys/audio_modules/lc3_module/prj.conf | 22 + .../lc3_module/src/decoder_test.c | 525 ++++++++++++++++++ .../lc3_module/src/lc3_test_common.c | 92 +++ .../lc3_module/src/lc3_test_common.h | 63 +++ .../lc3_module/src/lc3_test_fakes.c | 368 ++++++++++++ .../lc3_module/src/lc3_test_fakes.h | 99 ++++ .../audio_modules/lc3_module/src/main.c | 25 + .../src/sweep21ms_16b48khz_mono_lc3.c | 38 ++ .../src/sweep21ms_16b48khz_mono_lc3.h | 16 + .../src/sweep21ms_16b48khz_mono_wav.c | 102 ++++ .../src/sweep21ms_16b48khz_mono_wav.h | 16 + .../audio_modules/lc3_module/testcase.yaml | 7 + 19 files changed, 2085 insertions(+) create mode 100644 include/audio_modules/lc3_decoder.h create mode 100644 subsys/audio_modules/lc3/t2/decoder/CMakeLists.txt create mode 100644 subsys/audio_modules/lc3/t2/decoder/Kconfig create mode 100644 subsys/audio_modules/lc3/t2/decoder/lc3_decoder.c create mode 100644 tests/subsys/audio_modules/lc3_module/CMakeLists.txt create mode 100644 tests/subsys/audio_modules/lc3_module/prj.conf create mode 100644 tests/subsys/audio_modules/lc3_module/src/decoder_test.c create mode 100644 tests/subsys/audio_modules/lc3_module/src/lc3_test_common.c create mode 100644 tests/subsys/audio_modules/lc3_module/src/lc3_test_common.h create mode 100644 tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.c create mode 100644 tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.h create mode 100644 tests/subsys/audio_modules/lc3_module/src/main.c create mode 100644 tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.c create mode 100644 tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.h create mode 100644 tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.c create mode 100644 tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.h create mode 100644 tests/subsys/audio_modules/lc3_module/testcase.yaml diff --git a/include/audio_modules/lc3_decoder.h b/include/audio_modules/lc3_decoder.h new file mode 100644 index 000000000000..430cd29d3bd4 --- /dev/null +++ b/include/audio_modules/lc3_decoder.h @@ -0,0 +1,83 @@ +/* + * Copyright(c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#ifndef _LC3_DECODER_H_ +#define _LC3_DECODER_H_ + +#include "audio_defines.h" +#include "audio_module.h" + +#define LC3_DECODER_PCM_NUM_BYTES_MONO \ + ((CONFIG_LC3_DECODER_SAMPLE_RATE_HZ * CONFIG_LC3_DECODER_BIT_DEPTH_OCTETS * \ + CONFIG_LC3_DECODER_FRAME_DURATION_US) / \ + 1000000) + +/** + * @brief Private pointer to the module's parameters. + */ +extern struct audio_module_description *lc3_decoder_description; + +/** + * @brief Private pointer to the decoders handle. + */ +struct lc3_decoder_handle; + +/** + * @brief The module configuration structure. + */ +struct lc3_decoder_configuration { + /* Sample rate for the decoder instance. */ + uint32_t sample_rate_hz; + + /* Number of valid bits for a sample (bit depth). + * Typically 16 or 24. + */ + uint8_t bits_per_sample; + + /* Number of bits used to carry a sample of size bits_per_sample. + * For example, say we have a 24 bit sample stored in a 32 bit + * word (int32_t), then: + * bits_per_sample = 24 + * carrier_size = 32 + */ + uint32_t carried_bits_per_sample; + + /* Frame duration for this decoder instance. */ + uint32_t data_len_us; + + /* A flag indicating if the decoded buffer is sample interleaved or not. */ + bool interleaved; + + /* Channel locations for this decoder instance. */ + uint32_t locations; + + /* Maximum bitrate supported by the decoder. */ + uint32_t bitrate_bps_max; +}; + +/** + * @brief Private module context. + */ +struct lc3_decoder_context { + /* Array of decoder channel handles. */ + struct lc3_decoder_handle *lc3_dec_channel[CONFIG_LC3_DEC_CHANNELS_MAX]; + + /* Number of decoder channel handles. */ + uint32_t dec_handles_count; + + /* The decoder configuration. */ + struct lc3_decoder_configuration config; + + /* Minimum coded bytes required for this decoder instance. */ + uint16_t coded_bytes_req; + + /* Audio sample bytes per frame. */ + size_t sample_frame_bytes; + + /* Number of successive frames to which PLC has been applied. */ + uint16_t plc_count; +}; + +#endif /* _LC3_DECODER_H_ */ diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index 66f6a888a244..7f551edfa21c 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -71,6 +71,7 @@ add_subdirectory_ifdef(CONFIG_EMDS emds) add_subdirectory_ifdef(CONFIG_NET_CORE_MONITOR net_core_monitor) add_subdirectory_ifdef(CONFIG_AUDIO_MODULE audio_module) add_subdirectory_ifdef(CONFIG_AUDIO_MODULE_TEMPLATE audio_modules/audio_module_template) +add_subdirectory_ifdef(CONFIG_AUDIO_MODULE_T2_LC3_DECODER audio_modules/lc3/t2/decoder) add_subdirectory_ifdef(CONFIG_UART_ASYNC_ADAPTER uart_async_adapter) add_subdirectory_ifdef(CONFIG_SDFW_SERVICES_ENABLED sdfw_services) add_subdirectory(suit) diff --git a/subsys/Kconfig b/subsys/Kconfig index be5bf05bed23..e60c31881484 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -35,6 +35,7 @@ rsource "nrf_security/Kconfig" rsource "net_core_monitor/Kconfig" rsource "audio_module/Kconfig" rsource "audio_modules/audio_module_template/Kconfig" +rsource "audio_modules/lc3/t2/decoder/Kconfig" rsource "uart_async_adapter/Kconfig" rsource "trusted_storage/Kconfig" rsource "logging/Kconfig" diff --git a/subsys/audio_modules/lc3/t2/decoder/CMakeLists.txt b/subsys/audio_modules/lc3/t2/decoder/CMakeLists.txt new file mode 100644 index 000000000000..4a2fb38fafc2 --- /dev/null +++ b/subsys/audio_modules/lc3/t2/decoder/CMakeLists.txt @@ -0,0 +1,16 @@ +# +# Copyright (c) 2023 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lc3_decoder.c) + +target_include_directories(app PRIVATE ${ZEPHYR_NRF_MODULE_DIR}/include/audio_module + ${ZEPHYR_NRF_MODULE_DIR}/include/audio_modules) + +zephyr_include_directories( + ${ZEPHYR_BASE}/../nrfxlib/lc3/codec/inc + ${ZEPHYR_BASE}/../nrfxlib/lc3/platform/os/inc + ${ZEPHYR_BASE}/../nrfxlib/lc3/platform/os/baremetal/inc +) diff --git a/subsys/audio_modules/lc3/t2/decoder/Kconfig b/subsys/audio_modules/lc3/t2/decoder/Kconfig new file mode 100644 index 000000000000..6f8da59f9e7b --- /dev/null +++ b/subsys/audio_modules/lc3/t2/decoder/Kconfig @@ -0,0 +1,146 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menu "Audio Modules" + +config AUDIO_MODULE_T2_LC3_DECODER + bool "Enable the T2 LC3 decoder" + default y + help + Enable the T2 LC3 decoder audio module in test mode. + +config AUDIO_MODULE_T2_LC3_DECODER_TEST + bool "Enable the T2 LC3 decoder" + default n + help + Enable the T2 LC3 decoder audio module in test mode. + +config LC3_DEC_CHANNELS_MAX + int "Maximum audio channels" + default 2 + help + The maximum audio channels the decoder can support. + +choice LC3_DECODER_FRAME_DURATION + prompt "Select frame duration - 7.5 ms frame duration is not tested" + default LC3_DECODER_FRAME_DURATION_10_MS + help + LC3 supports frame duration of 7.5 and 10 ms. + +config LC3_DECODER_FRAME_DURATION_7_5_MS + bool "7.5 ms" + +config LC3_DECODER_FRAME_DURATION_10_MS + bool "10 ms" +endchoice + +config LC3_DECODER_FRAME_DURATION_US + int + default 7500 if LC3_DECODER_FRAME_DURATION_7_5_MS + default 10000 if LC3_DECODER_FRAME_DURATION_10_MS + help + Audio frame duration in µs. + +choice LC3_DECODER_SYSTEM_SAMPLE_RATE + prompt "System audio sample rate" + default LC3_DECODER_SAMPLE_RATE_48000_HZ + help + This configuration reflects the decoding sample rate. + +config LC3_DECODER_SAMPLE_RATE_8000_HZ + bool "8 kHz" + help + Sample rate of 16kHz. + +config LC3_DECODER_SAMPLE_RATE_16000_HZ + bool "16 kHz" + help + Sample rate of 16kHz. + +config LC3_DECODER_SAMPLE_RATE_24000_HZ + bool "24 kHz" + help + Sample rate of 24kHz. + +config LC3_DECODER_SAMPLE_RATE_32000_HZ + bool "32 kHz" + help + Sample rate of 16kHz. + +config LC3_DECODER_SAMPLE_RATE_44100_HZ + bool "44.1 kHz" + help + Sample rate of 16kHz. + +config LC3_DECODER_SAMPLE_RATE_48000_HZ + bool "48 kHz" + help + Sample rate of 48kHz. +endchoice + +config LC3_DECODER_SAMPLE_RATE_HZ + int + default 8000 if LC3_DECODER_SAMPLE_RATE_8000_HZ + default 16000 if LC3_DECODER_SAMPLE_RATE_16000_HZ + default 24000 if LC3_DECODER_SAMPLE_RATE_24000_HZ + default 32000 if LC3_DECODER_SAMPLE_RATE_32000_HZ + default 44100 if LC3_DECODER_SAMPLE_RATE_44100_HZ + default 48000 if LC3_DECODER_SAMPLE_RATE_48000_HZ + help + I2S supports 16, 24, and 48 kHz sample rates for both input and output. + USB supports only 48 kHz for input. + +choice LC3_DECODER_BIT_DEPTH + prompt "Audio bit depth" + default LC3_DECODER_BIT_DEPTH_16 + help + Select the bit depth for audio. + +config LC3_DECODER_BIT_DEPTH_16 + bool "16 bit audio" + +config LC3_DECODER_BIT_DEPTH_32 + bool "32 bit audio" +endchoice + +config LC3_DECODER_BIT_DEPTH_BITS + int + default 16 if LC3_DECODER_BIT_DEPTH_16 + default 32 if LC3_DECODER_BIT_DEPTH_32 + help + Bit depth of one sample in storage. + +config LC3_DECODER_BIT_DEPTH_OCTETS + int + default 2 if LC3_DECODER_BIT_DEPTH_16 + default 4 if LC3_DECODER_BIT_DEPTH_32 + help + Bit depth of one sample in storage given in octets. + +config LC3_DECODER_THREAD_PRIO + int "Priority for the decoder thread" + default 3 + help + This is a preemptible thread. + +config LC3_DECODER_STACK_SIZE + int "Stack size for the decoder thread" + default 4092 if LC3_DECODER_BIT_DEPTH_16 + default 5115 if LC3_DECODER_BIT_DEPTH_32 + help + The modules thread stack size. + +osource "../nrfxlib/lc3/Kconfig" + +#----------------------------------------------------------------------------# +menu "Log levels" + +module = AUDIO_MODULE_LC3_DECODER +module-str = t2_lc3_decoder +source "subsys/logging/Kconfig.template.log_config" + +endmenu # Log levels +endmenu # Audio Modules diff --git a/subsys/audio_modules/lc3/t2/decoder/lc3_decoder.c b/subsys/audio_modules/lc3/t2/decoder/lc3_decoder.c new file mode 100644 index 000000000000..99a5b721d53b --- /dev/null +++ b/subsys/audio_modules/lc3/t2/decoder/lc3_decoder.c @@ -0,0 +1,435 @@ +/* + * Copyright(c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "audio_modules/lc3_decoder.h" + +#include +#include +#include + +#include "audio_defines.h" +#include "audio_module/audio_module.h" +#include "LC3API.h" + +#include +LOG_MODULE_REGISTER(t2_lc3_decoder, CONFIG_AUDIO_MODULE_LC3_DECODER_LOG_LEVEL); + +/** + * @brief Number of micro seconds in a second. + * + */ +#define LC3_DECODER_US_IN_A_SECOND (1000000) + +/** + * @brief Interleave a channel into a buffer of N channels of PCM + * + * @note: The interleaver can not be executed inplace (i.e. input != output) + * + * @param[in] input Pointer to the single channel input buffer. + * @param[in] input_size Number of bytes in input. Must be divisible by two. + * @param[in] channel Channel to interleave into. + * @param[in] pcm_bit_depth Bit depth of PCM samples (8, 16, 24, or 32). + * @param[out] output Pointer to the output start of the multi-channel output. + * @param[in] output_size Number of bytes in output. Must be divisible by two and + * at least (input_size * bytes_per_sample * output_channels). + * @param[in] output_channels Number of output channels in the output buffer. + * + * @return 0 if successful, error value + */ +static int interleave(void const *const input, size_t input_size, uint8_t channel, + uint8_t pcm_bit_depth, void *output, size_t output_size, + uint8_t output_channels) +{ + uint8_t bytes_per_sample = pcm_bit_depth / 8; + size_t step; + uint8_t *pointer_input; + uint8_t *pointer_output; + + if (input == NULL || input_size == 0 || channel > output_channels || pcm_bit_depth == 0 || + output == NULL || output_size == 0 || output_channels == 0) { + return -EINVAL; + } + + if (output_size < (input_size * output_channels)) { + LOG_DBG("Output buffer too small to interleave input into"); + return -EINVAL; + } + + step = bytes_per_sample * (output_channels - 1); + pointer_input = (uint8_t *)input; + pointer_output = (uint8_t *)output + (step * channel); + + for (size_t i = 0; i < input_size; i += bytes_per_sample) { + for (size_t j = 0; j < bytes_per_sample; j++) { + *pointer_output++ = *pointer_input++; + } + + pointer_output += step; + } + + return 0; +} + +/** + * @brief Open the LC3 module. + * + * @param handle A pointer to the audio module's handle. + * @param configuration A pointer to the audio module's configuration to set. + * + * @return 0 if successful, error value + */ +static int lc3_dec_t2_open(struct audio_module_handle_private *handle, + struct audio_module_configuration const *const configuration) +{ + struct audio_module_handle *hdl = (struct audio_module_handle *)handle; + struct lc3_decoder_context *ctx = (struct lc3_decoder_context *)hdl->context; + + /* Clear the context */ + memset(ctx, 0, sizeof(struct lc3_decoder_context)); + + LOG_DBG("Open LC3 decoder module"); + + return 0; +} + +/** + * @brief Close the LC3 audio module. + * + * @param handle A pointer to the audio module's handle. + * + * @return 0 if successful, error value + */ +static int lc3_dec_t2_close(struct audio_module_handle_private *handle) +{ + struct audio_module_handle *hdl = (struct audio_module_handle *)handle; + struct lc3_decoder_context *ctx = (struct lc3_decoder_context *)hdl->context; + LC3DecoderHandle_t *dec_handles = (LC3DecoderHandle_t *)&ctx->lc3_dec_channel[0]; + int8_t number_channels; + + audio_module_number_channels_calculate(ctx->config.locations, &number_channels); + + /* Close decoder sessions */ + for (uint8_t i = 0; i < number_channels; i++) { + if (dec_handles[i] != NULL) { + LC3DecodeSessionClose(dec_handles[i]); + dec_handles[i] = NULL; + } + } + + return 0; +} + +/** + * @brief Set the configuration of an audio module. + * + * @param handle A pointer to the audio module's handle. + * @param configuration A pointer to the audio module's configuration to set. + * + * @return 0 if successful, error value + */ +static int +lc3_dec_t2_configuration_set(struct audio_module_handle_private *handle, + struct audio_module_configuration const *const configuration) +{ + int ret; + struct audio_module_handle *hdl = (struct audio_module_handle *)handle; + struct lc3_decoder_context *ctx = (struct lc3_decoder_context *)hdl->context; + struct lc3_decoder_configuration *config = + (struct lc3_decoder_configuration *)configuration; + LC3DecoderHandle_t *dec_handles = (LC3DecoderHandle_t *)&ctx->lc3_dec_channel[0]; + LC3FrameSize_t framesize; + uint16_t coded_bytes_req; + int8_t number_channels; + + /* Need to validate the config parameters here before proceeding */ + + audio_module_number_channels_calculate(config->locations, &number_channels); + + /* Free previous decoder memory */ + for (uint8_t i = 0; i < number_channels; i++) { + if (dec_handles[i] != NULL) { + LC3DecodeSessionClose(dec_handles[i]); + dec_handles[i] = NULL; + } + } + + switch (config->data_len_us) { + case 7500: + framesize = LC3FrameSize7_5Ms; + break; + case 10000: + framesize = LC3FrameSize10Ms; + break; + default: + LOG_ERR("Unsupported framesize: %d", config->data_len_us); + return -EINVAL; + } + + coded_bytes_req = LC3BitstreamBuffersize(config->sample_rate_hz, config->bitrate_bps_max, + framesize, &ret); + if (coded_bytes_req == 0) { + LOG_ERR("Required coded bytes to LC3 instance %s is zero", hdl->name); + return -EPERM; + } + + for (uint8_t i = 0; i < number_channels; i++) { + dec_handles[i] = + LC3DecodeSessionOpen(config->sample_rate_hz, config->bits_per_sample, + framesize, NULL, NULL, &ret); + if (ret) { + LOG_ERR("LC3 decoder channel %d failed to initialise for module %s", i, + hdl->name); + return ret; + } + + LOG_DBG("LC3 decode module %s session %d: %dus %dbits", hdl->name, i, + config->data_len_us, config->bits_per_sample); + + ctx->dec_handles_count += 1; + } + + memcpy(&ctx->config, config, sizeof(struct lc3_decoder_configuration)); + + ctx->coded_bytes_req = coded_bytes_req; + ctx->sample_frame_bytes = ((ctx->config.data_len_us * ctx->config.sample_rate_hz) / + LC3_DECODER_US_IN_A_SECOND) * + (ctx->config.carried_bits_per_sample / 8); + + LOG_DBG("LC3 decode module %s requires %d coded bytes to produce %d decoded sample bytes", + hdl->name, ctx->coded_bytes_req, ctx->sample_frame_bytes); + + /* Configure decoder */ + LOG_DBG("LC3 decode module %s configuration: %d Hz %d bits (sample bits %d) " + "%d us %d " + "channel(s)", + hdl->name, ctx->config.sample_rate_hz, ctx->config.carried_bits_per_sample, + ctx->config.bits_per_sample, ctx->config.data_len_us, number_channels); + + return 0; +} + +/** + * @brief Get the current configuration of an audio module. + * + * @param handle A pointer to the audio module's handle. + * @param configuration A pointer to the audio module's current configuration. + * + * @return 0 if successful, error value + */ +static int lc3_dec_t2_configuration_get(struct audio_module_handle_private const *const handle, + struct audio_module_configuration *configuration) +{ + struct audio_module_handle *hdl = (struct audio_module_handle *)handle; + struct lc3_decoder_context *ctx = (struct lc3_decoder_context *)hdl->context; + struct lc3_decoder_configuration *config = + (struct lc3_decoder_configuration *)configuration; + memcpy(config, &ctx->config, sizeof(struct lc3_decoder_configuration)); + + /* Configure decoder */ + LOG_DBG("LC3 decode module %s configuration: %dHz %dbits (sample bits %d) %dus channel(s) " + "mapped as 0x%X", + hdl->name, config->sample_rate_hz, config->carried_bits_per_sample, + config->bits_per_sample, config->data_len_us, config->locations); + + return 0; +} + +/** + * @brief This processes the input audio data into the output audio data. + * + * @param handle A handle to this audio module's instance + * @param audio_data_in Pointer to the input audio data or NULL for an input module + * @param audio_data_out Pointer to the output audio data or NULL for an output module + * + * @return 0 if successful, error value + */ +static int lc3_dec_t2_data_process(struct audio_module_handle_private *handle, + struct audio_data const *const audio_data_in, + struct audio_data *audio_data_out) +{ + int ret; + struct audio_module_handle *hdl = (struct audio_module_handle *)handle; + struct lc3_decoder_context *ctx = (struct lc3_decoder_context *)hdl->context; + LC3DecoderHandle_t *dec_handles = (LC3DecoderHandle_t *)&ctx->lc3_dec_channel[0]; + LC3BFI_t frame_status; + uint16_t plc_counter = 0; + size_t data_out_size; + size_t session_in_size; + uint8_t *data_in; + uint8_t *data_out; + uint8_t temp_pcm[LC3_DECODER_PCM_NUM_BYTES_MONO]; + int8_t number_channels; + + LOG_DBG("LC3 decoder module %s start process", hdl->name); + + if (audio_data_in->meta.data_coding != LC3) { + LOG_DBG("LC3 decoder module %s has incorrect input data type: %d", hdl->name, + audio_data_in->meta.data_coding); + return -EINVAL; + } + + if (ctx->config.locations != audio_data_in->meta.locations) { + LOG_DBG("LC3 decoder module %s has incorrect channel map in the new audio_data: %d", + hdl->name, audio_data_in->meta.locations); + return -EINVAL; + } + + if (audio_data_in->meta.bad_data) { + frame_status = BadFrame; + } else { + frame_status = GoodFrame; + ctx->plc_count = 0; + } + + audio_module_number_channels_calculate(ctx->config.locations, &number_channels); + + if (audio_data_in->data_size) { + session_in_size = audio_data_in->data_size / number_channels; + if (session_in_size < ctx->coded_bytes_req) { + LOG_ERR("Too few coded bytes to decode. Bytes required- %d, input " + "framesize " + "is %d", + ctx->coded_bytes_req, session_in_size); + return -EINVAL; + } + } else { + session_in_size = 0; + } + + if (audio_data_out->data_size < ctx->sample_frame_bytes * number_channels) { + LOG_ERR("Output buffer too small. Bytes required %d, output buffer is %d", + (ctx->sample_frame_bytes * number_channels), audio_data_out->data_size); + return -EINVAL; + } + + if (ctx->config.interleaved) { + data_out = temp_pcm; + } else { + data_out = (uint8_t *)audio_data_out->data; + } + + data_out_size = 0; + + /* Should be able to decode only the channel(s) of interest here. + * These will be put in the first channel or channels and the location + * will indicate which channel(s) they are. Prior to playout (I2S or TDM) + * all other channels can be zeroed. + */ + for (uint8_t chan = 0; chan < number_channels; chan++) { + data_in = (uint8_t *)audio_data_in->data + (session_in_size * chan); + + LC3DecodeInput_t LC3DecodeInput = { + .inputData = data_in, .inputDataLength = session_in_size, frame_status}; + LC3DecodeOutput_t LC3DecodeOutput = {.PCMData = data_out, + .PCMDataLength = ctx->sample_frame_bytes, + .bytesWritten = 0, + .PLCCounter = plc_counter}; + + if (dec_handles[chan] == NULL) { + LOG_DBG("LC3 dec ch: %d is not initialized", chan); + return -EINVAL; + } + + ret = LC3DecodeSessionData(dec_handles[chan], &LC3DecodeInput, &LC3DecodeOutput); + if (ret) { + /* handle error */ + LOG_DBG("Error in decoder, ret: %d", ret); + return ret; + } + + if (LC3DecodeOutput.bytesWritten != ctx->sample_frame_bytes) { + /* handle error */ + LOG_DBG("Error in decoder, output incorrect size %d when should " + "be %d", + LC3DecodeOutput.bytesWritten, ctx->sample_frame_bytes); + + /* Clear this channel as it is not correct */ + memset(data_out, 0, LC3DecodeOutput.bytesWritten); + + return -EFAULT; + } + + /* Could also perform the resampling here, while we are operating + * on this area of memory. + */ + if (ctx->config.interleaved) { + ret = interleave(LC3DecodeOutput.PCMData, LC3DecodeOutput.bytesWritten, + chan, ctx->config.bits_per_sample, audio_data_out->data, + audio_data_out->data_size, number_channels); + + if (ret) { + LOG_DBG("Failed to interleave output"); + return ret; + } + + LOG_DBG("Completed decoders PCM interleaving for ch: %d", chan); + } else { + data_out += LC3DecodeOutput.bytesWritten; + } + + data_out_size += LC3DecodeOutput.bytesWritten; + + LOG_DBG("Completed LC3 decode of ch: %d", chan); + } + + ctx->plc_count = plc_counter; + + audio_data_out->data_size = data_out_size; + + return 0; +} + +/** + * @brief Table of the LC3 decoder module functions. + */ +struct audio_module_functions lc3_dec_t2_functions = { + /** + * @brief Function to an open the LC3 decoder module. + */ + .open = lc3_dec_t2_open, + + /** + * @brief Function to close the LC3 decoder module. + */ + .close = lc3_dec_t2_close, + + /** + * @brief Function to set the configuration of the LC3 decoder module. + */ + .configuration_set = lc3_dec_t2_configuration_set, + + /** + * @brief Function to get the configuration of the LC3 decoder module. + */ + .configuration_get = lc3_dec_t2_configuration_get, + + /** + * @brief Start a module processing data. + */ + .start = NULL, + + /** + * @brief Pause a module processing data. + */ + .stop = NULL, + + /** + * @brief The core data processing function in the LC3 decoder module. + */ + .data_process = lc3_dec_t2_data_process}; + +/** + * @brief The set-up parameters for the LC3 decoder. + */ +struct audio_module_description lc3_dec_t2_dept = { + .name = "LC3 Decoder (T2)", + .type = AUDIO_MODULE_TYPE_IN_OUT, + .functions = (struct audio_module_functions *)&lc3_dec_t2_functions}; + +/** + * @brief A private pointer to the LC3 decoder set-up parameters. + */ +struct audio_module_description *lc3_decoder_description = &lc3_dec_t2_dept; diff --git a/tests/subsys/audio_modules/lc3_module/CMakeLists.txt b/tests/subsys/audio_modules/lc3_module/CMakeLists.txt new file mode 100644 index 000000000000..06facc33b826 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project("Audio LC3 module") + +target_sources(app PRIVATE + src/main.c + src/lc3_test_fakes.c + src/sweep21ms_16b48khz_mono_lc3.c + src/sweep21ms_16b48khz_mono_wav.c + src/lc3_test_common.c + src/decoder_test.c +) + +zephyr_include_directories( + ${NRFXLIB_DIR}/lc3/codec/inc + ${NRFXLIB_DIR}/lc3/platform/os/inc + ${NRFXLIB_DIR}/lc3/platform/os/baremetal/inc +) + +target_include_directories(app PRIVATE + ${ZEPHYR_NRF_MODULE_DIR}/include/audio_module + ${ZEPHYR_NRF_MODULE_DIR}/subsys/audio_modules/lc3/t2/decoder +) diff --git a/tests/subsys/audio_modules/lc3_module/prj.conf b/tests/subsys/audio_modules/lc3_module/prj.conf new file mode 100644 index 000000000000..003459cf632f --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/prj.conf @@ -0,0 +1,22 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_ZTEST=y +CONFIG_ZTEST_STACK_SIZE=8000 +CONFIG_IRQ_OFFLOAD=y +CONFIG_DATA_FIFO=y +CONFIG_AUDIO_MODULE=y +CONFIG_AUDIO_MODULE_T2_LC3_DECODER_TEST=y +CONFIG_AUDIO_MODULE_T2_LC3_DECODER=y + +# Added large stack sizes. Can be optimized. +CONFIG_MAIN_STACK_SIZE=18000 + +# Decoder settings +CONFIG_LC3_DECODER_FRAME_DURATION_10_MS=y +CONFIG_LC3_DECODER_SAMPLE_RATE_48000_HZ=y +CONFIG_LC3_DECODER_BIT_DEPTH_16=y + +CONFIG_STACK_SENTINEL=y diff --git a/tests/subsys/audio_modules/lc3_module/src/decoder_test.c b/tests/subsys/audio_modules/lc3_module/src/decoder_test.c new file mode 100644 index 000000000000..feae866c1f20 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/decoder_test.c @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#include "lc3_test_fakes.h" +#include "audio_module.h" +#include "lc3_test_common.h" +#include "lc3_decoder.h" +#include "sweep21ms_16b48khz_mono_lc3.h" +#include "sweep21ms_16b48khz_mono_wav.h" + +#define TEST_LC3_DECODER_MSG_QUEUE_SIZE (4) +#define TEST_LC3_DECODER_DATA_OBJECTS_NUM (2) +#define TEST_LC3_DECODER_MODULES_NUM (TEST_AUDIO_CHANNELS_MAX) +#define TEST_LC3_DECODER_MSG_SIZE (sizeof(struct audio_module_message)) +#define TEST_LC3_DECODER_AUDIO_DATA_ITEMS_NUM (4) + +enum test_lc3_decoder_module_id { + TEST_MODULE_ID_DECODER_1 = 0, + TEST_MODULE_ID_DECODER_2, + TEST_MODULE_ID_DECODER_NUM +}; + +static struct audio_module_handle handle[TEST_LC3_DECODER_MODULES_NUM]; +static struct lc3_decoder_context decoder_ctx[TEST_LC3_DECODER_MODULES_NUM]; + +K_THREAD_STACK_ARRAY_DEFINE(lc3_dec_thread_stack, TEST_LC3_DECODER_MODULES_NUM, + CONFIG_LC3_DECODER_STACK_SIZE); +DATA_FIFO_DEFINE(lc3_dec_msg_fifo_tx, TEST_LC3_DECODER_MSG_QUEUE_SIZE, TEST_LC3_DECODER_MSG_SIZE); +DATA_FIFO_DEFINE(lc3_dec_msg_fifo_rx, TEST_LC3_DECODER_MSG_QUEUE_SIZE, TEST_LC3_DECODER_MSG_SIZE); +DATA_FIFO_DEFINE(lc3_dec_msg_fifo_tx1, TEST_LC3_DECODER_MSG_QUEUE_SIZE, TEST_LC3_DECODER_MSG_SIZE); +DATA_FIFO_DEFINE(lc3_dec_msg_fifo_rx1, TEST_LC3_DECODER_MSG_QUEUE_SIZE, TEST_LC3_DECODER_MSG_SIZE); + +static struct data_fifo *lc3_dec_msg_fifo_tx_array[] = {&lc3_dec_msg_fifo_tx, + &lc3_dec_msg_fifo_tx1}; +static struct data_fifo *lc3_dec_msg_fifo_rx_array[] = {&lc3_dec_msg_fifo_rx, + &lc3_dec_msg_fifo_rx1}; + +static char audio_data_memory[TEST_DEC_MULTI_BUF_SIZE * TEST_LC3_DECODER_DATA_OBJECTS_NUM]; +static struct k_mem_slab audio_data_slab; + +static struct audio_data audio_data = {.meta = {.data_coding = LC3, + .data_len_us = TEST_LC3_FRAME_SIZE_US, + .sample_rate_hz = TEST_PCM_SAMPLE_RATE, + .bits_per_sample = TEST_PCM_BIT_DEPTH, + .carried_bits_per_sample = TEST_PCM_BIT_DEPTH, + .reference_ts_us = 0, + .data_rx_ts_us = 0, + .bad_data = false}}; + +static void test_lc3_decoder_mono_multi_init(uint32_t locations) +{ + int ret; + struct audio_module_parameters test_decoder_param; + struct lc3_decoder_configuration test_decoder_config; + uint8_t number_channels; + + for (int i = 0; i < TEST_LC3_DECODER_MODULES_NUM; i++) { + memset(&handle[i], 0, sizeof(struct audio_module_handle)); + } + + audio_module_number_channels_calculate(locations, &number_channels); + + ret = k_mem_slab_init(&audio_data_slab, &audio_data_memory[0], + TEST_DEC_MONO_BUF_SIZE * number_channels, + TEST_LC3_DECODER_DATA_OBJECTS_NUM); + zassert_equal(ret, 0, "Failed to allocate the data slab: ret = %d", ret); + + test_decoder_config.sample_rate_hz = TEST_PCM_SAMPLE_RATE; + test_decoder_config.bits_per_sample = TEST_SAMPLE_BIT_DEPTH; + test_decoder_config.carried_bits_per_sample = TEST_PCM_BIT_DEPTH; + test_decoder_config.data_len_us = TEST_LC3_FRAME_SIZE_US; + test_decoder_config.interleaved = false; + test_decoder_config.bitrate_bps_max = TEST_LC3_BITRATE; + + for (int i = 0; i < number_channels; i++) { + test_decoder_config.locations = 1 << i; + + test_decoder_param.description = lc3_decoder_description; + test_decoder_param.thread.stack_size = CONFIG_LC3_DECODER_STACK_SIZE; + test_decoder_param.thread.priority = CONFIG_LC3_DECODER_THREAD_PRIO; + test_decoder_param.thread.data_slab = &audio_data_slab; + test_decoder_param.thread.data_size = TEST_DEC_MONO_BUF_SIZE; + test_decoder_param.thread.stack = lc3_dec_thread_stack[i], + test_decoder_param.thread.msg_rx = lc3_dec_msg_fifo_rx_array[i]; + test_decoder_param.thread.msg_tx = lc3_dec_msg_fifo_tx_array[i]; + + ret = audio_module_open(&test_decoder_param, + (struct audio_module_configuration *)&test_decoder_config, + "Dec Multi-channel", + (struct audio_module_context *)&decoder_ctx[i], &handle[i]); + zassert_equal(ret, 0, "Decoder left module open did not return zero"); + zassert_equal(LC3Initialize_fake.call_count, 1, + "Failed to call LC3 initialize %d times", + LC3Initialize_fake.call_count); + zassert_equal(LC3DecodeSessionClose_fake.call_count, 0, + "Called LC3 close decoder session %d times", + LC3DecodeSessionClose_fake.call_count); + zassert_equal(LC3BitstreamBuffersize_fake.call_count, (i + 1), + "Failed to call LC3 get buffer size %d times", + LC3BitstreamBuffersize_fake.call_count); + zassert_equal(LC3DecodeSessionOpen_fake.call_count, (i + 1), + "Failed to call LC3 open decoder session %d times", + LC3DecodeSessionOpen_fake.call_count); + + ret = audio_module_connect(&handle[i], NULL, true); + zassert_equal(ret, 0, "Decoder connect did not return zero"); + + ret = audio_module_start(&handle[i]); + zassert_equal(ret, 0, "Decoder left module start did not return zero"); + } +} + +static void test_lc3_decoder_multi_init(bool pcm_format, uint32_t locations) +{ + int ret; + struct audio_module_parameters test_decoder_param; + struct lc3_decoder_configuration test_decoder_config; + uint8_t number_channels; + + for (int i = 0; i < TEST_LC3_DECODER_MODULES_NUM; i++) { + memset(&handle[i], 0, sizeof(struct audio_module_handle)); + } + + audio_module_number_channels_calculate(locations, &number_channels); + + ret = k_mem_slab_init(&audio_data_slab, &audio_data_memory[0], + TEST_DEC_MONO_BUF_SIZE * number_channels, + TEST_LC3_DECODER_DATA_OBJECTS_NUM); + zassert_equal(ret, 0, "Failed to allocate the data slab: ret = %d", ret); + + test_decoder_config.sample_rate_hz = TEST_PCM_SAMPLE_RATE; + test_decoder_config.bits_per_sample = TEST_SAMPLE_BIT_DEPTH; + test_decoder_config.carried_bits_per_sample = TEST_PCM_BIT_DEPTH; + test_decoder_config.data_len_us = TEST_LC3_FRAME_SIZE_US; + test_decoder_config.interleaved = pcm_format; + test_decoder_config.locations = locations; + test_decoder_config.bitrate_bps_max = TEST_LC3_BITRATE; + + test_decoder_param.description = lc3_decoder_description; + test_decoder_param.thread.stack_size = CONFIG_LC3_DECODER_STACK_SIZE; + test_decoder_param.thread.priority = CONFIG_LC3_DECODER_THREAD_PRIO; + test_decoder_param.thread.data_slab = &audio_data_slab; + test_decoder_param.thread.data_size = TEST_DEC_MONO_BUF_SIZE * number_channels; + test_decoder_param.thread.stack = lc3_dec_thread_stack[0], + test_decoder_param.thread.msg_rx = lc3_dec_msg_fifo_rx_array[0]; + test_decoder_param.thread.msg_tx = lc3_dec_msg_fifo_tx_array[0]; + + ret = audio_module_open( + &test_decoder_param, (struct audio_module_configuration *)&test_decoder_config, + "Dec Multi-channel", (struct audio_module_context *)&decoder_ctx[0], &handle[0]); + zassert_equal(ret, 0, "Decoder left module open did not return zero"); + zassert_equal(LC3Initialize_fake.call_count, 1, "Failed to call LC3 initialize %d times", + LC3Initialize_fake.call_count); + zassert_equal(LC3DecodeSessionClose_fake.call_count, 0, + "Called LC3 close decoder session %d times", + LC3DecodeSessionClose_fake.call_count); + zassert_equal(LC3BitstreamBuffersize_fake.call_count, 1, + "Failed to call LC3 get buffer size %d times", + LC3BitstreamBuffersize_fake.call_count); + zassert_equal(LC3DecodeSessionOpen_fake.call_count, number_channels, + "Failed to call LC3 open decoder session %d times", + LC3DecodeSessionOpen_fake.call_count); + + ret = audio_module_connect(&handle[0], NULL, true); + zassert_equal(ret, 0, "Decoder connect did not return zero"); + + ret = audio_module_start(&handle[0]); + zassert_equal(ret, 0, "Decoder left module start did not return zero"); +} + +ZTEST(suite_lc3_decoder_functional, test_lc3_decode_mono) +{ + int ret; + int dec_call_count = 0; + uint8_t *data_in = &lc3_mono[0]; + uint8_t *data_ref = (uint8_t *)&wav_mono[0]; + uint8_t pcm_out[TEST_DEC_MONO_BUF_SIZE]; + struct audio_data audio_data_tx, audio_data_rx; + + /* Fake internal empty data FIFO success */ + ref_in = (uint8_t *)&wav_mono[0]; + ref_in_size = sizeof(wav_mono); + ref_in_read_size = TEST_DEC_MONO_BUF_SIZE; + LC3Initialize_fake.custom_fake = fake_LC3Initialize__succeeds; + LC3DecodeSessionClose_fake.custom_fake = fake_LC3DecodeSessionClose__succeeds; + LC3BitstreamBuffersize_fake.custom_fake = fake_LC3BitstreamBuffersize__succeeds; + LC3DecodeSessionOpen_fake.custom_fake = fake_LC3DecodeSessionOpen__succeeds; + LC3DecodeSessionData_fake.custom_fake = fake_LC3DecodeSessionData__succeeds; + LC3Deinitialize_fake.custom_fake = fake_LC3Deinitialize__succeeds; + LC3PCMBuffersize_fake.custom_fake = fake_LC3PCMBuffersize__succeeds; + + lc3_initialize(TEST_LC3_FRAME_SIZE_US); + zassert_equal(LC3Initialize_fake.call_count, 1, "Failed, called LC3 initialize %d times", + LC3Initialize_fake.call_count); + + test_lc3_decoder_mono_multi_init(TEST_AUDIO_MONO_LEFT_LOCATIONS); + + do { + memcpy(&audio_data_tx, &audio_data, sizeof(struct audio_data)); + memcpy(&audio_data_rx, &audio_data, sizeof(struct audio_data)); + + audio_data_tx.data = (void *)data_in; + audio_data_tx.data_size = TEST_ENC_MONO_BUF_SIZE; + audio_data_tx.meta.data_coding = LC3; + audio_data_tx.meta.locations = TEST_AUDIO_MONO_LEFT_LOCATIONS; + + audio_data_rx.data = pcm_out; + audio_data_rx.data_size = TEST_DEC_MONO_BUF_SIZE; + audio_data_rx.meta.data_coding = PCM; + + ret = audio_module_data_tx_rx(&handle[TEST_MODULE_ID_DECODER_1], + &handle[TEST_MODULE_ID_DECODER_1], &audio_data_tx, + &audio_data_rx, K_FOREVER); + zassert_equal(ret, 0, "Data TX-RX did not return zero for the decoder: %d", ret); + zassert_equal(audio_data_rx.data_size, TEST_DEC_MONO_BUF_SIZE, + "Decoded number of bytes incorrect: %d", audio_data_rx.data_size); + zassert_mem_equal(&pcm_out[0], data_ref, TEST_DEC_MONO_BUF_SIZE, + "Decoded PCM data does not match reference PCM data"); + zassert_equal(LC3DecodeSessionData_fake.call_count, dec_call_count + 1, + "Failed to call LC3 decode %d times", + LC3DecodeSessionData_fake.call_count); + + data_in += TEST_DEC_MONO_BUF_SIZE; + data_ref += TEST_DEC_MONO_BUF_SIZE; + + dec_call_count += 1; + + } while ((dec_call_count < (LC3_MONO_SIZE / TEST_ENC_MONO_BUF_SIZE) && + dec_call_count < (WAV_MONO_SIZE / TEST_DEC_MONO_BUF_SAMPLES))); + + ret = audio_module_stop(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to stop module did not return zero: ret = %d", ret); + + ret = audio_module_close(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to close module did not return zero: ret = %d", ret); + + lc3_deinitialize(); + zassert_equal(LC3Deinitialize_fake.call_count, 1, + "Failed, called LC3 deinitialize %d times", LC3Deinitialize_fake.call_count); +} + +ZTEST(suite_lc3_decoder_functional, test_lc3_decode_dual_mono) +{ + int ret; + int dec_call_count = 0; + uint8_t *data_l_in = (uint8_t *)&lc3_mono[0]; + uint8_t *data_r_in = (uint8_t *)&lc3_mono[0]; + uint8_t *data_l_ref = (uint8_t *)&wav_mono[0]; + uint8_t *data_r_ref = (uint8_t *)&wav_mono[0]; + uint8_t data_l[TEST_DEC_MONO_BUF_SIZE], data_r[TEST_DEC_MONO_BUF_SIZE]; + struct audio_data audio_data_l_tx, audio_data_l_rx; + struct audio_data audio_data_r_tx, audio_data_r_rx; + + /* Fake internal empty data FIFO success */ + ref_in = (uint8_t *)&wav_mono[0]; + ref_in_size = sizeof(wav_mono); + ref_in_read_size = TEST_DEC_MONO_BUF_SIZE; + LC3Initialize_fake.custom_fake = fake_LC3Initialize__succeeds; + LC3DecodeSessionClose_fake.custom_fake = fake_LC3DecodeSessionClose__succeeds; + LC3BitstreamBuffersize_fake.custom_fake = fake_LC3BitstreamBuffersize__succeeds; + LC3DecodeSessionOpen_fake.custom_fake = fake_LC3DecodeSessionOpen__succeeds; + LC3DecodeSessionData_fake.custom_fake = fake_LC3DecodeSessionData__succeeds; + LC3Deinitialize_fake.custom_fake = fake_LC3Deinitialize__succeeds; + LC3PCMBuffersize_fake.custom_fake = fake_LC3PCMBuffersize__succeeds; + + lc3_initialize(TEST_LC3_FRAME_SIZE_US); + zassert_equal(LC3Initialize_fake.call_count, 1, "Failed, called LC3 initialize %d times", + LC3Initialize_fake.call_count); + + test_lc3_decoder_mono_multi_init(TEST_AUDIO_MONO_LEFT_LOCATIONS + + TEST_AUDIO_MONO_RIGHT_LOCATIONS); + + do { + memcpy(&audio_data_l_tx, &audio_data, sizeof(struct audio_data)); + memcpy(&audio_data_l_rx, &audio_data, sizeof(struct audio_data)); + + audio_data_l_tx.data = (void *)data_l_in; + audio_data_l_tx.data_size = TEST_ENC_MONO_BUF_SIZE; + audio_data_l_tx.meta.data_coding = LC3; + audio_data_l_tx.meta.locations = TEST_AUDIO_MONO_LEFT_LOCATIONS; + + memset(&data_l[0], 0, TEST_DEC_MONO_BUF_SIZE); + audio_data_l_rx.data = (void *)&data_l[0]; + audio_data_l_rx.data_size = TEST_DEC_MONO_BUF_SIZE; + + ret = audio_module_data_tx_rx(&handle[TEST_MODULE_ID_DECODER_1], + &handle[TEST_MODULE_ID_DECODER_1], &audio_data_l_tx, + &audio_data_l_rx, K_FOREVER); + zassert_equal(ret, 0, "Data TX-RX did not return zero for left decoder: %d", ret); + zassert_equal(audio_data_l_rx.data_size, TEST_DEC_MONO_BUF_SIZE, + "Decoded number of bytes for right incorrect: %d", + audio_data_l_rx.data_size); + zassert_mem_equal(&data_l[0], data_l_ref, TEST_DEC_MONO_BUF_SIZE, + "Decoded left PCM data does not match reference PCM data"); + zassert_equal(LC3DecodeSessionData_fake.call_count, dec_call_count + 1, + "Failed to call LC3 decode %d times", + LC3DecodeSessionData_fake.call_count); + + data_l_in += TEST_ENC_MONO_BUF_SIZE; + data_l_ref += TEST_DEC_MONO_BUF_SIZE; + + dec_call_count += 1; + + memcpy(&audio_data_r_tx, &audio_data, sizeof(struct audio_data)); + memcpy(&audio_data_r_rx, &audio_data, sizeof(struct audio_data)); + + audio_data_r_tx.data = (void *)data_r_in; + audio_data_r_tx.data_size = TEST_ENC_MONO_BUF_SIZE; + audio_data_r_tx.meta.data_coding = LC3; + audio_data_r_tx.meta.locations = TEST_AUDIO_MONO_RIGHT_LOCATIONS; + + memset(&data_r[0], 0, TEST_DEC_MONO_BUF_SIZE); + audio_data_r_rx.data = (void *)&data_r[0]; + audio_data_r_rx.data_size = TEST_DEC_MONO_BUF_SIZE; + + ret = audio_module_data_tx_rx(&handle[TEST_MODULE_ID_DECODER_2], + &handle[TEST_MODULE_ID_DECODER_2], &audio_data_r_tx, + &audio_data_r_rx, K_FOREVER); + zassert_equal(ret, 0, "Data TX-RX did not return zero for right decoder: %d", ret); + zassert_equal(audio_data_r_rx.data_size, TEST_DEC_MONO_BUF_SIZE, + "Decoded number of bytes for left incorrect: %d", + audio_data_r_rx.data_size); + zassert_mem_equal(&data_r[0], data_r_ref, TEST_DEC_MONO_BUF_SIZE, + "Decoded right PCM data does not match reference PCM data"); + zassert_equal(LC3DecodeSessionData_fake.call_count, dec_call_count + 1, + "Failed to call LC3 decode %d times", + LC3DecodeSessionData_fake.call_count); + + data_r_in += TEST_ENC_MONO_BUF_SIZE; + data_r_ref += TEST_DEC_MONO_BUF_SIZE; + + dec_call_count += 1; + + } while (((dec_call_count >> 1) < (LC3_MONO_SIZE / TEST_ENC_MONO_BUF_SIZE) && + (dec_call_count >> 1) < (WAV_MONO_SIZE / TEST_DEC_MONO_BUF_SAMPLES))); + + ret = audio_module_stop(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to stop module did not return zero: ret = %d", ret); + ret = audio_module_stop(&handle[TEST_MODULE_ID_DECODER_2]); + zassert_equal(ret, 0, "Failed to stop module did not return zero: ret = %d", ret); + + ret = audio_module_close(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to close module did not return zero: ret = %d", ret); + ret = audio_module_close(&handle[TEST_MODULE_ID_DECODER_2]); + zassert_equal(ret, 0, "Failed to close module did not return zero: ret = %d", ret); + + lc3_deinitialize(); + zassert_equal(LC3Deinitialize_fake.call_count, 1, + "Failed, called LC3 deinitialize %d times", LC3Deinitialize_fake.call_count); +} + +ZTEST(suite_lc3_decoder_functional, test_lc3_decode_multi_deint) +{ + int ret; + int dec_call_count = 0; + uint8_t *data_source = (uint8_t *)&lc3_mono[0]; + uint8_t *data_reference = (uint8_t *)&wav_mono[0]; + uint8_t data_in[TEST_ENC_STEREO_BUF_SIZE]; + uint8_t data_out[TEST_DEC_STEREO_BUF_SIZE]; + uint8_t data_ref[TEST_DEC_STEREO_BUF_SIZE]; + struct audio_data audio_data_tx, audio_data_rx; + + /* Fake internal empty data FIFO success */ + ref_in = (uint8_t *)&wav_mono[0]; + ref_in_size = sizeof(wav_mono); + ref_in_read_size = TEST_DEC_MONO_BUF_SIZE; + LC3Initialize_fake.custom_fake = fake_LC3Initialize__succeeds; + LC3DecodeSessionClose_fake.custom_fake = fake_LC3DecodeSessionClose__succeeds; + LC3BitstreamBuffersize_fake.custom_fake = fake_LC3BitstreamBuffersize__succeeds; + LC3DecodeSessionOpen_fake.custom_fake = fake_LC3DecodeSessionOpen__succeeds; + LC3DecodeSessionData_fake.custom_fake = fake_LC3DecodeSessionData__succeeds; + LC3Deinitialize_fake.custom_fake = fake_LC3Deinitialize__succeeds; + LC3PCMBuffersize_fake.custom_fake = fake_LC3PCMBuffersize__succeeds; + + lc3_initialize(TEST_LC3_FRAME_SIZE_US); + zassert_equal(LC3Initialize_fake.call_count, 1, "Failed to call LC3 initialize %d times", + LC3Initialize_fake.call_count); + + test_lc3_decoder_multi_init(false, TEST_AUDIO_STEREO_LOCATIONS); + + do { + memcpy(&data_in[0], data_source, TEST_ENC_MONO_BUF_SIZE); + memcpy(&data_in[TEST_ENC_MONO_BUF_SIZE], data_source, TEST_ENC_MONO_BUF_SIZE); + memcpy(&data_ref[0], data_reference, TEST_DEC_MONO_BUF_SIZE); + memcpy(&data_ref[TEST_DEC_MONO_BUF_SIZE], data_reference, TEST_DEC_MONO_BUF_SIZE); + + memcpy(&audio_data_tx, &audio_data, sizeof(struct audio_data)); + memcpy(&audio_data_rx, &audio_data, sizeof(struct audio_data)); + + audio_data_tx.data = &data_in[0]; + audio_data_tx.data_size = TEST_ENC_STEREO_BUF_SIZE; + audio_data_tx.meta.data_coding = LC3; + audio_data_tx.meta.locations = TEST_AUDIO_STEREO_LOCATIONS; + + audio_data_rx.data = (void *)&data_out[0]; + audio_data_rx.data_size = TEST_DEC_STEREO_BUF_SIZE; + + ret = audio_module_data_tx_rx(&handle[0], &handle[0], &audio_data_tx, + &audio_data_rx, K_FOREVER); + zassert_equal(ret, 0, "Data TX-RX did not return zero for left decoder: %d", ret); + zassert_equal(audio_data_rx.data_size, TEST_DEC_STEREO_BUF_SIZE, + "Decoded number of bytes incorrect: %d", audio_data_rx.data_size); + zassert_mem_equal(&data_out[0], &data_ref[0], TEST_DEC_STEREO_BUF_SIZE, + "Decoded PCM data does not match reference PCM data"); + zassert_equal(LC3DecodeSessionData_fake.call_count, dec_call_count + 2, + "Failed to call LC3 decode %d times", + LC3DecodeSessionData_fake.call_count); + + data_source += TEST_ENC_MONO_BUF_SIZE; + data_reference += TEST_DEC_MONO_BUF_SIZE; + + dec_call_count += 2; + + } while (((dec_call_count >> 1) < (LC3_MONO_SIZE / TEST_ENC_MONO_BUF_SIZE) && + (dec_call_count >> 1) < (WAV_MONO_SIZE / TEST_DEC_MONO_BUF_SAMPLES))); + + ret = audio_module_stop(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to stop module did not return zero: ret = %d", ret); + + ret = audio_module_close(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to close module did not return zero: ret = %d", ret); + + lc3_deinitialize(); + zassert_equal(LC3Deinitialize_fake.call_count, 1, + "Failed to call LC3 deinitialize %d times", LC3Deinitialize_fake.call_count); +} + +ZTEST(suite_lc3_decoder_functional, test_lc3_decode_multi_int) +{ + int ret; + int dec_call_count = 0; + uint8_t *data_source = (uint8_t *)&lc3_mono[0]; + uint8_t *data_reference = (uint8_t *)&wav_mono[0]; + uint8_t data_in[TEST_ENC_STEREO_BUF_SIZE]; + uint8_t data_out[TEST_DEC_STEREO_BUF_SIZE]; + uint8_t data_ref[TEST_DEC_STEREO_BUF_SIZE]; + uint8_t *ref; + struct audio_data audio_data_tx, audio_data_rx; + size_t input_remaining = sizeof(lc3_mono); + size_t ref_remaining = sizeof(wav_mono); + + /* Fake internal empty data FIFO success */ + ref_in = (uint8_t *)&wav_mono[0]; + ref_in_size = sizeof(wav_mono); + ref_in_read_size = TEST_DEC_MONO_BUF_SIZE; + LC3Initialize_fake.custom_fake = fake_LC3Initialize__succeeds; + LC3DecodeSessionClose_fake.custom_fake = fake_LC3DecodeSessionClose__succeeds; + LC3BitstreamBuffersize_fake.custom_fake = fake_LC3BitstreamBuffersize__succeeds; + LC3DecodeSessionOpen_fake.custom_fake = fake_LC3DecodeSessionOpen__succeeds; + LC3DecodeSessionData_fake.custom_fake = fake_LC3DecodeSessionData__succeeds; + LC3Deinitialize_fake.custom_fake = fake_LC3Deinitialize__succeeds; + LC3PCMBuffersize_fake.custom_fake = fake_LC3PCMBuffersize__succeeds; + + lc3_initialize(TEST_LC3_FRAME_SIZE_US); + zassert_equal(LC3Initialize_fake.call_count, 1, "Failed to call LC3 initialize %d times", + LC3Initialize_fake.call_count); + + test_lc3_decoder_multi_init(true, TEST_AUDIO_STEREO_LOCATIONS); + + while (input_remaining > TEST_ENC_MONO_BUF_SIZE && ref_remaining > TEST_DEC_MONO_BUF_SIZE) { + + memcpy(&data_in[0], data_source, TEST_ENC_MONO_BUF_SIZE); + memcpy(&data_in[TEST_ENC_MONO_BUF_SIZE], data_source, TEST_ENC_MONO_BUF_SIZE); + + /* Interleave the channel samples */ + ref = &data_ref[0]; + for (uint32_t i = 0; i < TEST_DEC_MONO_BUF_SIZE; + i += CONFIG_LC3_DECODER_BIT_DEPTH_OCTETS) { + for (uint8_t j = 0; j < TEST_AUDIO_CHANNELS_STEREO; j++) { + for (uint8_t k = 0; k < CONFIG_LC3_DECODER_BIT_DEPTH_OCTETS; k++) { + *ref++ = data_reference[k]; + } + } + + data_reference += CONFIG_LC3_DECODER_BIT_DEPTH_OCTETS; + } + + memcpy(&audio_data_tx, &audio_data, sizeof(struct audio_data)); + memcpy(&audio_data_rx, &audio_data, sizeof(struct audio_data)); + + audio_data_tx.data = &data_in[0]; + audio_data_tx.data_size = TEST_ENC_STEREO_BUF_SIZE; + audio_data_tx.meta.data_coding = LC3; + audio_data_tx.meta.locations = TEST_AUDIO_STEREO_LOCATIONS; + + audio_data_rx.data = (void *)&data_out[0]; + audio_data_rx.data_size = TEST_DEC_STEREO_BUF_SIZE; + + ret = audio_module_data_tx_rx(&handle[0], &handle[0], &audio_data_tx, + &audio_data_rx, K_FOREVER); + zassert_equal(ret, 0, "Data TX-RX did not return zero for left decoder: %d", ret); + zassert_equal(audio_data_rx.data_size, TEST_DEC_STEREO_BUF_SIZE, + "Decoded number of bytes incorrect: %d", audio_data_rx.data_size); + zassert_mem_equal(audio_data_rx.data, data_ref, TEST_DEC_STEREO_BUF_SIZE, + "Decoded PCM data does not match reference PCM data"); + zassert_equal(LC3DecodeSessionData_fake.call_count, dec_call_count + 2, + "Failed to call LC3 decode %d times", + LC3DecodeSessionData_fake.call_count); + + dec_call_count += 2; + + data_source += TEST_ENC_MONO_BUF_SIZE; + + input_remaining -= TEST_ENC_MONO_BUF_SIZE; + ref_remaining -= TEST_DEC_MONO_BUF_SIZE; + } + + ret = audio_module_stop(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to stop module did not return zero: ret = %d", ret); + + ret = audio_module_close(&handle[TEST_MODULE_ID_DECODER_1]); + zassert_equal(ret, 0, "Failed to close module did not return zero: ret = %d", ret); + + lc3_deinitialize(); + zassert_equal(LC3Deinitialize_fake.call_count, 1, + "Failed to call LC3 deinitialize %d times", LC3Deinitialize_fake.call_count); +} diff --git a/tests/subsys/audio_modules/lc3_module/src/lc3_test_common.c b/tests/subsys/audio_modules/lc3_module/src/lc3_test_common.c new file mode 100644 index 000000000000..7b0eaf25f2ea --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/lc3_test_common.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#include "lc3_test_fakes.h" +#include "audio_module.h" +#include "lc3_test_common.h" + +void lc3_initialize(int duration_us) +{ + int ret; + uint8_t enc_sample_rates = 0; + uint8_t dec_sample_rates = 0; + uint8_t unique_session = 0; + LC3FrameSize_t framesize; + + /* Set unique session to 0 for using the default sharing memory setting. + * + * This could lead to higher heap consumption, but is able to manipulate + * different sample rate setting between encoder/decoder. + */ + + /* Check supported sample rates for encoder */ + if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_8KHZ_SUPPORT)) { + enc_sample_rates |= LC3_SAMPLE_RATE_8_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_16KHZ_SUPPORT)) { + enc_sample_rates |= LC3_SAMPLE_RATE_16_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_24KHZ_SUPPORT)) { + enc_sample_rates |= LC3_SAMPLE_RATE_24_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_32KHZ_SUPPORT)) { + enc_sample_rates |= LC3_SAMPLE_RATE_32_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_441KHZ_SUPPORT)) { + enc_sample_rates |= LC3_SAMPLE_RATE_441_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_ENC_SAMPLE_RATE_48KHZ_SUPPORT)) { + enc_sample_rates |= LC3_SAMPLE_RATE_48_KHZ; + } + + /* Check supported sample rates for encoder */ + if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_8KHZ_SUPPORT)) { + dec_sample_rates |= LC3_SAMPLE_RATE_8_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_16KHZ_SUPPORT)) { + dec_sample_rates |= LC3_SAMPLE_RATE_16_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_24KHZ_SUPPORT)) { + dec_sample_rates |= LC3_SAMPLE_RATE_24_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_32KHZ_SUPPORT)) { + dec_sample_rates |= LC3_SAMPLE_RATE_32_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_441KHZ_SUPPORT)) { + dec_sample_rates |= LC3_SAMPLE_RATE_441_KHZ; + } + if (IS_ENABLED(CONFIG_LC3_DEC_SAMPLE_RATE_48KHZ_SUPPORT)) { + dec_sample_rates |= LC3_SAMPLE_RATE_48_KHZ; + } + + switch (duration_us) { + case 7500: + framesize = LC3FrameSize7_5Ms; + break; + case 10000: + framesize = LC3FrameSize10Ms; + break; + default: + return; + } + + ret = LC3Initialize(enc_sample_rates, dec_sample_rates, framesize, unique_session, NULL, + NULL); + zassert_equal(ret, 0, "LC3 initialize did not return zero"); +} + +void lc3_deinitialize(void) +{ + int ret; + + ret = LC3Deinitialize(); + zassert_equal(ret, 0, "LC3 codec failed initialization"); +} diff --git a/tests/subsys/audio_modules/lc3_module/src/lc3_test_common.h b/tests/subsys/audio_modules/lc3_module/src/lc3_test_common.h new file mode 100644 index 000000000000..6c886a0f72c4 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/lc3_test_common.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include "audio_module.h" + +#define TEST_AUDIO_CHANNELS_MONO 1 +#define TEST_AUDIO_CHANNELS_DUAL_MONO 2 +#define TEST_AUDIO_CHANNELS_STEREO 2 +#define TEST_AUDIO_CHANNELS_MAX 2 + +#define TEST_SAMPLE_BIT_DEPTH 16 +#define TEST_PCM_BIT_DEPTH 16 +#define TEST_DEC_MONO_BUF_SIZE 960 +#define TEST_DEC_MONO_BUF_SAMPLES (TEST_DEC_MONO_BUF_SIZE / (TEST_PCM_BIT_DEPTH / 8)) +#define TEST_DEC_STEREO_BUF_SIZE (TEST_DEC_MONO_BUF_SIZE * TEST_AUDIO_CHANNELS_STEREO) +#define TEST_DEC_STEREO_BUF_SAMPLES (TEST_DEC_STEREO_BUF_SIZE / (TEST_PCM_BIT_DEPTH / 8)) +#define TEST_DEC_MULTI_BUF_SIZE (TEST_DEC_MONO_BUF_SIZE * TEST_AUDIO_CHANNELS_MAX) +#define TEST_DEC_MULTI_BUF_SAMPLES (TEST_DEC_MULTI_BUF_SIZE / (TEST_PCM_BIT_DEPTH / 8)) + +#define TEST_ENC_MONO_BUF_SIZE 120 +#define TEST_ENC_STEREO_BUF_SIZE (TEST_ENC_MONO_BUF_SIZE * TEST_AUDIO_CHANNELS_STEREO) +#define TEST_ENC_MULTI_BUF_SIZE (TEST_ENC_MONO_BUF_SIZE * TEST_AUDIO_CHANNELS_MAX) + +#define TEST_PCM_SAMPLE_RATE 48000 +#define TEST_LC3_BITRATE 96000 +#define TEST_LC3_FRAME_SIZE_US 10000 + +#define TEST_AUDIO_MONO_LEFT_LOCATIONS (1 << AUDIO_CH_L) +#define TEST_AUDIO_MONO_RIGHT_LOCATIONS (1 << AUDIO_CH_R) +#define TEST_AUDIO_STEREO_LOCATIONS ((1 << AUDIO_CH_L) | (1 << AUDIO_CH_R)) +#define TEST_AUDIO_MULTI_CHANNEL_LOCATIONS ((1 << AUDIO_CH_L) | (1 << AUDIO_CH_R)) + +enum test_module_id { + TEST_MODULE_LC3_DECODER = 0, + TEST_MODULE_LC3_ENCODER, + TEST_MODULES_NUM +}; + +/** + * @brief Initialize the LC3 codec. + * + * @note This function is called only once and configures both + * the encoder and decoder. + * + * @param duration_us [in] The duration of a frame of LC3 in micro seconds. + */ +void lc3_initialize(int duration_us); + +/** + * @brief Uninitialize the LC3 codec. + * + * @note This function is called only once and unintializes both + * the encoder and decoder. + */ +void lc3_deinitialize(void); + +#endif /* _COMMON_H_ */ diff --git a/tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.c b/tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.c new file mode 100644 index 000000000000..422c60b3c161 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#include "LC3API.h" +#include "lc3_test_fakes.h" +#include "lc3_test_common.h" + +#include +LOG_MODULE_REGISTER(module_fakes, 4); + +#define TEST_ENC_SESSIONS_NUM (5) +#define TEST_DEC_SESSIONS_NUM (10) + +struct lc3_session { + bool session_init; + uint8_t *ref_data; + int ref_read_size; + int ref_remaining; +}; + +uint8_t *ref_in; +int ref_in_size; +int ref_in_read_size; + +static int enc_count; +static int dec_count; + +struct lc3_session dec_session_ctx[TEST_DEC_SESSIONS_NUM]; +struct lc3_session enc_session_ctx[TEST_ENC_SESSIONS_NUM]; + +/* + * Stubs are defined here, so that multiple .C files can share them + * without having linker issues. + */ +DEFINE_FFF_GLOBALS; + +DEFINE_FAKE_VALUE_FUNC(int32_t OSALCALL, LC3Initialize, uint8_t, uint8_t, LC3FrameSizeConfig_t, + uint8_t, uint8_t *, uint32_t *); +DEFINE_FAKE_VALUE_FUNC(int32_t OSALCALL, LC3Deinitialize); +DEFINE_FAKE_VALUE_FUNC(LC3EncoderHandle_t OSALCALL, LC3EncodeSessionOpen, uint16_t, uint8_t, + LC3FrameSize_t, uint8_t *, uint16_t *, int32_t *); +DEFINE_FAKE_VALUE_FUNC(int32_t OSALCALL, LC3EncodeSessionData, LC3EncoderHandle_t, + LC3EncodeInput_t *, LC3EncodeOutput_t *); +DEFINE_FAKE_VOID_FUNC1(LC3EncodeSessionClose, LC3EncoderHandle_t); +DEFINE_FAKE_VALUE_FUNC(LC3DecoderHandle_t, LC3DecodeSessionOpen, uint16_t, uint8_t, LC3FrameSize_t, + uint8_t *, uint16_t *, int32_t *); +DEFINE_FAKE_VALUE_FUNC(int32_t, LC3DecodeSessionData, LC3DecoderHandle_t, LC3DecodeInput_t *, + LC3DecodeOutput_t *); +DEFINE_FAKE_VOID_FUNC1(LC3DecodeSessionClose, LC3DecoderHandle_t); +DEFINE_FAKE_VALUE_FUNC(uint16_t, LC3BitstreamBuffersize, uint16_t, uint32_t, LC3FrameSize_t, + int32_t *); +DEFINE_FAKE_VALUE_FUNC(uint16_t, LC3PCMBuffersize, uint16_t, uint8_t, LC3FrameSize_t, int32_t *); + +/* Custom fakes implementation */ +int32_t fake_LC3Initialize__succeeds(uint8_t encoderSampleRates, uint8_t decoderSampleRates, + LC3FrameSizeConfig_t frameSizeConfig, uint8_t uniqueSessions, + uint8_t *buffer, uint32_t *bufferSize) +{ + ARG_UNUSED(encoderSampleRates); + ARG_UNUSED(decoderSampleRates); + ARG_UNUSED(frameSizeConfig); + ARG_UNUSED(uniqueSessions); + ARG_UNUSED(buffer); + ARG_UNUSED(bufferSize); + + enc_count = 0; + dec_count = 0; + + for (int i = 0; i < TEST_ENC_SESSIONS_NUM; i++) { + enc_session_ctx[i].session_init = false; + enc_session_ctx[i].ref_data = NULL; + enc_session_ctx[i].ref_read_size = 0; + enc_session_ctx[i].ref_remaining = 0; + } + + for (int i = 0; i < TEST_DEC_SESSIONS_NUM; i++) { + dec_session_ctx[i].session_init = false; + dec_session_ctx[i].ref_data = NULL; + dec_session_ctx[i].ref_read_size = 0; + dec_session_ctx[i].ref_remaining = 0; + } + + return 0; +} + +/* Custom fakes implementation */ +int32_t fake_LC3Initialize__fails(uint8_t encoderSampleRates, uint8_t decoderSampleRates, + LC3FrameSizeConfig_t frameSizeConfig, uint8_t uniqueSessions, + uint8_t *buffer, uint32_t *bufferSize) +{ + ARG_UNUSED(encoderSampleRates); + ARG_UNUSED(decoderSampleRates); + ARG_UNUSED(frameSizeConfig); + ARG_UNUSED(uniqueSessions); + ARG_UNUSED(buffer); + ARG_UNUSED(bufferSize); + + return -1; +} + +/* Custom fakes implementation */ +int32_t fake_LC3Deinitialize__succeeds(void) +{ + enc_count = 0; + dec_count = 0; + + for (int i = 0; i < TEST_ENC_SESSIONS_NUM; i++) { + enc_session_ctx[i].session_init = false; + enc_session_ctx[i].ref_data = NULL; + enc_session_ctx[i].ref_read_size = 0; + enc_session_ctx[i].ref_remaining = 0; + } + + for (int i = 0; i < TEST_DEC_SESSIONS_NUM; i++) { + dec_session_ctx[i].session_init = false; + dec_session_ctx[i].ref_data = NULL; + dec_session_ctx[i].ref_read_size = 0; + dec_session_ctx[i].ref_remaining = 0; + } + + return 0; +} + +/* Custom fakes implementation */ +int32_t fake_LC3Deinitialize__fails(void) +{ + return -1; +} + +/* Custom fakes implementation */ +LC3EncoderHandle_t fake_LC3EncodeSessionOpen__succeeds(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + ARG_UNUSED(bitsPerSample); + ARG_UNUSED(frameSize); + ARG_UNUSED(buffer); + ARG_UNUSED(bufferSize); + struct lc3_session *session = (struct lc3_session *)&enc_session_ctx[enc_count]; + + session->session_init = true; + session->ref_data = ref_in; + session->ref_read_size = ref_in_read_size; + session->ref_remaining = ref_in_size; + + enc_count++; + + *result = 0; + + return (void *)session; +} + +/* Custom fakes implementation */ +LC3EncoderHandle_t fake_LC3EncodeSessionOpen__fails(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + ARG_UNUSED(bitsPerSample); + ARG_UNUSED(frameSize); + ARG_UNUSED(buffer); + ARG_UNUSED(bufferSize); + + *result = -1; + + return NULL; +} + +int32_t fake_LC3EncodeSessionData__succeeds(LC3EncoderHandle_t encodeHandle, + LC3EncodeInput_t *encodeInput, + LC3EncodeOutput_t *encodeOutput) +{ + ARG_UNUSED(encodeInput); + struct lc3_session *session = (struct lc3_session *)encodeHandle; + uint8_t *data_out = (uint8_t *)encodeOutput->outputData; + uint8_t *ref_data = session->ref_data; + int size; + + if (session->ref_read_size > session->ref_remaining) { + size = session->ref_remaining; + session->ref_remaining = 0; + } else { + size = session->ref_read_size; + session->ref_remaining -= session->ref_read_size; + } + + for (int i = 0; i < size; i++) { + *data_out++ = *ref_data++; + } + + session->ref_data = ref_data; + + encodeOutput->bytesWritten = size; + + return 0; +} + +int32_t fake_LC3EncodeSessionData__fails(LC3EncoderHandle_t encodeHandle, + LC3EncodeInput_t *encodeInput, + LC3EncodeOutput_t *encodeOutput) +{ + ARG_UNUSED(encodeHandle); + ARG_UNUSED(encodeInput); + ARG_UNUSED(encodeOutput); + + return -1; +} + +void fake_LC3EncodeSessionClose__succeeds(LC3EncoderHandle_t encodeHandle) +{ + struct lc3_session *session = (struct lc3_session *)encodeHandle; + + session->session_init = false; + session->ref_data = NULL; + session->ref_read_size = 0; +} + +void fake_LC3EncodeSessionClose__fails(LC3EncoderHandle_t encodeHandle) +{ + ARG_UNUSED(encodeHandle); +} + +LC3DecoderHandle_t fake_LC3DecodeSessionOpen__succeeds(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + ARG_UNUSED(bitsPerSample); + ARG_UNUSED(frameSize); + ARG_UNUSED(buffer); + ARG_UNUSED(bufferSize); + struct lc3_session *session = (struct lc3_session *)&dec_session_ctx[dec_count]; + + session->session_init = true; + session->ref_data = ref_in; + session->ref_read_size = ref_in_read_size; + session->ref_remaining = ref_in_size; + + dec_count++; + + *result = 0; + + return (void *)session; +} + +LC3DecoderHandle_t fake_LC3DecodeSessionOpen__fails(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + ARG_UNUSED(bitsPerSample); + ARG_UNUSED(frameSize); + ARG_UNUSED(buffer); + ARG_UNUSED(bufferSize); + + *result = -1; + + return NULL; +} + +int32_t fake_LC3DecodeSessionData__succeeds(LC3DecoderHandle_t decodeHandle, + LC3DecodeInput_t *decodeInput, + LC3DecodeOutput_t *decodeOutput) +{ + ARG_UNUSED(decodeInput); + struct lc3_session *session = (struct lc3_session *)decodeHandle; + uint8_t *data_out = (uint8_t *)decodeOutput->PCMData; + uint8_t *ref_data = session->ref_data; + int size; + + if (session->ref_read_size > session->ref_remaining) { + size = session->ref_remaining; + session->ref_remaining = 0; + } else { + size = session->ref_read_size; + session->ref_remaining -= session->ref_read_size; + } + + for (int i = 0; i < size; i++) { + *data_out++ = *ref_data++; + } + + session->ref_data = ref_data; + + decodeOutput->bytesWritten = size; + + return 0; +} + +int32_t fake_LC3DecodeSessionData__fails(LC3DecoderHandle_t decodeHandle, + LC3DecodeInput_t *decodeInput, + LC3DecodeOutput_t *decodeOutput) +{ + ARG_UNUSED(decodeHandle); + ARG_UNUSED(decodeInput); + ARG_UNUSED(decodeOutput); + + return -1; +} + +void fake_LC3DecodeSessionClose__succeeds(LC3DecoderHandle_t decodeHandle) +{ + struct lc3_session *session = (struct lc3_session *)decodeHandle; + + session->session_init = false; + session->ref_data = NULL; + session->ref_read_size = 0; +} + +void fake_LC3DecodeSessionClose__fails(LC3DecoderHandle_t decodeHandle) +{ + ARG_UNUSED(decodeHandle); +} + +uint16_t fake_LC3BitstreamBuffersize__succeeds(uint16_t sampleRate, uint32_t maxBitRate, + LC3FrameSize_t frameSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + + *result = 0; + + return TEST_ENC_MONO_BUF_SIZE; +} + +uint16_t fake_LC3BitstreamBuffersize__fails(uint16_t sampleRate, uint32_t maxBitRate, + LC3FrameSize_t frameSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + ARG_UNUSED(maxBitRate); + ARG_UNUSED(frameSize); + + *result = -1; + + return 0; +} + +uint16_t fake_LC3PCMBuffersize__succeeds(uint16_t sampleRate, uint8_t bitDepth, + LC3FrameSize_t frameSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + ARG_UNUSED(bitDepth); + ARG_UNUSED(frameSize); + + *result = 0; + + return TEST_DEC_MONO_BUF_SIZE; +} + +uint16_t fake_LC3PCMBuffersize__fails(uint16_t sampleRate, uint8_t bitDepth, + LC3FrameSize_t frameSize, int32_t *result) +{ + ARG_UNUSED(sampleRate); + ARG_UNUSED(bitDepth); + ARG_UNUSED(frameSize); + + *result = -1; + + return -1; +} diff --git a/tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.h b/tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.h new file mode 100644 index 000000000000..bfe7b824d101 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/lc3_test_fakes.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef _FAKES_H_ +#define _FAKES_H_ + +#include +#include +#include +#include + +#include "LC3API.h" + +/* Fake functions declaration */ +DECLARE_FAKE_VALUE_FUNC(int32_t, LC3Initialize, uint8_t, uint8_t, LC3FrameSizeConfig_t, uint8_t, + uint8_t *, uint32_t *); +DECLARE_FAKE_VALUE_FUNC(int32_t OSALCALL, LC3Deinitialize); +DECLARE_FAKE_VALUE_FUNC(LC3EncoderHandle_t OSALCALL, LC3EncodeSessionOpen, uint16_t, uint8_t, + LC3FrameSize_t, uint8_t *, uint16_t *, int32_t *); +DECLARE_FAKE_VALUE_FUNC(int32_t, LC3EncodeSessionData, LC3EncoderHandle_t, LC3EncodeInput_t *, + LC3EncodeOutput_t *); +DECLARE_FAKE_VOID_FUNC1(LC3EncodeSessionClose, LC3EncoderHandle_t); +DECLARE_FAKE_VALUE_FUNC(LC3DecoderHandle_t, LC3DecodeSessionOpen, uint16_t, uint8_t, LC3FrameSize_t, + uint8_t *, uint16_t *, int32_t *); +DECLARE_FAKE_VALUE_FUNC(int32_t, LC3DecodeSessionData, LC3DecoderHandle_t, LC3DecodeInput_t *, + LC3DecodeOutput_t *); +DECLARE_FAKE_VOID_FUNC1(LC3DecodeSessionClose, LC3DecoderHandle_t); +DECLARE_FAKE_VALUE_FUNC(uint16_t, LC3BitstreamBuffersize, uint16_t, uint32_t, LC3FrameSize_t, + int32_t *); +DECLARE_FAKE_VALUE_FUNC(uint16_t, LC3PCMBuffersize, uint16_t, uint8_t, LC3FrameSize_t, int32_t *); + +/* List of fakes used by this unit tester */ +#define DO_FOREACH_FAKE(FUNC) \ + do { \ + FUNC(LC3Initialize) \ + FUNC(LC3Deinitialize) \ + FUNC(LC3EncodeSessionOpen) \ + FUNC(LC3EncodeSessionData) \ + FUNC(LC3EncodeSessionClose) \ + FUNC(LC3DecodeSessionOpen) \ + FUNC(LC3DecodeSessionData) \ + FUNC(LC3DecodeSessionClose) \ + FUNC(LC3BitstreamBuffersize) \ + FUNC(LC3PCMBuffersize) \ + } while (0) + +int32_t fake_LC3Initialize__succeeds(uint8_t encoderSampleRates, uint8_t decoderSampleRates, + LC3FrameSizeConfig_t frameSizeConfig, uint8_t uniqueSessions, + uint8_t *buffer, uint32_t *bufferSize); +int32_t fake_LC3Initialize__fails(uint8_t encoderSampleRates, uint8_t decoderSampleRates, + LC3FrameSizeConfig_t frameSizeConfig, uint8_t uniqueSessions, + uint8_t *buffer, uint32_t *bufferSize); +int32_t fake_LC3Deinitialize__succeeds(void); +int32_t fake_LC3Deinitialize__fails(void); +LC3EncoderHandle_t fake_LC3EncodeSessionOpen__succeeds(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result); +LC3EncoderHandle_t fake_LC3EncodeSessionOpen__fails(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result); +int32_t fake_LC3EncodeSessionData__succeeds(LC3EncoderHandle_t encodeHandle, + LC3EncodeInput_t *encodeInput, + LC3EncodeOutput_t *encodeOutput); +int32_t fake_LC3EncodeSessionData__fails(LC3EncoderHandle_t encodeHandle, + LC3EncodeInput_t *encodeInput, + LC3EncodeOutput_t *encodeOutput); +void fake_LC3EncodeSessionClose__succeeds(LC3EncoderHandle_t encodeHandle); +void fake_LC3EncodeSessionClose__fails(LC3EncoderHandle_t encodeHandle); +LC3DecoderHandle_t fake_LC3DecodeSessionOpen__succeeds(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result); +LC3DecoderHandle_t fake_LC3DecodeSessionOpen__fails(uint16_t sampleRate, uint8_t bitsPerSample, + LC3FrameSize_t frameSize, uint8_t *buffer, + uint16_t *bufferSize, int32_t *result); +int32_t fake_LC3DecodeSessionData__succeeds(LC3DecoderHandle_t decodeHandle, + LC3DecodeInput_t *decodeInput, + LC3DecodeOutput_t *decodeOutput); +int32_t fake_LC3DecodeSessionData__fails(LC3DecoderHandle_t decodeHandle, + LC3DecodeInput_t *decodeInput, + LC3DecodeOutput_t *decodeOutput); +void fake_LC3DecodeSessionClose__succeeds(LC3DecoderHandle_t decodeHandle); +void fake_LC3DecodeSessionClose__fails(LC3DecoderHandle_t decodeHandle); +uint16_t fake_LC3BitstreamBuffersize__succeeds(uint16_t sampleRate, uint32_t maxBitRate, + LC3FrameSize_t frameSize, int32_t *result); +uint16_t fake_LC3BitstreamBuffersize__fails(uint16_t sampleRate, uint32_t maxBitRate, + LC3FrameSize_t frameSize, int32_t *result); +uint16_t fake_LC3PCMBuffersize__succeeds(uint16_t sampleRate, uint8_t bitDepth, + LC3FrameSize_t frameSize, int32_t *result); +uint16_t fake_LC3PCMBuffersize__fails(uint16_t sampleRate, uint8_t bitDepth, + LC3FrameSize_t frameSize, int32_t *result); + +extern uint8_t *ref_in; +extern int ref_in_size; +extern int ref_in_read_size; + +#endif /* _FAKES_H_ */ diff --git a/tests/subsys/audio_modules/lc3_module/src/main.c b/tests/subsys/audio_modules/lc3_module/src/main.c new file mode 100644 index 000000000000..840857420e17 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/main.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include "lc3_test_fakes.h" + +/* This function runs before each test */ +static void run_before(void *fixture) +{ + ARG_UNUSED(fixture); + + /* Register resets */ + DO_FOREACH_FAKE(RESET_FAKE); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); +} + +ZTEST_SUITE(suite_lc3_decoder_functional, NULL, NULL, run_before, NULL, NULL); +ZTEST_SUITE(suite_lc3_encoder_functional, NULL, NULL, run_before, NULL, NULL); diff --git a/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.c b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.c new file mode 100644 index 000000000000..cfa978af2114 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "sweep21ms_16b48khz_mono_lc3.h" + +#include + +uint8_t lc3_mono[LC3_MONO_SIZE] = { + 0x1C, 0xCC, 0x12, 0x00, 0xE0, 0x01, 0xC0, 0x03, 0x01, 0x00, 0xE8, 0x03, 0x00, 0x00, 0xE8, + 0x03, 0x00, 0x00, 0x78, 0x00, 0x45, 0x0B, 0x24, 0xBE, 0x5E, 0xAC, 0xFA, 0x38, 0xEE, 0x5B, + 0xFD, 0x7C, 0x8B, 0x9E, 0xF8, 0xC9, 0xF7, 0xED, 0xB9, 0x2C, 0x2A, 0xB3, 0x73, 0x7A, 0x1B, + 0x99, 0x1B, 0x65, 0x5E, 0x44, 0x7D, 0x5A, 0x3A, 0xDE, 0x9F, 0x2F, 0xD8, 0x1B, 0x65, 0xE4, + 0x2E, 0x21, 0x8D, 0x82, 0x21, 0x03, 0x35, 0x00, 0x15, 0xA9, 0xDE, 0x67, 0xA1, 0xF0, 0x00, + 0x00, 0x05, 0x5A, 0x78, 0xE2, 0x7F, 0x08, 0xAB, 0xFE, 0x0B, 0x91, 0xEA, 0x8B, 0x7E, 0x9B, + 0x1E, 0xB4, 0xFE, 0xBE, 0x7C, 0xAC, 0xC7, 0x0B, 0xE8, 0x2F, 0xA0, 0x86, 0x31, 0x5F, 0x5F, + 0x47, 0x2A, 0xF0, 0x3C, 0x47, 0x06, 0x52, 0xFB, 0x08, 0x14, 0xF2, 0x36, 0x17, 0xB2, 0x16, + 0x1F, 0x65, 0x08, 0x3B, 0x23, 0x72, 0x3C, 0x16, 0x01, 0x71, 0xC2, 0xA2, 0xBF, 0x14, 0xAA, + 0x33, 0x9C, 0x3A, 0xA6, 0x3C, 0x78, 0x00, 0x6A, 0x58, 0x75, 0x1A, 0x61, 0x1E, 0xBE, 0xE5, + 0xE8, 0x7A, 0x7D, 0xB0, 0x4D, 0x06, 0x71, 0x53, 0xB9, 0x60, 0xF3, 0x10, 0x1A, 0x03, 0xFB, + 0x47, 0x29, 0x23, 0x12, 0x5A, 0x05, 0x01, 0xA5, 0xB4, 0x20, 0x6F, 0xEA, 0xEF, 0x11, 0x39, + 0xE1, 0x0E, 0x13, 0x21, 0x45, 0x62, 0x4F, 0x18, 0xD8, 0xF9, 0x04, 0xD9, 0x70, 0xCA, 0xF0, + 0xF1, 0x61, 0xC7, 0xD2, 0x3D, 0x2D, 0xF6, 0x87, 0xC8, 0xC6, 0x43, 0x16, 0xCD, 0xD5, 0xAB, + 0xF0, 0x4D, 0xBF, 0xFF, 0x00, 0x00, 0x1F, 0xFC, 0x03, 0xF8, 0x1F, 0x83, 0xE1, 0xE0, 0x74, + 0x7F, 0xC4, 0xB9, 0x42, 0x73, 0x22, 0xCC, 0xD6, 0xD0, 0xDC, 0x06, 0xB4, 0xB2, 0x27, 0x4B, + 0xF2, 0x35, 0x3E, 0x21, 0xC2, 0xE5, 0x83, 0x59, 0x65, 0x69, 0x29, 0x8D, 0x65, 0x92, 0xE0, + 0xF9, 0x9C, 0x08, 0xD6, 0x3F, 0x93, 0x84, 0x78, 0x00, 0xA3, 0xDB, 0xB6, 0x61, 0x33, 0x77, + 0xF9, 0x63, 0x88, 0x1B, 0xC6, 0x03, 0x73, 0x20, 0x04, 0xFF, 0xEF, 0xE8, 0x38, 0x01, 0x35, + 0x97, 0x6A, 0x6D, 0x93, 0xDF, 0x1C, 0x26, 0x0E, 0x99, 0x82, 0x14, 0xCB, 0x08, 0xCF, 0x19, + 0x95, 0x4A, 0x6D, 0x13, 0xCD, 0xDF, 0xEF, 0x17, 0xF1, 0xF6, 0xC5, 0xFA, 0x41, 0xA0, 0x9B, + 0x45, 0x6E, 0xCE, 0xD6, 0x87, 0xCF, 0x9E, 0x05, 0x1C, 0x31, 0x38, 0x7B, 0xAB, 0x9B, 0x60, + 0x8F, 0xA2, 0x18, 0xC8, 0x70, 0x81, 0xEA, 0xAE, 0x06, 0xF6, 0x11, 0x45, 0xA2, 0xBA, 0x17, + 0xA5, 0x72, 0x02, 0xAF, 0x2A, 0xA5, 0x56, 0x35, 0x4A, 0x8A, 0x6D, 0x0F, 0xC5, 0x51, 0xA9, + 0x25, 0x5B, 0x1A, 0x94, 0xF1, 0xB7, 0xF1, 0x0C, 0xC0, 0xD4, 0xFB, 0x1D, 0x6B, 0x62, 0xF9, + 0x61, 0x83, 0xB9, 0x3F, 0xE9, 0xF1, 0x3D, 0x75, 0x2C, +}; diff --git a/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.h b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.h new file mode 100644 index 000000000000..c5266ba1d331 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_lc3.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef _LC3_H_ +#define _LC3_H_ + +#include + +#define LC3_MONO_SIZE (384) + +extern uint8_t lc3_mono[LC3_MONO_SIZE]; + +#endif /* _LC3_H_ */ diff --git a/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.c b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.c new file mode 100644 index 000000000000..a5ac75bf491b --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#include "sweep21ms_16b48khz_mono_wav.h" + +#include + +uint16_t wav_mono[WAV_MONO_SIZE] = { + 0xFFFF, 0x0047, 0x0087, 0x00D1, 0x0116, 0x0159, 0x01A7, 0x01E7, 0x0234, 0x027C, 0x02C2, + 0x030F, 0x0357, 0x03A2, 0x03EE, 0x0437, 0x0487, 0x04CE, 0x0522, 0x056A, 0x05B9, 0x060B, + 0x0657, 0x06A7, 0x06FA, 0x0746, 0x079C, 0x07EB, 0x0840, 0x0890, 0x08E6, 0x0939, 0x098C, + 0x09E6, 0x0A36, 0x0A91, 0x0AE4, 0x0B3F, 0x0B95, 0x0BEE, 0x0C49, 0x0C9D, 0x0D01, 0x0D51, + 0x0DB7, 0x0E0B, 0x0E6C, 0x0EC9, 0x0F26, 0x0F84, 0x0FE4, 0x1045, 0x10A2, 0x1109, 0x1162, + 0x11CC, 0x1229, 0x1291, 0x12F1, 0x135B, 0x13B8, 0x1427, 0x1486, 0x14F2, 0x1559, 0x15BE, + 0x162D, 0x168F, 0x1702, 0x1765, 0x17D7, 0x183E, 0x18AE, 0x191B, 0x1987, 0x19F9, 0x1A66, + 0x1AD7, 0x1B47, 0x1BB9, 0x1C2B, 0x1C9C, 0x1D13, 0x1D81, 0x1DFB, 0x1E6E, 0x1EE2, 0x1F5D, + 0x1FCF, 0x204A, 0x20C1, 0x213B, 0x21B2, 0x2231, 0x22A6, 0x2328, 0x239D, 0x2421, 0x2497, + 0x251C, 0x2596, 0x2617, 0x2697, 0x2716, 0x2799, 0x281A, 0x289B, 0x2920, 0x29A1, 0x2A29, + 0x2AA8, 0x2B33, 0x2BB3, 0x2C3F, 0x2CC3, 0x2D4A, 0x2DD3, 0x2E5B, 0x2EE5, 0x2F6F, 0x2FF7, + 0x3084, 0x310D, 0x319B, 0x3226, 0x32B1, 0x3344, 0x33C8, 0x3460, 0x34E7, 0x357B, 0x3608, + 0x3698, 0x3727, 0x37BB, 0x3848, 0x38DF, 0x3969, 0x3A02, 0x3A8F, 0x3B26, 0x3BB7, 0x3C4A, + 0x3CDF, 0x3D71, 0x3E06, 0x3E9B, 0x3F2E, 0x3FC4, 0x4057, 0x40EF, 0x4181, 0x4218, 0x42AE, + 0x433F, 0x43DC, 0x4469, 0x4505, 0x4597, 0x462F, 0x46C1, 0x475A, 0x47EB, 0x4884, 0x4916, + 0x49AC, 0x4A41, 0x4AD3, 0x4B69, 0x4BFA, 0x4C90, 0x4D21, 0x4DB4, 0x4E45, 0x4ED8, 0x4F67, + 0x4FFA, 0x5087, 0x5117, 0x51A7, 0x5231, 0x52C5, 0x5348, 0x53DE, 0x545C, 0x54F2, 0x5572, + 0x55FE, 0x5684, 0x5707, 0x578F, 0x5810, 0x5891, 0x5915, 0x5990, 0x5A13, 0x5A8B, 0x5B07, + 0x5B83, 0x5BF6, 0x5C71, 0x5CE2, 0x5D55, 0x5DC7, 0x5E35, 0x5EA0, 0x5F0E, 0x5F73, 0x5FDD, + 0x603C, 0x60A4, 0x60FE, 0x615F, 0x61B6, 0x6210, 0x6263, 0x62B8, 0x6305, 0x6352, 0x639D, + 0x63E2, 0x6425, 0x6467, 0x64A1, 0x64DC, 0x6512, 0x6543, 0x6573, 0x659C, 0x65C5, 0x65E7, + 0x6605, 0x6622, 0x6636, 0x664B, 0x6657, 0x6661, 0x6665, 0x6667, 0x6660, 0x6659, 0x6648, + 0x6634, 0x661E, 0x65FD, 0x65DC, 0x65B2, 0x6580, 0x6552, 0x6512, 0x64D5, 0x648F, 0x6440, + 0x63F1, 0x6395, 0x6339, 0x62D0, 0x6266, 0x61F0, 0x617A, 0x60F5, 0x6070, 0x5FDF, 0x5F49, + 0x5EAC, 0x5E05, 0x5D5B, 0x5CA4, 0x5BE9, 0x5B25, 0x5A58, 0x5987, 0x58A8, 0x57C6, 0x56D8, + 0x55E4, 0x54E7, 0x53E2, 0x52D2, 0x51BE, 0x509D, 0x4F74, 0x4E46, 0x4D07, 0x4BCB, 0x4A79, + 0x492B, 0x47C6, 0x4667, 0x44EF, 0x437F, 0x41F5, 0x4070, 0x3ED9, 0x3D3C, 0x3B97, 0x39E6, + 0x382F, 0x366D, 0x34A2, 0x32D1, 0x30F2, 0x2F10, 0x2D20, 0x2B2B, 0x292C, 0x2726, 0x2515, + 0x22FE, 0x20E0, 0x1EB4, 0x1C8D, 0x1A4C, 0x1818, 0x15CB, 0x1383, 0x1130, 0x0ED5, 0x0C76, + 0x0A12, 0x07A3, 0x0537, 0x02BE, 0x0045, 0xFDC6, 0xFB41, 0xF8BD, 0xF631, 0xF3A5, 0xF116, + 0xEE82, 0xEBF1, 0xE95B, 0xE6C7, 0xE431, 0xE19C, 0xDF08, 0xDC76, 0xD9E4, 0xD757, 0xD4CA, + 0xD245, 0xCFC1, 0xCD43, 0xCACC, 0xC857, 0xC5F1, 0xC38B, 0xC131, 0xBEE2, 0xBC98, 0xBA5F, + 0xB82B, 0xB60B, 0xB3EE, 0xB1EC, 0xAFEC, 0xAE05, 0xAC29, 0xAA60, 0xA8AA, 0xA706, 0xA578, + 0xA3FA, 0xA297, 0xA147, 0xA00F, 0x9EF1, 0x9DEA, 0x9CFD, 0x9C2C, 0x9B76, 0x9AD9, 0x9A62, + 0x99FC, 0x99C2, 0x999D, 0x999D, 0x99BD, 0x99FF, 0x9A5F, 0x9AE9, 0x9B8E, 0x9C5B, 0x9D4E, + 0x9E5E, 0x9F9C, 0xA0F8, 0xA27C, 0xA426, 0xA5F4, 0xA7E9, 0xAA04, 0xAC42, 0xAEA9, 0xB131, + 0xB3E2, 0xB6B3, 0xB9AE, 0xBCC5, 0xC005, 0xC360, 0xC6E6, 0xCA80, 0xCE45, 0xD21B, 0xD619, + 0xDA2C, 0xDE58, 0xE2A1, 0xE6F8, 0xEB6D, 0xEFEC, 0xF481, 0xF920, 0xFDD0, 0x0281, 0x0746, + 0x0BFF, 0x10CA, 0x1587, 0x1A48, 0x1EFE, 0x23AC, 0x284A, 0x2CDC, 0x3156, 0x35BB, 0x3A05, + 0x3E32, 0x423B, 0x4624, 0x49DF, 0x4D71, 0x50D5, 0x5403, 0x56FE, 0x59BD, 0x5C41, 0x5E83, + 0x6085, 0x623B, 0x63B2, 0x64D0, 0x65AF, 0x662E, 0x6669, 0x6644, 0x65CE, 0x6502, 0x63D7, + 0x625C, 0x607C, 0x5E4B, 0x5BBB, 0x58D2, 0x5593, 0x51FA, 0x4E0D, 0x49CC, 0x453B, 0x405A, + 0x3B34, 0x35C1, 0x3010, 0x2A23, 0x23F7, 0x1DA5, 0x171A, 0x1072, 0x09A8, 0x02C7, 0xFBD9, + 0xF4DE, 0xEDEE, 0xE6FA, 0xE027, 0xD965, 0xD2CC, 0xCC65, 0xC630, 0xC041, 0xBA9C, 0xB549, + 0xB05A, 0xABCB, 0xA7B0, 0xA40B, 0xA0E7, 0x9E49, 0x9C3A, 0x9ABF, 0x99DC, 0x999B, 0x99F9, + 0x9AFD, 0x9CA7, 0x9EF6, 0xA1F2, 0xA58D, 0xA9CD, 0xAEAC, 0xB420, 0xBA2A, 0xC0BD, 0xC7CF, + 0xCF57, 0xD74B, 0xDF97, 0xE836, 0xF111, 0xFA17, 0x0340, 0x0C69, 0x1591, 0x1E97, 0x276A, + 0x2FFD, 0x382D, 0x3FF3, 0x4734, 0x4DDE, 0x53DD, 0x5922, 0x5D98, 0x6133, 0x63E7, 0x65A0, + 0x6661, 0x6617, 0x64C3, 0x6267, 0x5EF6, 0x5A88, 0x550C, 0x4EA6, 0x4748, 0x3F1F, 0x3621, + 0x2C80, 0x2240, 0x178A, 0x0C77, 0x012B, 0xF5C6, 0xEA6F, 0xDF48, 0xD47C, 0xCA2B, 0xC083, + 0xB79F, 0xAFAA, 0xA8C4, 0xA307, 0x9E97, 0x9B7E, 0x99DF, 0x99B8, 0x9B22, 0x9E0E, 0xA28A, + 0xA87C, 0xAFE1, 0xB89A, 0xC28E, 0xCD96, 0xD988, 0xE63A, 0xF36F, 0x00FB, 0x0E91, 0x1C04, + 0x2907, 0x3564, 0x40D1, 0x4B1F, 0x5407, 0x5B5F, 0x60F8, 0x64A1, 0x6652, 0x65DE, 0x6351, + 0x5EA3, 0x57E3, 0x4F31, 0x44AE, 0x388D, 0x2B18, 0x1C7D, 0x0D33, 0xFD68, 0xEDA6, 0xDE2A, + 0xCF73, 0xC1D3, 0xB5AE, 0xAB61, 0xA32E, 0x9D6A, 0x9A33, 0x99C4, 0x9C1E, 0xA147, 0xA928, + 0xB392, 0xC048, 0xCEF5, 0xDF33, 0xF08B, 0x027E, 0x1477, 0x25F0, 0x364E, 0x4505, 0x5193, + 0x5B7B, 0x625E, 0x65F0, 0x65F8, 0x626C, 0x5B54, 0x50DF, 0x4363, 0x3346, 0x2126, 0x0D99, + 0xF969, 0xE555, 0xD22E, 0xC0C0, 0xB1CE, 0xA5FF, 0x9DE9, 0x99F6, 0x9A66, 0x9F51, 0xA890, + 0xB5D4, 0xC693, 0xDA1B, 0xEF8F, 0x05EE, 0x1C2D, 0x312F, 0x43E8, 0x534D, 0x5E97, 0x64FF, + 0x6631, 0x61EA, 0x5857, 0x49DD, 0x372D, 0x2144, 0x094C, 0xF0A6, 0xD8B5, 0xC2F8, 0xB0BA, + 0xA33E, 0x9B62, 0x99D2, 0x9EBA, 0xA9FA, 0xBAE8, 0xD08F, 0xE98A, 0x0439, 0x1ECD, 0x3764, + 0x4C36, 0x5BAF, 0x648F, 0x660B, 0x5FE0, 0x525C, 0x3E68, 0x256D, 0x0950, 0xEC3F, 0xD09E, + 0xB8B3, 0xA69F, 0x9BFB, 0x99EC, 0xA0BD, 0xB00E, 0xC6A4, 0xE297, 0x0178, 0x2061, 0x3C78, + 0x52E4, 0x616C, 0x6664, 0x612B, 0x5204, 0x3A57, 0x1C66, 0xFB44, 0xDA69, 0xBD6E, 0xA798, + 0x9B7D, 0x9AB3, 0xA586, 0xBAFD, 0xD8B2, 0xFB5D, 0x1EC4, 0x3EA9, 0x56EF, 0x6461, 0x6511, + 0x589C, 0x406C, 0x1F73, 0xFA05, 0xD529, 0xB602, 0xA119, 0x9999, 0xA0E7, 0xB634, 0xD6AA, + 0xFD8A, 0x2517, 0x471B, 0x5E31, 0x6666, 0x5E1D, 0x465F, 0x22C3, 0xF919, 0xD04F, 0xAF88, + 0x9CA2, 0x9B47, 0xAC15, 0xCC48, 0xF653, 0x2265, 0x4843, 0x6065, 0x65DC, 0x572C, 0x36E0, + 0x0B30, 0xDCEF, 0xB5AE, 0x9DFB, 0x9B2B, 0xAE55, 0xD3A6, 0x0323, 0x3239, 0x560A, 0x660B, + 0x5E00, 0x3F6B, 0x1134, 0xDE7F, 0xB3CD, 0x9C1E, 0x9DE5, 0xB92A, 0xE744, 0x1C44, 0x49F0, + 0x63A2, 0x61C9, 0x4460, 0x1342, 0xDC45, 0xAF65, 0x9A40, 0xA397, 0xC942, 0x0012, 0x373C, + 0x5D21, 0x654B, 0x4C5C, 0x1A0C, 0xDEA3, 0xAE41, 0x99B8, 0xA8CF, 0xD6D9, 0x13F0, 0x4A24, + 0x655C, 0x5ADD, 0x2DF0, 0xEF18, 0xB67A, 0x9A75, 0xA6D7, 0xD75B, 0x18D6, 0x5060, 0x6666, + 0x50D9, 0x1837, 0xD4A3, 0xA3EA, 0x9C5E, 0xC22F, 0x04D4, 0x458D, 0x65B0, 0x5510, 0x1AF1, + 0xD328, 0xA169, 0x9F57, 0xCEE6, 0x1849, 0x5548, 0x6508, 0x3E31, 0xF4FC, 0xB187, 0x99DF, + 0xBC60, 0x062D, 0x4CBB, 0x662B, 0x4246, 0xF5D5, 0xAF3F, 0x9AC9, 0xC656, 0x1724, 0x594D, + 0x6102, 0x27FC, 0xD3AC, 0x9D3E, 0xAB18, 0xF4A9, 0x46B0, 0x6628, 0x3B1F, 0xE424, 0xA19A, + 0xA66A, 0xF02C, 0x469E, 0x65BF, 0x33B4, 0xD78D, 0x9C22, 0xB362, 0x0B35, 0x59DF, 0x5B60, + 0x0CFE, 0xB2C9, 0x9D5B, 0xE16B, 0x41DC, 0x6574, 0x2980, 0xC5B3, 0x99D1, 0xD1CE, 0x37D5, + 0x6638, 0x2CE9, 0xC51D, 0x9A4B, 0xDA9C, 0x4326, 0x6371, 0x16F6, 0xB0B4, 0xA31C, 0xFF04, + 0x5C6B, 0x4E7C, 0xE3F8, 0x9A87, 0xCB74, 0x3CD2, 0x6344, 0x0D6D, 0xA648, 0xB200, 0x231F, + 0x6667, 0x218F, 0xAF4F, 0xAABD, 0x1BD0, 0x665C, 0x2076, 0xABFB, 0xB0BD, 0x2998, 0x6518, + 0x0938, 0x9F26, 0xC973, 0x4886, 0x55F8, 0xDAD7, 0x9B37, 0xFF55, 0x64BA, 0x22B8, 0xA62F, + 0xC254, 0x48AE, 0x50A9, 0xCAA6, 0xA377, 0x22E5, 0x62E1, 0xECDE, 0x9A62, 0x0725, 0x665E, + 0x00A3, 0x99A5, 0xFBEF, 0x6650, 0x0312, 0x9999, 0x026F, 0x662A, 0xF38F, 0x9B67, 0x1AD0, + 0x6011, 0xD31A, 0xA98E, 0x4118, 0x458E, 0xAB76, 0xD451, 0x62B0, 0x08E6, 0x9A31, 0x1FC9, + 0x5812, 0xB8EC, 0xC9B2, 0x61C8, 0x0342, 0x9CCF, 0x352E, 0x433D, 0xA17A, 0xF9D4, 0x6233, + 0xC227, 0xCAAA, 0x6516, 0xE786, 0xAEE1, 0x5A97, 0x0457, 0xA245, 0x4F40, 0x146C, 0x9E1F, + 0x4A51, 0x176B, 0x9E8B, 0x4E30, 0x0D45, 0xA41C, 0x5940, 0xF550, 0xB425, 0x649D, 0xD11C, + 0xD687, 0x627F, 0xAAA1, 0x0E52, 0x4121, 0x99D6, 0x4C52, 0xF9E3, 0xBDA1, 0x65B2, 0xAC59, + 0x186E, 0x2D39, 0xA302, 0x63A6, 0xBEB7, 0x064A, 0x359C, 0xA2B5, 0x6535, 0xB27C, +}; diff --git a/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.h b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.h new file mode 100644 index 000000000000..addeb05e592c --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/src/sweep21ms_16b48khz_mono_wav.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef _WAV_H_ +#define _WAV_H_ + +#include + +#define WAV_MONO_SIZE (1000) + +extern uint16_t wav_mono[WAV_MONO_SIZE]; + +#endif /* _WAV_H_ */ diff --git a/tests/subsys/audio_modules/lc3_module/testcase.yaml b/tests/subsys/audio_modules/lc3_module/testcase.yaml new file mode 100644 index 000000000000..573480765441 --- /dev/null +++ b/tests/subsys/audio_modules/lc3_module/testcase.yaml @@ -0,0 +1,7 @@ +tests: + nrf5340_audio.lc3_module_test: + sysbuild: true + platform_allow: qemu_cortex_m3 + integration_platforms: + - qemu_cortex_m3 + tags: lc3_module audio_module nrf5340_audio_unit_tests sysbuild