static int paCreateStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples) { AssertPtrReturn(pInterface, VERR_INVALID_POINTER); AssertPtrReturn(pStream, VERR_INVALID_POINTER); AssertPtrReturn(pCfg, VERR_INVALID_POINTER); /* pcSamples is optional. */ PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pStream; LogFlowFuncEnter(); pStrm->pDrainOp = NULL; pStrm->SampleSpec.format = paFmtToPulse(pCfg->enmFormat); pStrm->SampleSpec.rate = pCfg->uHz; pStrm->SampleSpec.channels = pCfg->cChannels; /* Note that setting maxlength to -1 does not work on PulseAudio servers * older than 0.9.10. So use the suggested value of 3/2 of tlength */ pStrm->BufAttr.tlength = (pa_bytes_per_second(&pStrm->SampleSpec) * s_pulseCfg.buffer_msecs_out) / 1000; pStrm->BufAttr.maxlength = (pStrm->BufAttr.tlength * 3) / 2; pStrm->BufAttr.prebuf = -1; /* Same as tlength */ pStrm->BufAttr.minreq = -1; /* Note that the struct BufAttr is updated to the obtained values after this call! */ int rc = paStreamOpen(pThis, false /* fIn */, "PulseAudio (Out)", &pStrm->SampleSpec, &pStrm->BufAttr, &pStrm->pPAStream); if (RT_FAILURE(rc)) return rc; PDMAUDIOSTREAMCFG streamCfg; rc = paPulseToFmt(pStrm->SampleSpec.format, &streamCfg.enmFormat, &streamCfg.enmEndianness); if (RT_FAILURE(rc)) { LogRel(("PulseAudio: Cannot find audio output format %ld\n", pStrm->SampleSpec.format)); return rc; } streamCfg.uHz = pStrm->SampleSpec.rate; streamCfg.cChannels = pStrm->SampleSpec.channels; rc = DrvAudioHlpStreamCfgToProps(&streamCfg, &pStream->Props); if (RT_SUCCESS(rc)) { uint32_t cbBuf = RT_MIN(pStrm->BufAttr.tlength * 2, pStrm->BufAttr.maxlength); /** @todo Make this configurable! */ if (cbBuf) { pStrm->pvPCMBuf = RTMemAllocZ(cbBuf); if (pStrm->pvPCMBuf) { pStrm->cbPCMBuf = cbBuf; uint32_t cSamples = cbBuf >> pStream->Props.cShift; if (pcSamples) *pcSamples = cSamples; /* Save pointer to driver instance. */ pStrm->pDrv = pThis; LogFunc(("cbBuf=%RU32, cSamples=%RU32\n", cbBuf, cSamples)); } else rc = VERR_NO_MEMORY; }
static DECLCALLBACK(int) drvHostPulseAudioInitOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples) { AssertPtrReturn(pInterface, VERR_INVALID_POINTER); AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER); AssertPtrReturn(pCfg, VERR_INVALID_POINTER); /* pcSamples is optional. */ PDRVHOSTPULSEAUDIO pDrv = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut; LogFlowFuncEnter(); pThisStrmOut->pDrainOp = NULL; pThisStrmOut->SampleSpec.format = drvHostPulseAudioFmtToPulse(pCfg->enmFormat); pThisStrmOut->SampleSpec.rate = pCfg->uHz; pThisStrmOut->SampleSpec.channels = pCfg->cChannels; /* Note that setting maxlength to -1 does not work on PulseAudio servers * older than 0.9.10. So use the suggested value of 3/2 of tlength */ pThisStrmOut->BufAttr.tlength = (pa_bytes_per_second(&pThisStrmOut->SampleSpec) * s_pulseCfg.buffer_msecs_out) / 1000; pThisStrmOut->BufAttr.maxlength = (pThisStrmOut->BufAttr.tlength * 3) / 2; pThisStrmOut->BufAttr.prebuf = -1; /* Same as tlength */ pThisStrmOut->BufAttr.minreq = -1; /* Pulse should set something sensible for minreq on it's own */ /* Note that the struct BufAttr is updated to the obtained values after this call! */ char achName[64]; RTStrPrintf(achName, sizeof(achName), "%.32s (out)", pDrv->pszStreamName); int rc = drvHostPulseAudioOpen(false /* fIn */, achName, &pThisStrmOut->SampleSpec, &pThisStrmOut->BufAttr, &pThisStrmOut->pStream); if (RT_FAILURE(rc)) return rc; PDMAUDIOSTREAMCFG streamCfg; rc = drvHostPulseAudioPulseToFmt(pThisStrmOut->SampleSpec.format, &streamCfg.enmFormat, &streamCfg.enmEndianness); if (RT_FAILURE(rc)) { LogRel(("PulseAudio: Cannot find audio output format %ld\n", pThisStrmOut->SampleSpec.format)); return rc; } streamCfg.uHz = pThisStrmOut->SampleSpec.rate; streamCfg.cChannels = pThisStrmOut->SampleSpec.channels; rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props); if (RT_SUCCESS(rc)) { uint32_t cbBuf = RT_MIN(pThisStrmOut->BufAttr.tlength * 2, pThisStrmOut->BufAttr.maxlength); /** @todo Make this configurable! */ if (cbBuf) { pThisStrmOut->pvPCMBuf = RTMemAllocZ(cbBuf); if (pThisStrmOut->pvPCMBuf) { pThisStrmOut->cbPCMBuf = cbBuf; uint32_t cSamples = cbBuf >> pHstStrmOut->Props.cShift; if (pcSamples) *pcSamples = cSamples; /* Save pointer to driver instance. */ pThisStrmOut->pDrv = pDrv; LogFunc(("cbBuf=%RU32, cSamples=%RU32\n", cbBuf, cSamples)); } else rc = VERR_NO_MEMORY; }
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; }