Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions src/audio/detect_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Comment thread
mrajwa marked this conversation as resolved.
Resolved
#define KEYPHRASE_DEFAULT_PREAMBLE_LENGTH 0

struct comp_data {
struct sof_detect_test_config config;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Comment thread
mrajwa marked this conversation as resolved.
Resolved
/* 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;

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 *

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the naming "keyphrase_length" sounds more reasonable to me. :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or "keyphrase_ms"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the naming "keyphrase_length" sounds more reasonable to me. :P

Hah really? To me "keyphrase length" is just confusing. It suggest how much buffered data we want to get which is what history_depth does. preamble_time however clearly says "After what time detection shall be activated?"

@lgirdwood what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrajwa do you mean that those data before preamble_time will be ignored and dropped?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@keyonjie, they are ignored but not dropped - they will always be stored in history buffer inside KPB but detector won't trigger detection before preamble time is over.

(dev->params.rate / 1000);
} else {
cd->keyphrase_samples = KEYPHRASE_DEFAULT_PREAMBLE_LENGTH;
Expand Down
135 changes: 107 additions & 28 deletions src/audio/kpb.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <sof/list.h>
#include <sof/audio/buffer.h>
#include <sof/ut.h>
#include <sof/clk.h>

/* KPB private data, runtime data */
struct comp_data {
Expand All @@ -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) */
Comment thread
mrajwa marked this conversation as resolved.
Resolved
struct hb *history_buffer;
bool is_internal_buffer_full;
size_t buffered_data;
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand All @@ -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;

Expand All @@ -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;
Expand All @@ -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);
Comment thread
mrajwa marked this conversation as resolved.
Resolved

/* 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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How shall we handle it once we are running with 24bit captured data? better to get it from fmt?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The support for more sampling options will be provided as separate patch.

*dest = *src;
src++;
j++;
}
}
}

/**
* \brief Initialize history buffer by zeroing its memory.
* \param[in] buff - pointer to current history buffer.
Expand Down Expand Up @@ -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 = {
Expand Down
9 changes: 6 additions & 3 deletions src/include/sof/audio/kpb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 */
Expand Down Expand Up @@ -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. */
Expand Down
10 changes: 6 additions & 4 deletions src/include/user/detect_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@ 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;

/** 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 */
Expand Down
1 change: 1 addition & 0 deletions src/platform/imx8/include/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <sof/platform.h>
#include <platform/interrupt.h>
#include <platform/clk.h>

struct sof;

Expand Down
Loading