aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--Makefile.am5
-rw-r--r--configure.ac6
-rw-r--r--src/cubeb_sndio.c289
4 files changed, 299 insertions, 2 deletions
diff --git a/AUTHORS b/AUTHORS
index 8204f40..b7d5fc1 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,2 @@
Matthew Gregan <[email protected]>
+Alexandre Ratchov <[email protected]>
diff --git a/Makefile.am b/Makefile.am
index fe3f3ad..dc531b2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -36,10 +36,13 @@ endif
if DSOUND
src_libcubeb_la_SOURCES = src/cubeb_directsound.c
endif
+if SNDIO
+src_libcubeb_la_SOURCES = src/cubeb_sndio.c
+endif
EXTRA_src_libcubeb_la_SOURCES = \
cubeb_alsa.c cubeb_pulse.c \
- cubeb_coreaudio.c \
+ cubeb_coreaudio.c cubeb_sndio.c \
cubeb_directsound.c cubeb_winmm.c
src_libcubeb_la_LDFLAGS = -pthread -export-symbols-regex '^cubeb_' $(platform_lib)
diff --git a/configure.ac b/configure.ac
index 1f527c8..2cd0fc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,7 +48,7 @@ AM_PROG_LIBTOOL
dnl Choose the target platform audio API
AC_CHECK_HEADERS([alsa/asoundlib.h pulse/pulseaudio.h \
AudioToolbox/AudioToolbox.h \
- windows.h dsound.h])
+ windows.h dsound.h sndio.h])
platform_api="none"
platform_lib="none"
if test "x$ac_cv_header_pulse_pulseaudio_h" = "xyes"; then
@@ -60,6 +60,9 @@ elif test "x$ac_cv_header_alsa_asoundlib_h" = "xyes"; then
elif test "x$ac_cv_header_AudioToolbox_AudioToolbox_h" = "xyes"; then
platform_api="coreaudio"
platform_lib="-framework AudioToolbox"
+elif test "x$ac_cv_header_sndio_h" = "xyes"; then
+ platform_api="sndio"
+ platform_lib="-lsndio -lm"
elif test "x$ac_cv_header_windows_h" = "xyes"; then
# if test "x$ac_cv_header_dsound_h" = "xyes"; then
# platform_api="dsound"
@@ -72,6 +75,7 @@ AM_CONDITIONAL([ALSA], [test ${platform_api} = "alsa"])
AM_CONDITIONAL([COREAUDIO], [test ${platform_api} = "coreaudio"])
AM_CONDITIONAL([DSOUND], [test ${platform_api} = "dsound"])
AM_CONDITIONAL([WAVE], [test ${platform_api} = "wave"])
+AM_CONDITIONAL([SNDIO], [test ${platform_api} = "sndio"])
dnl Check for doxygen
AC_ARG_ENABLE([doc],
diff --git a/src/cubeb_sndio.c b/src/cubeb_sndio.c
new file mode 100644
index 0000000..922a8a9
--- /dev/null
+++ b/src/cubeb_sndio.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2011 Alexandre Ratchov <[email protected]>
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+#include <poll.h>
+#include <pthread.h>
+#include <sndio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "cubeb/cubeb.h"
+
+#ifdef CUBEB_SNDIO_DEBUG
+#define DPR(...) fprintf(stderr, __VA_ARGS__);
+#else
+#define DPR(...) do {} while(0)
+#endif
+
+struct cubeb_stream {
+ 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 */
+};
+
+void
+float_to_s16(void *ptr, long nsamp)
+{
+ int16_t *dst = ptr;
+ float *src = ptr;
+
+ while (nsamp-- > 0)
+ *(dst++) = *(src++) * 32767;
+}
+
+void
+cubeb_onmove(void *arg, int delta)
+{
+ struct cubeb_stream *s = (struct cubeb_stream *)arg;
+
+ s->rdpos += delta;
+}
+
+void *
+cubeb_mainloop(void *arg)
+{
+#define MAXFDS 8
+ struct pollfd pfds[MAXFDS];
+ struct cubeb_stream *s = arg;
+ int n, nfds, revents, state;
+ size_t start = 0, end = 0;
+ long nfr;
+
+ DPR("cubeb_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("cubeb_mainloop(), started\n");
+
+ start = end = s->nfr;
+ for (;;) {
+ if (!s->active) {
+ DPR("cubeb_mainloop() stopped\n");
+ state = CUBEB_STATE_STOPPED;
+ break;
+ }
+ if (start == end) {
+ if (end < s->nfr) {
+ DPR("cubeb_mainloop() drained\n");
+ state = CUBEB_STATE_DRAINED;
+ break;
+ }
+ pthread_mutex_unlock(&s->mtx);
+ nfr = s->data_cb(s, s->arg, s->buf, s->nfr);
+ pthread_mutex_lock(&s->mtx);
+ if (nfr < 0) {
+ DPR("cubeb_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("cubeb_mainloop() werr\n");
+ state = CUBEB_STATE_ERROR;
+ break;
+ }
+ s->wrpos = 0;
+ start += n;
+ }
+ }
+ sio_stop(s->hdl);
+ s->rdpos = s->wrpos;
+ pthread_mutex_unlock(&s->mtx);
+ s->state_cb(s, s->arg, state);
+ return NULL;
+}
+
+int
+cubeb_init(cubeb **context, char const *context_name)
+{
+ DPR("cubeb_init(%s)\n", context_name);
+ *context = (void *)0xdeadbeef;
+ (void)context_name;
+ return CUBEB_OK;
+}
+
+void
+cubeb_destroy(cubeb *context)
+{
+ DPR("cubeb_destroy()\n");
+ (void)context;
+}
+
+int
+cubeb_stream_init(cubeb *context,
+ cubeb_stream **stream,
+ char const *stream_name,
+ cubeb_stream_params stream_params, unsigned int latency,
+ cubeb_data_callback data_callback,
+ cubeb_state_callback state_callback,
+ void *user_ptr)
+{
+ struct cubeb_stream *s;
+ struct sio_par wpar, rpar;
+ DPR("cubeb_stream_init(%s)\n", stream_name);
+ unsigned minbufsz = stream_params.rate * 50 / 1000;
+ size_t size;
+
+ s = malloc(sizeof(struct cubeb_stream));
+ if (s == NULL)
+ return CUBEB_ERROR;
+ s->hdl = sio_open(NULL, SIO_PLAY, 0);
+ if (s->hdl == NULL) {
+ free(s);
+ DPR("cubeb_stream_init(), sio_open() failed\n");
+ return CUBEB_ERROR;
+ }
+ sio_initpar(&wpar);
+ wpar.sig = 1;
+ wpar.bits = 16;
+ switch (stream_params.format) {
+ case CUBEB_SAMPLE_S16LE:
+ wpar.le = 1;
+ break;
+ case CUBEB_SAMPLE_S16BE:
+ wpar.le = 1;
+ break;
+ case CUBEB_SAMPLE_FLOAT32NE:
+ wpar.le = SIO_LE_NATIVE;
+ break;
+ default:
+ DPR("cubeb_stream_init() unsupported format\n");
+ return CUBEB_ERROR_INVALID_FORMAT;
+ }
+ wpar.rate = stream_params.rate;
+ wpar.pchan = stream_params.channels;
+ wpar.appbufsz = latency < minbufsz ? minbufsz : latency;
+ if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
+ sio_close(s->hdl);
+ free(s);
+ DPR("cubeb_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("cubeb_stream_init() unsupported params\n");
+ return CUBEB_ERROR_INVALID_FORMAT;
+ }
+ sio_onmove(s->hdl, cubeb_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 (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("cubeb_stream_init() end, ok\n");
+ (void)context;
+ (void)stream_name;
+ return CUBEB_OK;
+}
+
+void
+cubeb_stream_destroy(cubeb_stream *s)
+{
+ DPR("cubeb_stream_destroy()\n");
+ sio_close(s->hdl);
+ free(s);
+}
+
+int
+cubeb_stream_start(cubeb_stream *s)
+{
+ int err;
+
+ DPR("cubeb_stream_start()\n");
+ s->active = 1;
+ err = pthread_create(&s->th, NULL, cubeb_mainloop, s);
+ if (err) {
+ s->active = 0;
+ return CUBEB_ERROR;
+ }
+ return CUBEB_OK;
+}
+
+int
+cubeb_stream_stop(cubeb_stream *s)
+{
+ void *dummy;
+
+ DPR("cubeb_stream_stop()\n");
+ if (s->active) {
+ s->active = 0;
+ pthread_join(s->th, &dummy);
+ }
+ return CUBEB_OK;
+}
+
+int
+cubeb_stream_get_position(cubeb_stream *s, uint64_t *p)
+{
+ pthread_mutex_lock(&s->mtx);
+ DPR("cubeb_stream_get_position() %lld\n", s->rdpos);
+ *p = s->rdpos;
+ pthread_mutex_unlock(&s->mtx);
+ return CUBEB_OK;
+}
+
+int
+cubeb_stream_set_volume(cubeb_stream *s, float volume)
+{
+ DPR("cubeb_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;
+}