bool PulseSubscription::run() { pa_context *context = getContext(); pa_operation *operation; mainLoopLock(); // Must assume only one subscription will be alive. Can fix this if needed one day. pa_context_set_subscribe_callback(context, _subscribe_callback, this); m_triggered = 0; operation = pa_context_subscribe(context, (pa_subscription_mask_t)( PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_MODULE), NULL, NULL); while(pa_operation_get_state(operation) != PA_OPERATION_CANCELLED){ mainLoopWait(); if(m_triggered){ fireEvent(); m_triggered = 0; } } pa_operation_unref(operation); mainLoopUnlock(); return true; }
static void context_state_cb(pa_context *context, void *user_data) { struct audio_service *service = user_data; if (!service->context_initialized) { switch (pa_context_get_state(service->context)) { case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: g_message("Successfully established connection to pulseaudio context"); service->context_initialized = true; break; case PA_CONTEXT_TERMINATED: g_warning("Connection of our context was terminated from pulseaudio"); break; case PA_CONTEXT_FAILED: g_warning("Failed to establish a connection to pulseaudio"); break; default: break; } if (service->context_initialized) { pa_context_set_subscribe_callback(service->context, context_subscribe_cb, service); pa_context_subscribe(service->context, PA_SUBSCRIPTION_MASK_CARD, NULL, service); update_properties(service); } } }
void _cb_state_changed(pa_context *context, void *userdata) { callback_t *callback = (callback_t*)userdata; pa_context_state_t state = pa_context_get_state(context); PACMIXER_LOG("B:server state changed to %d", state); switch(state) { case PA_CONTEXT_READY: pa_context_set_subscribe_callback(context, _cb_event, callback); pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_ALL, NULL, NULL); pa_context_get_sink_input_info_list(context, _cb_sink_input, callback); pa_context_get_sink_info_list(context, _cb_sink, callback); pa_context_get_source_info_list(context, _cb_source, callback); pa_context_get_source_output_info_list(context, _cb_source_output, callback); pa_context_get_card_info_list(context, _cb_card, callback); pa_context_get_server_info(context, _cb_server, callback); ((tstate_callback_func)(callback->state))(callback->self, S_CAME); break; case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: pa_context_unref(context); ((tstate_callback_func)(callback->state))(callback->self, S_GONE); break; default: break; } }
/** * Create, set up and connect a context. * * Caller must lock the main loop. * * @return true on success, false on error */ static bool pulse_output_setup_context(struct pulse_output *po, GError **error_r) { assert(po != NULL); assert(po->mainloop != NULL); po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop), MPD_PULSE_NAME); if (po->context == NULL) { g_set_error(error_r, pulse_output_quark(), 0, "pa_context_new() has failed"); return false; } pa_context_set_state_callback(po->context, pulse_output_context_state_cb, po); pa_context_set_subscribe_callback(po->context, pulse_output_subscribe_cb, po); if (!pulse_output_connect(po, error_r)) { pulse_output_delete_context(po); return false; } return true; }
void QPulseAudioThread::initialize_callbacks ( QPulseAudioThread * pulseThread ) { pa_operation * op; pa_operation_unref ( pa_context_get_source_info_list ( context, pa_source_info_callback, pulseThread ) ); //pa_operation_unref(pa_context_get_server_info(&c, server_info_callback, this)); //pa_operation_unref(pa_context_get_sink_info_list(&c, sink_info_callback, this)); //pa_operation_unref(pa_context_get_source_info_list(&c, source_info_callback, this)); //pa_operation_unref(pa_context_get_module_info_list(&c, module_info_callback, this)); //pa_operation_unref(pa_context_get_client_info_list(&c, client_info_callback, this)); //pa_operation_unref(pa_context_get_sink_input_info_list(&c, sink_input_info_callback, this)); //pa_operation_unref(pa_context_get_source_output_info_list(&c, source_output_info_callback, this)); //pa_operation_unref(pa_context_get_sample_info_list(&c, sample_info_callback, this)); //pa_context_set_drain pa_context_set_subscribe_callback ( context, subscribe_callback, pulseThread ); if ( op = pa_context_subscribe ( context, ( enum pa_subscription_mask ) PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, NULL ) ) { pa_operation_unref ( op ); } else { qDebug() << "null op returned on subscribe"; } }
static void m_context_state_cb(pa_context *c, void *user_data) { if (!c) { printf("m_context_state_cb() invalid arguement\n"); return; } pa_operation *pa_op = NULL; switch (pa_context_get_state(c)) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: pa_context_set_subscribe_callback(c, m_pa_context_subscribe_cb, NULL); pa_op = pa_context_subscribe(c, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK| PA_SUBSCRIPTION_MASK_SOURCE| PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| PA_SUBSCRIPTION_MASK_CLIENT| PA_SUBSCRIPTION_MASK_SERVER| PA_SUBSCRIPTION_MASK_CARD), NULL, NULL); if (!pa_op) { printf("pa_context_subscribe() failed\n"); return; } pa_operation_unref(pa_op); pa_op = pa_context_get_sink_info_list(c, m_pa_sink_info_cb, NULL); if (!pa_op) { printf("pa_context_get_sink_info_list() failed\n"); return; } pa_operation_unref(pa_op); break; case PA_CONTEXT_FAILED: if (m_pa_ctx) { pa_context_unref(m_pa_ctx); m_pa_ctx = NULL; } printf("Connection failed, attempting reconnect\n"); g_timeout_add_seconds(13, m_connect_to_pulse, NULL); return; case PA_CONTEXT_TERMINATED: default: printf("pa_context terminated\n"); return; } }
/** * Frees and clears the context. * * Caller must lock the main loop. */ static void pulse_output_delete_context(struct pulse_output *po) { assert(po != NULL); assert(po->context != NULL); pa_context_set_state_callback(po->context, NULL, NULL); pa_context_set_subscribe_callback(po->context, NULL, NULL); pa_context_disconnect(po->context); pa_context_unref(po->context); po->context = NULL; }
/** * This is called whenever the context status changes */ static void context_state_callback(pa_context *c, void *userdata) { pa_audio_mode_t *pam = (pa_audio_mode_t *)userdata; pa_operation *o; switch(pa_context_get_state(c)) { case PA_CONTEXT_CONNECTING: case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: pa_context_set_subscribe_callback(pam->context, subscription_event_callback, pam); o = pa_context_subscribe(pam->context, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL); if(o != NULL) pa_operation_unref(o); TRACE(TRACE_DEBUG, "PA", "Context ready"); pa_threaded_mainloop_signal(mainloop, 0); break; case PA_CONTEXT_TERMINATED: if(pam->stream != NULL) { pa_stream_unref(pam->stream); pam->stream = NULL; } TRACE(TRACE_ERROR, "PA", "Context terminated"); pa_threaded_mainloop_signal(mainloop, 0); break; case PA_CONTEXT_FAILED: if(pam->stream != NULL) { pa_stream_unref(pam->stream); pam->stream = NULL; } TRACE(TRACE_ERROR, "PA", "Connection failure: %s", pa_strerror(pa_context_errno(c))); pa_threaded_mainloop_signal(mainloop, 0); break; } }
void context_state_cb(pa_context *c, void *userdata) { switch (pa_context_get_state(c)) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: { pa_operation *o; if (!(o = pa_context_get_source_info_list(c, source_cb, NULL ))) { show_error(_("pa_context_subscribe() failed")); return; } pa_operation_unref(o); if (!(o = pa_context_get_sink_info_list(c, sink_cb, NULL ))) { show_error(_("pa_context_subscribe() failed")); return; } pa_operation_unref(o); pa_context_set_subscribe_callback(c, subscribe_cb, NULL); if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK| PA_SUBSCRIPTION_MASK_SOURCE), NULL, NULL))) { show_error(_("pa_context_subscribe() failed")); return; } pa_operation_unref(o); break; } case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: default: return; } }
static void connect_server(struct ausrv *ausrv) { pa_mainloop_api *api = pa_glib_mainloop_get_api(ausrv->mainloop); char *server = ausrv->server; cancel_timer(ausrv); if (server != NULL && !strcmp(ausrv->server, DEFAULT_SERVER)) server = NULL; /* * Note: It is not possible to reconnect a context if it ever gets * disconnected. If we have a context here, get rid of it and * allocate a new one. */ if (ausrv->context != NULL) { pa_context_set_state_callback(ausrv->context, NULL, NULL); pa_context_set_subscribe_callback(ausrv->context, NULL, NULL); pa_context_unref(ausrv->context); ausrv->context = NULL; } if ((ausrv->context = pa_context_new(api, pa_client_name)) == NULL) { LOG_ERROR("%s(): pa_context_new() failed, exiting", __FUNCTION__); exit(1); } pa_context_set_state_callback(ausrv->context, context_callback, ausrv); pa_context_set_subscribe_callback(ausrv->context, event_callback, ausrv); LOG_INFO("Trying to connect to %s...", server ? server : DEFAULT_SERVER); pa_context_connect(ausrv->context, server, PA_CONTEXT_NOAUTOSPAWN, NULL); }
void AudioSinksManager::start_pa_connection() { context = pa_context_new(pa_mainloop.get_api(), "chromecast-sink"); if (!context) { report_error("Couldn't create context" + get_pa_error()); return; } pa_context_set_state_callback(context, context_state_callback, this); pa_context_set_event_callback(context, context_event_callback, this); pa_context_set_subscribe_callback(context, context_subscription_callback, this); if (pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { report_error("Couldn't connect to PulseAudio server" + get_pa_error()); return; } }
static int Open (vlc_object_t *obj) { services_discovery_t *sd = (services_discovery_t *)obj; pa_operation *op; pa_context *ctx; services_discovery_sys_t *sys = malloc (sizeof (*sys)); if (unlikely(sys == NULL)) return VLC_ENOMEM; ctx = vlc_pa_connect (obj, &sys->mainloop); if (ctx == NULL) { free (sys); return VLC_EGENERIC; } sd->p_sys = sys; sd->description = _("Audio capture"); sys->context = ctx; sys->root = NULL; /* Subscribe for source events */ const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SOURCE; pa_threaded_mainloop_lock (sys->mainloop); pa_context_set_subscribe_callback (ctx, ContextCallback, sd); op = pa_context_subscribe (ctx, mask, NULL, NULL); if (likely(op != NULL)) pa_operation_unref (op); /* Enumerate existing sources */ op = pa_context_get_source_info_list (ctx, SourceCallback, sd); if (likely(op != NULL)) { //while (pa_operation_get_state (op) == PA_OPERATION_RUNNING) // pa_threaded_mainloop_wait (sys->mainloop); pa_operation_unref (op); } pa_threaded_mainloop_unlock (sys->mainloop); return VLC_SUCCESS; /* error: pa_threaded_mainloop_unlock (sys->mainloop); vlc_pa_disconnect (obj, ctx, sys->mainloop); free (sys); return VLC_EGENERIC;*/ }
static void gst_pulsesrc_destroy_context (GstPulseSrc * pulsesrc) { gst_pulsesrc_destroy_stream (pulsesrc); if (pulsesrc->context) { pa_context_disconnect (pulsesrc->context); /* Make sure we don't get any further callbacks */ pa_context_set_state_callback (pulsesrc->context, NULL, NULL); pa_context_set_subscribe_callback (pulsesrc->context, NULL, NULL); pa_context_unref (pulsesrc->context); pulsesrc->context = NULL; } }
JNIEXPORT void JNICALL Java_com_harrcharr_pulse_PulseContext_JNISubscribeSource( JNIEnv *jenv, jobject jcontext, jobject runnable) { pa_context *c = get_context_ptr(jenv, jcontext); jni_pa_event_cbs_t *cbs = get_event_cbs_ptr(jenv, jcontext); if (cbs == NULL && runnable != NULL) { cbs = new_event_cbs(); set_event_cbs_ptr(jenv, jcontext, cbs); pa_context_set_subscribe_callback(c, context_subscription_cb, cbs); } if (cbs != NULL && cbs->source_cbo != NULL) { del_cb_globalref(jenv, cbs->source_cbo); cbs->source_cbo = NULL; } if (runnable != NULL) { cbs->source_cbo = get_cb_globalref(jenv, jcontext, runnable); } }
FXbool PulseOutput::open() { /// Start the mainloop //if (eventloop==nullptr) { // eventloop = new PulseReactor(); // } /// Get a context if (pulse_context==nullptr) { pulse_context = pa_context_new(&api,"Goggles Music Manager"); #ifdef DEBUG pa_context_set_state_callback(pulse_context,context_state_callback,this); #endif pa_context_set_subscribe_callback(pulse_context,context_subscribe_callback,this); } /// Try connecting GM_DEBUG_PRINT("[pulse] pa_context_connect()\n"); if (pa_context_get_state(pulse_context)==PA_CONTEXT_UNCONNECTED) { if (pa_context_connect(pulse_context,nullptr,PA_CONTEXT_NOFLAGS,nullptr)<0) { GM_DEBUG_PRINT("[pulse] pa_context_connect failed\n"); return false; } } /// Wait until we're connected to the pulse daemon GM_DEBUG_PRINT("[pulse] wait for connection\n"); pa_context_state_t state; while((state=pa_context_get_state(pulse_context))!=PA_CONTEXT_READY) { if (state==PA_CONTEXT_FAILED || state==PA_CONTEXT_TERMINATED){ GM_DEBUG_PRINT("[pulse] Unable to connect to pulsedaemon\n"); return false; } context->wait_plugin_events(); } pa_operation* operation = pa_context_subscribe(pulse_context,PA_SUBSCRIPTION_MASK_SINK_INPUT,nullptr,this); if (operation) pa_operation_unref(operation); GM_DEBUG_PRINT("[pulse] ready()\n"); return true; }
void PulseSrc::pa_context_state_callback(pa_context* pulse_context, void* user_data) { PulseSrc* context = static_cast<PulseSrc*>(user_data); switch (pa_context_get_state(pulse_context)) { case PA_CONTEXT_CONNECTING: // g_print ("PA_CONTEXT_CONNECTING\n"); break; case PA_CONTEXT_AUTHORIZING: // g_print ("PA_CONTEXT_AUTHORIZING\n"); break; case PA_CONTEXT_SETTING_NAME: // g_print ("PA_CONTEXT_SETTING_NAME\n"); break; case PA_CONTEXT_READY: // g_print ("PA_CONTEXT_READY\n"); context->make_device_description(pulse_context); // pa_operation_unref(pa_context_get_source_info_list(pulse_context, // get_source_info_callback, // nullptr)); pa_context_set_subscribe_callback(pulse_context, on_pa_event_callback, nullptr); pa_operation_unref(pa_context_subscribe( pulse_context, static_cast<pa_subscription_mask_t>( PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SINK_INPUT | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT | PA_SUBSCRIPTION_MASK_MODULE | PA_SUBSCRIPTION_MASK_CLIENT | PA_SUBSCRIPTION_MASK_SAMPLE_CACHE | PA_SUBSCRIPTION_MASK_SERVER | PA_SUBSCRIPTION_MASK_CARD), nullptr, // pa_context_success_cb_t cb, nullptr)); // void *userdata); break; case PA_CONTEXT_TERMINATED: { g_debug("PulseSrc: PA_CONTEXT_TERMINATED"); } break; case PA_CONTEXT_FAILED: g_debug("PA_CONTEXT_FAILED"); break; default: g_debug("PulseSrc Context error: %s", pa_strerror(pa_context_errno(pulse_context))); } }
PulseAudioSystem::PulseAudioSystem() { pasInput = pasOutput = pasSpeaker = NULL; bSourceDone=bSinkDone=bServerDone = false; iDelayCache = 0; bPositionalCache = false; bAttenuating = false; iRemainingOperations = 0; bPulseIsGood = false; iSinkId = -1; pam = pa_threaded_mainloop_new(); pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam); pa_proplist *proplist; proplist = pa_proplist_new(); pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "Mumble"); pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "net.sourceforge.mumble.mumble"); pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "mumble"); pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "game"); pacContext = pa_context_new_with_proplist(api, NULL, proplist); pa_proplist_free(proplist); pa_context_set_subscribe_callback(pacContext, subscribe_callback, this); pa_context_set_state_callback(pacContext, context_state_callback, this); pa_context_connect(pacContext, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); pade = api->defer_new(api, defer_event_callback, this); api->defer_enable(pade, false); pa_threaded_mainloop_start(pam); bRunning = true; }
int main(int argc, char **argv) { int retval = 0; struct timeval timeout, timeout_def; pa_mainloop * ml = pa_mainloop_new(); pa_mainloop_api * api = pa_mainloop_get_api(ml); pa_context_state_t pa_state; pa_context * context = pa_context_new(api, "pa-mixer"); pa_context_connect(context, NULL, 0, NULL); pa_context_set_state_callback(context, pa_state_changed, &pa_state); /* struct sigaction sigint_handler; sigint_handler.sa_handler = handle_sig_int; sigemptyset(&sigint_handler.sa_mask); sigint_handler.sa_flags = 0; sigaction(SIGINT, &sigint_handler, NULL); */ int init_done = 0; initscr(); keypad(stdscr, TRUE); start_color(); curs_set(0); cbreak(); noecho(); init_pair(1, COLOR_WHITE, COLOR_BLACK); init_pair(2, COLOR_WHITE, COLOR_BLUE); fd_set read_set, test_set; FD_ZERO(&read_set); FD_SET(0, &read_set); timeout_def.tv_sec = 0; timeout_def.tv_usec = 1000; view_init(); while(running) { if(pa_state == PA_CONTEXT_UNCONNECTED) { pa_mainloop_iterate(ml, 1, NULL); continue; } if(pa_state == PA_CONTEXT_FAILED || pa_state == PA_CONTEXT_TERMINATED) { running = 0; retval = -1; } if(pa_state == PA_CONTEXT_READY) { if(!init_done) { pa_context_set_subscribe_callback(context, on_event, NULL); pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_ALL, NULL, NULL); pa_context_get_sink_input_info_list(context, on_sink_input_update, context); pa_context_get_sink_info_list(context, on_sink_update, context); } init_done = 1; } test_set = read_set; timeout = timeout_def; int r = select(1, &test_set, NULL, NULL, &timeout); if(r == 0) { /* timeout */ pa_mainloop_iterate(ml, 0, NULL); } if(FD_ISSET(0, &test_set)) { /* stdin ready */ int key = getch(); switch(key) { case KEY_UP: view_select_prev(); view_show(); break; case KEY_DOWN: view_select_next(); view_show(); break; case KEY_LEFT: change_sel_vol(context, VOL_DOWN); break; case KEY_RIGHT: change_sel_vol(context, VOL_UP); break; case 'n': change_sel_vol(context, VOL_NORM); break; case 'm': change_sel_vol(context, VOL_MUTE); break; case 'q': running = 0; break; case KEY_RESIZE: view_show(); refresh(); break; } } } pa_context_disconnect(context); pa_context_unref(context); pa_mainloop_free(ml); endwin(); return retval; }
int force_volume(char* source_name, pa_volume_t desired_volume) { // Create a mainloop pa_mainloop* pa_ml = pa_mainloop_new(); pa_mainloop_api* pa_mlapi = pa_mainloop_get_api(pa_ml); // Establish PA context pa_context* pa_ctx = pa_context_new(pa_mlapi, "uneven"); pa_context_connect(pa_ctx, NULL, 0, NULL); // Set up PA state callback int pa_state = 0; pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_state); // Set up event subscription int state = 0; pa_context_set_subscribe_callback(pa_ctx, source_state_cb, &state); // Prepare loop variables pa_operation* pending_op = NULL; pa_cvolume current_volume = {0, {0}}; int do_update; int i; // Iterate PA mainloop until we're done or the context enters an error state while (done == -1 && pa_state != 2) { pa_mainloop_iterate(pa_ml, 1, NULL); // Can't do anything unless PA is ready if (pa_state == 1) { // Wait for any pending operation to complete if (!pending_op || (pending_op && pa_operation_get_state(pending_op) == PA_OPERATION_DONE)) { if (pending_op) { pa_operation_unref(pending_op); pending_op = NULL; } switch (state) { case 0: // Subscribe to source events pending_op = pa_context_subscribe(pa_ctx, PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL); state = 2; break; case 1: // Idle break; case 2: // Source change detected, retrive volume information pending_op = pa_context_get_source_info_by_name(pa_ctx, source_name, get_volume_cb, ¤t_volume); state++; break; case 3: // Check channel volumes and reset if required do_update = 0; for (i = 0; i < current_volume.channels; i++) { if (current_volume.values[i] != desired_volume) { do_update = 1; current_volume.values[i] = desired_volume; } } if (do_update) { pending_op = pa_context_set_source_volume_by_name(pa_ctx, source_name, ¤t_volume, NULL, NULL); } state = 1; break; default: fprintf(stderr, "in state %d\n", state); done = 2; } } } } // Clean up pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return done; }
static gboolean gst_pulsesrc_open (GstAudioSrc * asrc) { GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc); pa_threaded_mainloop_lock (pulsesrc->mainloop); g_assert (!pulsesrc->context); g_assert (!pulsesrc->stream); GST_DEBUG_OBJECT (pulsesrc, "opening device"); if (!(pulsesrc->context = pa_context_new (pa_threaded_mainloop_get_api (pulsesrc->mainloop), pulsesrc->client_name))) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to create context"), (NULL)); goto unlock_and_fail; } pa_context_set_state_callback (pulsesrc->context, gst_pulsesrc_context_state_cb, pulsesrc); pa_context_set_subscribe_callback (pulsesrc->context, gst_pulsesrc_context_subscribe_cb, pulsesrc); GST_DEBUG_OBJECT (pulsesrc, "connect to server %s", GST_STR_NULL (pulsesrc->server)); if (pa_context_connect (pulsesrc->context, pulsesrc->server, 0, NULL) < 0) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s", pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); goto unlock_and_fail; } for (;;) { pa_context_state_t state; state = pa_context_get_state (pulsesrc->context); if (!PA_CONTEXT_IS_GOOD (state)) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s", pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); goto unlock_and_fail; } if (state == PA_CONTEXT_READY) break; /* Wait until the context is ready */ pa_threaded_mainloop_wait (pulsesrc->mainloop); } GST_DEBUG_OBJECT (pulsesrc, "connected"); pa_threaded_mainloop_unlock (pulsesrc->mainloop); return TRUE; /* ERRORS */ unlock_and_fail: { gst_pulsesrc_destroy_context (pulsesrc); pa_threaded_mainloop_unlock (pulsesrc->mainloop); return FALSE; } }
static gboolean gst_pulsemixer_ctrl_open (GstPulseMixerCtrl * c) { int e; gchar *name = gst_pulse_client_name (); pa_operation *o = NULL; g_assert (c); c->mainloop = pa_threaded_mainloop_new (); g_assert (c->mainloop); e = pa_threaded_mainloop_start (c->mainloop); g_assert (e == 0); pa_threaded_mainloop_lock (c->mainloop); if (!(c->context = pa_context_new (pa_threaded_mainloop_get_api (c->mainloop), name))) { GST_WARNING_OBJECT (c->object, "Failed to create context"); goto unlock_and_fail; } pa_context_set_state_callback (c->context, gst_pulsemixer_ctrl_context_state_cb, c); pa_context_set_subscribe_callback (c->context, gst_pulsemixer_ctrl_subscribe_cb, c); if (pa_context_connect (c->context, c->server, 0, NULL) < 0) { GST_WARNING_OBJECT (c->object, "Failed to connect context: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } /* Wait until the context is ready */ pa_threaded_mainloop_wait (c->mainloop); if (pa_context_get_state (c->context) != PA_CONTEXT_READY) { GST_WARNING_OBJECT (c->object, "Failed to connect context: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } /* Subscribe to events */ if (!(o = pa_context_subscribe (c->context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, gst_pulsemixer_ctrl_success_cb, c))) { GST_WARNING_OBJECT (c->object, "Failed to subscribe to events: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } c->operation_success = 0; while (pa_operation_get_state (o) != PA_OPERATION_DONE) { pa_threaded_mainloop_wait (c->mainloop); CHECK_DEAD_GOTO (c, unlock_and_fail); } if (!c->operation_success) { GST_WARNING_OBJECT (c->object, "Failed to subscribe to events: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } pa_operation_unref (o); o = NULL; /* Get sink info */ if (c->type == GST_PULSEMIXER_UNKNOWN || c->type == GST_PULSEMIXER_SINK) { if (!(o = pa_context_get_sink_info_by_name (c->context, c->device, gst_pulsemixer_ctrl_sink_info_cb, c))) { GST_WARNING_OBJECT (c->object, "Failed to get sink info: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } c->operation_success = 0; while (pa_operation_get_state (o) != PA_OPERATION_DONE) { pa_threaded_mainloop_wait (c->mainloop); CHECK_DEAD_GOTO (c, unlock_and_fail); } pa_operation_unref (o); o = NULL; if (!c->operation_success && (c->type == GST_PULSEMIXER_SINK || pa_context_errno (c->context) != PA_ERR_NOENTITY)) { GST_WARNING_OBJECT (c->object, "Failed to get sink info: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } } if (c->type == GST_PULSEMIXER_UNKNOWN || c->type == GST_PULSEMIXER_SOURCE) { if (!(o = pa_context_get_source_info_by_name (c->context, c->device, gst_pulsemixer_ctrl_source_info_cb, c))) { GST_WARNING_OBJECT (c->object, "Failed to get source info: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } c->operation_success = 0; while (pa_operation_get_state (o) != PA_OPERATION_DONE) { pa_threaded_mainloop_wait (c->mainloop); CHECK_DEAD_GOTO (c, unlock_and_fail); } pa_operation_unref (o); o = NULL; if (!c->operation_success) { GST_WARNING_OBJECT (c->object, "Failed to get source info: %s", pa_strerror (pa_context_errno (c->context))); goto unlock_and_fail; } } g_assert (c->type != GST_PULSEMIXER_UNKNOWN); c->track = gst_pulsemixer_track_new (c); c->tracklist = g_list_append (c->tracklist, c->track); pa_threaded_mainloop_unlock (c->mainloop); g_free (name); return TRUE; unlock_and_fail: if (o) pa_operation_unref (o); if (c->mainloop) pa_threaded_mainloop_unlock (c->mainloop); g_free (name); return FALSE; }
AudioDevPulseAudio::AudioDevPulseAudio(QObject *parent): AudioDev(parent) { this->d = new AudioDevPulseAudioPrivate(this); // Create a threaded main loop for PulseAudio this->d->m_mainLoop = pa_threaded_mainloop_new(); if (!this->d->m_mainLoop) return; // Start main loop. if (pa_threaded_mainloop_start(this->d->m_mainLoop) != 0) { pa_threaded_mainloop_free(this->d->m_mainLoop); this->d->m_mainLoop = nullptr; return; } pa_threaded_mainloop_lock(this->d->m_mainLoop); // Get main loop abstration layer. auto mainLoopApi = pa_threaded_mainloop_get_api(this->d->m_mainLoop); if (!mainLoopApi) { pa_threaded_mainloop_unlock(this->d->m_mainLoop); pa_threaded_mainloop_stop(this->d->m_mainLoop); pa_threaded_mainloop_free(this->d->m_mainLoop); this->d->m_mainLoop = nullptr; return; } // Get a PulseAudio context. this->d->m_context = pa_context_new(mainLoopApi, QCoreApplication::applicationName() .toStdString() .c_str()); if (!this->d->m_context) { pa_threaded_mainloop_unlock(this->d->m_mainLoop); pa_threaded_mainloop_stop(this->d->m_mainLoop); pa_threaded_mainloop_free(this->d->m_mainLoop); this->d->m_mainLoop = nullptr; return; } // We need to set a state callback in order to connect to the server. pa_context_set_state_callback(this->d->m_context, AudioDevPulseAudioPrivate::contextStateCallbackInit, this); // Connect to PulseAudio server. if (pa_context_connect(this->d->m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) { pa_context_unref(this->d->m_context); this->d->m_context = nullptr; pa_threaded_mainloop_unlock(this->d->m_mainLoop); pa_threaded_mainloop_stop(this->d->m_mainLoop); pa_threaded_mainloop_free(this->d->m_mainLoop); this->d->m_mainLoop = nullptr; return; } static const QList<pa_context_state_t> expectedStates = { PA_CONTEXT_READY, PA_CONTEXT_FAILED, PA_CONTEXT_TERMINATED }; pa_context_state_t state; // Wait until the connection to the server is stablished. forever { state = pa_context_get_state(this->d->m_context); if (expectedStates.contains(state)) break; pa_threaded_mainloop_wait(this->d->m_mainLoop); } if (state != PA_CONTEXT_READY) { pa_context_disconnect(this->d->m_context); pa_context_unref(this->d->m_context); this->d->m_context = nullptr; pa_threaded_mainloop_unlock(this->d->m_mainLoop); pa_threaded_mainloop_stop(this->d->m_mainLoop); pa_threaded_mainloop_free(this->d->m_mainLoop); this->d->m_mainLoop = nullptr; return; } // Get server information. auto operation = pa_context_get_server_info(this->d->m_context, AudioDevPulseAudioPrivate::serverInfoCallback, this); while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(this->d->m_mainLoop); pa_operation_unref(operation); // Get sources information. operation = pa_context_get_source_info_list(this->d->m_context, AudioDevPulseAudioPrivate::sourceInfoCallback, this); while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(this->d->m_mainLoop); pa_operation_unref(operation); // Get sinks information. operation = pa_context_get_sink_info_list(this->d->m_context, AudioDevPulseAudioPrivate::sinkInfoCallback, this); while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(this->d->m_mainLoop); pa_operation_unref(operation); pa_context_set_subscribe_callback(this->d->m_context, AudioDevPulseAudioPrivate::deviceUpdateCallback, this); pa_operation_unref(pa_context_subscribe(this->d->m_context, pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SERVER), nullptr, this)); pa_threaded_mainloop_unlock(this->d->m_mainLoop); }
int main(int argc, char *const *argv) { static const char *context_name = "i3blocks-pulse-volume"; static const char *usage_str = "Usage: %s [-h] [-d] [-s INDEX] [-m FUNC]\n" "Options:\n" " -s INDEX: pulseaudio sink index on which to wait for " "changes (default: 0)\n" " -m FUNC : function used to compute the displayed volume value\n" " in there are multiple channels (eg. left/right):\n" " * avg: use average volume of all channels (default)\n" " * min: use minimum volume of all channels\n" " * max: use maximum volume of all channels\n" " -d : use decibel notation instead of 0-100 percentage; " "the sink may\n" " not support this feature\n"; options_t options; options.calculator = pa_cvolume_avg; options.use_decibel = 0; options.observed_index = 0; sink_info_data_t sink_info_data; sink_info_data.sink_ready = 0; sink_info_data.sink_changed = 0; pa_mainloop *pa_ml = NULL; pa_mainloop_api *pa_ml_api = NULL; pa_operation *pa_op = NULL; pa_context *pa_ctx = NULL; enum state_t state = FIRST_SINK_INFO; int pa_ready = CONN_WAIT; int opt; while ((opt = getopt(argc, argv, "m:s:dh")) != -1) { if (opt == 'm') { if (strcmp(optarg, "min") == 0) { options.calculator = pa_cvolume_min; } else if (strcmp(optarg, "max") == 0) { options.calculator = pa_cvolume_max; } else if (strcmp(optarg, "avg") == 0) { options.calculator = pa_cvolume_avg; } else { fprintf(stderr, usage_str, argv[0]); return 1; } } else if (opt == 's') { // Parse observed sink index errno = 0; char *endptr; uint32_t *oind = &options.observed_index; *oind = strtoul(optarg, &endptr, 10); if ((errno == ERANGE) || (errno != 0 && *oind == 0) || endptr == optarg) { fprintf(stderr, "%s: invalid sink index: %s\n", argv[0], optarg); fprintf(stderr, usage_str, argv[0]); return 1; } } else if (opt == 'd') { options.use_decibel = 1; } else if (opt == 'h') { fprintf(stderr, usage_str, argv[0]); return 0; } else { fprintf(stderr, usage_str, argv[0]); return 1; } } // Needed to filter out sink in callbacks sink_info_data.observed_index = options.observed_index; if ((pa_ml = pa_mainloop_new()) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio mainloop.\n"); return 2; } if ((pa_ml_api = pa_mainloop_get_api(pa_ml)) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio mainloop API.\n"); return 3; } if ((pa_ctx = pa_context_new(pa_ml_api, context_name)) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio context.\n"); return 4; } if (pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFAIL, NULL) < 0) { fprintf(stderr, "error: failed to connect to pulseaudio context: %s\n", pa_strerror(pa_context_errno(pa_ctx))); return 5; } pa_context_set_state_callback(pa_ctx, state_cb, &pa_ready); pa_context_set_subscribe_callback(pa_ctx, subscribe_cb, &sink_info_data); while (1) { if (pa_ready == CONN_WAIT) { pa_mainloop_iterate(pa_ml, 1, NULL); continue; } if (pa_ready == CONN_FAILED) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); fprintf(stderr, "error: failed to connect to pulseaudio context.\n"); return 6; } // Main loop automaton switch (state) { case FIRST_SINK_INFO: // First run pa_op = pa_context_get_sink_info_by_index(pa_ctx, options.observed_index, sink_info_cb, &sink_info_data); state = SUBSCRIBE; break; case SUBSCRIBE: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); if (!sink_info_data.sink_ready) { fprintf(stderr, "error: sink %u does not exist.\n", options.observed_index); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 7; } if (options.use_decibel && !sink_info_data.sink.can_decibel) { fprintf(stderr, "error: sink %u does not support decibel; " "try without `-d`.\n", options.observed_index); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 8; } // Show volume once at start show_volume(&sink_info_data.sink, &options); // Subsequent runs: wait for changes pa_op = pa_context_subscribe(pa_ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, &sink_info_data); state = SUBSCRIBED; } break; case SUBSCRIBED: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); state = WAIT_FOR_CHANGE; } break; case WAIT_FOR_CHANGE: if (sink_info_data.sink_changed) { sink_info_data.sink_changed = 0; pa_op = pa_context_get_sink_info_by_index(pa_ctx, options.observed_index, sink_info_cb, &sink_info_data); state = SHOW_CHANGE; } break; case SHOW_CHANGE: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); show_volume(&sink_info_data.sink, &options); state = WAIT_FOR_CHANGE; } break; default: return 7; } pa_mainloop_iterate(pa_ml, 1, NULL); } }
PulseAudioWrapper::PulseAudioWrapper(QObject *parent) : QObject(parent), d(new PulseAudioWrapperPrivate(this)) { PulseAudioWrapperPrivate::paMainLoop = pa_threaded_mainloop_new(); pa_threaded_mainloop_start(PulseAudioWrapperPrivate::paMainLoop); PulseAudioWrapperPrivate::paMainLoopApi = pa_threaded_mainloop_get_api(PulseAudioWrapperPrivate::paMainLoop); pa_threaded_mainloop_lock(PulseAudioWrapperPrivate::paMainLoop); PulseAudioWrapperPrivate::paContext = pa_context_new(PulseAudioWrapperPrivate::paMainLoopApi, qApp->applicationName().toUtf8().data()); pa_context_set_state_callback(PulseAudioWrapperPrivate::paContext, &PulseAudioWrapperPrivate::onContextNotify, NULL); pa_context_connect(PulseAudioWrapperPrivate::paContext, NULL, PA_CONTEXT_NOFLAGS, NULL); bool done = false; pa_context_state_t contextState; while (!done) { switch (contextState = pa_context_get_state(d->paContext)) { case PA_CONTEXT_UNCONNECTED: qDebug() << "Context state: PA_CONTEXT_UNCONNECTED"; break; case PA_CONTEXT_CONNECTING: qDebug() << "Context state: PA_CONTEXT_CONNECTING"; break; case PA_CONTEXT_AUTHORIZING: qDebug() << "Context state: PA_CONTEXT_AUTHORIZING"; break; case PA_CONTEXT_SETTING_NAME: qDebug() << "Context state: PA_CONTEXT_SETTING_NAME"; break; case PA_CONTEXT_READY: qDebug() << "Context state: PA_CONTEXT_READY"; done = true; break; case PA_CONTEXT_FAILED: qDebug() << "Context state: PA_CONTEXT_FAILED"; done = true; break; case PA_CONTEXT_TERMINATED: qDebug() << "Context state: PA_CONTEXT_TERMINATED"; done = true; break; } if (!done) pa_threaded_mainloop_wait(PulseAudioWrapperPrivate::paMainLoop); } if (contextState != PA_CONTEXT_READY) throw CallRecorderException("Unable to connect PulseAudio context!"); pa_operation* listCardsOp = pa_context_get_card_info_list(PulseAudioWrapperPrivate::paContext, &PulseAudioWrapperPrivate::onCardInfoList, d.data()); pa_threaded_mainloop_wait(PulseAudioWrapperPrivate::paMainLoop); pa_operation_unref(listCardsOp); pa_operation* listSinksOp = pa_context_get_sink_info_list(PulseAudioWrapperPrivate::paContext, &PulseAudioWrapperPrivate::onSinkInfoList, d.data()); pa_threaded_mainloop_wait(PulseAudioWrapperPrivate::paMainLoop); pa_operation_unref(listSinksOp); pa_operation* listSourcesOp = pa_context_get_source_info_list(PulseAudioWrapperPrivate::paContext, &PulseAudioWrapperPrivate::onSourceInfoList, d.data()); pa_threaded_mainloop_wait(PulseAudioWrapperPrivate::paMainLoop); pa_operation_unref(listSourcesOp); pa_context_set_subscribe_callback(PulseAudioWrapperPrivate::paContext, &PulseAudioWrapperPrivate::onContextSubscription, d.data()); pa_operation* subscriptionOp = pa_context_subscribe(PulseAudioWrapperPrivate::paContext, static_cast< pa_subscription_mask_t >( PA_SUBSCRIPTION_MASK_CARD | PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE), &PulseAudioWrapperPrivate::onContextSubscriptionSuccess, d.data()); pa_threaded_mainloop_wait(PulseAudioWrapperPrivate::paMainLoop); pa_operation_unref(subscriptionOp); pa_threaded_mainloop_unlock(PulseAudioWrapperPrivate::paMainLoop); }
static int pulse_open(int fmt, int rate, int nch) { pa_sample_spec ss; pa_operation *o = NULL; int success; assert(!mainloop); assert(!context); assert(!stream); assert(!connected); switch(fmt) { case FMT_U8: ss.format = PA_SAMPLE_U8; break; case FMT_S16_LE: ss.format = PA_SAMPLE_S16LE; break; case FMT_S16_BE: ss.format = PA_SAMPLE_S16BE; break; #ifdef PA_SAMPLE_S24_32LE case FMT_S24_LE: ss.format = PA_SAMPLE_S24_32LE; break; case FMT_S24_BE: ss.format = PA_SAMPLE_S24_32BE; break; #endif #ifdef PA_SAMPLE_S32LE case FMT_S32_LE: ss.format = PA_SAMPLE_S32LE; break; case FMT_S32_BE: ss.format = PA_SAMPLE_S32BE; break; #endif case FMT_FLOAT: ss.format = PA_SAMPLE_FLOAT32NE; break; default: return FALSE; } ss.rate = rate; ss.channels = nch; if (!pa_sample_spec_valid(&ss)) return FALSE; if (!(mainloop = pa_threaded_mainloop_new())) { ERROR ("Failed to allocate main loop"); goto fail; } pa_threaded_mainloop_lock(mainloop); if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Audacious"))) { ERROR ("Failed to allocate context"); goto unlock_and_fail; } pa_context_set_state_callback(context, context_state_cb, NULL); pa_context_set_subscribe_callback(context, subscribe_cb, NULL); if (pa_context_connect(context, NULL, 0, NULL) < 0) { ERROR ("Failed to connect to server: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } if (pa_threaded_mainloop_start(mainloop) < 0) { ERROR ("Failed to start main loop"); goto unlock_and_fail; } /* Wait until the context is ready */ pa_threaded_mainloop_wait(mainloop); if (pa_context_get_state(context) != PA_CONTEXT_READY) { ERROR ("Failed to connect to server: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } if (!(stream = pa_stream_new(context, "Audacious", &ss, NULL))) { ERROR ("Failed to create stream: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } pa_stream_set_state_callback(stream, stream_state_cb, NULL); pa_stream_set_write_callback(stream, stream_request_cb, NULL); pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL); /* Connect stream with sink and default volume */ /* Buffer struct */ int aud_buffer = aud_get_int(NULL, "output_buffer_size"); size_t buffer_size = pa_usec_to_bytes(aud_buffer, &ss) * 1000; pa_buffer_attr buffer = {(uint32_t) -1, buffer_size, (uint32_t) -1, (uint32_t) -1, buffer_size}; if (pa_stream_connect_playback(stream, NULL, &buffer, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) { ERROR ("Failed to connect stream: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } /* Wait until the stream is ready */ pa_threaded_mainloop_wait(mainloop); if (pa_stream_get_state(stream) != PA_STREAM_READY) { ERROR ("Failed to connect stream: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } /* Now subscribe to events */ if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) { ERROR ("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } success = 0; while (pa_operation_get_state(o) != PA_OPERATION_DONE) { CHECK_DEAD_GOTO(fail, 1); pa_threaded_mainloop_wait(mainloop); } if (!success) { ERROR ("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } pa_operation_unref(o); /* Now request the initial stream info */ if (!(o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_cb, NULL))) { ERROR ("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } while (pa_operation_get_state(o) != PA_OPERATION_DONE) { CHECK_DEAD_GOTO(fail, 1); pa_threaded_mainloop_wait(mainloop); } if (!volume_valid) { ERROR ("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } pa_operation_unref(o); do_trigger = 0; written = 0; flush_time = 0; bytes_per_second = FMT_SIZEOF (fmt) * nch * rate; connected = 1; volume_time_event = NULL; pa_threaded_mainloop_unlock(mainloop); return TRUE; unlock_and_fail: if (o) pa_operation_unref(o); pa_threaded_mainloop_unlock(mainloop); fail: pulse_close(); return FALSE; }
int soundio_pulseaudio_init(struct SoundIoPrivate *si) { struct SoundIo *soundio = &si->pub; struct SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; sipa->device_scan_queued = true; sipa->main_loop = pa_threaded_mainloop_new(); if (!sipa->main_loop) { destroy_pa(si); return SoundIoErrorNoMem; } pa_mainloop_api *main_loop_api = pa_threaded_mainloop_get_api(sipa->main_loop); sipa->props = pa_proplist_new(); if (!sipa->props) { destroy_pa(si); return SoundIoErrorNoMem; } sipa->pulse_context = pa_context_new_with_proplist(main_loop_api, soundio->app_name, sipa->props); if (!sipa->pulse_context) { destroy_pa(si); return SoundIoErrorNoMem; } pa_context_set_subscribe_callback(sipa->pulse_context, subscribe_callback, si); pa_context_set_state_callback(sipa->pulse_context, context_state_callback, si); int err = pa_context_connect(sipa->pulse_context, NULL, (pa_context_flags_t)0, NULL); if (err) { destroy_pa(si); return SoundIoErrorInitAudioBackend; } if (pa_threaded_mainloop_start(sipa->main_loop)) { destroy_pa(si); return SoundIoErrorNoMem; } pa_threaded_mainloop_lock(sipa->main_loop); // block until ready while (!sipa->ready_flag) pa_threaded_mainloop_wait(sipa->main_loop); if (sipa->connection_err) { pa_threaded_mainloop_unlock(sipa->main_loop); destroy_pa(si); return sipa->connection_err; } if ((err = subscribe_to_events(si))) { pa_threaded_mainloop_unlock(sipa->main_loop); destroy_pa(si); return err; } pa_threaded_mainloop_unlock(sipa->main_loop); si->destroy = destroy_pa; si->flush_events = flush_events_pa; si->wait_events = wait_events_pa; si->wakeup = wakeup_pa; si->force_device_scan = force_device_scan_pa; si->outstream_open = outstream_open_pa; si->outstream_destroy = outstream_destroy_pa; si->outstream_start = outstream_start_pa; si->outstream_begin_write = outstream_begin_write_pa; si->outstream_end_write = outstream_end_write_pa; si->outstream_clear_buffer = outstream_clear_buffer_pa; si->outstream_pause = outstream_pause_pa; si->outstream_get_latency = outstream_get_latency_pa; si->instream_open = instream_open_pa; si->instream_destroy = instream_destroy_pa; si->instream_start = instream_start_pa; si->instream_begin_read = instream_begin_read_pa; si->instream_end_read = instream_end_read_pa; si->instream_pause = instream_pause_pa; si->instream_get_latency = instream_get_latency_pa; return 0; }
static int pulse_open() { ENTER(__FUNCTION__); pa_sample_spec ss; pa_operation *o = NULL; int success; int ret = PULSE_ERROR; assert(!mainloop); assert(!context); assert(!stream); assert(!connected); pthread_mutex_init( &pulse_mutex, (const pthread_mutexattr_t *)NULL); ss.format = ESPEAK_FORMAT; ss.rate = wave_samplerate; ss.channels = ESPEAK_CHANNEL; if (!pa_sample_spec_valid(&ss)) return false; SHOW_TIME("pa_threaded_mainloop_new (call)"); if (!(mainloop = pa_threaded_mainloop_new())) { SHOW("Failed to allocate main loop\n",""); goto fail; } pa_threaded_mainloop_lock(mainloop); SHOW_TIME("pa_context_new (call)"); if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak"))) { SHOW("Failed to allocate context\n",""); goto unlock_and_fail; } pa_context_set_state_callback(context, context_state_cb, NULL); pa_context_set_subscribe_callback(context, subscribe_cb, NULL); SHOW_TIME("pa_context_connect (call)"); if (pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL) < 0) { SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context))); ret = PULSE_NO_CONNECTION; goto unlock_and_fail; } SHOW_TIME("pa_threaded_mainloop_start (call)"); if (pa_threaded_mainloop_start(mainloop) < 0) { SHOW("Failed to start main loop",""); goto unlock_and_fail; } /* Wait until the context is ready */ SHOW_TIME("pa_threaded_mainloop_wait"); pa_threaded_mainloop_wait(mainloop); if (pa_context_get_state(context) != PA_CONTEXT_READY) { SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context))); ret = PULSE_NO_CONNECTION; if (mainloop) pa_threaded_mainloop_stop(mainloop); goto unlock_and_fail; } SHOW_TIME("pa_stream_new"); if (!(stream = pa_stream_new(context, "unknown", &ss, NULL))) { SHOW("Failed to create stream: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } pa_stream_set_state_callback(stream, stream_state_cb, NULL); pa_stream_set_write_callback(stream, stream_request_cb, NULL); pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL); pa_buffer_attr a_attr; a_attr.maxlength = MAXLENGTH; a_attr.tlength = TLENGTH; a_attr.prebuf = PREBUF; a_attr.minreq = MINREQ; a_attr.fragsize = 0; SHOW_TIME("pa_connect_playback"); if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL) < 0) { SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } /* Wait until the stream is ready */ SHOW_TIME("pa_threaded_mainloop_wait"); pa_threaded_mainloop_wait(mainloop); if (pa_stream_get_state(stream) != PA_STREAM_READY) { SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } /* Now subscribe to events */ SHOW_TIME("pa_context_subscribe"); if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) { SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } success = 0; SHOW_TIME("pa_threaded_mainloop_wait"); while (pa_operation_get_state(o) != PA_OPERATION_DONE) { CHECK_DEAD_GOTO(fail, 1); pa_threaded_mainloop_wait(mainloop); } pa_operation_unref(o); if (!success) { SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context))); goto unlock_and_fail; } do_trigger = 0; written = 0; time_offset_msec = 0; just_flushed = 0; connected = 1; pa_threaded_mainloop_unlock(mainloop); SHOW_TIME("pulse_open (ret true)"); return PULSE_OK; unlock_and_fail: if (o) pa_operation_unref(o); pa_threaded_mainloop_unlock(mainloop); fail: // pulse_close(); if (ret == PULSE_NO_CONNECTION) { if (context) { SHOW_TIME("pa_context_disconnect (call)"); pa_context_disconnect(context); pa_context_unref(context); context = NULL; } if (mainloop) { SHOW_TIME("pa_threaded_mainloop_free (call)"); pa_threaded_mainloop_free(mainloop); mainloop = NULL; } } else { pulse_close(); } SHOW_TIME("pulse_open (ret false)"); return ret; }
/** * Callback to check the status of context initialization. */ static void xvd_context_state_callback (pa_context *c, void *userdata) { XvdInstance *i = (XvdInstance *) userdata; pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SERVER; pa_operation *op = NULL; if (!c || !userdata) { g_critical ("xvd_context_state_callback: invalid argument"); return; } switch (pa_context_get_state (c)) { case PA_CONTEXT_UNCONNECTED: g_debug ("xvd_context_state_callback: The context hasn't been connected yet"); break; case PA_CONTEXT_CONNECTING: g_debug ("xvd_context_state_callback: A connection is being established"); break; case PA_CONTEXT_AUTHORIZING: g_debug ("xvd_context_state_callback: The client is authorizing itself to the daemon"); break; case PA_CONTEXT_SETTING_NAME: g_debug ("xvd_context_state_callback: The client is passing its application name to the daemon"); break; case PA_CONTEXT_TERMINATED: g_debug ("xvd_context_state_callback: The connection was terminated cleanly"); i->sink_index = PA_INVALID_INDEX; xvd_connect_to_pulse (i); break; case PA_CONTEXT_FAILED: g_warning ("xvd_context_state_callback: The connection failed or was disconnected, is PulseAudio Daemon running?"); i->sink_index = PA_INVALID_INDEX; xvd_connect_to_pulse (i); break; case PA_CONTEXT_READY: g_debug ("xvd_context_state_callback: The connection is established, the context is ready to execute operations"); pa_context_set_subscribe_callback (c, xvd_subscribed_events_callback, userdata); /* subscribe to sink and server changes, we don't need more */ op = pa_context_subscribe (c, mask, NULL, NULL); if (!op) { g_critical ("xvd_context_state_callback: pa_context_subscribe() failed"); return; } pa_operation_unref(op); op = pa_context_get_server_info (c, xvd_server_info_callback, userdata); if (!op) { g_warning("xvd_context_state_callback: pa_context_get_server_info() failed"); return; } pa_operation_unref(op); break; } }
void Context::contextStateCallback(pa_context *c) { qCDebug(PLASMAPA) << "state callback"; pa_context_state_t state = pa_context_get_state(c); if (state == PA_CONTEXT_READY) { qCDebug(PLASMAPA) << "ready"; // 1. Register for the stream changes (except during probe) if (m_context == c) { pa_context_set_subscribe_callback(c, subscribe_cb, this); if (!PAOperation(pa_context_subscribe(c, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK| PA_SUBSCRIPTION_MASK_SOURCE| PA_SUBSCRIPTION_MASK_CLIENT| PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| PA_SUBSCRIPTION_MASK_CARD| PA_SUBSCRIPTION_MASK_SERVER), nullptr, nullptr))) { qCWarning(PLASMAPA) << "pa_context_subscribe() failed"; return; } } if (!PAOperation(pa_context_get_sink_info_list(c, sink_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_sink_info_list() failed"; return; } if (!PAOperation(pa_context_get_source_info_list(c, source_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_source_info_list() failed"; return; } if (!PAOperation(pa_context_get_client_info_list(c, client_cb, this))) { qCWarning(PLASMAPA) << "pa_context_client_info_list() failed"; return; } if (!PAOperation(pa_context_get_card_info_list(c, card_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_card_info_list() failed"; return; } if (!PAOperation(pa_context_get_sink_input_info_list(c, sink_input_callback, this))) { qCWarning(PLASMAPA) << "pa_context_get_sink_input_info_list() failed"; return; } if (!PAOperation(pa_context_get_source_output_info_list(c, source_output_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_source_output_info_list() failed"; return; } if (!PAOperation(pa_context_get_server_info(c, server_cb, this))) { qCWarning(PLASMAPA) << "pa_context_get_server_info() failed"; return; } if (PAOperation(pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, this))) { pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, this); PAOperation(pa_ext_stream_restore_subscribe(c, 1, nullptr, this)); } else { qCWarning(PLASMAPA) << "Failed to initialize stream_restore extension"; } } else if (!PA_CONTEXT_IS_GOOD(state)) { qCWarning(PLASMAPA) << "context kaput"; if (m_context) { pa_context_unref(m_context); m_context = nullptr; } reset(); QTimer::singleShot(1000, this, &Context::connectToDaemon); } }
bool CAESinkPULSE::Initialize(AEAudioFormat &format, std::string &device) { { CSingleLock lock(m_sec); m_IsAllocated = false; } m_passthrough = false; m_BytesPerSecond = 0; m_BufferSize = 0; m_filled_bytes = 0; m_lastPackageStamp = 0; m_Channels = 0; m_Stream = NULL; m_Context = NULL; m_periodSize = 0; if (!SetupContext(NULL, &m_Context, &m_MainLoop)) { CLog::Log(LOGNOTICE, "PulseAudio might not be running. Context was not created."); Deinitialize(); return false; } pa_threaded_mainloop_lock(m_MainLoop); struct pa_channel_map map; pa_channel_map_init(&map); // PULSE cannot cope with e.g. planar formats so we fallback to FLOAT // when we receive an invalid pulse format if (AEFormatToPulseFormat(format.m_dataFormat) == PA_SAMPLE_INVALID) { CLog::Log(LOGDEBUG, "PULSE does not support format: %s - will fallback to AE_FMT_FLOAT", CAEUtil::DataFormatToStr(format.m_dataFormat)); format.m_dataFormat = AE_FMT_FLOAT; } m_passthrough = AE_IS_RAW(format.m_dataFormat); if(m_passthrough) { map.channels = 2; format.m_channelLayout = AE_CH_LAYOUT_2_0; } else { map = AEChannelMapToPAChannel(format.m_channelLayout); // if count has changed we need to fit the AE Map if(map.channels != format.m_channelLayout.Count()) format.m_channelLayout = PAChannelToAEChannelMap(map); } m_Channels = format.m_channelLayout.Count(); // store information about current sink SinkInfoStruct sinkStruct; sinkStruct.mainloop = m_MainLoop; sinkStruct.device_found = false; // get real sample rate of the device we want to open - to avoid resampling bool isDefaultDevice = (device == "Default"); WaitForOperation(pa_context_get_sink_info_by_name(m_Context, isDefaultDevice ? NULL : device.c_str(), SinkInfoCallback, &sinkStruct), m_MainLoop, "Get Sink Info"); // only check if the device is existing - don't alter the sample rate if (!sinkStruct.device_found) { CLog::Log(LOGERROR, "PulseAudio: Sink %s not found", device.c_str()); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } // Pulse can resample everything between 1 hz and 192000 hz // Make sure we are in the range that we originally added format.m_sampleRate = std::max(5512U, std::min(format.m_sampleRate, 192000U)); pa_format_info *info[1]; info[0] = pa_format_info_new(); info[0]->encoding = AEFormatToPulseEncoding(format.m_dataFormat); if(!m_passthrough) { pa_format_info_set_sample_format(info[0], AEFormatToPulseFormat(format.m_dataFormat)); pa_format_info_set_channel_map(info[0], &map); } pa_format_info_set_channels(info[0], m_Channels); // PA requires m_encodedRate in order to do EAC3 unsigned int samplerate = format.m_sampleRate; if (m_passthrough && (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937)) { // this is only used internally for PA to use EAC3 samplerate = format.m_encodedRate; } pa_format_info_set_rate(info[0], samplerate); if (!pa_format_info_valid(info[0])) { CLog::Log(LOGERROR, "PulseAudio: Invalid format info"); pa_format_info_free(info[0]); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } pa_sample_spec spec; #if PA_CHECK_VERSION(2,0,0) pa_format_info_to_sample_spec(info[0], &spec, NULL); #else spec.rate = (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937) ? 4 * samplerate : samplerate; spec.format = AEFormatToPulseFormat(format.m_dataFormat); spec.channels = m_Channels; #endif if (!pa_sample_spec_valid(&spec)) { CLog::Log(LOGERROR, "PulseAudio: Invalid sample spec"); pa_format_info_free(info[0]); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } m_BytesPerSecond = pa_bytes_per_second(&spec); unsigned int frameSize = pa_frame_size(&spec); m_Stream = pa_stream_new_extended(m_Context, "kodi audio stream", info, 1, NULL); pa_format_info_free(info[0]); if (m_Stream == NULL) { CLog::Log(LOGERROR, "PulseAudio: Could not create a stream"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } pa_stream_set_state_callback(m_Stream, StreamStateCallback, m_MainLoop); pa_stream_set_write_callback(m_Stream, StreamRequestCallback, m_MainLoop); pa_stream_set_latency_update_callback(m_Stream, StreamLatencyUpdateCallback, m_MainLoop); // default buffer construction // align with AE's max buffer unsigned int latency = m_BytesPerSecond / 2.5; // 400 ms unsigned int process_time = latency / 4; // 100 ms if(sinkStruct.isHWDevice) { // on hw devices buffers can be further reduced // 200ms max latency // 50ms min packet size latency = m_BytesPerSecond / 5; process_time = latency / 4; } pa_buffer_attr buffer_attr; buffer_attr.fragsize = latency; buffer_attr.maxlength = (uint32_t) -1; buffer_attr.minreq = process_time; buffer_attr.prebuf = (uint32_t) -1; buffer_attr.tlength = latency; if (pa_stream_connect_playback(m_Stream, isDefaultDevice ? NULL : device.c_str(), &buffer_attr, ((pa_stream_flags)(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY)), NULL, NULL) < 0) { CLog::Log(LOGERROR, "PulseAudio: Failed to connect stream to output"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } /* Wait until the stream is ready */ do { pa_threaded_mainloop_wait(m_MainLoop); CLog::Log(LOGDEBUG, "PulseAudio: Stream %s", StreamStateToString(pa_stream_get_state(m_Stream))); } while (pa_stream_get_state(m_Stream) != PA_STREAM_READY && pa_stream_get_state(m_Stream) != PA_STREAM_FAILED); if (pa_stream_get_state(m_Stream) == PA_STREAM_FAILED) { CLog::Log(LOGERROR, "PulseAudio: Waited for the stream but it failed"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } const pa_buffer_attr *a; if (!(a = pa_stream_get_buffer_attr(m_Stream))) { CLog::Log(LOGERROR, "PulseAudio: %s", pa_strerror(pa_context_errno(m_Context))); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } else { unsigned int packetSize = a->minreq; m_BufferSize = a->tlength; m_periodSize = a->minreq; format.m_frames = packetSize / frameSize; } { CSingleLock lock(m_sec); // Register Callback for Sink changes pa_context_set_subscribe_callback(m_Context, SinkChangedCallback, this); const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SINK; pa_operation *op = pa_context_subscribe(m_Context, mask, NULL, this); if (op != NULL) pa_operation_unref(op); // Register Callback for Sink Info changes - this handles volume pa_context_set_subscribe_callback(m_Context, SinkInputInfoChangedCallback, this); const pa_subscription_mask_t mask_input = PA_SUBSCRIPTION_MASK_SINK_INPUT; pa_operation* op_sinfo = pa_context_subscribe(m_Context, mask_input, NULL, this); if (op_sinfo != NULL) pa_operation_unref(op_sinfo); } pa_threaded_mainloop_unlock(m_MainLoop); format.m_frameSize = frameSize; format.m_frameSamples = format.m_frames * format.m_channelLayout.Count(); m_format = format; format.m_dataFormat = m_passthrough ? AE_FMT_S16NE : format.m_dataFormat; CLog::Log(LOGNOTICE, "PulseAudio: Opened device %s in %s mode with Buffersize %u ms", device.c_str(), m_passthrough ? "passthrough" : "pcm", (unsigned int) ((m_BufferSize / (float) m_BytesPerSecond) * 1000)); // Cork stream will resume when adding first package Pause(true); { CSingleLock lock(m_sec); m_IsAllocated = true; } return true; }