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");
    

}
Exemple #2
0
static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice *pulse)
{
	pa_stream_state_t state;
	pa_buffer_attr buffer_attr = { 0 };
	if(!pulse->context)
		return FALSE;
	DEBUG_TSMF("");
	pa_threaded_mainloop_lock(pulse->mainloop);
	pulse->stream = pa_stream_new(pulse->context, "freerdp",
								  &pulse->sample_spec, NULL);
	if(!pulse->stream)
	{
		pa_threaded_mainloop_unlock(pulse->mainloop);
		CLOG_ERR("pa_stream_new failed (%d)",
				   pa_context_errno(pulse->context));
		return FALSE;
	}
	pa_stream_set_state_callback(pulse->stream,
								 tsmf_pulse_stream_state_callback, pulse);
	pa_stream_set_write_callback(pulse->stream,
								 tsmf_pulse_stream_request_callback, pulse);
	buffer_attr.maxlength = pa_usec_to_bytes(500000, &pulse->sample_spec);
	buffer_attr.tlength = pa_usec_to_bytes(250000, &pulse->sample_spec);
	buffer_attr.prebuf = (UINT32) -1;
	buffer_attr.minreq = (UINT32) -1;
	buffer_attr.fragsize = (UINT32) -1;
	if(pa_stream_connect_playback(pulse->stream,
								  pulse->device[0] ? pulse->device : NULL, &buffer_attr,
								  PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
								  NULL, NULL) < 0)
	{
		pa_threaded_mainloop_unlock(pulse->mainloop);
		CLOG_ERR("pa_stream_connect_playback failed (%d)",
				   pa_context_errno(pulse->context));
		return FALSE;
	}
	for(;;)
	{
		state = pa_stream_get_state(pulse->stream);
		if(state == PA_STREAM_READY)
			break;
		if(!PA_STREAM_IS_GOOD(state))
		{
			CLOG_ERR("bad stream state (%d)",
					   pa_context_errno(pulse->context));
			break;
		}
		pa_threaded_mainloop_wait(pulse->mainloop);
	}
	pa_threaded_mainloop_unlock(pulse->mainloop);
	if(state == PA_STREAM_READY)
	{
		DEBUG_TSMF("connected");
		return TRUE;
	}
	else
	{
		tsmf_pulse_close_stream(pulse);
		return FALSE;
	}
}
/* Read samples from the PulseAudio device.
 * Samples are converted to complex form based upon format, counted
 * and returned via cSamples pointer.
 * Returns the number of samples placed into cSamples
 */
int quisk_read_pulseaudio(struct sound_dev *dev, complex double *cSamples) {
    int i, nSamples;
    int read_frames;		// A frame is a sample from each channel
    const void * fbuffer;
    pa_stream *s = dev->handle;
    size_t read_bytes;
    
    if (!dev)
        return 0;

    if (dev->cork_status) {
        if (dev->read_frames != 0) {
            WaitForPoll();
        }
        return 0;
    }

   // Note: Access to PulseAudio data from our sound thread requires locking the threaded mainloop.
    if (dev->read_frames == 0) {		// non-blocking: read available frames
        pa_threaded_mainloop_lock(pa_ml);
        read_frames = pa_stream_readable_size(s) / dev->num_channels / dev->sample_bytes;
        
        if (read_frames == 0) {
            pa_threaded_mainloop_unlock(pa_ml);
            return 0;
        }
        dev->dev_latency = read_frames * dev->num_channels * 1000 / (dev->sample_rate / 1000);
        
    }
    
    else {		// Blocking read for dev->read_frames total frames
        WaitForPoll();
        pa_threaded_mainloop_lock(pa_ml);
        read_frames = pa_stream_readable_size(s) / dev->num_channels / dev->sample_bytes;
        
        if (read_frames == 0) {
            pa_threaded_mainloop_unlock(pa_ml);
            return 0;
        }
        dev->dev_latency = read_frames * dev->num_channels * 1000 / (dev->sample_rate / 1000);
    }
    
    
    nSamples = 0;
    
   while (nSamples < read_frames) {		// read samples in chunks until we have enough samples
       if (pa_stream_peek (s, &fbuffer, &read_bytes) < 0) {
           printf("Failure of pa_stream_peek in quisk_read_pulseaudio\n");
           pa_threaded_mainloop_unlock(pa_ml);
           return nSamples;
       }
       
       if (fbuffer == NULL && read_bytes == 0) {		// buffer is empty
           break;
       }
       
       if (fbuffer == NULL) {		// there is a hole in the buffer
           pa_stream_drop(s);
           continue;
       }
       
       if (nSamples * dev->num_channels * dev->sample_bytes + read_bytes >= SAMP_BUFFER_SIZE) {
           if (quisk_sound_state.verbose_pulse)
               printf("buffer full on %s\n", dev->name);
           pa_stream_drop(s);		// limit read request to buffer size
           break;
       }
       
       // Convert sampled data to complex data. dev->num_channels must be 2.
       if (dev->sample_bytes == 4) {  //float32
           float fi, fq;
           
           for( i = 0; i < read_bytes; i += (dev->num_channels * 4)) {
               memcpy(&fi, fbuffer + i + (dev->channel_I * 4), 4);
               memcpy(&fq, fbuffer + i + (dev->channel_Q * 4), 4);
               if (fi >=  1.0 || fi <= -1.0)
                   dev->overrange++;
               if (fq >=  1.0 || fq <= -1.0)
                   dev->overrange++;
               cSamples[nSamples++] = (fi + I * fq) * CLIP32;
           }
       }
       
       else if (dev->sample_bytes == 2) { //16bit integer little-endian
           int16_t si, sq;
           for( i = 0; i < read_bytes; i += (dev->num_channels * 2)) {
               memcpy(&si, fbuffer + i + (dev->channel_I * 2), 2);
               memcpy(&sq, fbuffer + i + (dev->channel_Q * 2), 2);
               if (si >= CLIP16  || si <= -CLIP16)
                   dev->overrange++;
               if (sq >= CLIP16 || sq <= -CLIP16)
                   dev->overrange++;
               int ii = si << 16;
               int qq = sq << 16;
               cSamples[nSamples++] = ii + I * qq;
           }
       }
       
       else {
           printf("Unknown sample size for %s", dev->name);
       }
       pa_stream_drop(s);
    }
    pa_threaded_mainloop_unlock(pa_ml);
   
    // DC removal; R.G. Lyons page 553
    complex double c;
    for (i = 0; i < nSamples; i++) {
        c = cSamples[i] + dev->dc_remove * 0.95;
        cSamples[i] = c - dev->dc_remove;
        dev->dc_remove = c;
    }
    return nSamples;
}
/*!
 * \Write outgoing samples directly to pulseaudio server.
 * \param playdev Input. Device to which to play the samples.
 * \param nSamples Input. Number of samples to play.
 * \param cSamples Input. Sample buffer to play from.
 * \param report_latency Input. 1 to update \c quisk_sound_state.latencyPlay, 0 otherwise.
 * \param volume Input. Ratio in [0,1] by which to scale the played samples.
 */
void quisk_play_pulseaudio(struct sound_dev *dev, int nSamples, complex double *cSamples, 
    int report_latency, double volume) {
    pa_stream *s = dev->handle;
    int i=0, n=0;
    void *fbuffer;
    int fbuffer_bytes = 0;
    
    if( !dev || nSamples <= 0)
        return;
    
    if (dev->cork_status)
        return;
    
    if (report_latency) {     // Report the latency, if requested.
        pa_operation *o;
        
        pa_threaded_mainloop_lock(pa_ml);
        
        if (!(o = pa_stream_update_timing_info(s, stream_timing_callback, dev))) {
            printf("pa_stream_update_timing(): %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
        }
        else {
            while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
                pa_threaded_mainloop_wait(pa_ml);
            pa_operation_unref(o);
        }
        
        pa_threaded_mainloop_unlock(pa_ml);
        
    }
    

    fbuffer = pa_xmalloc(nSamples * dev->num_channels * dev->sample_bytes);
   
    // Convert from complex data to framebuffer

    if (dev->sample_bytes == 4) {
        float fi=0.f, fq=0.f;
        for(i = 0, n = 0; n < nSamples; i += (dev->num_channels * 4), ++n) {
            fi = (volume * creal(cSamples[n])) / CLIP32;
            fq = (volume * cimag(cSamples[n])) / CLIP32;
            memcpy(fbuffer + i + (dev->channel_I * 4), &fi, 4);
            memcpy(fbuffer + i + (dev->channel_Q * 4), &fq, 4);
        }
    }

    else if (dev->sample_bytes == 2) {
        int ii, qq;
        for(i = 0, n = 0; n < nSamples; i += (dev->num_channels * 2), ++n) {
            ii = (int)(volume * creal(cSamples[n]) / 65536);
            qq = (int)(volume * cimag(cSamples[n]) / 65536);
            memcpy(fbuffer + i + (dev->channel_I * 2), &ii, 2);
            memcpy(fbuffer + i + (dev->channel_Q * 2), &qq, 2);
        }
    }

    else {
       printf("Unknown sample size for %s", dev->name);
       exit(1);
    }



    fbuffer_bytes = nSamples * dev->num_channels * dev->sample_bytes;
    pa_threaded_mainloop_lock(pa_ml);
    size_t writable = pa_stream_writable_size(s);


    if (writable > 0) {
        if ( writable > 1024*1000 ) //sanity check to prevent pa_xmalloc from crashing on monitor streams
            writable = 1024*1000;
        if (fbuffer_bytes > writable) {
            if (quisk_sound_state.verbose_pulse)
                printf("Truncating write by %u bytes\n", fbuffer_bytes - (int)writable);
            fbuffer_bytes = writable;
        }
        pa_stream_write(dev->handle, fbuffer, (size_t)fbuffer_bytes, NULL, 0, PA_SEEK_RELATIVE);
        //printf("wrote %d to %s\n", writable, dev->name);
    }
    else {
        if (quisk_sound_state.verbose_pulse)
            printf("Can't write to stream %s. Dropping %d bytes\n", dev->name, fbuffer_bytes);
    }
    
    pa_threaded_mainloop_unlock(pa_ml);
    pa_xfree(fbuffer);
    fbuffer=NULL;
}
static int drvHostPulseAudioOpen(bool fIn, const char *pszName,
                                 pa_sample_spec *pSampleSpec, pa_buffer_attr *pBufAttr,
                                 pa_stream **ppStream)
{
    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
    AssertPtrReturn(pSampleSpec, VERR_INVALID_POINTER);
    AssertPtrReturn(pBufAttr, VERR_INVALID_POINTER);
    AssertPtrReturn(ppStream, VERR_INVALID_POINTER);

    if (!pa_sample_spec_valid(pSampleSpec))
    {
        LogRel(("PulseAudio: Unsupported sample specification for stream \"%s\"\n",
                pszName));
        return VERR_NOT_SUPPORTED;
    }

    int rc = VINF_SUCCESS;

    pa_stream *pStream = NULL;
    uint32_t   flags = PA_STREAM_NOFLAGS;

    LogFunc(("Opening \"%s\", rate=%dHz, channels=%d, format=%s\n",
             pszName, pSampleSpec->rate, pSampleSpec->channels,
             pa_sample_format_to_string(pSampleSpec->format)));

    pa_threaded_mainloop_lock(g_pMainLoop);

    do
    {
        if (!(pStream = pa_stream_new(g_pContext, pszName, pSampleSpec,
                                      NULL /* pa_channel_map */)))
        {
            LogRel(("PulseAudio: Could not create stream \"%s\"\n", pszName));
            rc = VERR_NO_MEMORY;
            break;
        }

        pa_stream_set_state_callback(pStream, drvHostPulseAudioCbStreamState, NULL);

#if PA_API_VERSION >= 12
        /* XXX */
        flags |= PA_STREAM_ADJUST_LATENCY;
#endif

#if 0
        /* Not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time(). */
        flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
#endif
        /* No input/output right away after the stream was started. */
        flags |= PA_STREAM_START_CORKED;

        if (fIn)
        {
            LogFunc(("Input stream attributes: maxlength=%d fragsize=%d\n",
                     pBufAttr->maxlength, pBufAttr->fragsize));

            if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags) < 0)
            {
                LogRel(("PulseAudio: Could not connect input stream \"%s\": %s\n",
                        pszName, pa_strerror(pa_context_errno(g_pContext))));
                rc = VERR_AUDIO_BACKEND_INIT_FAILED;
                break;
            }
        }
        else
        {
            LogFunc(("Output buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
                     pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));

            if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags,
                                           /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
            {
                LogRel(("PulseAudio: Could not connect playback stream \"%s\": %s\n",
                        pszName, pa_strerror(pa_context_errno(g_pContext))));
                rc = VERR_AUDIO_BACKEND_INIT_FAILED;
                break;
            }
        }

        /* Wait until the stream is ready. */
        for (;;)
        {
            if (!g_fAbortMainLoop)
                pa_threaded_mainloop_wait(g_pMainLoop);
            g_fAbortMainLoop = false;

            pa_stream_state_t sstate = pa_stream_get_state(pStream);
            if (sstate == PA_STREAM_READY)
                break;
            else if (   sstate == PA_STREAM_FAILED
                     || sstate == PA_STREAM_TERMINATED)
            {
                LogRel(("PulseAudio: Failed to initialize stream \"%s\" (state %ld)\n",
                        pszName, sstate));
                rc = VERR_AUDIO_BACKEND_INIT_FAILED;
                break;
            }
        }

        if (RT_FAILURE(rc))
            break;

        const pa_buffer_attr *pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
        AssertPtr(pBufAttrObtained);
        memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));

        if (fIn)
            LogFunc(("Obtained record buffer attributes: maxlength=%RU32, fragsize=%RU32\n",
                     pBufAttr->maxlength, pBufAttr->fragsize));
        else
            LogFunc(("Obtained playback buffer attributes: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d\n",
                     pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));

    }
    while (0);

    if (   RT_FAILURE(rc)
        && pStream)
        pa_stream_disconnect(pStream);

    pa_threaded_mainloop_unlock(g_pMainLoop);

    if (RT_FAILURE(rc))
    {
        if (pStream)
            pa_stream_unref(pStream);
    }
    else
        *ppStream = pStream;

    LogFlowFuncLeaveRC(rc);
    return rc;
}
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_AUDIO_BACKEND_INIT_FAILED;
            break;
        }

        g_fAbortMainLoop = false;
        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_AUDIO_BACKEND_INIT_FAILED;
            break;
        }

        /* Wait until the g_pContext is ready */
        for (;;)
        {
            if (!g_fAbortMainLoop)
                pa_threaded_mainloop_wait(g_pMainLoop);
            g_fAbortMainLoop = false;

            pa_context_state_t 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_AUDIO_BACKEND_INIT_FAILED;
                break;
            }
        }
    }
    while (0);

    if (fLocked)
        pa_threaded_mainloop_unlock(g_pMainLoop);

    if (RT_FAILURE(rc))
    {
        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;
}
Exemple #7
0
/*****************************************************************************
 * 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;
}
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;
}
Exemple #10
0
static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
	pa_stream_state_t state;
	pa_buffer_attr buffer_attr = { 0 };
	AudinPulseDevice* pulse = (AudinPulseDevice*) device;

	if (!pulse->context)
		return;
	if (!pulse->sample_spec.rate || pulse->stream)
		return;

	DEBUG_DVC("");

	pulse->receive = receive;
	pulse->user_data = user_data;

	pa_threaded_mainloop_lock(pulse->mainloop);
	pulse->stream = pa_stream_new(pulse->context, "freerdp_audin",
		&pulse->sample_spec, NULL);
	if (!pulse->stream)
	{
		pa_threaded_mainloop_unlock(pulse->mainloop);
		DEBUG_DVC("pa_stream_new failed (%d)",
			pa_context_errno(pulse->context));
		return;
	}
	pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
	pa_stream_set_state_callback(pulse->stream,
		audin_pulse_stream_state_callback, pulse);
	pa_stream_set_read_callback(pulse->stream,
		audin_pulse_stream_request_callback, pulse);
	buffer_attr.maxlength = (uint32_t) -1;
	buffer_attr.tlength = (uint32_t) -1;
	buffer_attr.prebuf = (uint32_t) -1;
	buffer_attr.minreq = (uint32_t) -1;
	/* 500ms latency */
	buffer_attr.fragsize = pa_usec_to_bytes(500000, &pulse->sample_spec);
	if (pa_stream_connect_record(pulse->stream,
		pulse->device_name[0] ? pulse->device_name : NULL,
		&buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0)
	{
		pa_threaded_mainloop_unlock(pulse->mainloop);
		DEBUG_WARN("pa_stream_connect_playback failed (%d)",
			pa_context_errno(pulse->context));
		return;
	}

	for (;;)
	{
		state = pa_stream_get_state(pulse->stream);
		if (state == PA_STREAM_READY)
			break;
		if (!PA_STREAM_IS_GOOD(state))
		{
			DEBUG_WARN("bad stream state (%d)",
				pa_context_errno(pulse->context));
			break;
		}
		pa_threaded_mainloop_wait(pulse->mainloop);
	}
	pa_threaded_mainloop_unlock(pulse->mainloop);
	if (state == PA_STREAM_READY)
	{
		memset(&pulse->adpcm, 0, sizeof(ADPCM));
		pulse->buffer = xzalloc(pulse->bytes_per_frame * pulse->frames_per_packet);
		pulse->buffer_frames = 0;
		DEBUG_DVC("connected");
	}
	else
	{
		audin_pulse_close(device);
	}
}
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;
}