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;
}
예제 #2
0
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);
		}
	}
}
예제 #3
0
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;
    }
}
예제 #4
0
/**
 * 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;
}
예제 #5
0
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";
    }

}
예제 #6
0
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;                                                             
    }                                                                           
}
예제 #7
0
/**
 * 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;
}
예제 #8
0
파일: pulseaudio.c 프로젝트: Allba/showtime
/**
 * 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;
    }
}
예제 #10
0
파일: ausrv.c 프로젝트: jusa/tone-generator
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;
    }
}
예제 #12
0
파일: pulse.c 프로젝트: IAPark/vlc
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;*/
}
예제 #13
0
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;
  }
}
예제 #14
0
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);
	}
}
예제 #15
0
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;
  }
예제 #16
0
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)));
  }
}
예제 #17
0
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;
}
예제 #18
0
파일: main.c 프로젝트: smatting/pulsemixer
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;
}
예제 #19
0
파일: uneven.c 프로젝트: ASzc/uneven
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, &current_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, &current_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;
}
예제 #20
0
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;
  }
}
예제 #21
0
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;
}
예제 #22
0
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);
}
예제 #23
0
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);
}
예제 #25
0
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;
}
예제 #26
0
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;
}
예제 #27
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;

}
예제 #28
0
/**
 * 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;
    }
}
예제 #29
0
파일: context.cpp 프로젝트: KDE/plasma-pa
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);
    }
}
예제 #30
0
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;
}