Skip to content

Commit 69b8d59

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 69b8d59

8 files changed

Lines changed: 873 additions & 893 deletions

File tree

src/audio/mfcc/mfcc.c

Lines changed: 134 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,77 @@ 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;
167+
168+
comp_dbg(dev, "start");
169+
170+
/* In compress mode, retry pending output first and avoid producing
171+
* new frames until previous frame has been committed. In legacy
172+
* mode, pending output is held in a dedicated staging buffer
173+
* (state->out_stage) that STFT does not touch, so input processing
174+
* can continue while the previous period is drained.
175+
*/
176+
pending = state->header_pending || state->out_remain > 0;
177+
if (cd->config->compress_output && pending)
178+
return mfcc_process_output(mod, cd, sources, sinks, 0, 0);
117179

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

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

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

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

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");
257+
cd->source_func = mfcc_find_source_func(source_format);
258+
if (!cd->source_func) {
259+
comp_err(dev, "No source func");
178260
mfcc_free_buffers(mod);
179261
return -EINVAL;
180262
}
181263

264+
cd->source_format = source_format;
265+
266+
if (cd->config->compress_output)
267+
comp_info(dev, "compress PCM output mode enabled");
268+
269+
if (cd->config->enable_dtx && !cd->config->compress_output)
270+
comp_warn(dev, "enable_dtx ignored in normal PCM mode, only applies to compress");
271+
182272
/* Initialize VAD switch control notification if enabled */
183273
if (cd->config->enable_vad && cd->config->update_controls) {
184274
if (!cd->msg) {
@@ -194,6 +284,17 @@ static int mfcc_prepare(struct processing_module *mod,
194284
return 0;
195285
}
196286

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

208309
/* Reset to similar state as init() */
209-
cd->mfcc_func = NULL;
310+
cd->source_func = NULL;
210311
return 0;
211312
}
212313

@@ -215,7 +316,7 @@ static const struct module_interface mfcc_interface = {
215316
.free = mfcc_free,
216317
.set_configuration = mfcc_set_config,
217318
.get_configuration = mfcc_get_config,
218-
.process_audio_stream = mfcc_process,
319+
.process = mfcc_process,
219320
.prepare = mfcc_prepare,
220321
.reset = mfcc_reset,
221322
};

0 commit comments

Comments
 (0)