From adbd1f070dc93b71d0191e42746720d06df64e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Izzo?= Date: Sat, 23 Jul 2022 17:27:32 +0200 Subject: [PATCH] Fixed bug in circular buffer management inside linux output stream driver, added unit test for circular buffer mode. TG-220 --- platform/drivers/audio/outputStream_linux.c | 46 +++++++++++++------ tests/unit/play_sine.c | 50 ++++++++++++++++++--- 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/platform/drivers/audio/outputStream_linux.c b/platform/drivers/audio/outputStream_linux.c index b7c3b3f5..5efc857b 100644 --- a/platform/drivers/audio/outputStream_linux.c +++ b/platform/drivers/audio/outputStream_linux.c @@ -47,27 +47,38 @@ static enum BufMode buf_mode; // Buffer operation mode static stream_sample_t *buf = NULL; // Playback buffer static size_t buf_len = 0; // Buffer length static bool first_half_active = true; // Circular addressing mode flag +static size_t offset = 0; // Playback offset static void buf_circ_write_cb(pa_stream *s, size_t length, void *userdata) { (void) userdata; - // TODO: We can play length more bytes of data - // Start playback of the other half - stream_sample_t *active_buf = (first_half_active) ? - buf : buf + buf_len / 2; - first_half_active = !first_half_active; - size_t active_buf_len = buf_len / 2; + size_t remaining = 0; if (!s || length <= 0) return; - if (pa_stream_begin_write(s, (void **) &active_buf, &active_buf_len) < 0) - { - fprintf(stderr, "pa_stream_begin_write() failed: %s", pa_strerror(pa_context_errno(p->context))); - return; - } + if (offset >= buf_len / 2) + first_half_active = false; - pa_stream_write(s, active_buf, active_buf_len, NULL, 0, PA_SEEK_RELATIVE); + remaining = buf_len - offset; + + // We can play all the rest of the buffer + if (length > remaining) + { + pa_stream_write(s, buf + offset, remaining, NULL, 0, PA_SEEK_RELATIVE); + + if(first_half_active == true) + first_half_active = false; + else + first_half_active = true; + + offset = 0; + } + else + { + pa_stream_write(s, buf + offset, length, NULL, 0, PA_SEEK_RELATIVE); + offset += length; + } } streamId outputStream_start(const enum AudioSink destination, @@ -124,6 +135,12 @@ streamId outputStream_start(const enum AudioSink destination, { assert(p->stream && "Invalid PulseAudio Stream!"); + if (pa_simple_write(p, buf, length / 2, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); + return -1; + } + offset = length / 2; + // Register write callback pa_stream_set_write_callback(p->stream, buf_circ_write_cb, NULL); } @@ -155,7 +172,10 @@ bool outputStream_sync(const streamId id, const bool bufChanged) return false; } - running = false; + if (buf_mode == BUF_LINEAR) + { + running = false; + } return true; } diff --git a/tests/unit/play_sine.c b/tests/unit/play_sine.c index 9c581094..d745d670 100644 --- a/tests/unit/play_sine.c +++ b/tests/unit/play_sine.c @@ -24,6 +24,7 @@ #include #include +#include #define BUF_LEN 4096 * 10 #define AMPLITUDE 20000 @@ -37,16 +38,21 @@ int16_t buffer[BUF_LEN] = { 0 }; -int main() +void fill_buffer_sine(int16_t *buffer, size_t len) { - int id = 0; - - // Create 440 Hz sine wave - for(int i = 0; i < BUF_LEN; i++) + for(size_t i = 0; i < len; i++) { buffer[i] = AMPLITUDE * sin(2 * PI * i * ((float) FREQUENCY / SAMPLE_RATE)); } +} + +void play_sine_linear() +{ + int id = 0; + + // Create 440 Hz sine wave + fill_buffer_sine(buffer, BUF_LEN); // Play back sine wave for(int i = 0; i < 10; i++) @@ -64,3 +70,37 @@ int main() outputStream_stop(id); outputStream_terminate(id); } + +void play_sine_circular() +{ + int id = 0; + stream_sample_t *buf = NULL; + + // Fill first half of buffer with sine + fill_buffer_sine(buffer, BUF_LEN / 2); + + // Play back sine wave + id = outputStream_start(SINK_SPK, + PRIO_PROMPT, + buffer, + BUF_LEN, + BUF_CIRC_DOUBLE, + SAMPLE_RATE); + + for(int i = 0; i < 10; i++) + { + buf = outputStream_getIdleBuffer(id); + fill_buffer_sine(buf, BUF_LEN / 2); + outputStream_sync(id, true); + } + + // Sync, flush, terminate + outputStream_stop(id); + outputStream_terminate(id); +} + +int main() +{ + //play_sine_linear(); + play_sine_circular(); +}