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

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

mercurial