From 181e9c6d5d11cb1e5d36a2777eeb233ad8ed00e5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 9 Oct 2008 18:15:23 +0200 Subject: big pile of updates to match more what happened with libcanberra --- src/common.c | 1406 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 855 insertions(+), 551 deletions(-) (limited to 'src/common.c') diff --git a/src/common.c b/src/common.c index 0187990..5a50f9e 100644 --- a/src/common.c +++ b/src/common.c @@ -1,9 +1,30 @@ +/*** + This file is part of libsydney. + + Copyright 2007-2008 Lennart Poettering + + libsydney is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libsydney is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libsydney. If not, see + . +***/ + #ifdef HAVE_CONFIG_H #include #endif #include #include +#include #include #include "sydney.h" @@ -12,72 +33,114 @@ #include "common.h" #include "driver.h" #include "mutex.h" +#include "proplist.h" + +/* + Requirements & General observations + + - In sync mode, the device will automatically write data so that an + initial read causes writes of zeros to be issued to that one can + do "while (1); {read(); write()} + - All functions are thread-safe and can be called in any thread + context. None of the functions is async-signal safe. + - It is assumed that duplex streams have a single clock (synchronised) + - Property set extensible. To be kept in sync with PulseAudio and libcanberra + - Error codes are returned immediately, as negative integers + - It is recommended to set most properties before the _open() call. + +*/ -/* contains code */ -#include "meta-name-table.h" +#define DEFAULT_LATENCY_USEC (2000000ULL) /* 2s */ +#define DEFAULT_PROCESS_TIME_USEC (20000ULL) /* 20ms */ -static sa_stream_t *stream_alloc(void) { - sa_stream_t *d; +static int stream_alloc(sa_stream **_s) { + sa_stream *s; + const char *d; + int ret; + + sa_assert(_s); - if (!(d = sa_new0(sa_stream_t, 1))) - return NULL; + if (!(s = sa_new0(sa_stream, 1))) { + ret = SA_ERROR_OOM; + goto fail; + } /* All fields a carefully chosen in a way that initializing them - * NUL bytes is sufficient */ + * with NUL bytes is sufficient */ - if (!(d->mutex = sa_mutex_new(FALSE, TRUE))) { - sa_free(d); - return NULL; + if (!(s->mutex = sa_mutex_new(TRUE, TRUE))) { + ret = SA_ERROR_OOM; + goto fail; } - return d; + if ((ret = sa_proplist_create(&s->props)) < 0) + goto fail; + + if ((d = getenv("SYDNEY_DRIVER"))) + if ((ret = sa_stream_set_driver(s, d)) < 0) + goto fail; + + if ((d = getenv("SYDNEY_DEVICE"))) + if ((ret = sa_stream_change_device(s, d)) < 0) + goto fail; + + *_s = s; + + return SA_SUCCESS; + +fail: + + if (s) + sa_assert_se(sa_stream_destroy(s) == SA_SUCCESS); + + return ret; } int sa_stream_create_opaque( - sa_stream_t **s, - const char *client_name, + sa_stream **_s, sa_mode_t mode, const char *codec) { - int error; + int ret; + sa_stream *s; - sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(_s, 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); - if (!(*s = stream_alloc())) + if ((ret = stream_alloc(&s)) < 0) return SA_ERROR_OOM; - (*s)->mode = mode; + s->mode = mode; - if (!((*s)->codec = sa_strdup(codec))) { - error = SA_ERROR_OOM; + if (!(s->codec = sa_strdup(codec))) { + ret = SA_ERROR_OOM; goto fail; } oil_init(); - if (client_name) - if ((error = sa_stream_change_meta_data(*s, SA_META_CLIENT_NAME, client_name, strlen(client_name)+1)) < 0) - goto fail; + *_s = s; return SA_SUCCESS; fail: - sa_stream_destroy(*s); - return error; + + if (s) + sa_assert_se(sa_stream_destroy(s) == SA_SUCCESS); + + return ret; } int sa_stream_create_pcm( - sa_stream_t **s, - const char *client_name, + sa_stream **_s, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned nchannels) { int ret; - size_t lwm; + sa_stream *s; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(mode == SA_MODE_RDONLY || mode == SA_MODE_WRONLY || mode == SA_MODE_RDWR, SA_ERROR_INVALID); @@ -85,90 +148,122 @@ int sa_stream_create_pcm( sa_return_val_if_fail(rate > 0, SA_ERROR_INVALID); sa_return_val_if_fail(nchannels > 0, SA_ERROR_INVALID); - if (!(*s = stream_alloc())) + if ((ret = stream_alloc(&s)) < 0) return SA_ERROR_OOM; - (*s)->mode = mode; - (*s)->pcm_attrs.format = format; - (*s)->pcm_attrs.nchannels = nchannels; - (*s)->pcm_sample_size = sa_get_pcm_sample_size(format); - (*s)->pcm_frame_size = (*s)->pcm_sample_size * nchannels; + s->mode = mode; + s->pcm_attrs.format = format; + s->pcm_attrs.nchannels = nchannels; + s->pcm_sample_size = sa_get_pcm_sample_size(format); + s->pcm_frame_size = s->pcm_sample_size * nchannels; + + if ((ret = sa_stream_change_pcm_rate(s, rate))) + goto fail; if (nchannels <= 2) { static const sa_channel_t map_stereo[2] = { SA_CHANNEL_LEFT, SA_CHANNEL_RIGHT }; static const sa_channel_t map_mono[1] = { SA_CHANNEL_MONO }; - if ((ret = sa_stream_set_channel_map(*s, nchannels == 2 ? map_stereo : map_mono, nchannels))) + if ((ret = sa_stream_set_channel_map(s, nchannels == 2 ? map_stereo : map_mono, nchannels))) goto fail; } - if ((ret = sa_stream_change_rate(*s, rate))) - goto fail; + oil_init(); - lwm = ((*s)->pcm_frame_size * (*s)->pcm_attrs.rate) / 20; /* 50 ms */ + *_s = s; - if (lwm <= 0) - lwm = (*s)->pcm_frame_size * (*s)->pcm_attrs.rate; /* 1s */ + return SA_SUCCESS; -/* if (mode & SA_MODE_RDONLY) { */ +fail: -/* if ((ret = sa_stream_set_read_lower_watermark(*s, lwm))) */ -/* goto fail; */ + sa_assert_se(sa_stream_destroy(s) == SA_SUCCESS); + return ret; +} -/* if ((ret = sa_stream_set_read_upper_watermark(*s, lwm*2))) */ -/* goto fail; */ -/* } */ +static int fix_latency(sa_stream *s, size_t *latency_nbytes, size_t *process_time_nbytes) { -/* if (mode & SA_MODE_WRONLY) { */ + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(latency_nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(process_time_nbytes, SA_ERROR_INVALID); -/* if ((ret = sa_stream_set_write_lower_watermark(*s, lwm))) */ -/* goto fail; */ + if (*latency_nbytes <= 0) + *latency_nbytes = sa_stream_usec_to_bytes(s, DEFAULT_LATENCY_USEC, 0); -/* if ((ret = sa_stream_set_write_upper_watermark(*s, lwm*2))) */ -/* goto fail; */ -/* } */ + if (*process_time_nbytes <= 0) { + size_t n, m; - oil_init(); + n = sa_stream_frames_to_bytes(s, sa_stream_bytes_to_frames(s, *latency_nbytes, 1)/2); + m = sa_stream_usec_to_bytes(s, DEFAULT_PROCESS_TIME_USEC, 0); - if (client_name) - if ((ret = sa_stream_change_meta_data(*s, SA_META_CLIENT_NAME, client_name, strlen(client_name))) < 0) - goto fail; + *process_time_nbytes = SA_MIN(n, m); + } + + sa_return_val_if_fail(*latency_nbytes > 0, SA_ERROR_INVALID); + sa_return_val_if_fail(*process_time_nbytes >= *latency_nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(sa_stream_frame_aligned(s, (int64_t) *latency_nbytes), SA_ERROR_INVALID); + sa_return_val_if_fail(sa_stream_frame_aligned(s, (int64_t) *process_time_nbytes), SA_ERROR_INVALID); return SA_SUCCESS; +} -fail: - sa_stream_destroy(*s); +static int stream_open_unlocked(sa_stream *s) { + int ret; + + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(s->codec || s->pcm_attrs.channel_map, SA_ERROR_STATE); + + if (s->state == SA_STATE_DEAD) + return SA_ERROR_STATE; + else if (s->state != SA_STATE_INIT) + return SA_SUCCESS; + + if (s->mode & SA_MODE_RDONLY) + if ((ret = fix_latency(s, &s->read_latency, &s->read_process_time)) < 0) + return ret; + + if (s->mode & SA_MODE_WRONLY) + if ((ret = fix_latency(s, &s->write_latency, &s->write_process_time)) < 0) + return ret; + + if ((ret = driver_open(s)) == SA_SUCCESS) + s->state = SA_STATE_STOPPED; + return ret; } -int sa_stream_open(sa_stream_t *s) { +int sa_stream_open(sa_stream *s) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->codec || s->pcm_attrs.channel_map, SA_ERROR_NO_INIT); -/* sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_RDONLY) || (s->read_lower_watermark <= s->read_upper_watermark), SA_ERROR_INVALID); */ -/* sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_WRONLY) || (s->write_lower_watermark <= s->write_upper_watermark), SA_ERROR_INVALID); */ -/* sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_RDONLY) || !s->codec || (s->read_lower_watermark > 0 && s->read_upper_watermark > 0), SA_ERROR_NO_INIT); */ -/* sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_WRONLY) || !s->codec || (s->write_lower_watermark > 0 && s->write_upper_watermark > 0), SA_ERROR_NO_INIT); */ - - if ((ret = driver_open(s)) == 0) - s->state = SA_STATE_STOPPED; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); + + ret = stream_open_unlocked(s); sa_mutex_unlock(s->mutex); return ret; } -int sa_stream_destroy(sa_stream_t *s) { - int ret; - unsigned u; +int sa_stream_destroy(sa_stream *s) { + int ret = SA_SUCCESS; sa_return_val_if_fail(s, SA_ERROR_INVALID); - ret = driver_destroy(s); + /* There's no locking necessary here, because the application is + * broken anyway if it destructs this object in one thread and + * still is calling a method of it in another. */ + + if (s->state != SA_STATE_INIT) + ret = driver_destroy(s); + + if (s->props) + sa_assert_se(sa_proplist_destroy(s->props) == SA_SUCCESS); + + if (s->mutex) + sa_mutex_free(s->mutex); sa_free(s->codec); sa_free(s->driver); @@ -176,106 +271,100 @@ int sa_stream_destroy(sa_stream_t *s) { sa_free(s->pcm_attrs.channel_map); sa_free(s->read_volume); sa_free(s->write_volume); - - for (u = 0; u < _META_NAMES_MAX; u++) - sa_free(s->meta_data[u]); - - sa_mutex_free(s->mutex); sa_free(s); + return ret; } -/* int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size) { */ - -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size > 0, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); */ +int sa_stream_change_write_latency(sa_stream *s, size_t latency_nbytes, size_t process_time_nbytes) { + int ret; -/* s->write_lower_watermark = size; */ + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); -/* sa_mutex_unlock(s->mutex); */ + if ((ret = fix_latency(s, &latency_nbytes, &process_time_nbytes)) < 0) + return ret; -/* return SA_SUCCESS; */ -/* } */ + sa_mutex_lock(s->mutex); + sa_return_val_if_fail_unlock(s->state != SA_STATE_DEAD, SA_ERROR_STATE, s->mutex); -/* int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size) { */ + ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_write_latency(s, &latency_nbytes, &process_time_nbytes); -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size > 0, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); */ + if (ret == SA_SUCCESS) { + s->write_latency = latency_nbytes; + s->write_process_time = process_time_nbytes; + } -/* s->read_lower_watermark = size; */ + sa_mutex_unlock(s->mutex); -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ + return ret; +} -/* int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size) { */ +int sa_stream_change_read_latency(sa_stream *s, size_t latency_nbytes, size_t process_time_nbytes) { + int ret; -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size > 0, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); */ + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); -/* s->write_upper_watermark = size; */ + if ((ret = fix_latency(s, &latency_nbytes, &process_time_nbytes)) < 0) + return ret; -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ + sa_mutex_lock(s->mutex); + sa_return_val_if_fail_unlock(s->state != SA_STATE_DEAD, SA_ERROR_STATE, s->mutex); -/* int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size) { */ + ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_read_latency(s, &latency_nbytes, &process_time_nbytes); -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size > 0, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); */ + if (ret == SA_SUCCESS) { + s->read_latency = latency_nbytes; + s->read_process_time = process_time_nbytes; + } -/* s->read_upper_watermark = size; */ + sa_mutex_unlock(s->mutex); -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ + return ret; +} -int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t *map, unsigned n) { +int sa_stream_set_channel_map(sa_stream *s, const sa_channel_t *map, unsigned n) { const sa_channel_t *c; sa_channel_t *m; + int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(map, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(n == s->pcm_attrs.nchannels, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, n == s->pcm_attrs.nchannels, SA_ERROR_INVALID); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); for (c = map; n > 0; c++, n--) if (*c >= _SA_CHANNEL_MAX) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_INVALID; + ret = SA_ERROR_INVALID; + goto fail; } if (!(m = sa_memdup(map, sizeof(sa_channel_t) * s->pcm_attrs.nchannels))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_OOM; + ret = SA_ERROR_OOM; + goto fail; } sa_free(s->pcm_attrs.channel_map); s->pcm_attrs.channel_map = m; + ret = SA_SUCCESS; + +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + + return ret; } -int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode) { +int sa_stream_set_xrun_mode(sa_stream *s, sa_xrun_mode_t mode) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(mode == SA_XRUN_MODE_STOP || mode == SA_XRUN_MODE_SPIN, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->xrun_mode = mode; @@ -283,11 +372,12 @@ int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode) { return SA_SUCCESS; } -int sa_stream_set_non_interleaved(sa_stream_t *s, int enable) { +int sa_stream_set_non_interleaved(sa_stream *s, int enable) { sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_INVALID, s->mutex); s->ni_enabled = !!enable; @@ -295,11 +385,12 @@ int sa_stream_set_non_interleaved(sa_stream_t *s, int enable) { return SA_SUCCESS; } -int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable) { +int sa_stream_set_dynamic_pcm_rate(sa_stream *s, int enable) { sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_INVALID, s->mutex); s->dynamic_rate_enabled = !!enable; @@ -307,74 +398,63 @@ int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable) { return SA_SUCCESS; } -int sa_stream_set_driver(sa_stream_t *s, const char *driver) { +int sa_stream_set_driver(sa_stream *s, const char *driver) { char *d; + int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(driver, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); - if (!(d = sa_strdup(driver))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_OOM; + if (!driver) + d = NULL; + else if (!(d = sa_strdup(driver))) { + ret = SA_ERROR_OOM; + goto fail; } sa_free(s->driver); s->driver = d; - sa_mutex_unlock(s->mutex); - return SA_SUCCESS; -} - -int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback) { - int r; - - sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(callback, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, !s->callback, SA_ERROR_STATE); - - r = driver_start_thread(s, callback); - - if (r == SA_SUCCESS) - s->callback = callback; + ret = SA_SUCCESS; +fail: sa_mutex_unlock(s->mutex); - return r; -} -int sa_stream_stop_thread(sa_stream_t *s) { - int r; + return ret; +} +int sa_stream_set_event_callback(sa_stream *s, sa_event_callback_t callback) { sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->callback, SA_ERROR_STATE); - r = driver_stop_thread(s); + sa_mutex_lock(s->mutex); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); - if (r == SA_SUCCESS) - s->callback = NULL; + s->callback = callback; sa_mutex_unlock(s->mutex); - return r; + + return SA_SUCCESS; } -int sa_stream_change_device(sa_stream_t *s, const char *device_name) { +int sa_stream_change_device(sa_stream *s, const char *device) { char *d; int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(device_name, SA_ERROR_INVALID); - - if (!(d = sa_strdup(device_name))) - return SA_ERROR_OOM; sa_mutex_lock(s->mutex); + sa_return_val_if_fail_unlock(s->state != SA_STATE_DEAD, SA_ERROR_STATE, s->mutex); - ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_device(s, device_name); + if (!device) + d = NULL; + else if (!(d = sa_strdup(device))) { + ret = SA_ERROR_OOM; + goto fail; + } + + ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_device(s, d); if (ret == SA_SUCCESS) { sa_free(s->device); @@ -382,31 +462,36 @@ int sa_stream_change_device(sa_stream_t *s, const char *device_name) { } else sa_free(d); +fail: sa_mutex_unlock(s->mutex); return ret; } -int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned n) { +int sa_stream_change_read_volume(sa_stream *s, const int32_t vol[], unsigned n) { int *v, ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(vol, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(n == s->pcm_attrs.nchannels || n == 1, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, (!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID); - if (s->codec || s->pcm_attrs.nchannels == n) { + if (s->pcm_attrs.nchannels == n) { if (!(v = sa_newdup(int32_t, vol, n))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_OOM; + ret = SA_ERROR_OOM; + goto fail; } } else { unsigned i; + sa_assert_se(n == 1); + if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_OOM; + ret = SA_ERROR_OOM; + goto fail; } for (i = 0; i < s->pcm_attrs.nchannels; i++) @@ -421,30 +506,36 @@ int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned n } else sa_free(v); + ret = SA_SUCCESS; + +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned n) { +int sa_stream_change_write_volume(sa_stream *s, const int32_t vol[], unsigned n) { int *v, ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(vol, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(n == s->pcm_attrs.nchannels || n == 1, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, (!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID); - if (s->codec || s->pcm_attrs.nchannels == n) { + if (s->pcm_attrs.nchannels == n) { if (!(v = sa_newdup(int32_t, vol, n))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_OOM; + ret = SA_ERROR_OOM; + goto fail; } } else { unsigned i; if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_OOM; + ret = SA_ERROR_OOM; + goto fail; } for (i = 0; i < s->pcm_attrs.nchannels; i++) @@ -459,20 +550,23 @@ int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned } else sa_free(v); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_change_rate(sa_stream_t *s, unsigned rate) { +int sa_stream_change_pcm_rate(sa_stream *s, unsigned rate) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(rate > 0, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->dynamic_rate_enabled || s->state == SA_STATE_INIT, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->dynamic_rate_enabled || s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); - ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_rate(s, rate); + ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_pcm_rate(s, rate); if (ret == SA_SUCCESS) s->pcm_attrs.rate = rate; @@ -481,7 +575,55 @@ int sa_stream_change_rate(sa_stream_t *s, unsigned rate) { return ret; } -int sa_stream_change_user_data(sa_stream_t *s, const void *value) { +int sa_stream_change_props(sa_stream *s, ...) { + va_list ap; + int ret; + sa_proplist *p = NULL; + + sa_return_val_if_fail(s, SA_ERROR_INVALID); + + va_start(ap, s); + ret = sa_proplist_from_ap(&p, ap); + va_end(ap); + + if (ret < 0) + return ret; + + ret = sa_stream_change_props_full(s, p); + + sa_assert_se(sa_proplist_destroy(p) == 0); + + return ret; +} + +int sa_stream_change_props_full(sa_stream *s, sa_proplist *p) { + int ret; + sa_proplist *merged; + + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(p, SA_ERROR_INVALID); + + sa_mutex_lock(s->mutex); + + if ((ret = sa_proplist_merge(&merged, s->props, p)) < 0) + goto finish; + + ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_props(s, p, merged); + + if (ret == SA_SUCCESS) { + sa_assert_se(sa_proplist_destroy(s->props) == SA_SUCCESS); + s->props = merged; + } else + sa_assert_se(sa_proplist_destroy(merged) == SA_SUCCESS); + +finish: + + sa_mutex_unlock(s->mutex); + + return ret; +} + +int sa_stream_change_user_data(sa_stream *s, const void *value) { sa_return_val_if_fail(s->mutex, SA_ERROR_INVALID); sa_mutex_lock(s->mutex); @@ -491,11 +633,12 @@ int sa_stream_change_user_data(sa_stream_t *s, const void *value) { return SA_SUCCESS; } -int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction) { +int sa_stream_set_adjust_rate(sa_stream *s, sa_adjust_t direction) { sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->adjust_rate = direction; @@ -503,11 +646,12 @@ int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction) { return SA_SUCCESS; } -int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction) { +int sa_stream_set_adjust_nchannels(sa_stream *s, sa_adjust_t direction) { sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->adjust_rate = direction; @@ -515,11 +659,12 @@ int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction) { return SA_SUCCESS; } -int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction) { +int sa_stream_set_adjust_pcm_format(sa_stream *s, sa_adjust_t direction) { sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); + sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->adjust_pcm_format = direction; @@ -527,22 +672,12 @@ int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction) { return SA_SUCCESS; } -/* int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction) { */ -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE); */ - -/* s->adjust_watermarks = direction; */ - -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ - -int sa_stream_get_state(sa_stream_t *s, sa_state_t *state) { +int sa_stream_get_state(sa_stream *s, sa_state_t *state) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(state, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, state, SA_ERROR_INVALID); if (s->state == SA_STATE_INIT) { *state = s->state; @@ -554,508 +689,713 @@ int sa_stream_get_state(sa_stream_t *s, sa_state_t *state) { return ret; } -int sa_stream_get_rate(sa_stream_t *s, unsigned *rate) { +int sa_stream_get_pcm_rate(sa_stream *s, unsigned *rate) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(rate, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); *rate = s->pcm_attrs.rate; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels) { +int sa_stream_get_pcm_nchannels(sa_stream *s, unsigned *nchannels) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(nchannels, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + /* No locking necessary since we only touch immutable fields here */ *nchannels = s->pcm_attrs.nchannels; - sa_mutex_unlock(s->mutex); return SA_SUCCESS; } -int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *pcm_format) { +int sa_stream_get_pcm_format(sa_stream *s, sa_pcm_format_t *pcm_format) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(pcm_format, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + /* No locking necessary since we only touch immutable fields here */ *pcm_format = s->pcm_attrs.format; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode) { +int sa_stream_get_mode(sa_stream *s, sa_mode_t *access_mode) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(access_mode, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); + /* No locking necessary since we only touch immutable fields here */ *access_mode = s->mode; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size) { - size_t n; +int sa_stream_get_codec(sa_stream *s, char **codec) { + char *t; sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(size && (*size == 0 || codec), SA_ERROR_INVALID); + sa_return_val_if_fail(codec, SA_ERROR_INVALID); + sa_return_val_if_fail(s->codec, SA_ERROR_STATE); + + /* No locking necessary since we only touch immutable fields here */ + if (!(t = sa_strdup(s->codec))) + return SA_ERROR_OOM; + + *codec = t; + return SA_SUCCESS; +} + +int sa_stream_get_write_latency(sa_stream *s, size_t *nbytes) { + size_t latency, process_time; + int ret; + + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->codec, SA_ERROR_STATE); - n = strlen(s->codec)+1; - if (*size < n) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_SPACE; - } + latency = s->write_latency; + process_time = s->write_process_time; - if (codec) - strcpy(codec, s->codec); - *size = n; + /* In case the latency is not fixated yet, we calculate what it + * would be fixated to */ + if ((ret = fix_latency(s, &latency, &process_time)) < 0) + goto fail; + + *nbytes = latency; + ret = SA_SUCCESS; + +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + return ret; +} + +int sa_stream_get_read_latency(sa_stream *s, size_t *nbytes) { + size_t latency, process_time; + int ret; + + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + + sa_mutex_lock(s->mutex); + + latency = s->read_latency; + process_time = s->read_process_time; + + /* In case the latency is not fixated yet, we calculate what it + * would be fixated to */ + + if ((ret = fix_latency(s, &latency, &process_time)) < 0) + goto fail; + + *nbytes = latency; + ret = SA_SUCCESS; + +fail: + sa_mutex_unlock(s->mutex); + return ret; } -/* int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size) { */ -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); */ +int sa_stream_get_write_process_time(sa_stream *s, size_t *nbytes) { + size_t latency, process_time; + int ret; + + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + + sa_mutex_lock(s->mutex); + + latency = s->write_latency; + process_time = s->write_process_time; + + /* In case the latency is not fixated yet, we calculate what it + * would be fixated to */ + + if ((ret = fix_latency(s, &latency, &process_time)) < 0) + goto fail; -/* *size = s->write_lower_watermark; */ + *nbytes = process_time; + ret = SA_SUCCESS; -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ +fail: + sa_mutex_unlock(s->mutex); + return ret; +} -/* int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size) { */ -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); */ +int sa_stream_get_read_process_time(sa_stream *s, size_t *nbytes) { + size_t latency, process_time; + int ret; -/* *size = s->read_lower_watermark; */ + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ + sa_mutex_lock(s->mutex); -/* int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size) { */ -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); */ + latency = s->read_latency; + process_time = s->read_process_time; -/* *size = s->write_upper_watermark; */ + /* In case the latency is not fixated yet, we calculate what it + * would be fixated to */ -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ + if ((ret = fix_latency(s, &latency, &process_time)) < 0) + goto fail; -/* int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size) { */ -/* sa_return_val_if_fail(s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail(size, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ -/* sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); */ + *nbytes = process_time; + ret = SA_SUCCESS; -/* *size = s->read_upper_watermark; */ +fail: + sa_mutex_unlock(s->mutex); + return ret; +} -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ +int sa_stream_get_pcm_channel_map(sa_stream *s, sa_channel_t **map, unsigned *n) { + sa_channel_t *m; + int ret; -int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t *map, unsigned *n) { sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(n && (*n == 0 || map), SA_ERROR_INVALID); + sa_return_val_if_fail(map, SA_ERROR_INVALID); + sa_return_val_if_fail(n, SA_ERROR_INVALID); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - if (*n < s->pcm_attrs.nchannels) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_SPACE; + if (!(m = sa_newdup(sa_channel_t, s->pcm_attrs.channel_map, s->pcm_attrs.nchannels))) { + ret = SA_ERROR_OOM; + goto fail; } - if (map) - memcpy(map, s->pcm_attrs.channel_map, s->pcm_attrs.nchannels * sizeof(sa_channel_t)); + *map = m; *n = s->pcm_attrs.nchannels; + ret = SA_SUCCESS; +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + return ret; } -int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode) { +int sa_stream_get_xrun_mode(sa_stream *s, sa_xrun_mode_t *mode) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(mode, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); + sa_mutex_lock(s->mutex); *mode = s->xrun_mode; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled) { +int sa_stream_get_non_interleaved(sa_stream *s, int *enabled) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(enabled, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); + sa_mutex_lock(s->mutex); *enabled = s->ni_enabled; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled) { +int sa_stream_get_dynamic_pcm_rate(sa_stream *s, int *enabled) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(enabled, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); + sa_mutex_lock(s->mutex); *enabled = s->dynamic_rate_enabled; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_driver(sa_stream_t *s, char *driver, size_t *size) { - size_t n; +int sa_stream_get_driver(sa_stream *s, char **driver) { + char *t; + int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(size && (*size == 0 || driver), SA_ERROR_INVALID); + sa_return_val_if_fail(driver, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->driver, SA_ERROR_STATE); - n = strlen(s->driver)+1; - if (*size < n) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_SPACE; + sa_return_val_if_fail_unlock(s->driver, SA_ERROR_STATE, s->mutex); + + if (!(t = sa_strdup(s->driver))) { + ret = SA_ERROR_OOM; + goto fail; } - if (driver) - strcpy(driver, s->driver); - *size = n; + *driver = t; + ret = SA_SUCCESS; +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + + return ret; } -int sa_stream_get_device(sa_stream_t *s, char *device, size_t *size) { - size_t n; +int sa_stream_get_device(sa_stream *s, char **device) { + char *t; + int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(size && (*size == 0 || device), SA_ERROR_INVALID); + sa_return_val_if_fail(device, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->device, SA_ERROR_STATE); - n = strlen(s->device)+1; - if (*size < n) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_SPACE; + sa_return_val_if_fail_unlock(s->device, SA_ERROR_STATE, s->mutex); + + if (!(t = sa_strdup(s->device))) { + ret = SA_ERROR_OOM; + goto fail; } - if (device) - strcpy(device, s->device); - *size = n; + *device = t; + ret = SA_SUCCESS; +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + + return ret; } -int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned *n) { +int sa_stream_get_read_volume(sa_stream *s, int32_t **vol, unsigned *n) { + int32_t *v; + int ret; + sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(n && (*n == 0 || vol), SA_ERROR_INVALID); + sa_return_val_if_fail(vol, SA_ERROR_INVALID); + sa_return_val_if_fail(n, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->read_volume, SA_ERROR_STATE); - if (*n < s->pcm_attrs.nchannels) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_SPACE; + sa_return_val_if_fail_unlock(s->read_volume, SA_ERROR_STATE, s->mutex); + + if (!(v = sa_newdup(int32_t, s->read_volume, s->pcm_attrs.nchannels))) { + ret = SA_ERROR_OOM; + goto fail; } - if (vol) - memcpy(vol, s->read_volume, s->pcm_attrs.nchannels * sizeof(int32_t)); + *vol = v; *n = s->pcm_attrs.nchannels; + ret = SA_SUCCESS; +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + return ret; } -int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned *n) { +int sa_stream_get_write_volume(sa_stream *s, int32_t **vol, unsigned *n) { + int32_t *v; + int ret; + sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(n && (*n == 0 || vol), SA_ERROR_INVALID); + sa_return_val_if_fail(vol, SA_ERROR_INVALID); + sa_return_val_if_fail(n, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->write_volume, SA_ERROR_STATE); - if (*n < s->pcm_attrs.nchannels) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_SPACE; + sa_return_val_if_fail_unlock(s->write_volume, SA_ERROR_STATE, s->mutex); + + if (!(v = sa_newdup(int32_t, s->write_volume, s->pcm_attrs.nchannels))) { + ret = SA_ERROR_OOM; + goto fail; } - if (vol) - memcpy(vol, s->write_volume, s->pcm_attrs.nchannels * sizeof(int32_t)); + *vol = v; *n = s->pcm_attrs.nchannels; + ret = SA_SUCCESS; +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + return ret; } -int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction) { +int sa_stream_get_adjust_pcm_rate(sa_stream *s, sa_adjust_t *direction) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(direction, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); *direction = s->adjust_rate; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction) { +int sa_stream_get_adjust_pcm_nchannels(sa_stream *s, sa_adjust_t *direction) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(direction, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); *direction = s->adjust_nchannels; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction) { +int sa_stream_get_adjust_pcm_format(sa_stream *s, sa_adjust_t *direction) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(direction, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); *direction = s->adjust_pcm_format; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -/* int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction) { */ -/* sa_return_val_if_fail_mutex(s->mutex, s, SA_ERROR_INVALID); */ -/* sa_return_val_if_fail_mutex(s->mutex, direction, SA_ERROR_INVALID); */ -/* sa_mutex_lock(s->mutex); */ - -/* *direction = s->adjust_watermarks; */ - -/* sa_mutex_unlock(s->mutex); */ -/* return SA_SUCCESS; */ -/* } */ - -int sa_stream_get_user_data(sa_stream_t *s, void **value) { +int sa_stream_get_user_data(sa_stream *s, void **value) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(value, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); + sa_mutex_lock(s->mutex); *value = s->user_data; - sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error) { +int sa_stream_get_event_error(sa_stream *s, int *error) { + int ret; + sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(error, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->event == SA_EVENT_ERROR, SA_ERROR_STATE); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->event == SA_EVENT_ERROR, SA_ERROR_STATE, s->mutex); *error = s->error; +fail: sa_mutex_unlock(s->mutex); + return SA_SUCCESS; } -int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify) { +int sa_stream_get_event_notify(sa_stream *s, sa_notify_t *notify, void **data, size_t *data_nbytes) { + int ret; + sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(notify, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->event == SA_EVENT_NOTIFY, SA_ERROR_STATE); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->event == SA_EVENT_NOTIFY, SA_ERROR_STATE, s->mutex); + + if (data && s->notify_data) { + + if (s->notify_data_nbytes > 0) { + void *d; + + if (!(d = sa_memdup(s->notify_data, s->notify_data_nbytes))) { + ret = SA_ERROR_OOM; + goto fail; + } + + *data = d; + } else + *data = s->notify_data; + } + + if (data_nbytes && s->notify_data_nbytes) + *data_nbytes = s->notify_data_nbytes; *notify = s->notify; + ret = SA_SUCCESS; +fail: sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + return ret; } -int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) { +int sa_stream_get_position(sa_stream *s, sa_position_t position, int64_t *pos) { int ret; sa_return_val_if_fail(s, 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_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_get_position(s, position, pos); +fail: + sa_mutex_unlock(s->mutex); + + return ret; +} + +int sa_stream_wait(sa_stream *s, sa_event_t *event) { + int ret; + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(event, SA_ERROR_INVALID); + + sa_mutex_lock(s->mutex); + + sa_return_val_if_fail_unlock(!s->callback, SA_ERROR_STATE, s->mutex); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); + + ret = driver_wait(s); + + if (ret == SA_SUCCESS) + *event = s->event; + +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes) { +int sa_stream_read(sa_stream *s, void *data, size_t nbytes) { int ret; sa_return_val_if_fail(s, 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(s->codec || sa_stream_frame_aligned(s, (int64_t) nbytes), SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->ni_enabled, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + sa_return_val_if_fail_unlock(!s->ni_enabled, SA_ERROR_STATE, s->mutex); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_read(s, data, nbytes); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_read_ni(sa_stream_t *s, unsigned channel, void *data, size_t nbytes) { +int sa_stream_read_ni(sa_stream *s, unsigned channel, void *data, size_t nbytes) { int ret; sa_return_val_if_fail(s, 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(!s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID); + sa_return_val_if_fail(sa_stream_frame_aligned(s, (int64_t) nbytes), SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, s->ni_enabled, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, (nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + sa_return_val_if_fail_unlock(s->ni_enabled, SA_ERROR_STATE, s->mutex); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_read_ni(s, channel, data, nbytes); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) { +int sa_stream_write(sa_stream *s, const void *data, size_t nbytes) { return sa_stream_pwrite(s, data, nbytes, 0, SA_SEEK_RELATIVE); } -int sa_stream_write_ni(sa_stream_t *s, unsigned channel, const void *data, size_t nbytes) { +int sa_stream_write_ni(sa_stream *s, unsigned channel, const void *data, size_t nbytes) { return sa_stream_pwrite_ni(s, channel, data, nbytes, 0, SA_SEEK_RELATIVE); } -int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { +int sa_stream_pwrite(sa_stream *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { int ret; sa_return_val_if_fail(s, 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(s->codec || sa_stream_frame_aligned(s, (int64_t) nbytes), SA_ERROR_INVALID); + sa_return_val_if_fail(s->codec || sa_stream_frame_aligned(s, (int64_t) offset), SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->ni_enabled, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, s->codec || (offset % s->pcm_frame_size) == 0, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + sa_return_val_if_fail_unlock(!s->ni_enabled, SA_ERROR_STATE, s->mutex); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_pwrite(s, data, nbytes, offset, whence); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_pwrite_ni(sa_stream_t *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { +int sa_stream_pwrite_ni(sa_stream *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { int ret; sa_return_val_if_fail(s, 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(!s->codec, SA_ERROR_STATE); + sa_return_val_if_fail(channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID); + sa_return_val_if_fail(s->codec || sa_stream_frame_aligned(s, (int64_t) nbytes), SA_ERROR_INVALID); + sa_return_val_if_fail(s->codec || sa_stream_frame_aligned(s, (int64_t) offset), SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, s->ni_enabled, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, (nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, (offset % s->pcm_sample_size) == 0, SA_ERROR_INVALID); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + sa_return_val_if_fail_unlock(s->ni_enabled, SA_ERROR_STATE, s->mutex); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_pwrite_ni(s, channel, data, nbytes, offset, whence); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_get_read_size(sa_stream_t *s, size_t *size) { +int sa_stream_get_read_size(sa_stream *s, size_t *nbytes) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(size, SA_ERROR_INVALID); + sa_return_val_if_fail(nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); - ret = driver_get_read_size(s, size); + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); + + ret = driver_get_read_size(s, nbytes); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_get_write_size(sa_stream_t *s, size_t *size) { +int sa_stream_get_write_size(sa_stream *s, size_t *nbytes) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(size, SA_ERROR_INVALID); + sa_return_val_if_fail(nbytes, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); - ret = driver_get_write_size(s, size); + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); + + ret = driver_get_write_size(s, nbytes); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_resume(sa_stream_t *s) { +int sa_stream_start(sa_stream *s) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_resume(s); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_pause(sa_stream_t *s) { +int sa_stream_stop(sa_stream *s) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_pause(s); +fail: sa_mutex_unlock(s->mutex); + return ret; } -int sa_stream_drain(sa_stream_t *s) { +int sa_stream_drain(sa_stream *s) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); + sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); - sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE); + + sa_return_val_if_fail_unlock(!s->callback, SA_ERROR_STATE, s->mutex); + + if ((ret = stream_open_unlocked(s)) < 0) + goto fail; + + sa_return_val_if_fail_unlock(s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE, s->mutex); ret = driver_drain(s); +fail: + sa_mutex_unlock(s->mutex); + return ret; } @@ -1088,184 +1428,148 @@ size_t sa_get_pcm_sample_size(sa_pcm_format_t f) { sa_assert_not_reached(); } -static int meta_check_png(const void *data, size_t size) { - static const uint8_t png_signature[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; - sa_assert(data); +const char *sa_strerror(int code) { + const char * const error_table[-_SA_ERROR_MAX] = { + [-SA_SUCCESS] = "Success", + [-SA_ERROR_NOTSUPPORTED] = "Operation not supported", + [-SA_ERROR_INVALID] = "Invalid argument", + [-SA_ERROR_STATE] = "Invalid state", + [-SA_ERROR_OOM] = "Out of memory", + [-SA_ERROR_NODRIVER] = "No such driver", + [-SA_ERROR_SYSTEM] = "System error", + [-SA_ERROR_CORRUPT] = "File or data corrupt", + [-SA_ERROR_TOOBIG] = "File or data too large", + [-SA_ERROR_NOTFOUND] = "File or data not found", + [-SA_ERROR_DESTROYED] = "Destroyed", + [-SA_ERROR_CANCELED] = "Canceled", + [-SA_ERROR_NOTAVAILABLE] = "Not available", + [-SA_ERROR_ACCESS] = "Acess forbidden", + [-SA_ERROR_IO] = "IO error", + [-SA_ERROR_INTERNAL] = "Internal error", + [-SA_ERROR_DISABLED] = "Sound disabled", + [-SA_ERROR_NODEVICE] = "No such device", + [-SA_ERROR_NOCODEC] = "No such codec", + [-SA_ERROR_NOPCMFORMAT] = "No such PCM format" + }; - if (size < sizeof(png_signature)) - return 0; + sa_return_val_if_fail(code <= 0, NULL); + sa_return_val_if_fail(code > _SA_ERROR_MAX, NULL); - return memcmp(data, png_signature, 8) == 0; + return error_table[-code]; } -static int meta_check_utf8(const void *data, size_t size) { - int32_t idx; - - oil_utf8_validate(&idx, data, size); - - return (size_t) idx == size; +float volume_to_dB(int32_t v) { + return (float) v / 100.0f; } -static int meta_check_pid(const void *data, size_t size) { - long int pid; - char *t; - - if (size <= 0) - return 0; +int32_t volume_from_dB(float dB) { + return (int32_t) lrintf(dB * 100.0f); +} - if (memchr(data, 0, size)) - return 0; +float volume_to_linear(int32_t v) { - if (!(t = sa_strndup(data, size))) + if (v <= SA_VOLUME_MUTED) return 0; - errno = 0; - pid = strtol(t, NULL, 10); - sa_free(t); + return powf(10.0f, volume_to_dB(v) / 20.0f); +} - if (errno != 0) - return 0; +int32_t volume_from_linear(float f) { - if (pid <= 1) - return 0; + if (f <= 0) + return SA_VOLUME_MUTED; - return 1; + return volume_from_dB(20.0f * log10f(f)); } -static int meta_check_icon_name(const void *data, size_t size) { - const char *t = data; +unsigned sa_stream_bytes_to_frames(sa_stream *s, size_t nbytes, sa_bool_t round_up) { + sa_return_val_if_fail(s, (unsigned) -1); + sa_return_val_if_fail(!s->codec, (unsigned) -1); - if (size <= 0) - return 0; + /* We don't lock here since the data we access here is immutable */ - if (memchr(data, 0, size)) - return 0; + if (round_up) + nbytes += s->pcm_frame_size - 1U; - if (size == 1 && t[0] == '.') - return 0; + return (unsigned) (nbytes / s->pcm_frame_size); +} - if (size == 2 && t[0] == '.' && t[1] == '.') - return 0; +size_t sa_stream_frames_to_bytes(sa_stream *s, unsigned nframes) { + sa_return_val_if_fail(s, (size_t) -1); + sa_return_val_if_fail(!s->codec, (size_t) -1); - if (memchr(t, '/', size)) - return 0; + /* We don't lock here since the data we access here is immutable */ - return 1; + return (size_t) nframes * s->pcm_frame_size; } -static int meta_check_word(const void *data, size_t size) { - const char *t = data; +uint64_t sa_stream_bytes_to_usec(sa_stream *s, size_t nbytes, sa_bool_t round_up) { + unsigned nframes; + + sa_return_val_if_fail(s, (uint64_t) -1); + sa_return_val_if_fail(!s->codec, (uint64_t) -1); - for (; size > 0; size --, t++) - if (*t <= 32 || *t >= 127) - return 0; + /* We don't lock here since the data we access here is immutable */ - return 1; + nframes = sa_stream_bytes_to_frames(s, nbytes, round_up); + return sa_stream_frames_to_usec(s, nframes, round_up); } -typedef int (*meta_check_func_t)(const void *data, size_t size); +size_t sa_stream_usec_to_bytes(sa_stream *s, uint64_t usec, sa_bool_t round_up) { + unsigned nframes; -int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size) { - void *d = NULL; - const struct meta_name *m; - int ret; + sa_return_val_if_fail(s, (size_t) -1); + sa_return_val_if_fail(!s->codec, (size_t) -1); - static const meta_check_func_t check_table[] = { - meta_check_utf8, - meta_check_pid, - meta_check_word, /* FIXME */ - meta_check_utf8, - meta_check_icon_name, - meta_check_png, - meta_check_word, /* FIXME */ - meta_check_word, /* FIXME */ - }; + /* We don't lock here since the data we access here is immutable */ - sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(name, SA_ERROR_INVALID); - sa_mutex_lock(s->mutex); - sa_return_val_if_fail_mutex(s->mutex, data || size == 0, SA_ERROR_INVALID); + nframes = sa_stream_usec_to_frames(s, usec, round_up); + return sa_stream_frames_to_bytes(s, nframes); +} - if (!(m = sa_lookup_meta_name(name, strlen(name)))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_META; - } +uint64_t sa_stream_frames_to_usec(sa_stream *s, unsigned nframes, sa_bool_t round_up) { + uint64_t u; + sa_return_val_if_fail(s, (uint64_t) -1); + sa_return_val_if_fail(!s->codec, (uint64_t) -1); - if (!check_table[m->idx](data, size)) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_INVALID; - } + u = (uint64_t) nframes; + u *= 1000000LLU; - if (data) - if (!(d = sa_memdup(data, size))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_OOM; - } + sa_mutex_lock(s->mutex); - ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_meta_data(s, name, data, size); + if (round_up) + u += (uint64_t) s->pcm_attrs.rate - 1LLU; - if (ret == SA_SUCCESS) { - sa_free(s->meta_data[m->idx]); - s->meta_data[m->idx] = d; - s->meta_data_size[m->idx] = size; - } else - sa_free(d); + u /= (uint64_t) s->pcm_attrs.rate; sa_mutex_unlock(s->mutex); - return ret; + + return u; } -int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void *data, size_t *size) { - const struct meta_name *m; +unsigned sa_stream_usec_to_frames(sa_stream *s, uint64_t usec, sa_bool_t round_up) { + sa_return_val_if_fail(s, (unsigned) -1); + sa_return_val_if_fail(!s->codec, (unsigned) -1); - sa_return_val_if_fail(s, SA_ERROR_INVALID); - sa_return_val_if_fail(name, SA_ERROR_INVALID); - sa_return_val_if_fail(size && (*size == 0 || data), SA_ERROR_INVALID); sa_mutex_lock(s->mutex); - if (!(m = sa_lookup_meta_name(name, strlen(name)))) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_META; - } - - if (!s->meta_data[m->idx]) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_DATA; - } + usec *= (uint64_t) s->pcm_attrs.rate; - if (*size < s->meta_data_size[m->idx]) { - sa_mutex_unlock(s->mutex); - return SA_ERROR_NO_SPACE; - } + sa_mutex_unlock(s->mutex); - if (data) - memcpy(data, s->meta_data[m->idx], s->meta_data_size[m->idx]); + if (round_up) + usec += 999999LLU; - *size = s->meta_data_size[m->idx]; + usec /= 1000000LLU; - sa_mutex_unlock(s->mutex); - return SA_SUCCESS; + return (unsigned) usec; } -const char *sa_strerror(int code) { - const char * const error_table[-_SA_ERROR_MAX] = { - [-SA_SUCCESS] = "Success", - [-SA_ERROR_NOT_SUPPORTED] = "Operation not supported", - [-SA_ERROR_INVALID] = "Invalid argument", - [-SA_ERROR_STATE] = "Invalid state", - [-SA_ERROR_OOM] = "Out of memory", - [-SA_ERROR_NO_DEVICE] = "No such device", - [-SA_ERROR_NO_DRIVER] = "No such driver", - [-SA_ERROR_NO_CODEC] = "No such codec", - [-SA_ERROR_NO_PCM_FORMAT] = "No such PCM format", - [-SA_ERROR_SYSTEM] = "System error", - [-SA_ERROR_NO_INIT] = "Not initialized", - [-SA_ERROR_NO_META] = "No such meta name", - [-SA_ERROR_NO_DATA] = "No such data" - }; +sa_bool_t sa_stream_frame_aligned(sa_stream *s, int64_t nbytes) { + sa_return_val_if_fail(s, FALSE); + sa_return_val_if_fail(!s->codec, FALSE); - sa_return_val_if_fail(code <= 0, NULL); - sa_return_val_if_fail(code > _SA_ERROR_MAX, NULL); + /* We don't lock here since the data we access here is immutable */ - return error_table[-code]; + return (nbytes % (int64_t) s->pcm_frame_size) == 0; } -- cgit