Skip to content

Commit 291f48b

Browse files
committed
audio: mfcc: switch to source/sink API, int32 output, and DTX
Switch from process_audio_stream to source/sink API. Add compress PCM output mode (variable-size frames, no zero padding) alongside legacy mode (full period with zero-fill). Unify all output to int32 Q9.23 regardless of source format. Remove out_data_ptr_32, mel_spectra int16 copy, mfcc_func typedef, and per-format output functions from mfcc_common/hifi3/hifi4. Add DTX for compress mode: suppress silence frames after configurable trailing count, with optional periodic keepalive. Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
1 parent fd410ef commit 291f48b

8 files changed

Lines changed: 916 additions & 896 deletions

File tree

src/audio/mfcc/mfcc.c

Lines changed: 133 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <sof/audio/format.h>
1313
#include <sof/audio/pipeline.h>
1414
#include <sof/audio/ipc-config.h>
15+
#include <module/audio/source_api.h>
16+
#include <module/audio/sink_api.h>
1517
#include <sof/common.h>
1618
#include <rtos/panic.h>
1719
#include <sof/ipc/msg.h>
@@ -36,29 +38,39 @@ LOG_MODULE_REGISTER(mfcc, CONFIG_SOF_LOG_LEVEL);
3638

3739
SOF_DEFINE_REG_UUID(mfcc);
3840

39-
__cold_rodata const struct mfcc_func_map mfcc_fm[] = {
41+
/** \brief Source/sink API based source copy function map. */
42+
struct mfcc_source_func_map {
43+
uint8_t source_fmt;
44+
mfcc_source_func func;
45+
};
46+
47+
__cold_rodata static const struct mfcc_source_func_map mfcc_sfm[] = {
4048
#if CONFIG_FORMAT_S16LE
41-
{SOF_IPC_FRAME_S16_LE, mfcc_s16_default},
42-
#endif /* CONFIG_FORMAT_S16LE */
49+
{SOF_IPC_FRAME_S16_LE, mfcc_source_copy_s16},
50+
#endif
4351
#if CONFIG_FORMAT_S24LE
44-
{SOF_IPC_FRAME_S24_4LE, mfcc_s24_default},
45-
#endif /* CONFIG_FORMAT_S24LE */
52+
{SOF_IPC_FRAME_S24_4LE, mfcc_source_copy_s24},
53+
#endif
4654
#if CONFIG_FORMAT_S32LE
47-
{SOF_IPC_FRAME_S32_LE, mfcc_s32_default},
48-
#endif /* CONFIG_FORMAT_S32LE */
55+
{SOF_IPC_FRAME_S32_LE, mfcc_source_copy_s32},
56+
#endif
4957
};
5058

51-
static mfcc_func mfcc_find_func(enum sof_ipc_frame source_format,
52-
enum sof_ipc_frame sink_format,
53-
const struct mfcc_func_map *map,
54-
int n)
59+
/**
60+
* \brief Look up the source copy function for a given input format.
61+
*
62+
* \param[in] source_format SOF IPC frame format of the input stream.
63+
*
64+
* \return Pointer to the matching mfcc_source_func, or NULL if the
65+
* format is not supported in this build.
66+
*/
67+
static mfcc_source_func mfcc_find_source_func(enum sof_ipc_frame source_format)
5568
{
5669
int i;
5770

58-
/* Find suitable processing function from map. */
59-
for (i = 0; i < n; i++) {
60-
if (source_format == map[i].source)
61-
return map[i].func;
71+
for (i = 0; i < ARRAY_SIZE(mfcc_sfm); i++) {
72+
if (source_format == mfcc_sfm[i].source_fmt)
73+
return mfcc_sfm[i].func;
6274
}
6375

6476
return NULL;
@@ -68,6 +80,16 @@ static mfcc_func mfcc_find_func(enum sof_ipc_frame source_format,
6880
* End of MFCC setup code. Next the standard component methods.
6981
*/
7082

83+
/**
84+
* \brief MFCC module init callback.
85+
*
86+
* Allocates the component private data and the configuration blob
87+
* handler used to receive the MFCC configuration from user space.
88+
*
89+
* \param[in,out] mod Processing module being initialized.
90+
*
91+
* \return 0 on success, -ENOMEM on allocation failure.
92+
*/
7193
static int mfcc_init(struct processing_module *mod)
7294
{
7395
struct module_data *md = &mod->priv;
@@ -92,6 +114,16 @@ static int mfcc_init(struct processing_module *mod)
92114
return 0;
93115
}
94116

117+
/**
118+
* \brief MFCC module free callback.
119+
*
120+
* Releases the IPC notification message, configuration blob handler,
121+
* runtime buffers and the private data structure.
122+
*
123+
* \param[in,out] mod Processing module being freed.
124+
*
125+
* \return 0 on success.
126+
*/
95127
static int mfcc_free(struct processing_module *mod)
96128
{
97129
struct mfcc_comp_data *cd = module_get_private_data(mod);
@@ -105,28 +137,76 @@ static int mfcc_free(struct processing_module *mod)
105137
return 0;
106138
}
107139

108-
140+
/**
141+
* \brief Source/sink API based process function for MFCC.
142+
*
143+
* Reads input audio from sof_source, runs the STFT/Mel/DCT stage, and
144+
* delegates output formatting and commit handling to mfcc_common.c.
145+
*
146+
* \param[in,out] mod Processing module.
147+
* \param[in,out] sources Source array; only sources[0] is used.
148+
* \param[in] num_of_sources Number of sources (must be 1).
149+
* \param[in,out] sinks Sink array; only sinks[0] is used.
150+
* \param[in] num_of_sinks Number of sinks (must be 1).
151+
*
152+
* \return 0 on success, or a negative error code from the STFT or output stages.
153+
*/
109154
static int mfcc_process(struct processing_module *mod,
110-
struct input_stream_buffer *input_buffers, int num_input_buffers,
111-
struct output_stream_buffer *output_buffers, int num_output_buffers)
155+
struct sof_source **sources, int num_of_sources,
156+
struct sof_sink **sinks, int num_of_sinks)
112157
{
113158
struct mfcc_comp_data *cd = module_get_private_data(mod);
114-
struct audio_stream *source = input_buffers->data;
115-
struct audio_stream *sink = output_buffers->data;
116-
int frames = input_buffers->size;
159+
struct comp_dev *dev = mod->dev;
160+
struct mfcc_state *state = &cd->state;
161+
bool pending;
162+
size_t source_avail;
163+
int frames;
164+
int num_ceps;
165+
166+
comp_dbg(dev, "start");
167+
168+
/* In compress mode, retry pending output first and avoid producing
169+
* new frames until previous frame has been committed. In legacy
170+
* mode, pending output is held in a dedicated staging buffer
171+
* (state->out_stage) that STFT does not touch, so input processing
172+
* can continue while the previous period is drained.
173+
*/
174+
pending = state->header_pending || state->out_remain > 0;
175+
if (cd->config->compress_output && pending)
176+
return mfcc_process_output(mod, cd, sources, sinks, 0, 0);
117177

118-
comp_dbg(mod->dev, "start");
178+
source_avail = source_get_data_frames_available(sources[0]);
179+
frames = MIN(source_avail, cd->max_frames);
180+
if (!frames)
181+
return 0;
119182

120-
frames = MIN(frames, cd->max_frames);
121-
cd->mfcc_func(mod, input_buffers, output_buffers, frames);
183+
/* Copy input audio from source to MFCC internal circular buffer */
184+
cd->source_func(sources[0], &state->buf, &state->emph, frames, state->source_channel);
122185

123-
/* TODO: use module_update_buffer_position() from #6194 */
124-
input_buffers->consumed += audio_stream_frame_bytes(source) * frames;
125-
output_buffers->size += audio_stream_frame_bytes(sink) * frames;
126-
comp_dbg(mod->dev, "done");
127-
return 0;
186+
/* Run STFT and Mel/DCT processing */
187+
num_ceps = mfcc_stft_process(mod, cd);
188+
if (num_ceps < 0)
189+
return num_ceps;
190+
191+
return mfcc_process_output(mod, cd, sources, sinks, num_ceps, frames);
128192
}
129193

194+
/**
195+
* \brief MFCC module prepare callback.
196+
*
197+
* Validates the source/sink connection, reads the configuration blob,
198+
* initializes the MFCC processing state via mfcc_setup(), selects the
199+
* input copy function for the source frame format, and prepares the
200+
* VAD switch control notification when enabled.
201+
*
202+
* \param[in,out] mod Processing module being prepared.
203+
* \param[in,out] sources Source array; only sources[0] is used.
204+
* \param[in] num_of_sources Number of sources (must be 1).
205+
* \param[in,out] sinks Sink array; only sinks[0] is used.
206+
* \param[in] num_of_sinks Number of sinks (must be 1).
207+
*
208+
* \return 0 on success or a negative error code.
209+
*/
130210
static int mfcc_prepare(struct processing_module *mod,
131211
struct sof_source **sources, int num_of_sources,
132212
struct sof_sink **sinks, int num_of_sinks)
@@ -172,13 +252,21 @@ static int mfcc_prepare(struct processing_module *mod,
172252
return -EINVAL;
173253
}
174254

175-
cd->mfcc_func = mfcc_find_func(source_format, sink_format, mfcc_fm, ARRAY_SIZE(mfcc_fm));
176-
if (!cd->mfcc_func) {
177-
comp_err(dev, "No proc func");
255+
cd->source_func = mfcc_find_source_func(source_format);
256+
if (!cd->source_func) {
257+
comp_err(dev, "No source func");
178258
mfcc_free_buffers(mod);
179259
return -EINVAL;
180260
}
181261

262+
cd->source_format = source_format;
263+
264+
if (cd->config->compress_output)
265+
comp_info(dev, "compress PCM output mode enabled");
266+
267+
if (cd->config->enable_dtx && !cd->config->compress_output)
268+
comp_warn(dev, "enable_dtx ignored in normal PCM mode, only applies to compress");
269+
182270
/* Initialize VAD switch control notification if enabled */
183271
if (cd->config->enable_vad && cd->config->update_controls) {
184272
if (!cd->msg) {
@@ -194,6 +282,17 @@ static int mfcc_prepare(struct processing_module *mod,
194282
return 0;
195283
}
196284

285+
/**
286+
* \brief MFCC module reset callback.
287+
*
288+
* Frees runtime buffers (NULLing their pointers) so a subsequent
289+
* mfcc_prepare() can re-allocate cleanly. The configuration blob
290+
* handler and IPC notification message are preserved.
291+
*
292+
* \param[in,out] mod Processing module being reset.
293+
*
294+
* \return 0 on success.
295+
*/
197296
static int mfcc_reset(struct processing_module *mod)
198297
{
199298
struct mfcc_comp_data *cd = module_get_private_data(mod);
@@ -206,7 +305,7 @@ static int mfcc_reset(struct processing_module *mod)
206305
mfcc_free_buffers(mod);
207306

208307
/* Reset to similar state as init() */
209-
cd->mfcc_func = NULL;
308+
cd->source_func = NULL;
210309
return 0;
211310
}
212311

@@ -215,7 +314,7 @@ static const struct module_interface mfcc_interface = {
215314
.free = mfcc_free,
216315
.set_configuration = mfcc_set_config,
217316
.get_configuration = mfcc_get_config,
218-
.process_audio_stream = mfcc_process,
317+
.process = mfcc_process,
219318
.prepare = mfcc_prepare,
220319
.reset = mfcc_reset,
221320
};

0 commit comments

Comments
 (0)