From fc5e117f68ecc13b6c2a8e8762120c67f13d26f8 Mon Sep 17 00:00:00 2001 From: Andrula Song Date: Wed, 7 Jun 2023 09:52:03 +0800 Subject: [PATCH] Audio: Volume: Add passthrough functions Add passthrough functions implementation. If the gain of all channels equal 0dB, then we use passthrough functions to process the volume component. Signed-off-by: Andrula Song --- .../module_adapter/module/volume/volume.c | 49 ++++- .../module/volume/volume_generic.c | 133 ++++++++++- .../volume/volume_generic_with_peakvol.c | 172 ++++++++++++++- .../module/volume/volume_hifi3.c | 149 ++++++++++++- .../module/volume/volume_hifi3_with_peakvol.c | 185 +++++++++++++++- .../module/volume/volume_hifi4.c | 149 ++++++++++++- .../module/volume/volume_hifi4_with_peakvol.c | 208 +++++++++++++++++- src/include/sof/audio/volume.h | 53 +++-- test/cmocka/src/audio/volume/volume_process.c | 7 +- 9 files changed, 1061 insertions(+), 44 deletions(-) diff --git a/src/audio/module_adapter/module/volume/volume.c b/src/audio/module_adapter/module/volume/volume.c index f36b03da1590..03e47272f478 100644 --- a/src/audio/module_adapter/module/volume/volume.c +++ b/src/audio/module_adapter/module/volume/volume.c @@ -267,8 +267,10 @@ static inline int32_t volume_windows_fade_ramp(struct vol_data *cd, int32_t ramp */ /* Note: Using inline saves 0.4 MCPS */ -static inline void volume_ramp(struct vol_data *cd) +static inline void volume_ramp(struct processing_module *mod) { + struct vol_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; int32_t new_vol; int32_t tvolume; int32_t volume; @@ -326,6 +328,29 @@ static inline void volume_ramp(struct vol_data *cd) } cd->volume[i] = new_vol; } + + cd->is_passthrough = cd->ramp_finished; + for (i = 0; i < cd->channels; i++) { + if (cd->volume[i] != VOL_ZERO_DB) { + cd->is_passthrough = false; + break; + } + } + +#if CONFIG_IPC_MAJOR_4 + cd->scale_vol = vol_get_processing_function(dev, cd); +#else + struct comp_buffer *sourceb; + struct comp_buffer __sparse_cache *source_c; + + sourceb = list_first_item(&dev->bsource_list, + struct comp_buffer, sink_list); + source_c = buffer_acquire(sourceb); + + cd->scale_vol = vol_get_processing_function(dev, source_c, cd); + + buffer_release(source_c); +#endif } /** @@ -368,6 +393,7 @@ static void reset_state(struct vol_data *cd) cd->vol_ramp_elapsed_frames = 0; cd->sample_rate_inv = 0; cd->copy_gain = true; + cd->is_passthrough = false; } #if CONFIG_IPC_MAJOR_3 @@ -397,6 +423,7 @@ static int volume_init(struct processing_module *mod) } md->private = cd; + cd->is_passthrough = false; /* Set the default volumes. If IPC sets min_value or max_value to * not-zero, use them. Otherwise set to internal limits and notify @@ -605,6 +632,7 @@ static int volume_init(struct processing_module *mod) cd->mailbox_offset += instance_id * sizeof(struct ipc4_peak_volume_regs); cd->attenuation = 0; + cd->is_passthrough = false; reset_state(cd); @@ -971,6 +999,17 @@ static int volume_set_volume(struct processing_module *mod, const uint8_t *data, cd->ramp_finished = false; } + cd->is_passthrough = cd->ramp_finished; + + for (i = 0; i < channels_count; i++) { + if (cd->volume[i] != VOL_ZERO_DB) { + cd->is_passthrough = false; + break; + } + } + + cd->scale_vol = vol_get_processing_function(dev, cd); + prepare_ramp(dev, cd); return 0; @@ -1161,7 +1200,7 @@ static int volume_process(struct processing_module *mod, } if (!cd->ramp_finished) { - volume_ramp(cd); + volume_ramp(mod); cd->vol_ramp_elapsed_frames += frames; } @@ -1291,8 +1330,12 @@ static int volume_prepare(struct processing_module *mod, ret = -ENOMEM; goto err; } +#if CONFIG_IPC_MAJOR_4 + cd->scale_vol = vol_get_processing_function(dev, cd); +#else + cd->scale_vol = vol_get_processing_function(dev, sink_c, cd); +#endif - cd->scale_vol = vol_get_processing_function(dev, sink_c); if (!cd->scale_vol) { comp_err(dev, "volume_prepare(): invalid cd->scale_vol"); diff --git a/src/audio/module_adapter/module/volume/volume_generic.c b/src/audio/module_adapter/module/volume/volume_generic.c index b82b93faed09..edfd6abcc356 100644 --- a/src/audio/module_adapter/module/volume/volume_generic.c +++ b/src/audio/module_adapter/module/volume/volume_generic.c @@ -93,6 +93,47 @@ static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_bu y = audio_stream_wrap(sink, y + n); } } + +/** + * \brief Volume passthrough from 24/32 bit to 24/32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + * + * Copy and scale volume from 24/32 bit source buffer + * to 24/32 bit destination buffer. + */ +static void vol_passthrough_s24_to_s24(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int32_t *x; + int32_t *y; + int nmax, n; + const int nch = audio_stream_get_channels(source); + int remaining_samples = frames * nch; + + x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); + y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + while (remaining_samples) { + nmax = audio_stream_samples_without_wrap_s24(source, x); + n = MIN(remaining_samples, nmax); + nmax = audio_stream_samples_without_wrap_s24(sink, y); + n = MIN(n, nmax); + memcpy_s(y, n * sizeof(int32_t), x, n * sizeof(int32_t)); + remaining_samples -= n; + x = audio_stream_wrap(source, x + n); + y = audio_stream_wrap(sink, y + n); + } +} #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE @@ -147,6 +188,46 @@ static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_bu y = audio_stream_wrap(sink, y + n); } } + +/** + * \brief Volume passthrough from 32 bit to 32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + * + * Copy and scale volume from 32 bit source buffer + * to 32 bit destination buffer. + */ +static void vol_passthrough_s32_to_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int32_t *x; + int32_t *y; + int nmax, n; + const int nch = audio_stream_get_channels(source); + int remaining_samples = frames * nch; + + x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); + y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + while (remaining_samples) { + nmax = audio_stream_samples_without_wrap_s32(source, x); + n = MIN(remaining_samples, nmax); + nmax = audio_stream_samples_without_wrap_s32(sink, y); + n = MIN(n, nmax); + memcpy_s(y, n * sizeof(int32_t), x, n * sizeof(int32_t)); + remaining_samples -= n; + x = audio_stream_wrap(source, x + n); + y = audio_stream_wrap(sink, y + n); + } +} #endif /* CONFIG_FORMAT_S32LE */ #if CONFIG_FORMAT_S16LE @@ -198,18 +279,58 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu y = audio_stream_wrap(sink, y + n); } } + +/** + * \brief Volume passthrough from 16 bit to 16 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + * + * Copy and scale volume from 16 bit source buffer + * to 16 bit destination buffer. + */ +static void vol_passthrough_s16_to_s16(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int16_t *x; + int16_t *y; + int nmax, n; + const int nch = audio_stream_get_channels(source); + int remaining_samples = frames * nch; + + x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); + y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); + bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + bsink->size += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + while (remaining_samples) { + nmax = audio_stream_samples_without_wrap_s16(source, x); + n = MIN(remaining_samples, nmax); + nmax = audio_stream_samples_without_wrap_s16(sink, y); + n = MIN(n, nmax); + memcpy_s(y, n * sizeof(int16_t), x, n * sizeof(int16_t)); + remaining_samples -= n; + x = audio_stream_wrap(source, x + n); + y = audio_stream_wrap(sink, y + n); + } +} #endif /* CONFIG_FORMAT_S16LE */ const struct comp_func_map volume_func_map[] = { #if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16 }, -#endif /* CONFIG_FORMAT_S16LE */ + { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16, vol_passthrough_s16_to_s16}, +#endif #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24 }, -#endif /* CONFIG_FORMAT_S24LE */ + { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24, vol_passthrough_s24_to_s24}, +#endif #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, vol_s32_to_s32 }, -#endif /* CONFIG_FORMAT_S32LE */ + { SOF_IPC_FRAME_S32_LE, vol_s32_to_s32, vol_passthrough_s32_to_s32}, +#endif }; const size_t volume_func_count = ARRAY_SIZE(volume_func_map); diff --git a/src/audio/module_adapter/module/volume/volume_generic_with_peakvol.c b/src/audio/module_adapter/module/volume/volume_generic_with_peakvol.c index facdacd03563..35861a31bbe0 100644 --- a/src/audio/module_adapter/module/volume/volume_generic_with_peakvol.c +++ b/src/audio/module_adapter/module/volume/volume_generic_with_peakvol.c @@ -94,6 +94,59 @@ static void vol_s24_to_s24(struct processing_module *mod, struct input_stream_bu y = audio_stream_wrap(sink, y + n); } } + +/** + * \brief Volume passthrough from 24/32 bit to 24/32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment + * + * Copy and scale volume from 24/32 bit source buffer + * to 24/32 bit destination buffer. + */ +static void vol_passthrough_s24_to_s24(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int32_t *x, *x0; + int32_t *y, *y0; + int nmax, n, i, j; + const int nch = audio_stream_get_channels(source); + int remaining_samples = frames * nch; + int32_t tmp; + + x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); + y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + while (remaining_samples) { + nmax = audio_stream_samples_without_wrap_s24(source, x); + n = MIN(remaining_samples, nmax); + nmax = audio_stream_samples_without_wrap_s24(sink, y); + n = MIN(n, nmax); + for (j = 0; j < nch; j++) { + x0 = x + j; + y0 = y + j; + tmp = 0; + for (i = 0; i < n; i += nch) { + y0[i] = x0[i]; + tmp = MAX(abs(x0[i]), tmp); + } + tmp = tmp << (attenuation + PEAK_24S_32C_ADJUST); + cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); + } + remaining_samples -= n; + x = audio_stream_wrap(source, x + n); + y = audio_stream_wrap(sink, y + n); + } +} #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE @@ -153,6 +206,61 @@ static void vol_s32_to_s32(struct processing_module *mod, struct input_stream_bu y = audio_stream_wrap(sink, y + n); } } + +/** + * \brief Volume passthrough from 32 bit to 32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment + * + * Copy and scale volume from 32 bit source buffer + * to 32 bit destination buffer. + */ +static void vol_passthrough_s32_to_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int32_t *x, *x0; + int32_t *y, *y0; + int nmax, n, i, j; + const int nch = audio_stream_get_channels(source); + int remaining_samples = frames * nch; + int32_t tmp; + + x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); + y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(remaining_samples); + while (remaining_samples) { + nmax = audio_stream_samples_without_wrap_s32(source, x); + n = MIN(remaining_samples, nmax); + nmax = audio_stream_samples_without_wrap_s32(sink, y); + n = MIN(n, nmax); + /* Note: on Xtensa processing one channel volume at time performed slightly + * better than simpler interleaved code version (average 19 us vs. 20 us). + */ + for (j = 0; j < nch; j++) { + x0 = x + j; + y0 = y + j; + tmp = 0; + for (i = 0; i < n; i += nch) { + y0[i] = x0[i]; + tmp = MAX(abs(x0[i]), tmp); + } + tmp = tmp << attenuation; + cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); + } + remaining_samples -= n; + x = audio_stream_wrap(source, x + n); + y = audio_stream_wrap(sink, y + n); + } +} #endif /* CONFIG_FORMAT_S32LE */ #if CONFIG_FORMAT_S16LE @@ -209,18 +317,70 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu y = audio_stream_wrap(sink, y + n); } } + +/** + * \brief Volume passthrough from 16 bit to 16 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) + * + * Copy and scale volume from 16 bit source buffer + * to 16 bit destination buffer. + */ +static void vol_passthrough_s16_to_s16(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + int32_t *x, *x0; + int32_t *y, *y0; + int nmax, n, i, j; + const int nch = audio_stream_get_channels(source); + int remaining_samples = frames * nch; + int32_t tmp; + + x = audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + bsource->consumed); + y = audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + bsink->size); + + bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + bsink->size += VOL_S16_SAMPLES_TO_BYTES(remaining_samples); + while (remaining_samples) { + nmax = audio_stream_samples_without_wrap_s16(source, x); + n = MIN(remaining_samples, nmax); + nmax = audio_stream_samples_without_wrap_s16(sink, y); + n = MIN(n, nmax); + for (j = 0; j < nch; j++) { + x0 = x + j; + y0 = y + j; + tmp = 0; + for (i = 0; i < n; i += nch) { + y0[i] = x0[i]; + tmp = MAX(abs(x0[i]), tmp); + } + cd->peak_regs.peak_meter[j] = MAX(tmp, cd->peak_regs.peak_meter[j]); + } + remaining_samples -= n; + x = audio_stream_wrap(source, x + n); + y = audio_stream_wrap(sink, y + n); + } +} #endif const struct comp_func_map volume_func_map[] = { #if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16 }, -#endif /* CONFIG_FORMAT_S16LE */ + { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16, vol_passthrough_s16_to_s16}, +#endif #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24 }, -#endif /* CONFIG_FORMAT_S24LE */ + { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24, vol_passthrough_s24_to_s24}, +#endif #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, vol_s32_to_s32 }, -#endif /* CONFIG_FORMAT_S32LE */ + { SOF_IPC_FRAME_S32_LE, vol_s32_to_s32, vol_passthrough_s32_to_s32}, +#endif }; const size_t volume_func_count = ARRAY_SIZE(volume_func_map); diff --git a/src/audio/module_adapter/module/volume/volume_hifi3.c b/src/audio/module_adapter/module/volume/volume_hifi3.c index d95675761c90..df46eeb9969d 100644 --- a/src/audio/module_adapter/module/volume/volume_hifi3.c +++ b/src/audio/module_adapter/module/volume/volume_hifi3.c @@ -133,6 +133,55 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea out = audio_stream_wrap(sink, out); } } + +/** + * \brief HiFi3 enabled volume passthrough from 24/32 bit to 24/32 or 32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + */ +static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + const int channels_count = audio_stream_get_channels(sink); + int samples = channels_count * frames; + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + + while (samples) { + m = audio_stream_samples_without_wrap_s24(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s24(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + /* process two continuous sample data once */ + for (i = 0; i < n; i += 2) { + /* Load the input sample */ + AE_LA32X2_IP(in_sample, inu, in); + /* Store the output sample */ + AE_SA32X2_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } +} #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE @@ -226,6 +275,54 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea out = audio_stream_wrap(sink, out); } } + +/** + * \brief HiFi3 enabled volume passthrough from 32 bit to 24/32 or 32 bit. + * \param[in,out] mod Pointer to struct processing_module + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + */ +static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + const int channels_count = audio_stream_get_channels(sink); + int samples = channels_count * frames; + ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + + while (samples) { + m = audio_stream_samples_without_wrap_s32(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s32(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + /* process two continuous sample data once */ + for (i = 0; i < n; i += 2) { + /* Load the input sample */ + AE_LA32X2_IP(in_sample, inu, in); + AE_SA32X2_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } +} #endif /* CONFIG_FORMAT_S32LE */ #if CONFIG_FORMAT_S16LE @@ -325,16 +422,62 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu out = audio_stream_wrap(sink, out); } } + +/** + * \brief HiFi3 enabled volume passthrough from 16 bit to 16 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + */ +static void vol_passthrough_s16_to_s16(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f16x4 in_sample = AE_ZERO16(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + const int channels_count = audio_stream_get_channels(sink); + int samples = channels_count * frames; + + while (samples) { + m = audio_stream_samples_without_wrap_s16(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s16(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + for (i = 0; i < n; i += 4) { + /* Load the input sample */ + AE_LA16X4_IP(in_sample, inu, in); + AE_SA16X4_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); + bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } +} #endif /* CONFIG_FORMAT_S16LE */ const struct comp_func_map volume_func_map[] = { #if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16 }, + { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16, vol_passthrough_s16_to_s16}, #endif #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32 }, + { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32, vol_passthrough_s24_to_s24_s32 }, #endif #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32 }, + { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32, vol_passthrough_s32_to_s24_s32 }, #endif }; diff --git a/src/audio/module_adapter/module/volume/volume_hifi3_with_peakvol.c b/src/audio/module_adapter/module/volume/volume_hifi3_with_peakvol.c index 0113c46937f3..29ab315d74b7 100644 --- a/src/audio/module_adapter/module/volume/volume_hifi3_with_peakvol.c +++ b/src/audio/module_adapter/module/volume/volume_hifi3_with_peakvol.c @@ -101,6 +101,65 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea in0 = audio_stream_wrap(source, in0 + n); } } + +/** + * \brief HiFi3 enabled volume passthrough from 24/32 bit to 24/32 or 32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment + */ +static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + int channel, n, i, m; + ae_f32 *in0 = (ae_f32 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32 *out0 = (ae_f32 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + ae_f32 *in, *out; + const int channels_count = audio_stream_get_channels(sink); + const int inc = sizeof(ae_f32) * channels_count; + int samples = channels_count * frames; + ae_f32x2 peak_vol; + uint32_t *peak_meter = cd->peak_regs.peak_meter; + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + while (samples) { + m = audio_stream_samples_without_wrap_s32(source, in0); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s32(sink, out0); + n = MIN(m, n); + for (channel = 0; channel < channels_count; channel++) { + peak_vol = AE_ZERO32(); + /* set start address of sample load */ + in = in0 + channel; + /* set start address of sample store */ + out = out0 + channel; + for (i = 0; i < n; i += channels_count) { + /* Load the input sample */ + AE_L32_XP(in_sample, in, inc); + /* calc peak vol */ + peak_vol = AE_MAXABS32S(in_sample, peak_vol); + /* Store the output sample */ + AE_S32_L_XP(in_sample, out, inc); + } + peak_vol = AE_SLAA32S(peak_vol, attenuation + PEAK_24S_32C_ADJUST); + peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); + } + samples -= n; + out0 = audio_stream_wrap(sink, out0 + n); + in0 = audio_stream_wrap(source, in0 + n); + } +} #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE @@ -178,6 +237,65 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea in0 = audio_stream_wrap(source, in0 + n); } } + +/** + * \brief HiFi3 enabled volume passthrough from 32 bit to 24/32 or 32 bit. + * \param[in,out] mod Pointer to struct processing_module + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment + */ +static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + int i, n, channel, m; + const int channels_count = audio_stream_get_channels(sink); + const int inc = sizeof(ae_f32) * channels_count; + int samples = channels_count * frames; + ae_f32 *in0 = (ae_f32 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32 *out0 = (ae_f32 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + ae_f32 *in, *out; + ae_f32x2 peak_vol; + uint32_t *peak_meter = cd->peak_regs.peak_meter; + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + while (samples) { + m = audio_stream_samples_without_wrap_s32(source, in0); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s32(sink, out0); + n = MIN(m, n); + for (channel = 0; channel < channels_count; channel++) { + peak_vol = AE_ZERO32(); + /* set start address of sample load */ + in = in0 + channel; + /* set start address of sample store */ + out = out0 + channel; + for (i = 0; i < n; i += channels_count) { + /* Load the input sample */ + AE_L32_XP(in_sample, in, inc); + /* calc peak vol */ + peak_vol = AE_MAXABS32S(in_sample, peak_vol); + + AE_S32_L_XP(in_sample, out, inc); + } + peak_vol = AE_SLAA32S(peak_vol, attenuation); + peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); + } + samples -= n; + out0 = audio_stream_wrap(sink, out0 + n); + in0 = audio_stream_wrap(source, in0 + n); + } +} #endif /* CONFIG_FORMAT_S32LE */ #if CONFIG_FORMAT_S16LE @@ -260,17 +378,78 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); } } + +/** + * \brief HiFi3 enabled volume passthrough from 16 bit to 16 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) + */ +static void vol_passthrough_s16_to_s16(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f16x4 in_sample = AE_ZERO16(); + int i, n, channel, m; + ae_f16 *in; + ae_f16 *out; + ae_f16 *in0 = (ae_f16 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f16 *out0 = (ae_f16 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + const int channels_count = audio_stream_get_channels(sink); + const int inc = sizeof(ae_f16) * channels_count; + int samples = channels_count * frames; + ae_f32x2 peak_vol; + uint32_t *peak_meter = cd->peak_regs.peak_meter; + + while (samples) { + m = audio_stream_samples_without_wrap_s16(source, in0); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s16(sink, out0); + n = MIN(m, n); + for (channel = 0; channel < channels_count; channel++) { + peak_vol = AE_ZERO32(); + /* set start address of sample load */ + in = in0 + channel; + /* set start address of sample store */ + out = out0 + channel; + for (i = 0; i < n; i += channels_count) { + /* Load the input sample */ + AE_L16_XP(in_sample, in, inc); + + /* calc peak vol */ + peak_vol = AE_MAXABS32S(AE_SEXT32X2D16_32(in_sample), peak_vol); + + /* store the output */ + AE_S16_0_XP(in_sample, out, inc); + } + peak_meter[channel] = AE_MAX32(peak_vol, peak_meter[channel]); + } + out0 = audio_stream_wrap(sink, out0 + n); + in0 = audio_stream_wrap(source, in0 + n); + samples -= n; + bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); + bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); + } +} #endif /* CONFIG_FORMAT_S16LE */ const struct comp_func_map volume_func_map[] = { #if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16 }, + { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16, vol_passthrough_s16_to_s16}, #endif #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32 }, + { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32, vol_passthrough_s24_to_s24_s32}, #endif #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32 }, + { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32, vol_passthrough_s32_to_s24_s32}, #endif }; diff --git a/src/audio/module_adapter/module/volume/volume_hifi4.c b/src/audio/module_adapter/module/volume/volume_hifi4.c index d643e5c2c13c..6092054bdb52 100644 --- a/src/audio/module_adapter/module/volume/volume_hifi4.c +++ b/src/audio/module_adapter/module/volume/volume_hifi4.c @@ -133,6 +133,55 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea out = audio_stream_wrap(sink, out); } } + +/** + * \brief HiFi4 enabled volume passthrough from 24/32 bit to 24/32 or 32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + */ +static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + int samples = audio_stream_get_channels(sink) * frames; + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + + while (samples) { + m = audio_stream_samples_without_wrap_s24(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s24(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + /* process two continuous sample data once */ + for (i = 0; i < n; i += 2) { + /* Load the input sample */ + AE_LA32X2_IP(in_sample, inu, in); + /* Store the output sample */ + AE_SA32X2_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } +} + #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE @@ -226,6 +275,53 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea out = audio_stream_wrap(sink, out); } } + +/** + * \brief HiFi4 enabled volume passthrough from 32 bit to 24/32 or 32 bit. + * \param[in,out] mod Pointer to struct processing_module + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + */ +static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + const int channels_count = audio_stream_get_channels(sink); + int samples = channels_count * frames; + ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + while (samples) { + m = audio_stream_samples_without_wrap_s32(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s32(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + /* process two continuous sample data once */ + for (i = 0; i < n; i += 2) { + /* Load the input sample */ + AE_LA32X2_IP(in_sample, inu, in); + AE_SA32X2_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } +} #endif /* CONFIG_FORMAT_S32LE */ #if CONFIG_FORMAT_S16LE @@ -325,19 +421,64 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu out = audio_stream_wrap(sink, out); } } + +/** + * \brief HiFi4 enabled volume passthrough from 16 bit to 16 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused) + */ +static void vol_passthrough_s16_to_s16(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f16x4 in_sample = AE_ZERO16(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + const int channels_count = audio_stream_get_channels(sink); + int samples = channels_count * frames; + + bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S16_SAMPLES_TO_BYTES(samples); + while (samples) { + m = audio_stream_samples_without_wrap_s16(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s16(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + for (i = 0; i < n; i += 4) { + /* Load the input sample */ + AE_LA16X4_IP(in_sample, inu, in); + AE_SA16X4_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } +} #endif /* CONFIG_FORMAT_S16LE */ const struct comp_func_map volume_func_map[] = { #if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16 }, + { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16, vol_passthrough_s16_to_s16}, #endif #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32 }, + { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32, vol_passthrough_s24_to_s24_s32}, #endif #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32 }, + { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32, vol_passthrough_s32_to_s24_s32}, #endif }; - const size_t volume_func_count = ARRAY_SIZE(volume_func_map); #endif #endif diff --git a/src/audio/module_adapter/module/volume/volume_hifi4_with_peakvol.c b/src/audio/module_adapter/module/volume/volume_hifi4_with_peakvol.c index d9bde4250fb7..ba234b931313 100644 --- a/src/audio/module_adapter/module/volume/volume_hifi4_with_peakvol.c +++ b/src/audio/module_adapter/module/volume/volume_hifi4_with_peakvol.c @@ -136,6 +136,74 @@ static void vol_s24_to_s24_s32(struct processing_module *mod, struct input_strea cd->peak_vol[i + channels_count]) << (attenuation + PEAK_24S_32C_ADJUST); } + +/** + * \brief HiFi4 enabled volume passthrough from 24/32 bit to 24/32 or 32 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment + */ +static void vol_passthrough_s24_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + + int i, n, m; + ae_f32x2 *vol; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + const int channels_count = audio_stream_get_channels(sink); + const int inc = sizeof(ae_f32x2); + int samples = channels_count * frames; + ae_f32x2 temp; + ae_f32x2 *peakvol = (ae_f32x2 *)cd->peak_vol; + + /* Set peakvol(which stores the peak volume data twice) as circular buffer */ + memset(peakvol, 0, sizeof(ae_f32) * channels_count * 2); + AE_SETCBEGIN1(cd->peak_vol); + AE_SETCEND1(cd->peak_vol + channels_count * 2); + + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + + while (samples) { + m = audio_stream_samples_without_wrap_s32(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s16(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + /* process two continuous sample data once */ + for (i = 0; i < n; i += 2) { + /* Load the input sample */ + AE_LA32X2_IP(in_sample, inu, in); + /* calculate the peak volume*/ + AE_L32X2_XC1(temp, peakvol, 0); + temp = AE_MAXABS32S(in_sample, temp); + AE_S32X2_XC1(temp, peakvol, inc); + /* Store the output sample */ + AE_SA32X2_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } + for (i = 0; i < channels_count; i++) + cd->peak_regs.peak_meter[i] = MAX(cd->peak_vol[i], + cd->peak_vol[i + channels_count]) + << (attenuation + PEAK_24S_32C_ADJUST); +} #endif /* CONFIG_FORMAT_S24LE */ #if CONFIG_FORMAT_S32LE @@ -242,6 +310,70 @@ static void vol_s32_to_s24_s32(struct processing_module *mod, struct input_strea cd->peak_regs.peak_meter[i] = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]) << attenuation; } + +/** + * \brief HiFi4 enabled volume passthrough from 32 bit to 24/32 or 32 bit. + * \param[in,out] mod Pointer to struct processing_module + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment + */ +static void vol_passthrough_s32_to_s24_s32(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f32x2 in_sample = AE_ZERO32(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + const int channels_count = audio_stream_get_channels(sink); + const int inc = sizeof(ae_f32x2); + int samples = channels_count * frames; + ae_f32x2 *in = (ae_f32x2 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f32x2 *out = (ae_f32x2 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + ae_f32x2 temp; + ae_f32x2 *peakvol = (ae_f32x2 *)cd->peak_vol; + + /* Set peakvol(which stores the peak volume data twice) as circular buffer */ + memset(peakvol, 0, sizeof(ae_f32) * channels_count * 2); + AE_SETCBEGIN1(cd->peak_vol); + AE_SETCEND1(cd->peak_vol + channels_count * 2); + bsource->consumed += VOL_S32_SAMPLES_TO_BYTES(samples); + bsink->size += VOL_S32_SAMPLES_TO_BYTES(samples); + + while (samples) { + m = audio_stream_samples_without_wrap_s32(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s32(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + /* process two continuous sample data once */ + for (i = 0; i < n; i += 2) { + /* Load the input sample */ + AE_LA32X2_IP(in_sample, inu, in); + /* calculate the peak volume*/ + AE_L32X2_XC1(temp, peakvol, 0); + temp = AE_MAXABS32S(in_sample, temp); + AE_S32X2_XC1(temp, peakvol, inc); + + AE_SA32X2_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + } + for (i = 0; i < channels_count; i++) + cd->peak_regs.peak_meter[i] = MAX(cd->peak_vol[i], + cd->peak_vol[i + channels_count]) << attenuation; +} #endif /* CONFIG_FORMAT_S32LE */ #if CONFIG_FORMAT_S16LE @@ -360,17 +492,87 @@ static void vol_s16_to_s16(struct processing_module *mod, struct input_stream_bu cd->peak_regs.peak_meter[i] = m; } } + +/** + * \brief HiFi4 enabled volume passthrough from 16 bit to 16 bit. + * \param[in,out] dev Volume base component device. + * \param[in,out] sink Destination buffer. + * \param[in,out] source Input buffer. + * \param[in] frames Number of frames to process. + * \param[in] attenuation factor for peakmeter adjustment (unused for 16bit) + */ +static void vol_passthrough_s16_to_s16(struct processing_module *mod, + struct input_stream_buffer *bsource, + struct output_stream_buffer *bsink, uint32_t frames, + uint32_t attenuation) +{ + struct vol_data *cd = module_get_private_data(mod); + struct audio_stream __sparse_cache *source = bsource->data; + struct audio_stream __sparse_cache *sink = bsink->data; + ae_f16x4 in_sample = AE_ZERO16(); + int i, n, m; + ae_valign inu = AE_ZALIGN64(); + ae_valign outu = AE_ZALIGN64(); + ae_f16x4 *in = (ae_f16x4 *)audio_stream_wrap(source, (char *)audio_stream_get_rptr(source) + + bsource->consumed); + ae_f16x4 *out = (ae_f16x4 *)audio_stream_wrap(sink, (char *)audio_stream_get_wptr(sink) + + bsink->size); + const int channels_count = audio_stream_get_channels(sink); + const int inc = sizeof(ae_f32x2); + int samples = channels_count * frames; + ae_f32x2 temp; + ae_f32x2 *peakvol = (ae_f32x2 *)cd->peak_vol; + + /* Set peakvol(which stores the peak volume data 4 times) as circular buffer */ + memset(peakvol, 0, sizeof(ae_f32) * channels_count * 4); + AE_SETCBEGIN1(cd->peak_vol); + AE_SETCEND1(cd->peak_vol + channels_count * 4); + + while (samples) { + m = audio_stream_samples_without_wrap_s16(source, in); + n = MIN(m, samples); + m = audio_stream_samples_without_wrap_s16(sink, out); + n = MIN(m, n); + inu = AE_LA64_PP(in); + for (i = 0; i < n; i += 4) { + /* Load the input sample */ + AE_LA16X4_IP(in_sample, inu, in); + /* calculate the peak volume*/ + AE_L32X2_XC1(temp, peakvol, 0); + temp = AE_MAXABS32S(AE_SEXT32X2D16_32(in_sample), temp); + AE_S32X2_XC1(temp, peakvol, inc); + /* calculate the peak volume*/ + AE_L32X2_XC1(temp, peakvol, 0); + temp = AE_MAXABS32S(AE_SEXT32X2D16_10(in_sample), temp); + AE_S32X2_XC1(temp, peakvol, inc); + /* store the output */ + AE_SA16X4_IP(in_sample, outu, out); + } + AE_SA64POS_FP(outu, out); + samples -= n; + in = audio_stream_wrap(source, in); + out = audio_stream_wrap(sink, out); + bsource->consumed += VOL_S16_SAMPLES_TO_BYTES(n); + bsink->size += VOL_S16_SAMPLES_TO_BYTES(n); + } + for (i = 0; i < channels_count; i++) { + m = MAX(cd->peak_vol[i], cd->peak_vol[i + channels_count]); + m = MAX(m, cd->peak_vol[i + channels_count * 2]); + m = MAX(m, cd->peak_vol[i + channels_count * 3]); + cd->peak_regs.peak_meter[i] = m; + } +} #endif /* CONFIG_FORMAT_S16LE */ const struct comp_func_map volume_func_map[] = { #if CONFIG_FORMAT_S16LE - { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16 }, + { SOF_IPC_FRAME_S16_LE, vol_s16_to_s16, vol_passthrough_s16_to_s16}, #endif #if CONFIG_FORMAT_S24LE - { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32 }, + { SOF_IPC_FRAME_S24_4LE, vol_s24_to_s24_s32, vol_passthrough_s24_to_s24_s32}, #endif #if CONFIG_FORMAT_S32LE - { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32 }, + { SOF_IPC_FRAME_S32_LE, vol_s32_to_s24_s32, vol_passthrough_s32_to_s24_s32}, #endif }; diff --git a/src/include/sof/audio/volume.h b/src/include/sof/audio/volume.h index f1783a6dc942..0480145286d8 100644 --- a/src/include/sof/audio/volume.h +++ b/src/include/sof/audio/volume.h @@ -169,12 +169,14 @@ struct vol_data { vol_zc_func zc_get; /**< function getting nearest zero crossing frame */ bool copy_gain; /**< control copy gain or not */ uint32_t attenuation; /**< peakmeter adjustment in range [0 - 31] */ + bool is_passthrough; /**< is passthrough or do gain multiplication */ }; /** \brief Volume processing functions map. */ struct comp_func_map { uint16_t frame_fmt; /**< frame format */ vol_scale_func func; /**< volume processing function */ + vol_scale_func passthrough_func; /**< volume passthrough function */ }; /** \brief Map of formats with dedicated processing functions. */ @@ -194,9 +196,11 @@ struct comp_zc_func_map { * \brief Retrievies volume processing function. * \param[in,out] dev Volume base component device. * \param[in] sinkb Sink buffer to match against + * \param[in] cd Volume data structure. */ static inline vol_scale_func vol_get_processing_function(struct comp_dev *dev, - struct comp_buffer __sparse_cache *sinkb) + struct comp_buffer __sparse_cache *sinkb, + struct vol_data *cd) { int i; @@ -205,33 +209,52 @@ static inline vol_scale_func vol_get_processing_function(struct comp_dev *dev, if (audio_stream_get_frm_fmt(&sinkb->stream) != volume_func_map[i].frame_fmt) continue; - return volume_func_map[i].func; + if (cd->is_passthrough) + return volume_func_map[i].passthrough_func; + else + return volume_func_map[i].func; } return NULL; } + #else /** * \brief Retrievies volume processing function. * \param[in,out] dev Volume base component device. - * \param[in] sinkb Sink buffer to match against + * \param[in] cd Volume data structure */ static inline vol_scale_func vol_get_processing_function(struct comp_dev *dev, - struct comp_buffer __sparse_cache *sinkb) + struct vol_data *cd) { struct processing_module *mod = comp_get_drvdata(dev); - switch (mod->priv.cfg.base_cfg.audio_fmt.valid_bit_depth) { - case IPC4_DEPTH_16BIT: - return volume_func_map[0].func; - case IPC4_DEPTH_24BIT: - return volume_func_map[1].func; - case IPC4_DEPTH_32BIT: - return volume_func_map[2].func; - default: - comp_err(dev, "vol_get_processing_function(): unsupported depth %d", - mod->priv.cfg.base_cfg.audio_fmt.depth); - return NULL; + if (cd->is_passthrough) { + switch (mod->priv.cfg.base_cfg.audio_fmt.valid_bit_depth) { + case IPC4_DEPTH_16BIT: + return volume_func_map[0].passthrough_func; + case IPC4_DEPTH_24BIT: + return volume_func_map[1].passthrough_func; + case IPC4_DEPTH_32BIT: + return volume_func_map[2].passthrough_func; + default: + comp_err(dev, "vol_get_processing_function(): unsupported depth %d", + mod->priv.cfg.base_cfg.audio_fmt.depth); + return NULL; + } + } else { + switch (mod->priv.cfg.base_cfg.audio_fmt.valid_bit_depth) { + case IPC4_DEPTH_16BIT: + return volume_func_map[0].func; + case IPC4_DEPTH_24BIT: + return volume_func_map[1].func; + case IPC4_DEPTH_32BIT: + return volume_func_map[2].func; + default: + comp_err(dev, "vol_get_processing_function(): unsupported depth %d", + mod->priv.cfg.base_cfg.audio_fmt.depth); + return NULL; + } } } #endif diff --git a/test/cmocka/src/audio/volume/volume_process.c b/test/cmocka/src/audio/volume/volume_process.c index 94d72a6fb8a1..7b50fde2f310 100644 --- a/test/cmocka/src/audio/volume/volume_process.c +++ b/test/cmocka/src/audio/volume/volume_process.c @@ -62,6 +62,7 @@ static int setup(void **state) cd = test_malloc(sizeof(*cd)); md = &vol_state->mod->priv; md->private = cd; + cd->is_passthrough = false; /* malloc memory to store current volume 4 times to ensure the address * is 8-byte aligned for multi-way xtensa intrinsic operations. @@ -71,7 +72,11 @@ static int setup(void **state) cd->vol = test_malloc(vol_size); /* set processing function and volume */ - cd->scale_vol = vol_get_processing_function(vol_state->mod->dev, vol_state->sinks[0]); +#if CONFIG_IPC_MAJOR_4 + cd->scale_vol = vol_get_processing_function(vol_state->mod->dev, cd); +#else + cd->scale_vol = vol_get_processing_function(vol_state->mod->dev, vol_state->sinks[0], cd); +#endif set_volume(cd->volume, vol_parameters->volume, vol_state->parameters.channels); /* assign test state */