From 2eb6dec8e9f0114bdbad59cf8f11f197f8fdaaf3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 25 Apr 2007 10:27:13 +0000 Subject: initial commit git-svn-id: file:///home/lennart/svn/public/libsydney/trunk@3 9ba3c220-e4d3-45a2-8aa3-73fcc9aff6ce --- Makefile | 9 ++ TODO | 5 + common.c | 523 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common.h | 58 +++++++ driver.h | 29 ++++ macro.h | 33 ++++ malloc.c | 13 ++ malloc.h | 16 ++ oss.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++ resample.c | 164 +++++++++++++++++++ sydney.h | 341 +++++++++++++++++++++++++++++++++++++++ test-sine.c | 27 ++++ 12 files changed, 1642 insertions(+) create mode 100644 Makefile create mode 100644 TODO create mode 100644 common.c create mode 100644 common.h create mode 100644 driver.h create mode 100644 macro.h create mode 100644 malloc.c create mode 100644 malloc.h create mode 100644 oss.c create mode 100644 resample.c create mode 100644 sydney.h create mode 100644 test-sine.c 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 +#include + +#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 + +#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 +#include + +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include + +/* 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 + +#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; +} -- cgit