pa_simple* pa_simple_new_proplist(
        const char *server,
        const char *name,
        pa_stream_direction_t dir,
        const char *dev,
        const char *stream_name,
        const pa_sample_spec *ss,
        const pa_channel_map *map,
        const pa_buffer_attr *attr,
        pa_proplist *proplist,
        int *rerror) {

    pa_simple *p;
    int error = PA_ERR_INTERNAL, r;

    CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL);
    CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL);
    CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL);
    CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
    CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)

    p = pa_xnew0(pa_simple, 1);
    p->direction = dir;

    if (!(p->mainloop = pa_threaded_mainloop_new()))
        goto fail;

    if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
        goto fail;

    pa_context_set_state_callback(p->context, context_state_cb, p);

    if (pa_context_connect(p->context, server, 0, NULL) < 0) {
        error = pa_context_errno(p->context);
        goto fail;
    }

    pa_threaded_mainloop_lock(p->mainloop);

    if (pa_threaded_mainloop_start(p->mainloop) < 0)
        goto unlock_and_fail;

    for (;;) {
        pa_context_state_t state;

        state = pa_context_get_state(p->context);

        if (state == PA_CONTEXT_READY)
            break;

        if (!PA_CONTEXT_IS_GOOD(state)) {
            error = pa_context_errno(p->context);
            goto unlock_and_fail;
        }

        /* Wait until the context is ready */
        pa_threaded_mainloop_wait(p->mainloop);
    }

    if (!(p->stream = pa_stream_new_with_proplist(p->context, stream_name, ss, map, proplist))) {
        error = pa_context_errno(p->context);
        goto unlock_and_fail;
    }

    pa_stream_set_state_callback(p->stream, stream_state_cb, p);
    pa_stream_set_read_callback(p->stream, stream_request_cb, p);
    pa_stream_set_write_callback(p->stream, stream_request_cb, p);
    pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p);

    if (dir == PA_STREAM_PLAYBACK)
        r = pa_stream_connect_playback(p->stream, dev, attr,
                                       PA_STREAM_INTERPOLATE_TIMING
                                       |PA_STREAM_ADJUST_LATENCY
                                       |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
    else
        r = pa_stream_connect_record(p->stream, dev, attr,
                                     PA_STREAM_INTERPOLATE_TIMING
                                     |PA_STREAM_ADJUST_LATENCY
                                     |PA_STREAM_AUTO_TIMING_UPDATE
                                     |PA_STREAM_START_CORKED);

    if (r < 0) {
        error = pa_context_errno(p->context);
        goto unlock_and_fail;
    }

    for (;;) {
        pa_stream_state_t state;

        state = pa_stream_get_state(p->stream);

        if (state == PA_STREAM_READY)
            break;

        if (!PA_STREAM_IS_GOOD(state)) {
            error = pa_context_errno(p->context);
            goto unlock_and_fail;
        }

        /* Wait until the stream is ready */
        pa_threaded_mainloop_wait(p->mainloop);
    }

    pa_threaded_mainloop_unlock(p->mainloop);

    return p;

unlock_and_fail:
    pa_threaded_mainloop_unlock(p->mainloop);

fail:
    if (rerror)
        *rerror = error;
    pa_simple_free(p);
    return NULL;
}
예제 #2
0
파일: ao_pulse.c 프로젝트: jmglogow/mpv
static int pa_init_boilerplate(struct ao *ao)
{
    struct priv *priv = ao->priv;
    char *host = priv->cfg_host && priv->cfg_host[0] ? priv->cfg_host : NULL;
    bool locked = false;

    pthread_mutex_init(&priv->wakeup_lock, NULL);
    pthread_cond_init(&priv->wakeup, NULL);

    if (!(priv->mainloop = pa_threaded_mainloop_new())) {
        MP_ERR(ao, "Failed to allocate main loop\n");
        goto fail;
    }

    if (pa_threaded_mainloop_start(priv->mainloop) < 0)
        goto fail;

    pa_threaded_mainloop_lock(priv->mainloop);
    locked = true;

    if (!(priv->context = pa_context_new(pa_threaded_mainloop_get_api(
                                         priv->mainloop), ao->client_name)))
    {
        MP_ERR(ao, "Failed to allocate context\n");
        goto fail;
    }

    MP_VERBOSE(ao, "Library version: %s\n", pa_get_library_version());
    MP_VERBOSE(ao, "Proto: %lu\n",
        (long)pa_context_get_protocol_version(priv->context));
    MP_VERBOSE(ao, "Server proto: %lu\n",
        (long)pa_context_get_server_protocol_version(priv->context));

    pa_context_set_state_callback(priv->context, context_state_cb, ao);
    pa_context_set_subscribe_callback(priv->context, subscribe_cb, ao);

    if (pa_context_connect(priv->context, host, 0, NULL) < 0)
        goto fail;

    /* Wait until the context is ready */
    while (1) {
        int state = pa_context_get_state(priv->context);
        if (state == PA_CONTEXT_READY)
            break;
        if (!PA_CONTEXT_IS_GOOD(state))
            goto fail;
        pa_threaded_mainloop_wait(priv->mainloop);
    }

    pa_threaded_mainloop_unlock(priv->mainloop);
    return 0;

fail:
    if (locked)
        pa_threaded_mainloop_unlock(priv->mainloop);

    if (priv->context) {
        pa_threaded_mainloop_lock(priv->mainloop);
        if (!(pa_context_errno(priv->context) == PA_ERR_CONNECTIONREFUSED
              && ao->probing))
            GENERIC_ERR_MSG("Init failed");
        pa_threaded_mainloop_unlock(priv->mainloop);
    }
    uninit(ao);
    return -1;
}
예제 #3
0
bool AudioOutputPulseAudio::ContextConnect(void)
{
    QString fn_log_tag = "ContextConnect, ";
    if (pcontext)
    {
        VBERROR(fn_log_tag + "context appears to exist, but shouldn't (yet)");
        pa_context_unref(pcontext);
        pcontext = NULL;
        return false;
    }
    pa_proplist *proplist = pa_proplist_new();
    if (!proplist)
    {
        VBERROR(fn_log_tag + QString("failed to create new proplist"));
        return false;
    }
    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "MythTV");
    pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "mythtv");
    pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");
    pcontext =
        pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mainloop),
                                     "MythTV", proplist);
    if (!pcontext)
    {
        VBERROR(fn_log_tag + "failed to acquire new context");
        return false;
    }
    pa_context_set_state_callback(pcontext, ContextStateCallback, this);

    char *pulse_host = ChooseHost();
    int chk = pa_context_connect(
        pcontext, pulse_host, (pa_context_flags_t)0, NULL);

    delete[] pulse_host;

    if (chk < 0)
    {
        VBERROR(fn_log_tag + QString("context connect failed: %1")
                             .arg(pa_strerror(pa_context_errno(pcontext))));
        return false;
    }
    bool connected = false;
    pa_context_state_t state = pa_context_get_state(pcontext);
    for (; !connected; state = pa_context_get_state(pcontext))
    {
        switch(state)
        {
            case PA_CONTEXT_READY:
                VBAUDIO(fn_log_tag +"context connection ready");
                connected = true;
                continue;

            case PA_CONTEXT_FAILED:
            case PA_CONTEXT_TERMINATED:
                VBERROR(fn_log_tag +
                        QString("context connection failed or terminated: %1")
                        .arg(pa_strerror(pa_context_errno(pcontext))));
                return false;

            default:
                VBAUDIO(fn_log_tag + "waiting for context connection ready");
                pa_threaded_mainloop_wait(mainloop);
                break;
        }
    }

    pa_operation *op =
        pa_context_get_server_info(pcontext, ServerInfoCallback, this);

    if (op)
        pa_operation_unref(op);
    else
        VBERROR(fn_log_tag + "failed to get PulseAudio server info");

    return true;
}
예제 #4
0
/**
 * Initializes the PulseAudio main loop and connects to the PulseAudio server.
 * @return a PulseAudio context on success, or NULL on error
 */
pa_context *vlc_pa_connect (vlc_object_t *obj, pa_threaded_mainloop **mlp)
{
    msg_Dbg (obj, "using library version %s", pa_get_library_version ());
    msg_Dbg (obj, " (compiled with version %s, protocol %u)",
             pa_get_headers_version (), PA_PROTOCOL_VERSION);

    /* Initialize main loop */
    pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new ();
    if (unlikely(mainloop == NULL))
        return NULL;

    if (pa_threaded_mainloop_start (mainloop) < 0)
    {
        pa_threaded_mainloop_free (mainloop);
        return NULL;
    }

    /* Fill in context (client) properties */
    char *ua = var_InheritString (obj, "user-agent");
    pa_proplist *props = pa_proplist_new ();
    if (likely(props != NULL))
    {
        pa_proplist_sets (props, PA_PROP_APPLICATION_NAME, ua);
        pa_proplist_sets (props, PA_PROP_APPLICATION_ID, "org.VideoLAN.VLC");
        pa_proplist_sets (props, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
        pa_proplist_sets (props, PA_PROP_APPLICATION_ICON_NAME, PACKAGE_NAME);
        //pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE, _("C"));
        pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE,
                          setlocale (LC_MESSAGES, NULL));

        pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_ID, "%lu",
                          (unsigned long) getpid ());
        //pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_BINARY,
        //                  PACKAGE_NAME);

        for (size_t max = sysconf (_SC_GETPW_R_SIZE_MAX), len = max % 1024 + 1024;
             len < max; len += 1024)
        {
            struct passwd pwbuf, *pw;
            char buf[len];

            if (getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw) == 0
             && pw != NULL)
                pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_USER,
                                  pw->pw_name);
        }

        for (size_t max = sysconf (_SC_HOST_NAME_MAX), len = max % 1024 + 1024;
             len < max; len += 1024)
        {
            char hostname[len];

            if (gethostname (hostname, sizeof (hostname)) == 0)
                pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_HOST,
                                  hostname);
        }

        const char *session = getenv ("XDG_SESSION_COOKIE");
        if (session != NULL)
        {
            pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_MACHINE_ID,
                              "%.32s", session); /* XXX: is this valid? */
            pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_SESSION_ID,
                              session);
        }
    }

    /* Connect to PulseAudio daemon */
    pa_context *ctx;
    pa_mainloop_api *api;

    pa_threaded_mainloop_lock (mainloop);
    api = pa_threaded_mainloop_get_api (mainloop);
    ctx = pa_context_new_with_proplist (api, ua, props);
    free (ua);
    if (props != NULL)
        pa_proplist_free (props);
    if (unlikely(ctx == NULL))
        goto fail;

    pa_context_set_state_callback (ctx, context_state_cb, mainloop);
    pa_context_set_event_callback (ctx, context_event_cb, obj);
    if (pa_context_connect (ctx, NULL, 0, NULL) < 0
     || context_wait (ctx, mainloop))
    {
        vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
        pa_context_unref (ctx);
        goto fail;
    }
    msg_Dbg (obj, "connected %s to %s as client #%"PRIu32,
             pa_context_is_local (ctx) ? "locally" : "remotely",
             pa_context_get_server (ctx), pa_context_get_index (ctx));
    msg_Dbg (obj, "using protocol %"PRIu32", server protocol %"PRIu32,
             pa_context_get_protocol_version (ctx),
             pa_context_get_server_protocol_version (ctx));

    pa_threaded_mainloop_unlock (mainloop);
    *mlp = mainloop;
    return ctx;

fail:
    pa_threaded_mainloop_unlock (mainloop);
    pa_threaded_mainloop_stop (mainloop);
    pa_threaded_mainloop_free (mainloop);
    return NULL;
}
예제 #5
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;
}
int main(int argc, char *argv[]) {

	if (argc != 3)
	{
		printf("usage: %s property value\n  property: pulseaudio sink-input property (ex:application.name)\n  value: the value for the wanted property (ex: \"Quod Libet\")\n", argv[0]);
		return -1;
	}

	pa_mainloop* pa_ml;
	pa_mainloop_api* pa_mlapi;
	pa_context* pa_ctx;
	pa_operation* pa_op;

	int state = 0;
	int pa_ready = 0;

	pa_ml = pa_mainloop_new();
	pa_mlapi = pa_mainloop_get_api(pa_ml);
	pa_ctx = pa_context_new(pa_mlapi, "my_context");

	pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);

	pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);

	for (;;) {
		if (pa_ready == 0) {
			pa_mainloop_iterate(pa_ml, 1, NULL);
			continue;
        	}
		if (pa_ready == 2) {
			pa_context_disconnect(pa_ctx);
			pa_context_unref(pa_ctx);
			pa_mainloop_free(pa_ml);
			return -1;
        	}

		switch (state) {
			case 0:

				pa_op = pa_context_get_sink_input_info_list(pa_ctx, pa_sink_input_info_cb, (void*)argv);

				state++;
				break;
			case 1:
				if (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
					//printf("PA_OPERATION_RUNNING\n");
				}

				if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {

					// clean

					pa_operation_unref(pa_op);

					pa_context_disconnect(pa_ctx);
					pa_context_unref(pa_ctx);

					pa_mainloop_free(pa_ml);
				
					return 0;
				}
				break;
			default:
				// We should never see this state
				fprintf(stderr, "in state %d\n", state);
				return -1;
		}

		pa_mainloop_iterate(pa_ml, 1, NULL);

	}

	printf("we should not be here...\n");

	return -1;
}
예제 #7
0
	bool Init()
	{
		int ret = 0;

		mPMainLoop = pa_threaded_mainloop_new();
		pa_mainloop_api *mlapi = pa_threaded_mainloop_get_api(mPMainLoop);

		mPContext = pa_context_new (mlapi, "USBqemu-pulse");

		ret = pa_context_connect (mPContext,
			mServer,
			PA_CONTEXT_NOFLAGS,
			NULL
		);

		OSDebugOut("pa_context_connect %s\n", pa_strerror(ret));
		if (ret != PA_OK)
			goto error;

		pa_context_set_state_callback(mPContext,
			pa_context_state_cb,
			&mPAready
		);

		pa_threaded_mainloop_start(mPMainLoop);

		// wait for pa_context_state_cb
		for(;;)
		{
			if(mPAready == 1) break;
			if(mPAready == 2 || mQuit) goto error;
		}

		mStream = pa_stream_new(mPContext,
			"USBqemu-pulse",
			&mSSpec,
			NULL
		);

		pa_stream_set_read_callback(mStream,
			stream_read_cb,
			this
		);

		// Sets individual read callback fragsize but recording itself
		// still "lags" ~1sec (read_cb is called in bursts) without
		// PA_STREAM_ADJUST_LATENCY
		pa_buffer_attr buffer_attr;
		buffer_attr.maxlength = (uint32_t) -1;
		buffer_attr.tlength = (uint32_t) -1;
		buffer_attr.prebuf = (uint32_t) -1;
		buffer_attr.minreq = (uint32_t) -1;
		buffer_attr.fragsize = pa_usec_to_bytes(mBuffering * 1000, &mSSpec);
		OSDebugOut("usec_to_bytes %zu\n", buffer_attr.fragsize);

		ret = pa_stream_connect_record(mStream,
			mDevice.c_str(),
			&buffer_attr,
			PA_STREAM_ADJUST_LATENCY
		);

		OSDebugOut("pa_stream_connect_record %s\n", pa_strerror(ret));
		if (ret != PA_OK)
			goto error;

		// Setup resampler
		if (mResampler)
			mResampler = src_delete(mResampler);

		mResampler = src_new(SRC_SINC_FASTEST, mSSpec.channels, &ret);
		if (!mResampler)
		{
			OSDebugOut("Failed to create resampler: error %08X\n", ret);
			goto error;
		}

		mLastGetBuffer = hrc::now();
		return true;
	error:
		Uninit();
		return false;
	}
예제 #8
0
int soundio_pulseaudio_init(SoundIoPrivate *si) {
    SoundIo *soundio = &si->pub;
    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;
}
예제 #9
0
void quisk_start_sound_pulseaudio(struct sound_dev **pCapture, struct sound_dev **pPlayback) {
    int num_pa_devices = 0;
    int i;
    //sorted lists of local and remote devices
    struct sound_dev *LocalPulseDevices[PA_LIST_SIZE] = {NULL};
    struct sound_dev *RemotePulseDevices[PA_LIST_SIZE] = {NULL};

    sort_devices(pCapture, LocalPulseDevices, RemotePulseDevices);
    sort_devices(pPlayback, LocalPulseDevices, RemotePulseDevices);
    
    if (!RemotePulseDevices[0] && !LocalPulseDevices[0]) {
        if (quisk_sound_state.verbose_pulse)
            printf("No pulseaudio devices to open!\n");
        return; //nothing to open. No need to start the mainloop.
    }

    // Create a mainloop API
    pa_ml = pa_threaded_mainloop_new();
    pa_mlapi = pa_threaded_mainloop_get_api(pa_ml);

    assert(pa_signal_init(pa_mlapi) == 0);
  
    if (pa_threaded_mainloop_start(pa_ml) < 0) {
        printf("pa_mainloop_run() failed.");
        exit(1);
    }
    else
        printf("Pulseaudio threaded mainloop started\n");
  
    pa_threaded_mainloop_lock(pa_ml);

    if (RemotePulseDevices[0]) {	//we've got at least 1 remote device
        pa_IQ_ctx = pa_context_new(pa_mlapi, "Quisk-remote");
        if (pa_context_connect(pa_IQ_ctx, quisk_sound_state.IQ_server, 0, NULL) < 0)
            printf("Failed to connect to remote Pulseaudio server\n");
        pa_context_set_state_callback(pa_IQ_ctx, state_cb, RemotePulseDevices); //send a list of remote devices to open
    }

    if (LocalPulseDevices[0]) {	//we've got at least 1 local device
        pa_ctx = pa_context_new(pa_mlapi, "Quisk-local");
        if (pa_context_connect(pa_ctx, NULL, 0, NULL) < 0)
            printf("Failed to connect to local Pulseaudio server\n");
        pa_context_set_state_callback(pa_ctx, state_cb, LocalPulseDevices);
    }


    pa_threaded_mainloop_unlock(pa_ml);

    for(i=0; LocalPulseDevices[i]; i++) {
            num_pa_devices++;
    }

    for(i=0; RemotePulseDevices[i]; i++) {
            num_pa_devices++;
    }

    if (quisk_sound_state.verbose_pulse)
        printf("Waiting for %d streams to start\n", num_pa_devices);
    
    while (streams_ready < num_pa_devices); // wait for all the devices to open

    if (quisk_sound_state.verbose_pulse)
        printf("All streams started\n");
    

}
예제 #10
0
PyObject * quisk_pa_sound_devices(PyObject * self, PyObject * args)
{	// Return a list of PulseAudio device names [pycapt, pyplay]
	PyObject * pylist, * pycapt, * pyplay;
	pa_mainloop *pa_names_ml;
	pa_mainloop_api *pa_names_mlapi;
	pa_operation *pa_op=NULL;
	pa_context *pa_names_ctx;
	int state = 0;

	if (!PyArg_ParseTuple (args, ""))
		return NULL;
	// Each pycapt and pyplay is (dev name, description, alsa name)
	pylist = PyList_New(0);		// list [pycapt, pyplay]
	pycapt = PyList_New(0);		// list of capture devices
	pyplay = PyList_New(0);		// list of play devices
	PyList_Append(pylist, pycapt);
	PyList_Append(pylist, pyplay);
    
    //printf("Starting name loop\n");

	// Create a mainloop API and connection to the default server
	pa_names_ml = pa_mainloop_new();
	pa_names_mlapi = pa_mainloop_get_api(pa_names_ml);
	pa_names_ctx = pa_context_new(pa_names_mlapi, "DeviceNames");
 
	// This function connects to the pulse server
	if (pa_context_connect(pa_names_ctx, NULL, 0, NULL) < 0) {
	   if (quisk_sound_state.verbose_pulse)
	      printf("No local daemon to connect to for show_pulse_audio_devices option\n");
	   return pylist;
	}

	// This function defines a callback so the server will tell us it's state.
	pa_context_set_state_callback(pa_names_ctx, pa_names_state_cb, &state);

	// Now we'll enter into an infinite loop until we get the data we receive or if there's an error
	while (state < 10) {
		switch (state) {
		case 0:	// We can't do anything until PA is ready
			pa_mainloop_iterate(pa_names_ml, 1, NULL);
			break;
		case 1:
			// This sends an operation to the server.  pa_sinklist_info is
			// our callback function and a pointer to our devicelist will
			// be passed to the callback.
			pa_op = pa_context_get_sink_info_list(pa_names_ctx, pa_sinklist_cb, pyplay);
			// Update state for next iteration through the loop
			state++;
			pa_mainloop_iterate(pa_names_ml, 1, NULL);
			break;
		case 2:
			// Now we wait for our operation to complete.  When it's
			// complete our pa_output_devicelist is filled out, and we move
			// along to the next state
			if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
				pa_operation_unref(pa_op);
				// Now we perform another operation to get the source
				// (input device) list just like before.
				pa_op = pa_context_get_source_info_list(pa_names_ctx, pa_sourcelist_cb, pycapt);
				// Update the state so we know what to do next
				state++;
			}
			pa_mainloop_iterate(pa_names_ml, 1, NULL);
			break;
		case 3:
			if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
				pa_operation_unref(pa_op);
				state = 9;
			}
			else
				pa_mainloop_iterate(pa_names_ml, 1, NULL);
			break;
		case 9:				// Now we're done, clean up and disconnect and return
			pa_context_disconnect(pa_names_ctx);
			pa_context_unref(pa_names_ctx);
			pa_mainloop_free(pa_names_ml);
			state = 99;
			break;
		}
	}
    //printf("Finished with name loop\n");
	return pylist;
}
예제 #11
0
struct audio_service* audio_service_create()
{
	struct audio_service *service;
	LSError error;
	pa_mainloop_api *mainloop_api;
	char name[100];

	service = g_try_new0(struct audio_service, 1);
	if (!service)
		return NULL;

	LSErrorInit(&error);

	if (!LSRegisterPubPriv("org.webosports.audio", &service->handle, false, &error)) {
		g_warning("Failed to register the luna service: %s", error.message);
		LSErrorFree(&error);
		goto error;
	}

	if (!LSRegisterCategory(service->handle, "/", audio_service_methods,
			NULL, NULL, &error)) {
		g_warning("Could not register service category: %s", error.message);
		LSErrorFree(&error);
		goto error;
	}

	if (!LSCategorySetData(service->handle, "/", service, &error)) {
		g_warning("Could not set daa for service category: %s", error.message);
		LSErrorFree(&error);
		goto error;
	}

	if (!LSGmainAttach(service->handle, event_loop, &error)) {
		g_warning("Could not attach service handle to mainloop: %s", error.message);
		LSErrorFree(&error);
		goto error;
	}

	service->pa_mainloop = pa_glib_mainloop_new(g_main_context_default());
	mainloop_api = pa_glib_mainloop_get_api(service->pa_mainloop);

	snprintf(name, 100, "AudioServiceContext:%i", getpid());
	service->context = pa_context_new(mainloop_api, name);
	service->context_initialized = false;
	pa_context_set_state_callback(service->context, context_state_cb, service);

	if (pa_context_connect(service->context, NULL, 0, NULL) < 0) {
		g_warning("Failed to connect to PulseAudio");
		pa_context_unref(service->context);
		pa_glib_mainloop_free(service->pa_mainloop);
		goto error;
	}

	sample_list = g_slist_alloc();

	return service;

error:
	if (service->handle != NULL) {
		LSUnregister(service->handle, &error);
		LSErrorFree(&error);
	}

	g_free(service);

	return NULL;
}
예제 #12
0
파일: pulse.c 프로젝트: shanewfx/vlc-arib
/*****************************************************************************
 * Open: open the audio device
 *****************************************************************************/
static int Open ( vlc_object_t *p_this )
{
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    struct aout_sys_t * p_sys;
    struct pa_sample_spec ss;
    const struct pa_buffer_attr *buffer_attr;
    struct pa_buffer_attr a;
    struct pa_channel_map map;

    /* Allocate structures */
    p_aout->output.p_sys = p_sys = calloc( 1, sizeof( aout_sys_t ) );
    if( p_sys == NULL )
        return VLC_ENOMEM;

    PULSE_DEBUG( "Pulse start initialization");

    ss.channels = aout_FormatNbChannels( &p_aout->output.output ); /* Get the input stream channel count */

    /* Setup the pulse audio stream based on the input stream count */
    switch(ss.channels)
    {
        case 8:
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
                | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
                | AOUT_CHAN_LFE;
            break;
        case 6:
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
                | AOUT_CHAN_LFE;
            break;

        case 4:
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
            break;

        case 2:
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
            break;

        case 1:
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
            break;

        default:
            msg_Err(p_aout,"Invalid number of channels");
        goto fail;
    }

    /* Add a quick command line info message */
    msg_Dbg(p_aout, "%d audio channels", ss.channels);

    ss.rate = p_aout->output.output.i_rate;
    if (HAVE_FPU)
    {
        ss.format = PA_SAMPLE_FLOAT32NE;
        p_aout->output.output.i_format = VLC_CODEC_FL32;
    }
    else
    {
        ss.format = PA_SAMPLE_S16NE;
        p_aout->output.output.i_format = VLC_CODEC_S16N;
    }

    if (!pa_sample_spec_valid(&ss)) {
        msg_Err(p_aout,"Invalid sample spec");
        goto fail;
    }

    /* Reduce overall latency to 200mS to reduce audible clicks
     * Also pulse minreq and internal buffers are now 20mS which reduces resampling
     */
    a.tlength = pa_bytes_per_second(&ss)/5;
    a.maxlength = a.tlength * 2;
    a.prebuf = a.tlength / 2;
    a.minreq = a.tlength / 10;

    /* Buffer size is 20mS */
    p_sys->buffer_size = a.minreq;

    /* Initialise the speaker map setup above */
    pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);

    if (!(p_sys->mainloop = pa_threaded_mainloop_new())) {
        msg_Err(p_aout, "Failed to allocate main loop");
        goto fail;
    }

    if (!(p_sys->context = pa_context_new(pa_threaded_mainloop_get_api(p_sys->mainloop), _( PULSE_CLIENT_NAME )))) {
        msg_Err(p_aout, "Failed to allocate context");
        goto fail;
    }

    pa_context_set_state_callback(p_sys->context, context_state_cb, p_aout);

    PULSE_DEBUG( "Pulse before context connect");

    if (pa_context_connect(p_sys->context, NULL, 0, NULL) < 0) {
        msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
        goto fail;
    }

    PULSE_DEBUG( "Pulse after context connect");

    pa_threaded_mainloop_lock(p_sys->mainloop);

    if (pa_threaded_mainloop_start(p_sys->mainloop) < 0) {
        msg_Err(p_aout, "Failed to start main loop");
        goto unlock_and_fail;
    }

    msg_Dbg(p_aout, "Pulse mainloop started");

    /* Wait until the context is ready */
    pa_threaded_mainloop_wait(p_sys->mainloop);

    if (pa_context_get_state(p_sys->context) != PA_CONTEXT_READY) {
        msg_Dbg(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
        goto unlock_and_fail;
    }

    if (!(p_sys->stream = pa_stream_new(p_sys->context, "audio stream", &ss, &map))) {
        msg_Err(p_aout, "Failed to create stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
        goto unlock_and_fail;
    }

    PULSE_DEBUG( "Pulse after new stream");

    pa_stream_set_state_callback(p_sys->stream, stream_state_cb, p_aout);
    pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout);
    pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout);

    if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) {
        msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context)));
        goto unlock_and_fail;
    }

     PULSE_DEBUG("Pulse stream connect");

    /* Wait until the stream is ready */
    pa_threaded_mainloop_wait(p_sys->mainloop);

    msg_Dbg(p_aout,"Pulse stream connected");

    if (pa_stream_get_state(p_sys->stream) != PA_STREAM_READY) {
        msg_Err(p_aout, "Failed to connect to server: %s", pa_strerror(pa_context_errno(p_sys->context)));
        goto unlock_and_fail;
    }


    PULSE_DEBUG("Pulse after stream get status");

    pa_threaded_mainloop_unlock(p_sys->mainloop);

    buffer_attr = pa_stream_get_buffer_attr(p_sys->stream);
    p_aout->output.i_nb_samples = buffer_attr->minreq / pa_frame_size(&ss);
    p_aout->output.pf_play = Play;
    aout_VolumeSoftInit(p_aout);
    msg_Dbg(p_aout, "Pulse initialized successfully");
    {
        char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];

        msg_Dbg(p_aout, "Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u", buffer_attr->maxlength, buffer_attr->tlength, buffer_attr->prebuf, buffer_attr->minreq);
        msg_Dbg(p_aout, "Using sample spec '%s', channel map '%s'.",
                pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(p_sys->stream)),
                pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(p_sys->stream)));

            msg_Dbg(p_aout, "Connected to device %s (%u, %ssuspended).",
                        pa_stream_get_device_name(p_sys->stream),
                        pa_stream_get_device_index(p_sys->stream),
                        pa_stream_is_suspended(p_sys->stream) ? "" : "not ");
    }

    return VLC_SUCCESS;

unlock_and_fail:
    msg_Dbg(p_aout, "Pulse initialization unlock and fail");

    if (p_sys->mainloop)
        pa_threaded_mainloop_unlock(p_sys->mainloop);
fail:
    msg_Dbg(p_aout, "Pulse initialization failed");
    uninit(p_aout);
    return VLC_EGENERIC;
}
static int init(int rate_hz, int channels, int format, int flags) {
    struct pa_sample_spec ss;
    struct pa_channel_map map;
    const struct format_map_s *fmt_map;
    char *devarg = NULL;
    char *host = NULL;
    char *sink = NULL;
    char *version = pa_get_library_version();

    if (ao_subdevice) {
        devarg = strdup(ao_subdevice);
        sink = strchr(devarg, ':');
        if (sink) *sink++ = 0;
        if (devarg[0]) host = devarg;
    }

    broken_pause = 0;
    // not sure which versions are affected, assume 0.9.11* to 0.9.14*
    // known bad: 0.9.14, 0.9.13
    // known good: 0.9.9, 0.9.10, 0.9.15
    // to test: pause, wait ca. 5 seconds framestep and see if MPlayer hangs somewhen
    if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1' && version[5] <= '4') {
        mp_msg(MSGT_AO, MSGL_WARN, "[pulse] working around probably broken pause functionality,\n"
                                   "        see http://www.pulseaudio.org/ticket/440\n");
        broken_pause = 1;
    }

    ss.channels = channels;
    ss.rate = rate_hz;

    ao_data.samplerate = rate_hz;
    ao_data.channels = channels;

    fmt_map = format_maps;
    while (fmt_map->mp_format != format) {
        if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
            mp_msg(MSGT_AO, MSGL_V, "AO: [pulse] Unsupported format, using default\n");
            fmt_map = format_maps;
            break;
        }
        fmt_map++;
    }
    ao_data.format = fmt_map->mp_format;
    ss.format = fmt_map->pa_format;

    if (!pa_sample_spec_valid(&ss)) {
        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
        goto fail;
    }

    pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
    ao_data.bps = pa_bytes_per_second(&ss);

    pa_cvolume_reset(&volume, ss.channels);

    if (!(mainloop = pa_threaded_mainloop_new())) {
        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n");
        goto fail;
    }

    if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), PULSE_CLIENT_NAME))) {
        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n");
        goto fail;
    }

    pa_context_set_state_callback(context, context_state_cb, NULL);

    if (pa_context_connect(context, host, 0, NULL) < 0)
        goto fail;

    pa_threaded_mainloop_lock(mainloop);

    if (pa_threaded_mainloop_start(mainloop) < 0)
        goto unlock_and_fail;

    /* Wait until the context is ready */
    pa_threaded_mainloop_wait(mainloop);

    if (pa_context_get_state(context) != PA_CONTEXT_READY)
        goto unlock_and_fail;

    if (!(stream = pa_stream_new(context, "audio stream", &ss, &map)))
        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);

    if (pa_stream_connect_playback(stream, sink, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, &volume, NULL) < 0)
        goto unlock_and_fail;

    /* Wait until the stream is ready */
    pa_threaded_mainloop_wait(mainloop);

    if (pa_stream_get_state(stream) != PA_STREAM_READY)
        goto unlock_and_fail;

    pa_threaded_mainloop_unlock(mainloop);

    free(devarg);
    return 1;

unlock_and_fail:

    if (mainloop)
        pa_threaded_mainloop_unlock(mainloop);

fail:
    if (context)
        GENERIC_ERR_MSG(context, "Init failed");
    free(devarg);
    uninit(1);
    return 0;
}
예제 #14
0
void SetupSound (void)
{
     int error_number;

     // Acquire mainloop ///////////////////////////////////////////////////////
     device.mainloop = pa_threaded_mainloop_new ();
     if (device.mainloop == NULL)
     {
	  fprintf (stderr, "Could not acquire PulseAudio main loop\n");
	  return;
     }

     // Acquire context ////////////////////////////////////////////////////////
     device.api = pa_threaded_mainloop_get_api (device.mainloop);
     device.context = pa_context_new (device.api, "PCSX");
     pa_context_set_state_callback (device.context, context_state_cb, &device);

     if (device.context == NULL)
     {
	  fprintf (stderr, "Could not acquire PulseAudio device context\n");
	  return;
     }

     // Connect to PulseAudio server ///////////////////////////////////////////
     if (pa_context_connect (device.context, NULL, 0, NULL) < 0)
     {
	  error_number = pa_context_errno (device.context);
	  fprintf (stderr, "Could not connect to PulseAudio server: %s\n", pa_strerror(error_number));
	  return;
     }

     // Run mainloop until sever context is ready //////////////////////////////
     pa_threaded_mainloop_lock (device.mainloop);
     if (pa_threaded_mainloop_start (device.mainloop) < 0)
     {
	  fprintf (stderr, "Could not start mainloop\n");
	  return;
     }

     pa_context_state_t context_state;
     context_state = pa_context_get_state (device.context);
     while (context_state != PA_CONTEXT_READY)
     {
	  context_state = pa_context_get_state (device.context);
	  if (! PA_CONTEXT_IS_GOOD (context_state))
	  {
	       error_number = pa_context_errno (device.context);
	       fprintf (stderr, "Context state is not good: %s\n", pa_strerror (error_number));
	       return;
	  }
	  else if (context_state == PA_CONTEXT_READY)
	       break;
	  else
	       fprintf (stderr, "PulseAudio context state is %d\n", context_state);
	  pa_threaded_mainloop_wait (device.mainloop);
     }

     // Set sample spec ////////////////////////////////////////////////////////
     device.spec.format = PA_SAMPLE_S16NE;
     device.spec.channels = 2;
     device.spec.rate = settings.frequency;

     pa_buffer_attr buffer_attributes;
     buffer_attributes.tlength = pa_bytes_per_second (& device.spec) / 5;
     buffer_attributes.maxlength = buffer_attributes.tlength * 3;
     buffer_attributes.minreq = buffer_attributes.tlength / 3;
     buffer_attributes.prebuf = buffer_attributes.tlength;

     //maxlength = buffer_attributes.maxlength;
     //fprintf (stderr, "Total space: %u\n", buffer_attributes.maxlength);
     //fprintf (stderr, "Minimum request size: %u\n", buffer_attributes.minreq);
     //fprintf (stderr, "Bytes needed before playback: %u\n", buffer_attributes.prebuf);
     //fprintf (stderr, "Target buffer size: %lu\n", buffer_attributes.tlength);

     // Acquire new stream using spec //////////////////////////////////////////
     device.stream = pa_stream_new (device.context, "PCSX", &device.spec, NULL);
     if (device.stream == NULL)
     {
	  error_number = pa_context_errno (device.context);
	  fprintf (stderr, "Could not acquire new PulseAudio stream: %s\n", pa_strerror (error_number));
	  return;
     }

     // Set callbacks for server events ////////////////////////////////////////
     pa_stream_set_state_callback (device.stream, stream_state_cb, &device);
     pa_stream_set_write_callback (device.stream, stream_request_cb, &device);
     pa_stream_set_latency_update_callback (device.stream, stream_latency_update_cb, &device);

     // Ready stream for playback //////////////////////////////////////////////
     pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
     //pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS);
     if (pa_stream_connect_playback (device.stream, NULL, &buffer_attributes, flags, NULL, NULL) < 0)
     {
	  pa_context_errno (device.context);
	  fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number));
	  return;
     }

     // Run mainloop until stream is ready /////////////////////////////////////
     pa_stream_state_t stream_state;
     stream_state = pa_stream_get_state (device.stream);
     while (stream_state != PA_STREAM_READY)
     {
	  stream_state = pa_stream_get_state (device.stream);

	  if (stream_state == PA_STREAM_READY)
	       break;

	  else if (! PA_STREAM_IS_GOOD (stream_state))
	  {
	       error_number = pa_context_errno (device.context);
	       fprintf (stderr, "Stream state is not good: %s\n", pa_strerror (error_number));
	       return;
	  }
	  else
	       fprintf (stderr, "PulseAudio stream state is %d\n", stream_state);
	  pa_threaded_mainloop_wait (device.mainloop);
     }

     pa_threaded_mainloop_unlock (device.mainloop);

     fprintf  (stderr, "PulseAudio should be connected\n");
     return;
}
static DECLCALLBACK(int) drvHostPulseAudioInit(PPDMIHOSTAUDIO pInterface)
{
    NOREF(pInterface);

    LogFlowFuncEnter();

    int rc = audioLoadPulseLib();
    if (RT_FAILURE(rc))
    {
        LogRel(("PulseAudio: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
        return rc;
    }

    bool fLocked = false;

    do
    {
        if (!(g_pMainLoop = pa_threaded_mainloop_new()))
        {
            LogRel(("PulseAudio: Failed to allocate main loop: %s\n",
                     pa_strerror(pa_context_errno(g_pContext))));
            rc = VERR_NO_MEMORY;
            break;
        }

        if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VirtualBox")))
        {
            LogRel(("PulseAudio: Failed to allocate context: %s\n",
                     pa_strerror(pa_context_errno(g_pContext))));
            rc = VERR_NO_MEMORY;
            break;
        }

        if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
        {
            LogRel(("PulseAudio: Failed to start threaded mainloop: %s\n",
                     pa_strerror(pa_context_errno(g_pContext))));
            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
            break;
        }

        pa_context_set_state_callback(g_pContext, drvHostPulseAudioCbCtxState, NULL);
        pa_threaded_mainloop_lock(g_pMainLoop);
        fLocked = true;

        if (pa_context_connect(g_pContext, NULL /* pszServer */,
                               PA_CONTEXT_NOFLAGS, NULL) < 0)
        {
            LogRel(("PulseAudio: Failed to connect to server: %s\n",
                     pa_strerror(pa_context_errno(g_pContext))));
            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
            break;
        }

        /* Wait until the g_pContext is ready */
        for (;;)
        {
            pa_context_state_t cstate;
            pa_threaded_mainloop_wait(g_pMainLoop);

            cstate = pa_context_get_state(g_pContext);
            if (cstate == PA_CONTEXT_READY)
                break;
            else if (   cstate == PA_CONTEXT_TERMINATED
                     || cstate == PA_CONTEXT_FAILED)
            {
                LogRel(("PulseAudio: Failed to initialize context (state %d)\n", cstate));
                rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
                break;
            }
        }

        pa_threaded_mainloop_unlock(g_pMainLoop);
    }
    while (0);

    if (RT_FAILURE(rc))
    {
        if (g_pMainLoop)
        {
            if (fLocked)
                pa_threaded_mainloop_unlock(g_pMainLoop);

            if (g_pMainLoop)
                pa_threaded_mainloop_stop(g_pMainLoop);
        }

        if (g_pContext)
        {
            pa_context_disconnect(g_pContext);
            pa_context_unref(g_pContext);
            g_pContext = NULL;
        }

        if (g_pMainLoop)
        {
            pa_threaded_mainloop_free(g_pMainLoop);
            g_pMainLoop = NULL;
        }
    }

    LogFlowFuncLeaveRC(rc);
    return rc;
}
예제 #16
0
static int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output) {
    // Define our pulse audio loop and connection variables
    pa_mainloop *pa_ml;
    pa_mainloop_api *pa_mlapi;
    pa_operation *pa_op;
    pa_context *pa_ctx;

    // We'll need these state variables to keep track of our requests
    int state = 0;
    int pa_ready = 0;

    // Initialize our device lists
    memset(input, 0, sizeof(pa_devicelist_t) * 16);
    memset(output, 0, sizeof(pa_devicelist_t) * 16);

    // Create a mainloop API and connection to the default server
    pa_ml = pa_mainloop_new();
    pa_mlapi = pa_mainloop_get_api(pa_ml);
    pa_ctx = pa_context_new(pa_mlapi, "test");

    // This function connects to the pulse server
    pa_context_connect(pa_ctx, NULL, (pa_context_flags_t)0, NULL);

    // This function defines a callback so the server will tell us it's state.
    // Our callback will wait for the state to be ready.  The callback will
    // modify the variable to 1 so we know when we have a connection and it's
    // ready.
    // If there's an error, the callback will set pa_ready to 2
    pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);

    // Now we'll enter into an infinite loop until we get the data we receive
    // or if there's an error
    for (;;) {
        // We can't do anything until PA is ready, so just iterate the mainloop
        // and continue
        if (pa_ready == 0) {
            pa_mainloop_iterate(pa_ml, 1, NULL);
            continue;
        }
        // We couldn't get a connection to the server, so exit out
        if (pa_ready == 2) {
            pa_context_disconnect(pa_ctx);
            pa_context_unref(pa_ctx);
            pa_mainloop_free(pa_ml);
            return -1;
        }
        // At this point, we're connected to the server and ready to make
        // requests
        switch (state) {
            // State 0: we haven't done anything yet
            case 0:
                // This sends an operation to the server.  pa_sinklist_info is
                // our callback function and a pointer to our devicelist will
                // be passed to the callback The operation ID is stored in the
                // pa_op variable
                pa_op = pa_context_get_sink_info_list(pa_ctx,
                        pa_sinklist_cb,
                        output
                        );

                // Update state for next iteration through the loop
                state++;
                break;
            case 1:
                // Now we wait for our operation to complete.  When it's
                // complete our pa_output_devicelist is filled out, and we move
                // along to the next state
                if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
                    pa_operation_unref(pa_op);

                    // Now we perform another operation to get the source
                    // (input device) list just like before.  This time we pass
                    // a pointer to our input structure
                    pa_op = pa_context_get_source_info_list(pa_ctx,
                            pa_sourcelist_cb,
                            input
                            );
                    // Update the state so we know what to do next
                    state++;
                }
                break;
            case 2:
                if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
                    // Now we're done, clean up and disconnect and return
                    pa_operation_unref(pa_op);
                    pa_context_disconnect(pa_ctx);
                    pa_context_unref(pa_ctx);
                    pa_mainloop_free(pa_ml);
                    return 0;
                }
                break;
            default:
                // We should never see this state
                fprintf(stderr, "in state %d\n", state);
                return -1;
        }
        // Iterate the main loop and go again.  The second argument is whether
        // or not the iteration should block until something is ready to be
        // done.  Set it to zero for non-blocking.
        pa_mainloop_iterate(pa_ml, 1, NULL);
    }
}
예제 #17
0
/**
 * ags_pulse_client_open:
 * @pulse_client: the #AgsPulseClient
 * @client_name: the client's name
 *
 * Open the pulseaudio client's connection and read uuid.
 *
 * Since: 2.0.0
 */
void
ags_pulse_client_open(AgsPulseClient *pulse_client,
		      gchar *client_name)
{
  AgsPulseServer *pulse_server;
  
#ifdef AGS_WITH_PULSE
  pa_context *context;
  pa_mainloop *main_loop;
  pa_mainloop_api *main_loop_api;
#else
  gpointer context;
  gpointer main_loop;
  gpointer main_loop_api;
#endif

  gchar *client_uuid;

  pthread_mutex_t *pulse_server_mutex;
  pthread_mutex_t *pulse_client_mutex;

  if(!AGS_IS_PULSE_CLIENT(pulse_client) ||
     client_name == NULL){
    return;
  }

  /* get pulse client mutex */
  pthread_mutex_lock(ags_pulse_client_get_class_mutex());
  
  pulse_client_mutex = pulse_client->obj_mutex;
  
  pthread_mutex_unlock(ags_pulse_client_get_class_mutex());

  /* check already open */
  pthread_mutex_lock(pulse_client_mutex);

  if(pulse_client->context != NULL){
    pthread_mutex_unlock(pulse_client_mutex);

    g_message("Advanced Gtk+ Sequencer pulseaudio client already open");
    
    return;
  } 

  pulse_server = (AgsPulseServer *) pulse_client->pulse_server;
  
  pthread_mutex_unlock(pulse_client_mutex);
  
  g_message("Advanced Gtk+ Sequencer open pulseaudio client");
  
  g_object_set(pulse_client,
	       "client-name", client_name,
	       NULL);

  /* get pulse server mutex */
  pthread_mutex_lock(ags_pulse_server_get_class_mutex());
  
  pulse_server_mutex = pulse_server->obj_mutex;
  
  pthread_mutex_unlock(ags_pulse_server_get_class_mutex());

  /* get main loop API */
  pthread_mutex_lock(pulse_server_mutex);

  main_loop = pulse_server->main_loop;
  main_loop_api = pulse_server->main_loop_api;
  
  pthread_mutex_unlock(pulse_server_mutex);

  /* new context */
  pthread_mutex_lock(pulse_client_mutex);

#ifdef AGS_WITH_PULSE
  context = 
    pulse_client->context = pa_context_new(main_loop_api, client_name);
#else
  context = 
    pulse_client->context = NULL;
#endif

  pthread_mutex_unlock(pulse_client_mutex);  

  if(context != NULL){
#ifdef AGS_WITH_PULSE
    pa_context_connect(context,
		       NULL,
		       0,
		       NULL);
    pa_context_set_state_callback(context,
				  ags_pulse_client_state_callback,
				  pulse_client);
    
    while(!ags_pulse_client_test_flags(pulse_client, AGS_PULSE_CLIENT_READY)){
      if(!ags_pulse_client_test_flags(pulse_client, AGS_PULSE_CLIENT_READY)){
	pa_mainloop_iterate(main_loop,
			    TRUE,
			    NULL);
      }
    }
#endif
  }
}
예제 #18
0
파일: pacat.c 프로젝트: zonque/pulseaudio
int main(int argc, char *argv[]) {
    pa_mainloop* m = NULL;
    int ret = 1, c;
    char *bn, *server = NULL;
    pa_time_event *time_event = NULL;
    const char *filename = NULL;
    /* type for pa_read/_write. passed as userdata to the callbacks */
    unsigned long type = 0;

    static const struct option long_options[] = {
        {"record",       0, NULL, 'r'},
        {"playback",     0, NULL, 'p'},
        {"device",       1, NULL, 'd'},
        {"server",       1, NULL, 's'},
        {"client-name",  1, NULL, 'n'},
        {"stream-name",  1, NULL, ARG_STREAM_NAME},
        {"version",      0, NULL, ARG_VERSION},
        {"help",         0, NULL, 'h'},
        {"verbose",      0, NULL, 'v'},
        {"volume",       1, NULL, ARG_VOLUME},
        {"rate",         1, NULL, ARG_SAMPLERATE},
        {"format",       1, NULL, ARG_SAMPLEFORMAT},
        {"channels",     1, NULL, ARG_CHANNELS},
        {"channel-map",  1, NULL, ARG_CHANNELMAP},
        {"fix-format",   0, NULL, ARG_FIX_FORMAT},
        {"fix-rate",     0, NULL, ARG_FIX_RATE},
        {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
        {"no-remap",     0, NULL, ARG_NO_REMAP},
        {"no-remix",     0, NULL, ARG_NO_REMIX},
        {"latency",      1, NULL, ARG_LATENCY},
        {"process-time", 1, NULL, ARG_PROCESS_TIME},
        {"property",     1, NULL, ARG_PROPERTY},
        {"raw",          0, NULL, ARG_RAW},
        {"passthrough",  0, NULL, ARG_PASSTHROUGH},
        {"file-format",  2, NULL, ARG_FILE_FORMAT},
        {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
        {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
        {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
        {"monitor-stream", 1, NULL, ARG_MONITOR_STREAM},
        {NULL,           0, NULL, 0}
    };

    setlocale(LC_ALL, "");
#ifdef ENABLE_NLS
    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
#endif

    bn = pa_path_get_filename(argv[0]);

    if (strstr(bn, "play")) {
        mode = PLAYBACK;
        raw = false;
    } else if (strstr(bn, "record")) {
        mode = RECORD;
        raw = false;
    } else if (strstr(bn, "cat")) {
        mode = PLAYBACK;
        raw = true;
    } else if (strstr(bn, "rec") || strstr(bn, "mon")) {
        mode = RECORD;
        raw = true;
    }

    proplist = pa_proplist_new();

    while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {

        switch (c) {
            case 'h' :
                help(bn);
                ret = 0;
                goto quit;

            case ARG_VERSION:
                printf(_("pacat %s\n"
                         "Compiled with libpulse %s\n"
                         "Linked with libpulse %s\n"),
                       PACKAGE_VERSION,
                       pa_get_headers_version(),
                       pa_get_library_version());
                ret = 0;
                goto quit;

            case 'r':
                mode = RECORD;
                break;

            case 'p':
                mode = PLAYBACK;
                break;

            case 'd':
                pa_xfree(device);
                device = pa_xstrdup(optarg);
                break;

            case 's':
                pa_xfree(server);
                server = pa_xstrdup(optarg);
                break;

            case 'n': {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {

                    pa_log(_("Invalid client name '%s'"), t ? t : optarg);
                    pa_xfree(t);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case ARG_STREAM_NAME: {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {

                    pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
                    pa_xfree(t);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case 'v':
                verbose = 1;
                break;

            case ARG_VOLUME: {
                int v = atoi(optarg);
                volume = v < 0 ? 0U : (pa_volume_t) v;
                volume_is_set = true;
                break;
            }

            case ARG_CHANNELS:
                sample_spec.channels = (uint8_t) atoi(optarg);
                sample_spec_set = true;
                break;

            case ARG_SAMPLEFORMAT:
                sample_spec.format = pa_parse_sample_format(optarg);
                sample_spec_set = true;
                break;

            case ARG_SAMPLERATE:
                sample_spec.rate = (uint32_t) atoi(optarg);
                sample_spec_set = true;
                break;

            case ARG_CHANNELMAP:
                if (!pa_channel_map_parse(&channel_map, optarg)) {
                    pa_log(_("Invalid channel map '%s'"), optarg);
                    goto quit;
                }

                channel_map_set = true;
                break;

            case ARG_FIX_CHANNELS:
                flags |= PA_STREAM_FIX_CHANNELS;
                break;

            case ARG_FIX_RATE:
                flags |= PA_STREAM_FIX_RATE;
                break;

            case ARG_FIX_FORMAT:
                flags |= PA_STREAM_FIX_FORMAT;
                break;

            case ARG_NO_REMIX:
                flags |= PA_STREAM_NO_REMIX_CHANNELS;
                break;

            case ARG_NO_REMAP:
                flags |= PA_STREAM_NO_REMAP_CHANNELS;
                break;

            case ARG_LATENCY:
                if (((latency = (size_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid latency specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROCESS_TIME:
                if (((process_time = (size_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid process time specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_LATENCY_MSEC:
                if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid latency specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROCESS_TIME_MSEC:
                if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid process time specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROPERTY: {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_setp(proplist, t) < 0) {

                    pa_xfree(t);
                    pa_log(_("Invalid property '%s'"), optarg);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case ARG_RAW:
                raw = true;
                break;

            case ARG_PASSTHROUGH:
                flags |= PA_STREAM_PASSTHROUGH;
                break;

            case ARG_FILE_FORMAT:
                if (optarg) {
                    if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
                        pa_log(_("Unknown file format %s."), optarg);
                        goto quit;
                    }
                }

                raw = false;
                break;

            case ARG_LIST_FILE_FORMATS:
                pa_sndfile_dump_formats();
                ret = 0;
                goto quit;

            case ARG_MONITOR_STREAM:
                if (pa_atou(optarg, &monitor_stream) < 0) {
                    pa_log(_("Failed to parse the argument for --monitor-stream"));
                    goto quit;
                }
                break;

            default:
                goto quit;
        }
    }

    if (!pa_sample_spec_valid(&sample_spec)) {
        pa_log(_("Invalid sample specification"));
        goto quit;
    }

    if (optind+1 == argc) {
        int fd;

        filename = argv[optind];

        if ((fd = pa_open_cloexec(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
            pa_log(_("open(): %s"), strerror(errno));
            goto quit;
        }

        if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
            pa_log(_("dup2(): %s"), strerror(errno));
            goto quit;
        }

        pa_close(fd);

    } else if (optind+1 <= argc) {
        pa_log(_("Too many arguments."));
        goto quit;
    }

    if (!raw) {
        SF_INFO sfi;
        pa_zero(sfi);

        if (mode == RECORD) {
            /* This might patch up the sample spec */
            if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
                pa_log(_("Failed to generate sample specification for file."));
                goto quit;
            }

            if (file_format <= 0) {
                char *extension;
                if (filename && (extension = strrchr(filename, '.')))
                    file_format = pa_sndfile_format_from_string(extension+1);
                if (file_format <= 0)
                    file_format = SF_FORMAT_WAV;
                /* Transparently upgrade classic .wav to wavex for multichannel audio */
                if (file_format == SF_FORMAT_WAV &&
                    (sample_spec.channels > 2 ||
                    (channel_map_set &&
                    !(sample_spec.channels == 1 && channel_map.map[0] == PA_CHANNEL_POSITION_MONO) &&
                    !(sample_spec.channels == 2 && channel_map.map[0] == PA_CHANNEL_POSITION_LEFT
                                                && channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))))
                    file_format = SF_FORMAT_WAVEX;
            }

            sfi.format |= file_format;
        }

        if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
                                   mode == RECORD ? SFM_WRITE : SFM_READ,
                                   &sfi, 0))) {
            pa_log(_("Failed to open audio file."));
            goto quit;
        }

        if (mode == PLAYBACK) {
            if (sample_spec_set)
                pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));

            if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
                pa_log(_("Failed to determine sample specification from file."));
                goto quit;
            }
            sample_spec_set = true;

            if (!channel_map_set) {
                /* Allow the user to overwrite the channel map on the command line */
                if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
                    if (sample_spec.channels > 2)
                        pa_log(_("Warning: Failed to determine channel map from file."));
                } else
                    channel_map_set = true;
            }
        }
    }

    if (!channel_map_set)
        pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);

    if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
        pa_log(_("Channel map doesn't match sample specification"));
        goto quit;
    }

    if (!raw) {
        pa_proplist *sfp;

        if (mode == PLAYBACK)
            readf_function = pa_sndfile_readf_function(&sample_spec);
        else {
            if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
                pa_log(_("Warning: failed to write channel map to file."));

            writef_function = pa_sndfile_writef_function(&sample_spec);
        }

        /* Fill in libsndfile prop list data */
        sfp = pa_proplist_new();
        pa_sndfile_init_proplist(sndfile, sfp);
        pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
        pa_proplist_free(sfp);
    }

    if (verbose) {
        char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];

        pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
                mode == RECORD ? _("recording") : _("playback"),
                pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
                pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
    }

    /* Fill in client name if none was set */
    if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
        char *t;

        if ((t = pa_locale_to_utf8(bn))) {
            pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
            pa_xfree(t);
        }
    }

    /* Fill in media name if none was set */
    if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
        const char *t;

        if ((t = filename) ||
            (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
            pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);

        if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
            pa_log(_("Failed to set media name."));
            goto quit;
        }
    }

    /* Set up a new main loop */
    if (!(m = pa_mainloop_new())) {
        pa_log(_("pa_mainloop_new() failed."));
        goto quit;
    }

    mainloop_api = pa_mainloop_get_api(m);

    pa_assert_se(pa_signal_init(mainloop_api) == 0);
    pa_signal_new(SIGINT, exit_signal_callback, NULL);
    pa_signal_new(SIGTERM, exit_signal_callback, NULL);
#ifdef SIGUSR1
    pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
#endif
    pa_disable_sigpipe();

    if (raw) {
#ifdef OS_IS_WIN32
        /* need to turn on binary mode for stdio io. Windows, meh */
        setmode(mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, O_BINARY);
#endif
        if (!(stdio_event = mainloop_api->io_new(mainloop_api,
                                                 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
                                                 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
                                                 mode == PLAYBACK ? stdin_callback : stdout_callback, &type))) {
            pa_log(_("io_new() failed."));
            goto quit;
        }
    }

    /* Create a new connection context */
    if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
        pa_log(_("pa_context_new() failed."));
        goto quit;
    }

    pa_context_set_state_callback(context, context_state_callback, NULL);

    /* Connect the context */
    if (pa_context_connect(context, server, 0, NULL) < 0) {
        pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
        goto quit;
    }

    if (verbose) {
        if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
            pa_log(_("pa_context_rttime_new() failed."));
            goto quit;
        }
    }

    /* Run the main loop */
    if (pa_mainloop_run(m, &ret) < 0) {
        pa_log(_("pa_mainloop_run() failed."));
        goto quit;
    }

quit:
    if (stream)
        pa_stream_unref(stream);

    if (context)
        pa_context_unref(context);

    if (stdio_event) {
        pa_assert(mainloop_api);
        mainloop_api->io_free(stdio_event);
    }

    if (time_event) {
        pa_assert(mainloop_api);
        mainloop_api->time_free(time_event);
    }

    if (m) {
        pa_signal_done();
        pa_mainloop_free(m);
    }

    pa_xfree(silence_buffer);
    pa_xfree(buffer);

    pa_xfree(server);
    pa_xfree(device);

    if (sndfile)
        sf_close(sndfile);

    if (proplist)
        pa_proplist_free(proplist);

    return ret;
}
예제 #19
0
static DECLCALLBACK(int) drvHostPulseAudioInit(PPDMIHOSTAUDIO pInterface)
{
    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);

    PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);

    LogFlowFuncEnter();

    int rc = audioLoadPulseLib();
    if (RT_FAILURE(rc))
    {
        LogRel(("PulseAudio: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
        return rc;
    }

    pThis->fLoopWait = false;
    pThis->pMainLoop = NULL;

    bool fLocked = false;

    do
    {
        if (!(pThis->pMainLoop = pa_threaded_mainloop_new()))
        {
            LogRel(("PulseAudio: Failed to allocate main loop: %s\n",
                     pa_strerror(pa_context_errno(pThis->pContext))));
            rc = VERR_NO_MEMORY;
            break;
        }

        if (!(pThis->pContext = pa_context_new(pa_threaded_mainloop_get_api(pThis->pMainLoop), "VirtualBox")))
        {
            LogRel(("PulseAudio: Failed to allocate context: %s\n",
                     pa_strerror(pa_context_errno(pThis->pContext))));
            rc = VERR_NO_MEMORY;
            break;
        }

        if (pa_threaded_mainloop_start(pThis->pMainLoop) < 0)
        {
            LogRel(("PulseAudio: Failed to start threaded mainloop: %s\n",
                     pa_strerror(pa_context_errno(pThis->pContext))));
            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
            break;
        }

        /* Install a global callback to known if something happens to our acquired context. */
        pa_context_set_state_callback(pThis->pContext, paContextCbStateChanged, pThis /* pvUserData */);

        pa_threaded_mainloop_lock(pThis->pMainLoop);
        fLocked = true;

        if (pa_context_connect(pThis->pContext, NULL /* pszServer */,
                               PA_CONTEXT_NOFLAGS, NULL) < 0)
        {
            LogRel(("PulseAudio: Failed to connect to server: %s\n",
                     pa_strerror(pa_context_errno(pThis->pContext))));
            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
            break;
        }

        /* Wait until the pThis->pContext is ready. */
        for (;;)
        {
            if (!pThis->fLoopWait)
                pa_threaded_mainloop_wait(pThis->pMainLoop);
            pThis->fLoopWait = false;

            pa_context_state_t cstate = pa_context_get_state(pThis->pContext);
            if (cstate == PA_CONTEXT_READY)
                break;
            else if (   cstate == PA_CONTEXT_TERMINATED
                     || cstate == PA_CONTEXT_FAILED)
            {
                LogRel(("PulseAudio: Failed to initialize context (state %d)\n", cstate));
                rc = VERR_AUDIO_BACKEND_INIT_FAILED;
                break;
            }
        }
    }
    while (0);

    if (fLocked)
        pa_threaded_mainloop_unlock(pThis->pMainLoop);

    if (RT_FAILURE(rc))
    {
        if (pThis->pMainLoop)
            pa_threaded_mainloop_stop(pThis->pMainLoop);

        if (pThis->pContext)
        {
            pa_context_disconnect(pThis->pContext);
            pa_context_unref(pThis->pContext);
            pThis->pContext = NULL;
        }

        if (pThis->pMainLoop)
        {
            pa_threaded_mainloop_free(pThis->pMainLoop);
            pThis->pMainLoop = NULL;
        }
    }

    LogFlowFuncLeaveRC(rc);
    return rc;
}
예제 #20
0
/* This is called whenever the context status changes */
static void context_state_callback(pa_context *c, void *userdata) {
    fail_unless(c != NULL);

    switch (pa_context_get_state(c)) {
        case PA_CONTEXT_CONNECTING:
        case PA_CONTEXT_AUTHORIZING:
        case PA_CONTEXT_SETTING_NAME:
            break;

        case PA_CONTEXT_READY: {
            pa_stream_flags_t flags = PA_STREAM_AUTO_TIMING_UPDATE;
            pa_buffer_attr attr;
            static const pa_sample_spec ss = {
                .format = PA_SAMPLE_S16LE,
                .rate = 44100,
                .channels = 2
            };

            pa_zero(attr);
            attr.maxlength = (uint32_t) -1;
            attr.tlength = latency > 0 ? (uint32_t) pa_usec_to_bytes(latency, &ss) : (uint32_t) -1;
            attr.prebuf = (uint32_t) -1;
            attr.minreq = (uint32_t) -1;
            attr.fragsize = (uint32_t) -1;

#ifdef INTERPOLATE
            flags |= PA_STREAM_INTERPOLATE_TIMING;
#endif

            if (latency > 0)
                flags |= PA_STREAM_ADJUST_LATENCY;

            pa_log("Connection established");

            stream = pa_stream_new(c, "interpol-test", &ss, NULL);
            fail_unless(stream != NULL);

            if (playback) {
                pa_assert_se(pa_stream_connect_playback(stream, NULL, &attr, flags, NULL, NULL) == 0);
                pa_stream_set_write_callback(stream, stream_write_cb, NULL);
            } else {
                pa_assert_se(pa_stream_connect_record(stream, NULL, &attr, flags) == 0);
                pa_stream_set_read_callback(stream, stream_read_cb, NULL);
            }

            pa_stream_set_latency_update_callback(stream, stream_latency_cb, NULL);

            break;
        }

        case PA_CONTEXT_TERMINATED:
            break;

        case PA_CONTEXT_FAILED:
        default:
            pa_log_error("Context error: %s", pa_strerror(pa_context_errno(c)));
            ck_abort();
    }
}

START_TEST (interpol_test) {
    pa_threaded_mainloop* m = NULL;
    int k;
    struct timeval start, last_info = { 0, 0 };
    pa_usec_t old_t = 0, old_rtc = 0;
#ifdef CORK
    bool corked = false;
#endif

    /* Set up a new main loop */
    m = pa_threaded_mainloop_new();
    fail_unless(m != NULL);
    mainloop_api = pa_threaded_mainloop_get_api(m);
    fail_unless(mainloop_api != NULL);
    context = pa_context_new(mainloop_api, bname);
    fail_unless(context != NULL);

    pa_context_set_state_callback(context, context_state_callback, NULL);

    fail_unless(pa_context_connect(context, NULL, 0, NULL) >= 0);

    pa_gettimeofday(&start);

    fail_unless(pa_threaded_mainloop_start(m) >= 0);

/* #ifdef CORK */
    for (k = 0; k < 20000; k++)
/* #else */
/*     for (k = 0; k < 2000; k++) */
/* #endif */
    {
        bool success = false, changed = false;
        pa_usec_t t, rtc, d;
        struct timeval now, tv;
        bool playing = false;

        pa_threaded_mainloop_lock(m);

        if (stream) {
            const pa_timing_info *info;

            if (pa_stream_get_time(stream, &t) >= 0 &&
                pa_stream_get_latency(stream, &d, NULL) >= 0)
                success = true;

            if ((info = pa_stream_get_timing_info(stream))) {
                if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) {
                    changed = true;
                    last_info = info->timestamp;
                }
                if (info->playing)
                    playing = true;
            }
        }

        pa_threaded_mainloop_unlock(m);

        pa_gettimeofday(&now);

        if (success) {
#ifdef CORK
            bool cork_now;
#endif
            rtc = pa_timeval_diff(&now, &start);
            pa_log_info("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\t%llu\t%llu\n", k,
                   (unsigned long long) rtc,
                   (unsigned long long) t,
                   (unsigned long long) (rtc-old_rtc),
                   (unsigned long long) (t-old_t),
                   (signed long long) rtc - (signed long long) t,
                   changed,
                   playing,
                   (unsigned long long) latency,
                   (unsigned long long) d);

            fflush(stdout);
            old_t = t;
            old_rtc = rtc;

#ifdef CORK
            cork_now = (rtc / (2*PA_USEC_PER_SEC)) % 2 == 1;

            if (corked != cork_now) {
                pa_threaded_mainloop_lock(m);
                pa_operation_unref(pa_stream_cork(stream, cork_now, NULL, NULL));
                pa_threaded_mainloop_unlock(m);

                pa_log(cork_now ? "Corking" : "Uncorking");

                corked = cork_now;
            }
#endif
        }

        /* Spin loop, ugly but normal usleep() is just too badly grained */
        tv = now;
        while (pa_timeval_diff(pa_gettimeofday(&now), &tv) < 1000)
            pa_thread_yield();
    }

    if (m)
        pa_threaded_mainloop_stop(m);

    if (stream) {
        pa_stream_disconnect(stream);
        pa_stream_unref(stream);
    }

    if (context) {
        pa_context_disconnect(context);
        pa_context_unref(context);
    }

    if (m)
        pa_threaded_mainloop_free(m);
}
END_TEST

int main(int argc, char *argv[]) {
    int failed = 0;
    Suite *s;
    TCase *tc;
    SRunner *sr;

    if (!getenv("MAKE_CHECK"))
        pa_log_set_level(PA_LOG_DEBUG);

    bname = argv[0];
    playback = argc <= 1 || !pa_streq(argv[1], "-r");
    latency = (argc >= 2 && !pa_streq(argv[1], "-r")) ? atoi(argv[1]) : (argc >= 3 ? atoi(argv[2]) : 0);

    s = suite_create("Interpol");
    tc = tcase_create("interpol");
    tcase_add_test(tc, interpol_test);
    tcase_set_timeout(tc, 5 * 60);
    suite_add_tcase(s, tc);

    sr = srunner_create(s);
    srunner_run_all(sr, CK_NORMAL);
    failed = srunner_ntests_failed(sr);
    srunner_free(sr);

    return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
예제 #21
0
int
FreeRDPRdpsndDeviceEntry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
{
	rdpsndDevicePlugin * devplugin;
	struct pulse_device_data * pulse_data;
	RD_PLUGIN_DATA * data;
	int i;

	devplugin = pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->plugin);
	if (devplugin == NULL)
	{
		LLOGLN(0, ("rdpsnd_pulse: unable to register device."));
		return 1;
	}

	devplugin->open = rdpsnd_pulse_open;
	devplugin->format_supported = rdpsnd_pulse_format_supported;
	devplugin->set_format = rdpsnd_pulse_set_format;
	devplugin->set_volume = rdpsnd_pulse_set_volume;
	devplugin->play = rdpsnd_pulse_play;
	devplugin->close = rdpsnd_pulse_close;
	devplugin->free = rdpsnd_pulse_free;

	pulse_data = (struct pulse_device_data *) malloc(sizeof(struct pulse_device_data));
	memset(pulse_data, 0, sizeof(struct pulse_device_data));

	data = (RD_PLUGIN_DATA *) pEntryPoints->data;
	if (data && strcmp(data->data[0], "pulse") == 0)
	{
		for (i = 1; i < 4 && data->data[i]; i++)
		{
			if (i > 1)
			{
				strncat(pulse_data->device_name, ":",
					sizeof(pulse_data->device_name) - strlen(pulse_data->device_name));
			}
			strncat(pulse_data->device_name, (char*)data->data[i],
				sizeof(pulse_data->device_name) - strlen(pulse_data->device_name));
		}
	}
	pulse_data->pDecodeImaAdpcm = pEntryPoints->pDecodeImaAdpcm;
	devplugin->device_data = pulse_data;

	pulse_data->mainloop = pa_threaded_mainloop_new();
	if (!pulse_data->mainloop)
	{
		LLOGLN(0, ("rdpsnd_pulse: pa_threaded_mainloop_new failed"));
		rdpsnd_pulse_free(devplugin);
		return 1;
	}
	pulse_data->context = pa_context_new(pa_threaded_mainloop_get_api(pulse_data->mainloop), "freerdp");
	if (!pulse_data->context)
	{
		LLOGLN(0, ("rdpsnd_pulse: pa_context_new failed"));
		rdpsnd_pulse_free(devplugin);
		return 1;
	}
	pa_context_set_state_callback(pulse_data->context, rdpsnd_pulse_context_state_callback, devplugin);
	if (rdpsnd_pulse_connect(devplugin))
	{
		LLOGLN(0, ("rdpsnd_pulse: rdpsnd_pulse_connect failed"));
		rdpsnd_pulse_free(devplugin);
		return 1;
	}

	LLOGLN(0, ("rdpsnd_pulse: pulse device '%s' registered.", pulse_data->device_name));

	return 0;
}
예제 #22
0
int main(int argc, char **argv)
{
	pa_glib_mainloop *mainloop;
	pa_mainloop_api *mainloop_api;
	pa_context *context;
	GdkWindow *root;
	int status = EXIT_SUCCESS;
	int i;

	gtk_init(&argc, &argv);

	mainloop = pa_glib_mainloop_new(NULL);
	if (!mainloop) {
		fprintf(stderr, "pa_glib_mainloop_new() failed\n");
		status = EXIT_FAILURE;
		goto out;
	}

	mainloop_api = pa_glib_mainloop_get_api(mainloop);

	if (pa_signal_init(mainloop_api) < 0) {
		fprintf(stderr, "pa_signal_init() failed\n");
		status = EXIT_FAILURE;
		goto mainloop_free;
	}
	pa_signal_new(SIGINT, exit_signal_callback, NULL);
	pa_signal_new(SIGTERM, exit_signal_callback, NULL);

	context = pa_context_new(mainloop_api, NULL);
	if (!context) {
		fprintf(stderr, "pa_context_new() failed\n");
		status = EXIT_FAILURE;
		goto mainloop_free;
	}

	pa_context_set_state_callback(context, context_state_callback, NULL);
	if (pa_context_connect(context, NULL, 0, NULL) < 0) {
		fprintf(stderr, "pa_context_connect() failed: %s\n",
			pa_strerror(pa_context_errno(context)));
		status = EXIT_FAILURE;
		goto context_unref;
	}

	if (!notify_init(APP_NAME)) {
		fprintf(stderr, "Could not initialize libnotify\n");
		status = EXIT_FAILURE;
		goto context_unref;
	}

	notification = notify_notification_new(APP_NAME, NULL, NULL);
	if (!notification) {
		fprintf(stderr, "notify_notification_new() failed\n");
		status = EXIT_FAILURE;
		goto notify_uninit;
	}

	notify_notification_set_timeout(notification, NOTIFY_EXPIRES_DEFAULT);
	notify_notification_set_hint_string(notification, "synchronous", "volume");

	root = gdk_get_default_root_window();

	gdk_window_set_events(root, GDK_KEY_PRESS_MASK);
	gdk_window_add_filter(root, filter, context);

	for (i = 0; i < sizeof(keysyms) / sizeof(keysyms[0]); i++) {
		keycodes[i] = XKeysymToKeycode(GDK_WINDOW_XDISPLAY(root), keysyms[i]);
		if (!keycodes[i]) {
			fprintf(stderr, "%s is not mapped on this keyboard\n",
				XKeysymToString(keysyms[i]));
			continue;
		}
		XGrabKey(GDK_WINDOW_XDISPLAY(root), keycodes[i], AnyModifier,
			 GDK_WINDOW_XID(root), False, GrabModeAsync,
			 GrabModeAsync);
	}

	gtk_main();

	g_object_unref(G_OBJECT(notification));
notify_uninit:
	notify_uninit();
context_unref:
	pa_context_unref(context);
mainloop_free:
	pa_glib_mainloop_free(mainloop);
out:
	return status;
}
예제 #23
0
void PulseAudioPlayer::OpenStream()
{
	if (open) CloseStream();

	// Initialise a mainloop
	//printf("Initialising threaded main loop\n");
	mainloop = pa_threaded_mainloop_new();
	if (!mainloop) {
		throw agi::AudioPlayerOpenError("Failed to initialise PulseAudio threaded mainloop object", 0);
	}
	//printf("Starting main loop\n");
	pa_threaded_mainloop_start(mainloop);

	// Create context
	//printf("Creating context\n");
	context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub");
	if (!context) {
		pa_threaded_mainloop_free(mainloop);
		throw agi::AudioPlayerOpenError("Failed to create PulseAudio context", 0);
	}
	pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this);

	// Connect the context
	//printf("Connecting context\n");
	pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
	// Wait for connection
	while (true) {
		context_notify.Wait();
		if (cstate == PA_CONTEXT_READY) {
			break;
		} else if (cstate == PA_CONTEXT_FAILED) {
			// eww
			paerror = pa_context_errno(context);
			pa_context_unref(context);
			pa_threaded_mainloop_stop(mainloop);
			pa_threaded_mainloop_free(mainloop);
			throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0);
		}
		// otherwise loop once more
	}
	//printf("Context connected\n");

	// Set up stream
	bpf = provider->GetChannels() * provider->GetBytesPerSample();
	pa_sample_spec ss;
	ss.format = PA_SAMPLE_S16LE; // FIXME
	ss.rate = provider->GetSampleRate();
	ss.channels = provider->GetChannels();
	pa_channel_map map;
	pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
	//printf("Creating stream\n");
	stream = pa_stream_new(context, "Sound", &ss, &map);
	if (!stream) {
		// argh!
		pa_context_disconnect(context);
		pa_context_unref(context);
		pa_threaded_mainloop_stop(mainloop);
		pa_threaded_mainloop_free(mainloop);
		throw agi::AudioPlayerOpenError("PulseAudio could not create stream", 0);
	}
	pa_stream_set_state_callback(stream, (pa_stream_notify_cb_t)pa_stream_notify, this);
	pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this);

	// Connect stream
	//printf("Connecting playback stream\n");
	paerror = pa_stream_connect_playback(stream, NULL, NULL, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_NOT_MONOTONOUS|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL);
	if (paerror) {
		printf("PulseAudio reported error: %s (%d)\n", pa_strerror(paerror), paerror);
		throw agi::AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror), 0);
	}
	while (true) {
		stream_notify.Wait();
		if (sstate == PA_STREAM_READY) {
			break;
		} else if (sstate == PA_STREAM_FAILED) {
			paerror = pa_context_errno(context);
			printf("PulseAudio player: Stream connection failed: %s (%d)\n", pa_strerror(paerror), paerror);
			throw agi::AudioPlayerOpenError("PulseAudio player: Something went wrong connecting the stream", 0);
		}
	}
	//printf("Connected playback stream, now playing\n\n");

	// Hopefully this marks success
	//printf("Finished opening PulseAudio\n\n");
	open = true;
}
예제 #24
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;
  }
}
int main(int argc, char **argv) {
    pa_proplist *proplist = NULL;
    pa_mainloop *m = NULL;
    pa_mainloop_api *api = NULL;
    pa_context *context = NULL;
    struct context *ctx;
    struct audio_file *file;
    int ret;

    ctx = malloc(sizeof(struct context));
    if (!ctx) {
        errorp("Couldn't allocate async callbacks context");
        goto quit;
    }

    memset(ctx, 0, sizeof(*ctx));

    file = audio_file_new("samples/sample.wav");
    if (!file)
        goto quit;

    proplist = pa_proplist_new();
    if (!proplist) {
        error("Couldn't create a PulseAudio property list");
        goto quit;
    }

    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "malicious-client-kill-server");

    m = pa_mainloop_new();
    if (!m) {
        error("Couldn't create PulseAudio mainloop");
        goto quit;
    }

    api = pa_mainloop_get_api(m);
    context = pa_context_new_with_proplist(api, NULL, proplist);
    if (!context) {
        error("Couldn't create client context");
        goto quit;
    }

    ctx->file = file;
    ctx->mainloop_api = api;
    ctx->context = context;
    pa_context_set_state_callback(context, context_state_callback, ctx);

    ret = pa_context_connect(context, NULL, 0, NULL);
    if (ret < 0) {
        error ("Couldn't connect to PulseAudio server: %s",
               pa_strerror(pa_context_errno(context)));
        goto quit;
    }

    pa_mainloop_run(m, &ret);

    return ret;

 quit:
     exit(EXIT_FAILURE);
}
예제 #26
0
파일: ao_pulse.c 프로젝트: andre-d/mpv
static int init(struct ao *ao)
{
    struct pa_sample_spec ss;
    struct pa_channel_map map;
    pa_proplist *proplist = NULL;
    struct priv *priv = ao->priv;
    char *host = priv->cfg_host && priv->cfg_host[0] ? priv->cfg_host : NULL;
    char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : NULL;
    const char *version = pa_get_library_version();

    ao->per_application_mixer = true;

    priv->broken_pause = false;
    /* not sure which versions are affected, assume 0.9.11* to 0.9.14*
     * known bad: 0.9.14, 0.9.13
     * known good: 0.9.9, 0.9.10, 0.9.15
     * To test: pause, wait ca. 5 seconds, framestep and see if MPlayer
     * hangs somewhen. */
    if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1'
        && version[5] <= '4') {
        MP_WARN(ao, "working around probably broken pause functionality,\n"
                    "        see http://www.pulseaudio.org/ticket/440\n");
        priv->broken_pause = true;
    }

    if (!(priv->mainloop = pa_threaded_mainloop_new())) {
        MP_ERR(ao, "Failed to allocate main loop\n");
        goto fail;
    }

    if (!(priv->context = pa_context_new(pa_threaded_mainloop_get_api(
                                 priv->mainloop), PULSE_CLIENT_NAME))) {
        MP_ERR(ao, "Failed to allocate context\n");
        goto fail;
    }

    pa_context_set_state_callback(priv->context, context_state_cb, ao);

    if (pa_context_connect(priv->context, host, 0, NULL) < 0)
        goto fail;

    pa_threaded_mainloop_lock(priv->mainloop);

    if (pa_threaded_mainloop_start(priv->mainloop) < 0)
        goto unlock_and_fail;

    /* Wait until the context is ready */
    pa_threaded_mainloop_wait(priv->mainloop);

    if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
        goto unlock_and_fail;

    ss.channels = ao->channels.num;
    ss.rate = ao->samplerate;

    ao->format = af_fmt_from_planar(ao->format);

    const struct format_map *fmt_map = format_maps;
    while (fmt_map->mp_format != ao->format) {
        if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
            MP_VERBOSE(ao, "Unsupported format, using default\n");
            fmt_map = format_maps;
            break;
        }
        fmt_map++;
    }
    ao->format = fmt_map->mp_format;
    ss.format = fmt_map->pa_format;

    if (!pa_sample_spec_valid(&ss)) {
        MP_ERR(ao, "Invalid sample spec\n");
        goto unlock_and_fail;
    }

    if (!select_chmap(ao, &map))
        goto unlock_and_fail;

    if (!(proplist = pa_proplist_new())) {
        MP_ERR(ao, "Failed to allocate proplist\n");
        goto unlock_and_fail;
    }
    (void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");

    if (!(priv->stream = pa_stream_new_with_proplist(priv->context,
                                                     "audio stream", &ss,
                                                     &map, proplist)))
        goto unlock_and_fail;

    pa_proplist_free(proplist);
    proplist = NULL;

    pa_stream_set_state_callback(priv->stream, stream_state_cb, ao);
    pa_stream_set_write_callback(priv->stream, stream_request_cb, ao);
    pa_stream_set_latency_update_callback(priv->stream,
                                          stream_latency_update_cb, ao);
    pa_buffer_attr bufattr = {
        .maxlength = -1,
        .tlength = pa_usec_to_bytes(1000000, &ss),
        .prebuf = -1,
        .minreq = -1,
        .fragsize = -1,
    };
    if (pa_stream_connect_playback(priv->stream, sink, &bufattr,
                                   PA_STREAM_NOT_MONOTONIC, NULL, NULL) < 0)
        goto unlock_and_fail;

    /* Wait until the stream is ready */
    pa_threaded_mainloop_wait(priv->mainloop);

    if (pa_stream_get_state(priv->stream) != PA_STREAM_READY)
        goto unlock_and_fail;

    pa_threaded_mainloop_unlock(priv->mainloop);

    return 0;

unlock_and_fail:

    if (priv->mainloop)
        pa_threaded_mainloop_unlock(priv->mainloop);

fail:
    if (priv->context) {
        if (!(pa_context_errno(priv->context) == PA_ERR_CONNECTIONREFUSED
              && ao->probing))
            GENERIC_ERR_MSG("Init failed");
    }

    if (proplist)
        pa_proplist_free(proplist);

    uninit(ao, true);
    return -1;
}

static void cork(struct ao *ao, bool pause)
{
    struct priv *priv = ao->priv;
    pa_threaded_mainloop_lock(priv->mainloop);
    priv->retval = 0;
    if (!waitop(priv, pa_stream_cork(priv->stream, pause, success_cb, ao)) ||
        !priv->retval)
        GENERIC_ERR_MSG("pa_stream_cork() failed");
}

// Play the specified data to the pulseaudio server
static int play(struct ao *ao, void **data, int samples, int flags)
{
    struct priv *priv = ao->priv;
    pa_threaded_mainloop_lock(priv->mainloop);
    if (pa_stream_write(priv->stream, data[0], samples * ao->sstride, NULL, 0,
                        PA_SEEK_RELATIVE) < 0) {
        GENERIC_ERR_MSG("pa_stream_write() failed");
        samples = -1;
    }
    if (flags & AOPLAY_FINAL_CHUNK) {
        // Force start in case the stream was too short for prebuf
        pa_operation *op = pa_stream_trigger(priv->stream, NULL, NULL);
        pa_operation_unref(op);
    }
    pa_threaded_mainloop_unlock(priv->mainloop);
    return samples;
}

// Reset the audio stream, i.e. flush the playback buffer on the server side
static void reset(struct ao *ao)
{
    // pa_stream_flush() works badly if not corked
    cork(ao, true);
    struct priv *priv = ao->priv;
    pa_threaded_mainloop_lock(priv->mainloop);
    priv->retval = 0;
    if (!waitop(priv, pa_stream_flush(priv->stream, success_cb, ao)) ||
        !priv->retval)
        GENERIC_ERR_MSG("pa_stream_flush() failed");
    cork(ao, false);
}
int PulseAudio_init(output_PulseAudio *self, PyObject *args, PyObject *kwds)
{
    int sample_rate;
    int channels;
    int bits_per_sample;
    char *stream_name;
    pa_sample_spec sample_spec;

    self->mainloop = NULL;
    self->mainloop_api = NULL;
    self->context = NULL;
    self->stream = NULL;

    if (!PyArg_ParseTuple(args, "iiis",
                          &sample_rate,
                          &channels,
                          &bits_per_sample,
                          &stream_name))
        return -1;

    /*sanity check output parameters*/
    if (sample_rate > 0) {
        sample_spec.rate = sample_rate;
    } else {
        PyErr_SetString(
            PyExc_ValueError, "sample rate must be a postive value");
        return -1;
    }

    if (channels > 0) {
        sample_spec.channels = channels;
    } else {
        PyErr_SetString(
            PyExc_ValueError, "channels must be a positive value");
        return -1;
    }

    /*use .wav-style sample format*/
    switch (bits_per_sample) {
    case 8:
        sample_spec.format = PA_SAMPLE_U8;
        break;
    case 16:
        sample_spec.format = PA_SAMPLE_S16LE;
        break;
    case 24:
        sample_spec.format = PA_SAMPLE_S24LE;
        break;
    default:
        PyErr_SetString(
            PyExc_ValueError, "bits-per-sample must be 8, 16 or 24");
        return -1;
    }

    /*initialize threaded mainloop*/
    if ((self->mainloop = pa_threaded_mainloop_new()) == NULL) {
        PyErr_SetString(
            PyExc_ValueError, "unable to get new mainloop");
        return -1;
    }

    /*get abstract API from threaded mainloop*/
    if ((self->mainloop_api =
         pa_threaded_mainloop_get_api(self->mainloop)) == NULL) {
        PyErr_SetString(
            PyExc_ValueError, "unable to get mainloop API");
        return -1;
    }

    /*create new connection context*/
    if ((self->context = pa_context_new(self->mainloop_api,
                                        stream_name)) == NULL) {
        PyErr_SetString(
            PyExc_ValueError, "unable to create PulseAudio connection context");
        return -1;
    }

    /*setup context change callback*/
    pa_context_set_state_callback(
        self->context,
        (pa_context_notify_cb_t)context_state_callback,
        self->mainloop);

    /*connect the context to default server*/
    if (pa_context_connect(self->context, NULL, 0, NULL) < 0) {
        PyErr_SetString(
            PyExc_ValueError, "unable to connect context");
        return -1;
    }

    pa_threaded_mainloop_lock(self->mainloop);

    if (pa_threaded_mainloop_start(self->mainloop)) {
        PyErr_SetString(
            PyExc_ValueError, "unable to start mainloop thread");
        goto error;
    }

    do {
        pa_context_state_t state = pa_context_get_state(self->context);

        if (state == PA_CONTEXT_READY) {
            break;
        } else if ((state == PA_CONTEXT_FAILED) ||
                   (state == PA_CONTEXT_TERMINATED)) {
            PyErr_SetString(
                PyExc_ValueError, "failed to start main loop");
            goto error;
        } else {
            pa_threaded_mainloop_wait(self->mainloop);
        }
    } while (1);

    /*create new connection stream*/
    if ((self->stream = pa_stream_new(self->context,
                                      stream_name,
                                      &sample_spec,
                                      NULL)) == NULL) {
        PyErr_SetString(
            PyExc_ValueError, "unable to create PulseAudio connection stream");
        goto error;
    }

    /*setup stream state change callback*/
    pa_stream_set_state_callback(
        self->stream,
        (pa_stream_notify_cb_t)stream_state_callback,
        self->mainloop);

    /*setup stream write callback*/
    pa_stream_set_write_callback(
        self->stream,
        (pa_stream_request_cb_t)write_stream_callback,
        self->mainloop);

    /*perform connection to PulseAudio server's default output stream*/
    if (pa_stream_connect_playback(
            self->stream,
            NULL, /*device*/
            NULL, /*buffering attributes*/
            PA_STREAM_ADJUST_LATENCY |
            PA_STREAM_AUTO_TIMING_UPDATE |
            PA_STREAM_INTERPOLATE_TIMING, /*flags*/
            NULL, /*volume*/
            NULL  /*sync stream*/) < 0) {
        PyErr_SetString(
            PyExc_ValueError, "unable to connect for PulseAudio playback");
        goto error;
    }

    do {
        pa_stream_state_t state = pa_stream_get_state(self->stream);

        if (state == PA_STREAM_READY) {
            break;
        } else if ((state == PA_STREAM_FAILED) ||
                   (state == PA_STREAM_TERMINATED)) {
            PyErr_SetString(PyExc_ValueError, "failed to connect stream");
            goto error;
        } else {
            pa_threaded_mainloop_wait(self->mainloop);
        }
    } while (1);

    pa_threaded_mainloop_unlock(self->mainloop);

    return 0;
error:
    pa_threaded_mainloop_unlock(self->mainloop);

    return -1;
}