summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile9
-rw-r--r--TODO5
-rw-r--r--common.c523
-rw-r--r--common.h58
-rw-r--r--driver.h29
-rw-r--r--macro.h33
-rw-r--r--malloc.c13
-rw-r--r--malloc.h16
-rw-r--r--oss.c424
-rw-r--r--resample.c164
-rw-r--r--sydney.h341
-rw-r--r--test-sine.c27
12 files changed, 1642 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ab247e0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+CFLAGS=-Wall -O0 -g -W -Wno-unused-parameter
+
+all: common.o malloc.o test-sine.o oss.o
+ $(CC) $(CFLAGS) -o $@ $^
+
+*.o: *.h
+
+clean:
+ rm -f *.o
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..3186ba6
--- /dev/null
+++ b/TODO
@@ -0,0 +1,5 @@
+* Replace all "input", "output" with "read", "write"
+* Vielleicht client_name nicht erzwingen, stattdessen /proc/self/exename nehmen o.ä.
+* s/sa_device/sa_stream/
+* adjust channel map
+
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..b7a92d7
--- /dev/null
+++ b/common.c
@@ -0,0 +1,523 @@
+#include "sydney.h"
+#include "macro.h"
+#include "malloc.h"
+#include "common.h"
+#include "driver.h"
+
+int sa_device_create_opaque(sa_device_t **dev, const char *client_name, sa_mode_t mode, const char *codec) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(client_name, SA_ERROR_INVALID);
+ sa_return_val_if_fail(mode == SA_MODE_RDONLY || mode == SA_MODE_WRONLY || mode == SA_MODE_RDWR, SA_ERROR_INVALID);
+ sa_return_val_if_fail(codec, SA_ERROR_INVALID);
+
+ return device_create_opaque(dev, client_name, mode, codec);
+}
+
+int sa_device_create_pcm(sa_device_t **dev, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned channels) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(client_name, SA_ERROR_INVALID);
+ sa_return_val_if_fail(mode == SA_MODE_RDONLY || mode == SA_MODE_WRONLY || mode == SA_MODE_RDWR, SA_ERROR_INVALID);
+ sa_return_val_if_fail(format < SA_PCM_FORMAT_MAX, SA_ERROR_INVALID);
+ sa_return_val_if_fail(rate > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(channels > 0, SA_ERROR_INVALID);
+
+ return device_create_pcm(dev, client_name, mode, format, rate, channels);
+}
+
+int sa_device_open(sa_device_t *dev) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ return device_open(dev);
+}
+
+int sa_device_destroy(sa_device_t *dev) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+
+ return device_destroy(dev);
+}
+
+int sa_device_set_write_lower_watermark(sa_device_t *dev, size_t size) {
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->write_lower_watermark = size;
+ return SA_SUCCESS;
+}
+
+int sa_device_set_read_lower_watermark(sa_device_t *dev, size_t size) {
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->read_lower_watermark = size;
+ return SA_SUCCESS;
+}
+
+int sa_device_set_write_upper_watermark(sa_device_t *dev, size_t size) {
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->write_upper_watermark = size;
+ return SA_SUCCESS;
+}
+
+int sa_device_set_read_upper_watermark(sa_device_t *dev, size_t size) {
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->read_upper_watermark = size;
+ return SA_SUCCESS;
+}
+
+int sa_device_set_channel_map(sa_device_t *dev, const sa_channel_t *map) {
+ const sa_channel_t *c;
+ sa_channel_t *m;
+ unsigned n;
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(map, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+
+ for (c = map, n = dev->nchannels; n > 0; c++, n--)
+ if (*c >= SA_CHANNEL_MAX)
+ return SA_ERROR_INVALID;
+
+ if (!(m = sa_memdup(map, sizeof(sa_channel_t) * dev->nchannels)))
+ return SA_ERROR_OOM;
+
+ sa_free(dev->channel_map);
+ dev->channel_map = m;
+
+ return SA_SUCCESS;
+}
+
+int sa_device_set_xrun_mode(sa_device_t *dev, sa_xrun_mode_t mode) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(mode == SA_XRUN_MODE_STOP || mode == SA_XRUN_MODE_SPIN, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->xrun_mode = mode;
+ return SA_SUCCESS;
+}
+
+int sa_device_set_ni(sa_device_t *dev, int enable) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+
+ dev->ni_enabled = !!enable;
+ return SA_SUCCESS;
+}
+
+int sa_device_set_dsr(sa_device_t *dev, int enable) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+
+ dev->dsr_enabled = !!enable;
+ return SA_SUCCESS;
+}
+
+int sa_device_set_driver(sa_device_t *dev, const char *driver) {
+ char *d;
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(driver, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ if (!(d = sa_strdup(driver)))
+ return SA_ERROR_OOM;
+
+ sa_free(dev->driver);
+ dev->driver = d;
+
+ return SA_SUCCESS;
+}
+
+int sa_device_start_thread(sa_device_t *dev, sa_event_callback_t *callback) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(callback, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ return device_start_thread(dev, callback);
+}
+
+int sa_device_change_device(sa_device_t *dev, const char *device_name) {
+ char *d;
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(device_name, SA_ERROR_INVALID);
+
+ if (dev->state == SA_STATE_INIT) {
+ if (!(d = sa_strdup(device_name)))
+ return SA_ERROR_OOM;
+
+ sa_free(dev->device);
+ dev->device = d;
+
+ return SA_SUCCESS;
+ }
+
+ return device_change_device(dev, device_name);
+}
+
+int sa_device_change_input_volume(sa_device_t *dev, int *vol) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(vol, SA_ERROR_INVALID);
+
+ if (dev->state == SA_STATE_INIT) {
+ dev->input_volume = *vol;
+ return SA_SUCCESS;
+ }
+
+ return device_change_input_volume(dev, vol);
+}
+
+int sa_device_change_output_volume(sa_device_t *dev, int *vol) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(vol, SA_ERROR_INVALID);
+
+ if (dev->state == SA_STATE_INIT) {
+ dev->output_volume = *vol;
+ return SA_SUCCESS;
+ }
+
+ return device_change_output_volume(dev, vol);
+}
+
+int sa_device_change_sampling_rate(sa_device_t *dev, unsigned rate) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(rate > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->dsr_enabled || dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ if (dev->state == SA_STATE_INIT) {
+ dev->rate = rate;
+ return SA_SUCCESS;
+ }
+
+ return device_change_sampling_rate(dev, rate);
+}
+
+int sa_device_change_client_name(sa_device_t *dev, const char *client_name) {
+ char *n;
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(client_name, SA_ERROR_INVALID);
+
+ if (dev->state == SA_STATE_INIT) {
+ if (!(n = sa_strdup(client_name)))
+ return SA_ERROR_OOM;
+
+ sa_free(dev->client_name);
+ dev->client_name = n;
+
+ return SA_SUCCESS;
+ }
+
+ return device_change_client_name(dev, client_name);
+}
+
+int sa_device_change_stream_name(sa_device_t *dev, const char *stream_name) {
+ char *n;
+
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(stream_name, SA_ERROR_INVALID);
+
+ if (dev->state == SA_STATE_INIT) {
+ if (!(n = sa_strdup(stream_name)))
+ return SA_ERROR_OOM;
+
+ sa_free(dev->stream_name);
+ dev->stream_name = n;
+
+ return SA_SUCCESS;
+ }
+
+ return device_change_stream_name(dev, stream_name);
+}
+
+int sa_device_change_user_data(sa_device_t *dev, void *value) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+
+ dev->user_data = value;
+ return SA_SUCCESS;
+}
+
+int sa_device_adjust_rate(sa_device_t *dev, sa_adjust_t direction) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->adjust_rate = direction;
+ return SA_SUCCESS;
+}
+
+int sa_device_adjust_nchannels(sa_device_t *dev, sa_adjust_t direction) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->adjust_rate = direction;
+ return SA_SUCCESS;
+}
+
+int sa_device_adjust_pcm_format(sa_device_t *dev, sa_adjust_t direction) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ dev->adjust_pcm_format = direction;
+ return SA_SUCCESS;
+}
+
+int sa_device_get_state(sa_device_t *dev, sa_state_t *state) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(state, SA_ERROR_INVALID);
+
+ return device_get_state(dev, state);
+}
+
+int sa_device_get_sampling_rate(sa_device_t *dev, unsigned *rate) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(rate, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+
+ *rate = dev->rate;
+ return SA_SUCCESS;
+}
+
+int sa_device_get_nchannels(sa_device_t *dev, int *nchannels) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nchannels, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+
+ *nchannels = dev->nchannels;
+ return SA_SUCCESS;
+}
+
+int sa_device_get_pcm_format(sa_device_t *dev, sa_pcm_format_t *pcm_format) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(pcm_format, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->codec, SA_ERROR_STATE);
+
+ *pcm_format = dev->pcm_format;
+ return SA_SUCCESS;
+}
+
+int sa_device_get_user_data(sa_device_t *dev, void **value) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(value, SA_ERROR_INVALID);
+
+ *value = dev->user_data;
+ return SA_SUCCESS;
+}
+
+int sa_device_get_event_error(sa_device_t *dev, sa_error_t *error) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(error, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->event == SA_EVENT_ERROR, SA_ERROR_STATE);
+
+ *error = dev->error;
+ return SA_SUCCESS;
+}
+
+int sa_device_get_event_notify(sa_device_t *dev, sa_notify_t *notify) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(notify, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->event == SA_EVENT_NOTIFY, SA_ERROR_STATE);
+
+ *notify = dev->notify;
+ return SA_SUCCESS;
+}
+
+int sa_device_get_position(sa_device_t *dev, sa_position_t position, int64_t *pos) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(pos, SA_ERROR_INVALID);
+ sa_return_val_if_fail(position < SA_POSITION_MAX, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_get_position(dev, position, pos);
+}
+
+int sa_device_read(sa_device_t *dev, void *data, size_t nbytes) {
+ return sa_device_pread(dev, data, nbytes, 0, SA_SEEK_RELATIVE);
+}
+
+int sa_device_write(sa_device_t *dev, const void *data, size_t nbytes) {
+ return sa_device_pwrite(dev, data, nbytes, 0, SA_SEEK_RELATIVE);
+}
+
+int sa_device_read_ni(sa_device_t *dev, unsigned channel, void *data, size_t nbytes) {
+ return sa_device_pread_ni(dev, channel, data, nbytes, 0, SA_SEEK_RELATIVE);
+}
+
+int sa_device_write_ni(sa_device_t *dev, unsigned channel, const void *data, size_t nbytes) {
+ return sa_device_pwrite_ni(dev, channel, data, nbytes, 0, SA_SEEK_RELATIVE);
+}
+
+int sa_device_pread(sa_device_t *dev, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_pread(dev, data, nbytes, offset, whence);
+}
+
+int sa_device_pwrite(sa_device_t *dev, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!dev->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_pwrite(dev, data, nbytes, offset, whence);
+}
+
+int sa_device_pread_ni(sa_device_t *dev, unsigned channel, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
+ sa_return_val_if_fail(channel < dev->nchannels, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_pread_ni(dev, channel, data, nbytes, offset, whence);
+}
+
+int sa_device_pwrite_ni(sa_device_t *dev, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
+ sa_return_val_if_fail(channel < dev->nchannels, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_pwrite_ni(dev, channel, data, nbytes, offset, whence);
+}
+
+int sa_device_get_read_size(sa_device_t *dev, size_t *size) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_get_read_size(dev, size);
+}
+
+int sa_device_get_write_size(sa_device_t *dev, size_t *size) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_get_write_size(dev, size);
+}
+
+int sa_device_resume(sa_device_t *dev) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_resume(dev);
+}
+
+int sa_device_pause(sa_device_t *dev) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_pause(dev);
+}
+
+int sa_device_drain(sa_device_t *dev) {
+ sa_return_val_if_fail(dev, SA_ERROR_INVALID);
+ sa_return_val_if_fail(dev->state == SA_STATE_RUNNING || dev->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ return device_drain(dev);
+}
+
+sa_device_t *device_alloc(size_t total) {
+ sa_device_t *d;
+
+ sa_assert(total >= sizeof(sa_device_t));
+
+ if (!(d = sa_malloc0(total)))
+ return NULL;
+
+ /* All fields a carefully chosen in a way that initializing them
+ * NUL bytes is sufficient */
+
+ return d;
+}
+
+void device_free(sa_device_t *d) {
+ sa_free(d->codec);
+ sa_free(d->driver);
+ sa_free(d->device);
+ sa_free(d->channel_map);
+ sa_free(d->client_name);
+ sa_free(d->stream_name);
+ sa_free(d);
+}
+
+int device_alloc_opaque(sa_device_t **dev, size_t total, const char *client_name, sa_mode_t mode, const char *codec) {
+ int error;
+
+ if (!(*dev = device_alloc(total)))
+ return SA_ERROR_OOM;
+
+ (*dev)->mode = mode;
+
+ if (!((*dev)->codec = sa_strdup(codec))) {
+ device_free(*dev);
+ return SA_ERROR_OOM;
+ }
+
+ if ((error = sa_device_change_client_name(*dev, client_name))) {
+ device_free(*dev);
+ return error;
+ }
+
+ return SA_SUCCESS;
+}
+
+int device_alloc_pcm(sa_device_t **dev, size_t total, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned nchannels) {
+ int error;
+
+ if (!(*dev = device_alloc(total)))
+ return SA_ERROR_OOM;
+
+ (*dev)->mode = mode;
+ (*dev)->pcm_format = format;
+ (*dev)->nchannels = nchannels;
+
+ if ((error = sa_device_change_sampling_rate(*dev, rate))) {
+ device_free(*dev);
+ return error;
+ }
+
+ if ((error = sa_device_change_client_name(*dev, client_name))) {
+ device_free(*dev);
+ return error;
+ }
+
+ return SA_SUCCESS;
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..1500ef1
--- /dev/null
+++ b/common.h
@@ -0,0 +1,58 @@
+#ifndef foocommonh
+#define foocommonh
+
+#include "sydney.h"
+
+#define elementsof(x) (sizeof(x)/sizeof((x)[0]))
+
+struct sa_device {
+ sa_mode_t mode;
+
+ sa_pcm_format_t pcm_format;
+ unsigned rate;
+ unsigned nchannels;
+
+ char *codec;
+
+ char *client_name;
+ char *stream_name;
+
+ size_t read_lower_watermark;
+ size_t read_upper_watermark;
+ size_t write_lower_watermark;
+ size_t write_upper_watermark;
+
+ sa_channel_t *channel_map;
+
+ sa_xrun_mode_t xrun_mode;
+ int ni_enabled;
+ int dsr_enabled;
+
+ sa_event_callback_t event_callback;
+
+ char *device;
+ char *driver;
+
+ int input_volume;
+ int output_volume;
+
+ void *user_data;
+
+ sa_state_t state;
+
+ sa_adjust_t adjust_rate;
+ sa_adjust_t adjust_nchannels;
+ sa_adjust_t adjust_pcm_format;
+
+ sa_error_t error;
+ sa_notify_t notify;
+ sa_event_t event;
+};
+
+sa_device_t *device_alloc(size_t total);
+void device_free(sa_device_t *d);
+
+int device_alloc_pcm(sa_device_t **dev, size_t total, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned channels);
+int device_alloc_opaque(sa_device_t **dev, size_t total, const char *client_name, sa_mode_t mode, const char *codec);
+
+#endif
diff --git a/driver.h b/driver.h
new file mode 100644
index 0000000..49bebbd
--- /dev/null
+++ b/driver.h
@@ -0,0 +1,29 @@
+#ifndef foodriverhfoo
+#define foodriverhfoo
+
+#include "sydney.h"
+
+int device_create_opaque(sa_device_t **dev, const char *client_name, sa_mode_t mode, const char *codec);
+int device_create_pcm(sa_device_t **dev, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned channels);
+int device_open(sa_device_t *dev);
+int device_destroy(sa_device_t *dev);
+int device_start_thread(sa_device_t *dev, sa_event_callback_t *callback);
+int device_change_device(sa_device_t *dev, const char *device_name);
+int device_change_input_volume(sa_device_t *dev, int *vol);
+int device_change_output_volume(sa_device_t *dev, int *vol);
+int device_change_sampling_rate(sa_device_t *dev, unsigned rate);
+int device_change_client_name(sa_device_t *dev, const char *client_name);
+int device_change_stream_name(sa_device_t *dev, const char *stream_name);
+int device_get_state(sa_device_t *dev, sa_state_t *state);
+int device_get_position(sa_device_t *dev, sa_position_t position, int64_t *pos);
+int device_pread(sa_device_t *dev, void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+int device_pwrite(sa_device_t *dev, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+int device_pread_ni(sa_device_t *dev, unsigned channel, void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+int device_pwrite_ni(sa_device_t *dev, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+int device_get_read_size(sa_device_t *dev, size_t *size);
+int device_get_write_size(sa_device_t *dev, size_t *size);
+int device_resume(sa_device_t *dev);
+int device_pause(sa_device_t *dev);
+int device_drain(sa_device_t *dev);
+
+#endif
diff --git a/macro.h b/macro.h
new file mode 100644
index 0000000..d2edead
--- /dev/null
+++ b/macro.h
@@ -0,0 +1,33 @@
+#ifndef foomacrohfoo
+#define foomacrohfoo
+
+#include <stdio.h>
+#include <assert.h>
+
+#ifdef __GNUC__
+#define PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#define PRETTY_FUNCTION ""
+#endif
+
+#define sa_return_if_fail(expr) \
+ do { \
+ if (!(expr)) { \
+ fprintf(stderr, PRETTY_FUNCTION ": Assertion <" #expr "> failed.\n"); \
+ return; \
+ } \
+ } while(0)
+
+#define sa_return_val_if_fail(expr, val) \
+ do { \
+ if (!(expr)) { \
+ fprintf(stderr, "%s: Assertion <" #expr "> failed.\n", PRETTY_FUNCTION ); \
+ return (val); \
+ } \
+ } while(0)
+
+#define sa_assert assert
+
+#define sa_assert_not_reached() sa_assert(!"Should not be reached.")
+
+#endif
diff --git a/malloc.c b/malloc.c
new file mode 100644
index 0000000..080eb2f
--- /dev/null
+++ b/malloc.c
@@ -0,0 +1,13 @@
+#include <string.h>
+
+#include "malloc.h"
+
+void* sa_memdup(const void* p, size_t size) {
+ void *r;
+
+ if (!(r = malloc(size)))
+ return NULL;
+
+ memcpy(r, p, size);
+ return r;
+}
diff --git a/malloc.h b/malloc.h
new file mode 100644
index 0000000..df459ed
--- /dev/null
+++ b/malloc.h
@@ -0,0 +1,16 @@
+#ifndef foomallochfoo
+#define foomallochfoo
+
+#include <stdlib.h>
+#include <string.h>
+
+#define sa_malloc malloc
+#define sa_free free
+#define sa_malloc0(size) calloc(1, (size))
+#define sa_strdup strdup
+
+void* sa_memdup(const void* p, size_t size);
+
+#define sa_new(t, n) ((t*) sa_malloc(sizeof(t)*n))
+
+#endif
diff --git a/oss.c b/oss.c
new file mode 100644
index 0000000..f39571c
--- /dev/null
+++ b/oss.c
@@ -0,0 +1,424 @@
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include "sydney.h"
+#include "common.h"
+#include "macro.h"
+#include "malloc.h"
+
+#define DEFAULT_DEVICE "/dev/dsp"
+#define DRIVER_NAME "oss"
+
+typedef struct oss_device oss_device_t;
+#define OSS_DEVICE(x) ((oss_device_t*) (x))
+
+struct oss_device {
+ sa_device_t parent;
+ int fd;
+
+ unsigned real_rate;
+ unsigned real_nchannels;
+ sa_pcm_format_t real_pcm_format;
+};
+
+int device_create_opaque(sa_device_t **dev, const char *client_name, sa_mode_t mode, const char *codec) {
+ int error;
+
+ if ((error = device_alloc_opaque(dev, sizeof(oss_device_t), client_name, mode, codec)))
+ return error;
+
+ OSS_DEVICE(*dev)->fd = -1;
+
+ return SA_SUCCESS;
+}
+
+int device_create_pcm(sa_device_t **dev, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned channels) {
+ int error;
+
+ if ((error = device_alloc_pcm(dev, sizeof(oss_device_t), client_name, mode, format, rate, channels)))
+ return error;
+
+ OSS_DEVICE(*dev)->fd = -1;
+
+ return SA_SUCCESS;
+}
+
+int device_open(sa_device_t *dev) {
+ oss_device_t *oss = OSS_DEVICE(dev);
+ char *n;
+ int f, arg, bs, r, phase, i, found, suggested;
+ unsigned c;
+
+ static const int format_map[SA_PCM_FORMAT_MAX] = {
+ [SA_PCM_FORMAT_U8] = AFMT_U8,
+ [SA_PCM_FORMAT_ULAW] = AFMT_MU_LAW,
+ [SA_PCM_FORMAT_ALAW] = AFMT_A_LAW,
+ [SA_PCM_FORMAT_S16_LE] = AFMT_S16_LE,
+ [SA_PCM_FORMAT_S16_BE] = AFMT_S16_BE,
+ [SA_PCM_FORMAT_S24_LE] = AFMT_S16_NE, /* OSS doesn't know this format, hence we pick the best we can */
+ [SA_PCM_FORMAT_S24_BE] = AFMT_S16_NE,
+ [SA_PCM_FORMAT_S32_LE] = AFMT_S16_NE,
+ [SA_PCM_FORMAT_S32_BE] = AFMT_S16_NE,
+ [SA_PCM_FORMAT_FLOAT32_LE] = AFMT_S16_NE,
+ [SA_PCM_FORMAT_FLOAT32_BE] = AFMT_S16_NE
+ };
+ static const int try_rates[] = { 8000, 16000, 32000, 44100, 48000, 96000, 192000 };
+
+ if (dev->driver && strcmp(dev->driver, DRIVER_NAME))
+ return SA_ERROR_NO_DRIVER;
+
+ sa_assert(oss->fd < 0);
+
+ n = dev->device ? dev->device : DEFAULT_DEVICE;
+ if ((oss->fd = open(n, dev->mode == SA_MODE_RDONLY ? O_RDONLY : (dev->mode == SA_MODE_WRONLY ? O_WRONLY : O_RDWR) | O_NOCTTY | O_NONBLOCK)) < 0) {
+
+ if (errno == ENODEV || errno == ENOENT)
+ return SA_ERROR_NO_DEVICE;
+
+ return SA_ERROR_SYSTEM;
+ }
+
+ if (!dev->device) {
+ if (!(n = sa_strdup(n)))
+ return SA_ERROR_OOM;
+
+ dev->device = n;
+ }
+
+ if (dev->codec) {
+
+ if (strcmp(dev->codec, SA_CODEC_AC3) == 0)
+ f = AFMT_AC3;
+ else if (strcmp(dev->codec, SA_CODEC_MPEG) == 0)
+ f = AFMT_MPEG;
+ else
+ return SA_ERROR_NO_CODEC;
+
+ } else
+ f = format_map[dev->pcm_format];
+
+ bs = 0;
+
+ for (;;) {
+ arg = f;
+
+ if (ioctl(oss->fd, SNDCTL_DSP_SETFMT, &arg) < 0)
+ return SA_ERROR_SYSTEM;
+
+ if (arg == f)
+ break;
+
+ /* Hmm, the device doesn't support what we're looking for,
+ * let's try our luck */
+
+ if (f == AFMT_S16_LE && !bs) {
+ f = AFMT_S16_BE;
+ bs = 1;
+ } else if (f == AFMT_S16_BE && !bs) {
+ f = AFMT_S16_LE;
+ bs = 1;
+ } else if (f == AFMT_S16_LE || f == AFMT_S16_BE) {
+ f = AFMT_MU_LAW;
+ bs = 0;
+ } else if (f == AFMT_MU_LAW && !bs) {
+ f = AFMT_A_LAW;
+ bs = 1;
+ } else if (f == AFMT_A_LAW && !bs) {
+ f = AFMT_MU_LAW;
+ bs = 1;
+ } else if (f == AFMT_A_LAW || f == AFMT_MU_LAW) {
+ f = AFMT_U8;
+ } else if (f == AFMT_AC3 || f == AFMT_MPEG)
+ return SA_ERROR_NO_CODEC;
+ else
+ return SA_ERROR_NO_PCM_FORMAT;
+ }
+
+ if (!dev->codec) {
+
+ switch (f) {
+ case AFMT_MU_LAW:
+ oss->real_pcm_format = SA_PCM_FORMAT_ULAW;
+ break;
+
+ case AFMT_A_LAW:
+ oss->real_pcm_format = SA_PCM_FORMAT_ALAW;
+ break;
+
+ case AFMT_U8:
+ oss->real_pcm_format = SA_PCM_FORMAT_U8;
+ break;
+
+ case AFMT_S16_LE:
+ oss->real_pcm_format = SA_PCM_FORMAT_S16_LE;
+ break;
+
+ case AFMT_S16_BE:
+ oss->real_pcm_format = SA_PCM_FORMAT_S16_BE;
+ break;
+
+ default:
+ sa_assert_not_reached();
+ }
+
+
+ found = 0;
+
+ if (dev->adjust_nchannels >= 0) {
+
+ /* First try more channels ... */
+ for (c = dev->nchannels; c < 16 || c == dev->nchannels; c ++) {
+ arg = c;
+ if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+ return SA_ERROR_SYSTEM;
+
+ if (arg == (int) c) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* ... then try less channels */
+ if (!found) {
+ for (c = dev->nchannels - 1; c > 0; c --) {
+ arg = c;
+ if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+ return SA_ERROR_SYSTEM;
+
+ if (arg == (int) c) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ } else {
+
+ /* First try less channels ... */
+ for (c = dev->nchannels; c > 0; c --) {
+ arg = c;
+ if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+ return SA_ERROR_SYSTEM;
+
+ if (arg == (int) c) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* ... then try more channels */
+ if (!found) {
+ for (c = dev->nchannels + 1; c < 16; c ++) {
+ arg = c;
+ if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
+ return SA_ERROR_SYSTEM;
+
+ if (arg == (int) c) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ errno = EIO;
+ return SA_ERROR_SYSTEM;
+ }
+
+ oss->real_nchannels = c;
+
+ r = dev->rate;
+ suggested = 0;
+ phase = 0;
+
+ for (;;) {
+ arg = r;
+
+ if (ioctl(oss->fd, SNDCTL_DSP_SPEED, &arg) < 0)
+ return SA_ERROR_SYSTEM;
+
+ sa_assert(arg > 0);
+
+ if (arg >= r*0.95 || arg <= r *1.05)
+ break;
+
+ if (arg > suggested)
+ suggested = arg;
+
+ if (dev->adjust_rate >= 0) {
+
+ if (phase == 0) {
+ /* Find the next higher sample rate to try */
+
+ for (i = 0; i < (int) elementsof(try_rates); i++) {
+ /* Yes, we could optimize a little here */
+
+ if (try_rates[i] > r) {
+ r = try_rates[i];
+ break;
+ }
+ }
+
+
+ if (i == elementsof(try_rates)) {
+ phase = 1;
+ r = dev->rate;
+ }
+ }
+
+ if (phase == 1) {
+ /* Find the next lower sample rate to try */
+
+ for (i = elementsof(try_rates); i > 0; i--) {
+ if (suggested > try_rates[i-1] && suggested < r) {
+ r = suggested;
+ break;
+ } else if (try_rates[i-1] < r) {
+ r = try_rates[i-1];
+ break;
+ }
+ }
+
+ sa_assert(i > 0);
+ }
+
+ } else {
+
+ if (phase == 0) {
+ /* Find the next lower sample rate to try */
+
+ for (i = elementsof(try_rates); i > 0; i--) {
+
+ if (try_rates[i-1] < r) {
+ r = try_rates[i-1];
+ break;
+ }
+ }
+
+ if (i == 0) {
+ phase = 1;
+ r = dev->rate;
+ }
+ }
+
+ if (phase == 1) {
+ /* Find the next higher sample rate to try */
+
+ for (i = 0; i < (int) elementsof(try_rates); i++) {
+ if (suggested > r && suggested < try_rates[i]) {
+ r = suggested;
+ break;
+ } else if (try_rates[i] < r) {
+ r = try_rates[i];
+ break;
+ }
+ }
+
+ sa_assert(i < (int) elementsof(try_rates));
+ }
+ }
+
+ }
+
+ oss->real_rate = r;
+
+ printf("Chosen: %u channels, %uHz, format=%u\n", oss->real_nchannels, oss->real_rate, oss->real_pcm_format);
+
+ if (dev->adjust_nchannels != 0)
+ dev->nchannels = oss->real_nchannels;
+ if (dev->adjust_rate != 0)
+ dev->rate = oss->real_rate;
+ if (dev->pcm_format != 0)
+ dev->pcm_format = oss->real_pcm_format;
+ }
+
+ return SA_SUCCESS;
+}
+
+int device_destroy(sa_device_t *dev) {
+ oss_device_t *oss = OSS_DEVICE(dev);
+
+ if (oss->fd >= 0)
+ close(oss->fd);
+
+ device_free(dev);
+ return SA_SUCCESS;
+}
+
+int device_start_thread(sa_device_t *dev, sa_event_callback_t *callback) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_change_input_volume(sa_device_t *dev, int *vol) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_change_output_volume(sa_device_t *dev, int *vol) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_change_sampling_rate(sa_device_t *dev, unsigned rate) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_get_state(sa_device_t *dev, sa_state_t *state) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_get_position(sa_device_t *dev, sa_position_t position, int64_t *pos) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_pread(sa_device_t *dev, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_pwrite(sa_device_t *dev, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_pread_ni(sa_device_t *dev, unsigned channel, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_pwrite_ni(sa_device_t *dev, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_get_read_size(sa_device_t *dev, size_t *size) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_get_write_size(sa_device_t *dev, size_t *size) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_resume(sa_device_t *dev) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_pause(sa_device_t *dev) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_drain(sa_device_t *dev) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+/* Unsupported operations */
+
+int device_change_device(sa_device_t *dev, const char *device_name) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_change_client_name(sa_device_t *dev, const char *client_name) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
+
+int device_change_stream_name(sa_device_t *dev, const char *stream_name) {
+ return SA_ERROR_NOT_SUPPORTED;
+}
diff --git a/resample.c b/resample.c
new file mode 100644
index 0000000..f69e6d6
--- /dev/null
+++ b/resample.c
@@ -0,0 +1,164 @@
+#include "sydney.h"
+
+#define MAX_OPERATIONS 10
+
+typedef struct buffer_spec buffer_spec_t;
+
+struct buffer_spec {
+ void *data;
+ size_t size;
+ size_t max_size;
+};
+
+typedef struct operation operation_t;
+
+typedef void (*operation_execute_t)(operation_t *o, buffer_spec_t *source);
+
+struct operation {
+ operation_execute_t execute;
+
+ struct buffer_spec result;
+
+ size_t sample
+ unsigned nchannels;
+};
+
+typedef struct pipeline pipeline_t;
+
+struct pipeline {
+ operation_t operations[MAX_OPERATIONS];
+};
+
+static int native_pcm_format(pcm_format_t f) {
+
+ /* Sample formats we know to handle natively */
+
+ return
+ f == SA_PCM_FORMAT_S16_NE ||
+ f == SA_PCM_FORMAT_S32_NE ||
+ f == SA_PCM_FORMAT_FLOAT32_NE;
+
+}
+
+/* Steps: prefmt -> volume -> remap -> resample -> postfmt */
+
+int build_pipeline_output(
+ sa_device_t *dev,
+ sa_pipeline_t *pipeline,
+ sa_pcm_format format,
+ unsigned rate,
+ unsigned nchannels,
+ const sa_channel_t map[]) {
+
+ int *channel_map_table;
+ unsigned u, t;
+ int prefmt_required;
+ int sum_required = 0;
+ int o = 0;
+ pcm_format_t work_format;
+ operation_execute_t func;
+
+ sa_assert(dev);
+ sa_assert(pipeline);
+
+ if (!(channel_map_table = sa_new(int, nchannels * dev->nchannels)))
+ return SA_ERROR_OOM;
+
+ for (u = 0; u < nchannels; u++) {
+ unsigned k = 0;
+
+ for (t = 0; t < dev->nchannels; t++) {
+
+ if (map[u] == map[t])
+ channel_map_table[u * dev->nchannels + k++] += t;
+ }
+
+ if (k > 1)
+ sum_required = 1;
+
+ channel_map_table[k] = (unsigned) -1;
+ }
+
+ prefmt_required =
+ sum_required ||
+ dev->output_volume != 0 ||
+ dev->rate != rate ||
+ dev->dsr_enabled;
+
+ if (native_pcm_format(dev->pcm_format))
+ prefmt_required = 0;
+
+ if (prefmt_required) {
+
+ switch (dev->pcm_format) {
+ case SA_PCM_FORMAT_U8:
+ case SA_PCM_FORMAT_ULAW:
+ case SA_PCM_FORMAT_ALAW:
+ case SA_PCM_FORMAT_S16_LE:
+ case SA_PCM_FORMAT_S16_BE:
+ work_format = SA_PCM_FORMAT_S16_NE;
+ break;
+
+ case SA_PCM_FORMAT_S24_LE:
+ case SA_PCM_FORMAT_S24_BE:
+ case SA_PCM_FORMAT_S32_LE:
+ case SA_PCM_FORMAT_S32_BE:
+ work_format = SA_PCM_FORMAT_S32_NE;
+ break;
+
+ case SA_PCM_FORMAT_FLOAT32_LE:
+ case SA_PCM_FORMAT_FLOAT32_BE:
+ work_format = SA_PCM_FORMAT_FLOAT32_NE;
+ break;
+ }
+
+ sa_assert(native_pcm_format(work_format));
+
+ func = get_format_converter(dev->pcm_format, work_format);
+ pipeline.operations[o].execute = func;
+ pipeline.operations[o].buffer.data = NULL;
+ pipeline.operations[o].buffer.stride = dev->nchannels * get_sample_size(dev->work_format);
+ pipeline.operations[o].
+ o++;
+
+ } else
+ work_format = dev->pcm_format;
+
+ if (dev->output_volume != 0) {
+ pipeline.operations[o].execute = get_scaler(work_format);
+ pipeline.operations[o].buffer.data = NULL;
+ o++;
+ }
+
+ if (dev->rate != rate || dev->dsr_enabled) {
+ pipeline.operations[o].execute = get_resampler(work_format);
+ pipeline.operations[o].buffer.data = NULL;
+ o++;
+ }
+
+ if (work_format != pcm_format) {
+ pipeline.operations[o].execute = get_format_converter(work_format, pcm_format);
+ pipeline.operations[o].buffer.data = NULL;
+ o++;
+ }
+
+ pipeline.operations[o].execute = NULL;
+
+
+
+}
+
+void execute_pipeline(sa_device_t *dev, pipeline_t *pipeline, const buffer_spec_t *buffer, buffer_spec_t *output) {
+ sa_operation_t *o;
+
+ sa_assert(dev);
+ sa_assert(pipeline);
+ sa_assert(buffer);
+
+ for (o = pipeline->operations; o->execute; o++) {
+ o->execute(o, buffer);
+ buffer = &o->buffer;
+ }
+
+ *output = *buffer;
+}
diff --git a/sydney.h b/sydney.h
new file mode 100644
index 0000000..7f70add
--- /dev/null
+++ b/sydney.h
@@ -0,0 +1,341 @@
+#ifndef foosydneyhfoo
+#define foosydneyhfoo
+
+/* Requirements:
+
+- 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>
+#include <sys/param.h>
+#include <inttypes.h>
+
+/* Detect byte order, based on sys/param.h */
+#if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || defined(WIN32)
+# define SA_LITTLE_ENDIAN
+# undef SA_BIG_ENDIAN
+#elif (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN))
+# undef SA_LITTLE_ENDIAN
+# define SA_BIG_ENDIAN
+#else
+# error "Cannot determine byte order!"
+#endif
+
+typedef struct sa_device sa_device_t;
+
+/** Volume that corresponds to muted in/out */
+#define SA_VOLUME_MUTED 0x80000000
+
+/** Ways to express seek offsets for pread/pwrite */
+typedef enum {
+ SA_SEEK_RELATIVE,
+ SA_SEEK_ABSOLUTE,
+ SA_SEEK_RELATIVE_END,
+} sa_seek_t;
+
+/** Supported formats */
+typedef enum {
+ SA_PCM_FORMAT_U8,
+ SA_PCM_FORMAT_ULAW,
+ SA_PCM_FORMAT_ALAW,
+ SA_PCM_FORMAT_S16_LE,
+ SA_PCM_FORMAT_S16_BE,
+ SA_PCM_FORMAT_S24_LE,
+ SA_PCM_FORMAT_S24_BE,
+ SA_PCM_FORMAT_S32_LE,
+ SA_PCM_FORMAT_S32_BE,
+ SA_PCM_FORMAT_FLOAT32_LE,
+ SA_PCM_FORMAT_FLOAT32_BE,
+ SA_PCM_FORMAT_MAX
+} sa_pcm_format_t;
+
+/* Native endianness definitions for PCM */
+#ifdef SA_LITTLE_ENDIAN
+#define SA_PCM_FORMAT_S16_NE SA_PCM_FORMAT_S16_LE
+#define SA_PCM_FORMAT_S24_NE SA_PCM_FORMAT_S24_LE
+#define SA_PCM_FORMAT_S32_NE SA_PCM_FORMAT_S32_LE
+#define SA_PCM_FORMAT_FLOAT32_NE SA_PCM_FORMAT_FLOAT32_LE
+#else
+#define SA_PCM_FORMAT_S16_NE SA_PCM_FORMAT_S16_BE
+#define SA_PCM_FORMAT_S24_NE SA_PCM_FORMAT_S24_BE
+#define SA_PCM_FORMAT_S32_NE SA_PCM_FORMAT_S32_BE
+#define SA_PCM_FORMAT_FLOAT32_NE SA_PCM_FORMAT_FLOAT32_BE
+#endif
+
+#define SA_CODEC_MPEG "mpeg"
+#define SA_CODEC_AC3 "ac3"
+#define SA_CODEC_GSM "gsm"
+#define SA_CODEC_VORBIS "vorbis"
+#define SA_CODEC_SPEEX "speex"
+
+/** Device opening modes */
+typedef enum {
+ SA_MODE_RDONLY = 1,
+ SA_MODE_WRONLY = 2,
+ SA_MODE_RDWR = 3
+} sa_mode_t;
+
+/** Error codes */
+typedef enum {
+ SA_SUCCESS = 0,
+ SA_ERROR_NOT_SUPPORTED = -1,
+ SA_ERROR_INVALID = -2,
+ SA_ERROR_STATE = -3,
+ SA_ERROR_OOM = -4,
+ SA_ERROR_NO_DEVICE = -5,
+ SA_ERROR_NO_DRIVER = -6,
+ SA_ERROR_NO_CODEC = -7,
+ SA_ERROR_NO_PCM_FORMAT = -7,
+ SA_ERROR_SYSTEM = -8
+} sa_error_t;
+
+/** Possible events for notifications */
+typedef enum {
+ SA_NOTIFY_REQUEST_STOP,
+ SA_NOTIFY_REQUEST_START,
+ SA_NOTIFY_VOLUME_CHANGED_IN,
+ SA_NOTIFY_VOLUME_CHANGED_OUT,
+ SA_NOTIFY_DEVICE_CHANGED
+} sa_notify_t;
+
+/** Classes of events */
+typedef enum {
+ SA_EVENT_REQUEST_IO,
+ SA_EVENT_INIT_THREAD,
+ SA_EVENT_NOTIFY,
+ SA_EVENT_ERROR
+} sa_event_t;
+
+/** List of sample position queries */
+typedef enum {
+ SA_POSITION_WRITE_DELAY,
+ SA_POSITION_WRITE_HARDWARE,
+ SA_POSITION_WRITE_SOFTWARE,
+ SA_POSITION_READ_DELAY,
+ SA_POSITION_READ_HARDWARE,
+ SA_POSITION_READ_SOFTWARE,
+ SA_POSITION_DUPLEX_DELAY,
+ SA_POSITION_MAX
+} sa_position_t;
+
+/* Channel positions */
+typedef enum {
+ SA_CHANNEL_MONO,
+ SA_CHANNEL_LEFT,
+ SA_CHANNEL_RIGHT,
+ SA_CHANNEL_CENTER,
+ SA_CHANNEL_FRONT_LEFT,
+ SA_CHANNEL_FRONT_RIGHT,
+ SA_CHANNEL_FRONT_CENTER,
+ SA_CHANNEL_REAR_LEFT,
+ SA_CHANNEL_REAR_RIGHT,
+ SA_CHANNEL_REAR_CENTER,
+ SA_CHANNEL_LFE,
+ SA_CHANNEL_FRONT_LEFT_OF_CENTER,
+ SA_CHANNEL_FRONT_RIGHT_OF_CENTER,
+ SA_CHANNEL_SIDE_LEFT,
+ SA_CHANNEL_SIDE_RIGHT,
+ SA_CHANNEL_TOP_CENTER,
+ SA_CHANNEL_TOP_FRONT_LEFT,
+ SA_CHANNEL_TOP_FRONT_RIGHT,
+ SA_CHANNEL_TOP_FRONT_CENTER,
+ SA_CHANNEL_TOP_REAR_LEFT,
+ SA_CHANNEL_TOP_REAR_RIGHT,
+ SA_CHANNEL_TOP_REAR_CENTER,
+ SA_CHANNEL_AUX0,
+ SA_CHANNEL_AUX1,
+ SA_CHANNEL_AUX2,
+ SA_CHANNEL_AUX3,
+ SA_CHANNEL_AUX4,
+ SA_CHANNEL_AUX5,
+ SA_CHANNEL_AUX6,
+ SA_CHANNEL_AUX7,
+ SA_CHANNEL_AUX8,
+ SA_CHANNEL_AUX9,
+ SA_CHANNEL_AUX10,
+ SA_CHANNEL_AUX11,
+ SA_CHANNEL_AUX12,
+ SA_CHANNEL_AUX13,
+ SA_CHANNEL_AUX14,
+ SA_CHANNEL_AUX15,
+ SA_CHANNEL_AUX16,
+ SA_CHANNEL_AUX17,
+ SA_CHANNEL_AUX18,
+ SA_CHANNEL_AUX19,
+ SA_CHANNEL_AUX20,
+ SA_CHANNEL_AUX21,
+ SA_CHANNEL_AUX22,
+ SA_CHANNEL_AUX23,
+ SA_CHANNEL_AUX24,
+ SA_CHANNEL_AUX25,
+ SA_CHANNEL_AUX26,
+ SA_CHANNEL_AUX27,
+ SA_CHANNEL_AUX28,
+ SA_CHANNEL_AUX29,
+ SA_CHANNEL_AUX30,
+ SA_CHANNEL_AUX31,
+ SA_CHANNEL_MAX
+} sa_channel_t;
+
+typedef enum {
+ SA_STATE_INIT,
+ SA_STATE_RUNNING,
+ SA_STATE_STOPPED,
+ /* put more stuff */
+} sa_state_t;
+
+typedef enum {
+ SA_XRUN_MODE_STOP,
+ SA_XRUN_MODE_SPIN
+} sa_xrun_mode_t;
+
+typedef enum {
+ SA_ADJUST_UP = 1,
+ SA_ADJUST_DOWN = -1,
+ SA_ADJUST_NONE = 0,
+} sa_adjust_t;
+
+/** Main callback function */
+typedef int (*sa_event_callback_t)(sa_device_t *dev, sa_event_t event);
+
+/** Create an opaque (e.g. AC3) codec stream */
+int sa_device_create_opaque(sa_device_t **dev, const char *client_name, sa_mode_t mode, const char *codec);
+
+/** Normal way to open a PCM device */
+int sa_device_create_pcm(sa_device_t **dev, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned nchannels);
+
+/** Initialise the device */
+int sa_device_open(sa_device_t *dev);
+
+/** Close/destroy everything */
+int sa_device_destroy(sa_device_t *dev);
+
+/* "Soft" params */
+int sa_device_set_write_lower_watermark(sa_device_t *dev, size_t size);
+int sa_device_set_read_lower_watermark(sa_device_t *dev, size_t size);
+
+int sa_device_set_write_upper_watermark(sa_device_t *dev, size_t size);
+int sa_device_set_read_upper_watermark(sa_device_t *dev, size_t size);
+
+/** Set the mapping between channels and the loudspeakers */
+int sa_device_set_channel_map(sa_device_t *dev, const sa_channel_t map[]);
+
+/** Whether xruns cause the card to reset */
+int sa_device_set_xrun_mode(sa_device_t *dev, sa_xrun_mode_t mode);
+
+/** Set the device to non-interleaved mode */
+int sa_device_set_ni(sa_device_t *dev, int enable);
+
+/** Require dynamic sample rate */
+int sa_device_set_dsr(sa_device_t *dev, int enable);
+
+/** Select driver */
+int sa_device_set_driver(sa_device_t *dev, const char *driver);
+
+/** Start callback */
+int sa_device_start_thread(sa_device_t *dev, sa_event_callback_t *callback);
+
+/** Change the device connected to the stream */
+int sa_device_change_device(sa_device_t *dev, const char *device_name);
+
+/** volume in hundreths of dB's*/
+int sa_device_change_input_volume(sa_device_t *dev, int *vol);
+
+/** volume in hundreths of dB's*/
+int sa_device_change_output_volume(sa_device_t *dev, int *vol);
+
+/** Change the sampling rate */
+int sa_device_change_sampling_rate(sa_device_t *dev, unsigned rate);
+
+/** Change the name of the client application using the device */
+int sa_device_change_client_name(sa_device_t *dev, const char *client_name);
+
+/** Change the name of the stream being sent */
+int sa_device_change_stream_name(sa_device_t *dev, const char *stream_name);
+
+/** Associate opaque user data */
+int sa_device_change_user_data(sa_device_t *dev, void *value);
+
+/* Hardware-related. This is implementation-specific and hardware specific. */
+int sa_device_adjust_rate(sa_device_t *dev, sa_adjust_t direction);
+
+int sa_device_adjust_nchannels(sa_device_t *dev, sa_adjust_t direction);
+
+int sa_device_adjust_pcm_format(sa_device_t *dev, sa_adjust_t direction);
+
+/* Query functions */
+
+/** Get current state of the audio device */
+int sa_device_get_state(sa_device_t *dev, sa_state_t *state);
+
+/** Get current sampling rate */
+int sa_device_get_sampling_rate(sa_device_t *dev, unsigned *rate);
+
+/** Get number of channels */
+int sa_device_get_nchannels(sa_device_t *dev, int *nchannels);
+
+/** Get format being used */
+int sa_device_get_pcm_format(sa_device_t *dev, sa_pcm_format_t *format);
+
+/** Get opaque pointer associated to the device */
+int sa_device_get_user_data(sa_device_t *dev, void **value);
+
+/** Obtain the error code */
+int sa_device_get_event_error(sa_device_t *dev, sa_error_t *error);
+
+/** Obtain the notification code */
+int sa_device_get_event_notify(sa_device_t *dev, sa_notify_t *notify);
+
+/** sync/timing */
+int sa_device_get_position(sa_device_t *dev, sa_position_t position, int64_t *pos);
+
+
+
+
+/* Blocking IO calls */
+
+/** Interleaved capture function */
+int sa_device_read(sa_device_t *dev, void *data, size_t nbytes);
+/** Interleaved playback function */
+int sa_device_write(sa_device_t *dev, const void *data, size_t nbytes);
+
+/** Non-interleaved capture function */
+int sa_device_read_ni(sa_device_t *dev, unsigned channel, void *data, size_t nbytes);
+/** Non-interleaved playback function */
+int sa_device_write_ni(sa_device_t *dev, unsigned channel, const void *data, size_t nbytes);
+
+/** Interleaved capture function with seek offset */
+int sa_device_pread(sa_device_t *dev, void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+/** Interleaved playback function with seek offset */
+int sa_device_pwrite(sa_device_t *dev, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+
+/** Non-interleaved capture function with seek offset */
+int sa_device_pread_ni(sa_device_t *dev, unsigned channel, void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+/** Non-interleaved playback function with seek offset */
+int sa_device_pwrite_ni(sa_device_t *dev, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+
+
+/** Query how much can be read without blocking */
+int sa_device_get_read_size(sa_device_t *dev, size_t *size);
+/** Query how much can be written without blocking */
+int sa_device_get_write_size(sa_device_t *dev, size_t *size);
+
+
+/* Control/xrun */
+
+/** Resume playing after a pause */
+int sa_device_resume(sa_device_t *dev);
+
+/** Pause audio playback (do not empty the buffer) */
+int sa_device_pause(sa_device_t *dev);
+
+/** Block until all audio has been played */
+int sa_device_drain(sa_device_t *dev);
+
+#endif
diff --git a/test-sine.c b/test-sine.c
new file mode 100644
index 0000000..ae9ac9e
--- /dev/null
+++ b/test-sine.c
@@ -0,0 +1,27 @@
+#include <assert.h>
+
+#include "sydney.h"
+
+#define ASSERT_SUCCESS(x) assert(x == SA_SUCCESS)
+
+#define FREQ 440
+
+int main(int argc, char *argv[]) {
+
+ sa_device_t *dev;
+ float data[4] = { 0.0, 1.0, 0.0, -1.0 };
+ int i;
+
+ ASSERT_SUCCESS(sa_device_create_pcm(&dev, argv[0], SA_MODE_WRONLY, SA_PCM_FORMAT_FLOAT32_NE, FREQ * 4, 1));
+ ASSERT_SUCCESS(sa_device_change_device(dev, "/dev/dsp1"));
+ ASSERT_SUCCESS(sa_device_open(dev));
+
+ for (i = 0; i < 10; i++)
+ ASSERT_SUCCESS(sa_device_write(dev, data, sizeof(data)));
+
+ ASSERT_SUCCESS(sa_device_drain(dev));
+
+ ASSERT_SUCCESS(sa_device_destroy(dev));
+
+ return 0;
+}