media/libcubeb/src/cubeb_sndio.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /*
michael@0 2 * Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org>
michael@0 3 *
michael@0 4 * This program is made available under an ISC-style license. See the
michael@0 5 * accompanying file LICENSE for details.
michael@0 6 */
michael@0 7 #include <poll.h>
michael@0 8 #include <pthread.h>
michael@0 9 #include <sndio.h>
michael@0 10 #include <stdlib.h>
michael@0 11 #include <stdio.h>
michael@0 12 #include <assert.h>
michael@0 13 #include "cubeb/cubeb.h"
michael@0 14 #include "cubeb-internal.h"
michael@0 15
michael@0 16 #if defined(CUBEB_SNDIO_DEBUG)
michael@0 17 #define DPR(...) fprintf(stderr, __VA_ARGS__);
michael@0 18 #else
michael@0 19 #define DPR(...) do {} while(0)
michael@0 20 #endif
michael@0 21
michael@0 22 static struct cubeb_ops const sndio_ops;
michael@0 23
michael@0 24 struct cubeb {
michael@0 25 struct cubeb_ops const * ops;
michael@0 26 };
michael@0 27
michael@0 28 struct cubeb_stream {
michael@0 29 cubeb * context;
michael@0 30 pthread_t th; /* to run real-time audio i/o */
michael@0 31 pthread_mutex_t mtx; /* protects hdl and pos */
michael@0 32 struct sio_hdl *hdl; /* link us to sndio */
michael@0 33 int active; /* cubec_start() called */
michael@0 34 int conv; /* need float->s16 conversion */
michael@0 35 unsigned char *buf; /* data is prepared here */
michael@0 36 unsigned int nfr; /* number of frames in buf */
michael@0 37 unsigned int bpf; /* bytes per frame */
michael@0 38 unsigned int pchan; /* number of play channels */
michael@0 39 uint64_t rdpos; /* frame number Joe hears right now */
michael@0 40 uint64_t wrpos; /* number of written frames */
michael@0 41 cubeb_data_callback data_cb; /* cb to preapare data */
michael@0 42 cubeb_state_callback state_cb; /* cb to notify about state changes */
michael@0 43 void *arg; /* user arg to {data,state}_cb */
michael@0 44 };
michael@0 45
michael@0 46 static void
michael@0 47 float_to_s16(void *ptr, long nsamp)
michael@0 48 {
michael@0 49 int16_t *dst = ptr;
michael@0 50 float *src = ptr;
michael@0 51
michael@0 52 while (nsamp-- > 0)
michael@0 53 *(dst++) = *(src++) * 32767;
michael@0 54 }
michael@0 55
michael@0 56 static void
michael@0 57 sndio_onmove(void *arg, int delta)
michael@0 58 {
michael@0 59 cubeb_stream *s = (cubeb_stream *)arg;
michael@0 60
michael@0 61 s->rdpos += delta;
michael@0 62 }
michael@0 63
michael@0 64 static void *
michael@0 65 sndio_mainloop(void *arg)
michael@0 66 {
michael@0 67 #define MAXFDS 8
michael@0 68 struct pollfd pfds[MAXFDS];
michael@0 69 cubeb_stream *s = arg;
michael@0 70 int n, nfds, revents, state;
michael@0 71 size_t start = 0, end = 0;
michael@0 72 long nfr;
michael@0 73
michael@0 74 DPR("sndio_mainloop()\n");
michael@0 75 s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
michael@0 76 pthread_mutex_lock(&s->mtx);
michael@0 77 if (!sio_start(s->hdl)) {
michael@0 78 pthread_mutex_unlock(&s->mtx);
michael@0 79 return NULL;
michael@0 80 }
michael@0 81 DPR("sndio_mainloop(), started\n");
michael@0 82
michael@0 83 start = end = s->nfr;
michael@0 84 for (;;) {
michael@0 85 if (!s->active) {
michael@0 86 DPR("sndio_mainloop() stopped\n");
michael@0 87 state = CUBEB_STATE_STOPPED;
michael@0 88 break;
michael@0 89 }
michael@0 90 if (start == end) {
michael@0 91 if (end < s->nfr) {
michael@0 92 DPR("sndio_mainloop() drained\n");
michael@0 93 state = CUBEB_STATE_DRAINED;
michael@0 94 break;
michael@0 95 }
michael@0 96 pthread_mutex_unlock(&s->mtx);
michael@0 97 nfr = s->data_cb(s, s->arg, s->buf, s->nfr);
michael@0 98 pthread_mutex_lock(&s->mtx);
michael@0 99 if (nfr < 0) {
michael@0 100 DPR("sndio_mainloop() cb err\n");
michael@0 101 state = CUBEB_STATE_ERROR;
michael@0 102 break;
michael@0 103 }
michael@0 104 if (s->conv)
michael@0 105 float_to_s16(s->buf, nfr * s->pchan);
michael@0 106 start = 0;
michael@0 107 end = nfr * s->bpf;
michael@0 108 }
michael@0 109 if (end == 0)
michael@0 110 continue;
michael@0 111 nfds = sio_pollfd(s->hdl, pfds, POLLOUT);
michael@0 112 if (nfds > 0) {
michael@0 113 pthread_mutex_unlock(&s->mtx);
michael@0 114 n = poll(pfds, nfds, -1);
michael@0 115 pthread_mutex_lock(&s->mtx);
michael@0 116 if (n < 0)
michael@0 117 continue;
michael@0 118 }
michael@0 119 revents = sio_revents(s->hdl, pfds);
michael@0 120 if (revents & POLLHUP)
michael@0 121 break;
michael@0 122 if (revents & POLLOUT) {
michael@0 123 n = sio_write(s->hdl, s->buf + start, end - start);
michael@0 124 if (n == 0) {
michael@0 125 DPR("sndio_mainloop() werr\n");
michael@0 126 state = CUBEB_STATE_ERROR;
michael@0 127 break;
michael@0 128 }
michael@0 129 s->wrpos = 0;
michael@0 130 start += n;
michael@0 131 }
michael@0 132 }
michael@0 133 sio_stop(s->hdl);
michael@0 134 s->rdpos = s->wrpos;
michael@0 135 pthread_mutex_unlock(&s->mtx);
michael@0 136 s->state_cb(s, s->arg, state);
michael@0 137 return NULL;
michael@0 138 }
michael@0 139
michael@0 140 /*static*/ int
michael@0 141 sndio_init(cubeb **context, char const *context_name)
michael@0 142 {
michael@0 143 DPR("sndio_init(%s)\n", context_name);
michael@0 144 *context = malloc(sizeof(*context));
michael@0 145 (*context)->ops = &sndio_ops;
michael@0 146 (void)context_name;
michael@0 147 return CUBEB_OK;
michael@0 148 }
michael@0 149
michael@0 150 static char const *
michael@0 151 sndio_get_backend_id(cubeb *context)
michael@0 152 {
michael@0 153 return "sndio";
michael@0 154 }
michael@0 155
michael@0 156 static void
michael@0 157 sndio_destroy(cubeb *context)
michael@0 158 {
michael@0 159 DPR("sndio_destroy()\n");
michael@0 160 free(context);
michael@0 161 }
michael@0 162
michael@0 163 static int
michael@0 164 sndio_stream_init(cubeb *context,
michael@0 165 cubeb_stream **stream,
michael@0 166 char const *stream_name,
michael@0 167 cubeb_stream_params stream_params, unsigned int latency,
michael@0 168 cubeb_data_callback data_callback,
michael@0 169 cubeb_state_callback state_callback,
michael@0 170 void *user_ptr)
michael@0 171 {
michael@0 172 cubeb_stream *s;
michael@0 173 struct sio_par wpar, rpar;
michael@0 174 DPR("sndio_stream_init(%s)\n", stream_name);
michael@0 175 size_t size;
michael@0 176
michael@0 177 s = malloc(sizeof(cubeb_stream));
michael@0 178 if (s == NULL)
michael@0 179 return CUBEB_ERROR;
michael@0 180 s->context = context;
michael@0 181 s->hdl = sio_open(NULL, SIO_PLAY, 0);
michael@0 182 if (s->hdl == NULL) {
michael@0 183 free(s);
michael@0 184 DPR("sndio_stream_init(), sio_open() failed\n");
michael@0 185 return CUBEB_ERROR;
michael@0 186 }
michael@0 187 sio_initpar(&wpar);
michael@0 188 wpar.sig = 1;
michael@0 189 wpar.bits = 16;
michael@0 190 switch (stream_params.format) {
michael@0 191 case CUBEB_SAMPLE_S16LE:
michael@0 192 wpar.le = 1;
michael@0 193 break;
michael@0 194 case CUBEB_SAMPLE_S16BE:
michael@0 195 wpar.le = 0;
michael@0 196 break;
michael@0 197 case CUBEB_SAMPLE_FLOAT32NE:
michael@0 198 wpar.le = SIO_LE_NATIVE;
michael@0 199 break;
michael@0 200 default:
michael@0 201 DPR("sndio_stream_init() unsupported format\n");
michael@0 202 return CUBEB_ERROR_INVALID_FORMAT;
michael@0 203 }
michael@0 204 wpar.rate = stream_params.rate;
michael@0 205 wpar.pchan = stream_params.channels;
michael@0 206 wpar.appbufsz = latency * wpar.rate / 1000;
michael@0 207 if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
michael@0 208 sio_close(s->hdl);
michael@0 209 free(s);
michael@0 210 DPR("sndio_stream_init(), sio_setpar() failed\n");
michael@0 211 return CUBEB_ERROR;
michael@0 212 }
michael@0 213 if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
michael@0 214 rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
michael@0 215 rpar.pchan != wpar.pchan) {
michael@0 216 sio_close(s->hdl);
michael@0 217 free(s);
michael@0 218 DPR("sndio_stream_init() unsupported params\n");
michael@0 219 return CUBEB_ERROR_INVALID_FORMAT;
michael@0 220 }
michael@0 221 sio_onmove(s->hdl, sndio_onmove, s);
michael@0 222 s->active = 0;
michael@0 223 s->nfr = rpar.round;
michael@0 224 s->bpf = rpar.bps * rpar.pchan;
michael@0 225 s->pchan = rpar.pchan;
michael@0 226 s->data_cb = data_callback;
michael@0 227 s->state_cb = state_callback;
michael@0 228 s->arg = user_ptr;
michael@0 229 s->mtx = PTHREAD_MUTEX_INITIALIZER;
michael@0 230 s->rdpos = s->wrpos = 0;
michael@0 231 if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) {
michael@0 232 s->conv = 1;
michael@0 233 size = rpar.round * rpar.pchan * sizeof(float);
michael@0 234 } else {
michael@0 235 s->conv = 0;
michael@0 236 size = rpar.round * rpar.pchan * rpar.bps;
michael@0 237 }
michael@0 238 s->buf = malloc(size);
michael@0 239 if (s->buf == NULL) {
michael@0 240 sio_close(s->hdl);
michael@0 241 free(s);
michael@0 242 return CUBEB_ERROR;
michael@0 243 }
michael@0 244 *stream = s;
michael@0 245 DPR("sndio_stream_init() end, ok\n");
michael@0 246 (void)context;
michael@0 247 (void)stream_name;
michael@0 248 return CUBEB_OK;
michael@0 249 }
michael@0 250
michael@0 251 static int
michael@0 252 sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
michael@0 253 {
michael@0 254 assert(ctx && max_channels);
michael@0 255
michael@0 256 *max_channels = 8;
michael@0 257
michael@0 258 return CUBEB_OK;
michael@0 259 }
michael@0 260
michael@0 261 static int
michael@0 262 sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
michael@0 263 {
michael@0 264 // XXX Not yet implemented.
michael@0 265 *rate = 44100;
michael@0 266
michael@0 267 return CUBEB_OK;
michael@0 268 }
michael@0 269
michael@0 270 static int
michael@0 271 sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
michael@0 272 {
michael@0 273 // XXX Not yet implemented.
michael@0 274 latency_ms = 40;
michael@0 275
michael@0 276 return CUBEB_OK;
michael@0 277 }
michael@0 278
michael@0 279 static void
michael@0 280 sndio_stream_destroy(cubeb_stream *s)
michael@0 281 {
michael@0 282 DPR("sndio_stream_destroy()\n");
michael@0 283 sio_close(s->hdl);
michael@0 284 free(s);
michael@0 285 }
michael@0 286
michael@0 287 static int
michael@0 288 sndio_stream_start(cubeb_stream *s)
michael@0 289 {
michael@0 290 int err;
michael@0 291
michael@0 292 DPR("sndio_stream_start()\n");
michael@0 293 s->active = 1;
michael@0 294 err = pthread_create(&s->th, NULL, sndio_mainloop, s);
michael@0 295 if (err) {
michael@0 296 s->active = 0;
michael@0 297 return CUBEB_ERROR;
michael@0 298 }
michael@0 299 return CUBEB_OK;
michael@0 300 }
michael@0 301
michael@0 302 static int
michael@0 303 sndio_stream_stop(cubeb_stream *s)
michael@0 304 {
michael@0 305 void *dummy;
michael@0 306
michael@0 307 DPR("sndio_stream_stop()\n");
michael@0 308 if (s->active) {
michael@0 309 s->active = 0;
michael@0 310 pthread_join(s->th, &dummy);
michael@0 311 }
michael@0 312 return CUBEB_OK;
michael@0 313 }
michael@0 314
michael@0 315 static int
michael@0 316 sndio_stream_get_position(cubeb_stream *s, uint64_t *p)
michael@0 317 {
michael@0 318 pthread_mutex_lock(&s->mtx);
michael@0 319 DPR("sndio_stream_get_position() %lld\n", s->rdpos);
michael@0 320 *p = s->rdpos;
michael@0 321 pthread_mutex_unlock(&s->mtx);
michael@0 322 return CUBEB_OK;
michael@0 323 }
michael@0 324
michael@0 325 static int
michael@0 326 sndio_stream_set_volume(cubeb_stream *s, float volume)
michael@0 327 {
michael@0 328 DPR("sndio_stream_set_volume(%f)\n", volume);
michael@0 329 pthread_mutex_lock(&s->mtx);
michael@0 330 sio_setvol(s->hdl, SIO_MAXVOL * volume);
michael@0 331 pthread_mutex_unlock(&s->mtx);
michael@0 332 return CUBEB_OK;
michael@0 333 }
michael@0 334
michael@0 335 int
michael@0 336 sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
michael@0 337 {
michael@0 338 // http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open
michael@0 339 // in the "Measuring the latency and buffers usage" paragraph.
michael@0 340 *latency = stm->wrpos - stm->rdpos;
michael@0 341 return CUBEB_OK;
michael@0 342 }
michael@0 343
michael@0 344 static struct cubeb_ops const sndio_ops = {
michael@0 345 .init = sndio_init,
michael@0 346 .get_backend_id = sndio_get_backend_id,
michael@0 347 .get_max_channel_count = sndio_get_max_channel_count,
michael@0 348 .get_min_latency = sndio_get_min_latency,
michael@0 349 .get_preferred_sample_rate = sndio_get_preferred_sample_rate,
michael@0 350 .destroy = sndio_destroy,
michael@0 351 .stream_init = sndio_stream_init,
michael@0 352 .stream_destroy = sndio_stream_destroy,
michael@0 353 .stream_start = sndio_stream_start,
michael@0 354 .stream_stop = sndio_stream_stop,
michael@0 355 .stream_get_position = sndio_stream_get_position,
michael@0 356 .stream_get_latency = sndio_stream_get_latency
michael@0 357 };

mercurial