static int stream_update_timing_info(cubeb_stream * stm) { int r = -1; pa_operation * o = NULL; if (stm->output_stream) { o = WRAP(pa_stream_update_timing_info)(stm->output_stream, stream_success_callback, stm); if (o) { r = operation_wait(stm->context, stm->output_stream, o); WRAP(pa_operation_unref)(o); } if (r != 0) { return r; } } if (stm->input_stream) { o = WRAP(pa_stream_update_timing_info)(stm->input_stream, stream_success_callback, stm); if (o) { r = operation_wait(stm->context, stm->input_stream, o); WRAP(pa_operation_unref)(o); } } return r; }
static int pulse_enumerate_devices(cubeb * context, cubeb_device_type type, cubeb_device_collection ** collection) { pulse_dev_list_data user_data = { NULL, NULL, NULL, 0, 0, context }; pa_operation * o; uint32_t i; WRAP(pa_threaded_mainloop_lock)(context->mainloop); o = WRAP(pa_context_get_server_info)(context->context, pulse_server_info_cb, &user_data); if (o) { operation_wait(context, NULL, o); WRAP(pa_operation_unref)(o); } if (type & CUBEB_DEVICE_TYPE_OUTPUT) { o = WRAP(pa_context_get_sink_info_list)(context->context, pulse_sink_info_cb, &user_data); if (o) { operation_wait(context, NULL, o); WRAP(pa_operation_unref)(o); } } if (type & CUBEB_DEVICE_TYPE_INPUT) { o = WRAP(pa_context_get_source_info_list)(context->context, pulse_source_info_cb, &user_data); if (o) { operation_wait(context, NULL, o); WRAP(pa_operation_unref)(o); } } WRAP(pa_threaded_mainloop_unlock)(context->mainloop); *collection = malloc(sizeof(cubeb_device_collection) + sizeof(cubeb_device_info *) * (user_data.count > 0 ? user_data.count - 1 : 0)); (*collection)->count = user_data.count; for (i = 0; i < user_data.count; i++) (*collection)->device[i] = user_data.devinfo[i]; free(user_data.default_sink_name); free(user_data.default_source_name); free(user_data.devinfo); return CUBEB_OK; }
static void pulse_destroy(cubeb * ctx) { pa_operation * o; if (ctx->context) { WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx); if (o) { operation_wait(ctx, NULL, o); WRAP(pa_operation_unref)(o); } WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL); WRAP(pa_context_disconnect)(ctx->context); WRAP(pa_context_unref)(ctx->context); WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); } if (ctx->mainloop) { WRAP(pa_threaded_mainloop_stop)(ctx->mainloop); WRAP(pa_threaded_mainloop_free)(ctx->mainloop); } if (ctx->libpulse) { dlclose(ctx->libpulse); } if (ctx->default_sink_info) { free(ctx->default_sink_info); } free(ctx); }
int pulse_stream_set_volume(cubeb_stream * stm, float volume) { uint32_t index; pa_operation * op; pa_volume_t vol; pa_cvolume cvol; const pa_sample_spec * ss; WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); ss = WRAP(pa_stream_get_sample_spec)(stm->stream); vol = WRAP(pa_sw_volume_from_linear)(volume); WRAP(pa_cvolume_set)(&cvol, ss->channels, vol); index = WRAP(pa_stream_get_index)(stm->stream); op = WRAP(pa_context_set_sink_input_volume)(stm->context->context, index, &cvol, volume_success, stm); if (op) { operation_wait(stm->context, stm->stream, op); WRAP(pa_operation_unref)(op); } WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); return CUBEB_OK; }
static void cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state) { pa_operation * o; if (!io_stream) { return; } o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, stm); if (o) { operation_wait(stm->context, io_stream, o); WRAP(pa_operation_unref)(o); } }
static void pulse_context_destroy(cubeb * ctx) { pa_operation * o; WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx); if (o) { operation_wait(ctx, NULL, o); WRAP(pa_operation_unref)(o); } WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL); WRAP(pa_context_disconnect)(ctx->context); WRAP(pa_context_unref)(ctx->context); WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); }
static void stream_cork(cubeb_stream * stm, enum cork_state state) { pa_operation * o; pa_threaded_mainloop_lock(stm->context->mainloop); o = pa_stream_cork(stm->stream, state & CORK, stream_success_callback, stm->context->mainloop); operation_wait(stm->context, o); pa_operation_unref(o); pa_threaded_mainloop_unlock(stm->context->mainloop); if (state & NOTIFY) { stm->state_callback(stm, stm->user_ptr, state & CORK ? CUBEB_STATE_STOPPED : CUBEB_STATE_STARTED); } }
int pulse_stream_set_volume(cubeb_stream * stm, float volume) { uint32_t index; pa_operation * op; pa_volume_t vol; pa_cvolume cvol; const pa_sample_spec * ss; if (!stm->output_stream) { return CUBEB_ERROR; } WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); while (!stm->context->default_sink_info) { WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop); } /* if the pulse daemon is configured to use flat volumes, * apply our own gain instead of changing the input volume on the sink. */ if (stm->context->default_sink_info->flags & PA_SINK_FLAT_VOLUME) { stm->volume = volume; } else { ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream); vol = WRAP(pa_sw_volume_from_linear)(volume); WRAP(pa_cvolume_set)(&cvol, ss->channels, vol); index = WRAP(pa_stream_get_index)(stm->output_stream); op = WRAP(pa_context_set_sink_input_volume)(stm->context->context, index, &cvol, volume_success, stm); if (op) { operation_wait(stm->context, stm->output_stream, op); WRAP(pa_operation_unref)(op); } } WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); return CUBEB_OK; }
int pulse_register_device_collection_changed(cubeb * context, cubeb_device_type devtype, cubeb_device_collection_changed_callback collection_changed_callback, void * user_ptr) { context->collection_changed_callback = collection_changed_callback; context->collection_changed_user_ptr = user_ptr; WRAP(pa_threaded_mainloop_lock)(context->mainloop); pa_subscription_mask_t mask; if (context->collection_changed_callback == NULL) { // Unregister subscription WRAP(pa_context_set_subscribe_callback)(context->context, NULL, NULL); mask = PA_SUBSCRIPTION_MASK_NULL; } else { WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context); if (devtype == CUBEB_DEVICE_TYPE_INPUT) mask = PA_SUBSCRIPTION_MASK_SOURCE; else if (devtype == CUBEB_DEVICE_TYPE_OUTPUT) mask = PA_SUBSCRIPTION_MASK_SINK; else mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; } pa_operation * o; o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context); if (o == NULL) { LOG("Context subscribe failed\n"); return CUBEB_ERROR; } operation_wait(context, NULL, o); WRAP(pa_operation_unref)(o); WRAP(pa_threaded_mainloop_unlock)(context->mainloop); return CUBEB_OK; }
void cubeb_destroy(cubeb * ctx) { pa_operation * o; if (ctx->context) { pa_threaded_mainloop_lock(ctx->mainloop); o = pa_context_drain(ctx->context, context_notify_callback, ctx->mainloop); if (o) { operation_wait(ctx, o); pa_operation_unref(o); } pa_context_disconnect(ctx->context); pa_context_unref(ctx->context); pa_threaded_mainloop_unlock(ctx->mainloop); } if (ctx->mainloop) { pa_threaded_mainloop_stop(ctx->mainloop); pa_threaded_mainloop_free(ctx->mainloop); } free(ctx); }
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; pa_channel_map map; 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; } ss.rate = stream_params.rate; ss.channels = stream_params.channels; /* XXX check that this does the right thing for Vorbis and WaveEx */ WRAP(pa_channel_map_init_auto)(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT); 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, &map); 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; }