/*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; }
static inline int alsa_test_open(ao_device *device, char *dev, ao_sample_format *format) { ao_alsa_internal *internal = (ao_alsa_internal *) device->internal; snd_pcm_hw_params_t *params; int err; adebug("Trying to open ALSA device '%s'\n",dev); internal->local_config = init_local_config_with_workaround(device,dev); if(internal->local_config) err = snd_pcm_open_lconf(&(internal->pcm_handle), dev, SND_PCM_STREAM_PLAYBACK, 0, internal->local_config); else err = snd_pcm_open(&(internal->pcm_handle), dev, SND_PCM_STREAM_PLAYBACK, 0); if(err){ adebug("Unable to open ALSA device '%s'\n",dev); if(internal->local_config) snd_config_delete(internal->local_config); internal->local_config=NULL; return err; } /* try to set up hw params */ err = alsa_set_hwparams(device,format); if(err<0){ adebug("Unable to open ALSA device '%s'\n",dev); snd_pcm_close(internal->pcm_handle); if(internal->local_config) snd_config_delete(internal->local_config); internal->local_config=NULL; internal->pcm_handle = NULL; return err; } /* try to set up sw params */ err = alsa_set_swparams(device); if(err<0){ adebug("Unable to open ALSA device '%s'\n",dev); snd_pcm_close(internal->pcm_handle); if(internal->local_config) snd_config_delete(internal->local_config); internal->local_config=NULL; internal->pcm_handle = NULL; return err; } /* this is a hack and fragile if the exact device detection code flow changes! Nevertheless, this is a useful warning for users. Never fail silently if we can help it! */ if(!strcasecmp(dev,"default")){ /* default device */ if(device->output_channels>2){ awarn("ALSA 'default' device plays only channels 0,1.\n"); } } if(!strcasecmp(dev,"default") || !strncasecmp(dev,"plug",4)){ if(format->bits>16){ awarn("ALSA '%s' device may only simulate >16 bit playback\n",dev); } } /* success! */ return 0; }