summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bbuffer.c2
-rw-r--r--src/bbuffer.h4
-rw-r--r--src/bufferq.c18
-rw-r--r--src/bufferq.h3
-rw-r--r--src/common.c2
-rw-r--r--src/converter.c58
-rw-r--r--src/converter.h12
-rw-r--r--src/oss.c57
-rw-r--r--src/sydney.h147
9 files changed, 185 insertions, 118 deletions
diff --git a/src/bbuffer.c b/src/bbuffer.c
index 86bc5ee..733db4d 100644
--- a/src/bbuffer.c
+++ b/src/bbuffer.c
@@ -48,7 +48,7 @@ void sa_bbuffer_done(sa_bbuffer_t *b) {
b->size = NULL;
}
-void* sa_bbuffer_get(sa_bbuffer_t *b, unsigned channel, size_t size, int interleave) {
+void* sa_bbuffer_get(sa_bbuffer_t *b, unsigned channel, size_t size, sa_bool_t interleave) {
sa_assert(b);
sa_assert(channel < b->nchannels);
sa_assert(size > 0);
diff --git a/src/bbuffer.h b/src/bbuffer.h
index 6394a11..0c5731b 100644
--- a/src/bbuffer.h
+++ b/src/bbuffer.h
@@ -3,6 +3,8 @@
#include <sys/types.h>
+#include "macro.h"
+
/* Simple bounce buffer management routines */
typedef struct sa_bbuffer {
@@ -14,6 +16,6 @@ typedef struct sa_bbuffer {
int sa_bbuffer_init(sa_bbuffer_t *b, unsigned nchannels, size_t sample_size);
void sa_bbuffer_done(sa_bbuffer_t *b);
-void* sa_bbuffer_get(sa_bbuffer_t *b, unsigned channel, size_t size, int interleave);
+void* sa_bbuffer_get(sa_bbuffer_t *b, unsigned channel, size_t size, sa_bool_t interleave);
#endif
diff --git a/src/bufferq.c b/src/bufferq.c
index bafcaa1..46f9bbc 100644
--- a/src/bufferq.c
+++ b/src/bufferq.c
@@ -10,13 +10,11 @@
#define SA_BUFFERQ_ITEM_CONCAT_DATA(x) ((void*) ((uint8_t*) (x) + SA_ALIGN(sizeof(sa_bufferq_item_t))))
-int sa_bufferq_init(sa_bufferq_t *q, unsigned nchannels, size_t sample_size) {
+int sa_bufferq_init(sa_bufferq_t *q, unsigned nchannels) {
sa_assert(q);
- sa_assert(sample_size > 0);
sa_assert(nchannels > 0);
memset(q, 0, sizeof(*q));
- q->sample_size = sample_size;
q->nchannels = nchannels;
if (!(q->items = sa_new0(sa_bufferq_item_t*, nchannels)))
@@ -175,7 +173,7 @@ int sa_bufferq_push(sa_bufferq_t *q, unsigned channel, const void *data, size_t
}
void sa_bufferq_get(sa_bufferq_t *q, void *i[], size_t *bytes) {
- int first = 1;
+ sa_bool_t first = TRUE;
unsigned u;
sa_assert(q);
@@ -193,7 +191,7 @@ void sa_bufferq_get(sa_bufferq_t *q, void *i[], size_t *bytes) {
if (first) {
*bytes = l;
- first = 0;
+ first = FALSE;
} else
*bytes = l < *bytes ? l : *bytes;
} else {
@@ -205,7 +203,7 @@ void sa_bufferq_get(sa_bufferq_t *q, void *i[], size_t *bytes) {
if (first) {
*bytes = l;
- first = 0;
+ first = FALSE;
} else
*bytes = l < *bytes ? l : *bytes;
}
@@ -215,7 +213,6 @@ void sa_bufferq_get(sa_bufferq_t *q, void *i[], size_t *bytes) {
}
}
-
void sa_bufferq_drop(sa_bufferq_t *q, int64_t bytes) {
unsigned u;
@@ -244,19 +241,22 @@ void sa_bufferq_drop(sa_bufferq_t *q, int64_t bytes) {
int sa_bufferq_realloc(sa_bufferq_t *q) {
unsigned u;
- int fail = 0;
+ sa_bool_t fail = FALSE;
sa_assert(q);
for (u = 0; u < q->nchannels; u++) {
sa_bufferq_item_t *i;
+ /* FIXME: This goes through the list linearly. We might need
+ * to optimize this a little */
+
for (i = q->items[u]; i; i = i->bufferq_next) {
if (i->type != SA_BUFFERQ_ITEM_STATIC)
continue;
if (!bufferq_item_make_writable(i)) {
- fail = 1;
+ fail = TRUE;
/* Hmm, we couldn't allocate memory, but we can't
* return without doing anything, hence let's at least
diff --git a/src/bufferq.h b/src/bufferq.h
index 2d53a45..0f87fc9 100644
--- a/src/bufferq.h
+++ b/src/bufferq.h
@@ -25,11 +25,10 @@ typedef struct sa_bufferq {
SA_LLIST_HEAD(sa_bufferq_item_t, *items);
sa_bufferq_item_t **last;
int64_t read_index, write_index, end_index;
- size_t sample_size;
unsigned nchannels;
} sa_bufferq_t;
-int sa_bufferq_init(sa_bufferq_t *q, unsigned nchannels, size_t sample_size);
+int sa_bufferq_init(sa_bufferq_t *q, unsigned nchannels);
void sa_bufferq_done(sa_bufferq_t *q);
diff --git a/src/common.c b/src/common.c
index b366563..0187990 100644
--- a/src/common.c
+++ b/src/common.c
@@ -166,7 +166,7 @@ int sa_stream_destroy(sa_stream_t *s) {
int ret;
unsigned u;
- sa_return_val_if_fail_mutex(s->mutex, s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
ret = driver_destroy(s);
diff --git a/src/converter.c b/src/converter.c
index d895d83..90dba34 100644
--- a/src/converter.c
+++ b/src/converter.c
@@ -21,7 +21,7 @@
#include "malloc.h"
#include "resample.h"
-/* Steps: byteswap -> convert -> volscale -> remap -> resample -> convert -> byteswap -> interleave */
+/* Steps: byteswap -> convert -> volscale -> remap -> resample -> convert -> byteswap -> interleave|serialize */
/* Sample formats we know to process natively */
static int native_pcm_format_process(sa_pcm_format_t f) {
@@ -333,6 +333,8 @@ int sa_converter_init(
if (sa_bbuffer_init(&c->bb_tmp, 1, 1) < 0)
goto fail;
+ if (c
+
return 0;
fail:
@@ -391,8 +393,8 @@ void* sa_converter_get_zero_buffer(sa_converter_t *c, size_t size) {
int sa_converter_go(
sa_converter_t *c,
- const void *const src[], const size_t sstr[], sa_bool_t sinterleave,
- void **dst[], size_t *dstr[], sa_bool_t dinterleave,
+ const void *const src[], const size_t sstr[], sa_interleave_t sinterleave,
+ void **dst[], size_t *dstr[], sa_interleave_t dinterleave,
size_t *size) {
size_t* stride;
@@ -406,8 +408,6 @@ int sa_converter_go(
is_bounce = FALSE;
stride = (size_t*) sstr;
process_data = (void**) src;
- interleave = !!sinterleave;
- dinterleave = !!dinterleave;
if (c->no_volume &&
!c->remap_required &&
@@ -422,12 +422,12 @@ int sa_converter_go(
if (c->pre_byteswap_func) {
size_t k;
- k = dinterleave ? c->from_sample_size*c->from_nchannels : c->from_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->from_sample_size*c->from_nchannels : c->from_sample_size;
for (i = 0; i < c->from_nchannels; i++) {
void *b;
- if (!(b = sa_bbuffer_get(&c->bb_pre_byteswap, i, *size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_pre_byteswap, i, *size, dinterleave == SA_INTERLEAVE_YES)))
return SA_ERROR_OOM;
c->pre_byteswap_func(b, k, process_data[i], stride[i], *size);
@@ -455,12 +455,12 @@ int sa_converter_go(
size_t k, new_size;
new_size = *size / c->from_sample_size * c->work_sample_size;
- k = dinterleave ? c->work_sample_size*c->from_nchannels : c->work_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->work_sample_size*c->from_nchannels : c->work_sample_size;
for (i = 0; i < c->from_nchannels; i++) {
void *b;
- if (!(b = sa_bbuffer_get(&c->bb_pre_format, i, new_size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_pre_format, i, new_size, dinterleave == SA_INTERLEAVE_YES)))
return SA_ERROR_OOM;
c->pre_format_func(&c->bb_tmp, b, k, process_data[i], stride[i], *size);
@@ -491,12 +491,12 @@ int sa_converter_go(
} else {
size_t k;
- k = dinterleave ? c->work_sample_size*c->from_nchannels : c->work_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->work_sample_size*c->from_nchannels : c->work_sample_size;
for (i = 0; i < c->from_nchannels; i++) {
void *b;
- if (!(b = sa_bbuffer_get(&c->bb_volscale, i, *size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_volscale, i, *size, dinterleave == SA_INTERLEAVE_YES)))
return SA_ERROR_OOM;
c->volscale_func(b, k, process_data[i], stride[i], c->volume_factor[i], c->volume_divisor[i], *size);
@@ -513,9 +513,9 @@ int sa_converter_go(
if (c->remap_required) {
size_t k;
- int need_proper_interleave = 0;
+ sa_bool_t need_proper_interleave = FALSE;
- k = dinterleave ? c->work_sample_size*c->to_nchannels : c->work_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->work_sample_size*c->to_nchannels : c->work_sample_size;
for (i = 0; i < c->to_nchannels; i++) {
void *b;
@@ -530,20 +530,20 @@ int sa_converter_go(
c->to_process_data[i] = b;
c->to_stride[i] = c->work_sample_size;
- need_proper_interleave = 1;
+ need_proper_interleave = TRUE;
} else if (p[1] == -1) {
/* Just one channel, nothing to mix */
c->to_process_data[i] = process_data[p[0]];
c->to_stride[i] = stride[p[0]];
- need_proper_interleave = 1;
+ need_proper_interleave = TRUE;
} else {
int j;
/* We have to mix two or more channels */
- if (!(b = sa_bbuffer_get(&c->bb_remap, i, *size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_remap, i, *size, dinterleave == SA_INTERLEAVE_YES)))
return SA_ERROR_OOM;
c->add_func(b, k, process_data[p[0]], stride[p[0]], process_data[p[1]], stride[p[1]], *size);
@@ -558,7 +558,7 @@ int sa_converter_go(
process_data = c->to_process_data;
stride = c->to_stride;
- interleave = need_proper_interleave ? -1 : dinterleave;
+ interleave = need_proper_interleave ? SA_INTERLEAVE_ANY : dinterleave;
is_bounce = TRUE;
}
@@ -566,14 +566,14 @@ int sa_converter_go(
size_t k;
size_t new_size;
- k = dinterleave ? c->work_sample_size*c->to_nchannels : c->work_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->work_sample_size*c->to_nchannels : c->work_sample_size;
new_size = (size_t) (((((uint64_t) *size+c->work_sample_size-1)/c->work_sample_size)*c->to_rate)/c->from_rate+1)*c->work_sample_size; /* FIXME */
for (i = 0; i < c->to_nchannels; i++) {
void *b;
- if (!(b = sa_bbuffer_get(&c->bb_resample, i, new_size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_resample, i, new_size, dinterleave == SA_INTERLEAVE_YES)))
return SA_ERROR_OOM;
c->resample_func(c->speex, i, b, k, process_data[i], stride[i], *size, &new_size);
@@ -601,13 +601,13 @@ int sa_converter_go(
} else {
size_t k, new_size;
- k = dinterleave ? c->to_sample_size*c->to_nchannels : c->to_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->to_sample_size*c->to_nchannels : c->to_sample_size;
new_size = *size / c->work_sample_size * c->to_sample_size;
for (i = 0; i < c->to_nchannels; i++) {
void *b;
- if (!(b = sa_bbuffer_get(&c->bb_post_format, i, new_size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_post_format, i, new_size, dinterleave == SA_INTERLEAVE_YES)))
return SA_ERROR_OOM;
c->post_format_func(&c->bb_tmp, b, k, process_data[i], stride[i], *size);
@@ -636,12 +636,12 @@ int sa_converter_go(
} else {
size_t k;
- k = dinterleave ? c->to_sample_size*c->to_nchannels : c->to_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->to_sample_size*c->to_nchannels : c->to_sample_size;
for (i = 0; i < c->to_nchannels; i++) {
void *b;
- if (!(b = sa_bbuffer_get(&c->bb_post_byteswap, i, *size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_post_byteswap, i, *size, dinterleave == SA_INTERLEAVE_YES)))
return SA_ERROR_OOM;
c->post_byteswap_func(b, k, process_data[i], stride[i], *size);
@@ -659,15 +659,15 @@ int sa_converter_go(
do_interleave:
- if (interleave != dinterleave) {
+ if (dinterleave != SA_INTERLEAVE_ANY && interleave != dinterleave) {
size_t k;
- k = dinterleave ? c->to_sample_size*c->to_nchannels : c->to_sample_size;
+ k = dinterleave == SA_INTERLEAVE_YES ? c->to_sample_size*c->to_nchannels : c->to_sample_size;
for (i = 0; i < c->to_nchannels; i++) {
void *b;
- if (!(b = sa_bbuffer_get(&c->bb_interleave, i, *size, dinterleave)))
+ if (!(b = sa_bbuffer_get(&c->bb_interleave, i, *size, dinterleave == SA_INTERLEAVE_ANY)))
return SA_ERROR_OOM;
c->interleave_func(b, k, process_data[i], stride[i], *size);
@@ -723,7 +723,7 @@ void sa_converter_set_volume(sa_converter_t *c, const int32_t vol[]) {
int sa_converter_go_interleaved(
sa_converter_t *c,
const void *const data,
- void **dst[], size_t *dstr[], int dinterleave,
+ void **dst[], size_t *dstr[], sa_bool_t dinterleave,
size_t *size) {
unsigned i;
@@ -731,12 +731,12 @@ int sa_converter_go_interleaved(
unsigned stride = c->from_nchannels * c->from_sample_size;
for (i = 0; i < c->from_nchannels; i++) {
- c->from_process_data[i] = (void*) data;
+ c->from_process_data[i] = (void*) d;
d += c->from_sample_size;
c->from_stride[i] = stride;
}
- return sa_converter_go(c, (const void *const*) c->from_process_data, c->from_stride, 1, dst, dstr, dinterleave, size);
+ return sa_converter_go(c, (const void *const*) c->from_process_data, c->from_stride, SA_INTERLEAVE_YES, dst, dstr, dinterleave, size);
}
void sa_converter_set_ratio(sa_converter_t *c, unsigned rate1, unsigned rate2) {
diff --git a/src/converter.h b/src/converter.h
index 39338c2..a3ec182 100644
--- a/src/converter.h
+++ b/src/converter.h
@@ -14,6 +14,12 @@
typedef struct sa_converter sa_converter_t;
+typedef enum {
+ SA_INTERLEAVE_YES, /* samples are interleaved; left, right, left, right */
+ SA_INTERLEAVE_NO, /* samples are managed in seperate blocks */
+ SA_INTERLEAVE_ANY /* we don't care or don't know if the samples are interleaved or not */
+} sa_interleave_t;
+
struct sa_converter {
sa_pcm_format_t from_pcm_format, to_pcm_format;
unsigned from_nchannels, to_nchannels;
@@ -63,14 +69,14 @@ void sa_converter_done(sa_converter_t *c);
int sa_converter_go(
sa_converter_t *c,
- const void *const src[], const size_t sstr[], int sinterleave,
- void **dst[], size_t *dstr[], int dinterleave,
+ const void *const src[], const size_t sstr[], sa_interleave_t sinterleave,
+ void **dst[], size_t *dstr[], sa_interleave_t dinterleave,
size_t *size);
int sa_converter_go_interleaved(
sa_converter_t *c,
const void *const data,
- void **dst[], size_t *dstr[], int dinterleave,
+ void **dst[], size_t *dstr[], sa_interleave_t dinterleave,
size_t *size);
void sa_converter_set_volume(sa_converter_t *c, const int32_t vol[]);
diff --git a/src/oss.c b/src/oss.c
index 4e9cf4e..410fa45 100644
--- a/src/oss.c
+++ b/src/oss.c
@@ -38,6 +38,8 @@ struct oss_stream {
sa_thread *thread;
int socket_fds[2];
sa_bufferq_t bufferq;
+
+ void *cdata;
};
static int simple_log2(int v) {
@@ -558,7 +560,10 @@ int driver_open(sa_stream_t *s) {
if (s->mode & SA_MODE_WRONLY)
printf("Chosen for write: %u fragments, %u fragsize\n", oss->write_nfragments, oss->write_fragment_size);
- if (sa_bufferq_init(&oss->bufferq, s->pcm_attrs.nchannels, s->pcm_sample_size) < 0)
+ if (sa_bufferq_init(&oss->bufferq, s->ni_enabled ? s->pcm_attrs.nchannels : 1) < 0)
+ goto fail;
+
+ if ((oss->cdata = pa_new(void*, s->ni_enabled ? s->pcm_attrs.nchannels : 1) < 0)
goto fail;
return SA_SUCCESS;
@@ -585,6 +590,8 @@ int driver_destroy(sa_stream_t *s) {
sa_converter_done(&oss->converter_read);
sa_converter_done(&oss->converter_write);
+ sa_free(oss->cdata);
+
sa_free(oss);
}
@@ -603,30 +610,37 @@ static int feed(sa_stream_t *s) {
return ret;
while (w > 0) {
- void* i[1];
size_t nbytes;
uint8_t *d;
- int drop;
+ sa_bool_t drop;
- sa_bufferq_get(&oss->bufferq, i, &nbytes);
+ sa_bufferq_get(&oss->bufferq, cdata, &nbytes);
- if (nbytes) {
+ if (nbytes > 0) {
void **dst;
size_t *stride;
- if ((ret = sa_converter_go_interleaved(&oss->converter_write, i[0], &dst, &stride, 1, &nbytes)))
- return ret;
+ if (s->ni_enabled) {
+
+ if ((ret = sa_converter_go(&oss->converter_write, cdata, &dst, &stride,
- d = dst[0];
- drop = 1;
+ } else {
+
+ if ((ret = sa_converter_go_interleaved(&oss->converter_write, cdata, &dst, &stride, 1, &nbytes)))
+ goto fail;
+
+ d = dst[0];
+ }
+ drop = TRUE;
s->state = SA_STATE_RUNNING;
} else {
nbytes = w > oss->write_fragment_size ? oss->write_fragment_size : w;
- if (!(d = sa_converter_get_zero_buffer(&oss->converter_write, nbytes)))
- return SA_ERROR_OOM;
+ if (!(d = sa_converter_get_zero_buffer(&oss->converter_write, nbytes))) {
+ ret = SA_ERROR_OOM;
+ goto fail;
drop = s->xrun_mode == SA_XRUN_MODE_SPIN;
@@ -637,8 +651,10 @@ static int feed(sa_stream_t *s) {
while (nbytes > 0) {
ssize_t l;
- if ((l = write(oss->fd, d, nbytes)) < 0)
- return SA_ERROR_SYSTEM;
+ if ((l = write(oss->fd, d, nbytes)) < 0) {
+ ret = SA_ERROR_SYSTEM;
+ goto fail;
+ }
sa_assert(l > 0);
@@ -653,12 +669,18 @@ static int feed(sa_stream_t *s) {
}
}
- return SA_SUCCESS;
+ ret = SA_SUCCESS;
+
+fail:
+
+ sa_free(i);
+
+ return ret;
}
enum {
POLLFD_OSS_FD,
- POLLFD_SOCKET_FD,
+ POLLFD_PIPE_FD,
POLLFD_MAX
};
@@ -668,8 +690,8 @@ static void thread_func(void *data) {
oss_stream_t *oss = OSS_STREAM(s);
sigset_t mask;
- sigfillset(&mask);
- pthread_sigmask(SIG_BLOCK, &mask, NULL);
+ sa_assert_se(sigfillset(&mask) == 0);
+ sa_assert_se(pthread_sigmask(SIG_BLOCK, &mask, NULL) == 0);
if (s->callback) {
s->event = SA_EVENT_INIT_THREAD;
@@ -687,6 +709,7 @@ static void thread_func(void *data) {
for (;;) {
int ret;
+
if (poll(pollfds, POLLFD_MAX, -1) < 0) {
if (errno == EINTR)
continue;
diff --git a/src/sydney.h b/src/sydney.h
index eb1a7ec..58324ca 100644
--- a/src/sydney.h
+++ b/src/sydney.h
@@ -5,15 +5,23 @@
extern "C" {
#endif
-/* Requirements:
+/*
+ Requirements & General observations
+
+ - In sync mode, the device will automatically write data so that an
+ initial read causes writes of zeros to be issued to that one can
+ do "while (1); {read(); write()}
+ - All functions are thread-safe and can be called in any thread
+ context. None of the functions is async-signal safe.
+ - It is assumed that duplex streams have a single clock (synchronised)
+ - Property set extensible. To be kept in sync with PulseAudio and libsydney.
+ - Property keys need to be valid UTF-8, text values, too.
+ - Will warn if application.name or application.id not set.
+ - Error codes are returned immediately, as negative integers
+ - application.process.* will be filled in automatically but may be overwritten by the client.
+ They thus should not be used for authentication purposes.
+ - It is recommended to set most properties before the _open() call.
-- In sync mode, the device will automatically write data so that an initial read causes writes
-of zeros to be issued to that one can do "while (1); {read(); write()}
-
-- All functions are thread-safe and can be called in any thread context. None of the functions is
-async-signal safe.
-
-- It is assumed that duplex streams have a single clock (synchronised)
*/
#include <sys/types.h>
@@ -22,15 +30,16 @@ async-signal safe.
/* Detect byte order, based on sys/param.h */
#if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || defined(WIN32)
-# define SA_LITTLE_ENDIAN 1
-# undef SA_BIG_ENDIAN
+#define SA_LITTLE_ENDIAN 1
+#undef SA_BIG_ENDIAN
#elif (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN))
-# undef SA_LITTLE_ENDIAN
-# define SA_BIG_ENDIAN 1
+#undef SA_LITTLE_ENDIAN
+#define SA_BIG_ENDIAN 1
#else
-# error "Cannot determine byte order!"
+#error "Cannot determine byte order!"
#endif
+/** Stream object */
typedef struct sa_stream sa_stream_t;
/** Volume that corresponds to muted in/out */
@@ -96,7 +105,7 @@ typedef enum {
} sa_mode_t;
/** Error codes */
-typedef enum {
+enum {
SA_SUCCESS = 0,
SA_ERROR_NOT_SUPPORTED = -1,
SA_ERROR_INVALID = -2,
@@ -112,7 +121,7 @@ typedef enum {
SA_ERROR_NO_DATA = -11,
SA_ERROR_NO_SPACE = -12,
_SA_ERROR_MAX = -13
-} sa_error_t;
+};
/** Possible events for notifications */
typedef enum {
@@ -130,6 +139,7 @@ typedef enum {
SA_EVENT_INIT_THREAD,
SA_EVENT_NOTIFY,
SA_EVENT_ERROR,
+ SA_EVENT_XRUN,
_SA_EVENT_MAX
} sa_event_t;
@@ -208,6 +218,7 @@ typedef enum {
SA_STATE_INIT,
SA_STATE_RUNNING,
SA_STATE_STOPPED,
+ SA_STATE_DRAINING,
/* put more stuff */
_SA_STATE_MAX
} sa_state_t;
@@ -215,6 +226,7 @@ typedef enum {
typedef enum {
SA_XRUN_MODE_STOP,
SA_XRUN_MODE_SPIN,
+ SA_XRUN_MODE_FILL,
_SA_XRUN_MODE_MAX
} sa_xrun_mode_t;
@@ -224,18 +236,31 @@ typedef enum {
SA_ADJUST_NONE = 0
} sa_adjust_t;
-/* Some kind of meta information. */
-#define SA_META_CLIENT_NAME "sydney.client-name" /* utf-8 */
-#define SA_META_PROCESS_ID "sydney.process-id" /* getpid() */
-#define SA_META_LANGUAGE "sydney.language" /* de_DE and similar */
-
-/* Some kind of meta information. Not filled in */
-#define SA_META_STREAM_NAME "sydney.stream-name" /* utf-8 */
-#define SA_META_ICON_NAME "sydney.icon-name" /* file name (no slashes) */
-#define SA_META_ICON_PNG "sydney.icon-png" /* PNG blob */
-#define SA_META_ROLE "sydney.role" /* one of: "music", "phone", "game", "event" */
-#define SA_META_X11_DISPLAY "sydney.x11-display" /* X11 display */
-#define SA_META_X11_WINDOW "sydney.x11-window" /* X11 window id */
+/** Stream properties */
+#define SA_PROP_MEDIA_NAME "media.name"
+#define SA_PROP_MEDIA_TITLE "media.title"
+#define SA_PROP_MEDIA_ARTIST "media.artist"
+#define SA_PROP_MEDIA_LANGUAGE "media.language"
+#define SA_PROP_MEDIA_FILENAME "media.filename"
+#define SA_PROP_MEDIA_ICON "media.icon"
+#define SA_PROP_MEDIA_ICON_NAME "media.icon_name"
+#define SA_PROP_MEDIA_ROLE "media.role"
+#define SA_PROP_EVENT_ID "event.id"
+#define SA_PROP_EVENT_X11_DISPLAY "event.x11.display"
+#define SA_PROP_EVENT_X11_XID "event.x11.xid"
+#define SA_PROP_EVENT_MOUSE_X "event.mouse.x"
+#define SA_PROP_EVENT_MOUSE_Y "event.mouse.y"
+#define SA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
+#define SA_PROP_APPLICATION_NAME "application.name"
+#define SA_PROP_APPLICATION_ID "application.id"
+#define SA_PROP_APPLICATION_VERSION "application.version"
+#define SA_PROP_APPLICATION_ICON "application.icon"
+#define SA_PROP_APPLICATION_ICON_NAME "application.icon_name"
+#define SA_PROP_APPLICATION_LANGUAGE "application.language"
+#define SA_PROP_APPLICATION_PROCESS_ID "application.process.id"
+#define SA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
+#define SA_PROP_APPLICATION_PROCESS_USER "application.process.user"
+#define SA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
/** Main callback function */
typedef int (*sa_event_callback_t)(sa_stream_t *s, sa_event_t event);
@@ -252,11 +277,11 @@ int sa_stream_open(sa_stream_t *s);
/** Close/destroy everything */
int sa_stream_destroy(sa_stream_t *s);
-/* "Soft" params */
-/* int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size); */
-/* int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size); */
-/* int sa_stream_set_write_user_size(sa_stream_t *s, size_t size); */
-/* int sa_stream_set_read_user_size(sa_stream_t *s, size_t size); */
+/* Buffer params */
+int sa_stream_set_write_latency(sa_stream_t *s, size_t nbytes);
+int sa_stream_set_write_wakeup(sa_stream_t *s, size_t nbytes);
+int sa_stream_set_read_latency(sa_stream_t *s, size_t nbytes);
+int sa_stream_set_read_wakeup(sa_stream_t *s, size_t nbytes);
/** Set the mapping between channels and the loudspeakers */
int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned n);
@@ -270,13 +295,19 @@ int sa_stream_set_non_interleaved(sa_stream_t *s, int enable);
/** Allow that the sample rate may be changed during runtime */
int sa_stream_set_dynamic_rate(sa_stream_t *s, int enabled);
+/** Set the number of PCM channels for input */
+int sa_stream_set_read_pcm_nchannels(sa_stream *s, unsigned nchannels);
+
+/** Set the number of PCM channels for output */
+int sa_stream_set_write_pcm_nchannels(sa_stream *s, unsigned nchannels);
+
/** Select driver */
int sa_stream_set_driver(sa_stream_t *s, const char *driver);
-/** Start callback */
+/** Start callback thread */
int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback);
-/** Start callback */
+/** Start callback thread */
int sa_stream_stop_thread(sa_stream_t *s);
/** Change the device connected to the stream */
@@ -291,9 +322,6 @@ int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned
/** Change the sampling rate */
int sa_stream_change_rate(sa_stream_t *s, unsigned rate);
-/** Change some meta data that is attached to the stream */
-int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size);
-
/** Associate opaque user data */
int sa_stream_change_user_data(sa_stream_t *s, const void *value);
@@ -301,39 +329,37 @@ int sa_stream_change_user_data(sa_stream_t *s, const void *value);
int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction);
int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction);
int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction);
-/* int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction); */
/* Query functions */
-
int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode);
int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size);
int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format);
-int sa_stream_get_rate(sa_stream_t *s, unsigned *rate);
-int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels);
+int sa_stream_get_pcm_rate(sa_stream_t *s, unsigned *rate);
+int sa_stream_get_pcm_nchannels(sa_stream_t *s, int *nchannels);
int sa_stream_get_user_data(sa_stream_t *s, void **value);
-/* int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size); */
-/* int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size); */
-/* int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size); */
-/* int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size); */
-int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned *n);
+int sa_stream_get_write_latency(sa_stream_t *s, size_t *nbytes);
+int sa_stream_get_write_wakeup(sa_stream_t *s, size_t *nbytes);
+int sa_stream_get_read_latency(sa_stream_t *s, size_t *nbytes);
+int sa_stream_get_read_wakeup(sa_stream_t *s, size_t *nbytes);
+int sa_stream_get_pcm_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned *n);
int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode);
int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled);
-int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled);
+int sa_stream_get_pcm_dynamic_rate(sa_stream_t *s, int *enabled);
int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size);
int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size);
int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned *n);
int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned *n);
int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size);
-int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction);
-int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction);
+int sa_stream_get_adjust_pcm_rate(sa_stream_t *s, sa_adjust_t *direction);
+int sa_stream_get_adjust_pcm_nchannels(sa_stream_t *s, sa_adjust_t *direction);
int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction);
-/* int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction); */
+
/** Get current state of the audio device */
int sa_stream_get_state(sa_stream_t *s, sa_state_t *state);
/** Obtain the error code */
-int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error);
+int sa_stream_get_event_error(sa_stream_t *s, int *error);
/** Obtain the notification code */
int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify);
@@ -360,18 +386,18 @@ int sa_stream_pwrite_ni(sa_stream_t *s, unsigned channel, const void *data, size
/** Query how much can be read without blocking */
-int sa_stream_get_read_size(sa_stream_t *s, size_t *size);
+int sa_stream_get_read_size(sa_stream_t *s, size_t *nbytes);
/** Query how much can be written without blocking */
-int sa_stream_get_write_size(sa_stream_t *s, size_t *size);
+int sa_stream_get_write_size(sa_stream_t *s, size_t *nbytes);
/* Control/xrun */
/** Resume playing after a pause */
-int sa_stream_resume(sa_stream_t *s);
+int sa_stream_start(sa_stream_t *s);
/** Pause audio playback (do not empty the buffer) */
-int sa_stream_pause(sa_stream_t *s);
+int sa_stream_stop(sa_stream_t *s);
/** Block until all audio has been played */
int sa_stream_drain(sa_stream_t *s);
@@ -379,6 +405,17 @@ int sa_stream_drain(sa_stream_t *s);
/** Return a human readable error */
const char *sa_strerror(int code);
+/* Stream properties */
+
+/** Update meta data string properties that are attached to this stream. Takes a NULL terminated list of string key/value pairs. */
+int sa_stream_change_props(sa_stream_t *s, ...) SA_GCC_SENTINEL;
+
+/** Update abritrary meta data properties that are attached to this stream */
+int sa_stream_change_prop(sa_stream_t *s, const char *key, const void *data, size_t nbytes);
+
+/** Remove abritrary meta data properties that are attached to this stream */
+int sa_stream_remove_prop(sa_stream_t *s, ...) SA_GCC_SENTINEL;
+
#ifdef __cplusplus
}
#endif