[Sound-open-firmware] [PATCH 4/4] This change provides an IIR equalizer component. The IIR equalizer can be used for any transducer or effects equalizer needs but it is especially recommended for speaker response equalization and other applications without dependence to matched phase response or best possible THD+N performance where FIR may be a better choise. IIR equalization is capable to strong response enhancement without need for large DSP resources consumption. The tool to create the EQ setup blobs is
--- src/audio/Makefile.am | 2 + src/audio/eq_iir.c | 496 +++++++++++++++++++++++++++++++++++++ src/audio/eq_iir.h | 83 +++++++ src/audio/iir.c | 161 ++++++++++++ src/audio/iir.h | 75 ++++++ src/include/reef/audio/component.h | 3 + src/include/reef/trace.h | 1 + src/include/uapi/ipc.h | 10 + src/tasks/audio.c | 1 + 9 files changed, 832 insertions(+) create mode 100644 src/audio/eq_iir.c create mode 100644 src/audio/eq_iir.h create mode 100644 src/audio/iir.c create mode 100644 src/audio/iir.h
diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am index 1da6757..285422e 100644 --- a/src/audio/Makefile.am +++ b/src/audio/Makefile.am @@ -1,6 +1,8 @@ noinst_LIBRARIES = libaudio.a
libaudio_a_SOURCES = \ + eq_iir.c \ + iir.c \ eq_fir.c \ fir.c \ tone.c \ diff --git a/src/audio/eq_iir.c b/src/audio/eq_iir.c new file mode 100644 index 0000000..96570b2 --- /dev/null +++ b/src/audio/eq_iir.c @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2017, 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + */ + +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <reef/reef.h> +#include <reef/lock.h> +#include <reef/list.h> +#include <reef/stream.h> +#include <reef/alloc.h> +#include <reef/work.h> +#include <reef/clock.h> +#include <reef/audio/component.h> +#include <reef/audio/pipeline.h> +#include <reef/audio/format.h> +#include "eq_iir.h" +#include "iir.h" + +#ifdef MODULE_TEST +#include <stdio.h> +#endif + +#define trace_eq_iir(__e) trace_event(TRACE_CLASS_EQ_IIR, __e) +#define tracev_eq_iir(__e) tracev_event(TRACE_CLASS_EQ_IIR, __e) +#define trace_eq_iir_error(__e) trace_error(TRACE_CLASS_EQ_IIR, __e) + +/* src component private data */ +struct comp_data { + struct eq_iir_configuration *config; + struct iir_state_df2t iir[PLATFORM_MAX_CHANNELS]; + void (*eq_iir_func)(struct comp_dev *dev, + struct comp_buffer *source, + struct comp_buffer *sink, + uint32_t frames); +}; + +/* + * EQ IIR algorithm code + */ + +static void eq_iir_s32_default(struct comp_dev *dev, + struct comp_buffer *source, struct comp_buffer *sink, uint32_t frames) +{ + + int ch, n, n_wrap_src, n_wrap_snk, n_wrap_min; + int32_t *src = (int32_t *) source->r_ptr; + int32_t *snk = (int32_t *) sink->w_ptr; + int nch = source->params.pcm->channels; + int32_t *x = src + nch - 1; + int32_t *y = snk + nch - 1; + struct comp_data *cd = comp_get_drvdata(dev); + + + for (ch = 0; ch < nch; ch++) { + n = frames * nch; + x = src++; + y = snk++; + while (n > 0) { + n_wrap_src = (int32_t *) source->end_addr - x; + n_wrap_snk = (int32_t *) sink->end_addr - y; + n_wrap_min = (n_wrap_src < n_wrap_snk) ? + n_wrap_src : n_wrap_snk; + if (n < n_wrap_min) { + /* No circular wrap need */ + while (n > 0) { + *y = iir_df2t(&cd->iir[ch], *x); + x += nch; + y += nch; + n -= nch; + } + } else { + /* Wrap in n_wrap_min/nch samples */ + while (n_wrap_min > 0) { + *y = iir_df2t(&cd->iir[ch], *x); + x += nch; + y += nch; + n_wrap_min -= nch; + n -= nch; + } + /* Check both source and destination for wrap */ + if (x > (int32_t *) source->end_addr) + x = (int32_t *) + ((size_t) x - source->alloc_size); + if (snk > (int32_t *) sink->end_addr) + y = (int32_t *) + ((size_t) y - sink->alloc_size); + } + } + + } + source->r_ptr = x - nch + 1; /* After previous loop the x and y */ + sink->w_ptr = y - nch + 1; /* point to one frame -1. */ + + comp_wrap_source_r_ptr_circular(source); + comp_wrap_sink_w_ptr_circular(sink); + comp_update_source_free_avail(source, frames * nch); + comp_update_sink_free_avail(sink, frames * nch); +} + +static void eq_iir_free_parameters(struct eq_iir_configuration **config) +{ + if (*config != NULL) + rfree(*config); + + *config = NULL; +} + +static void eq_iir_free_delaylines(struct iir_state_df2t *iir) +{ + int i = 0; + int64_t *delay = NULL; /* TODO should not need to know the type */ + + /* 1st active EQ delay line data is at beginning of the single + * allocated buffer + */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) { + if ((iir[i].delay != NULL) && (delay == NULL)) + delay = iir[i].delay; + + /* Point all delays to NULL to avoid duplicated free later */ + iir[i].delay = NULL; + } + + if (delay != NULL) + rfree(delay); + +} + +static int eq_iir_setup(struct iir_state_df2t iir[], + struct eq_iir_configuration *config, int nch) +{ + int i, j, idx, resp; + size_t s; + size_t size_sum = 0; + int64_t *iir_delay; /* TODO should not need to know the type */ + int response_index[PLATFORM_MAX_CHANNELS]; + + if (nch > PLATFORM_MAX_CHANNELS) + return -EINVAL; + + /* Free existing IIR channels data if it was allocated */ + eq_iir_free_delaylines(iir); + + /* Collect index of respose start positions in all_coefficients[] */ + j = 0; + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) { + if (i < config->number_of_responses_defined) { + response_index[i] = j; + j += NHEADER_DF2T + + NBIQUAD_DF2T * config->all_coefficients[j]; + } else { + response_index[i] = 0; + } + } + + /* Initialize 1st phase */ + for (i = 0; i < nch; i++) { + resp = config->assign_response[i]; + if (resp < 0) { + /* Initialize EQ channel to bypass */ + iir_reset_df2t(&iir[i]); + } else { + /* Initialize EQ coefficients */ + idx = response_index[resp]; + s = iir_init_coef_df2t(&iir[i], + &config->all_coefficients[idx]); + if (s > 0) + size_sum += s; + else + return -EINVAL; + } + + } + + /* Allocate all IIR channels data in a big chunk and clear it */ + iir_delay = rmalloc(RZONE_RUNTIME, RFLAGS_NONE, size_sum); + if (iir_delay == NULL) + return -EINVAL; + + memset(iir_delay, 0, size_sum); + + /* Initialize 2nd phase to set EQ delay lines pointers */ + for (i = 0; i < nch; i++) { + resp = config->assign_response[i]; + if (resp >= 0) { + idx = response_index[resp]; + iir_init_delay_df2t(&iir[i], &iir_delay); + } + + } + + return 0; +} + +static int eq_iir_switch_response(struct iir_state_df2t iir[], + struct eq_iir_configuration *config, struct eq_iir_update *update, + int nch) +{ + int i; + + /* Copy assign response from update and re-initilize EQ */ + if (config == NULL) + return -EINVAL; + + for (i = 0; i < config->stream_max_channels; i++) { + if (i < update->stream_max_channels) + config->assign_response[i] = update->assign_response[i]; + } + + eq_iir_setup(iir, config, nch); + + return 0; +} + +/* + * End of EQ setup code. Next the standard component methods. + */ + +static struct comp_dev *eq_iir_new(struct sof_ipc_comp *comp) +{ + int i; + //struct comp_buffer *sink; + //struct comp_buffer *source; + struct comp_dev *dev; + struct comp_data *cd; + + trace_eq_iir("ENw"); + dev = rmalloc(RZONE_RUNTIME, RFLAGS_NONE, sizeof(*dev)); + if (dev == NULL) + return NULL; + + //memcpy(&dev->comp, comp, sizeof(struct sof_ipc_comp_eq_iir)); + + cd = rmalloc(RZONE_RUNTIME, RFLAGS_NONE, sizeof(*cd)); + if (cd == NULL) { + rfree(dev); + return NULL; + } + + comp_set_drvdata(dev, cd); + + cd->eq_iir_func = eq_iir_s32_default; + cd->config = NULL; + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + iir_reset_df2t(&cd->iir[i]); + + return dev; +} + +static void eq_iir_free(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + + trace_eq_iir("EFr"); + + eq_iir_free_delaylines(cd->iir); + eq_iir_free_parameters(&cd->config); + + rfree(cd); + rfree(dev); +} + +/* set component audio stream parameters */ +static int eq_iir_params(struct comp_dev *dev, struct stream_params *params) +{ + + trace_eq_iir("EPa"); + + /* EQ supports only S32_LE PCM format */ + if ((params->type != STREAM_TYPE_PCM) + || (params->pcm->frame_fmt != SOF_IPC_FRAME_S32_LE)) + return -EINVAL; + + /* don't do any data transformation */ + comp_set_sink_params(dev, params); + + return 0; +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int eq_iir_cmd(struct comp_dev *dev, int cmd, void *data) +{ + trace_eq_iir("ECm"); + struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *source = list_first_item(&dev->bsource_list, + struct comp_buffer, sink_list); + struct sof_ipc_eq_iir_switch *assign; + struct sof_ipc_eq_iir_blob *blob; + int i; + size_t bs; + switch (cmd) { + case COMP_CMD_EQ_IIR_SWITCH: + trace_eq_iir("EFx"); + assign = (struct sof_ipc_eq_iir_switch *) data; + eq_iir_switch_response(cd->iir, cd->config, + (struct eq_iir_update *) assign->data, + source->params.pcm->channels); + break; + case COMP_CMD_EQ_IIR_CONFIG: + trace_eq_iir("EFc"); + /* Check and free old config */ + eq_iir_free_parameters(&cd->config); + + /* Copy new config, need to decode data to know the size */ + blob = (struct sof_ipc_eq_iir_blob *) data; + bs = blob->hdr.size - sizeof(struct sof_ipc_hdr); + if (bs > EQ_IIR_MAX_BLOB_SIZE) + return -EINVAL; + + /* Allocate and make a copy of the blob and setup IIR */ + cd->config = rmalloc(RZONE_RUNTIME, RFLAGS_NONE, bs); + if (cd->config != NULL) + memcpy(cd->config, blob->data, bs); + + eq_iir_setup(cd->iir, cd->config, source->params.pcm->channels); + break; + case COMP_CMD_MUTE: + trace_eq_iir("EFm"); + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + iir_mute_df2t(&cd->iir[i]); + + break; + case COMP_CMD_UNMUTE: + trace_eq_iir("EFu"); + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + iir_unmute_df2t(&cd->iir[i]); + + break; + case COMP_CMD_START: + trace_eq_iir("EFs"); + dev->state = COMP_STATE_RUNNING; + break; + case COMP_CMD_STOP: + trace_eq_iir("ESp"); + if (dev->state == COMP_STATE_RUNNING || + dev->state == COMP_STATE_DRAINING || + dev->state == COMP_STATE_PAUSED) { + comp_buffer_reset(dev); + dev->state = COMP_STATE_SETUP; + } + break; + case COMP_CMD_PAUSE: + trace_eq_iir("EPe"); + /* only support pausing for running */ + if (dev->state == COMP_STATE_RUNNING) + dev->state = COMP_STATE_PAUSED; + + break; + case COMP_CMD_RELEASE: + trace_eq_iir("ERl"); + dev->state = COMP_STATE_RUNNING; + break; + default: + trace_eq_iir("EDf"); + break; + } + + return 0; +} + +/* copy and process stream data from source to sink buffers */ +static int eq_iir_copy(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *source; + struct comp_buffer *sink; + int frames; + int need_source, need_sink; + + trace_comp("EqI"); + + /* src component needs 1 source and 1 sink buffer */ + source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + sink = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + /* Check that source has enough frames available and that sink has + * enough frames free. + */ + //frames = source->params.period_frames; + //need_source = frames * source->params.frame_size; + //need_sink = frames * sink->params.frame_size; + frames = source->params.pcm->period_count; + need_source = frames * source->params.pcm->frame_size; + need_sink = frames * sink->params.pcm->frame_size; + + /* Run EQ if buffers have enough room */ + if ((source->avail >= need_source) && (sink->free >= need_sink)) + cd->eq_iir_func(dev, source, sink, frames); + + return 0; +} + +static int eq_iir_prepare(struct comp_dev *dev) +{ + struct comp_data *cd = comp_get_drvdata(dev); + struct comp_buffer *source; + + trace_eq_iir("EPp"); + + cd->eq_iir_func = eq_iir_s32_default; + + /* Initialize EQ */ + if (cd->config == NULL) + return -EINVAL; + + source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + if (eq_iir_setup(cd->iir, cd->config, source->params.pcm->channels) < 0) + return -EINVAL; + + //dev->preload = PLAT_INT_PERIODS; + dev->state = COMP_STATE_PREPARE; + return 0; +} + +static int eq_iir_preload(struct comp_dev *dev) +{ + //int i; + + trace_eq_iir("EPl"); + + //for (i = 0; i < dev->preload; i++) + // eq_iir_copy(dev); + + return 0; +} + +static int eq_iir_reset(struct comp_dev *dev) +{ + int i; + struct comp_data *cd = comp_get_drvdata(dev); + + trace_eq_iir("ERe"); + + eq_iir_free_delaylines(cd->iir); + eq_iir_free_parameters(&cd->config); + + cd->eq_iir_func = eq_iir_s32_default; + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + iir_reset_df2t(&cd->iir[i]); + + dev->state = COMP_STATE_INIT; + return 0; +} + +struct comp_driver comp_eq_iir = { + .type = SOF_COMP_EQ_IIR, + .ops = + { + .new = eq_iir_new, + .free = eq_iir_free, + .params = eq_iir_params, + .cmd = eq_iir_cmd, + .copy = eq_iir_copy, + .prepare = eq_iir_prepare, + .reset = eq_iir_reset, + .preload = eq_iir_preload, + }, +}; + +void sys_comp_eq_iir_init(void) +{ + comp_register(&comp_eq_iir); +} diff --git a/src/audio/eq_iir.h b/src/audio/eq_iir.h new file mode 100644 index 0000000..43c666d --- /dev/null +++ b/src/audio/eq_iir.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017, 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + */ + +#ifndef EQ_IIR_H +#define EQ_IIR_H + + + +/* eq_iir_configuration + * uint32_t platform max channels + * uint32_t number_of_responses_defined + * 0=no responses, 1=one response defined, 2=two responses defined, etc. + * uint32_t assign_response[PLATFORM_MAX_CHANNELS] + * -1 = not defined, 0 = use first response, 1 = use 2nd response, etc. + * E.g. {0, 0, 0, 0, -1, -1, -1, -1} would apply to channels 0-3 the + * same first defined response and leave channels 4-7 unequalized. + * all_coefficients[] + * <1st EQ> + * uint32_t num_biquads + * uint32_t num_biquads_in_series + * <1st biquad> + * int32_t coef_a2 Q2.30 format + * int32_t coef_a1 Q2.30 format + * int32_t coef_b2 Q2.30 format + * int32_t coef_b1 Q2.30 format + * int32_t coef_b0 Q2.30 format + * int32_t output_shift number of shifts right, shift left is negative + * int32_t output_gain Q2.14 format + * <2nd biquad> + * ... + * <2nd EQ> + * + * Note: A flat response biquad can be made with a section set to + * b0 = 1.0, gain = 1.0, and other parameters set to 0 + * {0, 0, 0, 0, 1073741824, 0, 16484} + */ + +#define EQ_IIR_MAX_BLOB_SIZE 1024 + +#define NHEADER_EQ_IIR_BLOB 2 /* Blob is two words plus asssigns plus coef */ + +struct eq_iir_configuration { + int32_t stream_max_channels; + int32_t number_of_responses_defined; + int32_t assign_response[PLATFORM_MAX_CHANNELS]; + int32_t all_coefficients[]; +}; + +struct eq_iir_update { + int32_t stream_max_channels; + int32_t assign_response[PLATFORM_MAX_CHANNELS]; +}; + +#endif diff --git a/src/audio/iir.c b/src/audio/iir.c new file mode 100644 index 0000000..a713cdf --- /dev/null +++ b/src/audio/iir.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2017, 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + */ + +#include <stdint.h> +#include <stddef.h> +#include <errno.h> + +#ifdef MODULE_TEST +#include <stdio.h> +#endif + +#include <reef/audio/format.h> +#include "iir.h" + +/* + * Direct form II transposed second order filter block (biquad) + * + * +----+ +---+ +-------+ + * X(z) ---o--->| b0 |---> + -------------o--->| g |--->| shift |---> Y(z) + * | +----+ ^ | +---+ +-------+ + * | | | + * | +------+ | + * | | z^-1 | | + * | +------+ | + * | ^ | + * | +----+ | +----+ | + * o--->| b1 |---> + <---| a1 |---o + * | +----+ ^ +----+ | + * | | | + * | +------+ | + * | | z^-1 | | + * | +------+ | + * | ^ | + * | +----+ | +----+ | + * o--->| b2 |---> + <---| a2 |---+ + * +----+ +----+ + * + */ + +/* Series DF2T IIR */ + +/* 32 bit data, 32 bit coefficients and 64 bit state variables */ + +int32_t iir_df2t(struct iir_state_df2t *iir, int32_t x) +{ + int32_t in, tmp; + int64_t acc; + int32_t out = 0; + int i, j; + int d = 0; /* Index to delays */ + int c = 2; /* Index to coefficient a2 */ + + /* Coefficients order in coef[] is {a2, a1, b2, b1, b0, shift, gain} */ + in = x; + for (j = 0; j < iir->biquads; j += iir->biquads_in_series) { + for (i = 0; i < iir->biquads_in_series; i++) { + /* Compute output: Delay is Q3.61 + * Q2.30 x Q1.31 -> Q3.61 + * Shift Q3.61 to Q3.31 with rounding + */ + acc = ((int64_t) iir->coef[c + 4]) * in + iir->delay[d]; + tmp = (int32_t) Q_SHIFT_RND(acc, 61, 31); + + /* Compute 1st delay */ + acc = iir->delay[d + 1]; + acc += ((int64_t) iir->coef[c + 3]) * in; /* Coef b1 */ + acc += ((int64_t) iir->coef[c + 1]) * tmp; /* Coef a1 */ + iir->delay[d] = acc; + + /* Compute 2nd delay */ + acc = ((int64_t) iir->coef[c + 2]) * in; /* Coef b2 */ + acc += ((int64_t) iir->coef[c]) * tmp; /* Coef a2 */ + iir->delay[d + 1] = acc; + + /* Gain, output shift, prepare for next biquad + * Q2.14 x Q1.31 -> Q3.45, shift too Q3.31 and saturate + */ + acc = ((int64_t) iir->coef[c + 6]) * tmp; /* Gain */ + acc = Q_SHIFT_RND(acc, 45 + iir->coef[c + 5], 31); + in = sat_int32(acc); + c += 7; /* Next coefficients section */ + d += 2; /* Next biquad delays */ + } + /* Output of previous section is in variable in */ + out = sat_int32((int64_t) out + in); + } + return out; +} + +size_t iir_init_coef_df2t(struct iir_state_df2t *iir, int32_t config[]) +{ + iir->mute = 0; + iir->biquads = (int) config[0]; + iir->biquads_in_series = (int) config[1]; + iir->coef = &config[0]; /* TODO: Could change this to config[2] */ + iir->delay = NULL; + + if ((iir->biquads > IIR_DF2T_BIQUADS_MAX) || (iir->biquads < 1)) { + iir_reset_df2t(iir); + return -EINVAL; + } + + return 2 * iir->biquads * sizeof(int64_t); /* Needed delay line size */ +} + +void iir_init_delay_df2t(struct iir_state_df2t *iir, int64_t **delay) +{ + iir->delay = *delay; /* Delay line of this IIR */ + *delay += 2 * iir->biquads; /* Point to next IIR delay line start */ + +} + +void iir_mute_df2t(struct iir_state_df2t *iir) +{ + iir->mute = 1; +} + +void iir_unmute_df2t(struct iir_state_df2t *iir) +{ + iir->mute = 0; +} + +void iir_reset_df2t(struct iir_state_df2t *iir) +{ + iir->mute = 1; + iir->biquads = 0; + iir->biquads_in_series = 0; + iir->coef = NULL; + /* Note: May need to know the beginning of dynamic allocation after so + * omitting setting iir->delay to NULL. + */ +} diff --git a/src/audio/iir.h b/src/audio/iir.h new file mode 100644 index 0000000..0de275a --- /dev/null +++ b/src/audio/iir.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + */ + +/* A full 22th order equalizer with 11 biquads cover octave bands 1-11 in + * in the 0 - 20 kHz bandwidth. + */ +#define IIR_DF2T_BIQUADS_MAX 11 + +struct iir_state_df2t { + int mute; /* Set to 1 to mute EQ output, 0 otherwise */ + int biquads; /* Number of IIR 2nd order sections total */ + int biquads_in_series; /* Number of IIR 2nd order sections in series*/ + int32_t *coef; /* Pointer to IIR coefficients */ + int64_t *delay; /* Pointer to IIR delay line */ +}; + +#define NHEADER_DF2T 2 + +struct iir_header_df2t { + int32_t num_sections; + int32_t num_sections_in_series; +}; + +#define NBIQUAD_DF2T 7 + +struct iir_biquad_df2t { + int32_t a2; /* Q2.30 */ + int32_t a1; /* Q2.30 */ + int32_t b2; /* Q2.30 */ + int32_t b1; /* Q2.30 */ + int32_t b0; /* Q2.30 */ + int32_t output_shift; /* Number of right shifts */ + int32_t output_gain; /* Q2.14 */ +}; + +int32_t iir_df2t(struct iir_state_df2t *iir, int32_t x); + +size_t iir_init_coef_df2t(struct iir_state_df2t *iir, int32_t config[]); + +void iir_init_delay_df2t(struct iir_state_df2t *iir, int64_t **delay); + +void iir_mute_df2t(struct iir_state_df2t *iir); + +void iir_unmute_df2t(struct iir_state_df2t *iir); + +void iir_reset_df2t(struct iir_state_df2t *iir); diff --git a/src/include/reef/audio/component.h b/src/include/reef/audio/component.h index 7de47c2..7e28dfc 100644 --- a/src/include/reef/audio/component.h +++ b/src/include/reef/audio/component.h @@ -78,6 +78,8 @@ #define COMP_CMD_TONE 106 /* Tone generator amplitude and frequency */ #define COMP_CMD_EQ_FIR_CONFIG 107 /* Configuration data for FIR EQ */ #define COMP_CMD_EQ_FIR_SWITCH 108 /* Update request for FIR EQ */ +#define COMP_CMD_EQ_IIR_CONFIG 109 /* Configuration data for IIR EQ */ +#define COMP_CMD_EQ_IIR_SWITCH 110 /* Response update request for IIR EQ */
/* MMAP IPC status */ #define COMP_CMD_IPC_MMAP_RPOS 200 /* host read position */ @@ -263,6 +265,7 @@ void sys_comp_switch_init(void); void sys_comp_volume_init(void); void sys_comp_src_init(void); void sys_comp_tone_init(void); +void sys_comp_eq_iir_init(void);
void sys_comp_eq_fir_init(void); diff --git a/src/include/reef/trace.h b/src/include/reef/trace.h index ff41caf..00dbb5b 100644 --- a/src/include/reef/trace.h +++ b/src/include/reef/trace.h @@ -85,6 +85,7 @@ #define TRACE_CLASS_SRC (17 << 24) #define TRACE_CLASS_TONE (18 << 24) #define TRACE_CLASS_EQ_FIR (19 << 24) +#define TRACE_CLASS_EQ_IIR (20 << 24)
/* move to config.h */ #define TRACE 1 diff --git a/src/include/uapi/ipc.h b/src/include/uapi/ipc.h index 3bb6f61..20048b6 100644 --- a/src/include/uapi/ipc.h +++ b/src/include/uapi/ipc.h @@ -680,10 +680,20 @@ struct sof_ipc_eq_fir_blob { int32_t data[]; } __attribute__((packed));
+struct sof_ipc_eq_iir_blob { + struct sof_ipc_hdr hdr; + struct sof_ipc_host_buffer buffer; + int32_t data[]; +} __attribute__((packed)); + struct sof_ipc_eq_fir_switch { struct sof_ipc_hdr hdr; int32_t data[]; } __attribute__((packed));
+struct sof_ipc_eq_iir_switch { + struct sof_ipc_hdr hdr; + int32_t data[]; +} __attribute__((packed));
#endif diff --git a/src/tasks/audio.c b/src/tasks/audio.c index c420fb1..123b964 100644 --- a/src/tasks/audio.c +++ b/src/tasks/audio.c @@ -65,6 +65,7 @@ int do_task(struct reef *reef) sys_comp_volume_init(); sys_comp_src_init(); sys_comp_tone_init(); + sys_comp_eq_iir_init(); sys_comp_eq_fir_init();
#if 0
participants (1)
-
Seppo Ingalsuo