Skip to content

Commit 3dfb0f5

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 3dfb0f5

8 files changed

Lines changed: 918 additions & 895 deletions

File tree

src/audio/mfcc/mfcc.c

Lines changed: 133 additions & 33 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);
@@ -106,27 +138,76 @@ static int mfcc_free(struct processing_module *mod)
106138
}
107139

108140

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

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

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

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;
187+
/* Run STFT and Mel/DCT processing */
188+
num_ceps = mfcc_stft_process(mod, cd);
189+
if (num_ceps < 0)
190+
return num_ceps;
191+
192+
return mfcc_process_output(mod, cd, sources, sinks, num_ceps, frames);
128193
}
129194

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

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");
256+
cd->source_func = mfcc_find_source_func(source_format);
257+
if (!cd->source_func) {
258+
comp_err(dev, "No source func");
178259
mfcc_free_buffers(mod);
179260
return -EINVAL;
180261
}
181262

263+
cd->source_format = source_format;
264+
265+
if (cd->config->compress_output)
266+
comp_info(dev, "compress PCM output mode enabled");
267+
268+
if (cd->config->enable_dtx && !cd->config->compress_output)
269+
comp_warn(dev, "enable_dtx ignored in normal PCM mode, only applies to compress");
270+
182271
/* Initialize VAD switch control notification if enabled */
183272
if (cd->config->enable_vad && cd->config->update_controls) {
184273
if (!cd->msg) {
@@ -194,6 +283,17 @@ static int mfcc_prepare(struct processing_module *mod,
194283
return 0;
195284
}
196285

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

208308
/* Reset to similar state as init() */
209-
cd->mfcc_func = NULL;
309+
cd->source_func = NULL;
210310
return 0;
211311
}
212312

@@ -215,7 +315,7 @@ static const struct module_interface mfcc_interface = {
215315
.free = mfcc_free,
216316
.set_configuration = mfcc_set_config,
217317
.get_configuration = mfcc_get_config,
218-
.process_audio_stream = mfcc_process,
318+
.process = mfcc_process,
219319
.prepare = mfcc_prepare,
220320
.reset = mfcc_reset,
221321
};

0 commit comments

Comments
 (0)