summaryrefslogtreecommitdiffstats
path: root/media/libcubeb/src/cubeb_sndio.c
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /media/libcubeb/src/cubeb_sndio.c
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'media/libcubeb/src/cubeb_sndio.c')
-rw-r--r--media/libcubeb/src/cubeb_sndio.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/media/libcubeb/src/cubeb_sndio.c b/media/libcubeb/src/cubeb_sndio.c
new file mode 100644
index 000000000..793789765
--- /dev/null
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org>
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+#include <math.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sndio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+
+#if defined(CUBEB_SNDIO_DEBUG)
+#define DPR(...) fprintf(stderr, __VA_ARGS__);
+#else
+#define DPR(...) do {} while(0)
+#endif
+
+static struct cubeb_ops const sndio_ops;
+
+struct cubeb {
+ struct cubeb_ops const * ops;
+};
+
+struct cubeb_stream {
+ cubeb * context;
+ pthread_t th; /* to run real-time audio i/o */
+ pthread_mutex_t mtx; /* protects hdl and pos */
+ struct sio_hdl *hdl; /* link us to sndio */
+ int active; /* cubec_start() called */
+ int conv; /* need float->s16 conversion */
+ unsigned char *buf; /* data is prepared here */
+ unsigned int nfr; /* number of frames in buf */
+ unsigned int bpf; /* bytes per frame */
+ unsigned int pchan; /* number of play channels */
+ uint64_t rdpos; /* frame number Joe hears right now */
+ uint64_t wrpos; /* number of written frames */
+ cubeb_data_callback data_cb; /* cb to preapare data */
+ cubeb_state_callback state_cb; /* cb to notify about state changes */
+ void *arg; /* user arg to {data,state}_cb */
+};
+
+static void
+float_to_s16(void *ptr, long nsamp)
+{
+ int16_t *dst = ptr;
+ float *src = ptr;
+ int s;
+
+ while (nsamp-- > 0) {
+ s = lrintf(*(src++) * 32768);
+ if (s < -32768)
+ s = -32768;
+ else if (s > 32767)
+ s = 32767;
+ *(dst++) = s;
+ }
+}
+
+static void
+sndio_onmove(void *arg, int delta)
+{
+ cubeb_stream *s = (cubeb_stream *)arg;
+
+ s->rdpos += delta * s->bpf;
+}
+
+static void *
+sndio_mainloop(void *arg)
+{
+#define MAXFDS 8
+ struct pollfd pfds[MAXFDS];
+ cubeb_stream *s = arg;
+ int n, nfds, revents, state = CUBEB_STATE_STARTED;
+ size_t start = 0, end = 0;
+ long nfr;
+
+ DPR("sndio_mainloop()\n");
+ s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
+ pthread_mutex_lock(&s->mtx);
+ if (!sio_start(s->hdl)) {
+ pthread_mutex_unlock(&s->mtx);
+ return NULL;
+ }
+ DPR("sndio_mainloop(), started\n");
+
+ start = end = s->nfr;
+ for (;;) {
+ if (!s->active) {
+ DPR("sndio_mainloop() stopped\n");
+ state = CUBEB_STATE_STOPPED;
+ break;
+ }
+ if (start == end) {
+ if (end < s->nfr) {
+ DPR("sndio_mainloop() drained\n");
+ state = CUBEB_STATE_DRAINED;
+ break;
+ }
+ pthread_mutex_unlock(&s->mtx);
+ nfr = s->data_cb(s, s->arg, NULL, s->buf, s->nfr);
+ pthread_mutex_lock(&s->mtx);
+ if (nfr < 0) {
+ DPR("sndio_mainloop() cb err\n");
+ state = CUBEB_STATE_ERROR;
+ break;
+ }
+ if (s->conv)
+ float_to_s16(s->buf, nfr * s->pchan);
+ start = 0;
+ end = nfr * s->bpf;
+ }
+ if (end == 0)
+ continue;
+ nfds = sio_pollfd(s->hdl, pfds, POLLOUT);
+ if (nfds > 0) {
+ pthread_mutex_unlock(&s->mtx);
+ n = poll(pfds, nfds, -1);
+ pthread_mutex_lock(&s->mtx);
+ if (n < 0)
+ continue;
+ }
+ revents = sio_revents(s->hdl, pfds);
+ if (revents & POLLHUP)
+ break;
+ if (revents & POLLOUT) {
+ n = sio_write(s->hdl, s->buf + start, end - start);
+ if (n == 0) {
+ DPR("sndio_mainloop() werr\n");
+ state = CUBEB_STATE_ERROR;
+ break;
+ }
+ s->wrpos += n;
+ start += n;
+ }
+ }
+ sio_stop(s->hdl);
+ s->rdpos = s->wrpos;
+ pthread_mutex_unlock(&s->mtx);
+ s->state_cb(s, s->arg, state);
+ return NULL;
+}
+
+/*static*/ int
+sndio_init(cubeb **context, char const *context_name)
+{
+ DPR("sndio_init(%s)\n", context_name);
+ *context = malloc(sizeof(*context));
+ (*context)->ops = &sndio_ops;
+ (void)context_name;
+ return CUBEB_OK;
+}
+
+static char const *
+sndio_get_backend_id(cubeb *context)
+{
+ return "sndio";
+}
+
+static void
+sndio_destroy(cubeb *context)
+{
+ DPR("sndio_destroy()\n");
+ free(context);
+}
+
+static int
+sndio_stream_init(cubeb * context,
+ cubeb_stream ** stream,
+ char const * stream_name,
+ cubeb_devid input_device,
+ cubeb_stream_params * input_stream_params,
+ cubeb_devid output_device,
+ cubeb_stream_params * output_stream_params,
+ unsigned int latency_frames,
+ cubeb_data_callback data_callback,
+ cubeb_state_callback state_callback,
+ void *user_ptr)
+{
+ cubeb_stream *s;
+ struct sio_par wpar, rpar;
+ DPR("sndio_stream_init(%s)\n", stream_name);
+ size_t size;
+
+ assert(!input_stream_params && "not supported.");
+ if (input_device || output_device) {
+ /* Device selection not yet implemented. */
+ return CUBEB_ERROR_DEVICE_UNAVAILABLE;
+ }
+
+ s = malloc(sizeof(cubeb_stream));
+ if (s == NULL)
+ return CUBEB_ERROR;
+ s->context = context;
+ s->hdl = sio_open(NULL, SIO_PLAY, 1);
+ if (s->hdl == NULL) {
+ free(s);
+ DPR("sndio_stream_init(), sio_open() failed\n");
+ return CUBEB_ERROR;
+ }
+ sio_initpar(&wpar);
+ wpar.sig = 1;
+ wpar.bits = 16;
+ switch (output_stream_params->format) {
+ case CUBEB_SAMPLE_S16LE:
+ wpar.le = 1;
+ break;
+ case CUBEB_SAMPLE_S16BE:
+ wpar.le = 0;
+ break;
+ case CUBEB_SAMPLE_FLOAT32NE:
+ wpar.le = SIO_LE_NATIVE;
+ break;
+ default:
+ DPR("sndio_stream_init() unsupported format\n");
+ return CUBEB_ERROR_INVALID_FORMAT;
+ }
+ wpar.rate = output_stream_params->rate;
+ wpar.pchan = output_stream_params->channels;
+ wpar.appbufsz = latency_frames;
+ if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
+ sio_close(s->hdl);
+ free(s);
+ DPR("sndio_stream_init(), sio_setpar() failed\n");
+ return CUBEB_ERROR;
+ }
+ if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
+ rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
+ rpar.pchan != wpar.pchan) {
+ sio_close(s->hdl);
+ free(s);
+ DPR("sndio_stream_init() unsupported params\n");
+ return CUBEB_ERROR_INVALID_FORMAT;
+ }
+ sio_onmove(s->hdl, sndio_onmove, s);
+ s->active = 0;
+ s->nfr = rpar.round;
+ s->bpf = rpar.bps * rpar.pchan;
+ s->pchan = rpar.pchan;
+ s->data_cb = data_callback;
+ s->state_cb = state_callback;
+ s->arg = user_ptr;
+ s->mtx = PTHREAD_MUTEX_INITIALIZER;
+ s->rdpos = s->wrpos = 0;
+ if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) {
+ s->conv = 1;
+ size = rpar.round * rpar.pchan * sizeof(float);
+ } else {
+ s->conv = 0;
+ size = rpar.round * rpar.pchan * rpar.bps;
+ }
+ s->buf = malloc(size);
+ if (s->buf == NULL) {
+ sio_close(s->hdl);
+ free(s);
+ return CUBEB_ERROR;
+ }
+ *stream = s;
+ DPR("sndio_stream_init() end, ok\n");
+ (void)context;
+ (void)stream_name;
+ return CUBEB_OK;
+}
+
+static int
+sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+ assert(ctx && max_channels);
+
+ *max_channels = 8;
+
+ return CUBEB_OK;
+}
+
+static int
+sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
+{
+ // XXX Not yet implemented.
+ *rate = 44100;
+
+ return CUBEB_OK;
+}
+
+static int
+sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
+{
+ // XXX Not yet implemented.
+ *latency_frames = 2048;
+
+ return CUBEB_OK;
+}
+
+static void
+sndio_stream_destroy(cubeb_stream *s)
+{
+ DPR("sndio_stream_destroy()\n");
+ sio_close(s->hdl);
+ free(s);
+}
+
+static int
+sndio_stream_start(cubeb_stream *s)
+{
+ int err;
+
+ DPR("sndio_stream_start()\n");
+ s->active = 1;
+ err = pthread_create(&s->th, NULL, sndio_mainloop, s);
+ if (err) {
+ s->active = 0;
+ return CUBEB_ERROR;
+ }
+ return CUBEB_OK;
+}
+
+static int
+sndio_stream_stop(cubeb_stream *s)
+{
+ void *dummy;
+
+ DPR("sndio_stream_stop()\n");
+ if (s->active) {
+ s->active = 0;
+ pthread_join(s->th, &dummy);
+ }
+ return CUBEB_OK;
+}
+
+static int
+sndio_stream_get_position(cubeb_stream *s, uint64_t *p)
+{
+ pthread_mutex_lock(&s->mtx);
+ DPR("sndio_stream_get_position() %lld\n", s->rdpos);
+ *p = s->rdpos / s->bpf;
+ pthread_mutex_unlock(&s->mtx);
+ return CUBEB_OK;
+}
+
+static int
+sndio_stream_set_volume(cubeb_stream *s, float volume)
+{
+ DPR("sndio_stream_set_volume(%f)\n", volume);
+ pthread_mutex_lock(&s->mtx);
+ sio_setvol(s->hdl, SIO_MAXVOL * volume);
+ pthread_mutex_unlock(&s->mtx);
+ return CUBEB_OK;
+}
+
+int
+sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
+{
+ // http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open
+ // in the "Measuring the latency and buffers usage" paragraph.
+ *latency = (stm->wrpos - stm->rdpos) / stm->bpf;
+ return CUBEB_OK;
+}
+
+static struct cubeb_ops const sndio_ops = {
+ .init = sndio_init,
+ .get_backend_id = sndio_get_backend_id,
+ .get_max_channel_count = sndio_get_max_channel_count,
+ .get_min_latency = sndio_get_min_latency,
+ .get_preferred_sample_rate = sndio_get_preferred_sample_rate,
+ .enumerate_devices = NULL,
+ .destroy = sndio_destroy,
+ .stream_init = sndio_stream_init,
+ .stream_destroy = sndio_stream_destroy,
+ .stream_start = sndio_stream_start,
+ .stream_stop = sndio_stream_stop,
+ .stream_get_position = sndio_stream_get_position,
+ .stream_get_latency = sndio_stream_get_latency,
+ .stream_set_volume = sndio_stream_set_volume,
+ .stream_set_panning = NULL,
+ .stream_get_current_device = NULL,
+ .stream_device_destroy = NULL,
+ .stream_register_device_changed_callback = NULL,
+ .register_device_collection_changed = NULL
+};