/*** 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" #include "macro.h" #include "malloc.h" #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. */ #define DEFAULT_BUFFER_LATENCY_USEC (2000000ULL) /* 2s */ #define DEFAULT_IMMUTABLE_LATENCY_USEC (500000ULL) /* 500ms */ #define DEFAULT_PROCESS_TIME_USEC (20000ULL) /* 20ms */ static int stream_alloc(sa_stream **_s) { sa_stream *s; const char *d; int ret; sa_assert(_s); if (!(s = sa_new0(sa_stream, 1))) { ret = SA_ERROR_OOM; goto fail; } /* All fields a carefully chosen in a way that initializing them * with NUL bytes is sufficient */ if (!(s->mutex = sa_mutex_new(TRUE, TRUE))) { ret = SA_ERROR_OOM; goto fail; } 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 **_s, sa_mode_t mode, const char *codec) { int ret; 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); sa_return_val_if_fail(codec, SA_ERROR_INVALID); if ((ret = stream_alloc(&s)) < 0) return SA_ERROR_OOM; s->mode = mode; if (!(s->codec = sa_strdup(codec))) { ret = SA_ERROR_OOM; goto fail; } oil_init(); *_s = s; return SA_SUCCESS; fail: if (s) sa_assert_se(sa_stream_destroy(s) == SA_SUCCESS); return ret; } int sa_stream_create_pcm( sa_stream **_s, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned nchannels) { int ret; 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); 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(nchannels > 0, SA_ERROR_INVALID); 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; 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))) goto fail; } oil_init(); *_s = s; return SA_SUCCESS; fail: sa_assert_se(sa_stream_destroy(s) == SA_SUCCESS); return ret; } static int fix_latency( sa_stream *s, size_t *buffer_latency_nbytes, size_t *immutable_latency_nbytes, size_t *process_time_nbytes) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(buffer_latency_nbytes, SA_ERROR_INVALID); sa_return_val_if_fail(process_time_nbytes, SA_ERROR_INVALID); if (*buffer_latency_nbytes <= 0) *buffer_latency_nbytes = sa_stream_usec_to_bytes(s, DEFAULT_BUFFER_LATENCY_USEC, 0); if (immutable_latency_nbytes && *immutable_latency_nbytes <= 0) { size_t n, m; n = sa_stream_frames_to_bytes(s, sa_stream_bytes_to_frames(s, *buffer_latency_nbytes, 0)/2); m = sa_stream_usec_to_bytes(s, DEFAULT_IMMUTABLE_LATENCY_USEC, 0); *immutable_latency_nbytes = SA_MIN(n, m); } if (*process_time_nbytes <= 0) { size_t l, n, m; l = immutable_latency_nbytes ? *immutable_latency_nbytes : *buffer_latency_nbytes; n = sa_stream_frames_to_bytes(s, sa_stream_bytes_to_frames(s, l, 1)/2); m = sa_stream_usec_to_bytes(s, DEFAULT_PROCESS_TIME_USEC, 0); *process_time_nbytes = SA_MIN(n, m); } if (immutable_latency_nbytes) { sa_return_val_if_fail(*immutable_latency_nbytes > 0, SA_ERROR_INVALID); sa_return_val_if_fail(*immutable_latency_nbytes <= *buffer_latency_nbytes, SA_ERROR_INVALID); sa_return_val_if_fail(*process_time_nbytes <= *immutable_latency_nbytes, SA_ERROR_INVALID); sa_return_val_if_fail(sa_stream_frame_aligned(s, (int64_t) *immutable_latency_nbytes), SA_ERROR_INVALID); } sa_return_val_if_fail(*buffer_latency_nbytes > 0, SA_ERROR_INVALID); sa_return_val_if_fail(*process_time_nbytes > 0, SA_ERROR_INVALID); sa_return_val_if_fail(*process_time_nbytes <= *buffer_latency_nbytes, SA_ERROR_INVALID); sa_return_val_if_fail(sa_stream_frame_aligned(s, (int64_t) *buffer_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; } 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_overall_latency, NULL, &s->read_process_time)) < 0) return ret; if (s->mode & SA_MODE_WRONLY) if ((ret = fix_latency(s, &s->write_overall_latency, &s->write_immutable_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 *s) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_mutex_lock(s->mutex); 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 *s) { int ret = SA_SUCCESS; sa_return_val_if_fail(s, SA_ERROR_INVALID); /* 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); sa_free(s->device); sa_free(s->pcm_attrs.channel_map); sa_free(s->read_volume); sa_free(s->write_volume); sa_free(s); return ret; } int sa_stream_change_write_latency(sa_stream *s, size_t overall_latency_nbytes, size_t immutable_latency_nbytes, size_t process_time_nbytes) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); if ((ret = fix_latency(s, &overall_latency_nbytes, &immutable_latency_nbytes, &process_time_nbytes)) < 0) return ret; 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_write_latency(s, &overall_latency_nbytes, &immutable_latency_nbytes, &process_time_nbytes); if (ret == SA_SUCCESS) { s->write_overall_latency = overall_latency_nbytes; s->write_immutable_latency = immutable_latency_nbytes; s->write_process_time = process_time_nbytes; } sa_mutex_unlock(s->mutex); return ret; } int sa_stream_change_read_latency(sa_stream *s, size_t overall_latency_nbytes, size_t process_time_nbytes) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(s->mode & SA_MODE_RDONLY, SA_ERROR_STATE); if ((ret = fix_latency(s, &overall_latency_nbytes, NULL, &process_time_nbytes)) < 0) return ret; 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_read_latency(s, &overall_latency_nbytes, &process_time_nbytes); if (ret == SA_SUCCESS) { s->read_overall_latency = overall_latency_nbytes; s->read_process_time = process_time_nbytes; } sa_mutex_unlock(s->mutex); return ret; } 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); for (c = map; n > 0; c++, n--) if (*c >= _SA_CHANNEL_MAX) { ret = SA_ERROR_INVALID; goto fail; } if (!(m = sa_memdup(map, sizeof(sa_channel_t) * s->pcm_attrs.nchannels))) { 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 ret; } 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->xrun_mode = mode; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_INVALID, s->mutex); s->ni_enabled = !!enable; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_INVALID, s->mutex); s->dynamic_rate_enabled = !!enable; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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_mutex_lock(s->mutex); sa_return_val_if_fail_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); if (!driver) d = NULL; else if (!(d = sa_strdup(driver))) { ret = SA_ERROR_OOM; goto fail; } sa_free(s->driver); s->driver = d; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->callback = callback; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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_mutex_lock(s->mutex); sa_return_val_if_fail_unlock(s->state != SA_STATE_DEAD, SA_ERROR_STATE, s->mutex); 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); s->device = d; } else sa_free(d); fail: sa_mutex_unlock(s->mutex); return ret; } 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); if (s->pcm_attrs.nchannels == n) { if (!(v = sa_newdup(int32_t, vol, n))) { ret = SA_ERROR_OOM; goto fail; } } else { unsigned i; sa_assert_se(n == 1); if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) { ret = SA_ERROR_OOM; goto fail; } for (i = 0; i < s->pcm_attrs.nchannels; i++) v[i] = vol[0]; } ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_read_volume(s, v); if (ret == SA_SUCCESS) { sa_free(s->read_volume); s->read_volume = v; } else sa_free(v); ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } 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); if (s->pcm_attrs.nchannels == n) { if (!(v = sa_newdup(int32_t, vol, n))) { ret = SA_ERROR_OOM; goto fail; } } else { unsigned i; if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) { ret = SA_ERROR_OOM; goto fail; } for (i = 0; i < s->pcm_attrs.nchannels; i++) v[i] = vol[0]; } ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_write_volume(s, v); if (ret == SA_SUCCESS) { sa_free(s->write_volume); s->write_volume = v; } else sa_free(v); fail: sa_mutex_unlock(s->mutex); return ret; } 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_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_pcm_rate(s, rate); if (ret == SA_SUCCESS) s->pcm_attrs.rate = rate; sa_mutex_unlock(s->mutex); return ret; } 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); s->user_data = (void*) value; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->adjust_rate = direction; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->adjust_rate = direction; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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_unlock(s->state == SA_STATE_INIT, SA_ERROR_STATE, s->mutex); s->adjust_pcm_format = direction; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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); if (s->state == SA_STATE_INIT) { *state = s->state; ret = SA_SUCCESS; } else ret = driver_get_state(s, state); sa_mutex_unlock(s->mutex); return ret; } 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_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_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_return_val_if_fail(!s->codec, SA_ERROR_STATE); /* No locking necessary since we only touch immutable fields here */ *nchannels = s->pcm_attrs.nchannels; return SA_SUCCESS; } 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_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; return SA_SUCCESS; } 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); /* No locking necessary since we only touch immutable fields here */ *access_mode = s->mode; return SA_SUCCESS; } 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(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_overall_latency(sa_stream *s, size_t *nbytes) { size_t overall_latency, immutable_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); overall_latency = s->write_overall_latency; immutable_latency = s->write_immutable_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, &overall_latency, &immutable_latency, &process_time)) < 0) goto fail; *nbytes = overall_latency; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } int sa_stream_get_read_overall_latency(sa_stream *s, size_t *nbytes) { size_t overall_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); overall_latency = s->read_overall_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, &overall_latency, NULL, &process_time)) < 0) goto fail; *nbytes = overall_latency; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } int sa_stream_get_write_immutable_latency(sa_stream *s, size_t *nbytes) { size_t overall_latency, immutable_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); overall_latency = s->write_overall_latency; immutable_latency = s->write_immutable_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, &overall_latency, &immutable_latency, &process_time)) < 0) goto fail; *nbytes = immutable_latency; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } int sa_stream_get_write_process_time(sa_stream *s, size_t *nbytes) { size_t overall_latency, immutable_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); overall_latency = s->write_overall_latency; immutable_latency = s->write_immutable_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, &overall_latency, &immutable_latency, &process_time)) < 0) goto fail; *nbytes = process_time; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } int sa_stream_get_read_process_time(sa_stream *s, size_t *nbytes) { size_t overall_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); overall_latency = s->read_overall_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, &overall_latency, NULL, &process_time)) < 0) goto fail; *nbytes = process_time; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } int sa_stream_get_pcm_channel_map(sa_stream *s, sa_channel_t **map, unsigned *n) { 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(n, SA_ERROR_INVALID); sa_return_val_if_fail(!s->codec, SA_ERROR_STATE); sa_mutex_lock(s->mutex); if (!(m = sa_newdup(sa_channel_t, s->pcm_attrs.channel_map, s->pcm_attrs.nchannels))) { ret = SA_ERROR_OOM; goto fail; } *map = m; *n = s->pcm_attrs.nchannels; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } 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); *mode = s->xrun_mode; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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); *enabled = s->ni_enabled; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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); *enabled = s->dynamic_rate_enabled; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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(driver, SA_ERROR_INVALID); sa_mutex_lock(s->mutex); 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; } *driver = t; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } 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(device, SA_ERROR_INVALID); sa_mutex_lock(s->mutex); 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; } *device = t; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } 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(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_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; } *vol = v; *n = s->pcm_attrs.nchannels; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } 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(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_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; } *vol = v; *n = s->pcm_attrs.nchannels; ret = SA_SUCCESS; fail: sa_mutex_unlock(s->mutex); return ret; } 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_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_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_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 *s, sa_adjust_t *direction) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(direction, SA_ERROR_INVALID); 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_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); *value = s->user_data; sa_mutex_unlock(s->mutex); return SA_SUCCESS; } 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); 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 *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); 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 ret; } 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); 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 *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_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 *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_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 *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 *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 *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_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 *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_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 *s, size_t *nbytes) { 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_RDONLY, SA_ERROR_STATE); sa_mutex_lock(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_get_read_size(s, nbytes); fail: sa_mutex_unlock(s->mutex); return ret; } 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(nbytes, SA_ERROR_INVALID); sa_return_val_if_fail(s->mode & SA_MODE_WRONLY, SA_ERROR_STATE); sa_mutex_lock(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_get_write_size(s, nbytes); fail: sa_mutex_unlock(s->mutex); return ret; } int sa_stream_start(sa_stream *s) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_mutex_lock(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_resume(s); fail: sa_mutex_unlock(s->mutex); return ret; } int sa_stream_stop(sa_stream *s) { int ret; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_mutex_lock(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_pause(s); fail: sa_mutex_unlock(s->mutex); return ret; } 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_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; } size_t sa_get_pcm_sample_size(sa_pcm_format_t f) { switch (f) { case SA_PCM_FORMAT_U8: case SA_PCM_FORMAT_ULAW: case SA_PCM_FORMAT_ALAW: return 1; case SA_PCM_FORMAT_S16_LE: case SA_PCM_FORMAT_S16_BE: return 2; case SA_PCM_FORMAT_S24_LE: case SA_PCM_FORMAT_S24_BE: return 3; case SA_PCM_FORMAT_S32_LE: case SA_PCM_FORMAT_S32_BE: case SA_PCM_FORMAT_FLOAT32_LE: case SA_PCM_FORMAT_FLOAT32_BE: return 4; case _SA_PCM_FORMAT_MAX: ; } sa_assert_not_reached(); } 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" }; sa_return_val_if_fail(code <= 0, NULL); sa_return_val_if_fail(code > _SA_ERROR_MAX, NULL); return error_table[-code]; } float volume_to_dB(int32_t v) { return (float) v / 100.0f; } int32_t volume_from_dB(float dB) { return (int32_t) lrintf(dB * 100.0f); } float volume_to_linear(int32_t v) { if (v <= SA_VOLUME_MUTED) return 0; return powf(10.0f, volume_to_dB(v) / 20.0f); } int32_t volume_from_linear(float f) { if (f <= 0) return SA_VOLUME_MUTED; return volume_from_dB(20.0f * log10f(f)); } 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); /* We don't lock here since the data we access here is immutable */ if (round_up) nbytes += s->pcm_frame_size - 1U; return (unsigned) (nbytes / s->pcm_frame_size); } 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); /* We don't lock here since the data we access here is immutable */ return (size_t) nframes * s->pcm_frame_size; } 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); /* We don't lock here since the data we access here is immutable */ nframes = sa_stream_bytes_to_frames(s, nbytes, round_up); return sa_stream_frames_to_usec(s, nframes, round_up); } size_t sa_stream_usec_to_bytes(sa_stream *s, uint64_t usec, sa_bool_t round_up) { unsigned nframes; sa_return_val_if_fail(s, (size_t) -1); sa_return_val_if_fail(!s->codec, (size_t) -1); /* We don't lock here since the data we access here is immutable */ nframes = sa_stream_usec_to_frames(s, usec, round_up); return sa_stream_frames_to_bytes(s, nframes); } 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); u = (uint64_t) nframes; u *= 1000000LLU; sa_mutex_lock(s->mutex); if (round_up) u += (uint64_t) s->pcm_attrs.rate - 1LLU; u /= (uint64_t) s->pcm_attrs.rate; sa_mutex_unlock(s->mutex); return u; } 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_mutex_lock(s->mutex); usec *= (uint64_t) s->pcm_attrs.rate; sa_mutex_unlock(s->mutex); if (round_up) usec += 999999LLU; usec /= 1000000LLU; return (unsigned) usec; } 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); /* We don't lock here since the data we access here is immutable */ return (nbytes % (int64_t) s->pcm_frame_size) == 0; }