Skip to content

Commit

Permalink
Audio: Host: Add triangle generate and glicth detect
Browse files Browse the repository at this point in the history
The playback stream is replaced with a triangle wave. The
capture stream is passed but checked for glitches.

Signed-off-by: Seppo Ingalsuo <[email protected]>
  • Loading branch information
singalsu committed Feb 6, 2024
1 parent cce60bd commit 4c06cc4
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 4 deletions.
17 changes: 17 additions & 0 deletions src/audio/copier/host_copier.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ struct hc_buf {
uint32_t current_end;
};

struct host_triangle_generator_state {
int32_t prev_pcm_value[SOF_IPC_MAX_CHANNELS];
int32_t pcm_increment[SOF_IPC_MAX_CHANNELS];
int32_t countdown;
};

struct host_glitch_detect_state {
int32_t prev_pcm_value[SOF_IPC_MAX_CHANNELS];
int32_t glitch_count[SOF_IPC_MAX_CHANNELS];
int32_t ignore_count;
bool first_value;
};

/**
* \brief Host component data.
*
Expand All @@ -47,6 +60,10 @@ struct hc_buf {
* host_size is the host buffer size (in bytes) specified in the IPC parameters.
*/
struct host_data {
#if CONFIG_ZEPHYR_SIMULATED_HOST_DRIVER
struct host_triangle_generator_state triangle;
struct host_glitch_detect_state glitch;
#endif
/* local DMA config */
struct dma *dma;
struct dma_chan_data *chan;
Expand Down
282 changes: 278 additions & 4 deletions src/audio/simulated-host-zephyr.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#include "copier/copier.h"
#include "copier/host_copier.h"

#define FAKE_HOST_BEGIN_IGNORE_GLITCHES_COUNT 100 /* Only warn in first # periods */
#define FAKE_HOST_PRODUCE_GLITCHES 0 /* Force glitches to waveform */
#define FAKE_HOST_PRODUCE_GLITCH_RATE 5000 /* Glitch every # periods */

static const struct comp_driver comp_host;

LOG_MODULE_REGISTER(host_comp, CONFIG_SOF_LOG_LEVEL);
Expand Down Expand Up @@ -230,6 +234,252 @@ static int host_copy_one_shot(struct host_data *hd, struct comp_dev *dev, copy_c
}
#endif

static int host_detect_glitch(struct comp_dev *dev,
struct host_glitch_detect_state *gd,
struct comp_buffer *source_buffer,
struct comp_buffer *sink_buffer,
dma_process_func process,
uint32_t sink_bytes)
{
struct audio_stream *source = &source_buffer->stream;
struct audio_stream *sink = &sink_buffer->stream;
int32_t value, delta;
int32_t *x32;
int16_t *x16;
int source_bytes;
int channels;
int frames;
int samples;
enum sof_ipc_frame source_format;
int c, i, n, nmax, ret;
int processed = 0;
bool glitch = false;

source_format = audio_stream_get_frm_fmt(source);
switch (source_format) {
case SOF_IPC_FRAME_S16_LE:
case SOF_IPC_FRAME_S24_4LE:
case SOF_IPC_FRAME_S32_LE:
break;
default:
return -EINVAL;
}

frames = sink_bytes / audio_stream_frame_bytes(sink);
channels = audio_stream_get_channels(source);
samples = frames * channels;
source_bytes = audio_stream_sample_bytes(source) * samples;

comp_dbg(dev, "host_detect_glitch() frames = %d, channels = %d, source_bytes = %d",
frames, channels, source_bytes);

buffer_stream_invalidate(source_buffer, source_bytes);

if (source_format == SOF_IPC_FRAME_S16_LE) {
x16 = audio_stream_get_rptr(source);
c = 0;
while (processed < samples) {
nmax = samples - processed;
n = audio_stream_bytes_without_wrap(source, x16) >> 2;
n = MIN(n, nmax);
for (i = 0; i < n; i++) {
value = *x16;
delta = gd->prev_pcm_value[c] - value;
if (!gd->first_value && ABS(delta) > 1) {
gd->glitch_count[c]++;
glitch = true;
}

gd->prev_pcm_value[c] = value;
x16++;
c++;
if (c == channels) {
gd->first_value = false;
c = 0;
}
}
x16 = audio_stream_wrap(source, x16);
processed += n;
}
} else {
x32 = audio_stream_get_rptr(source);
c = 0;
while (processed < samples) {
nmax = samples - processed;
n = audio_stream_bytes_without_wrap(source, x32) >> 2;
n = MIN(n, nmax);
for (i = 0; i < n; i++) {
value = *x32;
delta = gd->prev_pcm_value[c] - value;
if (!gd->first_value && ABS(delta) > 1) {
comp_dbg(dev, "host_detect_glitch(), current %d, previous %d",
value, gd->prev_pcm_value[c]);
gd->glitch_count[c]++;
glitch = true;
}

gd->prev_pcm_value[c] = value;
x32++;
c++;
if (c == channels) {
gd->first_value = false;
c = 0;
}
}
x32 = audio_stream_wrap(source, x32);
processed += n;
}
}

/* Then same as dma_buffer_copy_to get to user "real" capture data */

/* process data */
ret = process(source, 0, sink, 0, samples);

/* sink buffer contains data meant to copied to DMA */
audio_stream_writeback(sink, sink_bytes);

/*
* produce to sink using audio_stream API because this buffer doesn't
* appear in topology so notifier event is not needed
*/
audio_stream_produce(sink, sink_bytes);
comp_update_buffer_consume(source_buffer, source_bytes);

if (gd->ignore_count > 0)
gd->ignore_count--;

if (glitch) {
if (gd->ignore_count)
comp_warn(dev, "host_detect_glitch(): Glitches count for channels %d %d %d %d",
gd->glitch_count[0], gd->glitch_count[1],
gd->glitch_count[2], gd->glitch_count[3]);
else
comp_err(dev, "host_detect_glitch(): Glitches count for channels %d %d %d %d",
gd->glitch_count[0], gd->glitch_count[1],
gd->glitch_count[2], gd->glitch_count[3]);
}

return ret;
}

static int host_generate_triangle(struct comp_dev *dev,
struct host_triangle_generator_state *tg,
struct comp_buffer *source_buffer,
struct comp_buffer *sink_buffer,
uint32_t source_bytes)
{
struct audio_stream *source = &source_buffer->stream;
struct audio_stream *sink = &sink_buffer->stream;
int32_t max_val;
int32_t next;
int32_t *y32;
int16_t *y16;
int sink_bytes;
int channels;
int frames;
int samples;
enum sof_ipc_frame sink_format;
int c, i, n, nmax;
int processed = 0;

sink_format = audio_stream_get_frm_fmt(sink);
switch (sink_format) {
case SOF_IPC_FRAME_S16_LE:
max_val = INT16_MAX;
break;
case SOF_IPC_FRAME_S24_4LE:
max_val = INT32_MAX;
break;
case SOF_IPC_FRAME_S32_LE:
max_val = INT32_MAX;
break;
default:
return -EINVAL;
}

frames = source_bytes / audio_stream_frame_bytes(source);
channels = audio_stream_get_channels(sink);
samples = frames * channels;
sink_bytes = audio_stream_sample_bytes(sink) * samples;

comp_dbg(dev, "host_produce_triangle() frames = %d, channels = %d, sink_bytes = %d",
frames, channels, sink_bytes);

/* source buffer contains data copied by DMA, discard it */
audio_stream_invalidate(source, source_bytes);

#if FAKE_HOST_PRODUCE_GLITCHES
tg->countdown--;
#endif
if (sink_format == SOF_IPC_FRAME_S16_LE) {
y16 = audio_stream_get_wptr(sink);
c = 0;
while (processed < samples) {
nmax = samples - processed;
n = audio_stream_bytes_without_wrap(sink, y16) >> 1;
n = MIN(n, nmax);
for (i = 0; i < n; i++) {
next = tg->prev_pcm_value[c] + tg->pcm_increment[c];
tg->prev_pcm_value[c] = next;
if (next == max_val || next == -max_val)
tg->pcm_increment[c] = -tg->pcm_increment[c];
#if FAKE_HOST_PRODUCE_GLITCHES
if (!tg->countdown)
next = 0;
#endif
*y16 = next;
y16++;
c++;
if (c == channels)
c = 0;
}
y16 = audio_stream_wrap(sink, y16);
processed += n;
}
} else {
y32 = audio_stream_get_wptr(sink);
c = 0;
while (processed < samples) {
nmax = samples - processed;
n = audio_stream_bytes_without_wrap(sink, y32) >> 2;
n = MIN(n, nmax);
for (i = 0; i < n; i++) {
next = tg->prev_pcm_value[c] + tg->pcm_increment[c];
tg->prev_pcm_value[c] = next;
if (next == max_val || next == -max_val)
tg->pcm_increment[c] = -tg->pcm_increment[c];
#if FAKE_HOST_PRODUCE_GLITCHES
if (!tg->countdown)
next = 0;
#endif
*y32 = next;
y32++;
c++;
if (c == channels)
c = 0;
}
y32 = audio_stream_wrap(sink, y32);
processed += n;
}
}

#if FAKE_HOST_PRODUCE_GLITCHES
if (!tg->countdown)
tg->countdown = FAKE_HOST_PRODUCE_GLITCH_RATE;
#endif

buffer_stream_writeback(sink_buffer, sink_bytes);

/*
* consume source using audio_stream API because this buffer doesn't
* appear in topology so notifier event is not needed
*/
audio_stream_consume(source, source_bytes);
comp_update_buffer_produce(sink_buffer, sink_bytes);
return 0;
}

void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t bytes)
{
struct comp_buffer *source;
Expand All @@ -241,11 +491,13 @@ void host_common_update(struct host_data *hd, struct comp_dev *dev, uint32_t byt
if (dev->direction == SOF_IPC_STREAM_PLAYBACK) {
source = hd->dma_buffer;
sink = hd->local_buffer;
ret = dma_buffer_copy_from(source, sink, hd->process, bytes);
ret = host_generate_triangle(dev, &hd->triangle, source, sink, bytes);
// ret = dma_buffer_copy_from(source, sink, hd->process, bytes);
} else {
source = hd->local_buffer;
sink = hd->dma_buffer;
ret = dma_buffer_copy_to(source, sink, hd->process, bytes);
ret = host_detect_glitch(dev, &hd->glitch, source, sink, hd->process, bytes);
// ret = dma_buffer_copy_to(source, sink, hd->process, bytes);
}

if (ret < 0) {
Expand Down Expand Up @@ -634,7 +886,7 @@ static struct comp_dev *host_new(const struct comp_driver *drv,
const struct ipc_config_host *ipc_host = spec;
int ret;

comp_cl_dbg(&comp_host, "host_new()");
comp_cl_warn(&comp_host, "host_new(), Simulated host, no real playback or capture happens");

dev = comp_alloc(drv, sizeof(*dev));
if (!dev)
Expand Down Expand Up @@ -973,6 +1225,28 @@ static int host_params(struct comp_dev *dev,

int host_common_prepare(struct host_data *hd)
{
struct host_triangle_generator_state *tg = &hd->triangle;
struct host_glitch_detect_state *gd = &hd->glitch;
int i;

/* Start PCM code for channels, 0, 0, 1000, 1000, ...
* Channels 0, 2, ... count up
* Channels 1, 3, ... count down
* Count direction becomes opposite when max PCM code value is reached
*/
for (i = 0; i < SOF_IPC_MAX_CHANNELS; i++) {
gd->prev_pcm_value[i] = 0;
tg->prev_pcm_value[i] = (i >> 1) * 1000;
if ((i & 1) == 0)
tg->pcm_increment[i] = 1;
else
tg->pcm_increment[i] = -1;
}

gd->first_value = true;
gd->ignore_count = FAKE_HOST_BEGIN_IGNORE_GLITCHES_COUNT;
tg->countdown = FAKE_HOST_PRODUCE_GLITCH_RATE;

buffer_zero(hd->dma_buffer);
return 0;
}
Expand All @@ -982,7 +1256,7 @@ static int host_prepare(struct comp_dev *dev)
struct host_data *hd = comp_get_drvdata(dev);
int ret;

comp_dbg(dev, "host_prepare()");
comp_warn(dev, "host_prepare(), Simulated host, no real playback or capture happens");

ret = comp_set_state(dev, COMP_TRIGGER_PREPARE);
if (ret < 0)
Expand Down

0 comments on commit 4c06cc4

Please sign in to comment.