Skip to content

Commit e42209c

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 cc94891 commit e42209c

8 files changed

Lines changed: 844 additions & 893 deletions

File tree

src/audio/mfcc/mfcc.c

Lines changed: 131 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;
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)
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,74 @@ 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, -ENODATA if no input frames are available,
154+
* or a negative error code from the STFT or output stages.
155+
*/
109156
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)
157+
struct sof_source **sources, int num_of_sources,
158+
struct sof_sink **sinks, int num_of_sinks)
112159
{
113160
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;
161+
struct comp_dev *dev = mod->dev;
162+
struct mfcc_state *state = &cd->state;
163+
bool pending;
164+
size_t source_avail;
165+
int frames;
166+
int num_ceps;
117167

118-
comp_dbg(mod->dev, "start");
168+
comp_dbg(dev, "start");
119169

120-
frames = MIN(frames, cd->max_frames);
121-
cd->mfcc_func(mod, input_buffers, output_buffers, frames);
170+
/* In compress mode, retry pending output first and avoid producing
171+
* new frames until previous frame has been committed.
172+
*/
173+
pending = state->header_pending || state->out_remain > 0;
174+
if (cd->config->compress_output && pending)
175+
return mfcc_process_output(mod, cd, sources, sinks, 0, 0);
122176

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;
177+
source_avail = source_get_data_frames_available(sources[0]);
178+
frames = MIN(source_avail, cd->max_frames);
179+
if (frames == 0)
180+
return -ENODATA;
181+
182+
/* Copy input audio from source to MFCC internal circular buffer */
183+
cd->source_func(sources[0], &state->buf, &state->emph, frames, state->source_channel);
184+
185+
/* Run STFT and Mel/DCT processing */
186+
num_ceps = mfcc_stft_process(mod, cd);
187+
if (num_ceps < 0)
188+
return num_ceps;
189+
190+
return mfcc_process_output(mod, cd, sources, sinks, num_ceps, frames);
128191
}
129192

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

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");
254+
cd->source_func = mfcc_find_source_func(source_format);
255+
if (!cd->source_func) {
256+
comp_err(dev, "No source func");
178257
mfcc_free_buffers(mod);
179258
return -EINVAL;
180259
}
181260

261+
cd->source_format = source_format;
262+
263+
if (cd->config->compress_output)
264+
comp_info(dev, "compress PCM output mode enabled");
265+
266+
if (cd->config->enable_dtx && !cd->config->compress_output)
267+
comp_warn(dev, "enable_dtx ignored in normal PCM mode, only applies to compress");
268+
182269
/* Initialize VAD switch control notification if enabled */
183270
if (cd->config->enable_vad && cd->config->update_controls) {
184271
if (!cd->msg) {
@@ -194,6 +281,17 @@ static int mfcc_prepare(struct processing_module *mod,
194281
return 0;
195282
}
196283

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

208306
/* Reset to similar state as init() */
209-
cd->mfcc_func = NULL;
307+
cd->source_func = NULL;
210308
return 0;
211309
}
212310

@@ -215,7 +313,7 @@ static const struct module_interface mfcc_interface = {
215313
.free = mfcc_free,
216314
.set_configuration = mfcc_set_config,
217315
.get_configuration = mfcc_get_config,
218-
.process_audio_stream = mfcc_process,
316+
.process = mfcc_process,
219317
.prepare = mfcc_prepare,
220318
.reset = mfcc_reset,
221319
};

0 commit comments

Comments
 (0)