diff --git a/test/cmocka/Makefile.am b/test/cmocka/Makefile.am index 04376658d27e..6f15f4b05c05 100644 --- a/test/cmocka/Makefile.am +++ b/test/cmocka/Makefile.am @@ -19,6 +19,7 @@ endif if BUILD_XTENSA AM_CFLAGS += -I../../src/arch/xtensa/include AM_CFLAGS += -I../../src/platform/apollolake/include +AM_CFLAGS += -I../../src/audio endif if BUILD_HOST @@ -30,6 +31,14 @@ LDFLAGS := $(filter-out -nostdlib,$(LDFLAGS)) LDADD = -lcmocka +# volume tests + +check_PROGRAMS += volume_process +volume_process_SOURCES = src/audio/volume/volume_process.c +volume_process_LDADD = ../../src/audio/libaudio.a $(LDADD) + +# buffer tests + check_PROGRAMS += buffer_new buffer_new_SOURCES = src/audio/buffer/buffer_new.c src/audio/buffer/mock.c buffer_new_LDADD = ../../src/audio/libaudio.a $(LDADD) diff --git a/test/cmocka/src/audio/volume/volume_process.c b/test/cmocka/src/audio/volume/volume_process.c new file mode 100644 index 000000000000..ee7e698c9d18 --- /dev/null +++ b/test/cmocka/src/audio/volume/volume_process.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2018, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Tomasz Lauda + */ + +#include +#include +#include +#include +#include +#include "volume.h" + +#define VOL_SCALE (uint32_t)((double)INT32_MAX / VOL_MAX) + +struct vol_test_state { + struct comp_dev *dev; + struct comp_buffer *sink; + struct comp_buffer *source; + void (*verify)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source); +}; + +struct vol_test_parameters { + uint32_t volume; + uint32_t channels; + uint32_t frames; + uint32_t buffer_size_ms; + uint32_t source_format; + uint32_t sink_format; + void (*verify)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source); +}; + +static void set_volume(uint32_t *vol, uint32_t value, uint32_t channels) +{ + int i; + + for (i = 0; i < channels; i++) + vol[i] = value; +} + +static int setup(void **state) +{ + struct vol_test_parameters *parameters = *state; + struct vol_test_state *vol_state; + struct comp_data *cd; + uint32_t size = 0; + + /* allocate new state */ + vol_state = test_malloc(sizeof(*vol_state)); + + /* allocate and set new device */ + vol_state->dev = test_malloc(COMP_SIZE(struct sof_ipc_comp_volume)); + vol_state->dev->params.channels = parameters->channels; + + /* allocate and set new data */ + cd = test_malloc(sizeof(*cd)); + comp_set_drvdata(vol_state->dev, cd); + cd->source_format = parameters->source_format; + cd->sink_format = parameters->sink_format; + cd->scale_vol = vol_get_processing_function(vol_state->dev); + set_volume(cd->volume, parameters->volume, parameters->channels); + + /* allocate new sink buffer */ + vol_state->sink = test_malloc(sizeof(*vol_state->sink)); + vol_state->dev->params.frame_fmt = parameters->sink_format; + size = parameters->frames * comp_frame_bytes(vol_state->dev); + vol_state->sink->w_ptr = test_calloc(parameters->buffer_size_ms, + size); + vol_state->sink->size = parameters->buffer_size_ms * size; + + /* allocate new source buffer */ + vol_state->source = test_malloc(sizeof(*vol_state->source)); + vol_state->dev->params.frame_fmt = parameters->source_format; + size = parameters->frames * comp_frame_bytes(vol_state->dev); + vol_state->source->r_ptr = test_calloc(parameters->buffer_size_ms, + size); + vol_state->source->size = parameters->buffer_size_ms * size; + + /* assigns verification function */ + vol_state->verify = parameters->verify; + + /* assign test state */ + *state = vol_state; + + return 0; +} + +static int teardown(void **state) +{ + struct vol_test_state *vol_state = *state; + struct comp_data *cd = comp_get_drvdata(vol_state->dev); + + /* free everything */ + test_free(cd); + test_free(vol_state->dev); + test_free(vol_state->sink->w_ptr); + test_free(vol_state->sink); + test_free(vol_state->source->r_ptr); + test_free(vol_state->source); + test_free(vol_state); + + return 0; +} + +static void fill_source_s16(struct vol_test_state *vol_state) +{ + int16_t *src = (int16_t *)vol_state->source->r_ptr; + int i; + + for (i = 0; i < vol_state->source->size / sizeof(int16_t); i++) + src[i] = i; +} + +static void fill_source_s32(struct vol_test_state *vol_state) +{ + int32_t *src = (int32_t *)vol_state->source->r_ptr; + int i; + + for (i = 0; i < vol_state->source->size / sizeof(int32_t); i++) + src[i] = i << 16; +} + +static void verify_s16_to_s16(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source) +{ + struct comp_data *cd = comp_get_drvdata(dev); + const uint16_t *src = (uint16_t *)source->r_ptr; + const uint16_t *dst = (uint16_t *)sink->w_ptr; + int channels = dev->params.channels; + int channel; + int i; + double processed; + + for (i = 0; i < sink->size / sizeof(uint16_t); i += channels) { + for (channel = 0; channel < channels; channel++) { + processed = src[i + channel] * + ((double)cd->volume[channel] * + (double)VOL_SCALE / INT32_MAX); + assert_int_equal(dst[i + channel], processed + 0.5); + } + } +} + +static void verify_s16_to_sX(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source) +{ + struct comp_data *cd = comp_get_drvdata(dev); + const uint16_t *src = (uint16_t *)source->r_ptr; + const uint32_t *dst = (uint32_t *)sink->w_ptr; + int channels = dev->params.channels; + int channel; + int i; + double processed; + int shift = 0; + + /* get shift value */ + if (cd->sink_format == SOF_IPC_FRAME_S24_4LE) + shift = 8; + else if (cd->sink_format == SOF_IPC_FRAME_S32_LE) + shift = 16; + + for (i = 0; i < sink->size / sizeof(uint32_t); i += channels) { + for (channel = 0; channel < channels; channel++) { + processed = src[i + channel] * + ((double)cd->volume[channel] * + (double)VOL_SCALE / INT32_MAX); + assert_int_equal(dst[i + channel], + (uint32_t)(processed + 0.5) << shift); + } + } +} + +static void verify_sX_to_s16(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source) +{ + struct comp_data *cd = comp_get_drvdata(dev); + const uint32_t *src = (uint32_t *)source->r_ptr; + const uint16_t *dst = (uint16_t *)sink->w_ptr; + int channels = dev->params.channels; + int channel; + int i; + double processed; + int shift = 0; + + /* get shift value */ + if (cd->source_format == SOF_IPC_FRAME_S24_4LE) + shift = 8; + + for (i = 0; i < sink->size / sizeof(uint16_t); i += channels) { + for (channel = 0; channel < channels; channel++) { + processed = (src[i + channel] << shift) * + ((double)cd->volume[channel] * + (double)VOL_SCALE / INT32_MAX); + assert_int_equal(dst[i + channel], + (uint32_t)(processed + 0.5) >> 16); + } + } +} + +static void verify_s24_to_s24_s32(struct comp_dev *dev, + struct comp_buffer *sink, + struct comp_buffer *source) +{ + struct comp_data *cd = comp_get_drvdata(dev); + const uint32_t *src = (uint32_t *)source->r_ptr; + const uint32_t *dst = (uint32_t *)sink->w_ptr; + int channels = dev->params.channels; + int channel; + int i; + double processed; + int shift = 0; + + /* get shift value */ + if (cd->sink_format == SOF_IPC_FRAME_S32_LE) + shift = 8; + + for (i = 0; i < sink->size / sizeof(uint32_t); i += channels) { + for (channel = 0; channel < channels; channel++) { + processed = (src[i + channel] << 8) * + ((double)cd->volume[channel] * + (double)VOL_SCALE / INT32_MAX); + assert_int_equal(dst[i + channel], + ((uint32_t)(processed + 0.5) >> 8) << shift); + } + } +} + +static void verify_s32_to_s24_s32(struct comp_dev *dev, + struct comp_buffer *sink, + struct comp_buffer *source) +{ + struct comp_data *cd = comp_get_drvdata(dev); + const uint32_t *src = (uint32_t *)source->r_ptr; + const uint32_t *dst = (uint32_t *)sink->w_ptr; + int channels = dev->params.channels; + int channel; + int i; + double processed; + int shift = 0; + + /* get shift value */ + if (cd->sink_format == SOF_IPC_FRAME_S24_4LE) + shift = 8; + + for (i = 0; i < sink->size / sizeof(uint32_t); i += channels) { + for (channel = 0; channel < channels; channel++) { + processed = src[i + channel] * + ((double)cd->volume[channel] * + (double)VOL_SCALE / INT32_MAX); + assert_int_equal(dst[i + channel], + (uint32_t)(processed + 0.5) >> shift); + } + } +} + +static void test_vol(void **state) +{ + struct vol_test_state *vol_state = *state; + struct comp_data *cd = comp_get_drvdata(vol_state->dev); + + switch (cd->source_format) { + case SOF_IPC_FRAME_S16_LE: + fill_source_s16(vol_state); + break; + case SOF_IPC_FRAME_S24_4LE: + case SOF_IPC_FRAME_S32_LE: + case SOF_IPC_FRAME_FLOAT: + fill_source_s32(vol_state); + break; + } + + cd->scale_vol(vol_state->dev, vol_state->sink, vol_state->source); + + vol_state->verify(vol_state->dev, vol_state->sink, vol_state->source); +} + +static struct vol_test_parameters parameters[] = { + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, verify_s16_to_s16 }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, verify_s16_to_s16 }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S16_LE, verify_s16_to_s16 }, + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, verify_s16_to_sX }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, verify_s16_to_sX }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S24_4LE, verify_s16_to_sX }, + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, verify_s16_to_sX }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, verify_s16_to_sX }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S16_LE, SOF_IPC_FRAME_S32_LE, verify_s16_to_sX }, + + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S16_LE, verify_sX_to_s16 }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S16_LE, verify_sX_to_s16 }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S16_LE, verify_sX_to_s16 }, + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, verify_s24_to_s24_s32 }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, verify_s24_to_s24_s32 }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S24_4LE, verify_s24_to_s24_s32 }, + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, verify_s24_to_s24_s32 }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, verify_s24_to_s24_s32 }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S24_4LE, SOF_IPC_FRAME_S32_LE, verify_s24_to_s24_s32 }, + + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, verify_sX_to_s16 }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, verify_sX_to_s16 }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S16_LE, verify_sX_to_s16 }, + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, verify_s32_to_s24_s32 }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, verify_s32_to_s24_s32 }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S24_4LE, verify_s32_to_s24_s32 }, + { VOL_MAX, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, verify_s32_to_s24_s32 }, + { VOL_MAX / 2, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, verify_s32_to_s24_s32 }, + { VOL_MAX / 3, 2, 48, 1, SOF_IPC_FRAME_S32_LE, SOF_IPC_FRAME_S32_LE, verify_s32_to_s24_s32 }, +}; + +int main(void) +{ + int i; + + struct CMUnitTest tests[ARRAY_SIZE(parameters)]; + + for (i = 0; i < ARRAY_SIZE(parameters); i++) { + tests[i].name = "test_vol"; + tests[i].test_func = test_vol; + tests[i].setup_func = setup; + tests[i].teardown_func = teardown; + tests[i].initial_state = ¶meters[i]; + } + + cmocka_set_message_output(CM_OUTPUT_TAP); + + return cmocka_run_group_tests(tests, NULL, NULL); +}