1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/libcubeb/src/cubeb_sndio.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,357 @@ 1.4 +/* 1.5 + * Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org> 1.6 + * 1.7 + * This program is made available under an ISC-style license. See the 1.8 + * accompanying file LICENSE for details. 1.9 + */ 1.10 +#include <poll.h> 1.11 +#include <pthread.h> 1.12 +#include <sndio.h> 1.13 +#include <stdlib.h> 1.14 +#include <stdio.h> 1.15 +#include <assert.h> 1.16 +#include "cubeb/cubeb.h" 1.17 +#include "cubeb-internal.h" 1.18 + 1.19 +#if defined(CUBEB_SNDIO_DEBUG) 1.20 +#define DPR(...) fprintf(stderr, __VA_ARGS__); 1.21 +#else 1.22 +#define DPR(...) do {} while(0) 1.23 +#endif 1.24 + 1.25 +static struct cubeb_ops const sndio_ops; 1.26 + 1.27 +struct cubeb { 1.28 + struct cubeb_ops const * ops; 1.29 +}; 1.30 + 1.31 +struct cubeb_stream { 1.32 + cubeb * context; 1.33 + pthread_t th; /* to run real-time audio i/o */ 1.34 + pthread_mutex_t mtx; /* protects hdl and pos */ 1.35 + struct sio_hdl *hdl; /* link us to sndio */ 1.36 + int active; /* cubec_start() called */ 1.37 + int conv; /* need float->s16 conversion */ 1.38 + unsigned char *buf; /* data is prepared here */ 1.39 + unsigned int nfr; /* number of frames in buf */ 1.40 + unsigned int bpf; /* bytes per frame */ 1.41 + unsigned int pchan; /* number of play channels */ 1.42 + uint64_t rdpos; /* frame number Joe hears right now */ 1.43 + uint64_t wrpos; /* number of written frames */ 1.44 + cubeb_data_callback data_cb; /* cb to preapare data */ 1.45 + cubeb_state_callback state_cb; /* cb to notify about state changes */ 1.46 + void *arg; /* user arg to {data,state}_cb */ 1.47 +}; 1.48 + 1.49 +static void 1.50 +float_to_s16(void *ptr, long nsamp) 1.51 +{ 1.52 + int16_t *dst = ptr; 1.53 + float *src = ptr; 1.54 + 1.55 + while (nsamp-- > 0) 1.56 + *(dst++) = *(src++) * 32767; 1.57 +} 1.58 + 1.59 +static void 1.60 +sndio_onmove(void *arg, int delta) 1.61 +{ 1.62 + cubeb_stream *s = (cubeb_stream *)arg; 1.63 + 1.64 + s->rdpos += delta; 1.65 +} 1.66 + 1.67 +static void * 1.68 +sndio_mainloop(void *arg) 1.69 +{ 1.70 +#define MAXFDS 8 1.71 + struct pollfd pfds[MAXFDS]; 1.72 + cubeb_stream *s = arg; 1.73 + int n, nfds, revents, state; 1.74 + size_t start = 0, end = 0; 1.75 + long nfr; 1.76 + 1.77 + DPR("sndio_mainloop()\n"); 1.78 + s->state_cb(s, s->arg, CUBEB_STATE_STARTED); 1.79 + pthread_mutex_lock(&s->mtx); 1.80 + if (!sio_start(s->hdl)) { 1.81 + pthread_mutex_unlock(&s->mtx); 1.82 + return NULL; 1.83 + } 1.84 + DPR("sndio_mainloop(), started\n"); 1.85 + 1.86 + start = end = s->nfr; 1.87 + for (;;) { 1.88 + if (!s->active) { 1.89 + DPR("sndio_mainloop() stopped\n"); 1.90 + state = CUBEB_STATE_STOPPED; 1.91 + break; 1.92 + } 1.93 + if (start == end) { 1.94 + if (end < s->nfr) { 1.95 + DPR("sndio_mainloop() drained\n"); 1.96 + state = CUBEB_STATE_DRAINED; 1.97 + break; 1.98 + } 1.99 + pthread_mutex_unlock(&s->mtx); 1.100 + nfr = s->data_cb(s, s->arg, s->buf, s->nfr); 1.101 + pthread_mutex_lock(&s->mtx); 1.102 + if (nfr < 0) { 1.103 + DPR("sndio_mainloop() cb err\n"); 1.104 + state = CUBEB_STATE_ERROR; 1.105 + break; 1.106 + } 1.107 + if (s->conv) 1.108 + float_to_s16(s->buf, nfr * s->pchan); 1.109 + start = 0; 1.110 + end = nfr * s->bpf; 1.111 + } 1.112 + if (end == 0) 1.113 + continue; 1.114 + nfds = sio_pollfd(s->hdl, pfds, POLLOUT); 1.115 + if (nfds > 0) { 1.116 + pthread_mutex_unlock(&s->mtx); 1.117 + n = poll(pfds, nfds, -1); 1.118 + pthread_mutex_lock(&s->mtx); 1.119 + if (n < 0) 1.120 + continue; 1.121 + } 1.122 + revents = sio_revents(s->hdl, pfds); 1.123 + if (revents & POLLHUP) 1.124 + break; 1.125 + if (revents & POLLOUT) { 1.126 + n = sio_write(s->hdl, s->buf + start, end - start); 1.127 + if (n == 0) { 1.128 + DPR("sndio_mainloop() werr\n"); 1.129 + state = CUBEB_STATE_ERROR; 1.130 + break; 1.131 + } 1.132 + s->wrpos = 0; 1.133 + start += n; 1.134 + } 1.135 + } 1.136 + sio_stop(s->hdl); 1.137 + s->rdpos = s->wrpos; 1.138 + pthread_mutex_unlock(&s->mtx); 1.139 + s->state_cb(s, s->arg, state); 1.140 + return NULL; 1.141 +} 1.142 + 1.143 +/*static*/ int 1.144 +sndio_init(cubeb **context, char const *context_name) 1.145 +{ 1.146 + DPR("sndio_init(%s)\n", context_name); 1.147 + *context = malloc(sizeof(*context)); 1.148 + (*context)->ops = &sndio_ops; 1.149 + (void)context_name; 1.150 + return CUBEB_OK; 1.151 +} 1.152 + 1.153 +static char const * 1.154 +sndio_get_backend_id(cubeb *context) 1.155 +{ 1.156 + return "sndio"; 1.157 +} 1.158 + 1.159 +static void 1.160 +sndio_destroy(cubeb *context) 1.161 +{ 1.162 + DPR("sndio_destroy()\n"); 1.163 + free(context); 1.164 +} 1.165 + 1.166 +static int 1.167 +sndio_stream_init(cubeb *context, 1.168 + cubeb_stream **stream, 1.169 + char const *stream_name, 1.170 + cubeb_stream_params stream_params, unsigned int latency, 1.171 + cubeb_data_callback data_callback, 1.172 + cubeb_state_callback state_callback, 1.173 + void *user_ptr) 1.174 +{ 1.175 + cubeb_stream *s; 1.176 + struct sio_par wpar, rpar; 1.177 + DPR("sndio_stream_init(%s)\n", stream_name); 1.178 + size_t size; 1.179 + 1.180 + s = malloc(sizeof(cubeb_stream)); 1.181 + if (s == NULL) 1.182 + return CUBEB_ERROR; 1.183 + s->context = context; 1.184 + s->hdl = sio_open(NULL, SIO_PLAY, 0); 1.185 + if (s->hdl == NULL) { 1.186 + free(s); 1.187 + DPR("sndio_stream_init(), sio_open() failed\n"); 1.188 + return CUBEB_ERROR; 1.189 + } 1.190 + sio_initpar(&wpar); 1.191 + wpar.sig = 1; 1.192 + wpar.bits = 16; 1.193 + switch (stream_params.format) { 1.194 + case CUBEB_SAMPLE_S16LE: 1.195 + wpar.le = 1; 1.196 + break; 1.197 + case CUBEB_SAMPLE_S16BE: 1.198 + wpar.le = 0; 1.199 + break; 1.200 + case CUBEB_SAMPLE_FLOAT32NE: 1.201 + wpar.le = SIO_LE_NATIVE; 1.202 + break; 1.203 + default: 1.204 + DPR("sndio_stream_init() unsupported format\n"); 1.205 + return CUBEB_ERROR_INVALID_FORMAT; 1.206 + } 1.207 + wpar.rate = stream_params.rate; 1.208 + wpar.pchan = stream_params.channels; 1.209 + wpar.appbufsz = latency * wpar.rate / 1000; 1.210 + if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) { 1.211 + sio_close(s->hdl); 1.212 + free(s); 1.213 + DPR("sndio_stream_init(), sio_setpar() failed\n"); 1.214 + return CUBEB_ERROR; 1.215 + } 1.216 + if (rpar.bits != wpar.bits || rpar.le != wpar.le || 1.217 + rpar.sig != wpar.sig || rpar.rate != wpar.rate || 1.218 + rpar.pchan != wpar.pchan) { 1.219 + sio_close(s->hdl); 1.220 + free(s); 1.221 + DPR("sndio_stream_init() unsupported params\n"); 1.222 + return CUBEB_ERROR_INVALID_FORMAT; 1.223 + } 1.224 + sio_onmove(s->hdl, sndio_onmove, s); 1.225 + s->active = 0; 1.226 + s->nfr = rpar.round; 1.227 + s->bpf = rpar.bps * rpar.pchan; 1.228 + s->pchan = rpar.pchan; 1.229 + s->data_cb = data_callback; 1.230 + s->state_cb = state_callback; 1.231 + s->arg = user_ptr; 1.232 + s->mtx = PTHREAD_MUTEX_INITIALIZER; 1.233 + s->rdpos = s->wrpos = 0; 1.234 + if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) { 1.235 + s->conv = 1; 1.236 + size = rpar.round * rpar.pchan * sizeof(float); 1.237 + } else { 1.238 + s->conv = 0; 1.239 + size = rpar.round * rpar.pchan * rpar.bps; 1.240 + } 1.241 + s->buf = malloc(size); 1.242 + if (s->buf == NULL) { 1.243 + sio_close(s->hdl); 1.244 + free(s); 1.245 + return CUBEB_ERROR; 1.246 + } 1.247 + *stream = s; 1.248 + DPR("sndio_stream_init() end, ok\n"); 1.249 + (void)context; 1.250 + (void)stream_name; 1.251 + return CUBEB_OK; 1.252 +} 1.253 + 1.254 +static int 1.255 +sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) 1.256 +{ 1.257 + assert(ctx && max_channels); 1.258 + 1.259 + *max_channels = 8; 1.260 + 1.261 + return CUBEB_OK; 1.262 +} 1.263 + 1.264 +static int 1.265 +sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) 1.266 +{ 1.267 + // XXX Not yet implemented. 1.268 + *rate = 44100; 1.269 + 1.270 + return CUBEB_OK; 1.271 +} 1.272 + 1.273 +static int 1.274 +sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) 1.275 +{ 1.276 + // XXX Not yet implemented. 1.277 + latency_ms = 40; 1.278 + 1.279 + return CUBEB_OK; 1.280 +} 1.281 + 1.282 +static void 1.283 +sndio_stream_destroy(cubeb_stream *s) 1.284 +{ 1.285 + DPR("sndio_stream_destroy()\n"); 1.286 + sio_close(s->hdl); 1.287 + free(s); 1.288 +} 1.289 + 1.290 +static int 1.291 +sndio_stream_start(cubeb_stream *s) 1.292 +{ 1.293 + int err; 1.294 + 1.295 + DPR("sndio_stream_start()\n"); 1.296 + s->active = 1; 1.297 + err = pthread_create(&s->th, NULL, sndio_mainloop, s); 1.298 + if (err) { 1.299 + s->active = 0; 1.300 + return CUBEB_ERROR; 1.301 + } 1.302 + return CUBEB_OK; 1.303 +} 1.304 + 1.305 +static int 1.306 +sndio_stream_stop(cubeb_stream *s) 1.307 +{ 1.308 + void *dummy; 1.309 + 1.310 + DPR("sndio_stream_stop()\n"); 1.311 + if (s->active) { 1.312 + s->active = 0; 1.313 + pthread_join(s->th, &dummy); 1.314 + } 1.315 + return CUBEB_OK; 1.316 +} 1.317 + 1.318 +static int 1.319 +sndio_stream_get_position(cubeb_stream *s, uint64_t *p) 1.320 +{ 1.321 + pthread_mutex_lock(&s->mtx); 1.322 + DPR("sndio_stream_get_position() %lld\n", s->rdpos); 1.323 + *p = s->rdpos; 1.324 + pthread_mutex_unlock(&s->mtx); 1.325 + return CUBEB_OK; 1.326 +} 1.327 + 1.328 +static int 1.329 +sndio_stream_set_volume(cubeb_stream *s, float volume) 1.330 +{ 1.331 + DPR("sndio_stream_set_volume(%f)\n", volume); 1.332 + pthread_mutex_lock(&s->mtx); 1.333 + sio_setvol(s->hdl, SIO_MAXVOL * volume); 1.334 + pthread_mutex_unlock(&s->mtx); 1.335 + return CUBEB_OK; 1.336 +} 1.337 + 1.338 +int 1.339 +sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency) 1.340 +{ 1.341 + // http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open 1.342 + // in the "Measuring the latency and buffers usage" paragraph. 1.343 + *latency = stm->wrpos - stm->rdpos; 1.344 + return CUBEB_OK; 1.345 +} 1.346 + 1.347 +static struct cubeb_ops const sndio_ops = { 1.348 + .init = sndio_init, 1.349 + .get_backend_id = sndio_get_backend_id, 1.350 + .get_max_channel_count = sndio_get_max_channel_count, 1.351 + .get_min_latency = sndio_get_min_latency, 1.352 + .get_preferred_sample_rate = sndio_get_preferred_sample_rate, 1.353 + .destroy = sndio_destroy, 1.354 + .stream_init = sndio_stream_init, 1.355 + .stream_destroy = sndio_stream_destroy, 1.356 + .stream_start = sndio_stream_start, 1.357 + .stream_stop = sndio_stream_stop, 1.358 + .stream_get_position = sndio_stream_get_position, 1.359 + .stream_get_latency = sndio_stream_get_latency 1.360 +};