From d9da44e694e5f8f233173411ffa1b84cd460aeba Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Dec 2025 17:08:01 -0800 Subject: [PATCH 1/7] audio: tone: Add IPC4 tone implementation Add a new implementation for IPC4 for the tone module. Signed-off-by: Ranjani Sridharan --- src/arch/host/configs/library_defconfig | 1 + src/audio/CMakeLists.txt | 4 +- src/audio/Kconfig | 8 +- src/audio/tone/CMakeLists.txt | 12 + src/audio/tone/Kconfig | 16 ++ src/audio/tone/llext/CMakeLists.txt | 7 + src/audio/tone/llext/llext.toml.h | 6 + src/audio/{tone.c => tone/tone-ipc3.c} | 341 +---------------------- src/audio/tone/tone-ipc4.c | 229 +++++++++++++++ src/audio/tone/tone.c | 355 ++++++++++++++++++++++++ src/audio/tone/tone.h | 172 ++++++++++++ src/audio/tone/tone.toml | 22 ++ src/include/sof/audio/component.h | 1 + tools/rimage/config/lnl.toml.h | 4 + tools/rimage/config/mtl.toml.h | 4 + tools/rimage/config/nvl.toml.h | 4 + tools/rimage/config/ptl.toml.h | 4 + tools/rimage/config/wcl.toml.h | 4 + 18 files changed, 846 insertions(+), 348 deletions(-) create mode 100644 src/audio/tone/CMakeLists.txt create mode 100644 src/audio/tone/Kconfig create mode 100644 src/audio/tone/llext/CMakeLists.txt create mode 100644 src/audio/tone/llext/llext.toml.h rename src/audio/{tone.c => tone/tone-ipc3.c} (53%) create mode 100644 src/audio/tone/tone-ipc4.c create mode 100644 src/audio/tone/tone.c create mode 100644 src/audio/tone/tone.h create mode 100644 src/audio/tone/tone.toml diff --git a/src/arch/host/configs/library_defconfig b/src/arch/host/configs/library_defconfig index 912b93768abc..acf55993174a 100644 --- a/src/arch/host/configs/library_defconfig +++ b/src/arch/host/configs/library_defconfig @@ -21,6 +21,7 @@ CONFIG_COMP_SRC=y CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y CONFIG_COMP_STUBS=y CONFIG_COMP_TDFB=y +CONFIG_COMP_TONE=y CONFIG_COMP_VOLUME=y CONFIG_COMP_VOLUME_LINEAR_RAMP=y CONFIG_COMP_VOLUME_WINDOWS_FADE=y diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index d9de3c578d8e..732daf5e8da3 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -121,9 +121,7 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) add_subdirectory(mic_privacy_manager) endif() if(CONFIG_COMP_TONE) - add_local_sources(sof - tone.c - ) + add_subdirectory(tone) endif() if(CONFIG_ZEPHYR_NATIVE_DRIVERS) list(APPEND base_files host-zephyr.c) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index 6bc1bd6ca012..2dd5d54b18d9 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -93,13 +93,6 @@ config COMP_STUBS Select to force all 3P blocks to link against stubs rather than their libraries. This should only be used in testing environments like fuzzers or CI. -config COMP_TONE - bool "Tone component" - select CORDIC_FIXED - help - Select for Tone component. - Warning: This component is deprecated and will be removed from SOF v2.8. - config COMP_KPB bool "KPB component" default y @@ -154,6 +147,7 @@ rsource "src/Kconfig" rsource "tdfb/Kconfig" rsource "template/Kconfig" rsource "tensorflow/Kconfig" +rsource "tone/Kconfig" rsource "up_down_mixer/Kconfig" rsource "volume/Kconfig" # --- End Kconfig Sources (alphabetical order) --- diff --git a/src/audio/tone/CMakeLists.txt b/src/audio/tone/CMakeLists.txt new file mode 100644 index 000000000000..58f22ec6c59e --- /dev/null +++ b/src/audio/tone/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause + +if(CONFIG_COMP_TONE STREQUAL "m" AND DEFINED CONFIG_LLEXT) + add_subdirectory(llext ${PROJECT_BINARY_DIR}/tone_llext) + add_dependencies(app tone) +else() + if(CONFIG_IPC_MAJOR_3) + add_local_sources(sof tone.c tone-ipc3.c) + elseif(CONFIG_IPC_MAJOR_4) + add_local_sources(sof tone.c tone-ipc4.c) + endif() +endif() diff --git a/src/audio/tone/Kconfig b/src/audio/tone/Kconfig new file mode 100644 index 000000000000..8cb6f7302497 --- /dev/null +++ b/src/audio/tone/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-3-Clause + +config COMP_TONE + tristate "Tone component" + default m if LIBRARY_DEFAULT_MODULAR + default y + depends on COMP_MODULE_ADAPTER + depends on IPC_MAJOR_4 + select CORDIC_FIXED + help + Select for Tone component. This component is used to generate + audio tones (sine waves) at specified frequencies when the Tone + mode is enabled. The two other modes it supports are silence where + it generates 0s and passthrough where the input is passed to the + output. Warning: This component is untested with IPC_MAJOR_3 and + is unavailable. diff --git a/src/audio/tone/llext/CMakeLists.txt b/src/audio/tone/llext/CMakeLists.txt new file mode 100644 index 000000000000..39b0894a013a --- /dev/null +++ b/src/audio/tone/llext/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +sof_llext_build("tone" + SOURCES ../tone.c ../tone-ipc4.c + LIB openmodules +) diff --git a/src/audio/tone/llext/llext.toml.h b/src/audio/tone/llext/llext.toml.h new file mode 100644 index 000000000000..20f5940c3555 --- /dev/null +++ b/src/audio/tone/llext/llext.toml.h @@ -0,0 +1,6 @@ +#include +#define LOAD_TYPE "2" +#include "../tone.toml" + +[module] +count = __COUNTER__ diff --git a/src/audio/tone.c b/src/audio/tone/tone-ipc3.c similarity index 53% rename from src/audio/tone.c rename to src/audio/tone/tone-ipc3.c index 39c2ad820581..87df5e130b49 100644 --- a/src/audio/tone.c +++ b/src/audio/tone/tone-ipc3.c @@ -33,348 +33,13 @@ #include #include #include - -/* Convert float frequency in Hz to Q16.16 fractional format */ -#define TONE_FREQ(f) Q_CONVERT_FLOAT(f, 16) - -/* Convert float gain to Q1.31 fractional format */ -#define TONE_GAIN(v) Q_CONVERT_FLOAT(v, 31) - -/* Set default tone amplitude and frequency */ -#define TONE_AMPLITUDE_DEFAULT TONE_GAIN(0.1) /* -20 dB */ -#define TONE_FREQUENCY_DEFAULT TONE_FREQ(997.0) -#define TONE_NUM_FS 13 /* Table size for 8-192 kHz range */ - -static const struct comp_driver comp_tone; - -LOG_MODULE_REGISTER(tone, CONFIG_SOF_LOG_LEVEL); +#include "tone.h" SOF_DEFINE_REG_UUID(tone); - +LOG_MODULE_DECLARE(tone, CONFIG_SOF_LOG_LEVEL); DECLARE_TR_CTX(tone_tr, SOF_UUID(tone_uuid), LOG_LEVEL_INFO); -/* 2*pi/Fs lookup tables in Q1.31 for each Fs */ -static const int32_t tone_fs_list[TONE_NUM_FS] = { - 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, - 64000, 88200, 96000, 176400, 192000 -}; - -static const int32_t tone_pi2_div_fs[TONE_NUM_FS] = { - 1686630, 1223858, 843315, 611929, 562210, 421657, 305965, - 281105, 210829, 152982, 140552, 76491, 70276 -}; - -/* tone component private data */ - -struct tone_state { - int mute; - int32_t a; /* Current amplitude Q1.31 */ - int32_t a_target; /* Target amplitude Q1.31 */ - int32_t ampl_coef; /* Amplitude multiplier Q2.30 */ - int32_t c; /* Coefficient 2*pi/Fs Q1.31 */ - int32_t f; /* Frequency Q16.16 */ - int32_t freq_coef; /* Frequency multiplier Q2.30 */ - int32_t fs; /* Sample rate in Hertz Q32.0 */ - int32_t ramp_step; /* Amplitude ramp step Q1.31 */ - int32_t w; /* Angle radians Q4.28 */ - int32_t w_step; /* Angle step Q4.28 */ - uint32_t block_count; - uint32_t repeat_count; - uint32_t repeats; /* Number of repeats for tone (sweep steps) */ - uint32_t sample_count; - uint32_t samples_in_block; /* Samples in 125 us block */ - uint32_t tone_length; /* Active length in 125 us blocks */ - uint32_t tone_period; /* Active + idle time in 125 us blocks */ -}; - -struct comp_data { - uint32_t period_bytes; - uint32_t channels; - uint32_t frame_bytes; - uint32_t rate; - struct tone_state sg[PLATFORM_MAX_CHANNELS]; - void (*tone_func)(struct comp_dev *dev, struct audio_stream *sink, - uint32_t frames); -}; - -static int32_t tonegen(struct tone_state *sg); -static void tonegen_control(struct tone_state *sg); -static void tonegen_update_f(struct tone_state *sg, int32_t f); - -/* - * Tone generator algorithm code - */ - -static inline void tone_circ_inc_wrap(int32_t **ptr, int32_t *end, size_t size) -{ - if (*ptr >= end) - *ptr = (int32_t *)((size_t)*ptr - size); -} - -static void tone_s32_default(struct comp_dev *dev, struct audio_stream *sink, - uint32_t frames) -{ - struct comp_data *cd = comp_get_drvdata(dev); - int32_t *dest = audio_stream_get_wptr(sink); - int i; - int n; - int n_wrap_dest; - int n_min; - int nch = cd->channels; - - n = frames * nch; - while (n > 0) { - n_wrap_dest = (int32_t *)audio_stream_get_end_addr(sink) - dest; - n_min = (n < n_wrap_dest) ? n : n_wrap_dest; - /* Process until wrap or completed n */ - while (n_min > 0) { - n -= nch; - n_min -= nch; - for (i = 0; i < nch; i++) { - tonegen_control(&cd->sg[i]); - *dest = tonegen(&cd->sg[i]); - dest++; - } - } - tone_circ_inc_wrap(&dest, audio_stream_get_end_addr(sink), - audio_stream_get_size(sink)); - } -} - -static int32_t tonegen(struct tone_state *sg) -{ - int64_t sine; - int64_t w; - /* sg->w is angle in Q4.28 radians format, sin() returns Q1.31 */ - /* sg->a is amplitude as Q1.31 */ - sine = - q_mults_32x32(sin_fixed_32b(sg->w), sg->a, - Q_SHIFT_BITS_64(31, 31, 31)); - - /* Next point */ - w = (int64_t)sg->w + sg->w_step; - sg->w = (w > PI_MUL2_Q4_28) - ? (int32_t)(w - PI_MUL2_Q4_28) : (int32_t)w; - - if (sg->mute) - return 0; - else - return (int32_t)sine; /* Q1.31 no saturation need */ -} - -static void tonegen_control(struct tone_state *sg) -{ - int64_t a; - int64_t p; - - /* Count samples, 125 us blocks */ - sg->sample_count++; - if (sg->sample_count < sg->samples_in_block) - return; - - sg->sample_count = 0; - if (sg->block_count < INT32_MAX) - sg->block_count++; - - /* Fade-in ramp during tone */ - if (sg->block_count < sg->tone_length) { - if (sg->a == 0) - sg->w = 0; /* Reset phase to have less clicky ramp */ - - if (sg->a > sg->a_target) { - a = (int64_t)sg->a - sg->ramp_step; - if (a < sg->a_target) - a = sg->a_target; - - } else { - a = (int64_t)sg->a + sg->ramp_step; - if (a > sg->a_target) - a = sg->a_target; - } - sg->a = (int32_t)a; - } - - /* Fade-out ramp after tone*/ - if (sg->block_count > sg->tone_length) { - a = (int64_t)sg->a - sg->ramp_step; - if (a < 0) - a = 0; - - sg->a = (int32_t)a; - } - - /* New repeated tone, update for frequency or amplitude sweep */ - if ((sg->block_count > sg->tone_period) && - (sg->repeat_count + 1 < sg->repeats)) { - sg->block_count = 0; - if (sg->ampl_coef > 0) { - sg->a_target = - sat_int32(q_multsr_32x32(sg->a_target, - sg->ampl_coef, Q_SHIFT_BITS_64(31, 30, 31))); - sg->a = (sg->ramp_step > sg->a_target) - ? sg->a_target : sg->ramp_step; - } - if (sg->freq_coef > 0) { - /* f is Q16.16, freq_coef is Q2.30 */ - p = q_multsr_32x32(sg->f, sg->freq_coef, - Q_SHIFT_BITS_64(16, 30, 16)); - tonegen_update_f(sg, (int32_t)p); /* No saturation */ - } - sg->repeat_count++; - } -} - -/* Set sine amplitude */ -static inline void tonegen_set_a(struct tone_state *sg, int32_t a) -{ - sg->a_target = a; -} - -/* Repeated number of beeps */ -static void tonegen_set_repeats(struct tone_state *sg, uint32_t r) -{ - sg->repeats = r; -} - -/* The next functions support zero as shortcut for defaults to get - * make a nicer API without need to remember the neutral steady - * non-swept tone settings. - */ - -/* Multiplication factor for frequency as Q2.30 for logarithmic change */ -static void tonegen_set_freq_mult(struct tone_state *sg, int32_t fm) -{ - sg->freq_coef = (fm > 0) ? fm : ONE_Q2_30; /* Set freq mult to 1.0 */ -} - -/* Multiplication factor for amplitude as Q2.30 for logarithmic change */ -static void tonegen_set_ampl_mult(struct tone_state *sg, int32_t am) -{ - sg->ampl_coef = (am > 0) ? am : ONE_Q2_30; /* Set ampl mult to 1.0 */ -} - -/* Tone length in samples, this is the active length of tone */ -static void tonegen_set_length(struct tone_state *sg, uint32_t tl) -{ - sg->tone_length = (tl > 0) ? tl : INT32_MAX; /* Count rate 125 us */ -} - -/* Tone period in samples, this is the length including the pause after beep */ -static void tonegen_set_period(struct tone_state *sg, uint32_t tp) -{ - sg->tone_period = (tp > 0) ? tp : INT32_MAX; /* Count rate 125 us */ -} - -/* Tone ramp parameters: - * step - Value that is added or subtracted to amplitude. A zero or negative - * number disables the ramp and amplitude is immediately modified to - * final value. - */ - -static inline void tonegen_set_linramp(struct tone_state *sg, int32_t step) -{ - sg->ramp_step = (step > 0) ? step : INT32_MAX; -} - -static inline int32_t tonegen_get_f(struct tone_state *sg) -{ - return sg->f; -} - -static inline int32_t tonegen_get_a(struct tone_state *sg) -{ - return sg->a_target; -} - -static inline void tonegen_mute(struct tone_state *sg) -{ - sg->mute = 1; -} - -static inline void tonegen_unmute(struct tone_state *sg) -{ - sg->mute = 0; -} - -static void tonegen_update_f(struct tone_state *sg, int32_t f) -{ - int64_t w_tmp; - int64_t f_max; - - /* Calculate Fs/2, fs is Q32.0, f is Q16.16 */ - f_max = Q_SHIFT_LEFT((int64_t)sg->fs, 0, 16 - 1); - f_max = (f_max > INT32_MAX) ? INT32_MAX : f_max; - sg->f = (f > f_max) ? f_max : f; - /* Q16 x Q31 -> Q28 */ - w_tmp = q_multsr_32x32(sg->f, sg->c, Q_SHIFT_BITS_64(16, 31, 28)); - w_tmp = (w_tmp > PI_Q4_28) ? PI_Q4_28 : w_tmp; /* Limit to pi Q4.28 */ - sg->w_step = (int32_t)w_tmp; -} - -static void tonegen_reset(struct tone_state *sg) -{ - sg->mute = 1; - sg->a = 0; - sg->a_target = TONE_AMPLITUDE_DEFAULT; - sg->c = 0; - sg->f = TONE_FREQUENCY_DEFAULT; - sg->w = 0; - sg->w_step = 0; - - sg->block_count = 0; - sg->repeat_count = 0; - sg->repeats = 0; - sg->sample_count = 0; - sg->samples_in_block = 0; - - /* Continuous tone */ - sg->freq_coef = ONE_Q2_30; /* Set freq multiplier to 1.0 */ - sg->ampl_coef = ONE_Q2_30; /* Set ampl multiplier to 1.0 */ - sg->tone_length = INT32_MAX; - sg->tone_period = INT32_MAX; - sg->ramp_step = ONE_Q1_31; /* Set lin ramp modification to max */ -} - -static int tonegen_init(struct tone_state *sg, int32_t fs, int32_t f, int32_t a) -{ - int idx; - int i; - - sg->a_target = a; - sg->a = (sg->ramp_step > sg->a_target) ? sg->a_target : sg->ramp_step; - - idx = -1; - sg->mute = 1; - sg->fs = 0; - - /* Find index of current sample rate and then get from lookup table the - * corresponding 2*pi/Fs value. - */ - for (i = 0; i < TONE_NUM_FS; i++) { - if (fs == tone_fs_list[i]) - idx = i; - } - - if (idx < 0) { - sg->w_step = 0; - return -EINVAL; - } - - sg->fs = fs; - sg->c = tone_pi2_div_fs[idx]; /* Store 2*pi/Fs */ - sg->mute = 0; - tonegen_update_f(sg, f); - - /* 125us as Q1.31 is 268435, calculate fs * 125e-6 in Q31.0 */ - sg->samples_in_block = - (int32_t) q_multsr_32x32(fs, 268435, Q_SHIFT_BITS_64(0, 31, 0)); - - return 0; -} - -/* - * End of algorithm code. Next the standard component methods. - */ +static const struct comp_driver comp_tone; static struct comp_dev *tone_new(const struct comp_driver *drv, const struct comp_ipc_config *config, diff --git a/src/audio/tone/tone-ipc4.c b/src/audio/tone/tone-ipc4.c new file mode 100644 index 000000000000..2a4a030a6a71 --- /dev/null +++ b/src/audio/tone/tone-ipc4.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2016 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo +// Liam Girdwood +// Keyon Jie + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for SHARED_DATA */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tone.h" + +SOF_DEFINE_REG_UUID(tone); +LOG_MODULE_DECLARE(tone, CONFIG_SOF_LOG_LEVEL); +DECLARE_TR_CTX(tone_tr, SOF_UUID(tone_uuid), LOG_LEVEL_INFO); + +static int tone_init(struct processing_module *mod) +{ + struct module_data *mod_data = &mod->priv; + struct module_config *mod_config = &mod->priv.cfg; + struct comp_dev *dev = mod->dev; + struct comp_data *cd; + int i; + + cd = mod_alloc(mod, sizeof(*cd)); + if (!cd) + return -ENOMEM; + + mod_data->private = cd; + + /* Tnoe only supports 32-bit format */ + cd->tone_func = tone_s32_default; + + /* + * set direction for the comp. In the case of the tone generator being used for + * echo reference, the number of input pins will be non-zero + */ + if (mod_config->nb_input_pins > 0) { + dev->direction = SOF_IPC_STREAM_CAPTURE; + cd->mode = TONE_MODE_SILENCE; + + } else { + dev->direction = SOF_IPC_STREAM_PLAYBACK; + cd->mode = TONE_MODE_TONEGEN; + } + + dev->direction_set = true; + + /* Reset tone generator and set channels volumes to default */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + tonegen_reset(&cd->sg[i]); + + return 0; +} + +static int tone_free(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + + comp_info(mod->dev, "tone_free()"); + + mod_free(mod, cd); + return 0; +} + +/* set component audio stream parameters */ +static int tone_params(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + struct comp_dev *dev = mod->dev; + struct comp_buffer *sinkb; + enum sof_ipc_frame frame_fmt, valid_fmt; + + sinkb = comp_dev_get_first_data_consumer(dev); + if (!sinkb) { + comp_err(dev, "no sink buffer found for tone component"); + return -ENOTCONN; + } + + audio_stream_fmt_conversion(mod->priv.cfg.base_cfg.audio_fmt.depth, + mod->priv.cfg.base_cfg.audio_fmt.valid_bit_depth, + &frame_fmt, &valid_fmt, + mod->priv.cfg.base_cfg.audio_fmt.s_type); + cd->rate = mod->priv.cfg.base_cfg.audio_fmt.sampling_frequency; + + /* Tone supports only S32_LE PCM format atm */ + if (frame_fmt != SOF_IPC_FRAME_S32_LE) { + comp_err(dev, "unsupported frame_fmt = %u", frame_fmt); + return -EINVAL; + } + + return 0; +} + +/* copy and process stream data from source to sink buffers */ +static int tone_process(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) +{ + struct comp_data *cd = module_get_private_data(mod); + + if (num_of_sources > 0) + return cd->tone_func(mod, sinks[0], sources[0]); + + return cd->tone_func(mod, sinks[0], NULL); +} + +static int tone_prepare(struct processing_module *mod, struct sof_source **sources, + int num_of_sources, struct sof_sink **sinks, int num_of_sinks) +{ + struct comp_data *cd = module_get_private_data(mod); + int32_t f; + int32_t a; + int ret; + int i; + + ret = tone_params(mod); + if (ret < 0) + return ret; + + cd->channels = mod->priv.cfg.base_cfg.audio_fmt.channels_count; + + for (i = 0; i < cd->channels; i++) { + f = tonegen_get_f(&cd->sg[i]); + a = tonegen_get_a(&cd->sg[i]); + if (tonegen_init(&cd->sg[i], cd->rate, f, a) < 0) + return -EINVAL; + } + + return 0; +} + +static int tone_reset(struct processing_module *mod) +{ + struct comp_data *cd = module_get_private_data(mod); + int i; + + /* Initialize with the defaults */ + for (i = 0; i < PLATFORM_MAX_CHANNELS; i++) + tonegen_reset(&cd->sg[i]); + + return 0; +} + +static int tone_bind(struct processing_module *mod, struct bind_info *bind_data) +{ + struct comp_data *cd = module_get_private_data(mod); + + /* nothing to do when tone is not the sink */ + if (bind_data->bind_type != COMP_BIND_TYPE_SOURCE) + return 0; + + /* set passthrough mode when tone generator is bound as a sink */ + cd->mode = TONE_MODE_PASSTHROUGH; + + return 0; +} + +static int tone_unbind(struct processing_module *mod, struct bind_info *unbind_data) +{ + struct comp_data *cd = module_get_private_data(mod); + + /* nothing to do when tone is not the sink */ + if (unbind_data->bind_type != COMP_BIND_TYPE_SOURCE) + return 0; + + /* set silence mode when tone generator is unbound from a source module */ + cd->mode = TONE_MODE_SILENCE; + + return 0; +} + +static const struct module_interface tone_interface = { + .init = tone_init, + .prepare = tone_prepare, + .process = tone_process, + .reset = tone_reset, + .free = tone_free, + .bind = tone_bind, + .unbind = tone_unbind, +}; + +#if CONFIG_COMP_TONE_MODULE +/* modular: llext dynamic link */ + +#include +#include +#include + +static const struct sof_man_module_manifest mod_manifest[] __section(".module") __used = { + SOF_LLEXT_MODULE_MANIFEST("TONE", &tone_interface, 1, SOF_REG_UUID(tone), 30), +}; + +SOF_LLEXT_BUILDINFO; + +#else + +DECLARE_MODULE_ADAPTER(tone_interface, tone_uuid, tone_tr); +SOF_MODULE_INIT(tone, sys_comp_module_tone_interface_init); + +#endif diff --git a/src/audio/tone/tone.c b/src/audio/tone/tone.c new file mode 100644 index 000000000000..4123b2831da3 --- /dev/null +++ b/src/audio/tone/tone.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2016 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo +// Liam Girdwood +// Keyon Jie + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for SHARED_DATA */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tone.h" + +LOG_MODULE_REGISTER(tone, CONFIG_SOF_LOG_LEVEL); + +static int32_t tonegen(struct tone_state *sg) +{ + int64_t sine; + int64_t w; + /* sg->w is angle in Q4.28 radians format, sin() returns Q1.31 */ + /* sg->a is amplitude as Q1.31 */ + sine = + q_mults_32x32(sin_fixed_32b(sg->w), sg->a, + Q_SHIFT_BITS_64(31, 31, 31)); + + /* Next point */ + w = (int64_t)sg->w + sg->w_step; + sg->w = (w > PI_MUL2_Q4_28) + ? (int32_t)(w - PI_MUL2_Q4_28) : (int32_t)w; + + if (sg->mute) + return 0; + else + return (int32_t)sine; /* Q1.31 no saturation need */ +} + +static void tonegen_control(struct tone_state *sg) +{ + int64_t a; + int64_t p; + + /* Count samples, 125 us blocks */ + sg->sample_count++; + if (sg->sample_count < sg->samples_in_block) + return; + + sg->sample_count = 0; + if (sg->block_count < INT32_MAX) + sg->block_count++; + + /* Fade-in ramp during tone */ + if (sg->block_count < sg->tone_length) { + if (sg->a == 0) + sg->w = 0; /* Reset phase to have less clicky ramp */ + + if (sg->a > sg->a_target) { + a = (int64_t)sg->a - sg->ramp_step; + if (a < sg->a_target) + a = sg->a_target; + + } else { + a = (int64_t)sg->a + sg->ramp_step; + if (a > sg->a_target) + a = sg->a_target; + } + sg->a = (int32_t)a; + } + + /* Fade-out ramp after tone*/ + if (sg->block_count > sg->tone_length) { + a = (int64_t)sg->a - sg->ramp_step; + if (a < 0) + a = 0; + + sg->a = (int32_t)a; + } + + /* New repeated tone, update for frequency or amplitude sweep */ + if (sg->block_count > sg->tone_period && + (sg->repeat_count + 1 < sg->repeats)) { + sg->block_count = 0; + if (sg->ampl_coef > 0) { + sg->a_target = + sat_int32(q_multsr_32x32(sg->a_target, + sg->ampl_coef, + Q_SHIFT_BITS_64(31, 30, 31))); + sg->a = (sg->ramp_step > sg->a_target) + ? sg->a_target : sg->ramp_step; + } + if (sg->freq_coef > 0) { + /* f is Q16.16, freq_coef is Q2.30 */ + p = q_multsr_32x32(sg->f, sg->freq_coef, + Q_SHIFT_BITS_64(16, 30, 16)); + tonegen_update_f(sg, (int32_t)p); /* No saturation */ + } + sg->repeat_count++; + } +} + +static int tone_s32_passthrough(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source) +{ + struct comp_data *cd = module_get_private_data(mod); + size_t output_frame_bytes, output_frames; + size_t input_frame_bytes, input_frames; + int32_t *output_pos, *output_start, output_cirbuf_size; + int32_t const *input_pos, *input_start, *input_end; + int32_t *output_end, input_cirbuf_size; + uint32_t frames, bytes; + int nch = cd->channels; + int n; + int ret; + + /* tone generator only ever has 1 sink */ + output_frames = sink_get_free_frames(sink); + output_frame_bytes = sink_get_frame_bytes(sink); + output_frames = mod->period_bytes / output_frame_bytes; + + ret = sink_get_buffer_s32(sink, output_frames * output_frame_bytes, + &output_pos, &output_start, &output_cirbuf_size); + if (ret) { + comp_err(mod->dev, "tone_s32_passthrough(): sink_get_buffer_s32() failed"); + return -ENODATA; + } + + input_frames = source_get_data_frames_available(source); + input_frame_bytes = source_get_frame_bytes(source); + + ret = source_get_data_s32(source, input_frames * input_frame_bytes, + &input_pos, &input_start, &input_cirbuf_size); + if (ret) { + comp_err(mod->dev, "tone_s32_passthrough(): source_get_data_s32() failed"); + return -ENODATA; + } + input_end = input_start + input_cirbuf_size; + + frames = MIN(output_frames, input_frames); + + if (frames * output_frame_bytes >= mod->period_bytes) + frames = mod->period_bytes / output_frame_bytes; + bytes = frames * output_frame_bytes; + + output_end = output_start + output_cirbuf_size; + + n = frames * nch; + + while (n > 0) { + int n_wrap_source, n_wrap_dest, n_min; + int i; + + n_wrap_dest = output_end - output_pos; + n_wrap_source = input_end - input_pos; + + /* Process until source/dest wrap or completed n */ + n_min = (n < n_wrap_dest) ? n : n_wrap_dest; + n_min = (n_min < n_wrap_source) ? n_min : n_wrap_source; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + *output_pos = *input_pos; + output_pos++; + input_pos++; + } + } + + /* Wrap destination/source buffer */ + if (output_pos >= output_end) + output_pos = output_start; + if (input_pos >= input_end) + input_pos = input_start; + } + + ret = sink_commit_buffer(sink, bytes); + if (ret) + return ret; + + return source_release_data(source, bytes); +} + +/* + * Tone generator algorithm code + */ +int tone_s32_default(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source) +{ + struct comp_data *cd = module_get_private_data(mod); + size_t output_frame_bytes, output_frames; + int32_t *output_pos, *output_start, output_cirbuf_size; + int32_t *output_end; + uint32_t frames, bytes; + int nch = cd->channels; + int i; + int n; + int n_wrap_dest; + int n_min; + int ret; + + if (cd->mode == TONE_MODE_PASSTHROUGH) + return tone_s32_passthrough(mod, sink, source); + + /* tone generator only ever has 1 sink */ + output_frames = sink_get_free_frames(sink); + output_frame_bytes = sink_get_frame_bytes(sink); + output_frames = mod->period_bytes / output_frame_bytes; + + ret = sink_get_buffer_s32(sink, output_frames * output_frame_bytes, + &output_pos, &output_start, &output_cirbuf_size); + if (ret) + return -ENODATA; + + frames = output_frames; + + if (frames * output_frame_bytes >= mod->period_bytes) + frames = mod->period_bytes / output_frame_bytes; + bytes = frames * output_frame_bytes; + + output_end = output_start + output_cirbuf_size; + + n = frames * nch; + if (!source) { + while (n > 0) { + n_wrap_dest = output_end - output_pos; + + /* Process until wrap or completed n */ + n_min = (n < n_wrap_dest) ? n : n_wrap_dest; + while (n_min > 0) { + n -= nch; + n_min -= nch; + for (i = 0; i < nch; i++) { + switch (cd->mode) { + case TONE_MODE_TONEGEN: + tonegen_control(&cd->sg[i]); + *output_pos = tonegen(&cd->sg[i]); + break; + case TONE_MODE_SILENCE: + *output_pos = 0; + break; + default: + break; + } + output_pos++; + } + } + + /* Wrap destination buffer */ + output_pos = output_start; + } + } + + return sink_commit_buffer(sink, bytes); +} + +void tonegen_update_f(struct tone_state *sg, int32_t f) +{ + int64_t w_tmp; + int64_t f_max; + + /* Calculate Fs/2, fs is Q32.0, f is Q16.16 */ + f_max = Q_SHIFT_LEFT((int64_t)sg->fs, 0, 16 - 1); + f_max = (f_max > INT32_MAX) ? INT32_MAX : f_max; + sg->f = (f > f_max) ? f_max : f; + /* Q16 x Q31 -> Q28 */ + w_tmp = q_multsr_32x32(sg->f, sg->c, Q_SHIFT_BITS_64(16, 31, 28)); + w_tmp = (w_tmp > PI_Q4_28) ? PI_Q4_28 : w_tmp; /* Limit to pi Q4.28 */ + sg->w_step = (int32_t)w_tmp; +} + +void tonegen_reset(struct tone_state *sg) +{ + sg->mute = 1; + sg->a = 0; + sg->a_target = TONE_AMPLITUDE_DEFAULT; + sg->c = 0; + sg->f = TONE_FREQUENCY_DEFAULT; + sg->w = 0; + sg->w_step = 0; + + sg->block_count = 0; + sg->repeat_count = 0; + sg->repeats = 0; + sg->sample_count = 0; + sg->samples_in_block = 0; + + /* Continuous tone */ + sg->freq_coef = ONE_Q2_30; /* Set freq multiplier to 1.0 */ + sg->ampl_coef = ONE_Q2_30; /* Set ampl multiplier to 1.0 */ + sg->tone_length = INT32_MAX; + sg->tone_period = INT32_MAX; + sg->ramp_step = ONE_Q1_31; /* Set lin ramp modification to max */ +} + +int tonegen_init(struct tone_state *sg, int32_t fs, int32_t f, int32_t a) +{ + int idx; + int i; + + sg->a_target = a; + sg->a = (sg->ramp_step > sg->a_target) ? sg->a_target : sg->ramp_step; + + idx = -1; + sg->mute = 1; + sg->fs = 0; + + /* Find index of current sample rate and then get from lookup table the + * corresponding 2*pi/Fs value. + */ + for (i = 0; i < TONE_NUM_FS; i++) { + if (fs == tone_fs_list[i]) + idx = i; + } + + if (idx < 0) { + sg->w_step = 0; + return -EINVAL; + } + + sg->fs = fs; + sg->c = tone_pi2_div_fs[idx]; /* Store 2*pi/Fs */ + sg->mute = 0; + tonegen_update_f(sg, f); + + /* 125us as Q1.31 is 268435, calculate fs * 125e-6 in Q31.0 */ + sg->samples_in_block = + (int32_t)q_multsr_32x32(fs, 268435, Q_SHIFT_BITS_64(0, 31, 0)); + + return 0; +} diff --git a/src/audio/tone/tone.h b/src/audio/tone/tone.h new file mode 100644 index 000000000000..4183543e0beb --- /dev/null +++ b/src/audio/tone/tone.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2016 Intel Corporation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for SHARED_DATA */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Convert float frequency in Hz to Q16.16 fractional format */ +#define TONE_FREQ(f) Q_CONVERT_FLOAT(f, 16) + +/* Convert float gain to Q1.31 fractional format */ +#define TONE_GAIN(v) Q_CONVERT_FLOAT(v, 31) + +/* Set default tone amplitude and frequency */ +#define TONE_AMPLITUDE_DEFAULT TONE_GAIN(0.1) /* -20 dB */ +#define TONE_FREQUENCY_DEFAULT TONE_FREQ(997.0) +#define TONE_NUM_FS 13 /* Table size for 8-192 kHz range */ + +#define TONE_MODE_TONEGEN 0 +#define TONE_MODE_PASSTHROUGH 1 +#define TONE_MODE_SILENCE 2 + +extern struct tr_ctx tone_tr; +extern const struct sof_uuid tone; + +/* 2*pi/Fs lookup tables in Q1.31 for each Fs */ +static const int32_t tone_fs_list[TONE_NUM_FS] = { + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, + 64000, 88200, 96000, 176400, 192000 +}; + +static const int32_t tone_pi2_div_fs[TONE_NUM_FS] = { + 1686630, 1223858, 843315, 611929, 562210, 421657, 305965, + 281105, 210829, 152982, 140552, 76491, 70276 +}; + +/* tone component private data */ + +struct tone_state { + int mute; + int32_t a; /* Current amplitude Q1.31 */ + int32_t a_target; /* Target amplitude Q1.31 */ + int32_t ampl_coef; /* Amplitude multiplier Q2.30 */ + int32_t c; /* Coefficient 2*pi/Fs Q1.31 */ + int32_t f; /* Frequency Q16.16 */ + int32_t freq_coef; /* Frequency multiplier Q2.30 */ + int32_t fs; /* Sample rate in Hertz Q32.0 */ + int32_t ramp_step; /* Amplitude ramp step Q1.31 */ + int32_t w; /* Angle radians Q4.28 */ + int32_t w_step; /* Angle step Q4.28 */ + uint32_t block_count; + uint32_t repeat_count; + uint32_t repeats; /* Number of repeats for tone (sweep steps) */ + uint32_t sample_count; + uint32_t samples_in_block; /* Samples in 125 us block */ + uint32_t tone_length; /* Active length in 125 us blocks */ + uint32_t tone_period; /* Active + idle time in 125 us blocks */ +}; + +struct comp_data { + uint32_t channels; + uint32_t rate; + struct tone_state sg[PLATFORM_MAX_CHANNELS]; + int (*tone_func)(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source); + int mode; +}; + +void tonegen_reset(struct tone_state *sg); +int tonegen_init(struct tone_state *sg, int32_t fs, int32_t f, int32_t a); +void tonegen_update_f(struct tone_state *sg, int32_t f); +int tone_s32_default(struct processing_module *mod, struct sof_sink *sink, + struct sof_source *source); + +/* Set sine amplitude */ +static inline void tonegen_set_a(struct tone_state *sg, int32_t a) +{ + sg->a_target = a; +} + +/* Repeated number of beeps */ +static inline void tonegen_set_repeats(struct tone_state *sg, uint32_t r) +{ + sg->repeats = r; +} + +/* The next functions support zero as shortcut for defaults to get + * make a nicer API without need to remember the neutral steady + * non-swept tone settings. + */ + +/* Multiplication factor for frequency as Q2.30 for logarithmic change */ +static inline void tonegen_set_freq_mult(struct tone_state *sg, int32_t fm) +{ + sg->freq_coef = (fm > 0) ? fm : ONE_Q2_30; /* Set freq mult to 1.0 */ +} + +/* Multiplication factor for amplitude as Q2.30 for logarithmic change */ +static inline void tonegen_set_ampl_mult(struct tone_state *sg, int32_t am) +{ + sg->ampl_coef = (am > 0) ? am : ONE_Q2_30; /* Set ampl mult to 1.0 */ +} + +/* Tone length in samples, this is the active length of tone */ +static inline void tonegen_set_length(struct tone_state *sg, uint32_t tl) +{ + sg->tone_length = (tl > 0) ? tl : INT32_MAX; /* Count rate 125 us */ +} + +/* Tone period in samples, this is the length including the pause after beep */ +static inline void tonegen_set_period(struct tone_state *sg, uint32_t tp) +{ + sg->tone_period = (tp > 0) ? tp : INT32_MAX; /* Count rate 125 us */ +} + +/* Tone ramp parameters: + * step - Value that is added or subtracted to amplitude. A zero or negative + * number disables the ramp and amplitude is immediately modified to + * final value. + */ + +static inline void tonegen_set_linramp(struct tone_state *sg, int32_t step) +{ + sg->ramp_step = (step > 0) ? step : INT32_MAX; +} + +static inline int32_t tonegen_get_f(struct tone_state *sg) +{ + return sg->f; +} + +static inline int32_t tonegen_get_a(struct tone_state *sg) +{ + return sg->a_target; +} + +static inline void tonegen_mute(struct tone_state *sg) +{ + sg->mute = 1; +} + +static inline void tonegen_unmute(struct tone_state *sg) +{ + sg->mute = 0; +} diff --git a/src/audio/tone/tone.toml b/src/audio/tone/tone.toml new file mode 100644 index 000000000000..02d6e5dca96b --- /dev/null +++ b/src/audio/tone/tone.toml @@ -0,0 +1,22 @@ +#ifndef LOAD_TYPE +#define LOAD_TYPE "0" +#endif + + REM # Template component module config + [[module.entry]] + name = "TONE" + uuid = UUIDREG_STR_TONE + affinity_mask = "0x1" + instance_count = "40" + domain_types = "0" + load_type = LOAD_TYPE + init_config = "1" + module_type = "9" + auto_start = "0" + sched_caps = [1, 0x00008000] + REM # pin = [dir, type, sample rate, size, container, channel-cfg] + pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff] + REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS] + mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0] + + index = __COUNTER__ diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index dfc840d67bb9..df44ae6dad37 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -956,6 +956,7 @@ void sys_comp_module_sound_dose_interface_init(void); void sys_comp_module_src_interface_init(void); void sys_comp_module_src_lite_interface_init(void); void sys_comp_module_tdfb_interface_init(void); +void sys_comp_module_tone_interface_init(void); void sys_comp_module_template_interface_init(void); void sys_comp_module_tester_interface_init(void); void sys_comp_module_volume_interface_init(void); diff --git a/tools/rimage/config/lnl.toml.h b/tools/rimage/config/lnl.toml.h index 5fc7008bfbe8..a58272369c0b 100644 --- a/tools/rimage/config/lnl.toml.h +++ b/tools/rimage/config/lnl.toml.h @@ -150,5 +150,9 @@ #include