Skip to content

Commit c7bb7cb

Browse files
committed
Audio: Level multiplier: Add new component
Level multiplier is used to set a fixed gain to stream, e.g. add +20 dB sensitivity to voice capture compared to media audio capture from the same microphone. The gain is defined by boot time set bytes control. Usually it would be set by topology. Runtime set by ALSA UCM2 is also possible usage. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent 172589b commit c7bb7cb

22 files changed

Lines changed: 839 additions & 2 deletions

app/boards/intel_adsp_ace15_mtpm.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ CONFIG_COMP_MFCC=y
1818
CONFIG_COMP_MULTIBAND_DRC=y
1919
CONFIG_COMP_UP_DOWN_MIXER=y
2020
CONFIG_COMP_VOLUME_WINDOWS_FADE=y
21+
CONFIG_COMP_LEVEL_MULTIPLIER=y
2122
CONFIG_FORMAT_CONVERT_HIFI3=n
2223
CONFIG_PCM_CONVERTER_FORMAT_S16_C16_AND_S16_C32=y
2324
CONFIG_PCM_CONVERTER_FORMAT_S16_C32_AND_S32_C32=y

src/arch/host/configs/library_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=y
88
CONFIG_COMP_GOOGLE_CTC_AUDIO_PROCESSING=y
99
CONFIG_COMP_IIR=y
1010
CONFIG_COMP_IGO_NR=y
11+
CONFIG_COMP_LEVEL_MULTIPLIER=y
1112
CONFIG_COMP_MFCC=y
1213
CONFIG_COMP_MODULE_ADAPTER=y
1314
CONFIG_COMP_MULTIBAND_DRC=y

src/audio/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD)
123123
if(CONFIG_COMP_TEMPLATE_COMP)
124124
add_subdirectory(template_comp)
125125
endif()
126+
if(CONFIG_COMP_LEVEL_MULTIPLIER)
127+
add_subdirectory(level_multiplier)
128+
endif()
126129
endif()
127130

128131
### Common files (also used in shared library build)

src/audio/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ rsource "codec/Kconfig"
184184

185185
rsource "template_comp/Kconfig"
186186

187+
rsource "level_multiplier/Kconfig"
188+
187189
endmenu # "Audio components"
188190

189191
menu "Data formats"
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_LEVEL_MULTIPLIER STREQUAL "m")
4+
add_subdirectory(llext ${PROJECT_BINARY_DIR}/level_multiplier_llext)
5+
add_dependencies(app level_multiplier)
6+
else()
7+
add_local_sources(sof level_multiplier.c)
8+
add_local_sources(sof level_multiplier-generic.c)
9+
10+
if(CONFIG_IPC_MAJOR_3)
11+
add_local_sources(sof level_multiplier-ipc3.c)
12+
elseif(CONFIG_IPC_MAJOR_4)
13+
add_local_sources(sof level_multiplier-ipc4.c)
14+
endif()
15+
endif()

src/audio/level_multiplier/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
3+
config COMP_LEVEL_MULTIPLIER
4+
tristate "Level multiplier component"
5+
default y
6+
help
7+
Select for Level multiplier component. This component
8+
applies a fixed gain to audio. The amount of gain
9+
is configured in the bytes control that is typically
10+
set in boot time from topology. It can e.g. increase
11+
capture sensitivity of voice applications by 20 dB
12+
compared to media capture.
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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 <stdint.h>
11+
#include "level_multiplier.h"
12+
13+
#define LEVEL_MULTIPLIER_S16_SHIFT Q_SHIFT_BITS_32(15, LEVEL_MULTIPLIER_QXY_Y, 15)
14+
#define LEVEL_MULTIPLIER_S24_SHIFT Q_SHIFT_BITS_64(23, LEVEL_MULTIPLIER_QXY_Y, 23)
15+
#define LEVEL_MULTIPLIER_S32_SHIFT Q_SHIFT_BITS_64(31, LEVEL_MULTIPLIER_QXY_Y, 31)
16+
17+
#if CONFIG_FORMAT_S16LE
18+
/**
19+
* level_multiplier_s16() - Process S16_LE format.
20+
* @mod: Pointer to module data.
21+
* @source: Source for PCM samples data.
22+
* @sink: Sink for PCM samples data.
23+
* @frames: Number of audio data frames to process.
24+
*
25+
* This is the processing function for 16-bit signed integer PCM formats. The
26+
* audio samples are copied from source to sink with gain defined in cd->gain.
27+
*
28+
* Return: Value zero for success, otherwise an error code.
29+
*/
30+
static int level_multiplier_s16(const struct processing_module *mod,
31+
struct sof_source *source,
32+
struct sof_sink *sink,
33+
uint32_t frames)
34+
{
35+
struct level_multiplier_comp_data *cd = module_get_private_data(mod);
36+
const int32_t gain = cd->gain;
37+
int16_t const *x, *x_start, *x_end;
38+
int16_t *y, *y_start, *y_end;
39+
int x_size, y_size;
40+
int source_samples_without_wrap;
41+
int samples_without_wrap;
42+
int remaining_samples = frames * cd->channels;
43+
int bytes = frames * cd->frame_bytes;
44+
int ret;
45+
int i;
46+
47+
ret = source_get_data_s16(source, bytes, &x, &x_start, &x_size);
48+
if (ret)
49+
return ret;
50+
51+
/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
52+
ret = sink_get_buffer_s16(sink, bytes, &y, &y_start, &y_size);
53+
if (ret)
54+
return ret;
55+
56+
/* Set helper pointers to buffer end for wrap check. Then loop until all
57+
* samples are processed.
58+
*/
59+
x_end = x_start + x_size;
60+
y_end = y_start + y_size;
61+
while (remaining_samples) {
62+
/* Find out samples to process before first wrap or end of data. */
63+
source_samples_without_wrap = x_end - x;
64+
samples_without_wrap = y_end - y;
65+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
66+
samples_without_wrap = MIN(samples_without_wrap, remaining_samples);
67+
for (i = 0; i < samples_without_wrap; i++) {
68+
*y = q_multsr_sat_32x32_16(*x, gain, LEVEL_MULTIPLIER_S16_SHIFT);
69+
x++;
70+
y++;
71+
}
72+
73+
/* One of the buffers needs a wrap (or end of data), so check for wrap */
74+
x = (x >= x_end) ? x - x_size : x;
75+
y = (y >= y_end) ? y - y_size : y;
76+
77+
remaining_samples -= samples_without_wrap;
78+
}
79+
80+
/* Update the source and sink for bytes consumed and produced. Return success. */
81+
source_release_data(source, bytes);
82+
sink_commit_buffer(sink, bytes);
83+
return 0;
84+
}
85+
#endif /* CONFIG_FORMAT_S16LE */
86+
87+
#if CONFIG_FORMAT_S24LE
88+
/**
89+
* level_multiplier_s32() - Process S32_LE or S24_4LE format.
90+
* @mod: Pointer to module data.
91+
* @source: Source for PCM samples data.
92+
* @sink: Sink for PCM samples data.
93+
* @frames: Number of audio data frames to process.
94+
*
95+
* This is the processing function for 24-bit signed integer PCM formats. The
96+
* audio samples are copied from source to sink with gain defined in cd->gain.
97+
*
98+
* Return: Value zero for success, otherwise an error code.
99+
*/
100+
static int level_multiplier_s24(const struct processing_module *mod,
101+
struct sof_source *source,
102+
struct sof_sink *sink,
103+
uint32_t frames)
104+
{
105+
struct level_multiplier_comp_data *cd = module_get_private_data(mod);
106+
const int32_t gain = cd->gain;
107+
int32_t const *x, *x_start, *x_end;
108+
int32_t *y, *y_start, *y_end;
109+
int x_size, y_size;
110+
int source_samples_without_wrap;
111+
int samples_without_wrap;
112+
int remaining_samples = frames * cd->channels;
113+
int bytes = frames * cd->frame_bytes;
114+
int ret;
115+
int i;
116+
117+
ret = source_get_data_s32(source, bytes, &x, &x_start, &x_size);
118+
if (ret)
119+
return ret;
120+
121+
/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
122+
ret = sink_get_buffer_s32(sink, bytes, &y, &y_start, &y_size);
123+
if (ret)
124+
return ret;
125+
126+
/* Set helper pointers to buffer end for wrap check. Then loop until all
127+
* samples are processed.
128+
*/
129+
x_end = x_start + x_size;
130+
y_end = y_start + y_size;
131+
while (remaining_samples) {
132+
/* Find out samples to process before first wrap or end of data. */
133+
source_samples_without_wrap = x_end - x;
134+
samples_without_wrap = y_end - y;
135+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
136+
samples_without_wrap = MIN(samples_without_wrap, remaining_samples);
137+
for (i = 0; i < samples_without_wrap; i++) {
138+
*y = q_multsr_sat_32x32_24(sign_extend_s24(*x), gain,
139+
LEVEL_MULTIPLIER_S32_SHIFT);
140+
x++;
141+
y++;
142+
}
143+
144+
/* One of the buffers needs a wrap (or end of data), so check for wrap */
145+
x = (x >= x_end) ? x - x_size : x;
146+
y = (y >= y_end) ? y - y_size : y;
147+
148+
remaining_samples -= samples_without_wrap;
149+
}
150+
151+
/* Update the source and sink for bytes consumed and produced. Return success. */
152+
source_release_data(source, bytes);
153+
sink_commit_buffer(sink, bytes);
154+
return 0;
155+
}
156+
#endif /* CONFIG_FORMAT_S24LE */
157+
158+
159+
#if CONFIG_FORMAT_S32LE
160+
/**
161+
* level_multiplier_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+
* This is the processing function for 32-bit signed integer PCM formats. The
168+
* audio samples are copied from source to sink with gain defined in cd->gain.
169+
*
170+
* Return: Value zero for success, otherwise an error code.
171+
*/
172+
static int level_multiplier_s32(const struct processing_module *mod,
173+
struct sof_source *source,
174+
struct sof_sink *sink,
175+
uint32_t frames)
176+
{
177+
struct level_multiplier_comp_data *cd = module_get_private_data(mod);
178+
const int32_t gain = cd->gain;
179+
int32_t const *x, *x_start, *x_end;
180+
int32_t *y, *y_start, *y_end;
181+
int x_size, y_size;
182+
int source_samples_without_wrap;
183+
int samples_without_wrap;
184+
int remaining_samples = frames * cd->channels;
185+
int bytes = frames * cd->frame_bytes;
186+
int ret;
187+
int i;
188+
189+
ret = source_get_data_s32(source, bytes, &x, &x_start, &x_size);
190+
if (ret)
191+
return ret;
192+
193+
/* Similarly get pointer to sink data in circular buffer, buffer start and size. */
194+
ret = sink_get_buffer_s32(sink, bytes, &y, &y_start, &y_size);
195+
if (ret)
196+
return ret;
197+
198+
/* Set helper pointers to buffer end for wrap check. Then loop until all
199+
* samples are processed.
200+
*/
201+
x_end = x_start + x_size;
202+
y_end = y_start + y_size;
203+
while (remaining_samples) {
204+
/* Find out samples to process before first wrap or end of data. */
205+
source_samples_without_wrap = x_end - x;
206+
samples_without_wrap = y_end - y;
207+
samples_without_wrap = MIN(samples_without_wrap, source_samples_without_wrap);
208+
samples_without_wrap = MIN(samples_without_wrap, remaining_samples);
209+
for (i = 0; i < samples_without_wrap; i++) {
210+
*y = q_multsr_sat_32x32(*x, gain, LEVEL_MULTIPLIER_S32_SHIFT);
211+
x++;
212+
y++;
213+
}
214+
215+
/* One of the buffers needs a wrap (or end of data), so check for wrap */
216+
x = (x >= x_end) ? x - x_size : x;
217+
y = (y >= y_end) ? y - y_size : y;
218+
219+
remaining_samples -= samples_without_wrap;
220+
}
221+
222+
/* Update the source and sink for bytes consumed and produced. Return success. */
223+
source_release_data(source, bytes);
224+
sink_commit_buffer(sink, bytes);
225+
return 0;
226+
}
227+
#endif /* CONFIG_FORMAT_S32LE */
228+
229+
/* This struct array defines the used processing functions for
230+
* the PCM formats
231+
*/
232+
const struct level_multiplier_proc_fnmap level_multiplier_proc_fnmap[] = {
233+
#if CONFIG_FORMAT_S16LE
234+
{ SOF_IPC_FRAME_S16_LE, level_multiplier_s16 },
235+
#endif
236+
#if CONFIG_FORMAT_S24LE
237+
{ SOF_IPC_FRAME_S24_4LE, level_multiplier_s24 },
238+
#endif
239+
#if CONFIG_FORMAT_S32LE
240+
{ SOF_IPC_FRAME_S32_LE, level_multiplier_s32 },
241+
#endif
242+
};
243+
244+
/**
245+
* level_multiplier_find_proc_func() - Find suitable processing function.
246+
* @src_fmt: Enum value for PCM format.
247+
*
248+
* This function finds the suitable processing function to use for
249+
* the used PCM format. If not found, return NULL.
250+
*
251+
* Return: Pointer to processing function for the requested PCM format.
252+
*/
253+
level_multiplier_func level_multiplier_find_proc_func(enum sof_ipc_frame src_fmt)
254+
{
255+
int i;
256+
257+
/* Find suitable processing function from map */
258+
for (i = 0; i < ARRAY_SIZE(level_multiplier_proc_fnmap); i++)
259+
if (src_fmt == level_multiplier_proc_fnmap[i].frame_fmt)
260+
return level_multiplier_proc_fnmap[i].level_multiplier_proc_func;
261+
262+
return NULL;
263+
}

0 commit comments

Comments
 (0)