media/libcubeb/src/cubeb_sndio.c

changeset 0
6474c204b198
     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 +};

mercurial