diff --git a/src/audio/detect_test.c b/src/audio/detect_test.c index d0713d6421d2..d9a7f4edf631 100644 --- a/src/audio/detect_test.c +++ b/src/audio/detect_test.c @@ -29,8 +29,8 @@ #define ACTIVATION_DEFAULT_THRESHOLD_S16 \ ((int16_t)((INT16_MAX) * (ACTIVATION_DEFAULT_THRESHOLD))) -/* number of samples to be treated as a full keyphrase */ -#define KEYPHRASE_DEFAULT_PREAMBLE_LENGTH (30 * 1024) +/* default number of samples before detection is activated */ +#define KEYPHRASE_DEFAULT_PREAMBLE_LENGTH 0 struct comp_data { struct sof_detect_test_config config; @@ -40,6 +40,7 @@ struct comp_data { uint32_t detect_preamble; /**< current keyphrase preamble length */ uint32_t keyphrase_samples; /**< keyphrase length in samples */ uint32_t buf_copy_pos; /**< current copy position for incoming data */ + uint32_t history_depth; /** defines draining size in bytes. */ struct notify_data event; struct kpb_event_data event_data; @@ -70,12 +71,10 @@ static void notify_kpb(struct comp_dev *dev) cd->client_data.r_ptr = NULL; cd->client_data.sink = NULL; cd->client_data.id = 0; /**< TODO: acquire proper id from kpb */ - cd->client_data.history_end = 0; /**< keyphrase end, 0 is now */ - cd->client_data.history_begin = cd->detect_preamble; /* time in milliseconds */ - cd->client_data.history_depth = cd->detect_preamble / - (dev->params.rate / 1000); - + cd->client_data.history_depth = (cd->history_depth != 0) ? + cd->history_depth : + cd->config.history_depth; cd->event_data.event_id = KPB_EVENT_BEGIN_DRAINING; cd->event_data.client_data = &cd->client_data; @@ -118,6 +117,12 @@ static void default_detect_test(struct comp_dev *dev, if (cd->detect_preamble >= cd->keyphrase_samples) { if (cd->activation >= cd->config.activation_threshold) { + /* The algorithm shall use cd->history_depth + * to specify its draining size request. + * Zero value means default config value + * will be used. + */ + cd->history_depth = 0; detect_test_notify(dev); cd->detected = 1; } @@ -280,14 +285,8 @@ static int test_keyword_params(struct comp_dev *dev) dev->frame_bytes = comp_frame_bytes(dev); /* calculate the length of the preamble */ - if (cd->config.keyphrase_length) { - if (cd->config.keyphrase_length > KPB_MAX_BUFF_TIME) { - trace_keyword_error("test_keyword_params() " - "error: kp length too long"); - return -EINVAL; - } - - cd->keyphrase_samples = cd->config.keyphrase_length * + if (cd->config.preamble_time) { + cd->keyphrase_samples = cd->config.preamble_time * (dev->params.rate / 1000); } else { cd->keyphrase_samples = KEYPHRASE_DEFAULT_PREAMBLE_LENGTH; diff --git a/src/audio/kpb.c b/src/audio/kpb.c index a5b786ee6896..d96b033d5c55 100644 --- a/src/audio/kpb.c +++ b/src/audio/kpb.c @@ -22,6 +22,7 @@ #include #include #include +#include /* KPB private data, runtime data */ struct comp_data { @@ -33,8 +34,8 @@ struct comp_data { uint32_t source_period_bytes; /**< source number of period bytes */ uint32_t sink_period_bytes; /**< sink number of period bytes */ struct sof_kpb_config config; /**< component configuration data */ - struct comp_buffer *rt_sink; /**< real time sink (channel selector ) */ - struct comp_buffer *cli_sink; /**< draining sink (client) */ + struct comp_buffer *sel_sink; /**< real time sink (channel selector ) */ + struct comp_buffer *host_sink; /**< draining sink (client) */ struct hb *history_buffer; bool is_internal_buffer_full; size_t buffered_data; @@ -53,9 +54,13 @@ static size_t kpb_allocate_history_buffer(struct comp_data *kpb); static void kpb_clear_history_buffer(struct hb *buff); static void kpb_free_history_buffer(struct hb *buff); static bool kpb_has_enough_history_data(struct comp_data *kpb, - struct hb *buff, size_t his_req); + struct hb *buff, size_t his_req); static inline bool kpb_is_sample_width_supported(uint32_t sampling_width); - +static void kpb_copy_samples(struct comp_buffer *sink, + struct comp_buffer *source, size_t size, + size_t sample_width); +static void kpb_drain_samples(void *source, struct comp_buffer *sink, + size_t size, size_t sample_width); /** * \brief Create a key phrase buffer component. @@ -379,10 +384,10 @@ static int kpb_prepare(struct comp_dev *dev) } if (sink->sink->comp.type == SOF_COMP_SELECTOR) { /* We found proper real time sink */ - cd->rt_sink = sink; + cd->sel_sink = sink; } else if (sink->sink->comp.type == SOF_COMP_HOST) { /* We found proper host sink */ - cd->cli_sink = sink; + cd->host_sink = sink; } } @@ -462,17 +467,14 @@ static int kpb_copy(struct comp_dev *dev) /* Get source and sink buffers */ source = list_first_item(&dev->bsource_list, struct comp_buffer, sink_list); - sink = (kpb->state == KPB_STATE_BUFFERING) ? kpb->rt_sink - : kpb->cli_sink; - - /* Pause selector copy during draining and/or draining on demand. - * We keep selector in this "paused" state as long as draining - * is going and later during direct copy to client's/host sink, - * in so called "draining on demand" state. In order to rearm - * detection algorithm kpb_reset is needed. - */ - if (kpb->state != KPB_STATE_BUFFERING) + sink = (kpb->state == KPB_STATE_BUFFERING) ? kpb->sel_sink + : kpb->host_sink; + + /* Stop copying downstream if in draining mode */ + if (kpb->state == KPB_STATE_DRAINING) { + comp_update_buffer_consume(source, source->avail); return PPL_STATUS_PATH_STOP; + } /* Process source data */ /* Check if there are valid pointers */ @@ -500,7 +502,8 @@ static int kpb_copy(struct comp_dev *dev) /* Sink and source are both ready and have space */ copy_bytes = MIN(sink->free, source->avail); - assert(!memcpy_s(sink->w_ptr, copy_bytes, source->r_ptr, copy_bytes)); + kpb_copy_samples(sink, source, copy_bytes, + kpb->config.sampling_width); /* Buffer source data internally in history buffer for future * use by clients. @@ -689,10 +692,11 @@ static int kpb_register_client(struct comp_data *kpb, struct kpb_client *cli) */ static void kpb_init_draining(struct comp_data *kpb, struct kpb_client *cli) { - bool is_sink_ready = (kpb->cli_sink->sink->state == COMP_STATE_ACTIVE); + bool is_sink_ready = (kpb->host_sink->sink->state == COMP_STATE_ACTIVE); + size_t sample_width = kpb->config.sampling_width; size_t history_depth = cli->history_depth * kpb->config.no_channels * (kpb->config.sampling_freq / 1000) * - (kpb->config.sampling_width / 8); + (sample_width / 8); struct hb *buff = kpb->history_buffer; struct hb *first_buff = buff; size_t buffered = 0; @@ -769,16 +773,17 @@ static void kpb_init_draining(struct comp_data *kpb, struct kpb_client *cli) trace_kpb("kpb_init_draining(), schedule draining task"); /* Add one-time draining task into the scheduler. */ - kpb->draining_task_data.sink = kpb->cli_sink; + kpb->draining_task_data.sink = kpb->host_sink; kpb->draining_task_data.history_buffer = buff; kpb->draining_task_data.history_depth = history_depth; kpb->draining_task_data.state = &kpb->state; + kpb->draining_task_data.sample_width = sample_width; - /* Pause selector copy. */ - kpb->rt_sink->sink->state = COMP_STATE_PAUSED; + /* Change KPB internal state to DRAINING */ + kpb->state = KPB_STATE_DRAINING; /* Set host-sink copy mode to blocking */ - comp_set_attribute(kpb->cli_sink->sink, + comp_set_attribute(kpb->host_sink->sink, COMP_ATTR_COPY_BLOCKING, 1); /* Schedule draining task */ @@ -801,12 +806,18 @@ static uint64_t kpb_draining_task(void *arg) struct comp_buffer *sink = draining_data->sink; struct hb *buff = draining_data->history_buffer; size_t history_depth = draining_data->history_depth; + size_t sample_width = draining_data->sample_width; size_t size_to_read; size_t size_to_copy; bool move_buffer = false; + uint32_t drained = 0; + uint64_t time_start; + uint64_t time_end; trace_kpb("kpb_draining_task(), start."); + time_start = platform_timer_get(platform_timer); + while (history_depth > 0) { size_to_read = (uint32_t)buff->end_addr - (uint32_t)buff->r_ptr; @@ -824,10 +835,12 @@ static uint64_t kpb_draining_task(void *arg) } } - assert(!memcpy_s(sink->w_ptr, size_to_copy, buff->r_ptr, - size_to_copy)); + kpb_drain_samples(buff->r_ptr, sink, size_to_copy, + sample_width); + buff->r_ptr += (uint32_t)size_to_copy; history_depth -= size_to_copy; + drained += size_to_copy; if (move_buffer) { buff->r_ptr = buff->start_addr; @@ -838,19 +851,54 @@ static uint64_t kpb_draining_task(void *arg) comp_update_buffer_produce(sink, size_to_copy); } + time_end = platform_timer_get(platform_timer); + /* Draining is done. Now switch KPB to copy real time stream - * to client's sink + * to client's sink. This state is called "draining on demand" */ - *draining_data->state = KPB_STATE_DRAINING_ON_DEMAND; + *draining_data->state = KPB_STATE_HOST_COPY; /* Reset host-sink copy mode back to unblocking */ comp_set_attribute(sink->sink, COMP_ATTR_COPY_BLOCKING, 0); - trace_kpb("kpb_draining_task(), done."); + trace_kpb("kpb_draining_task(), done. %u drained in %d ms.", + drained, + (time_end - time_start) + / clock_ms_to_ticks(PLATFORM_DEFAULT_CLOCK, 1)); return 0; } +/** + * \brief Drain data samples safe, according to configuration. + * + * \param[in] dev - kpb component device pointer + * \param[in] sink - pointer to sink buffer. + * \param[in] source - pointer to source buffer. + * \param[in] size - requested copy size in bytes. + * + * \return none. + */ +static void kpb_drain_samples(void *source, struct comp_buffer *sink, + size_t size, size_t sample_width) +{ + int16_t *dest; + int16_t *src = (int16_t *)source; + uint32_t i; + uint32_t j = 0; + uint32_t channel; + uint32_t frames = KPB_BYTES_TO_FRAMES(size, sample_width); + + for (i = 0; i < frames; i++) { + for (channel = 0; channel < KPB_NR_OF_CHANNELS; channel++) { + dest = buffer_write_frag_s16(sink, j); + *dest = *src; + src++; + j++; + } + } +} + /** * \brief Initialize history buffer by zeroing its memory. * \param[in] buff - pointer to current history buffer. @@ -941,6 +989,37 @@ static inline bool kpb_is_sample_width_supported(uint32_t sampling_width) return ret; } +/** + * \brief Copy data samples safe, according to configuration. + * + * \param[in] dev - kpb component device pointer + * \param[in] sink - pointer to sink buffer. + * \param[in] source - pointer to source buffer. + * \param[in] size - requested copy size in bytes. + * + * \return none. + */ +static void kpb_copy_samples(struct comp_buffer *sink, + struct comp_buffer *source, size_t size, + size_t sample_width) +{ + int16_t *src; + int16_t *dest; + uint32_t i; + uint32_t j = 0; + uint32_t channel; + uint32_t frames = KPB_BYTES_TO_FRAMES(size, sample_width); + + for (i = 0; i < frames; i++) { + for (channel = 0; channel < KPB_NR_OF_CHANNELS; channel++) { + src = buffer_read_frag_s16(source, j); + dest = buffer_write_frag_s16(sink, j); + *dest = *src; + j++; + } + } +} + struct comp_driver comp_kpb = { .type = SOF_COMP_KPB, .ops = { diff --git a/src/include/sof/audio/kpb.h b/src/include/sof/audio/kpb.h index 9939429cb502..685064f5b766 100644 --- a/src/include/sof/audio/kpb.h +++ b/src/include/sof/audio/kpb.h @@ -32,10 +32,14 @@ #define KPB_NO_OF_HISTORY_BUFFERS 2 /**< no of internal buffers */ #define KPB_ALLOCATION_STEP 0x100 #define KPB_NO_OF_MEM_POOLS 3 +#define KPB_BYTES_TO_FRAMES(bytes, sample_width) \ + (bytes / ((KPB_SAMPLE_CONTAINER_SIZE(sample_width) / 8) * \ + KPB_NR_OF_CHANNELS)) enum kpb_state { KPB_STATE_BUFFERING = 0, - KPB_STATE_DRAINING_ON_DEMAND, + KPB_STATE_DRAINING, + KPB_STATE_HOST_COPY, }; enum kpb_event { @@ -61,8 +65,6 @@ enum kpb_client_state { struct kpb_client { uint8_t id; /**< id associated with output sink */ uint32_t history_depth; /**< normalized value of buffered bytes */ - uint32_t history_begin; /**< place where key phrase begins */ - uint32_t history_end; /**< place where key phrase ends */ enum kpb_client_state state; /**< current state of a client */ void *r_ptr; /**< current read position */ struct comp_buffer *sink; /**< client's sink */ @@ -95,6 +97,7 @@ struct dd { size_t history_depth; uint8_t is_draining_active; enum kpb_state *state; + size_t sample_width; }; /** \brief kpb component configuration data. */ diff --git a/src/include/user/detect_test.h b/src/include/user/detect_test.h index d56efd084281..02cc85a84863 100644 --- a/src/include/user/detect_test.h +++ b/src/include/user/detect_test.h @@ -18,9 +18,8 @@ struct sof_detect_test_config { /** synthetic system load settings */ uint32_t load_mips; uint32_t load_memory_size; - - /** length of the keyphrase in milliseconds */ - uint32_t keyphrase_length; + /** time in ms after which detection is activated */ + uint32_t preamble_time; /** activation right shift, determines the speed of activation */ uint16_t activation_shift; @@ -28,8 +27,11 @@ struct sof_detect_test_config { /** activation threshold */ int16_t activation_threshold; + /** default draining size in bytes */ + uint32_t history_depth; + /** reserved for future use */ - uint32_t reserved[3]; + uint32_t reserved[2]; } __attribute__((packed)); /** used for binary blob size sanity checks */ diff --git a/src/platform/imx8/include/platform/platform.h b/src/platform/imx8/include/platform/platform.h index c8778687b62c..96c8357d0771 100644 --- a/src/platform/imx8/include/platform/platform.h +++ b/src/platform/imx8/include/platform/platform.h @@ -10,6 +10,7 @@ #include #include +#include struct sof; diff --git a/test/cmocka/src/audio/kpb/kpb_mock.c b/test/cmocka/src/audio/kpb/kpb_mock.c index a803bab161ad..7b0eae9cddc9 100644 --- a/test/cmocka/src/audio/kpb/kpb_mock.c +++ b/test/cmocka/src/audio/kpb/kpb_mock.c @@ -14,6 +14,7 @@ #include #include #include +#include TRACE_IMPL() @@ -73,3 +74,18 @@ void __panic(uint32_t p, char *filename, uint32_t linenum) (void)filename; (void)linenum; } + +uint64_t platform_timer_get(struct timer *timer) +{ + (void)timer; + + return 0; +} + +uint64_t clock_ms_to_ticks(int clock, uint64_t ms) +{ + (void)clock; + (void)ms; + + return 0; +} diff --git a/test/cmocka/src/audio/kpb/kpb_mock.h b/test/cmocka/src/audio/kpb/kpb_mock.h index b9fbdc738a28..5f3e67d79dfd 100644 --- a/test/cmocka/src/audio/kpb/kpb_mock.h +++ b/test/cmocka/src/audio/kpb/kpb_mock.h @@ -6,3 +6,5 @@ */ int comp_set_state(struct comp_dev *dev, int cmd); + +struct timer *platform_timer; diff --git a/tools/topology/m4/detect_test_coef.m4 b/tools/topology/m4/detect_test_coef.m4 index 6358be7ad8ec..bf02e54d3577 100644 --- a/tools/topology/m4/detect_test_coef.m4 +++ b/tools/topology/m4/detect_test_coef.m4 @@ -5,7 +5,7 @@ CONTROLBYTES_PRIV(DETECTOR_priv, ` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' ` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' ` 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,' -` 0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x00,' -` 0x03,0x00,0xff,0x3f,0x00,0x00,0x00,0x00,' +` 0x00,0x00,0x00,0x00,0x34,0x08,0x00,0x00,' +` 0x03,0x00,0xff,0x3f,0x34,0x08,0x00,0x00,' ` 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00"' )