static void my_flush_events(struct SoundIoPrivate *si, bool wait) { SoundIo *soundio = &si->pub; SoundIoJack *sij = &si->backend_data.jack; int err; bool cb_shutdown = false; soundio_os_mutex_lock(sij->mutex); if (wait) soundio_os_cond_wait(sij->cond, sij->mutex); if (sij->is_shutdown && !sij->emitted_shutdown_cb) { sij->emitted_shutdown_cb = true; cb_shutdown = true; } soundio_os_mutex_unlock(sij->mutex); if (cb_shutdown) { soundio->on_backend_disconnect(soundio, SoundIoErrorBackendDisconnected); } else { if (!sij->refresh_devices_flag.test_and_set()) { if ((err = refresh_devices(si))) { sij->refresh_devices_flag.clear(); } else { soundio->on_devices_change(soundio); } } } }
static void context_state_callback(pa_context *context, void *userdata) { SoundIoPrivate *si = (SoundIoPrivate *)userdata; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; SoundIo *soundio = &si->pub; switch (pa_context_get_state(context)) { case PA_CONTEXT_UNCONNECTED: // The context hasn't been connected yet. return; case PA_CONTEXT_CONNECTING: // A connection is being established. return; case PA_CONTEXT_AUTHORIZING: // The client is authorizing itself to the daemon. return; case PA_CONTEXT_SETTING_NAME: // The client is passing its application name to the daemon. return; case PA_CONTEXT_READY: // The connection is established, the context is ready to execute operations. sipa->ready_flag = true; pa_threaded_mainloop_signal(sipa->main_loop, 0); return; case PA_CONTEXT_TERMINATED: // The connection was terminated cleanly. pa_threaded_mainloop_signal(sipa->main_loop, 0); return; case PA_CONTEXT_FAILED: // The connection failed or was disconnected. if (sipa->ready_flag) { sipa->connection_err = SoundIoErrorBackendDisconnected; } else { sipa->connection_err = SoundIoErrorInitAudioBackend; sipa->ready_flag = true; } pa_threaded_mainloop_signal(sipa->main_loop, 0); soundio->on_events_signal(soundio); return; } }
static void notify_devices_change(SoundIoPrivate *si) { SoundIo *soundio = &si->pub; SoundIoJack *sij = &si->backend_data.jack; sij->refresh_devices_flag.clear(); soundio_os_mutex_lock(sij->mutex); soundio_os_cond_signal(sij->cond, sij->mutex); soundio->on_events_signal(soundio); soundio_os_mutex_unlock(sij->mutex); }
static void force_device_scan_pa(SoundIoPrivate *si) { SoundIo *soundio = &si->pub; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; pa_threaded_mainloop_lock(sipa->main_loop); sipa->device_scan_queued = true; pa_threaded_mainloop_signal(sipa->main_loop, 0); soundio->on_events_signal(soundio); pa_threaded_mainloop_unlock(sipa->main_loop); }
static void shutdown_callback(void *arg) { SoundIoPrivate *si = (SoundIoPrivate *)arg; SoundIo *soundio = &si->pub; SoundIoJack *sij = &si->backend_data.jack; soundio_os_mutex_lock(sij->mutex); sij->is_shutdown = true; soundio_os_cond_signal(sij->cond, sij->mutex); soundio->on_events_signal(soundio); soundio_os_mutex_unlock(sij->mutex); }
static void subscribe_callback(pa_context *context, pa_subscription_event_type_t event_bits, uint32_t index, void *userdata) { SoundIoPrivate *si = (SoundIoPrivate *)userdata; SoundIo *soundio = &si->pub; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; sipa->device_scan_queued = true; pa_threaded_mainloop_signal(sipa->main_loop, 0); soundio->on_events_signal(soundio); }
static void my_flush_events(SoundIoPrivate *si, bool wait) { SoundIo *soundio = &si->pub; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; bool change = false; bool cb_shutdown = false; SoundIoDevicesInfo *old_devices_info = nullptr; pa_threaded_mainloop_lock(sipa->main_loop); if (wait) pa_threaded_mainloop_wait(sipa->main_loop); if (sipa->device_scan_queued && !sipa->connection_err) { sipa->device_scan_queued = false; sipa->connection_err = refresh_devices(si); cleanup_refresh_devices(si); } if (sipa->connection_err && !sipa->emitted_shutdown_cb) { sipa->emitted_shutdown_cb = true; cb_shutdown = true; } else if (sipa->ready_devices_info) { old_devices_info = si->safe_devices_info; si->safe_devices_info = sipa->ready_devices_info; sipa->ready_devices_info = nullptr; change = true; } pa_threaded_mainloop_unlock(sipa->main_loop); if (cb_shutdown) soundio->on_backend_disconnect(soundio, sipa->connection_err); else if (change) soundio->on_devices_change(soundio); soundio_destroy_devices_info(old_devices_info); }
// call this while holding the main loop lock static int refresh_devices(SoundIoPrivate *si) { SoundIo *soundio = &si->pub; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; assert(!sipa->current_devices_info); sipa->current_devices_info = allocate<SoundIoDevicesInfo>(1); if (!sipa->current_devices_info) return SoundIoErrorNoMem; pa_operation *list_sink_op = pa_context_get_sink_info_list(sipa->pulse_context, sink_info_callback, si); pa_operation *list_source_op = pa_context_get_source_info_list(sipa->pulse_context, source_info_callback, si); pa_operation *server_info_op = pa_context_get_server_info(sipa->pulse_context, server_info_callback, si); int err; if ((err = perform_operation(si, list_sink_op))) { return err; } if ((err = perform_operation(si, list_source_op))) { return err; } if ((err = perform_operation(si, server_info_op))) { return err; } if (sipa->device_query_err) { return sipa->device_query_err; } // based on the default sink name, figure out the default output index // if the name doesn't match just pick the first one. if there are no // devices then we need to set it to -1. sipa->current_devices_info->default_output_index = -1; sipa->current_devices_info->default_input_index = -1; if (sipa->current_devices_info->input_devices.length > 0) { sipa->current_devices_info->default_input_index = 0; for (int i = 0; i < sipa->current_devices_info->input_devices.length; i += 1) { SoundIoDevice *device = sipa->current_devices_info->input_devices.at(i); assert(device->aim == SoundIoDeviceAimInput); if (strcmp(device->id, sipa->default_source_name) == 0) { sipa->current_devices_info->default_input_index = i; } } } if (sipa->current_devices_info->output_devices.length > 0) { sipa->current_devices_info->default_output_index = 0; for (int i = 0; i < sipa->current_devices_info->output_devices.length; i += 1) { SoundIoDevice *device = sipa->current_devices_info->output_devices.at(i); assert(device->aim == SoundIoDeviceAimOutput); if (strcmp(device->id, sipa->default_sink_name) == 0) { sipa->current_devices_info->default_output_index = i; } } } soundio_destroy_devices_info(sipa->ready_devices_info); sipa->ready_devices_info = sipa->current_devices_info; sipa->current_devices_info = nullptr; pa_threaded_mainloop_signal(sipa->main_loop, 0); soundio->on_events_signal(soundio); return 0; }