Implemented circular buffer mode in linux output stream driver.
This commit is contained in:
parent
081b19e52c
commit
58c1c3bbd6
|
|
@ -106,6 +106,7 @@ void inputStream_stop(streamId id);
|
||||||
* @param prio: priority of the requester.
|
* @param prio: priority of the requester.
|
||||||
* @param buf: buffer containing the audio samples.
|
* @param buf: buffer containing the audio samples.
|
||||||
* @param length: length of the buffer, in elements.
|
* @param length: length of the buffer, in elements.
|
||||||
|
* @param mode: operation mode of the buffer
|
||||||
* @param sampleRate: sample rate in Hz.
|
* @param sampleRate: sample rate in Hz.
|
||||||
* @return a unique identifier for the stream or -1 if the stream could not be
|
* @return a unique identifier for the stream or -1 if the stream could not be
|
||||||
* opened.
|
* opened.
|
||||||
|
|
@ -138,7 +139,7 @@ stream_sample_t *outputStream_getIdleBuffer(const streamId id);
|
||||||
* synchronisation point, the function returns immediately.
|
* synchronisation point, the function returns immediately.
|
||||||
*
|
*
|
||||||
* @param id: stream identifier.
|
* @param id: stream identifier.
|
||||||
* @param bufChanged: if true, notifies the strem handler that new data has
|
* @param bufChanged: if true, notifies the stream handler that new data has
|
||||||
* been written to the idle section of the data buffer. This field is valid
|
* been written to the idle section of the data buffer. This field is valid
|
||||||
* only in circular double buffered mode.
|
* only in circular double buffered mode.
|
||||||
* @return true if execution was effectively blocked, false if stream is not
|
* @return true if execution was effectively blocked, false if stream is not
|
||||||
|
|
|
||||||
|
|
@ -22,23 +22,62 @@
|
||||||
#include <interfaces/audio_stream.h>
|
#include <interfaces/audio_stream.h>
|
||||||
#include <pulse/error.h>
|
#include <pulse/error.h>
|
||||||
#include <pulse/simple.h>
|
#include <pulse/simple.h>
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// Expand opaque pa_simple struct
|
||||||
|
struct pa_simple {
|
||||||
|
pa_threaded_mainloop *mainloop;
|
||||||
|
pa_context *context;
|
||||||
|
pa_stream *stream;
|
||||||
|
pa_stream_direction_t direction;
|
||||||
|
|
||||||
|
const void *read_data;
|
||||||
|
size_t read_index, read_length;
|
||||||
|
|
||||||
|
int operation_success;
|
||||||
|
};
|
||||||
|
|
||||||
static int priority = PRIO_BEEP; // Current priority
|
static int priority = PRIO_BEEP; // Current priority
|
||||||
static bool running = false; // Stream is running
|
static bool running = false; // Stream is running
|
||||||
pa_simple *s = NULL; // Pulseaudio instance
|
static pa_simple *p = NULL; // Pulseaudio instance
|
||||||
int error; // Error code
|
static int error = 0; // Error code
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_stream_write(s, active_buf, active_buf_len, NULL, 0, PA_SEEK_RELATIVE);
|
||||||
|
}
|
||||||
|
|
||||||
streamId outputStream_start(const enum AudioSink destination,
|
streamId outputStream_start(const enum AudioSink destination,
|
||||||
const enum AudioPriority prio,
|
const enum AudioPriority prio,
|
||||||
stream_sample_t * const buf,
|
stream_sample_t * const buffer,
|
||||||
const size_t length,
|
const size_t length,
|
||||||
const enum BufMode mode,
|
const enum BufMode mode,
|
||||||
const uint32_t sampleRate)
|
const uint32_t sampleRate)
|
||||||
{
|
{
|
||||||
assert(destination == SINK_SPK && "Only speaker sink was implemented!\n");
|
assert(destination == SINK_SPK && "Only speaker sink was implemented!\n");
|
||||||
assert(mode == BUF_LINEAR && "Only linear buffering was implemented!\n");
|
|
||||||
|
|
||||||
/* The Sample format to use */
|
/* The Sample format to use */
|
||||||
static pa_sample_spec ss = {
|
static pa_sample_spec ss = {
|
||||||
|
|
@ -60,28 +99,34 @@ streamId outputStream_start(const enum AudioSink destination,
|
||||||
// Assign priority and set stream as running
|
// Assign priority and set stream as running
|
||||||
priority = prio;
|
priority = prio;
|
||||||
running = true;
|
running = true;
|
||||||
|
buf_mode = mode;
|
||||||
|
buf = buffer;
|
||||||
|
buf_len = length;
|
||||||
|
first_half_active = true;
|
||||||
|
|
||||||
if (!s)
|
if (!p)
|
||||||
{
|
|
||||||
if (!(s = pa_simple_new(NULL,
|
|
||||||
"OpenRTX",
|
|
||||||
PA_STREAM_PLAYBACK,
|
|
||||||
NULL,
|
|
||||||
"playback",
|
|
||||||
&ss,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
&error)))
|
|
||||||
{
|
{
|
||||||
|
// Create a new playback stream
|
||||||
|
if (!(p = pa_simple_new(NULL, "OpenRTX", PA_STREAM_PLAYBACK, NULL, "Audio out", &ss, NULL, NULL, &error))) {
|
||||||
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
|
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_simple_write(s, buf, length, &error) < 0) {
|
if (mode == BUF_LINEAR)
|
||||||
|
{
|
||||||
|
if (pa_simple_write(p, buf, length, &error) < 0) {
|
||||||
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
|
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (mode == BUF_CIRC_DOUBLE)
|
||||||
|
{
|
||||||
|
assert(p->stream && "Invalid PulseAudio Stream!");
|
||||||
|
|
||||||
|
// Register write callback
|
||||||
|
pa_stream_set_write_callback(p->stream, buf_circ_write_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -90,6 +135,12 @@ stream_sample_t *outputStream_getIdleBuffer(const streamId id)
|
||||||
{
|
{
|
||||||
(void) id;
|
(void) id;
|
||||||
|
|
||||||
|
if (buf_mode == BUF_CIRC_DOUBLE)
|
||||||
|
{
|
||||||
|
// Return half of the buffer not currently being read
|
||||||
|
return (!first_half_active) ? buf : buf + buf_len / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,7 +150,7 @@ bool outputStream_sync(const streamId id, const bool bufChanged)
|
||||||
(void) bufChanged;
|
(void) bufChanged;
|
||||||
|
|
||||||
/* Make sure that every single sample was played */
|
/* Make sure that every single sample was played */
|
||||||
if (pa_simple_drain(s, &error) < 0) {
|
if (pa_simple_drain(p, &error) < 0) {
|
||||||
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
|
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +165,7 @@ void outputStream_stop(const streamId id)
|
||||||
(void) id;
|
(void) id;
|
||||||
|
|
||||||
/* Make sure that every single sample was played */
|
/* Make sure that every single sample was played */
|
||||||
if (pa_simple_flush(s, &error) < 0) {
|
if (pa_simple_flush(p, &error) < 0) {
|
||||||
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
|
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +176,6 @@ void outputStream_terminate(const streamId id)
|
||||||
{
|
{
|
||||||
(void) id;
|
(void) id;
|
||||||
|
|
||||||
if (s)
|
if (p)
|
||||||
pa_simple_free(s);
|
pa_simple_free(p);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue