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"); }
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; }
/***************************************************************************** * 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; }
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; }