Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/cubeb-sys/libcubeb/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 18 kB image not shown  

Quelle  cubeb_sndio.c   Sprache: C

 
/*
 * Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org>
 *
 * This program is made available under an ISC-style license.  See the
 * accompanying file LICENSE for details.
 */

#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_tracing.h"
#include <assert.h>
#include <dlfcn.h>
#include <inttypes.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <sndio.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#if defined(CUBEB_SNDIO_DEBUG)
#define DPR(...) fprintf(stderr, __VA_ARGS__);
#else
#define DPR(...)                                                               \
  do {                                                                         \
  } while (0)
#endif

#ifdef DISABLE_LIBSNDIO_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) (*cubeb_##x)
#define LIBSNDIO_API_VISIT(X)                                                  \
  X(sio_close)                                                                 \
  X(sio_eof)                                                                   \
  X(sio_getpar)                                                                \
  X(sio_initpar)                                                               \
  X(sio_nfds)                                                                  \
  X(sio_onmove)                                                                \
  X(sio_open)                                                                  \
  X(sio_pollfd)                                                                \
  X(sio_read)                                                                  \
  X(sio_revents)                                                               \
  X(sio_setpar)                                                                \
  X(sio_start)                                                                 \
  X(sio_stop)                                                                  \
  X(sio_write)

#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
#undef MAKE_TYPEDEF
#endif

static struct cubeb_ops const sndio_ops;

struct cubeb {
  struct cubeb_ops const * ops;
  void * libsndio;
};

struct cubeb_stream {
  /* Note: Must match cubeb_stream layout in cubeb.c. */
  cubeb * context;
  void * arg; /* user arg to {data,state}_cb */
  /**/
  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 mode;                      /* bitmap of SIO_{PLAY,REC} */
  int active;                    /* cubec_start() called */
  int conv;                      /* need float->s24 conversion */
  unsigned char * rbuf;          /* rec data consumed from here */
  unsigned char * pbuf;          /* play data is prepared here */
  unsigned int nfr;              /* number of frames in ibuf and obuf */
  unsigned int rbpf;             /* rec bytes per frame */
  unsigned int pbpf;             /* play bytes per frame */
  unsigned int rchan;            /* number of rec channels */
  unsigned int pchan;            /* number of play channels */
  unsigned int nblks;            /* number of blocks in the buffer */
  uint64_t hwpos;                /* frame number Joe hears right now */
  uint64_t swpos;                /* number of frames produced/consumed */
  cubeb_data_callback data_cb;   /* cb to preapare data */
  cubeb_state_callback state_cb; /* cb to notify about state changes */
  float volume;                  /* current volume */
};

static void
s16_setvol(void * ptr, long nsamp, float volume)
{
  int16_t * dst = ptr;
  int32_t mult = volume * 32768;
  int32_t s;

  while (nsamp-- > 0) {
    s = *dst;
    s = (s * mult) >> 15;
    *(dst++) = s;
  }
}

static void
float_to_s24(void * ptr, long nsamp, float volume)
{
  int32_t * dst = ptr;
  float * src = ptr;
  float mult = volume * 8388608;
  int s;

  while (nsamp-- > 0) {
    s = lrintf(*(src++) * mult);
    if (s < -8388608)
      s = -8388608;
    else if (s > 8388607)
      s = 8388607;
    *(dst++) = s;
  }
}

static void
s24_to_float(void * ptr, long nsamp)
{
  int32_t * src = ptr;
  float * dst = ptr;

  src += nsamp;
  dst += nsamp;
  while (nsamp-- > 0)
    *(--dst) = (1. / 8388608) * *(--src);
}

static const char *
sndio_get_device()
{
#ifdef __linux__
  /*
   * On other platforms default to sndio devices,
   * so cubebs other backends can be used instead.
   */

  const char * dev = getenv("AUDIODEVICE");
  if (dev == NULL || *dev == '\0')
    return "snd/0";
  return dev;
#else
  return SIO_DEVANY;
#endif
}

static void
sndio_onmove(void * arg, int delta)
{
  cubeb_stream * s = (cubeb_stream *)arg;

  s->hwpos += delta;
}

static void *
sndio_mainloop(void * arg)
{
  struct pollfd * pfds;
  cubeb_stream * s = arg;
  int n, eof = 0, prime, nfds, events, revents, state = CUBEB_STATE_STARTED;
  size_t pstart = 0, pend = 0, rstart = 0, rend = 0;
  long nfr;

  CUBEB_REGISTER_THREAD("cubeb rendering thread");

  nfds = WRAP(sio_nfds)(s->hdl);
  pfds = calloc(nfds, sizeof(struct pollfd));
  if (pfds == NULL) {
    CUBEB_UNREGISTER_THREAD();
    return NULL;
  }

  DPR("sndio_mainloop()\n");
  s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
  pthread_mutex_lock(&s->mtx);
  if (!WRAP(sio_start)(s->hdl)) {
    pthread_mutex_unlock(&s->mtx);
    free(pfds);
    CUBEB_UNREGISTER_THREAD();
    return NULL;
  }
  DPR("sndio_mainloop(), started\n");

  if (s->mode & SIO_PLAY) {
    pstart = pend = s->nfr * s->pbpf;
    prime = s->nblks;
    if (s->mode & SIO_REC) {
      memset(s->rbuf, 0, s->nfr * s->rbpf);
      rstart = rend = s->nfr * s->rbpf;
    }
  } else {
    prime = 0;
    rstart = 0;
    rend = s->nfr * s->rbpf;
  }

  for (;;) {
    if (!s->active) {
      DPR("sndio_mainloop() stopped\n");
      state = CUBEB_STATE_STOPPED;
      break;
    }

    /* do we have a complete block? */
    if ((!(s->mode & SIO_PLAY) || pstart == pend) &&
        (!(s->mode & SIO_REC) || rstart == rend)) {

      if (eof) {
        DPR("sndio_mainloop() drained\n");
        state = CUBEB_STATE_DRAINED;
        break;
      }

      if ((s->mode & SIO_REC) && s->conv)
        s24_to_float(s->rbuf, s->nfr * s->rchan);

      /* invoke call-back, it returns less that s->nfr if done */
      pthread_mutex_unlock(&s->mtx);
      nfr = s->data_cb(s, s->arg, s->rbuf, s->pbuf, s->nfr);
      pthread_mutex_lock(&s->mtx);
      if (nfr < 0) {
        DPR("sndio_mainloop() cb err\n");
        state = CUBEB_STATE_ERROR;
        break;
      }
      s->swpos += nfr;

      /* was this last call-back invocation (aka end-of-stream) ? */
      if (nfr < s->nfr) {

        if (!(s->mode & SIO_PLAY) || nfr == 0) {
          state = CUBEB_STATE_DRAINED;
          break;
        }

        /* need to write (aka drain) the partial play block we got */
        pend = nfr * s->pbpf;
        eof = 1;
      }

      if (prime > 0)
        prime--;

      if (s->mode & SIO_PLAY) {
        if (s->conv)
          float_to_s24(s->pbuf, nfr * s->pchan, s->volume);
        else
          s16_setvol(s->pbuf, nfr * s->pchan, s->volume);
      }

      if (s->mode & SIO_REC)
        rstart = 0;
      if (s->mode & SIO_PLAY)
        pstart = 0;
    }

    events = 0;
    if ((s->mode & SIO_REC) && rstart < rend && prime == 0)
      events |= POLLIN;
    if ((s->mode & SIO_PLAY) && pstart < pend)
      events |= POLLOUT;
    nfds = WRAP(sio_pollfd)(s->hdl, pfds, events);

    if (nfds > 0) {
      pthread_mutex_unlock(&s->mtx);
      n = poll(pfds, nfds, -1);
      pthread_mutex_lock(&s->mtx);
      if (n < 0)
        continue;
    }

    revents = WRAP(sio_revents)(s->hdl, pfds);

    if (revents & POLLHUP) {
      state = CUBEB_STATE_ERROR;
      break;
    }

    if (revents & POLLOUT) {
      n = WRAP(sio_write)(s->hdl, s->pbuf + pstart, pend - pstart);
      if (n == 0 && WRAP(sio_eof)(s->hdl)) {
        DPR("sndio_mainloop() werr\n");
        state = CUBEB_STATE_ERROR;
        break;
      }
      pstart += n;
    }

    if (revents & POLLIN) {
      n = WRAP(sio_read)(s->hdl, s->rbuf + rstart, rend - rstart);
      if (n == 0 && WRAP(sio_eof)(s->hdl)) {
        DPR("sndio_mainloop() rerr\n");
        state = CUBEB_STATE_ERROR;
        break;
      }
      rstart += n;
    }

    /* skip rec block, if not recording (yet) */
    if (prime > 0 && (s->mode & SIO_REC))
      rstart = rend;
  }
  WRAP(sio_stop)(s->hdl);
  s->hwpos = s->swpos;
  pthread_mutex_unlock(&s->mtx);
  s->state_cb(s, s->arg, state);
  free(pfds);
  CUBEB_UNREGISTER_THREAD();
  return NULL;
}

/*static*/ int
sndio_init(cubeb ** context, char const * context_name)
{
  void * libsndio = NULL;
  struct sio_hdl * hdl;

  assert(context);

#ifndef DISABLE_LIBSNDIO_DLOPEN
  libsndio = dlopen("libsndio.so.7.0", RTLD_LAZY);
  if (!libsndio) {
    libsndio = dlopen("libsndio.so", RTLD_LAZY);
    if (!libsndio) {
      DPR("sndio_init(%s) failed dlopen(libsndio.so)\n", context_name);
      return CUBEB_ERROR;
    }
  }

#define LOAD(x)                                                                \
  {                                                                            \
    cubeb_##x = dlsym(libsndio, #x);                                           \
    if (!cubeb_##x) {                                                          \
      DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x);              \
      dlclose(libsndio);                                                       \
      return CUBEB_ERROR;                                                      \
    }                                                                          \
  }

  LIBSNDIO_API_VISIT(LOAD);
#undef LOAD
#endif

  /* test if sndio works */
  hdl = WRAP(sio_open)(sndio_get_device(), SIO_PLAY, 1);
  if (hdl == NULL) {
    return CUBEB_ERROR;
  }
  WRAP(sio_close)(hdl);

  DPR("sndio_init(%s)\n", context_name);
  *context = malloc(sizeof(**context));
  if (*context == NULL)
    return CUBEB_ERROR;
  (*context)->libsndio = libsndio;
  (*context)->ops = &sndio_ops;
  (void)context_name;
  return CUBEB_OK;
}

static char const *
sndio_get_backend_id(cubeb * context)
{
  return "sndio";
}

static void
sndio_destroy(cubeb * context)
{
  DPR("sndio_destroy()\n");
#ifndef DISABLE_LIBSNDIO_DLOPEN
  if (context->libsndio)
    dlclose(context->libsndio);
#endif
  free(context);
}

static int
sndio_stream_init(cubeb * context, cubeb_stream ** stream,
                  char const * stream_name, cubeb_devid input_device,
                  cubeb_stream_params * input_stream_params,
                  cubeb_devid output_device,
                  cubeb_stream_params * output_stream_params,
                  unsigned int latency_frames,
                  cubeb_data_callback data_callback,
                  cubeb_state_callback state_callback, void * user_ptr)
{
  cubeb_stream * s;
  struct sio_par wpar, rpar;
  cubeb_sample_format format;
  int rate;
  size_t bps;

  DPR("sndio_stream_init(%s)\n", stream_name);

  s = malloc(sizeof(cubeb_stream));
  if (s == NULL)
    return CUBEB_ERROR;
  memset(s, 0, sizeof(cubeb_stream));
  s->mode = 0;
  if (input_stream_params) {
    if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
      DPR("sndio_stream_init(), loopback not supported\n");
      goto err;
    }
    s->mode |= SIO_REC;
    format = input_stream_params->format;
    rate = input_stream_params->rate;
  }
  if (output_stream_params) {
    if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
      DPR("sndio_stream_init(), loopback not supported\n");
      goto err;
    }
    s->mode |= SIO_PLAY;
    format = output_stream_params->format;
    rate = output_stream_params->rate;
  }
  if (s->mode == 0) {
    DPR("sndio_stream_init(), neither playing nor recording\n");
    goto err;
  }
  s->context = context;
  s->hdl = WRAP(sio_open)(sndio_get_device(), s->mode, 1);
  if (s->hdl == NULL) {
    DPR("sndio_stream_init(), sio_open() failed\n");
    goto err;
  }
  WRAP(sio_initpar)(&wpar);
  wpar.sig = 1;
  switch (format) {
  case CUBEB_SAMPLE_S16LE:
    wpar.le = 1;
    wpar.bits = 16;
    break;
  case CUBEB_SAMPLE_S16BE:
    wpar.le = 0;
    wpar.bits = 16;
    break;
  case CUBEB_SAMPLE_FLOAT32NE:
    wpar.le = SIO_LE_NATIVE;
    wpar.bits = 24;
    wpar.msb = 0;
    break;
  default:
    DPR("sndio_stream_init() unsupported format\n");
    goto err;
  }
  wpar.bps = SIO_BPS(wpar.bits);
  wpar.rate = rate;
  if (s->mode & SIO_REC)
    wpar.rchan = input_stream_params->channels;
  if (s->mode & SIO_PLAY)
    wpar.pchan = output_stream_params->channels;
  wpar.appbufsz = latency_frames;
  if (!WRAP(sio_setpar)(s->hdl, &wpar) || !WRAP(sio_getpar)(s->hdl, &rpar)) {
    DPR("sndio_stream_init(), sio_setpar() failed\n");
    goto err;
  }
  if (rpar.bits != wpar.bits || rpar.le != wpar.le || rpar.sig != wpar.sig ||
      rpar.bps != wpar.bps ||
      (wpar.bits < 8 * wpar.bps && rpar.msb != wpar.msb) ||
      rpar.rate != wpar.rate ||
      ((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) ||
      ((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) {
    DPR("sndio_stream_init() unsupported params\n");
    goto err;
  }
  WRAP(sio_onmove)(s->hdl, sndio_onmove, s);
  s->active = 0;
  s->nfr = rpar.round;
  s->rbpf = rpar.bps * rpar.rchan;
  s->pbpf = rpar.bps * rpar.pchan;
  s->rchan = rpar.rchan;
  s->pchan = rpar.pchan;
  s->nblks = rpar.bufsz / rpar.round;
  s->data_cb = data_callback;
  s->state_cb = state_callback;
  s->arg = user_ptr;
  s->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
  s->hwpos = s->swpos = 0;
  if (format == CUBEB_SAMPLE_FLOAT32LE) {
    s->conv = 1;
    bps = sizeof(float);
  } else {
    s->conv = 0;
    bps = rpar.bps;
  }
  if (s->mode & SIO_PLAY) {
    s->pbuf = malloc(bps * rpar.pchan * rpar.round);
    if (s->pbuf == NULL)
      goto err;
  }
  if (s->mode & SIO_REC) {
    s->rbuf = malloc(bps * rpar.rchan * rpar.round);
    if (s->rbuf == NULL)
      goto err;
  }
  s->volume = 1.;
  *stream = s;
  DPR("sndio_stream_init() end, ok\n");
  (void)context;
  (void)stream_name;
  return CUBEB_OK;
err:
  if (s->hdl)
    WRAP(sio_close)(s->hdl);
  if (s->pbuf)
    free(s->pbuf);
  if (s->rbuf)
    free(s->pbuf);
  free(s);
  return CUBEB_ERROR;
}

static int
sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
  assert(ctx && max_channels);

  *max_channels = 8;

  return CUBEB_OK;
}

static int
sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
  /*
   * We've no device-independent prefered rate; any rate will work if
   * sndiod is running. If it isn't, 48kHz is what is most likely to
   * work as most (but not all) devices support it.
   */

  *rate = 48000;
  return CUBEB_OK;
}

static int
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params,
                      uint32_t * latency_frames)
{
  /*
   * We've no device-independent minimum latency.
   */

  *latency_frames = 2048;

  return CUBEB_OK;
}

static void
sndio_stream_destroy(cubeb_stream * s)
{
  DPR("sndio_stream_destroy()\n");
  WRAP(sio_close)(s->hdl);
  if (s->mode & SIO_PLAY)
    free(s->pbuf);
  if (s->mode & SIO_REC)
    free(s->rbuf);
  free(s);
}

static int
sndio_stream_start(cubeb_stream * s)
{
  int err;

  DPR("sndio_stream_start()\n");
  s->active = 1;
  err = pthread_create(&s->th, NULL, sndio_mainloop, s);
  if (err) {
    s->active = 0;
    return CUBEB_ERROR;
  }
  return CUBEB_OK;
}

static int
sndio_stream_stop(cubeb_stream * s)
{
  void * dummy;

  DPR("sndio_stream_stop()\n");
  if (s->active) {
    s->active = 0;
    pthread_join(s->th, &dummy);
  }
  return CUBEB_OK;
}

static int
sndio_stream_get_position(cubeb_stream * s, uint64_t * p)
{
  pthread_mutex_lock(&s->mtx);
  DPR("sndio_stream_get_position() %" PRId64 "\n", s->hwpos);
  *p = s->hwpos;
  pthread_mutex_unlock(&s->mtx);
  return CUBEB_OK;
}

static int
sndio_stream_set_volume(cubeb_stream * s, float volume)
{
  DPR("sndio_stream_set_volume(%f)\n", volume);
  pthread_mutex_lock(&s->mtx);
  if (volume < 0.)
    volume = 0.;
  else if (volume > 1.0)
    volume = 1.;
  s->volume = volume;
  pthread_mutex_unlock(&s->mtx);
  return CUBEB_OK;
}

int
sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
  // http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open
  // in the "Measuring the latency and buffers usage" paragraph.
  *latency = stm->swpos - stm->hwpos;
  return CUBEB_OK;
}

static int
sndio_enumerate_devices(cubeb * context, cubeb_device_type type,
                        cubeb_device_collection * collection)
{
  static char dev[] = SIO_DEVANY;
  cubeb_device_info * device;

  device = malloc(sizeof(cubeb_device_info));
  if (device == NULL)
    return CUBEB_ERROR;

  device->devid = dev;         /* passed to stream_init() */
  device->device_id = dev;     /* printable in UI */
  device->friendly_name = dev; /* same, but friendly */
  device->group_id = dev;      /* actual device if full-duplex */
  device->vendor_name = NULL;  /* may be NULL */
  device->type = type;         /* Input/Output */
  device->state = CUBEB_DEVICE_STATE_ENABLED;
  device->preferred = CUBEB_DEVICE_PREF_ALL;
  device->format = CUBEB_DEVICE_FMT_S16NE;
  device->default_format = CUBEB_DEVICE_FMT_S16NE;
  device->max_channels = (type == CUBEB_DEVICE_TYPE_INPUT) ? 2 : 8;
  device->default_rate = 48000;
  device->min_rate = 4000;
  device->max_rate = 192000;
  device->latency_lo = 480;
  device->latency_hi = 9600;
  collection->device = device;
  collection->count = 1;
  return CUBEB_OK;
}

static int
sndio_device_collection_destroy(cubeb * context,
                                cubeb_device_collection * collection)
{
  free(collection->device);
  return CUBEB_OK;
}

static struct cubeb_ops const sndio_ops = {
    .init = sndio_init,
    .get_backend_id = sndio_get_backend_id,
    .get_max_channel_count = sndio_get_max_channel_count,
    .get_min_latency = sndio_get_min_latency,
    .get_preferred_sample_rate = sndio_get_preferred_sample_rate,
    .get_supported_input_processing_params = NULL,
    .enumerate_devices = sndio_enumerate_devices,
    .device_collection_destroy = sndio_device_collection_destroy,
    .destroy = sndio_destroy,
    .stream_init = sndio_stream_init,
    .stream_destroy = sndio_stream_destroy,
    .stream_start = sndio_stream_start,
    .stream_stop = sndio_stream_stop,
    .stream_get_position = sndio_stream_get_position,
    .stream_get_latency = sndio_stream_get_latency,
    .stream_set_volume = sndio_stream_set_volume,
    .stream_set_name = NULL,
    .stream_get_current_device = NULL,
    .stream_set_input_mute = NULL,
    .stream_set_input_processing_params = NULL,
    .stream_device_destroy = NULL,
    .stream_register_device_changed_callback = NULL,
    .register_device_collection_changed = NULL};

Messung V0.5
C=94 H=93 G=93

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.