Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ab9d56c

Browse files
committedJun 19, 2025·
Audio: Sound Dose: Add new component
This patch adds a new SOF component Sound Dose. The purpose is to calculate for audio playback MEL values (momentary sound exposure level) to provide to user space the data to compute the sound dose CSD as defined in EN 50332-3. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent a2502bf commit ab9d56c

27 files changed

+1078
-2
lines changed
 

‎app/boards/intel_adsp_ace15_mtpm.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y
1616
CONFIG_COMP_SRC_LITE=y
1717
CONFIG_COMP_MFCC=y
1818
CONFIG_COMP_MULTIBAND_DRC=y
19+
CONFIG_COMP_SOUND_DOSE=y
1920
CONFIG_COMP_UP_DOWN_MIXER=y
2021
CONFIG_COMP_VOLUME_WINDOWS_FADE=y
2122
CONFIG_FORMAT_CONVERT_HIFI3=n

‎app/boards/intel_adsp_ace20_lnl.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CONFIG_COMP_CHAIN_DMA=y
1111
CONFIG_COMP_DRC=m
1212
CONFIG_COMP_KPB=y
1313
CONFIG_COMP_TESTER=m
14+
CONFIG_COMP_SOUND_DOSE=y
1415
CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y
1516
CONFIG_COMP_UP_DOWN_MIXER=y
1617
CONFIG_COMP_VOLUME_WINDOWS_FADE=y

‎app/boards/intel_adsp_ace30_ptl.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CONFIG_MM_DRV=y
1010
CONFIG_COMP_CHAIN_DMA=y
1111
CONFIG_COMP_KPB=y
1212
CONFIG_COMP_TESTER=m
13+
CONFIG_COMP_SOUND_DOSE=y
1314
CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y
1415
CONFIG_COMP_UP_DOWN_MIXER=y
1516
CONFIG_COMP_VOLUME_WINDOWS_FADE=y

‎app/boards/intel_adsp_ace30_wcl.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CONFIG_MM_DRV=y
1010
CONFIG_COMP_CHAIN_DMA=y
1111
CONFIG_COMP_KPB=y
1212
CONFIG_COMP_TESTER=m
13+
CONFIG_COMP_SOUND_DOSE=y
1314
CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y
1415
CONFIG_COMP_UP_DOWN_MIXER=y
1516
CONFIG_COMP_VOLUME_WINDOWS_FADE=y

‎app/boards/intel_adsp_cavs25.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ CONFIG_COMP_CROSSOVER=y
1212
CONFIG_COMP_DRC=y
1313
CONFIG_COMP_MFCC=y
1414
CONFIG_COMP_MULTIBAND_DRC=y
15+
CONFIG_COMP_SOUND_DOSE=y
1516
CONFIG_COMP_VOLUME_WINDOWS_FADE=y
1617
CONFIG_FORMAT_CONVERT_HIFI3=n
1718
CONFIG_PCM_CONVERTER_FORMAT_S16LE=y

‎app/boards/intel_adsp_cavs25_tgph.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ CONFIG_COMP_CROSSOVER=y
1212
CONFIG_COMP_DRC=y
1313
CONFIG_COMP_MFCC=y
1414
CONFIG_COMP_MULTIBAND_DRC=y
15+
CONFIG_COMP_SOUND_DOSE=y
1516
CONFIG_COMP_VOLUME_WINDOWS_FADE=y
1617
CONFIG_FORMAT_CONVERT_HIFI3=n
1718
CONFIG_PCM_CONVERTER_FORMAT_S16LE=y

‎src/arch/host/configs/library_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ CONFIG_COMP_MULTIBAND_DRC=y
1414
CONFIG_COMP_MUX=y
1515
CONFIG_COMP_RTNR=y
1616
CONFIG_COMP_SEL=y
17+
CONFIG_COMP_SOUND_DOSE=y
1718
CONFIG_COMP_SRC=y
1819
CONFIG_COMP_SRC_IPC4_FULL_MATRIX=y
1920
CONFIG_COMP_STUBS=y

‎src/audio/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)
120120
list(APPEND base_files host-legacy.c)
121121
sof_list_append_ifdef(CONFIG_COMP_DAI base_files dai-legacy.c)
122122
endif()
123+
if(CONFIG_COMP_SOUND_DOSE)
124+
add_subdirectory(sound_dose)
125+
endif()
123126
if(CONFIG_COMP_TEMPLATE_COMP)
124127
add_subdirectory(template_comp)
125128
endif()

‎src/audio/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ rsource "mfcc/Kconfig"
168168

169169
rsource "codec/Kconfig"
170170

171+
rsource "sound_dose/Kconfig"
172+
171173
rsource "template_comp/Kconfig"
172174

173175
endmenu # "Audio components"

‎src/audio/sound_dose/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
3+
if(CONFIG_COMP_TEMPLATE_COMP STREQUAL "m")
4+
add_subdirectory(llext ${PROJECT_BINARY_DIR}/sound_dose_llext)
5+
add_dependencies(app sound_dose)
6+
else()
7+
add_local_sources(sof sound_dose.c)
8+
add_local_sources(sof sound_dose-generic.c)
9+
10+
if(CONFIG_IPC_MAJOR_3)
11+
add_local_sources(sof sound_dose-ipc3.c)
12+
elseif(CONFIG_IPC_MAJOR_4)
13+
add_local_sources(sof sound_dose-ipc4.c)
14+
endif()
15+
endif()

‎src/audio/sound_dose/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
3+
config COMP_SOUND_DOSE
4+
tristate "Sound Dose module"
5+
default m if LIBRARY_DEFAULT_MODULAR
6+
default n
7+
depends on COMP_MODULE_ADAPTER
8+
select MATH_IIR
9+
select MATH_IIR_DF1
10+
help
11+
Select this for Sound Dose SOF module. The purpose is
12+
to calculate for audio playback MEL values (momentary
13+
sound exposure level) to provide to user space the data
14+
to compute the sound dose CSD as defined in EN 50332-3.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Intel Corporation.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
sof_llext_build("sound_dose"
5+
SOURCES ../sound_dose.c
6+
../sound_dose-generic.c
7+
../sound_dose-ipc4.c
8+
LIB openmodules
9+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <tools/rimage/config/platform.toml>
2+
#define LOAD_TYPE "2"
3+
#include "../sound_dose.toml"
4+
5+
[module]
6+
count = __COUNTER__
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation.
4+
5+
#include <sof/audio/module_adapter/module/generic.h>
6+
#include <sof/audio/component.h>
7+
#include <sof/audio/sink_api.h>
8+
#include <sof/audio/sink_source_utils.h>
9+
#include <sof/audio/source_api.h>
10+
#include <sof/math/log.h>
11+
#include <sof/math/iir_df1.h>
12+
#include <user/eq.h>
13+
#include <stdbool.h>
14+
#include <stdint.h>
15+
16+
#include "sound_dose.h"
17+
18+
LOG_MODULE_DECLARE(sound_dose, CONFIG_SOF_LOG_LEVEL);
19+
20+
static void sound_dose_calculate_mel(const struct processing_module *mod, int frames)
21+
{
22+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
23+
uint64_t energy_sum;
24+
uint32_t log_arg;
25+
int32_t tmp;
26+
int ch;
27+
28+
cd->frames_count += frames;
29+
if (cd->frames_count < cd->report_count)
30+
return;
31+
32+
cd->frames_count = 0;
33+
energy_sum = 0;
34+
for (ch = 0; ch < cd->channels; ch++) {
35+
energy_sum += cd->energy[ch];
36+
cd->energy[ch] = 0;
37+
}
38+
39+
/* Log2 argument is Q32.0 unsigned, log2 returns Q16.16 signed.
40+
* Energy is Qx.30, so the argument 2^30 times scaled. Also to keep
41+
* argument within uint32_t range, need to scale it down by 19.
42+
* To compensate these, need to add 19 (Q16.16) and subtract 30 (Q16.16)
43+
* from logarithm value.
44+
*/
45+
log_arg = (uint32_t)(energy_sum >> SOUND_DOSE_ENERGY_SHIFT);
46+
log_arg = MAX(log_arg, 1);
47+
tmp = base2_logarithm(log_arg);
48+
tmp += SOUND_DOSE_LOG_FIXED_OFFSET; /* Compensate Q2.30 and energy shift */
49+
tmp += cd->log_offset_for_mean; /* logarithm subtract for mean */
50+
tmp = Q_MULTSR_32X32((int64_t)tmp, SOUND_DOSE_TEN_OVER_LOG2_10_Q29, 16, 29, 16);
51+
cd->level_dbfs = tmp + SOUND_DOSE_WEIGHT_FILTERS_OFFS_Q16 + SOUND_DOSE_DFBS_OFFS_Q16;
52+
53+
/* If stereo sum channel level values and subtract 3 dB, to generalize
54+
* For stereo or more subtract -1.5 dB per channel.
55+
*/
56+
if (cd->channels > 1)
57+
cd->level_dbfs += cd->channels * SOUND_DOSE_MEL_CHANNELS_SUM_FIX;
58+
59+
sound_dose_report_mel(mod);
60+
}
61+
62+
#if CONFIG_FORMAT_S16LE
63+
64+
/**
65+
* sound_dose_s16() - Process S16_LE format.
66+
* @mod: Pointer to module data.
67+
* @source: Source for PCM samples data.
68+
* @sink: Sink for PCM samples data.
69+
* @frames: Number of audio data frames to process.
70+
*
71+
* This is the processing function for 16-bit signed integer PCM formats. The
72+
* audio samples in every frame are re-order to channels order defined in
73+
* component data channel_map[].
74+
*
75+
* Return: Value zero for success, otherwise an error code.
76+
*/
77+
static int sound_dose_s16(const struct processing_module *mod,
78+
struct sof_source *source,
79+
struct sof_sink *sink,
80+
uint32_t frames)
81+
{
82+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
83+
struct iir_state_df1 *iir;
84+
int32_t weighted;
85+
int16_t sample;
86+
int16_t const *x0, *x, *x_start, *x_end;
87+
int16_t *y0, *y, *y_start, *y_end;
88+
int x_size, y_size;
89+
int source_samples_without_wrap;
90+
int samples_without_wrap;
91+
int samples = frames * cd->channels;
92+
int bytes = frames * cd->frame_bytes;
93+
int ret;
94+
int ch;
95+
int i;
96+
const int channels = cd->channels;
97+
98+
/* Get pointer to source data in circular buffer, get buffer start and size to
99+
* check for wrap. The size in bytes is converted to number of s16 samples to
100+
* control the samples process loop. If the number of bytes requested is not
101+
* possible, an error is returned.
102+
*/
103+
ret = source_get_data_s16(source, bytes, &x0, &x_start, &x_size);
104+
if (ret)
105+
return ret;
106+
107+
/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
108+
ret = sink_get_buffer_s16(sink, bytes, &y0, &y_start, &y_size);
109+
if (ret)
110+
return ret;
111+
112+
/* Set helper pointers to buffer end for wrap check. Then loop until all
113+
* samples are processed.
114+
*/
115+
x_end = x_start + x_size;
116+
y_end = y_start + y_size;
117+
while (samples) {
118+
/* Find out samples to process before first wrap or end of data. */
119+
source_samples_without_wrap = x_end - x0;
120+
samples_without_wrap = y_end - y0;
121+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
122+
samples_without_wrap = MIN(samples_without_wrap, samples);
123+
for (ch = 0; ch < cd->channels; ch++) {
124+
iir = &cd->iir[ch];
125+
x = x0++;
126+
y = y0++;
127+
for (i = 0; i < samples_without_wrap; i += channels) {
128+
sample = *x;
129+
*y = sample;
130+
x += channels;
131+
y += channels;
132+
weighted = iir_df1(iir, ((int32_t)sample) << 16) >> 16;
133+
134+
/* Update sound dose, energy is Q1.15 * Q1.15 --> Q2.30 */
135+
cd->energy[ch] += weighted * weighted;
136+
}
137+
}
138+
139+
/* One of the buffers needs a wrap (or end of data), so check for wrap */
140+
x0 += samples_without_wrap;
141+
y0 += samples_without_wrap;
142+
x0 = (x0 >= x_end) ? x0 - x_size : x0;
143+
y0 = (y0 >= y_end) ? y0 - y_size : y0;
144+
145+
/* Update processed samples count for next loop iteration. */
146+
samples -= samples_without_wrap;
147+
}
148+
149+
/* Update the source and sink for bytes consumed and produced. Return success. */
150+
source_release_data(source, bytes);
151+
sink_commit_buffer(sink, bytes);
152+
153+
sound_dose_calculate_mel(mod, frames);
154+
return 0;
155+
}
156+
#endif /* CONFIG_FORMAT_S16LE */
157+
158+
#if CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S32LE
159+
160+
/**
161+
* sound_dose_s32() - Process S32_LE or S24_4LE format.
162+
* @mod: Pointer to module data.
163+
* @source: Source for PCM samples data.
164+
* @sink: Sink for PCM samples data.
165+
* @frames: Number of audio data frames to process.
166+
*
167+
* Processing function for signed integer 32-bit PCM formats. The same
168+
* function works for s24 and s32 formats since the samples values are
169+
* not modified in computation. The audio samples in every frame are
170+
* re-order to channels order defined in component data channel_map[].
171+
*
172+
* Return: Value zero for success, otherwise an error code.
173+
*/
174+
static int sound_dose_s32(const struct processing_module *mod,
175+
struct sof_source *source,
176+
struct sof_sink *sink,
177+
uint32_t frames)
178+
{
179+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
180+
struct iir_state_df1 *iir;
181+
int32_t sample;
182+
int32_t const *x0, *x, *x_start, *x_end;
183+
int32_t *y0, *y, *y_start, *y_end;
184+
int32_t weighted;
185+
int x_size, y_size;
186+
int source_samples_without_wrap;
187+
int samples_without_wrap;
188+
int samples = frames * cd->channels;
189+
int bytes = frames * cd->frame_bytes;
190+
int ret;
191+
int ch;
192+
int i;
193+
const int channels = cd->channels;
194+
195+
/* Get pointer to source data in circular buffer, get buffer start and size to
196+
* check for wrap. The size in bytes is converted to number of s16 samples to
197+
* control the samples process loop. If the number of bytes requested is not
198+
* possible, an error is returned.
199+
*/
200+
ret = source_get_data_s32(source, bytes, &x0, &x_start, &x_size);
201+
if (ret)
202+
return ret;
203+
204+
/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
205+
ret = sink_get_buffer_s32(sink, bytes, &y0, &y_start, &y_size);
206+
if (ret)
207+
return ret;
208+
209+
/* Set helper pointers to buffer end for wrap check. Then loop until all
210+
* samples are processed.
211+
*/
212+
x_end = x_start + x_size;
213+
y_end = y_start + y_size;
214+
while (samples) {
215+
/* Find out samples to process before first wrap or end of data. */
216+
source_samples_without_wrap = x_end - x0;
217+
samples_without_wrap = y_end - y0;
218+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
219+
samples_without_wrap = MIN(samples_without_wrap, samples);
220+
for (ch = 0; ch < cd->channels; ch++) {
221+
iir = &cd->iir[ch];
222+
x = x0++;
223+
y = y0++;
224+
for (i = 0; i < samples_without_wrap; i += channels) {
225+
sample = *x;
226+
*y = sample;
227+
x += channels;
228+
y += channels;
229+
weighted = iir_df1(iir, sample) >> 16;
230+
231+
/* Update sound dose, energy is Q1.15 * Q1.15 --> Q2.30 */
232+
cd->energy[ch] += weighted * weighted;
233+
}
234+
}
235+
236+
/* One of the buffers needs a wrap (or end of data), so check for wrap */
237+
x0 += samples_without_wrap;
238+
y0 += samples_without_wrap;
239+
x0 = (x0 >= x_end) ? x0 - x_size : x0;
240+
y0 = (y0 >= y_end) ? y0 - y_size : y0;
241+
242+
/* Update processed samples count for next loop iteration. */
243+
samples -= samples_without_wrap;
244+
}
245+
246+
/* Update the source and sink for bytes consumed and produced. Return success. */
247+
source_release_data(source, bytes);
248+
sink_commit_buffer(sink, bytes);
249+
250+
sound_dose_calculate_mel(mod, frames);
251+
return 0;
252+
}
253+
#endif /* CONFIG_FORMAT_S32LE || CONFIG_FORMAT_S24LE */
254+
255+
/* This struct array defines the used processing functions for
256+
* the PCM formats
257+
*/
258+
const struct sound_dose_proc_fnmap sound_dose_proc_fnmap[] = {
259+
#if CONFIG_FORMAT_S16LE
260+
{ SOF_IPC_FRAME_S16_LE, sound_dose_s16},
261+
#endif
262+
#if CONFIG_FORMAT_S24LE
263+
{ SOF_IPC_FRAME_S24_4LE, sound_dose_s32},
264+
#endif
265+
#if CONFIG_FORMAT_S32LE
266+
{ SOF_IPC_FRAME_S32_LE, sound_dose_s32},
267+
#endif
268+
};
269+
270+
/**
271+
* sound_dose_find_proc_func() - Find suitable processing function.
272+
* @src_fmt: Enum value for PCM format.
273+
*
274+
* This function finds the suitable processing function to use for
275+
* the used PCM format. If not found, return NULL.
276+
*
277+
* Return: Pointer to processing function for the requested PCM format.
278+
*/
279+
sound_dose_func sound_dose_find_proc_func(enum sof_ipc_frame src_fmt)
280+
{
281+
int i;
282+
283+
/* Find suitable processing function from map */
284+
for (i = 0; i < ARRAY_SIZE(sound_dose_proc_fnmap); i++)
285+
if (src_fmt == sound_dose_proc_fnmap[i].frame_fmt)
286+
return sound_dose_proc_fnmap[i].sound_dose_proc_func;
287+
288+
return NULL;
289+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation.
4+
5+
#include <sof/audio/module_adapter/module/generic.h>
6+
#include <sof/audio/component.h>
7+
#include "sound_dose.h"
8+
9+
LOG_MODULE_DECLARE(sound_dose, CONFIG_SOF_LOG_LEVEL);
10+
11+
/* This function handles the real-time controls. The ALSA controls have the
12+
* param_id set to indicate the control type. The control ID, from topology,
13+
* is used to separate the controls instances of same type. In control payload
14+
* the num_elems defines to how many channels the control is applied to.
15+
*/
16+
__cold int sound_dose_set_config(struct processing_module *mod, uint32_t param_id,
17+
enum module_cfg_fragment_position pos,
18+
uint32_t data_offset_size, const uint8_t *fragment,
19+
size_t fragment_size, uint8_t *response,
20+
size_t response_size)
21+
{
22+
assert_can_be_cold();
23+
return 0;
24+
}
25+
26+
__cold int sound_dose_get_config(struct processing_module *mod,
27+
uint32_t config_id, uint32_t *data_offset_size,
28+
uint8_t *fragment, size_t fragment_size)
29+
{
30+
assert_can_be_cold();
31+
return 0;
32+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation.
4+
5+
#include <sof/audio/module_adapter/module/generic.h>
6+
#include <sof/audio/component.h>
7+
#include "sound_dose.h"
8+
9+
LOG_MODULE_DECLARE(sound_dose, CONFIG_SOF_LOG_LEVEL);
10+
/* IPC4 controls handler */
11+
__cold int sound_dose_set_config(struct processing_module *mod,
12+
uint32_t param_id,
13+
enum module_cfg_fragment_position pos,
14+
uint32_t data_offset_size,
15+
const uint8_t *fragment,
16+
size_t fragment_size,
17+
uint8_t *response,
18+
size_t response_size)
19+
{
20+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
21+
struct sound_dose_setup_config *new_setup;
22+
struct sound_dose_volume_config *new_volume;
23+
struct sound_dose_gain_config *new_gain;
24+
struct comp_dev *dev = mod->dev;
25+
uint8_t *dest;
26+
size_t dest_size;
27+
28+
assert_can_be_cold();
29+
comp_info(dev, "sound_dose_set_config()");
30+
31+
switch (param_id) {
32+
case SOF_SOUND_DOSE_SETUP_PARAM_ID:
33+
new_setup = (struct sound_dose_setup_config *)fragment;
34+
if (new_setup->sens_dbfs_dbspl < SOF_SOUND_DOSE_SENS_MIN_DB ||
35+
new_setup->sens_dbfs_dbspl > SOF_SOUND_DOSE_SENS_MAX_DB) {
36+
comp_err(dev, "Illegal sensitivity = %d", new_setup->sens_dbfs_dbspl);
37+
return -EINVAL;
38+
}
39+
dest_size = sizeof(struct sound_dose_setup_config);
40+
dest = (uint8_t *)&cd->setup;
41+
break;
42+
case SOF_SOUND_DOSE_VOLUME_PARAM_ID:
43+
new_volume = (struct sound_dose_volume_config *)fragment;
44+
if (new_volume->volume_offset < SOF_SOUND_DOSE_VOLUME_MIN_DB ||
45+
new_volume->volume_offset > SOF_SOUND_DOSE_VOLUME_MIN_DB) {
46+
comp_err(dev, "Illegal volume = %d", new_volume->volume_offset);
47+
return -EINVAL;
48+
}
49+
dest_size = sizeof(struct sound_dose_volume_config);
50+
dest = (uint8_t *)&cd->vol;
51+
break;
52+
case SOF_SOUND_DOSE_GAIN_PARAM_ID:
53+
new_gain = (struct sound_dose_gain_config *)fragment;
54+
if (new_gain->gain < SOF_SOUND_DOSE_GAIN_MIN_DB ||
55+
new_gain->gain > SOF_SOUND_DOSE_GAIN_MIN_DB) {
56+
comp_err(dev, "Illegal gain = %d", new_gain->gain);
57+
return -EINVAL;
58+
}
59+
dest_size = sizeof(struct sound_dose_gain_config);
60+
dest = (uint8_t *)&cd->att;
61+
break;
62+
default:
63+
comp_err(dev, "Illegal param_id = %d", param_id);
64+
return -EINVAL;
65+
}
66+
67+
if (fragment_size != dest_size) {
68+
comp_err(dev, "Illegal fragment_size = %d", fragment_size);
69+
return -EINVAL;
70+
}
71+
memcpy_s(dest, dest_size, fragment, fragment_size);
72+
return 0;
73+
}
74+
75+
/* Not used in IPC4 systems, if IPC4 only component, omit .get_configuration set */
76+
__cold int sound_dose_get_config(struct processing_module *mod,
77+
uint32_t config_id, uint32_t *data_offset_size,
78+
uint8_t *fragment, size_t fragment_size)
79+
{
80+
assert_can_be_cold();
81+
return 0;
82+
}

‎src/audio/sound_dose/sound_dose.c

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2025 Intel Corporation.
4+
5+
#include <sof/audio/module_adapter/module/generic.h>
6+
#include <sof/audio/sink_source_utils.h>
7+
#include <sof/audio/sink_api.h>
8+
#include <sof/audio/source_api.h>
9+
#include <sof/math/iir_df1.h>
10+
#include <kernel/abi.h>
11+
#include <kernel/header.h>
12+
#include <rtos/init.h>
13+
#include <user/sound_dose.h>
14+
#include <user/eq.h>
15+
16+
#include "sound_dose.h"
17+
#include "sound_dose_iir_48k.h"
18+
19+
/* UUID identifies the components. Use e.g. command uuidgen from package
20+
* uuid-runtime, add it to uuid-registry.txt in SOF top level.
21+
*/
22+
SOF_DEFINE_REG_UUID(sound_dose);
23+
24+
/* Creates logging data for the component */
25+
LOG_MODULE_REGISTER(sound_dose, CONFIG_SOF_LOG_LEVEL);
26+
27+
/* Creates the compont trace. Traces show in trace console the component
28+
* info, warning, and error messages.
29+
*/
30+
DECLARE_TR_CTX(sound_dose_tr, SOF_UUID(sound_dose_uuid), LOG_LEVEL_INFO);
31+
32+
void sound_dose_report_mel(const struct processing_module *mod)
33+
{
34+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
35+
struct comp_dev *dev = mod->dev;
36+
37+
cd->dose->current_sens_dbfs_dbspl = cd->setup.sens_dbfs_dbspl;
38+
cd->dose->current_volume_offset = cd->vol.volume_offset;
39+
cd->dose->current_gain = cd->gain;
40+
cd->dose->dbfs_value = (int32_t)(((int64_t)cd->level_dbfs * 100) >> 16);
41+
cd->dose->mel_value = cd->dose->dbfs_value +
42+
cd->setup.sens_dbfs_dbspl +
43+
cd->vol.volume_offset;
44+
cd->feature->stream_time_ms = (uint64_t)cd->total_frames_count; // TODO convert to time
45+
46+
comp_info(dev, "dBFS %d MEL %d", cd->dose->dbfs_value, cd->dose->mel_value);
47+
}
48+
49+
int sound_dose_filters_init(struct processing_module *mod)
50+
{
51+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
52+
struct comp_dev *dev = mod->dev;
53+
struct sof_abi_hdr *blob;
54+
struct sof_eq_iir_config *iir_config;
55+
struct sof_eq_iir_header *iir_coef;
56+
size_t iir_size, alloc_size;
57+
int32_t *data;
58+
int i;
59+
60+
/* Initialize FIR */
61+
switch (cd->rate) {
62+
case 48000:
63+
blob = (struct sof_abi_hdr *)sound_dose_iir_48k;
64+
iir_config = (struct sof_eq_iir_config *)blob->data;
65+
cd->log_offset_for_mean = SOUND_DOSE_LOG2_INV_48K_Q16;
66+
break;
67+
default:
68+
/* TODO: Add 44100 rate handling and integer ratio decimation code from
69+
* e.g. 96 kHz to 48 kHz. The A-weight is not defined above 20 kHz, so
70+
* high frequency energy is not needed. Also it will help keep the
71+
* module load reasonable.
72+
*/
73+
comp_err(dev, "error: unsupported sample rate %d", cd->rate);
74+
return -EINVAL;
75+
}
76+
77+
/* Apply the first responses in the blobs */
78+
iir_coef = (struct sof_eq_iir_header *)&iir_config->data[iir_config->channels_in_config];
79+
iir_size = iir_delay_size_df1(iir_coef);
80+
alloc_size = cd->channels * iir_size;
81+
cd->delay_lines = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, alloc_size);
82+
if (!cd->delay_lines) {
83+
comp_err(dev, "Failed to allocate memory for weighting filters.");
84+
return -ENOMEM;
85+
}
86+
87+
data = cd->delay_lines;
88+
for (i = 0; i < cd->channels; i++) {
89+
iir_init_coef_df1(&cd->iir[i], iir_coef);
90+
iir_init_delay_df1(&cd->iir[i], &data);
91+
cd->energy[i] = 0;
92+
}
93+
94+
cd->frames_count = 0;
95+
cd->report_count = cd->rate; /* report every 1s, for 48k frames */
96+
return 0;
97+
}
98+
99+
void sound_dose_filters_free(struct sound_dose_comp_data *cd)
100+
{
101+
rfree(cd->delay_lines);
102+
}
103+
104+
__cold static void sound_dose_setup_init(struct sound_dose_comp_data *cd)
105+
{
106+
cd->setup.sens_dbfs_dbspl = 10000; /* 0 dbFS is 100 dB */
107+
cd->vol.volume_offset = 0; /* Assume max vol */
108+
cd->att.gain = INT32_MAX; /* No attenuation */
109+
cd->gain = cd->att.gain;
110+
}
111+
112+
static int sound_dose_audio_feature_init(struct processing_module *mod)
113+
{
114+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
115+
size_t alloc_size = sizeof(struct sof_abi_hdr) +
116+
sizeof(struct sof_audio_feature) +
117+
sizeof(struct sof_sound_dose);
118+
119+
cd->abi = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, alloc_size);
120+
if (!cd->abi) {
121+
comp_err(mod->dev, "Failed to allocate audio feature data.");
122+
return -ENOMEM;
123+
}
124+
125+
cd->abi->magic = SOF_IPC4_ABI_MAGIC;
126+
cd->abi->abi = SOF_ABI_VERSION;
127+
cd->abi->type = SOF_AUDIO_FEATURE_PARAM_ID;
128+
cd->abi->size = sizeof(struct sof_audio_feature) + sizeof(struct sof_sound_dose);
129+
cd->feature = (struct sof_audio_feature *)cd->abi->data;
130+
cd->feature->data_size = sizeof(struct sof_sound_dose);
131+
cd->feature->type = SOF_AUDIO_FETURE_SOUND_DOSE_MEL;
132+
cd->dose = (struct sof_sound_dose *)cd->feature->data;
133+
return 0;
134+
}
135+
136+
/**
137+
* sound_dose_init() - Initialize the template component.
138+
* @mod: Pointer to module data.
139+
*
140+
* This function is called when the instance is created. The
141+
* macro __cold informs that the code that is non-critical
142+
* is loaded to slower but large DRAM.
143+
*
144+
* Return: Zero if success, otherwise error code.
145+
*/
146+
__cold static int sound_dose_init(struct processing_module *mod)
147+
{
148+
struct module_data *md = &mod->priv;
149+
struct comp_dev *dev = mod->dev;
150+
struct sound_dose_comp_data *cd;
151+
int ret;
152+
153+
comp_info(dev, "sound_dose_init()");
154+
155+
cd = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(*cd));
156+
if (!cd)
157+
return -ENOMEM;
158+
159+
md->private = cd;
160+
161+
sound_dose_setup_init(cd);
162+
ret = sound_dose_audio_feature_init(mod);
163+
return ret;
164+
}
165+
166+
/**
167+
* sound_dose_process() - The audio data processing function.
168+
* @mod: Pointer to module data.
169+
* @sources: Pointer to audio samples data sources array.
170+
* @num_of_sources: Number of sources in the array.
171+
* @sinks: Pointer to audio samples data sinks array.
172+
* @num_of_sinks: Number of sinks in the array.
173+
*
174+
* This is the processing function that is called for scheduled
175+
* pipelines. The processing is controlled by the enable switch.
176+
*
177+
* Return: Zero if success, otherwise error code.
178+
*/
179+
static int sound_dose_process(struct processing_module *mod,
180+
struct sof_source **sources,
181+
int num_of_sources,
182+
struct sof_sink **sinks,
183+
int num_of_sinks)
184+
{
185+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
186+
struct comp_dev *dev = mod->dev;
187+
struct sof_source *source = sources[0]; /* One input in this example */
188+
struct sof_sink *sink = sinks[0]; /* One output in this example */
189+
int frames = source_get_data_frames_available(source);
190+
int sink_frames = sink_get_free_frames(sink);
191+
192+
comp_dbg(dev, "sound_dose_process()");
193+
194+
frames = MIN(frames, sink_frames);
195+
cd->total_frames_count += frames;
196+
return cd->sound_dose_func(mod, source, sink, frames);
197+
}
198+
199+
/**
200+
* sound_dose_prepare() - Prepare the component for processing.
201+
* @mod: Pointer to module data.
202+
* @sources: Pointer to audio samples data sources array.
203+
* @num_of_sources: Number of sources in the array.
204+
* @sinks: Pointer to audio samples data sinks array.
205+
* @num_of_sinks: Number of sinks in the array.
206+
*
207+
* Function prepare is called just before the pipeline is started. In
208+
* this case the audio format parameters are for better code performance
209+
* saved to component data to avoid to find out them in process. The
210+
* processing function pointer is set to process the current audio format.
211+
*
212+
* Return: Value zero if success, otherwise error code.
213+
*/
214+
static int sound_dose_prepare(struct processing_module *mod,
215+
struct sof_source **sources, int num_of_sources,
216+
struct sof_sink **sinks, int num_of_sinks)
217+
{
218+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
219+
struct comp_dev *dev = mod->dev;
220+
enum sof_ipc_frame source_format;
221+
int ret;
222+
223+
comp_dbg(dev, "sound_dose_prepare()");
224+
225+
/* The processing example in this component supports one input and one
226+
* output. Generally there can be more.
227+
*/
228+
if (num_of_sources != 1 || num_of_sinks != 1)
229+
return -EINVAL;
230+
231+
/* get source data format */
232+
cd->frame_bytes = source_get_frame_bytes(sources[0]);
233+
cd->channels = source_get_channels(sources[0]);
234+
source_format = source_get_frm_fmt(sources[0]);
235+
cd->rate = source_get_rate(sources[0]);
236+
237+
cd->sound_dose_func = sound_dose_find_proc_func(source_format);
238+
if (!cd->sound_dose_func) {
239+
comp_err(dev, "No processing function found for format %d.",
240+
source_format);
241+
return -EINVAL;
242+
}
243+
244+
ret = sound_dose_filters_init(mod);
245+
if (ret)
246+
return ret;
247+
248+
return 0;
249+
}
250+
251+
/**
252+
* sound_dose_reset() - Reset the component.
253+
* @mod: Pointer to module data.
254+
*
255+
* The component reset is called when pipeline is stopped. The reset
256+
* should return the component to same state as init.
257+
*
258+
* Return: Value zero, always success.
259+
*/
260+
static int sound_dose_reset(struct processing_module *mod)
261+
{
262+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
263+
264+
comp_dbg(mod->dev, "sound_dose_reset()");
265+
memset(cd, 0, sizeof(*cd));
266+
return 0;
267+
}
268+
269+
/**
270+
* sound_dose_free() - Free dynamic allocations.
271+
* @mod: Pointer to module data.
272+
*
273+
* Component free is called when the pipelines are deleted. All
274+
* dynamic allocations need to be freed here. The macro __cold
275+
* instructs the build to locate this performance wise non-critical
276+
* function to large and slower DRAM.
277+
*
278+
* Return: Value zero, always success.
279+
*/
280+
__cold static int sound_dose_free(struct processing_module *mod)
281+
{
282+
struct sound_dose_comp_data *cd = module_get_private_data(mod);
283+
284+
assert_can_be_cold();
285+
286+
comp_dbg(mod->dev, "sound_dose_free()");
287+
sound_dose_filters_free(cd);
288+
rfree(cd);
289+
return 0;
290+
}
291+
292+
/* This defines the module operations */
293+
static const struct module_interface sound_dose_interface = {
294+
.init = sound_dose_init,
295+
.prepare = sound_dose_prepare,
296+
.process = sound_dose_process,
297+
.set_configuration = sound_dose_set_config,
298+
.get_configuration = sound_dose_get_config,
299+
.reset = sound_dose_reset,
300+
.free = sound_dose_free
301+
};
302+
303+
/* This controls build of the module. If COMP_MODULE is selected in kconfig
304+
* this is build as dynamically loadable module.
305+
*/
306+
#if CONFIG_COMP_TEMPLATE_COMP_MODULE
307+
308+
#include <module/module/api_ver.h>
309+
#include <module/module/llext.h>
310+
#include <rimage/sof/user/manifest.h>
311+
312+
SOF_LLEXT_MOD_ENTRY(sound_dose, &sound_dose_interface);
313+
314+
static const struct sof_man_module_manifest mod_manifest __section(".module") __used =
315+
SOF_LLEXT_MODULE_MANIFEST("TEMPLATE", sound_dose_llext_entry, 1,
316+
SOF_REG_UUID(sound_dose), 40);
317+
318+
SOF_LLEXT_BUILDINFO;
319+
320+
#else
321+
322+
DECLARE_MODULE_ADAPTER(sound_dose_interface, sound_dose_uuid, sound_dose_tr);
323+
SOF_MODULE_INIT(sound_dose, sys_comp_module_sound_dose_interface_init);
324+
325+
#endif

‎src/audio/sound_dose/sound_dose.h

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* SPDX-License-Identifier: BSD-3-Clause
2+
*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*
5+
*/
6+
#ifndef __SOF_AUDIO_SOUND_DOSE_H__
7+
#define __SOF_AUDIO_SOUND_DOSE_H__
8+
9+
#include <sof/audio/module_adapter/module/generic.h>
10+
#include <sof/math/iir_df1.h>
11+
#include <user/sound_dose.h>
12+
#include <stdint.h>
13+
14+
#define SOUND_DOSE_GAIN_UP_Q30 1079940603 /* int32(10^(+0.05/20)*2^30) */
15+
#define SOUND_DOSE_GAIN_DOWN_Q30 1067578625 /* int32(10^(-0.05/20)*2^30) */
16+
#define SOUND_DOSE_LOG2_INV_48K_Q16 -1019134 /* int32(log2(1 / 48e3) * 2^16) */
17+
#define SOUND_DOSE_TEN_OVER_LOG2_10_Q29 1616142483 /* int32(10 / log2(10) * 2^29) */
18+
#define SOUND_DOSE_WEIGHT_FILTERS_OFFS_Q16 196608 /* int32(3 * 2^16) */
19+
#define SOUND_DOSE_DFBS_OFFS_Q16 197263 /* int32(3.01 * 2^16) */
20+
#define SOUND_DOSE_MEL_CHANNELS_SUM_FIX -98304 /* int32(-1.5 * 2^16) */
21+
#define SOUND_DOSE_ENERGY_SHIFT 19 /* Scale shift for 1s energy */
22+
#define SOUND_DOSE_LOG_FIXED_OFFSET (65536 * (SOUND_DOSE_ENERGY_SHIFT - 30))
23+
24+
/**
25+
* struct sound_dose_func - Function call pointer for process function
26+
* @mod: Pointer to module data.
27+
* @source: Source for PCM samples data.
28+
* @sink: Sink for PCM samples data.
29+
* @frames: Number of audio data frames to process.
30+
*/
31+
typedef int (*sound_dose_func)(const struct processing_module *mod,
32+
struct sof_source *source,
33+
struct sof_sink *sink,
34+
uint32_t frames);
35+
36+
/* Template_Comp component private data */
37+
38+
/**
39+
* struct sound_dose_comp_data
40+
* @sound_dose_func: Pointer to used processing function.
41+
* @channels_order[]: Vector with desired sink channels order.
42+
* @source_format: Source samples format.
43+
* @frame_bytes: Number of bytes in an audio frame.
44+
* @channels: Channels count.
45+
* @enable: Control processing on/off, on - reorder channels
46+
*/
47+
struct sound_dose_comp_data {
48+
struct iir_state_df1 iir[PLATFORM_MAX_CHANNELS];
49+
struct sound_dose_setup_config setup;
50+
struct sound_dose_volume_config vol;
51+
struct sound_dose_gain_config att;
52+
struct sof_abi_hdr *abi;
53+
struct sof_audio_feature *feature;
54+
struct sof_sound_dose *dose;
55+
sound_dose_func sound_dose_func;
56+
int64_t energy[PLATFORM_MAX_CHANNELS];
57+
int64_t total_frames_count;
58+
int32_t log_offset_for_mean;
59+
int32_t *delay_lines;
60+
int32_t level_dbfs;
61+
int32_t new_gain;
62+
int32_t gain;
63+
int report_count;
64+
int frames_count;
65+
int frame_bytes;
66+
int channels;
67+
int rate;
68+
};
69+
70+
/**
71+
* struct sound_dose_proc_fnmap - processing functions for frame formats
72+
* @frame_fmt: Current frame format
73+
* @sound_dose_proc_func: Function pointer for the suitable processing function
74+
*/
75+
struct sound_dose_proc_fnmap {
76+
enum sof_ipc_frame frame_fmt;
77+
sound_dose_func sound_dose_proc_func;
78+
};
79+
80+
/**
81+
* sound_dose_find_proc_func() - Find suitable processing function.
82+
* @src_fmt: Enum value for PCM format.
83+
*
84+
* This function finds the suitable processing function to use for
85+
* the used PCM format. If not found, return NULL.
86+
*
87+
* Return: Pointer to processing function for the requested PCM format.
88+
*/
89+
sound_dose_func sound_dose_find_proc_func(enum sof_ipc_frame src_fmt);
90+
91+
/**
92+
* sound_dose_set_config() - Handle controls set
93+
* @mod: Pointer to module data.
94+
* @param_id: Id to know control type, used to know ALSA control type.
95+
* @pos: Position of the fragment in the large message.
96+
* @data_offset_size: Size of the whole configuration if it is the first or only
97+
* fragment. Otherwise it is offset of the fragment.
98+
* @fragment: Message payload data.
99+
* @fragment_size: Size of this fragment.
100+
* @response_size: Size of response.
101+
*
102+
* This function handles the real-time controls. The ALSA controls have the
103+
* param_id set to indicate the control type. The control ID, from topology,
104+
* is used to separate the controls instances of same type. In control payload
105+
* the num_elems defines to how many channels the control is applied to.
106+
*
107+
* Return: Zero if success, otherwise error code.
108+
*/
109+
int sound_dose_set_config(struct processing_module *mod,
110+
uint32_t param_id,
111+
enum module_cfg_fragment_position pos,
112+
uint32_t data_offset_size,
113+
const uint8_t *fragment,
114+
size_t fragment_size,
115+
uint8_t *response,
116+
size_t response_size);
117+
118+
/**
119+
* sound_dose_set_config() - Handle controls get
120+
* @mod: Pointer to module data.
121+
* @config_id: Configuration ID.
122+
* @data_offset_size: Size of the whole configuration if it is the first or only
123+
* fragment. Otherwise it is offset of the fragment.
124+
* @fragment: Message payload data.
125+
* @fragment_size: Size of this fragment.
126+
*
127+
* This function is used for controls get.
128+
*
129+
* Return: Zero if success, otherwise error code.
130+
*/
131+
int sound_dose_get_config(struct processing_module *mod,
132+
uint32_t config_id, uint32_t *data_offset_size,
133+
uint8_t *fragment, size_t fragment_size);
134+
135+
void sound_dose_filters_free(struct sound_dose_comp_data *cd);
136+
int sound_dose_filters_init(struct processing_module *mod);
137+
void sound_dose_report_mel(const struct processing_module *mod);
138+
139+
#endif // __SOF_AUDIO_SOUND_DOSE_H__

‎src/audio/sound_dose/sound_dose.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef LOAD_TYPE
2+
#define LOAD_TYPE "0"
3+
#endif
4+
5+
REM # Sound Dose component module config
6+
[[module.entry]]
7+
name = "SNDDOSE"
8+
uuid = UUIDREG_STR_SOUND_DOSE
9+
affinity_mask = "0x1"
10+
instance_count = "40"
11+
domain_types = "0"
12+
load_type = LOAD_TYPE
13+
module_type = "9"
14+
auto_start = "0"
15+
sched_caps = [1, 0x00008000]
16+
REM # pin = [dir, type, sample rate, size, container, channel-cfg]
17+
pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff]
18+
REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS]
19+
mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0]
20+
21+
index = __COUNTER__
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* SPDX-License-Identifier: BSD-3-Clause
2+
*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*/
5+
6+
#include <stdint.h>
7+
8+
static const uint32_t sound_dose_iir_48k[51] = {
9+
0x34464f53, 0x00000000, 0x000000ac, 0x0301d001,
10+
0x00000000, 0x00000000, 0x00000000, 0x00000000,
11+
0x000000ac, 0x00000002, 0x00000001, 0x00000000,
12+
0x00000000, 0x00000000, 0x00000000, 0x00000000,
13+
0x00000000, 0x00000004, 0x00000004, 0x00000000,
14+
0x00000000, 0x00000000, 0x00000000, 0xc0447c54,
15+
0x7fbb7275, 0x200247f9, 0xbffb700e, 0x200247f9,
16+
0x00000000, 0x00004000, 0xc56175ce, 0x7a8e6600,
17+
0x1eb83f18, 0xc28f81d0, 0x1eb83f18, 0x00000000,
18+
0x00004000, 0xc33b9ba4, 0x7caef0cf, 0x1e6ee6bd,
19+
0xc1b48ddf, 0x1fe4bfc4, 0x00000000, 0x00004000,
20+
0xff729f84, 0xf3612432, 0x01acb380, 0x0cd1120d,
21+
0x182fcd24, 0xfffffffd, 0x000067e0
22+
};

‎src/include/sof/audio/component.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ void sys_comp_module_mux_interface_init(void);
899899
void sys_comp_module_asrc_interface_init(void);
900900
void sys_comp_module_rtnr_interface_init(void);
901901
void sys_comp_module_selector_interface_init(void);
902+
void sys_comp_module_sound_dose_interface_init(void);
902903
void sys_comp_module_src_interface_init(void);
903904
void sys_comp_module_src_lite_interface_init(void);
904905
void sys_comp_module_tdfb_interface_init(void);

‎src/include/user/audio_feature.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* SPDX-License-Identifier: BSD-3-Clause
2+
*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*
5+
*/
6+
7+
#ifndef __USER_AUDIO_FEATURE_H__
8+
#define __USER_AUDIO_FEATURE_H__
9+
10+
#define SOF_AUDIO_FEATURE_PARAM_ID 0 /* Use in data ABI header */
11+
12+
enum sof_audio_feature_type {
13+
SOF_AUDIO_FEATURE_MFCC,
14+
SOF_AUDIO_FETURE_SOUND_DOSE_MEL,
15+
};
16+
17+
struct sof_audio_feature {
18+
uint64_t stream_time_ms;
19+
enum sof_audio_feature_type type;
20+
size_t data_size;
21+
uint32_t reserved[4]; /**< reserved for future use */
22+
int32_t data[];
23+
} __attribute__((packed));
24+
25+
#endif /* __USER_AUDIO_FEATURE_H__ */

‎src/include/user/sound_dose.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/* SPDX-License-Identifier: BSD-3-Clause
2+
*
3+
* Copyright(c) 2025 Intel Corporation.
4+
*
5+
*/
6+
7+
#include <kernel/abi.h>
8+
#include <kernel/header.h>
9+
#include <user/audio_feature.h>
10+
11+
#ifndef __USER_SOUND_DOSE_H__
12+
#define __USER_SOUND_DOSE_H__
13+
14+
#define SOF_SOUND_DOSE_SETUP_PARAM_ID 0
15+
#define SOF_SOUND_DOSE_VOLUME_PARAM_ID 1
16+
#define SOF_SOUND_DOSE_GAIN_PARAM_ID 2
17+
18+
#define SOF_SOUND_DOSE_SENS_MIN_DB (-10 * 100) /* -10 to +130 dB */
19+
#define SOF_SOUND_DOSE_SENS_MAX_DB (130 * 100)
20+
#define SOF_SOUND_DOSE_VOLUME_MIN_DB (-100 * 100) /* -100 to +40 dB */
21+
#define SOF_SOUND_DOSE_VOLUME_MAX_DB (40 * 100)
22+
#define SOF_SOUND_DOSE_GAIN_MIN_DB (-100 * 100) /* -100 to 0 dB */
23+
#define SOF_SOUND_DOSE_GAIN_MAX_DB (0 * 100)
24+
25+
struct sof_sound_dose {
26+
int16_t mel_value; /* Decibels x100, e.g. 85 dB is 8500 */
27+
int16_t dbfs_value; /* Decibels x100 */
28+
int16_t current_sens_dbfs_dbspl; /* Decibels x100 */
29+
int16_t current_volume_offset; /* Decibels x100 */
30+
int32_t current_gain; /* Q1.31 */
31+
uint32_t reserved[4]; /**< reserved for future use */
32+
} __attribute__((packed));
33+
34+
struct sound_dose_setup_config {
35+
int16_t sens_dbfs_dbspl; /* Desibels x100 */
36+
int16_t reserved;
37+
} __attribute__((packed));
38+
39+
struct sound_dose_volume_config {
40+
int16_t volume_offset; /* Desibels x100 */
41+
int16_t reserved;
42+
} __attribute__((packed));
43+
44+
struct sound_dose_gain_config {
45+
int16_t gain; /* Q1.31 */
46+
int16_t reserved;
47+
} __attribute__((packed));
48+
49+
#endif /* __USER_SOUND_DOSE_H__ */

‎tools/rimage/config/tgl-h.toml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ name = "ADSPFW"
6060
load_offset = "0x30000"
6161

6262
[module]
63-
count = 27
63+
count = 28
6464
[[module.entry]]
6565
name = "BRNGUP"
6666
uuid = "61EB0CB9-34D8-4F59-A21D-04C54C21D3A4"
@@ -606,3 +606,20 @@ count = 27
606606
pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff]
607607
# mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS]
608608
mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0]
609+
610+
# Sound Dose component module config
611+
[[module.entry]]
612+
name = "SNDDOSE"
613+
uuid = "A43F9D7C-EA75-44D5-942D-967991A33809"
614+
affinity_mask = "0x1"
615+
instance_count = "40"
616+
domain_types = "0"
617+
load_type = "0"
618+
module_type = "9"
619+
auto_start = "0"
620+
sched_caps = [1, 0x00008000]
621+
# pin = [dir, type, sample rate, size, container, channel-cfg]
622+
pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff]
623+
# mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS]
624+
mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0]
625+

‎tools/rimage/config/tgl.toml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ name = "ADSPFW"
6060
load_offset = "0x30000"
6161

6262
[module]
63-
count = 27
63+
count = 28
6464
[[module.entry]]
6565
name = "BRNGUP"
6666
uuid = "61EB0CB9-34D8-4F59-A21D-04C54C21D3A4"
@@ -606,3 +606,19 @@ count = 27
606606
pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff]
607607
# mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS]
608608
mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0]
609+
610+
# Sound Dose component module config
611+
[[module.entry]]
612+
name = "SNDDOSE"
613+
uuid = "A43F9D7C-EA75-44D5-942D-967991A33809"
614+
affinity_mask = "0x1"
615+
instance_count = "40"
616+
domain_types = "0"
617+
load_type = "0"
618+
module_type = "9"
619+
auto_start = "0"
620+
sched_caps = [1, 0x00008000]
621+
# pin = [dir, type, sample rate, size, container, channel-cfg]
622+
pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff, 1, 0, 0xfeef, 0xf, 0xf, 0x1ff]
623+
# mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS]
624+
mod_cfg = [0, 0, 0, 0, 4096, 1000000, 128, 128, 0, 0, 0]

‎tools/testbench/utils_ipc4.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ int tb_setup(struct sof *sof, struct testbench_prm *tp)
6060
sys_comp_module_mux_interface_init();
6161
sys_comp_module_rtnr_interface_init();
6262
sys_comp_module_selector_interface_init();
63+
sys_comp_module_sound_dose_interface_init();
6364
sys_comp_module_src_interface_init();
6465
sys_comp_module_asrc_interface_init();
6566
sys_comp_module_tdfb_interface_init();

‎uuid-registry.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ bcf54c06-5702-4a60-ac4abb509123c649 sgen_mt8196
146146
dabe8814-47e8-11ed-a58bb309974fecce shmread
147147
e2b6031c-47e8-11ed-07a97f801b6efa6c shmwrite
148148
167a961e-8ae4-11ea-89f1000c29ce1635 smart_amp_test
149+
a43f9d7c-ea75-44d5-942d967991a33809 sound_dose
149150
4abd71ba-8619-458a-b33f160fc0cf809b spdai
150151
a417b6fb-459d-4cf9-be65d38dc9057b80 spi_completion
151152
9d346d98-203d-4791-baee1770a03d4a71 spinlock

0 commit comments

Comments
 (0)
Please sign in to comment.