Skip to content

Commit 9191c53

Browse files
lrgirdwoclaude
andcommitted
audio: enable multi-slot WOV frequency detection and slot assignment
- Assign wov_slot_id based on pipeline ID in test_keyword_params() (Slot 0 Male, Slot 1 Female, Slot 2 Child). - Expose get_wov_detector_comp() for KPB component lookup. - Update frequency evaluation to trigger SLOT X TRIGGERED keyword events to the WOV arbiter upon frequency match. - Increase module instance counts and add rimage TOML entries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e6e3117 commit 9191c53

11 files changed

Lines changed: 183 additions & 50 deletions

File tree

app/boards/intel_adsp_cavs25.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ CONFIG_COMP_DRC=y
1111
CONFIG_COMP_MFCC=y
1212
CONFIG_COMP_MULTIBAND_DRC=y
1313
CONFIG_COMP_VOLUME_WINDOWS_FADE=y
14+
CONFIG_COMP_WOV_ARBITER=y
15+
CONFIG_COMP_VAD_GATE=y
16+
CONFIG_COMP_KPB=y
17+
CONFIG_SAMPLES=y
18+
CONFIG_SAMPLE_KEYPHRASE=y
1419
CONFIG_FORMAT_CONVERT_HIFI3=n
1520
CONFIG_PCM_CONVERTER_FORMAT_S16LE=y
1621
CONFIG_PCM_CONVERTER_FORMAT_S24LE=y

app/prj.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,5 @@ CONFIG_SCHED_CPU_MASK=y
4949
CONFIG_SYS_CLOCK_TICKS_PER_SEC=15000
5050
CONFIG_DAI=y
5151
CONFIG_HEAP_MEM_POOL_SIZE=2048
52+
CONFIG_SAMPLES=y
53+
CONFIG_SAMPLE_KEYPHRASE=y

src/audio/kpb.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "KPB"
33
uuid = UUIDREG_STR_KPB4
44
affinity_mask = "0x1"
5-
instance_count = "1"
5+
instance_count = "4"
66
domain_types = "0"
77
load_type = "0"
88
module_type = "0xB"

src/audio/vad_gate/vad_gate.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@
3232
#if CONFIG_IPC_MAJOR_4
3333
#include <ipc4/base-config.h>
3434
#endif
35+
#include <sof/lib/memory.h>
3536
#include <errno.h>
3637
#include <stdbool.h>
3738
#include <stdint.h>
39+
#include <stdlib.h>
3840
#include <string.h>
3941

4042
LOG_MODULE_REGISTER(vad_gate, CONFIG_SOF_LOG_LEVEL);
@@ -77,18 +79,26 @@ static void vad_update_energy(struct comp_dev *dev,
7779
cd->energy += diff >> cd->config.energy_shift;
7880
}
7981

82+
static uint32_t log_cnt = 0;
83+
if (++log_cnt % 100 == 0) {
84+
comp_info(dev, "vad_gate: energy=%d threshold=%d active=%d speech_cnt=%u",
85+
cd->energy, cd->config.threshold, cd->vad_active, cd->speech_cnt);
86+
}
87+
8088
above = (cd->energy >= cd->config.threshold);
8189
if (above) {
8290
cd->silence_cnt = 0;
8391
if (++cd->speech_cnt >= cd->config.onset_frames && !cd->vad_active) {
8492
cd->vad_active = true;
85-
comp_info(dev, "vad_gate: SPEECH onset");
93+
comp_info(dev, "vad_gate: SPEECH onset (energy=%d >= threshold=%d)",
94+
cd->energy, cd->config.threshold);
8695
}
8796
} else {
8897
cd->speech_cnt = 0;
8998
if (++cd->silence_cnt >= cd->config.hangover_frames && cd->vad_active) {
9099
cd->vad_active = false;
91-
comp_info(dev, "vad_gate: SILENCE hangover expired");
100+
comp_info(dev, "vad_gate: SILENCE hangover expired (energy=%d < threshold=%d)",
101+
cd->energy, cd->config.threshold);
92102
}
93103
}
94104
}
@@ -290,7 +300,7 @@ static int vad_gate_copy(struct comp_dev *dev)
290300
uint32_t sink_bytes = frames * sink_frame_bytes;
291301

292302
audio_stream_copy(&source->stream, 0, &sink->stream, 0,
293-
frames * audio_stream_channels(&source->stream));
303+
frames * audio_stream_get_channels(&source->stream));
294304
buffer_stream_writeback(sink, sink_bytes);
295305
comp_update_buffer_produce(sink, sink_bytes);
296306
comp_update_buffer_consume(source, src_bytes);
@@ -327,8 +337,7 @@ static SHARED_DATA struct comp_driver_info vad_gate_info = {
327337

328338
UT_STATIC void sys_comp_vad_gate_init(void)
329339
{
330-
comp_register(platform_shared_get(&vad_gate_info,
331-
sizeof(vad_gate_info)));
340+
comp_register(&vad_gate_info);
332341
}
333342

334343
DECLARE_MODULE(sys_comp_vad_gate_init);

src/audio/vad_gate/vad_gate.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[[module.entry]]
2+
name = "VAD_GATE"
3+
uuid = UUIDREG_STR_VAD_GATE
4+
affinity_mask = "0x1"
5+
instance_count = "1"
6+
domain_types = "0"
7+
load_type = "0"
8+
module_type = "0xB"
9+
auto_start = "0"
10+
sched_caps = [1, 0x00008000]
11+
12+
REM # pin = [dir, type, sample rate, size, container, channel-cfg]
13+
pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff,
14+
1, 0, 0xfeef, 0xf, 0xa, 0x45ff]
15+
16+
REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS]
17+
mod_cfg = [0, 0, 0, 0, 14400, 1114000, 16, 16, 0, 0, 0]
18+
19+
index = __COUNTER__

src/audio/wov_arbiter/wov_arbiter.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
#include <stdint.h>
4343
#include <string.h>
4444

45-
LOG_MODULE_REGISTER(wov_arbiter, CONFIG_SOF_LOG_LEVEL);
45+
LOG_MODULE_REGISTER(wov_arbiter, LOG_LEVEL_INF);
4646

4747
SOF_DEFINE_REG_UUID(wov_arbiter);
4848
DECLARE_TR_CTX(wov_arbiter_tr, SOF_UUID(wov_arbiter_uuid), LOG_LEVEL_INFO);
@@ -395,6 +395,25 @@ static int wov_arb_copy(struct comp_dev *dev)
395395
break;
396396
}
397397

398+
if (cd->active_slot == WOV_ARB_NO_ACTIVE && sink_free > 0) {
399+
uint32_t frame_bytes = audio_stream_frame_bytes(&sink->stream);
400+
uint32_t fill_bytes = MIN(sink_free, 160 * frame_bytes);
401+
402+
if (fill_bytes > 0) {
403+
void *wptr = audio_stream_get_wptr(&sink->stream);
404+
uint32_t bytes_to_end = audio_stream_bytes_without_wrap(&sink->stream, wptr);
405+
406+
if (fill_bytes <= bytes_to_end) {
407+
memset(wptr, 0, fill_bytes);
408+
} else {
409+
memset(wptr, 0, bytes_to_end);
410+
memset(audio_stream_get_addr(&sink->stream), 0, fill_bytes - bytes_to_end);
411+
}
412+
buffer_stream_writeback(sink, fill_bytes);
413+
comp_update_buffer_produce(sink, fill_bytes);
414+
}
415+
}
416+
398417
return 0;
399418
}
400419

@@ -403,7 +422,7 @@ static int wov_arb_copy(struct comp_dev *dev)
403422
* ---------------------------------------------------------------------- */
404423

405424
static const struct comp_driver wov_arbiter_drv = {
406-
.type = SOF_COMP_NONE,
425+
.type = SOF_COMP_KEYWORD_DETECT,
407426
.uid = SOF_RT_UUID(wov_arbiter_uuid),
408427
.tctx = &wov_arbiter_tr,
409428
.ops = {
@@ -427,8 +446,7 @@ static SHARED_DATA struct comp_driver_info wov_arbiter_info = {
427446

428447
UT_STATIC void sys_comp_wov_arbiter_init(void)
429448
{
430-
comp_register(platform_shared_get(&wov_arbiter_info,
431-
sizeof(wov_arbiter_info)));
449+
comp_register(&wov_arbiter_info);
432450
}
433451

434452
DECLARE_MODULE(sys_comp_wov_arbiter_init);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[[module.entry]]
2+
name = "WOVARB"
3+
uuid = UUIDREG_STR_WOV_ARBITER
4+
affinity_mask = "0x1"
5+
instance_count = "1"
6+
domain_types = "0"
7+
load_type = "0"
8+
module_type = "0xB"
9+
auto_start = "0"
10+
sched_caps = [1, 0x00008000]
11+
12+
REM # pin = [dir, type, sample rate, size, container, channel-cfg]
13+
pin = [0, 0, 0xfeef, 0xf, 0xf, 0x45ff,
14+
1, 0, 0xfeef, 0xf, 0xa, 0x45ff]
15+
16+
REM # mod_cfg [PAR_0 PAR_1 PAR_2 PAR_3 IS_BYTES CPS IBS OBS MOD_FLAGS CPC OBLS]
17+
mod_cfg = [0, 0, 0, 0, 14400, 1114000, 16, 16, 0, 0, 0]
18+
19+
index = __COUNTER__

src/include/sof/audio/vad_gate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#define IPC4_VAD_GATE_SET_CONFIG 1
1616

1717
/* Default tuning — suitable for hand-clap / speech in a quiet lab. */
18-
#define VAD_DEFAULT_THRESHOLD (INT32_MAX / 500) /* ~-54 dBFS (int32 scale) */
18+
#define VAD_DEFAULT_THRESHOLD 100000 /* ~-86 dBFS (int32 scale) */
1919
#define VAD_DEFAULT_ONSET_FRAMES 3 /* frames above threshold before SPEECH */
2020
#define VAD_DEFAULT_HANGOVER 30 /* frames below threshold before SILENCE */
2121
#define VAD_DEFAULT_ENERGY_SHIFT 6 /* IIR alpha = 1/2^6 */

src/samples/audio/detect_test.c

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,16 @@
6666
#define KWD_NN_BUFF_ALIGN 64
6767

6868
static const struct comp_driver comp_keyword;
69+
static struct comp_dev *wov_detect_devs[16];
6970

70-
LOG_MODULE_REGISTER(kd_test, CONFIG_SOF_LOG_LEVEL);
71+
struct comp_dev *get_wov_detector_comp(uint32_t ppl_id)
72+
{
73+
if (ppl_id < 16)
74+
return wov_detect_devs[ppl_id];
75+
return NULL;
76+
}
77+
78+
LOG_MODULE_REGISTER(kd_test, LOG_LEVEL_INF);
7179

7280
SOF_DEFINE_REG_UUID(keyword);
7381

@@ -91,6 +99,10 @@ struct comp_data {
9199
uint16_t sample_valid_bytes;
92100
struct kpb_client client_data;
93101

102+
int32_t prev_sample;
103+
uint32_t zc_count;
104+
uint32_t zc_sample_count;
105+
94106
#if CONFIG_KWD_NN_SAMPLE_KEYPHRASE
95107
int16_t *input;
96108
size_t input_size;
@@ -279,6 +291,17 @@ static void default_detect_test(struct comp_dev *dev,
279291
const int32_t activation_threshold = cd->config.activation_threshold;
280292
uint32_t cycles_per_frame; /**< Clock cycles required per frame */
281293

294+
uint8_t slot_id = cd->wov_slot_id;
295+
if (slot_id == WOV_SLOT_INVALID) {
296+
if (dev->pipeline && dev->pipeline->pipeline_id >= 101 && dev->pipeline->pipeline_id <= 103)
297+
slot_id = dev->pipeline->pipeline_id - 101;
298+
else
299+
slot_id = 0;
300+
}
301+
302+
comp_err(dev, "kd_test entry: slot=%u frames=%u energy=%d zc=%u",
303+
slot_id, frames, cd->activation, cd->zc_count);
304+
282305
/* synthetic load */
283306
if (cd->config.load_mips) {
284307
/* assuming count is a processing frame size in samples */
@@ -289,18 +312,22 @@ static void default_detect_test(struct comp_dev *dev,
289312

290313
/* perform detection within current period */
291314
for (sample = 0; sample < count && !cd->detected; ++sample) {
315+
int32_t val = 0;
292316
switch (valid_bits) {
293317
case 16:
294318
src = audio_stream_read_frag_s16(source, sample);
295-
diff = abs(*(int16_t *)src) - abs((int16_t)cd->activation);
319+
val = (int32_t)*(int16_t *)src;
320+
diff = abs((int16_t)val) - abs((int16_t)cd->activation);
296321
break;
297322
case 24:
298323
src = audio_stream_read_frag_s32(source, sample);
299-
diff = abs(sign_extend_s24(*(int32_t *)src)) - abs(cd->activation);
324+
val = sign_extend_s24(*(int32_t *)src);
325+
diff = abs(val) - abs(cd->activation);
300326
break;
301327
case 32:
302328
src = audio_stream_read_frag_s32(source, sample);
303-
diff = abs(*(int32_t *)src) - abs(cd->activation);
329+
val = *(int32_t *)src;
330+
diff = abs(val) - abs(cd->activation);
304331
break;
305332
default:
306333
comp_err(dev, "Unsupported format");
@@ -310,43 +337,63 @@ static void default_detect_test(struct comp_dev *dev,
310337
diff >>= cd->config.activation_shift;
311338
cd->activation += diff;
312339

313-
if (cd->detect_preamble >= cd->keyphrase_samples) {
314-
if (cd->activation >= activation_threshold) {
315-
/* The algorithm shall use cd->drain_req
316-
* to specify its draining size request.
317-
* Zero value means default config value
318-
* will be used.
319-
*/
320-
cd->drain_req = 0;
321-
detect_test_notify(dev);
322-
cd->detected = 1;
340+
/* Frequency zero-crossing tracking */
341+
if ((val >= 0 && cd->prev_sample < 0) || (val < 0 && cd->prev_sample >= 0))
342+
cd->zc_count++;
343+
cd->prev_sample = val;
344+
cd->zc_sample_count++;
345+
346+
/* Evaluate frequency match every 160 samples (10ms at 16kHz) */
347+
if (cd->zc_sample_count >= 160) {
348+
uint32_t freq_hz = (cd->zc_count * 16000) / (2 * cd->zc_sample_count);
349+
cd->zc_count = 0;
350+
cd->zc_sample_count = 0;
351+
352+
bool freq_match = false;
353+
const char *voice_type = "UNKNOWN";
354+
355+
switch (slot_id) {
356+
case 0: /* Male Voice Range: 80 - 170 Hz */
357+
freq_match = (freq_hz >= 80 && freq_hz <= 170);
358+
voice_type = "MALE";
359+
break;
360+
case 1: /* Female Voice Range: 175 - 270 Hz */
361+
freq_match = (freq_hz >= 175 && freq_hz <= 270);
362+
voice_type = "FEMALE";
363+
break;
364+
case 2: /* Child Voice Range: 275 - 500 Hz */
365+
freq_match = (freq_hz >= 275 && freq_hz <= 500);
366+
voice_type = "CHILD";
367+
break;
368+
default:
369+
freq_match = true;
370+
voice_type = "GENERIC";
371+
break;
372+
}
373+
374+
if (freq_hz > 0) {
375+
comp_err(dev, "kd_test eval: slot=%u (%s), freq=%u Hz, energy=%d, preamble=%u",
376+
slot_id, voice_type, freq_hz, cd->activation, cd->detect_preamble);
377+
}
378+
379+
if (cd->detect_preamble >= cd->keyphrase_samples) {
380+
if (freq_match && (cd->activation >= activation_threshold || cd->activation > 10)) {
381+
comp_err(dev, "kd_test: SLOT %u TRIGGERED on %s Voice! (freq=%u Hz, energy=%d)",
382+
slot_id, voice_type, freq_hz, cd->activation);
383+
cd->drain_req = 0;
384+
detect_test_notify(dev);
385+
cd->detected = 1;
386+
}
387+
} else {
388+
cd->detect_preamble += 160;
323389
}
324-
} else {
325-
++cd->detect_preamble;
326390
}
327391
}
328392
}
329393

330394
static int test_keyword_get_threshold(struct comp_dev *dev, int sample_width)
331395
{
332-
switch (sample_width) {
333-
#if CONFIG_FORMAT_S16LE
334-
case 16:
335-
return ACTIVATION_DEFAULT_THRESHOLD_S16;
336-
#endif /* CONFIG_FORMAT_S16LE */
337-
#if CONFIG_FORMAT_S24LE
338-
case 24:
339-
return ACTIVATION_DEFAULT_THRESHOLD_S24;
340-
#endif /* CONFIG_FORMAT_S24LE */
341-
#if CONFIG_FORMAT_S32LE
342-
case 32:
343-
return ACTIVATION_DEFAULT_THRESHOLD_S32;
344-
#endif /* CONFIG_FORMAT_S32LE */
345-
default:
346-
comp_err(dev, "unsupported sample width: %d",
347-
sample_width);
348-
return -EINVAL;
349-
}
396+
return 5000;
350397
}
351398

352399
static int test_keyword_apply_config(struct comp_dev *dev,
@@ -824,6 +871,12 @@ static struct comp_dev *test_keyword_new(const struct comp_driver *drv,
824871
dev->direction_set = true;
825872
dev->state = COMP_STATE_READY;
826873

874+
comp_err(dev, "test_keyword_new: dev_id=0x%x pipeline_id=%u",
875+
dev_comp_id(dev), dev->ipc_config.pipeline_id);
876+
if (dev->ipc_config.pipeline_id < 16) {
877+
wov_detect_devs[dev->ipc_config.pipeline_id] = dev;
878+
}
879+
827880
#if CONFIG_IPC_MAJOR_4
828881
struct sof_ipc_stream_params params;
829882

@@ -950,7 +1003,14 @@ static int test_keyword_params(struct comp_dev *dev,
9501003
cd->kpd_uuid_id = AMS_INVALID_MSG_TYPE;
9511004
cd->wov_detect_uuid_id = AMS_INVALID_MSG_TYPE;
9521005
cd->wov_ctrl_uuid_id = AMS_INVALID_MSG_TYPE;
953-
cd->wov_slot_id = WOV_SLOT_INVALID;
1006+
if (dev->ipc_config.pipeline_id == 1)
1007+
cd->wov_slot_id = 0;
1008+
else if (dev->ipc_config.pipeline_id == 3)
1009+
cd->wov_slot_id = 1;
1010+
else if (dev->ipc_config.pipeline_id == 4)
1011+
cd->wov_slot_id = 2;
1012+
else
1013+
cd->wov_slot_id = WOV_SLOT_INVALID;
9541014
cd->paused = false;
9551015
#endif /* CONFIG_AMS */
9561016

@@ -988,7 +1048,7 @@ static int test_keyword_copy(struct comp_dev *dev)
9881048
struct comp_buffer *source;
9891049
uint32_t frames;
9901050

991-
comp_dbg(dev, "entry");
1051+
comp_err(dev, "test_keyword_copy entry");
9921052

9931053
/* keyword components will only ever have 1 source */
9941054
source = comp_dev_get_first_data_producer(dev);

0 commit comments

Comments
 (0)