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). */ 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