Пример #1
0
/*static*/ int
alsa_init(cubeb ** context, char const * context_name)
{
  cubeb * ctx;
  int r;
  int i;
  int fd[2];
  pthread_attr_t attr;
  snd_pcm_t * dummy;

  assert(context);
  *context = NULL;

  pthread_mutex_lock(&cubeb_alsa_mutex);
  if (!cubeb_alsa_error_handler_set) {
    snd_lib_error_set_handler(silent_error_handler);
    cubeb_alsa_error_handler_set = 1;
  }
  pthread_mutex_unlock(&cubeb_alsa_mutex);

  ctx = calloc(1, sizeof(*ctx));
  assert(ctx);

  ctx->ops = &alsa_ops;

  r = pthread_mutex_init(&ctx->mutex, NULL);
  assert(r == 0);

  r = pipe(fd);
  assert(r == 0);

  for (i = 0; i < 2; ++i) {
    fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
    fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
  }

  ctx->control_fd_read = fd[0];
  ctx->control_fd_write = fd[1];

  /* Force an early rebuild when alsa_run is first called to ensure fds and
     nfds have been initialized. */
  ctx->rebuild = 1;

  r = pthread_attr_init(&attr);
  assert(r == 0);

  r = pthread_attr_setstacksize(&attr, 256 * 1024);
  assert(r == 0);

  r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
  assert(r == 0);

  r = pthread_attr_destroy(&attr);
  assert(r == 0);

  /* Open a dummy PCM to force the configuration space to be evaluated so that
     init_local_config_with_workaround can find and modify the default node. */
  r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, NULL);
  if (r >= 0) {
    alsa_locked_pcm_close(dummy);
  }
  ctx->is_pa = 0;
  pthread_mutex_lock(&cubeb_alsa_mutex);
  ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
  pthread_mutex_unlock(&cubeb_alsa_mutex);
  if (ctx->local_config) {
    ctx->is_pa = 1;
    r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
    /* If we got a local_config, we found a PA PCM.  If opening a PCM with that
       config fails with EINVAL, the PA PCM is too old for this workaround. */
    if (r == -EINVAL) {
      pthread_mutex_lock(&cubeb_alsa_mutex);
      snd_config_delete(ctx->local_config);
      pthread_mutex_unlock(&cubeb_alsa_mutex);
      ctx->local_config = NULL;
    } else if (r >= 0) {
      alsa_locked_pcm_close(dummy);
    }
  }

  *context = ctx;

  return CUBEB_OK;
}
Пример #2
0
static int
alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                 cubeb_stream_params stream_params, unsigned int latency,
                 cubeb_data_callback data_callback, cubeb_state_callback state_callback,
                 void * user_ptr)
{
  cubeb_stream * stm;
  int r;
  snd_pcm_format_t format;

  assert(ctx && stream);

  *stream = NULL;

  switch (stream_params.format) {
  case CUBEB_SAMPLE_S16LE:
    format = SND_PCM_FORMAT_S16_LE;
    break;
  case CUBEB_SAMPLE_S16BE:
    format = SND_PCM_FORMAT_S16_BE;
    break;
  case CUBEB_SAMPLE_FLOAT32LE:
    format = SND_PCM_FORMAT_FLOAT_LE;
    break;
  case CUBEB_SAMPLE_FLOAT32BE:
    format = SND_PCM_FORMAT_FLOAT_BE;
    break;
  default:
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  pthread_mutex_lock(&ctx->mutex);
  if (ctx->active_streams >= CUBEB_STREAM_MAX) {
    pthread_mutex_unlock(&ctx->mutex);
    return CUBEB_ERROR;
  }
  ctx->active_streams += 1;
  pthread_mutex_unlock(&ctx->mutex);

  stm = calloc(1, sizeof(*stm));
  assert(stm);

  stm->context = ctx;
  stm->data_callback = data_callback;
  stm->state_callback = state_callback;
  stm->user_ptr = user_ptr;
  stm->params = stream_params;
  stm->state = INACTIVE;

  r = pthread_mutex_init(&stm->mutex, NULL);
  assert(r == 0);

  r = alsa_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
  if (r < 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  r = snd_pcm_nonblock(stm->pcm, 1);
  assert(r == 0);

  /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
     possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
     Only resort to this hack if the handle_underrun workaround failed. */
  if (!ctx->local_config && ctx->is_pa) {
    latency = latency < 500 ? 500 : latency;
  }

  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
                         stm->params.channels, stm->params.rate, 1,
                         latency * 1000);
  if (r < 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
  assert(r == 0);

  stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
  assert(stm->nfds > 0);

  stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
  assert(stm->saved_fds);
  r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
  assert((nfds_t) r == stm->nfds);

  r = pthread_cond_init(&stm->cond, NULL);
  assert(r == 0);

  if (alsa_register_stream(ctx, stm) != 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  *stream = stm;

  return CUBEB_OK;
}
Пример #3
0
static int
alsa_stream_init(cubeb * ctx, 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)
{
  (void)stream_name;
  cubeb_stream * stm;
  int r;
  snd_pcm_format_t format;
  snd_pcm_uframes_t period_size;
  int latency_us = 0;


  assert(ctx && stream);

  if (input_stream_params) {
    /* Capture support not yet implemented. */
    return CUBEB_ERROR_NOT_SUPPORTED;
  }

  if (input_device || output_device) {
    /* Device selection not yet implemented. */
    return CUBEB_ERROR_DEVICE_UNAVAILABLE;
  }

  *stream = NULL;

  switch (output_stream_params->format) {
  case CUBEB_SAMPLE_S16LE:
    format = SND_PCM_FORMAT_S16_LE;
    break;
  case CUBEB_SAMPLE_S16BE:
    format = SND_PCM_FORMAT_S16_BE;
    break;
  case CUBEB_SAMPLE_FLOAT32LE:
    format = SND_PCM_FORMAT_FLOAT_LE;
    break;
  case CUBEB_SAMPLE_FLOAT32BE:
    format = SND_PCM_FORMAT_FLOAT_BE;
    break;
  default:
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  pthread_mutex_lock(&ctx->mutex);
  if (ctx->active_streams >= CUBEB_STREAM_MAX) {
    pthread_mutex_unlock(&ctx->mutex);
    return CUBEB_ERROR;
  }
  ctx->active_streams += 1;
  pthread_mutex_unlock(&ctx->mutex);

  stm = calloc(1, sizeof(*stm));
  assert(stm);

  stm->context = ctx;
  stm->data_callback = data_callback;
  stm->state_callback = state_callback;
  stm->user_ptr = user_ptr;
  stm->params = *output_stream_params;
  stm->state = INACTIVE;
  stm->volume = 1.0;

  r = pthread_mutex_init(&stm->mutex, NULL);
  assert(r == 0);

  r = alsa_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
  if (r < 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  r = snd_pcm_nonblock(stm->pcm, 1);
  assert(r == 0);

  latency_us = latency_frames * 1e6 / stm->params.rate;

  /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
     possibly work.  See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
     Only resort to this hack if the handle_underrun workaround failed. */
  if (!ctx->local_config && ctx->is_pa) {
    const int min_latency = 5e5;
    latency_us = latency_us < min_latency ? min_latency: latency_us;
  }

  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
                         stm->params.channels, stm->params.rate, 1,
                         latency_us);
  if (r < 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size);
  assert(r == 0);

  stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
  assert(stm->nfds > 0);

  stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
  assert(stm->saved_fds);
  r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
  assert((nfds_t) r == stm->nfds);

  r = pthread_cond_init(&stm->cond, NULL);
  assert(r == 0);

  if (alsa_register_stream(ctx, stm) != 0) {
    alsa_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  *stream = stm;

  return CUBEB_OK;
}