Skip to content

Commit 02cecbc

Browse files
committed
Audio: MFCC: Add VAD switch control notification to user space
Add IPC4 notification that sends the VAD state to user space via a switch control whenever the VAD decision changes between speech and silence. The notification is initialized during prepare and sent from the audio processing path on VAD state transitions. The implementation follows the TDFB/sound_dose notification pattern: mfcc_ipc4.c contains the IPC4-specific notification init and send functions, while mfcc.c provides weak stubs so IPC3 builds link without the IPC4 dependencies. Add handling for SOF_IPC4_SWITCH_CONTROL_PARAM_ID in mfcc_get_config and mfcc_set_config so the kernel driver can read back the current VAD state after receiving a notification. The switch control is read-only from the DSP side. Both the notification init and the VAD state change detection are gated on the update_controls flag in the configuration blob struct. Add a switch control (mixer) to the MFCC topology2 widget definition for the VAD notification. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent aecf89f commit 02cecbc

7 files changed

Lines changed: 257 additions & 35 deletions

File tree

src/audio/mfcc/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ if(CONFIG_COMP_MFCC STREQUAL "m" AND DEFINED CONFIG_LLEXT)
55
add_dependencies(app mfcc)
66
else()
77
add_local_sources(sof mfcc.c mfcc_setup.c mfcc_common.c mfcc_generic.c mfcc_hifi4.c mfcc_hifi3.c mfcc_vad.c)
8+
if(CONFIG_IPC_MAJOR_4)
9+
add_local_sources(sof mfcc_ipc4.c)
10+
endif()
811
endif()

src/audio/mfcc/mfcc.c

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -97,36 +97,14 @@ static int mfcc_free(struct processing_module *mod)
9797
struct mfcc_comp_data *cd = module_get_private_data(mod);
9898

9999
comp_info(mod->dev, "entry");
100+
ipc_msg_free(cd->msg);
101+
cd->msg = NULL;
100102
mod_data_blob_handler_free(mod, cd->model_handler);
101103
mfcc_free_buffers(mod);
102104
mod_free(mod, cd);
103105
return 0;
104106
}
105107

106-
static int mfcc_get_config(struct processing_module *mod,
107-
uint32_t config_id, uint32_t *data_offset_size,
108-
uint8_t *fragment, size_t fragment_size)
109-
{
110-
struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment;
111-
struct mfcc_comp_data *cd = module_get_private_data(mod);
112-
113-
comp_info(mod->dev, "entry");
114-
115-
return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size);
116-
}
117-
118-
static int mfcc_set_config(struct processing_module *mod, uint32_t config_id,
119-
enum module_cfg_fragment_position pos, uint32_t data_offset_size,
120-
const uint8_t *fragment, size_t fragment_size, uint8_t *response,
121-
size_t response_size)
122-
{
123-
struct mfcc_comp_data *cd = module_get_private_data(mod);
124-
125-
comp_info(mod->dev, "entry");
126-
127-
return comp_data_blob_set(cd->model_handler, pos, data_offset_size,
128-
fragment, fragment_size);
129-
}
130108

131109
static int mfcc_process(struct processing_module *mod,
132110
struct input_stream_buffer *input_buffers, int num_input_buffers,
@@ -187,22 +165,29 @@ static int mfcc_prepare(struct processing_module *mod,
187165
audio_stream_get_channels(&sourceb->stream));
188166
if (ret < 0) {
189167
comp_err(dev, "setup failed.");
190-
goto err;
168+
return ret;
191169
}
170+
} else {
171+
comp_err(dev, "configuration is missing.");
172+
return -EINVAL;
192173
}
193174

194175
cd->mfcc_func = mfcc_find_func(source_format, sink_format, mfcc_fm, ARRAY_SIZE(mfcc_fm));
195176
if (!cd->mfcc_func) {
196177
comp_err(dev, "No proc func");
197-
ret = -EINVAL;
198-
goto err;
178+
return -EINVAL;
199179
}
200180

201-
return 0;
181+
/* Initialize VAD switch control notification if enabled */
182+
if (cd->config->enable_vad && cd->config->update_controls && !cd->msg) {
183+
ret = mfcc_ipc_notification_init(mod);
184+
if (ret < 0)
185+
return ret;
186+
187+
cd->vad_prev = false;
188+
}
202189

203-
err:
204-
comp_set_state(dev, COMP_TRIGGER_RESET);
205-
return ret;
190+
return 0;
206191
}
207192

208193
static int mfcc_reset(struct processing_module *mod)

src/audio/mfcc/mfcc_common.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ LOG_MODULE_REGISTER(mfcc_common, CONFIG_SOF_LOG_LEVEL);
2929
* The main processing function for MFCC
3030
*/
3131

32-
static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_comp_data *cd)
32+
static int mfcc_stft_process(struct processing_module *mod, struct mfcc_comp_data *cd)
3333
{
34+
const struct comp_dev *dev = mod->dev;
3435
struct sof_mfcc_config *config = cd->config;
3536
struct mfcc_state *state = &cd->state;
3637
struct mfcc_buffer *buf = &state->buf;
@@ -187,6 +188,16 @@ static int mfcc_stft_process(const struct comp_dev *dev, struct mfcc_comp_data *
187188

188189
/* Increment hop counter at end of hop processing */
189190
state->hop_count++;
191+
192+
/* Send notification when VAD state changes */
193+
if (config->enable_vad && config->update_controls) {
194+
bool vad_now = cd->vad.is_speech;
195+
196+
if (vad_now != cd->vad_prev) {
197+
mfcc_send_vad_notification(mod, vad_now ? 1 : 0);
198+
cd->vad_prev = vad_now;
199+
}
200+
}
190201
}
191202

192203
return cc_count;
@@ -295,7 +306,7 @@ void mfcc_s16_default(struct processing_module *mod, struct input_stream_buffer
295306
mfcc_source_copy_s16(bsource, buf, &state->emph, frames, state->source_channel);
296307

297308
/* Run STFT and processing after FFT: Mel auditory filter and DCT. */
298-
num_ceps = mfcc_stft_process(mod->dev, cd);
309+
num_ceps = mfcc_stft_process(mod, cd);
299310

300311
/* If new output produced, set up pointer into scratch data and mark header pending */
301312
if (num_ceps > 0) {
@@ -394,7 +405,7 @@ void mfcc_s24_default(struct processing_module *mod, struct input_stream_buffer
394405
mfcc_source_copy_s24(bsource, buf, &state->emph, frames, state->source_channel);
395406

396407
/* Run STFT and processing after FFT */
397-
num_ceps = mfcc_stft_process(mod->dev, cd);
408+
num_ceps = mfcc_stft_process(mod, cd);
398409

399410
/* If new output produced, set up pointer into scratch data */
400411
if (num_ceps > 0) {
@@ -473,7 +484,7 @@ void mfcc_s32_default(struct processing_module *mod, struct input_stream_buffer
473484
mfcc_source_copy_s32(bsource, buf, &state->emph, frames, state->source_channel);
474485

475486
/* Run STFT and processing after FFT */
476-
num_ceps = mfcc_stft_process(mod->dev, cd);
487+
num_ceps = mfcc_stft_process(mod, cd);
477488

478489
/* If new output produced, set up pointer into scratch data */
479490
if (num_ceps > 0) {

src/audio/mfcc/mfcc_ipc4.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//
3+
// Copyright(c) 2026 Intel Corporation.
4+
//
5+
// Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
6+
7+
/**
8+
* \file mfcc_ipc4.c
9+
* \brief IPC4-specific functions for MFCC component.
10+
*
11+
* Provides VAD switch control notification to user space via the
12+
* IPC4 module notification mechanism.
13+
*/
14+
15+
#include <sof/audio/mfcc/mfcc_comp.h>
16+
#include <sof/audio/module_adapter/module/generic.h>
17+
#include <sof/audio/component.h>
18+
#include <sof/ipc/msg.h>
19+
#include <sof/trace/trace.h>
20+
#include <ipc4/base-config.h>
21+
#include <ipc4/header.h>
22+
#include <ipc4/module.h>
23+
#include <ipc4/notification.h>
24+
#include <rtos/string.h>
25+
#include <errno.h>
26+
#include <stdint.h>
27+
28+
LOG_MODULE_DECLARE(mfcc, CONFIG_SOF_LOG_LEVEL);
29+
30+
/**
31+
* \brief Initialize IPC notification message for VAD switch control.
32+
*
33+
* Allocates and configures the IPC message used to send VAD state
34+
* change notifications to user space via a switch control.
35+
*/
36+
int mfcc_ipc_notification_init(struct processing_module *mod)
37+
{
38+
struct mfcc_comp_data *cd = module_get_private_data(mod);
39+
struct ipc_msg msg_proto;
40+
struct comp_dev *dev = mod->dev;
41+
struct comp_ipc_config *ipc_config = &dev->ipc_config;
42+
union ipc4_notification_header *primary =
43+
(union ipc4_notification_header *)&msg_proto.header;
44+
struct sof_ipc4_notify_module_data *msg_module_data;
45+
struct sof_ipc4_control_msg_payload *msg_payload;
46+
47+
memset_s(&msg_proto, sizeof(msg_proto), 0, sizeof(msg_proto));
48+
primary->r.notif_type = SOF_IPC4_MODULE_NOTIFICATION;
49+
primary->r.type = SOF_IPC4_GLB_NOTIFICATION;
50+
primary->r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REQUEST;
51+
primary->r.msg_tgt = SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG;
52+
cd->msg = ipc_msg_w_ext_init(msg_proto.header, msg_proto.extension,
53+
sizeof(struct sof_ipc4_notify_module_data) +
54+
sizeof(struct sof_ipc4_control_msg_payload) +
55+
sizeof(struct sof_ipc4_ctrl_value_chan));
56+
if (!cd->msg) {
57+
comp_err(dev, "Failed to initialize VAD notification");
58+
return -ENOMEM;
59+
}
60+
61+
msg_module_data = (struct sof_ipc4_notify_module_data *)cd->msg->tx_data;
62+
msg_module_data->instance_id = IPC4_INST_ID(ipc_config->id);
63+
msg_module_data->module_id = IPC4_MOD_ID(ipc_config->id);
64+
msg_module_data->event_id = SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL |
65+
SOF_IPC4_SWITCH_CONTROL_PARAM_ID;
66+
msg_module_data->event_data_size = sizeof(struct sof_ipc4_control_msg_payload) +
67+
sizeof(struct sof_ipc4_ctrl_value_chan);
68+
69+
msg_payload = (struct sof_ipc4_control_msg_payload *)msg_module_data->event_data;
70+
msg_payload->id = MFCC_CTRL_INDEX_VAD;
71+
msg_payload->num_elems = 1;
72+
msg_payload->chanv[0].channel = 0;
73+
74+
comp_dbg(dev, "VAD notification init: instance_id = 0x%08x, module_id = 0x%08x",
75+
msg_module_data->instance_id, msg_module_data->module_id);
76+
return 0;
77+
}
78+
79+
/**
80+
* \brief Send VAD switch control notification to user space.
81+
* \param mod Processing module.
82+
* \param val VAD value: 1 = speech, 0 = silence.
83+
*/
84+
void mfcc_send_vad_notification(struct processing_module *mod, uint32_t val)
85+
{
86+
struct mfcc_comp_data *cd = module_get_private_data(mod);
87+
struct sof_ipc4_notify_module_data *msg_module_data;
88+
struct sof_ipc4_control_msg_payload *msg_payload;
89+
90+
if (!cd->msg)
91+
return;
92+
93+
msg_module_data = (struct sof_ipc4_notify_module_data *)cd->msg->tx_data;
94+
msg_payload = (struct sof_ipc4_control_msg_payload *)msg_module_data->event_data;
95+
msg_payload->chanv[0].value = val;
96+
ipc_msg_send(cd->msg, NULL, false);
97+
}
98+
99+
int mfcc_get_config(struct processing_module *mod,
100+
uint32_t config_id, uint32_t *data_offset_size,
101+
uint8_t *fragment, size_t fragment_size)
102+
{
103+
struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment;
104+
struct mfcc_comp_data *cd = module_get_private_data(mod);
105+
struct sof_ipc4_control_msg_payload *ctl;
106+
107+
comp_info(mod->dev, "entry");
108+
109+
switch (config_id) {
110+
case SOF_IPC4_SWITCH_CONTROL_PARAM_ID:
111+
ctl = (struct sof_ipc4_control_msg_payload *)fragment;
112+
if (ctl->id == MFCC_CTRL_INDEX_VAD && ctl->num_elems == 1) {
113+
ctl->chanv[0].value = cd->vad_prev ? 1 : 0;
114+
*data_offset_size = sizeof(*ctl) + sizeof(ctl->chanv[0]);
115+
return 0;
116+
}
117+
return -EINVAL;
118+
default:
119+
return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size);
120+
}
121+
}
122+
123+
int mfcc_set_config(struct processing_module *mod, uint32_t config_id,
124+
enum module_cfg_fragment_position pos, uint32_t data_offset_size,
125+
const uint8_t *fragment, size_t fragment_size, uint8_t *response,
126+
size_t response_size)
127+
{
128+
struct mfcc_comp_data *cd = module_get_private_data(mod);
129+
130+
comp_info(mod->dev, "entry");
131+
132+
switch (config_id) {
133+
case SOF_IPC4_SWITCH_CONTROL_PARAM_ID:
134+
/* VAD switch is read-only, ignore set requests */
135+
return 0;
136+
default:
137+
return comp_data_blob_set(cd->model_handler, pos, data_offset_size,
138+
fragment, fragment_size);
139+
}
140+
}

src/include/sof/audio/mfcc/mfcc_comp.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
#define __SOF_AUDIO_MFCC_MFCC_COMP_H__
1010

1111
#include <sof/audio/module_adapter/module/generic.h>
12+
#include <sof/audio/data_blob.h>
1213
#include <sof/math/auditory.h>
1314
#include <sof/math/dct.h>
1415
#include <sof/math/fft.h>
1516
#include <sof/audio/mfcc/mfcc_vad.h>
17+
#include <sof/ipc/msg.h>
1618
#include <stddef.h>
1719
#include <stdint.h>
1820

@@ -34,6 +36,9 @@
3436
#define MFCC_FFT_BITS 32
3537
#define MFCC_MAX_SAMPLE_RATE 64000 /* Max sample rate in Hz, limited by int16_t Mel scale */
3638

39+
/** \brief Switch control index for VAD notification to user space */
40+
#define MFCC_CTRL_INDEX_VAD 0
41+
3742
/**
3843
* \brief Data header prepended to every MFCC output frame.
3944
*
@@ -137,7 +142,9 @@ struct mfcc_comp_data {
137142
struct mfcc_vad_state vad;
138143
struct comp_data_blob_handler *model_handler;
139144
struct sof_mfcc_config *config;
145+
struct ipc_msg *msg; /**< IPC notification for VAD switch control */
140146
int max_frames;
147+
bool vad_prev; /**< Previous VAD state for edge detection */
141148
mfcc_func mfcc_func; /**< processing function */
142149
};
143150

@@ -192,6 +199,52 @@ void mfcc_s32_default(struct processing_module *mod, struct input_stream_buffer
192199
struct output_stream_buffer *bsink, int frames);
193200
#endif
194201

202+
#if CONFIG_IPC_MAJOR_4
203+
int mfcc_ipc_notification_init(struct processing_module *mod);
204+
205+
void mfcc_send_vad_notification(struct processing_module *mod, uint32_t val);
206+
207+
int mfcc_get_config(struct processing_module *mod,
208+
uint32_t config_id, uint32_t *data_offset_size,
209+
uint8_t *fragment, size_t fragment_size);
210+
211+
int mfcc_set_config(struct processing_module *mod, uint32_t config_id,
212+
enum module_cfg_fragment_position pos, uint32_t data_offset_size,
213+
const uint8_t *fragment, size_t fragment_size, uint8_t *response,
214+
size_t response_size);
215+
216+
#else
217+
static inline int mfcc_ipc_notification_init(struct processing_module *mod)
218+
{
219+
return 0;
220+
}
221+
222+
static inline void mfcc_send_vad_notification(struct processing_module *mod, uint32_t val)
223+
{
224+
}
225+
226+
static inline int mfcc_get_config(struct processing_module *mod,
227+
uint32_t config_id, uint32_t *data_offset_size,
228+
uint8_t *fragment, size_t fragment_size)
229+
{
230+
struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment;
231+
struct mfcc_comp_data *cd = module_get_private_data(mod);
232+
233+
return comp_data_blob_get_cmd(cd->model_handler, cdata, fragment_size);
234+
}
235+
236+
static inline int mfcc_set_config(struct processing_module *mod, uint32_t config_id,
237+
enum module_cfg_fragment_position pos, uint32_t data_offset_size,
238+
const uint8_t *fragment, size_t fragment_size, uint8_t *response,
239+
size_t response_size)
240+
{
241+
struct mfcc_comp_data *cd = module_get_private_data(mod);
242+
243+
return comp_data_blob_set(cd->model_handler, pos, data_offset_size,
244+
fragment, fragment_size);
245+
}
246+
#endif
247+
195248
#ifdef UNIT_TEST
196249
void sys_comp_module_mfcc_interface_init(void);
197250
#endif

tools/topology/topology2/platform/intel/sdw-dmic-audio-feature.conf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ Object.Pipeline.host-gateway-src-mfcc-capture [
2121
name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC bytes"
2222
<include/components/mfcc/mel80.conf>
2323
}
24+
mixer."1" {
25+
name "$SDW_DMIC_AUDIO_FEATURE_CAPTURE_PCM_NAME MFCC VAD"
26+
Object.Base.channel.1 {
27+
name "fc"
28+
shift 0
29+
}
30+
Object.Base.ops.1 {
31+
name "ctl"
32+
info "volsw"
33+
#259 binds the mixer control to switch get/put handlers
34+
get 259
35+
put 259
36+
}
37+
max 1
38+
}
2439
}
2540
}
2641
}

0 commit comments

Comments
 (0)