From bf8607841409c1fae5dc7606d968c2b24d0d3781 Mon Sep 17 00:00:00 2001 From: Piotr Hoppe Date: Wed, 2 Sep 2026 14:04:28 +0200 Subject: [PATCH 1/2] test: drc: add ztest unit tests for drc module Add native_sim Ztest coverage for two-channel DRC processing. Cover S16, S24, and S32 pass-through and enabled configurations through component creation, configuration, preparation, and processing. Add the minimal SOF allocator and context support required by the standalone test application while keeping existing CMocka tests and production sources unchanged. Signed-off-by: Piotr Hoppe --- test/ztest/unit/audio/drc/CMakeLists.txt | 73 ++ test/ztest/unit/audio/drc/drc_process_ztest.c | 648 ++++++++++++++++++ test/ztest/unit/audio/drc/drc_test_alloc.c | 109 +++ test/ztest/unit/audio/drc/drc_test_coef.h | 41 ++ test/ztest/unit/audio/drc/prj.conf | 14 + test/ztest/unit/audio/drc/testcase.yaml | 18 + 6 files changed, 903 insertions(+) create mode 100644 test/ztest/unit/audio/drc/CMakeLists.txt create mode 100644 test/ztest/unit/audio/drc/drc_process_ztest.c create mode 100644 test/ztest/unit/audio/drc/drc_test_alloc.c create mode 100644 test/ztest/unit/audio/drc/drc_test_coef.h create mode 100644 test/ztest/unit/audio/drc/prj.conf create mode 100644 test/ztest/unit/audio/drc/testcase.yaml diff --git a/test/ztest/unit/audio/drc/CMakeLists.txt b/test/ztest/unit/audio/drc/CMakeLists.txt new file mode 100644 index 000000000000..5379dcb546ab --- /dev/null +++ b/test/ztest/unit/audio/drc/CMakeLists.txt @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2026 Intel Corporation. + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sof_unit_audio_drc) + +set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../../../") +set(sof_top_dir ${SOF_ROOT}) + +# Include SOF CMake helpers used by trace source annotations. +include(${SOF_ROOT}/scripts/cmake/misc.cmake) +include(${SOF_ROOT}/scripts/cmake/uuid-registry.cmake) + +target_include_directories(app PRIVATE + ${SOF_ROOT}/zephyr/include + ${SOF_ROOT}/src/include + ${SOF_ROOT}/src/platform/posix/include + ${SOF_ROOT}/src/audio + ${SOF_ROOT}/test/cmocka/include + ${PROJECT_BINARY_DIR}/include/generated +) + +target_compile_definitions(app PRIVATE + -DCONFIG_ZEPHYR_POSIX=1 + -DCONFIG_LIBRARY=1 + -DCONFIG_NUMBERS_NORM=1 + -DUNIT_TEST=1 +) + +target_compile_options(app PRIVATE + -Wno-error=deprecated-declarations +) + +target_sources(app PRIVATE + drc_process_ztest.c + drc_test_alloc.c + ${SOF_ROOT}/src/audio/drc/drc.c + ${SOF_ROOT}/src/audio/drc/drc_generic.c + ${SOF_ROOT}/src/audio/drc/drc_math_generic.c + ${SOF_ROOT}/src/audio/drc/drc_math_hifi3.c + ${SOF_ROOT}/src/audio/drc/drc_log.c + ${SOF_ROOT}/src/math/numbers.c + ${SOF_ROOT}/src/math/exp_fcn.c + ${SOF_ROOT}/src/math/exp_fcn_hifi.c + ${SOF_ROOT}/src/math/lut_trig.c + ${SOF_ROOT}/src/audio/module_adapter/module_adapter.c + ${SOF_ROOT}/src/audio/module_adapter/module_adapter_ipc3.c + ${SOF_ROOT}/src/audio/module_adapter/module/generic.c + ${SOF_ROOT}/src/audio/buffers/comp_buffer.c + ${SOF_ROOT}/src/audio/buffers/audio_buffer.c + ${SOF_ROOT}/src/audio/source_api_helper.c + ${SOF_ROOT}/src/audio/sink_api_helper.c + ${SOF_ROOT}/src/audio/sink_source_utils.c + ${SOF_ROOT}/src/audio/audio_stream.c + ${SOF_ROOT}/src/audio/component.c + ${SOF_ROOT}/src/audio/data_blob.c + ${SOF_ROOT}/src/module/audio/source_api.c + ${SOF_ROOT}/src/module/audio/sink_api.c + ${SOF_ROOT}/src/ipc/ipc3/helper.c + ${SOF_ROOT}/src/ipc/ipc-common.c + ${SOF_ROOT}/src/ipc/ipc-helper.c + ${SOF_ROOT}/src/lib/objpool.c + ${SOF_ROOT}/src/audio/pipeline/pipeline-graph.c + ${SOF_ROOT}/src/audio/pipeline/pipeline-params.c + ${SOF_ROOT}/src/audio/pipeline/pipeline-schedule.c + ${SOF_ROOT}/src/audio/pipeline/pipeline-stream.c + ${SOF_ROOT}/src/audio/pipeline/pipeline-xrun.c +) + +sof_append_relative_path_definitions(app) diff --git a/test/ztest/unit/audio/drc/drc_process_ztest.c b/test/ztest/unit/audio/drc/drc_process_ztest.c new file mode 100644 index 000000000000..aaaf57c85913 --- /dev/null +++ b/test/ztest/unit/audio/drc/drc_process_ztest.c @@ -0,0 +1,648 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "drc/drc.h" +#include "drc_test_coef.h" + +/* Maximum absolute value of a signed 24-bit sample. */ +#define S24_MAX_ABS 0x800000 + +struct test_parameters { + uint32_t channels; + uint32_t frames; + uint32_t buffer_size_mult; + uint32_t source_format; + uint32_t sink_format; + const uint32_t *config; + bool passthrough; +}; + +struct test_result { + bool passed; + uint32_t sample; + int32_t output; + int32_t expected; +}; + +struct test_data { + struct comp_dev *dev; + struct comp_buffer *sink; + struct comp_buffer *source; + struct test_parameters params; + struct test_result result; + bool continue_loop; + int fill_idx; + int verify_idx; + int diff_count; +}; + +/** + * @brief Create a test sink buffer connected to a component. + */ +static struct comp_buffer *create_test_sink(struct comp_dev *dev, + uint32_t pipeline_id, + uint32_t frame_fmt, + uint16_t channels, + uint16_t buffer_size) +{ + struct sof_ipc_buffer desc = { + .comp = { + .pipeline_id = pipeline_id, + }, + .size = buffer_size, + }; + struct comp_buffer *buffer = buffer_new(NULL, &desc, BUFFER_USAGE_NOT_SHARED); + + if (!buffer) + return NULL; + + memset(buffer->stream.addr, 0, buffer_size); + + buffer->sink = calloc(1, sizeof(*buffer->sink)); + if (!buffer->sink) { + buffer_free(buffer); + return NULL; + } + + if (dev) + list_item_append(&buffer->source_list, &dev->bsink_list); + + buffer->sink->state = COMP_STATE_PREPARE; + audio_stream_set_frm_fmt(&buffer->stream, frame_fmt); + audio_stream_set_channels(&buffer->stream, channels); + + return buffer; +} + +/** + * @brief Release a test sink buffer and its component endpoint. + */ +static void free_test_sink(struct comp_buffer *buffer) +{ + if (!buffer) + return; + + free(comp_buffer_get_sink_component(buffer)); + buffer_free(buffer); +} + +/** + * @brief Create a test source buffer connected to a component. + */ +static struct comp_buffer *create_test_source(struct comp_dev *dev, + uint32_t pipeline_id, + uint32_t frame_fmt, + uint16_t channels, + uint16_t buffer_size) +{ + struct sof_ipc_buffer desc = { + .comp = { + .pipeline_id = pipeline_id, + }, + .size = buffer_size, + }; + struct comp_buffer *buffer = buffer_new(NULL, &desc, BUFFER_USAGE_NOT_SHARED); + + if (!buffer) + return NULL; + + memset(buffer->stream.addr, 0, buffer_size); + + buffer->source = calloc(1, sizeof(*buffer->source)); + if (!buffer->source) { + buffer_free(buffer); + return NULL; + } + + if (dev) + list_item_append(&buffer->sink_list, &dev->bsource_list); + + buffer->source->state = COMP_STATE_PREPARE; + audio_stream_set_frm_fmt(&buffer->stream, frame_fmt); + audio_stream_set_channels(&buffer->stream, channels); + + return buffer; +} + +/** + * @brief Release a test source buffer and its component endpoint. + */ +static void free_test_source(struct comp_buffer *buffer) +{ + if (!buffer) + return; + + free(buffer->source); + buffer_free(buffer); +} + +/** + * @brief Release all resources allocated for one DRC processing case. + */ +static void destroy_test_data(struct test_data *td) +{ + if (!td) + return; + + free_test_source(td->source); + free_test_sink(td->sink); + if (td->dev) + comp_free(td->dev); + free(td); +} + +/** + * @brief Create the IPC description used to instantiate the DRC module. + */ +static struct sof_ipc_comp_process *create_drc_comp_ipc(void) +{ + struct sof_ipc_comp_process *ipc; + const size_t ipc_size = sizeof(*ipc); + const struct sof_uuid uuid = SOF_REG_UUID(drc); + + ipc = calloc(1, ipc_size + SOF_UUID_SIZE); + if (!ipc) + return NULL; + + memcpy_s(ipc + 1, SOF_UUID_SIZE, &uuid, SOF_UUID_SIZE); + ipc->comp.hdr.size = ipc_size + SOF_UUID_SIZE; + ipc->comp.type = SOF_COMP_MODULE_ADAPTER; + ipc->config.hdr.size = sizeof(struct sof_ipc_comp_config); + ipc->size = 0; + ipc->comp.ext_data_length = SOF_UUID_SIZE; + + return ipc; +} + +/** + * @brief Send one complete DRC configuration blob through the module API. + */ +static int drc_send_config(struct processing_module *mod, const uint32_t *config) +{ + const struct module_interface *const ops = mod->dev->drv->adapter_ops; + const struct sof_abi_hdr *blob = (const struct sof_abi_hdr *)config; + const size_t cdata_size = sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_abi_hdr) + blob->size; + struct sof_ipc_ctrl_data *cdata; + int ret; + + cdata = calloc(1, cdata_size); + if (!cdata) + return -ENOMEM; + + cdata->cmd = SOF_CTRL_CMD_BINARY; + cdata->num_elems = blob->size; + cdata->data[0].magic = blob->magic; + cdata->data[0].type = blob->type; + cdata->data[0].size = blob->size; + cdata->data[0].abi = blob->abi; + memcpy_s(cdata->data[0].data, blob->size, blob->data, blob->size); + + ret = ops->set_configuration(mod, 0, MODULE_CFG_FRAGMENT_SINGLE, + blob->size, (const uint8_t *)cdata, + blob->size, NULL, 0); + + free(cdata); + return ret; +} + +/** + * @brief Allocate and prepare the DRC component and its test buffers. + */ +static struct test_data *create_test_data(const struct test_parameters *params) +{ + struct test_data *td; + struct processing_module *mod; + struct sof_ipc_comp_process *ipc; + struct comp_dev *dev; + size_t src_size; + size_t sink_size; + int ret; + + td = calloc(1, sizeof(*td)); + if (!td) + return NULL; + + td->params = *params; + td->continue_loop = true; + td->result.passed = true; + + ipc = create_drc_comp_ipc(); + if (!ipc) + goto error; + + dev = comp_new((struct sof_ipc_comp *)ipc); + free(ipc); + if (!dev) + goto error; + + td->dev = dev; + dev->frames = params->frames; + mod = comp_mod(dev); + + ret = drc_send_config(mod, params->config); + if (ret) + goto error; + + src_size = params->frames * get_frame_bytes(params->source_format, params->channels) * + params->buffer_size_mult; + sink_size = params->frames * get_frame_bytes(params->sink_format, params->channels) * + params->buffer_size_mult; + + td->source = create_test_source(dev, 0, params->source_format, params->channels, + src_size); + td->sink = create_test_sink(dev, 0, params->sink_format, params->channels, + sink_size); + if (!td->source || !td->sink) + goto error; + + ret = module_prepare(mod, NULL, 0, NULL, 0); + if (ret) + goto error; + + return td; + +error: + destroy_test_data(td); + return NULL; +} + +/** + * @brief Initialize circular-buffer views for the source and sink buffers. + */ +static void make_views(struct test_data *td, struct cir_buf_source *source_buf, + struct cir_buf_sink *sink_buf) +{ + struct audio_stream *ss = &td->source->stream; + struct audio_stream *ds = &td->sink->stream; + + source_buf->buf_start = audio_stream_get_addr(ss); + source_buf->buf_end = audio_stream_get_end_addr(ss); + source_buf->ptr = audio_stream_get_addr(ss); + + sink_buf->buf_start = audio_stream_get_addr(ds); + sink_buf->buf_end = audio_stream_get_end_addr(ds); + sink_buf->ptr = audio_stream_get_addr(ds); +} + +#if CONFIG_FORMAT_S16LE +/** + * @brief Fill the S16 source buffer with test-vector samples. + */ +static int fill_source_s16(struct test_data *td, int frames) +{ + int16_t *x = audio_stream_get_addr(&td->source->stream); + int samples = frames * td->params.channels; + int available = CHIRP_2CH_LENGTH - td->fill_idx; + int i; + + samples = MIN(samples, available); + for (i = 0; i < samples; i++) + x[i] = sat_int16(Q_SHIFT_RND(chirp_2ch[td->fill_idx++], 31, 15)); + + if (td->fill_idx == CHIRP_2CH_LENGTH) + td->continue_loop = false; + + return i / td->params.channels; +} + +/** + * @brief Verify S16 output against pass-through or processing expectations. + */ +static bool verify_sink_s16(struct test_data *td, int frames) +{ + int16_t *y = audio_stream_get_addr(&td->sink->stream); + int samples = frames * td->params.channels; + int i; + + for (i = 0; i < samples; i++) { + const int32_t output = y[i]; + const int32_t expected = + sat_int16(Q_SHIFT_RND(chirp_2ch[td->verify_idx++], 31, 15)); + + if (td->params.passthrough && output != expected) { + td->result.passed = false; + td->result.sample = td->verify_idx - 1; + td->result.output = output; + td->result.expected = expected; + return false; + } + + if (!td->params.passthrough && output != expected) + td->diff_count++; + } + + return true; +} +#endif /* CONFIG_FORMAT_S16LE */ + +#if CONFIG_FORMAT_S24LE +/** + * @brief Fill the S24 source buffer with test-vector samples. + */ +static int fill_source_s24(struct test_data *td, int frames) +{ + int32_t *x = audio_stream_get_addr(&td->source->stream); + int samples = frames * td->params.channels; + int available = CHIRP_2CH_LENGTH - td->fill_idx; + int i; + + samples = MIN(samples, available); + for (i = 0; i < samples; i++) + x[i] = sat_int24(Q_SHIFT_RND(chirp_2ch[td->fill_idx++], 31, 23)); + + if (td->fill_idx == CHIRP_2CH_LENGTH) + td->continue_loop = false; + + return i / td->params.channels; +} + +/** + * @brief Verify S24 output range and pass-through or processing expectations. + */ +static bool verify_sink_s24(struct test_data *td, int frames) +{ + int32_t *y = audio_stream_get_addr(&td->sink->stream); + int samples = frames * td->params.channels; + int i; + + for (i = 0; i < samples; i++) { + const int32_t output = (y[i] << 8) >> 8; + const int32_t expected = + sat_int24(Q_SHIFT_RND(chirp_2ch[td->verify_idx++], 31, 23)); + + if (output >= S24_MAX_ABS || output < -S24_MAX_ABS) { + td->result.passed = false; + td->result.sample = td->verify_idx - 1; + td->result.output = output; + td->result.expected = expected; + return false; + } + + if (td->params.passthrough && output != expected) { + td->result.passed = false; + td->result.sample = td->verify_idx - 1; + td->result.output = output; + td->result.expected = expected; + return false; + } + + if (!td->params.passthrough && output != expected) + td->diff_count++; + } + + return true; +} +#endif /* CONFIG_FORMAT_S24LE */ + +#if CONFIG_FORMAT_S32LE +/** + * @brief Fill the S32 source buffer with test-vector samples. + */ +static int fill_source_s32(struct test_data *td, int frames) +{ + int32_t *x = audio_stream_get_addr(&td->source->stream); + int samples = frames * td->params.channels; + int available = CHIRP_2CH_LENGTH - td->fill_idx; + int i; + + samples = MIN(samples, available); + for (i = 0; i < samples; i++) + x[i] = chirp_2ch[td->fill_idx++]; + + if (td->fill_idx == CHIRP_2CH_LENGTH) + td->continue_loop = false; + + return i / td->params.channels; +} + +/** + * @brief Verify S32 output against pass-through or processing expectations. + */ +static bool verify_sink_s32(struct test_data *td, int frames) +{ + int32_t *y = audio_stream_get_addr(&td->sink->stream); + int samples = frames * td->params.channels; + int i; + + for (i = 0; i < samples; i++) { + const int32_t output = y[i]; + const int32_t expected = chirp_2ch[td->verify_idx++]; + + if (td->params.passthrough && output != expected) { + td->result.passed = false; + td->result.sample = td->verify_idx - 1; + td->result.output = output; + td->result.expected = expected; + return false; + } + + if (!td->params.passthrough && output != expected) + td->diff_count++; + } + + return true; +} +#endif /* CONFIG_FORMAT_S32LE */ + +/** + * @brief Fill the source buffer with the next part of the common test vector. + */ +static int fill_source(struct test_data *td, int frames) +{ + switch (td->params.source_format) { +#if CONFIG_FORMAT_S16LE + case SOF_IPC_FRAME_S16_LE: + return fill_source_s16(td, frames); +#endif +#if CONFIG_FORMAT_S24LE + case SOF_IPC_FRAME_S24_4LE: + return fill_source_s24(td, frames); +#endif +#if CONFIG_FORMAT_S32LE + case SOF_IPC_FRAME_S32_LE: + return fill_source_s32(td, frames); +#endif + default: + td->result.passed = false; + return 0; + } +} + +/** + * @brief Verify one output buffer against the input vector or range limits. + */ +static bool verify_sink(struct test_data *td, int frames) +{ + switch (td->params.sink_format) { +#if CONFIG_FORMAT_S16LE + case SOF_IPC_FRAME_S16_LE: + return verify_sink_s16(td, frames); +#endif +#if CONFIG_FORMAT_S24LE + case SOF_IPC_FRAME_S24_4LE: + return verify_sink_s24(td, frames); +#endif +#if CONFIG_FORMAT_S32LE + case SOF_IPC_FRAME_S32_LE: + return verify_sink_s32(td, frames); +#endif + default: + td->result.passed = false; + return false; + } +} + +/** + * @brief Run one DRC processing case over the complete two-channel vector. + */ +static bool run_drc_test(struct test_data *td) +{ + struct processing_module *mod = comp_mod(td->dev); + struct drc_comp_data *cd = module_get_private_data(mod); + struct cir_buf_source source_buf; + struct cir_buf_sink sink_buf; + int frames; + + while (td->continue_loop) { + frames = fill_source(td, td->params.frames); + if (frames <= 0) + break; + + make_views(td, &source_buf, &sink_buf); + cd->drc_func(mod, &source_buf, &sink_buf, frames); + + if (!verify_sink(td, frames)) + return false; + } + + if (!td->params.passthrough && td->diff_count == 0) + td->result.passed = false; + + return td->result.passed; +} + +static const struct test_parameters drc_parameters[] = { +#if CONFIG_FORMAT_S16LE + { 2, 48, 2, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, drc_coef_pass_2ch, true }, + { 2, 48, 2, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, drc_coef_enabled_2ch, false }, +#endif /* CONFIG_FORMAT_S16LE */ +#if CONFIG_FORMAT_S24LE + { 2, 48, 2, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, drc_coef_pass_2ch, true }, + { 2, 48, 2, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, drc_coef_enabled_2ch, false }, +#endif /* CONFIG_FORMAT_S24LE */ +#if CONFIG_FORMAT_S32LE + { 2, 48, 2, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, drc_coef_pass_2ch, true }, + { 2, 48, 2, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, drc_coef_enabled_2ch, false }, +#endif /* CONFIG_FORMAT_S32LE */ +}; + +/** + * @brief Execute one DRC case and report failures through Ztest. + */ +static void run_drc_case(const struct test_parameters *params) +{ + struct test_data *td = create_test_data(params); + struct test_result result; + bool passed; + + if (!td) { + zassert_true(false, "Failed to initialize DRC test case"); + return; + } + + passed = run_drc_test(td); + result = td->result; + destroy_test_data(td); + + zassert_true(passed, "DRC output mismatch at sample %u: output %d, expected %d", + result.sample, result.output, result.expected); +} + +/** + * @brief Initialize the SOF component registry for the processing suite. + */ +static void *drc_process_suite_setup(void) +{ + sys_comp_init(sof_get()); + sys_comp_module_drc_interface_init(); + + return NULL; +} + +#if CONFIG_FORMAT_S16LE +/** + * @brief Verify S16 DRC pass-through output. + */ +ZTEST(drc_process_suite, test_drc_process_s16_passthrough) +{ + run_drc_case(&drc_parameters[0]); +} + +/** + * @brief Verify enabled S16 DRC processing changes the signal. + */ +ZTEST(drc_process_suite, test_drc_process_s16_enabled) +{ + run_drc_case(&drc_parameters[1]); +} +#endif /* CONFIG_FORMAT_S16LE */ + +#if CONFIG_FORMAT_S24LE +/** + * @brief Verify S24 DRC pass-through output. + */ +ZTEST(drc_process_suite, test_drc_process_s24_passthrough) +{ + run_drc_case(&drc_parameters[2]); +} + +/** + * @brief Verify enabled S24 DRC processing changes the signal. + */ +ZTEST(drc_process_suite, test_drc_process_s24_enabled) +{ + run_drc_case(&drc_parameters[3]); +} +#endif /* CONFIG_FORMAT_S24LE */ + +#if CONFIG_FORMAT_S32LE +/** + * @brief Verify S32 DRC pass-through output. + */ +ZTEST(drc_process_suite, test_drc_process_s32_passthrough) +{ + run_drc_case(&drc_parameters[4]); +} + +/** + * @brief Verify enabled S32 DRC processing changes the signal. + */ +ZTEST(drc_process_suite, test_drc_process_s32_enabled) +{ + run_drc_case(&drc_parameters[5]); +} +#endif /* CONFIG_FORMAT_S32LE */ + +ZTEST_SUITE(drc_process_suite, NULL, drc_process_suite_setup, NULL, NULL, NULL); diff --git a/test/ztest/unit/audio/drc/drc_test_alloc.c b/test/ztest/unit/audio/drc/drc_test_alloc.c new file mode 100644 index 000000000000..a3c6794b60eb --- /dev/null +++ b/test/ztest/unit/audio/drc/drc_test_alloc.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +#include +#include +#include + +#include +#include +#include +#include + +static struct sof sof_context; +static bool sof_context_initialized; + +/** + * @brief Return the minimal SOF context used by the DRC Ztest application. + */ +struct sof *sof_get(void) +{ + if (!sof_context_initialized) { + sys_comp_init(&sof_context); + sof_context_initialized = true; + } + + return &sof_context; +} + +/** + * @brief Allocate aligned runtime memory for the standalone test application. + */ +void *rmalloc_align(uint32_t flags, size_t bytes, uint32_t alignment) +{ + (void)flags; + (void)alignment; + + return malloc(bytes); +} + +/** + * @brief Allocate runtime memory for the standalone test application. + */ +void *rmalloc(uint32_t flags, size_t bytes) +{ + (void)flags; + + return malloc(bytes); +} + +/** + * @brief Allocate zero-initialized runtime memory for the test application. + */ +void *rzalloc(uint32_t flags, size_t bytes) +{ + (void)flags; + + return calloc(bytes, 1); +} + +/** + * @brief Allocate aligned buffer memory for the standalone test application. + */ +void *rballoc_align(uint32_t flags, size_t bytes, uint32_t alignment) +{ + (void)flags; + (void)alignment; + + return malloc(bytes); +} + +/** + * @brief Release memory allocated by the standalone test application. + */ +void rfree(void *ptr) +{ + free(ptr); +} + +/** + * @brief Allocate memory from the test application's heap abstraction. + */ +void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes, + size_t alignment) +{ + (void)heap; + (void)flags; + (void)alignment; + + return malloc(bytes); +} + +/** + * @brief Release memory allocated through the test heap abstraction. + */ +void sof_heap_free(struct k_heap *heap, void *addr) +{ + (void)heap; + + free(addr); +} + +/** + * @brief Return the absent user heap in the standalone test application. + */ +struct k_heap *sof_sys_user_heap_get(void) +{ + return NULL; +} diff --git a/test/ztest/unit/audio/drc/drc_test_coef.h b/test/ztest/unit/audio/drc/drc_test_coef.h new file mode 100644 index 000000000000..8875d530aa3c --- /dev/null +++ b/test/ztest/unit/audio/drc/drc_test_coef.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2024 Intel Corporation. + * + * DRC configuration blobs for the process unit test. Generated from + * tools/ctl/ipc4/drc/{passthrough,speaker_default}.txt (see + * src/audio/drc/tune/sof_example_drc.m). + */ + +#ifndef SOF_ZTEST_DRC_TEST_COEF_H +#define SOF_ZTEST_DRC_TEST_COEF_H + +#include + +/** DRC configuration with processing disabled. */ +static const uint32_t drc_coef_pass_2ch[35] = { + 0x00464f53, 0x00000000, 0x0000006c, 0x03013000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000006c, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xe8000000, 0x1e000000, + 0x0c000000, 0x00624dd3, 0x0409c2b1, 0x05555555, + 0x001efa50, 0x00946055, 0xff6a987e, 0x01fec983, + 0x22474764, 0x01745617, 0x0071c71c, 0xff777777, + 0x001f77d8, 0x00000005, 0x00438000, 0x00047dd7, + 0x0025cea0, 0x00097dd7, 0x0000b5b1 +}; + +/** DRC configuration with the small-speaker processing curve enabled. */ +static const uint32_t drc_coef_enabled_2ch[35] = { + 0x00464f53, 0x00000000, 0x0000006c, 0x03013000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000006c, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0xe2000000, 0x14000000, + 0x0a000000, 0x00624dd3, 0x02061b8a, 0x06666666, + 0x00ba972f, 0x001e0c18, 0xffe04220, 0x0050f44e, + 0x08349f9a, 0x04d82cd3, 0x0071c71c, 0xff777777, + 0x001f77d8, 0x00000005, 0x00438000, 0x00047dd7, + 0x0025cea0, 0x00097dd7, 0x0000b5b1 +}; + +#endif /* SOF_ZTEST_DRC_TEST_COEF_H */ diff --git a/test/ztest/unit/audio/drc/prj.conf b/test/ztest/unit/audio/drc/prj.conf new file mode 100644 index 000000000000..165b83b0f1e4 --- /dev/null +++ b/test/ztest/unit/audio/drc/prj.conf @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2026 Intel Corporation. + +CONFIG_ZTEST=y +CONFIG_SOF_FULL_ZEPHYR_APPLICATION=n +CONFIG_LOG=y +CONFIG_COMP_DRC=y +CONFIG_FORMAT_S16LE=y +CONFIG_FORMAT_S24LE=y +CONFIG_FORMAT_S32LE=y +CONFIG_MATH_EXP=y +CONFIG_MATH_LUT_SINE_FIXED=y +CONFIG_NUMBERS_NORM=y diff --git a/test/ztest/unit/audio/drc/testcase.yaml b/test/ztest/unit/audio/drc/testcase.yaml new file mode 100644 index 000000000000..70ba96e762ba --- /dev/null +++ b/test/ztest/unit/audio/drc/testcase.yaml @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: BSD-3-Clause +# +# Copyright(c) 2026 Intel Corporation. + +common: + tags: + - SOF + - unit_test + - audio + - drc + integration_platforms: + - native_sim + arch_exclude: xtensa + +tests: + sof.unit.audio.drc: + platform_allow: + - native_sim From b70c5db2f521bbb9fe57af28f23c4721df93d2d8 Mon Sep 17 00:00:00 2001 From: Piotr Hoppe Date: Fri, 10 Jul 2026 21:12:47 +0200 Subject: [PATCH 2/2] audio: drc: switch to source/sink processing API Convert the DRC module from the legacy audio_stream processing API to the modern sof_source/sof_sink API. --- src/audio/drc/drc.c | 70 +++++++++++++++---- src/audio/drc/drc.h | 11 +-- src/audio/drc/drc_generic.c | 133 ++++++++++++++++++++---------------- src/audio/drc/drc_hifi4.c | 122 +++++++++++++++++++-------------- 4 files changed, 211 insertions(+), 125 deletions(-) diff --git a/src/audio/drc/drc.c b/src/audio/drc/drc.c index a727b68d55fd..8709d9abcb4b 100644 --- a/src/audio/drc/drc.c +++ b/src/audio/drc/drc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -269,16 +270,22 @@ __cold static int drc_get_config(struct processing_module *mod, } static int drc_process(struct processing_module *mod, - struct input_stream_buffer *input_buffers, - int num_input_buffers, - struct output_stream_buffer *output_buffers, - int num_output_buffers) + struct sof_source **sources, + int num_of_sources, + struct sof_sink **sinks, + int num_of_sinks) { struct drc_comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct audio_stream *source = input_buffers[0].data; - struct audio_stream *sink = output_buffers[0].data; - int frames = input_buffers[0].size; + struct sof_source *source = sources[0]; + struct sof_sink *sink = sinks[0]; + struct cir_buf_source source_buf; + struct cir_buf_sink sink_buf; + size_t source_frame_bytes = source_get_frame_bytes(source); + size_t sink_frame_bytes = sink_get_frame_bytes(sink); + size_t source_bytes, sink_bytes; + size_t source_buf_size, sink_buf_size; + uint32_t frames; int ret; comp_dbg(dev, "entry"); @@ -286,8 +293,8 @@ static int drc_process(struct processing_module *mod, /* Check for changed configuration */ if (comp_is_new_data_blob_available(cd->model_handler)) { cd->config = comp_get_data_blob(cd->model_handler, NULL, NULL); - ret = drc_setup(mod, audio_stream_get_channels(source), - audio_stream_get_rate(source)); + ret = drc_setup(mod, source_get_channels(source), + source_get_rate(source)); if (ret < 0) { comp_err(dev, "drc_copy(), failed DRC setup"); return ret; @@ -309,10 +316,46 @@ static int drc_process(struct processing_module *mod, /* Control pass-though in processing function with switch control */ cd->enabled = cd->config && cd->config->params.enabled && cd->enable_switch; - cd->drc_func(mod, source, sink, frames); + frames = source_sink_avail_frames_aligned(source, sink); + if (!frames) + return 0; + + source_bytes = frames * source_frame_bytes; + sink_bytes = frames * sink_frame_bytes; + + /* acquire source and sink circular buffers for the whole period */ + ret = source_get_data(source, source_bytes, &source_buf.ptr, + &source_buf.buf_start, &source_buf_size); + if (ret < 0) + return ret; + if (source_buf_size < source_bytes) { + comp_err(dev, "source buffer size %zu is insufficient for %zu bytes", + source_buf_size, source_bytes); + source_release_data(source, 0); + return -EINVAL; + } + source_buf.buf_end = (const char *)source_buf.buf_start + source_buf_size; + + ret = sink_get_buffer(sink, sink_bytes, &sink_buf.ptr, &sink_buf.buf_start, + &sink_buf_size); + if (ret < 0) { + source_release_data(source, 0); + return ret; + } + if (sink_buf_size < sink_bytes) { + comp_err(dev, "sink buffer size %zu is insufficient for %zu bytes", + sink_buf_size, sink_bytes); + source_release_data(source, 0); + sink_commit_buffer(sink, 0); + return -EINVAL; + } + sink_buf.buf_end = (char *)sink_buf.buf_start + sink_buf_size; + + cd->drc_func(mod, &source_buf, &sink_buf, frames); - /* calc new free and available */ - module_update_buffer_position(&input_buffers[0], &output_buffers[0], frames); + /* commit the consumed and produced data */ + source_release_data(source, source_bytes); + sink_commit_buffer(sink, sink_bytes); return 0; } @@ -367,6 +410,7 @@ static int drc_prepare(struct processing_module *mod, cd->source_format = audio_stream_get_frm_fmt(&sourceb->stream); channels = audio_stream_get_channels(&sinkb->stream); rate = audio_stream_get_rate(&sinkb->stream); + cd->channels = channels; /* Initialize DRC */ comp_info(dev, "source_format=%d", cd->source_format); @@ -414,7 +458,7 @@ static int drc_reset(struct processing_module *mod) static const struct module_interface drc_interface = { .init = drc_init, .prepare = drc_prepare, - .process_audio_stream = drc_process, + .process = drc_process, .set_configuration = drc_set_config, .get_configuration = drc_get_config, .reset = drc_reset, diff --git a/src/audio/drc/drc.h b/src/audio/drc/drc.h index 5598d64c87de..140877b21895 100644 --- a/src/audio/drc/drc.h +++ b/src/audio/drc/drc.h @@ -15,6 +15,8 @@ #include "drc_user.h" struct audio_stream; +struct cir_buf_source; +struct cir_buf_sink; struct comp_dev; /* Define CONFIG_DRC_MAX_PRE_DELAY_FRAMES for the build purposes without Kconfig, @@ -66,8 +68,8 @@ struct drc_state { }; typedef void (*drc_func)(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames); /* DRC component private data */ @@ -79,6 +81,7 @@ struct drc_comp_data { bool enabled; /**< control processing via blob and switch */ bool enable_switch; /**< enable switch state */ enum sof_ipc_frame source_format; /**< source frame format */ + int channels; /**< number of channels */ drc_func drc_func; /**< processing function */ }; @@ -91,8 +94,8 @@ extern const struct drc_proc_fnmap drc_proc_fnmap[]; extern const size_t drc_proc_fncount; void drc_default_pass(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, uint32_t frames); + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames); /** * \brief Returns DRC processing function. */ diff --git a/src/audio/drc/drc_generic.c b/src/audio/drc/drc_generic.c index 8ace19623045..4b0a686bc04a 100644 --- a/src/audio/drc/drc_generic.c +++ b/src/audio/drc/drc_generic.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "drc.h" @@ -471,10 +472,31 @@ static void drc_process_one_division(struct drc_state *state, } void drc_default_pass(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, uint32_t frames) + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - audio_stream_copy(source, 0, sink, 0, frames * audio_stream_get_channels(source)); + struct drc_comp_data *cd = module_get_private_data(mod); + const int sample_bytes = get_sample_bytes(cd->source_format); + size_t bytes = (size_t)frames * cd->channels * sample_bytes; + const uint8_t *src = source->ptr; + uint8_t *dst = sink->ptr; + int n; + + while (bytes) { + n = MIN((const uint8_t *)source->buf_end - src, + (uint8_t *)sink->buf_end - dst); + n = MIN(n, bytes); + if (!n) { + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); + continue; + } + memcpy_s(dst, n, src, n); + src = cir_buf_wrap((void *)(src + n), (void *)source->buf_start, + (void *)source->buf_end); + dst = cir_buf_wrap(dst + n, sink->buf_start, sink->buf_end); + bytes -= n; + } } static inline void drc_pre_delay_index_inc(int *idx, int increment) @@ -484,26 +506,25 @@ static inline void drc_pre_delay_index_inc(int *idx, int increment) #if CONFIG_FORMAT_S16LE static void drc_delay_input_sample_s16(struct drc_state *state, - const struct audio_stream *source, - struct audio_stream *sink, - int16_t **x, int16_t **y, int samples) + const struct cir_buf_source *source, + struct cir_buf_sink *sink, + const int16_t **x, int16_t **y, int samples, int nch) { - int16_t *x1; + const int16_t *x1; int16_t *y1; int16_t *pd; int pd_write_index, pd_read_index; int nbuf, npcm, nfrm; int ch; int i; - int16_t *x0 = *x; + const int16_t *x0 = *x; int16_t *y0 = *y; int remaining_samples = samples; - int nch = audio_stream_get_channels(source); while (remaining_samples) { - nbuf = audio_stream_samples_without_wrap_s16(source, x0); + nbuf = cir_buf_samples_without_wrap_s16(x0, source->buf_end); npcm = MIN(remaining_samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s16(sink, y0); + nbuf = cir_buf_samples_without_wrap_s16(y0, sink->buf_end); npcm = MIN(npcm, nbuf); nfrm = npcm / nch; for (ch = 0; ch < nch; ++ch) { @@ -522,8 +543,8 @@ static void drc_delay_input_sample_s16(struct drc_state *state, } } remaining_samples -= npcm; - x0 = audio_stream_wrap(source, x0 + npcm); - y0 = audio_stream_wrap(sink, y0 + npcm); + x0 = cir_buf_wrap(x0 + npcm, source->buf_start, source->buf_end); + y0 = cir_buf_wrap(y0 + npcm, sink->buf_start, sink->buf_end); drc_pre_delay_index_inc(&state->pre_delay_write_index, nfrm); drc_pre_delay_index_inc(&state->pre_delay_read_index, nfrm); } @@ -533,15 +554,15 @@ static void drc_delay_input_sample_s16(struct drc_state *state, } static void drc_s16_default(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - int16_t *x = audio_stream_get_rptr(source); - int16_t *y = audio_stream_get_wptr(sink); - int nch = audio_stream_get_channels(source); - int samples = frames * nch; struct drc_comp_data *cd = module_get_private_data(mod); + int nch = cd->channels; + const int16_t *x = (int16_t *)source->ptr; + int16_t *y = (int16_t *)sink->ptr; + int samples = frames * nch; struct drc_state *state = &cd->state; const struct sof_drc_params *p = &cd->config->params; /* Read-only */ int fragment_samples; @@ -552,7 +573,7 @@ static void drc_s16_default(struct processing_module *mod, * DRC is disabled. We want to do this to match the processing delay of other bands * in multi-band DRC kernel case. */ - drc_delay_input_sample_s16(state, source, sink, &x, &y, samples); + drc_delay_input_sample_s16(state, source, sink, &x, &y, samples, nch); return; } @@ -567,7 +588,7 @@ static void drc_s16_default(struct processing_module *mod, (state->pre_delay_write_index & DRC_DIVISION_FRAMES_MASK); fragment_samples = fragment * nch; fragment_samples = MIN(samples, fragment_samples); - drc_delay_input_sample_s16(state, source, sink, &x, &y, fragment_samples); + drc_delay_input_sample_s16(state, source, sink, &x, &y, fragment_samples, nch); samples -= fragment_samples; /* Process the input division (32 frames). */ @@ -579,26 +600,25 @@ static void drc_s16_default(struct processing_module *mod, #if CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE static void drc_delay_input_sample_s32(struct drc_state *state, - const struct audio_stream *source, - struct audio_stream *sink, - int32_t **x, int32_t **y, int samples) + const struct cir_buf_source *source, + struct cir_buf_sink *sink, + const int32_t **x, int32_t **y, int samples, int nch) { - int32_t *x1; + const int32_t *x1; int32_t *y1; int32_t *pd; int pd_write_index, pd_read_index; int nbuf, npcm, nfrm; int ch; int i; - int32_t *x0 = *x; + const int32_t *x0 = *x; int32_t *y0 = *y; int remaining_samples = samples; - int nch = audio_stream_get_channels(source); while (remaining_samples) { - nbuf = audio_stream_samples_without_wrap_s32(source, x0); + nbuf = cir_buf_samples_without_wrap_s32(x0, source->buf_end); npcm = MIN(remaining_samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s32(sink, y0); + nbuf = cir_buf_samples_without_wrap_s32(y0, sink->buf_end); npcm = MIN(npcm, nbuf); nfrm = npcm / nch; for (ch = 0; ch < nch; ++ch) { @@ -617,8 +637,8 @@ static void drc_delay_input_sample_s32(struct drc_state *state, } } remaining_samples -= npcm; - x0 = audio_stream_wrap(source, x0 + npcm); - y0 = audio_stream_wrap(sink, y0 + npcm); + x0 = cir_buf_wrap(x0 + npcm, source->buf_start, source->buf_end); + y0 = cir_buf_wrap(y0 + npcm, sink->buf_start, sink->buf_end); drc_pre_delay_index_inc(&state->pre_delay_write_index, nfrm); drc_pre_delay_index_inc(&state->pre_delay_read_index, nfrm); } @@ -630,26 +650,25 @@ static void drc_delay_input_sample_s32(struct drc_state *state, #if CONFIG_FORMAT_S24LE static void drc_delay_input_sample_s24(struct drc_state *state, - const struct audio_stream *source, - struct audio_stream *sink, - int32_t **x, int32_t **y, int samples) + const struct cir_buf_source *source, + struct cir_buf_sink *sink, + const int32_t **x, int32_t **y, int samples, int nch) { - int32_t *x1; + const int32_t *x1; int32_t *y1; int32_t *pd; int pd_write_index, pd_read_index; int nbuf, npcm, nfrm; int ch; int i; - int32_t *x0 = *x; + const int32_t *x0 = *x; int32_t *y0 = *y; int remaining_samples = samples; - int nch = audio_stream_get_channels(source); while (remaining_samples) { - nbuf = audio_stream_samples_without_wrap_s24(source, x0); + nbuf = cir_buf_samples_without_wrap_s32(x0, source->buf_end); npcm = MIN(remaining_samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s24(sink, y0); + nbuf = cir_buf_samples_without_wrap_s32(y0, sink->buf_end); npcm = MIN(npcm, nbuf); nfrm = npcm / nch; for (ch = 0; ch < nch; ++ch) { @@ -668,8 +687,8 @@ static void drc_delay_input_sample_s24(struct drc_state *state, } } remaining_samples -= npcm; - x0 = audio_stream_wrap(source, x0 + npcm); - y0 = audio_stream_wrap(sink, y0 + npcm); + x0 = cir_buf_wrap(x0 + npcm, source->buf_start, source->buf_end); + y0 = cir_buf_wrap(y0 + npcm, sink->buf_start, sink->buf_end); drc_pre_delay_index_inc(&state->pre_delay_write_index, nfrm); drc_pre_delay_index_inc(&state->pre_delay_read_index, nfrm); } @@ -679,15 +698,15 @@ static void drc_delay_input_sample_s24(struct drc_state *state, } static void drc_s24_default(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); - int nch = audio_stream_get_channels(source); - int samples = frames * nch; struct drc_comp_data *cd = module_get_private_data(mod); + int nch = cd->channels; + const int32_t *x = (int32_t *)source->ptr; + int32_t *y = (int32_t *)sink->ptr; + int samples = frames * nch; struct drc_state *state = &cd->state; const struct sof_drc_params *p = &cd->config->params; /* Read-only */ int fragment_samples; @@ -698,7 +717,7 @@ static void drc_s24_default(struct processing_module *mod, * DRC is disabled. We want to do this to match the processing delay of other bands * in multi-band DRC kernel case. Note: use 32 bit delay function. */ - drc_delay_input_sample_s32(state, source, sink, &x, &y, samples); + drc_delay_input_sample_s32(state, source, sink, &x, &y, samples, nch); return; } @@ -715,7 +734,7 @@ static void drc_s24_default(struct processing_module *mod, fragment_samples = MIN(samples, fragment_samples); /* Use 24 bit delay function */ - drc_delay_input_sample_s24(state, source, sink, &x, &y, fragment_samples); + drc_delay_input_sample_s24(state, source, sink, &x, &y, fragment_samples, nch); samples -= fragment_samples; /* Process the input division (32 frames). */ @@ -727,15 +746,15 @@ static void drc_s24_default(struct processing_module *mod, #if CONFIG_FORMAT_S32LE static void drc_s32_default(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); - int nch = audio_stream_get_channels(source); - int samples = frames * nch; struct drc_comp_data *cd = module_get_private_data(mod); + int nch = cd->channels; + const int32_t *x = (int32_t *)source->ptr; + int32_t *y = (int32_t *)sink->ptr; + int samples = frames * nch; struct drc_state *state = &cd->state; const struct sof_drc_params *p = &cd->config->params; /* Read-only */ int fragment_samples; @@ -746,7 +765,7 @@ static void drc_s32_default(struct processing_module *mod, * DRC is disabled. We want to do this to match the processing delay of other bands * in multi-band DRC kernel case. */ - drc_delay_input_sample_s32(state, source, sink, &x, &y, samples); + drc_delay_input_sample_s32(state, source, sink, &x, &y, samples, nch); return; } @@ -761,7 +780,7 @@ static void drc_s32_default(struct processing_module *mod, (state->pre_delay_write_index & DRC_DIVISION_FRAMES_MASK); fragment_samples = fragment * nch; fragment_samples = MIN(samples, fragment_samples); - drc_delay_input_sample_s32(state, source, sink, &x, &y, fragment_samples); + drc_delay_input_sample_s32(state, source, sink, &x, &y, fragment_samples, nch); samples -= fragment_samples; /* Process the input division (32 frames). */ diff --git a/src/audio/drc/drc_hifi4.c b/src/audio/drc/drc_hifi4.c index a4c32c0deb15..707aa2d89d98 100644 --- a/src/audio/drc/drc_hifi4.c +++ b/src/audio/drc/drc_hifi4.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "drc.h" @@ -525,10 +526,31 @@ static void drc_process_one_division(struct drc_state *state, } void drc_default_pass(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, uint32_t frames) + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - audio_stream_copy(source, 0, sink, 0, frames * audio_stream_get_channels(source)); + struct drc_comp_data *cd = module_get_private_data(mod); + const int sample_bytes = get_sample_bytes(cd->source_format); + size_t bytes = (size_t)frames * cd->channels * sample_bytes; + const uint8_t *src = source->ptr; + uint8_t *dst = sink->ptr; + int n; + + while (bytes) { + n = MIN((const uint8_t *)source->buf_end - src, + (uint8_t *)sink->buf_end - dst); + n = MIN(n, bytes); + if (!n) { + src = cir_buf_wrap(src, source->buf_start, source->buf_end); + dst = cir_buf_wrap(dst, sink->buf_start, sink->buf_end); + continue; + } + memcpy_s(dst, n, src, n); + src = cir_buf_wrap((void *)(src + n), (void *)source->buf_start, + (void *)source->buf_end); + dst = cir_buf_wrap(dst + n, sink->buf_start, sink->buf_end); + bytes -= n; + } } static inline void drc_pre_delay_index_inc(int *idx, int increment) @@ -538,9 +560,9 @@ static inline void drc_pre_delay_index_inc(int *idx, int increment) #if CONFIG_FORMAT_S16LE static void drc_delay_input_sample_s16(struct drc_state *state, - const struct audio_stream *source, - struct audio_stream *sink, - int16_t **x, int16_t **y, int samples) + const struct cir_buf_source *source, + struct cir_buf_sink *sink, + int16_t **x, int16_t **y, int samples, int nch) { ae_int16 *x1; ae_int16 *y1; @@ -553,15 +575,14 @@ static void drc_delay_input_sample_s16(struct drc_state *state, ae_int16 *x0 = (ae_int16 *)*x; ae_int16 *y0 = (ae_int16 *)*y; int remaining_samples = samples; - int nch = audio_stream_get_channels(source); const int sample_inc = nch * sizeof(int16_t); const int delay_inc = sizeof(int16_t); ae_int16x4 sample; while (remaining_samples) { - nbuf = audio_stream_samples_without_wrap_s16(source, x0); + nbuf = cir_buf_samples_without_wrap_s16(x0, source->buf_end); npcm = MIN(remaining_samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s16(sink, y0); + nbuf = cir_buf_samples_without_wrap_s16(y0, sink->buf_end); npcm = MIN(npcm, nbuf); nfrm = npcm / nch; for (ch = 0; ch < nch; ++ch) { @@ -580,8 +601,8 @@ static void drc_delay_input_sample_s16(struct drc_state *state, } } remaining_samples -= npcm; - x0 = audio_stream_wrap(source, x0 + npcm); - y0 = audio_stream_wrap(sink, y0 + npcm); + x0 = cir_buf_wrap(x0 + npcm, source->buf_start, source->buf_end); + y0 = cir_buf_wrap(y0 + npcm, sink->buf_start, sink->buf_end); drc_pre_delay_index_inc(&state->pre_delay_write_index, nfrm); drc_pre_delay_index_inc(&state->pre_delay_read_index, nfrm); } @@ -591,15 +612,15 @@ static void drc_delay_input_sample_s16(struct drc_state *state, } static void drc_s16_default(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - int16_t *x = audio_stream_get_rptr(source); - int16_t *y = audio_stream_get_wptr(sink); - int nch = audio_stream_get_channels(source); - int samples = frames * nch; struct drc_comp_data *cd = module_get_private_data(mod); + int nch = cd->channels; + int16_t *x = (int16_t *)source->ptr; + int16_t *y = (int16_t *)sink->ptr; + int samples = frames * nch; struct drc_state *state = &cd->state; const struct sof_drc_params *p = &cd->config->params; /* Read-only */ int fragment; @@ -618,7 +639,7 @@ static void drc_s16_default(struct processing_module *mod, * DRC is disabled. We want to do this to match the processing delay of other bands * in multi-band DRC kernel case. */ - drc_delay_input_sample_s16(state, source, sink, &x, &y, samples); + drc_delay_input_sample_s16(state, source, sink, &x, &y, samples, nch); return; } @@ -628,8 +649,8 @@ static void drc_s16_default(struct processing_module *mod, state->processed = 1; } - set_circular_buf0(source->addr, source->end_addr); - set_circular_buf1(sink->addr, sink->end_addr); + set_circular_buf0((void *)source->buf_start, (void *)source->buf_end); + set_circular_buf1(sink->buf_start, sink->buf_end); while (frames) { fragment = DRC_DIVISION_FRAMES - @@ -659,8 +680,8 @@ static void drc_s16_default(struct processing_module *mod, } drc_pre_delay_index_inc(&state->pre_delay_write_index, fragment); drc_pre_delay_index_inc(&state->pre_delay_read_index, fragment); - x = audio_stream_wrap(source, x + fragment * nch); - y = audio_stream_wrap(sink, y + fragment * nch); + x = cir_buf_wrap(x + fragment * nch, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + fragment * nch, sink->buf_start, sink->buf_end); frames -= fragment; /* Process the input division (32 frames). */ @@ -672,9 +693,9 @@ static void drc_s16_default(struct processing_module *mod, #if CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE static void drc_delay_input_sample_s32(struct drc_state *state, - const struct audio_stream *source, - struct audio_stream *sink, - int32_t **x, int32_t **y, int samples) + const struct cir_buf_source *source, + struct cir_buf_sink *sink, + int32_t **x, int32_t **y, int samples, int nch) { ae_int32 *x1; ae_int32 *y1; @@ -689,14 +710,13 @@ static void drc_delay_input_sample_s32(struct drc_state *state, ae_int32x2 sample; int remaining_samples = samples; - int nch = audio_stream_get_channels(source); const int sample_inc = nch * sizeof(int32_t); const int delay_inc = sizeof(int32_t); while (remaining_samples) { - nbuf = audio_stream_samples_without_wrap_s32(source, x0); + nbuf = cir_buf_samples_without_wrap_s32(x0, source->buf_end); npcm = MIN(remaining_samples, nbuf); - nbuf = audio_stream_samples_without_wrap_s32(sink, y0); + nbuf = cir_buf_samples_without_wrap_s32(y0, sink->buf_end); npcm = MIN(npcm, nbuf); nfrm = npcm / nch; for (ch = 0; ch < nch; ++ch) { @@ -718,8 +738,8 @@ static void drc_delay_input_sample_s32(struct drc_state *state, } } remaining_samples -= npcm; - x0 = audio_stream_wrap(source, x0 + npcm); - y0 = audio_stream_wrap(sink, y0 + npcm); + x0 = cir_buf_wrap(x0 + npcm, source->buf_start, source->buf_end); + y0 = cir_buf_wrap(y0 + npcm, sink->buf_start, sink->buf_end); drc_pre_delay_index_inc(&state->pre_delay_write_index, nfrm); drc_pre_delay_index_inc(&state->pre_delay_read_index, nfrm); } @@ -732,15 +752,15 @@ static void drc_delay_input_sample_s32(struct drc_state *state, #if CONFIG_FORMAT_S24LE static void drc_s24_default(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); - int nch = audio_stream_get_channels(source); - int samples = frames * nch; struct drc_comp_data *cd = module_get_private_data(mod); + int nch = cd->channels; + int32_t *x = (int32_t *)source->ptr; + int32_t *y = (int32_t *)sink->ptr; + int samples = frames * nch; struct drc_state *state = &cd->state; const struct sof_drc_params *p = &cd->config->params; /* Read-only */ int fragment; @@ -759,7 +779,7 @@ static void drc_s24_default(struct processing_module *mod, * DRC is disabled. We want to do this to match the processing delay of other bands * in multi-band DRC kernel case. Note: use 32 bit delay function. */ - drc_delay_input_sample_s32(state, source, sink, &x, &y, samples); + drc_delay_input_sample_s32(state, source, sink, &x, &y, samples, nch); return; } @@ -769,8 +789,8 @@ static void drc_s24_default(struct processing_module *mod, state->processed = 1; } - set_circular_buf0(source->addr, source->end_addr); - set_circular_buf1(sink->addr, sink->end_addr); + set_circular_buf0((void *)source->buf_start, (void *)source->buf_end); + set_circular_buf1(sink->buf_start, sink->buf_end); while (frames) { fragment = DRC_DIVISION_FRAMES - @@ -807,8 +827,8 @@ static void drc_s24_default(struct processing_module *mod, } drc_pre_delay_index_inc(&state->pre_delay_write_index, fragment); drc_pre_delay_index_inc(&state->pre_delay_read_index, fragment); - x = audio_stream_wrap(source, x + fragment * nch); - y = audio_stream_wrap(sink, y + fragment * nch); + x = cir_buf_wrap(x + fragment * nch, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + fragment * nch, sink->buf_start, sink->buf_end); frames -= fragment; /* Process the input division (32 frames). */ @@ -820,14 +840,14 @@ static void drc_s24_default(struct processing_module *mod, #if CONFIG_FORMAT_S32LE static void drc_s32_default(struct processing_module *mod, - const struct audio_stream *source, - struct audio_stream *sink, + const struct cir_buf_source *source, + struct cir_buf_sink *sink, uint32_t frames) { - int32_t *x = audio_stream_get_rptr(source); - int32_t *y = audio_stream_get_wptr(sink); - int nch = audio_stream_get_channels(source); struct drc_comp_data *cd = module_get_private_data(mod); + int nch = cd->channels; + int32_t *x = (int32_t *)source->ptr; + int32_t *y = (int32_t *)sink->ptr; struct drc_state *state = &cd->state; const struct sof_drc_params *p = &cd->config->params; /* Read-only */ int fragment; @@ -846,7 +866,7 @@ static void drc_s32_default(struct processing_module *mod, * DRC is disabled. We want to do this to match the processing delay of other bands * in multi-band DRC kernel case. */ - drc_delay_input_sample_s32(state, source, sink, &x, &y, frames * nch); + drc_delay_input_sample_s32(state, source, sink, &x, &y, frames * nch, nch); return; } @@ -856,8 +876,8 @@ static void drc_s32_default(struct processing_module *mod, state->processed = 1; } - set_circular_buf0(source->addr, source->end_addr); - set_circular_buf1(sink->addr, sink->end_addr); + set_circular_buf0((void *)source->buf_start, (void *)source->buf_end); + set_circular_buf1(sink->buf_start, sink->buf_end); while (frames) { fragment = DRC_DIVISION_FRAMES - @@ -889,8 +909,8 @@ static void drc_s32_default(struct processing_module *mod, } drc_pre_delay_index_inc(&state->pre_delay_write_index, fragment); drc_pre_delay_index_inc(&state->pre_delay_read_index, fragment); - x = audio_stream_wrap(source, x + fragment * nch); - y = audio_stream_wrap(sink, y + fragment * nch); + x = cir_buf_wrap(x + fragment * nch, source->buf_start, source->buf_end); + y = cir_buf_wrap(y + fragment * nch, sink->buf_start, sink->buf_end); frames -= fragment; /* Process the input division (32 frames). */