/*static*/ int pulse_init(cubeb ** context, char const * context_name) { void * libpulse = NULL; cubeb * ctx; *context = NULL; #ifndef DISABLE_LIBPULSE_DLOPEN libpulse = dlopen("libpulse.so.0", RTLD_LAZY); if (!libpulse) { return CUBEB_ERROR; } #define LOAD(x) { \ cubeb_##x = dlsym(libpulse, #x); \ if (!cubeb_##x) { \ dlclose(libpulse); \ return CUBEB_ERROR; \ } \ } LIBPULSE_API_VISIT(LOAD); #undef LOAD #endif ctx = calloc(1, sizeof(*ctx)); assert(ctx); ctx->ops = &pulse_ops; ctx->libpulse = libpulse; ctx->mainloop = WRAP(pa_threaded_mainloop_new)(); ctx->default_sink_info = NULL; WRAP(pa_threaded_mainloop_start)(ctx->mainloop); ctx->context_name = context_name ? strdup(context_name) : NULL; if (pulse_context_init(ctx) != 0) { pulse_destroy(ctx); return CUBEB_ERROR; } WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx); WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); *context = ctx; return CUBEB_OK; }
static int pulse_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, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { cubeb_stream * stm; pa_buffer_attr battr; int r; assert(context); // If the connection failed for some reason, try to reconnect if (context->error == 1 && pulse_context_init(context) != 0) { return CUBEB_ERROR; } *stream = NULL; stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = context; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->volume = PULSE_NO_GAIN; WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); if (output_stream_params) { r = create_pa_stream(stm, &stm->output_stream, output_stream_params, stream_name); if (r != CUBEB_OK) { WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); pulse_stream_destroy(stm); return r; } stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream)); WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm); WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm); battr = set_buffering_attribute(latency, &stm->output_sample_spec); WRAP(pa_stream_connect_playback)(stm->output_stream, output_device, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY, NULL, NULL); } // Set up input stream if (input_stream_params) { r = create_pa_stream(stm, &stm->input_stream, input_stream_params, stream_name); if (r != CUBEB_OK) { WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); pulse_stream_destroy(stm); return r; } stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream)); WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm); WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm); battr = set_buffering_attribute(latency, &stm->input_sample_spec); WRAP(pa_stream_connect_record)(stm->input_stream, input_device, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY); } r = wait_until_stream_ready(stm); if (r == 0) { /* force a timing update now, otherwise timing info does not become valid until some point after initialization has completed. */ r = stream_update_timing_info(stm); } WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); if (r != 0) { pulse_stream_destroy(stm); return CUBEB_ERROR; } #ifdef LOGGING_ENABLED if (output_stream_params){ const pa_buffer_attr * output_att; output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream); LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",output_att->maxlength, output_att->tlength, output_att->prebuf, output_att->minreq, output_att->fragsize); } if (input_stream_params){ const pa_buffer_attr * input_att; input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream); LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",input_att->maxlength, input_att->tlength, input_att->prebuf, input_att->minreq, input_att->fragsize); } #endif *stream = stm; return CUBEB_OK; }
static int pulse_stream_init(cubeb * context, 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) { pa_sample_spec ss; cubeb_stream * stm; pa_operation * o; pa_buffer_attr battr; int r; assert(context); *stream = NULL; switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: ss.format = PA_SAMPLE_S16LE; break; case CUBEB_SAMPLE_S16BE: ss.format = PA_SAMPLE_S16BE; break; case CUBEB_SAMPLE_FLOAT32LE: ss.format = PA_SAMPLE_FLOAT32LE; break; case CUBEB_SAMPLE_FLOAT32BE: ss.format = PA_SAMPLE_FLOAT32BE; break; default: return CUBEB_ERROR_INVALID_FORMAT; } // If the connection failed for some reason, try to reconnect if (context->error == 1 && pulse_context_init(context) != 0) { return CUBEB_ERROR; } ss.rate = stream_params.rate; ss.channels = stream_params.channels; stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = context; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->sample_spec = ss; battr.maxlength = -1; battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, &stm->sample_spec); battr.prebuf = -1; battr.minreq = battr.tlength / 4; battr.fragsize = -1; WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); stm->stream = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); if (!stm->stream) { pulse_stream_destroy(stm); return CUBEB_ERROR; } WRAP(pa_stream_set_state_callback)(stm->stream, stream_state_callback, stm); WRAP(pa_stream_set_write_callback)(stm->stream, stream_request_callback, stm); WRAP(pa_stream_connect_playback)(stm->stream, NULL, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED, NULL, NULL); r = wait_until_stream_ready(stm); if (r == 0) { /* force a timing update now, otherwise timing info does not become valid until some point after initialization has completed. */ o = WRAP(pa_stream_update_timing_info)(stm->stream, stream_success_callback, stm); if (o) { r = operation_wait(stm->context, stm->stream, o); WRAP(pa_operation_unref)(o); } } WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); if (r != 0) { pulse_stream_destroy(stm); return CUBEB_ERROR; } *stream = stm; return CUBEB_OK; }
/*static*/ int pulse_init(cubeb ** context, char const * context_name) { void * libpulse = NULL; cubeb * ctx; *context = NULL; #ifndef DISABLE_LIBPULSE_DLOPEN libpulse = dlopen("libpulse.so.0", RTLD_LAZY); if (!libpulse) { return CUBEB_ERROR; } #define LOAD(x) do { \ cubeb_##x = dlsym(libpulse, #x); \ if (!cubeb_##x) { \ dlclose(libpulse); \ return CUBEB_ERROR; \ } \ } while(0) LOAD(pa_channel_map_can_balance); LOAD(pa_channel_map_init_auto); LOAD(pa_context_connect); LOAD(pa_context_disconnect); LOAD(pa_context_drain); LOAD(pa_context_get_server_info); LOAD(pa_context_get_sink_info_by_name); LOAD(pa_context_get_state); LOAD(pa_context_new); LOAD(pa_context_rttime_new); LOAD(pa_context_set_sink_input_volume); LOAD(pa_context_set_state_callback); LOAD(pa_context_unref); LOAD(pa_cvolume_set); LOAD(pa_cvolume_set_balance); LOAD(pa_frame_size); LOAD(pa_operation_get_state); LOAD(pa_operation_unref); LOAD(pa_rtclock_now); LOAD(pa_stream_begin_write); LOAD(pa_stream_cancel_write); LOAD(pa_stream_connect_playback); LOAD(pa_stream_cork); LOAD(pa_stream_disconnect); LOAD(pa_stream_get_channel_map); LOAD(pa_stream_get_index); LOAD(pa_stream_get_latency); LOAD(pa_stream_get_sample_spec); LOAD(pa_stream_get_state); LOAD(pa_stream_get_time); LOAD(pa_stream_new); LOAD(pa_stream_set_state_callback); LOAD(pa_stream_set_write_callback); LOAD(pa_stream_unref); LOAD(pa_stream_update_timing_info); LOAD(pa_stream_write); LOAD(pa_sw_volume_from_linear); LOAD(pa_threaded_mainloop_free); LOAD(pa_threaded_mainloop_get_api); LOAD(pa_threaded_mainloop_in_thread); LOAD(pa_threaded_mainloop_lock); LOAD(pa_threaded_mainloop_new); LOAD(pa_threaded_mainloop_signal); LOAD(pa_threaded_mainloop_start); LOAD(pa_threaded_mainloop_stop); LOAD(pa_threaded_mainloop_unlock); LOAD(pa_threaded_mainloop_wait); LOAD(pa_usec_to_bytes); #undef LOAD #endif ctx = calloc(1, sizeof(*ctx)); assert(ctx); ctx->ops = &pulse_ops; ctx->libpulse = libpulse; ctx->mainloop = WRAP(pa_threaded_mainloop_new)(); ctx->default_sink_info = NULL; WRAP(pa_threaded_mainloop_start)(ctx->mainloop); ctx->context_name = context_name ? strdup(context_name) : NULL; if (pulse_context_init(ctx) != 0) { pulse_destroy(ctx); return CUBEB_ERROR; } WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx); WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); *context = ctx; return CUBEB_OK; }