VirtualBox

Changeset 88466 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Apr 12, 2021 11:54:26 AM (4 years ago)
Author:
vboxsync
Message:

DrvHostAudioPulseAudio: Re-ordering functions. Changed and unified prefix. Some cleanups. bugref:9890

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp

    r88390 r88466  
    167167
    168168
     169
    169170/*
    170  * To allow running on systems with PulseAudio < 0.9.11.
     171 * Glue to make the code work systems with PulseAudio < 0.9.11.
    171172 */
    172173#if !defined(PA_CONTEXT_IS_GOOD) && PA_API_VERSION < 12 /* 12 = 0.9.11 where PA_STREAM_IS_GOOD was added */
     
    189190
    190191
    191 /*********************************************************************************************************************************
    192 *   Prototypes                                                                                                                   *
    193 *********************************************************************************************************************************/
    194 
    195 static int  paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum);
    196 static int  paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg);
    197 #ifdef DEBUG
    198 static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext);
    199 static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext);
    200 #endif
    201 static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext);
     192
     193/** @todo Implement va handling. */
     194static int drvHostAudioPaError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg)
     195{
     196    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
     197    AssertPtrReturn(szMsg, VERR_INVALID_POINTER);
     198
     199    if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)
     200    {
     201        int rc2 = pa_context_errno(pThis->pContext);
     202        LogRel2(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2)));
     203    }
     204
     205    /** @todo Implement some PulseAudio -> IPRT mapping here. */
     206    return VERR_GENERAL_FAILURE;
     207}
    202208
    203209
     
    206212 * mainloop might not have been entered yet.
    207213 */
    208 static void paSignalWaiter(PDRVHOSTPULSEAUDIO pThis)
     214static void drvHostAudioPaSignalWaiter(PDRVHOSTPULSEAUDIO pThis)
    209215{
    210216    if (!pThis)
     
    216222
    217223
    218 static pa_sample_format_t paAudioPropsToPulse(PPDMAUDIOPCMPROPS pProps)
    219 {
    220     switch (PDMAudioPropsSampleSize(pProps))
    221     {
    222         case 1:
    223             if (!pProps->fSigned)
    224                 return PA_SAMPLE_U8;
    225             break;
    226 
    227         case 2:
    228             if (pProps->fSigned)
    229                 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
    230             break;
    231 
    232 #ifdef PA_SAMPLE_S32LE
    233         case 4:
    234             if (pProps->fSigned)
    235                 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S32LE : PA_SAMPLE_S32BE;
    236             break;
    237 #endif
    238     }
    239 
    240     AssertMsgFailed(("%RU8%s not supported\n", PDMAudioPropsSampleSize(pProps), pProps->fSigned ? "S" : "U"));
    241     return PA_SAMPLE_INVALID;
    242 }
    243 
    244 
    245 static int paPulseToAudioProps(PPDMAUDIOPCMPROPS pProps, pa_sample_format_t pulsefmt, uint8_t cChannels, uint32_t uHz)
    246 {
    247     AssertReturn(cChannels > 0, VERR_INVALID_PARAMETER);
    248     AssertReturn(cChannels < 16, VERR_INVALID_PARAMETER);
    249 
    250     switch (pulsefmt)
    251     {
    252         case PA_SAMPLE_U8:
    253             PDMAudioPropsInit(pProps, 1 /*8-bit*/, false /*signed*/, cChannels, uHz);
    254             break;
    255 
    256         case PA_SAMPLE_S16LE:
    257             PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
    258             break;
    259 
    260         case PA_SAMPLE_S16BE:
    261             PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
    262             break;
    263 
    264 #ifdef PA_SAMPLE_S32LE
    265         case PA_SAMPLE_S32LE:
    266             PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
    267             break;
    268 #endif
    269 
    270 #ifdef PA_SAMPLE_S32BE
    271         case PA_SAMPLE_S32BE:
    272             PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
    273             break;
    274 #endif
     224
     225/**
     226 * Pulse audio callback for context status changes, init variant.
     227 */
     228static void drvHostAudioPaCtxCallbackStateChanged(pa_context *pCtx, void *pvUser)
     229{
     230    AssertPtrReturnVoid(pCtx);
     231
     232    PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser;
     233    AssertPtrReturnVoid(pThis);
     234
     235    switch (pa_context_get_state(pCtx))
     236    {
     237        case PA_CONTEXT_READY:
     238        case PA_CONTEXT_TERMINATED:
     239        case PA_CONTEXT_FAILED:
     240            drvHostAudioPaSignalWaiter(pThis);
     241            break;
    275242
    276243        default:
    277             AssertLogRelMsgFailed(("PulseAudio: Format (%d) not supported\n", pulsefmt));
    278             return VERR_NOT_SUPPORTED;
    279     }
    280 
    281     return VINF_SUCCESS;
     244            break;
     245    }
     246}
     247
     248
     249/**
     250 * Callback used with pa_stream_cork() in a number of places.
     251 */
     252static void drvHostAudioPaStreamSuccessCallback(pa_stream *pStream, int fSuccess, void *pvUser)
     253{
     254    AssertPtrReturnVoid(pStream);
     255    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser;
     256    AssertPtrReturnVoid(pStrm);
     257
     258    pStrm->fOpSuccess = fSuccess;
     259
     260    if (fSuccess)
     261        drvHostAudioPaSignalWaiter(pStrm->pDrv);
     262    else
     263        drvHostAudioPaError(pStrm->pDrv, "Failed to finish stream operation");
    282264}
    283265
     
    286268 * Synchronously wait until an operation completed.
    287269 */
    288 static int paWaitForEx(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP, RTMSINTERVAL cMsTimeout)
     270static int drvHostAudioPaWaitForEx(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP, RTMSINTERVAL cMsTimeout)
    289271{
    290272    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
     
    323305
    324306
    325 static int paWaitFor(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP)
    326 {
    327     return paWaitForEx(pThis, pOP, 10 * 1000 /* 10s timeout */);
    328 }
    329 
    330 
    331 /**
    332  * Context status changed, init variant signalling our own event semaphore
    333  * so we can do a timed wait.
    334  */
    335 static void paContextCbStateChangedInit(pa_context *pCtx, void *pvUser)
    336 {
     307static int drvHostAudioPaWaitFor(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP)
     308{
     309    return drvHostAudioPaWaitForEx(pThis, pOP, 10 * RT_MS_1SEC);
     310}
     311
     312
     313static void drvHostAudioPaEnumSinkCallback(pa_context *pCtx, const pa_sink_info *pInfo, int eol, void *pvUserData)
     314{
     315    if (eol > 0)
     316        return;
     317
     318    PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
     319    AssertPtrReturnVoid(pCbCtx);
     320    PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
     321    AssertPtrReturnVoid(pThis);
     322    if (eol < 0)
     323    {
     324        pThis->fEnumOpSuccess = false;
     325        pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
     326        return;
     327    }
     328
    337329    AssertPtrReturnVoid(pCtx);
    338 
    339     PPULSEAUDIOSTATECHGCTX pStateChgCtx = (PPULSEAUDIOSTATECHGCTX)pvUser;
    340     pa_context_state_t enmCtxState = pa_context_get_state(pCtx);
    341     switch (enmCtxState)
    342     {
    343         case PA_CONTEXT_READY:
    344         case PA_CONTEXT_TERMINATED:
    345         case PA_CONTEXT_FAILED:
    346             pStateChgCtx->enmCtxState = enmCtxState;
    347             RTSemEventSignal(pStateChgCtx->hEvtInit);
    348             break;
     330    AssertPtrReturnVoid(pInfo);
     331
     332    LogRel2(("PulseAudio: Using output sink '%s'\n", pInfo->name));
     333
     334    /** @todo Store sinks + channel mapping in callback context as soon as we have surround support. */
     335    pCbCtx->cDevOut++;
     336
     337    pThis->fEnumOpSuccess = true;
     338    pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
     339}
     340
     341
     342static void drvHostAudioPaEnumSourceCallback(pa_context *pCtx, const pa_source_info *pInfo, int eol, void *pvUserData)
     343{
     344    if (eol > 0)
     345        return;
     346
     347    PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
     348    AssertPtrReturnVoid(pCbCtx);
     349    PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
     350    AssertPtrReturnVoid(pThis);
     351    if (eol < 0)
     352    {
     353        pThis->fEnumOpSuccess = false;
     354        pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
     355        return;
     356    }
     357
     358    AssertPtrReturnVoid(pCtx);
     359    AssertPtrReturnVoid(pInfo);
     360
     361    LogRel2(("PulseAudio: Using input source '%s'\n", pInfo->name));
     362
     363    /** @todo Store sources + channel mapping in callback context as soon as we have surround support. */
     364    pCbCtx->cDevIn++;
     365
     366    pThis->fEnumOpSuccess = true;
     367    pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
     368}
     369
     370
     371static void drvHostAudioPaEnumServerCallback(pa_context *pCtx, const pa_server_info *pInfo, void *pvUserData)
     372{
     373    AssertPtrReturnVoid(pCtx);
     374    PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
     375    AssertPtrReturnVoid(pCbCtx);
     376    PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
     377    AssertPtrReturnVoid(pThis);
     378
     379    if (!pInfo)
     380    {
     381        pThis->fEnumOpSuccess = false;
     382        pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
     383        return;
     384    }
     385
     386    if (pInfo->default_sink_name)
     387    {
     388        Assert(RTStrIsValidEncoding(pInfo->default_sink_name));
     389        pCbCtx->pszDefaultSink   = RTStrDup(pInfo->default_sink_name);
     390    }
     391
     392    if (pInfo->default_sink_name)
     393    {
     394        Assert(RTStrIsValidEncoding(pInfo->default_source_name));
     395        pCbCtx->pszDefaultSource = RTStrDup(pInfo->default_source_name);
     396    }
     397
     398    pThis->fEnumOpSuccess = true;
     399    pa_threaded_mainloop_signal(pThis->pMainLoop, 0);
     400}
     401
     402
     403static int drvHostAudioPaEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
     404{
     405    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
     406    AssertPtrReturn(pCfg,  VERR_INVALID_POINTER);
     407
     408    PDMAUDIOBACKENDCFG Cfg;
     409    RT_ZERO(Cfg);
     410
     411    RTStrPrintf2(Cfg.szName, sizeof(Cfg.szName), "PulseAudio");
     412
     413    Cfg.cbStreamOut    = sizeof(PULSEAUDIOSTREAM);
     414    Cfg.cbStreamIn     = sizeof(PULSEAUDIOSTREAM);
     415    Cfg.cMaxStreamsOut = UINT32_MAX;
     416    Cfg.cMaxStreamsIn  = UINT32_MAX;
     417
     418    PULSEAUDIOENUMCBCTX CbCtx;
     419    RT_ZERO(CbCtx);
     420
     421    CbCtx.pDrv   = pThis;
     422    CbCtx.fFlags = fEnum;
     423
     424    bool fLog = (fEnum & PULSEAUDIOENUMCBFLAGS_LOG);
     425
     426    pa_threaded_mainloop_lock(pThis->pMainLoop);
     427
     428    pThis->fEnumOpSuccess = false;
     429
     430    LogRel(("PulseAudio: Retrieving server information ...\n"));
     431
     432    /* Check if server information is available and bail out early if it isn't. */
     433    pa_operation *paOpServerInfo = pa_context_get_server_info(pThis->pContext, drvHostAudioPaEnumServerCallback, &CbCtx);
     434    if (!paOpServerInfo)
     435    {
     436        pa_threaded_mainloop_unlock(pThis->pMainLoop);
     437
     438        LogRel(("PulseAudio: Server information not available, skipping enumeration\n"));
     439        return VINF_SUCCESS;
     440    }
     441
     442    int rc = drvHostAudioPaWaitFor(pThis, paOpServerInfo);
     443    if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess)
     444        rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */
     445    if (RT_SUCCESS(rc))
     446    {
     447        if (CbCtx.pszDefaultSink)
     448        {
     449            if (fLog)
     450                LogRel2(("PulseAudio: Default output sink is '%s'\n", CbCtx.pszDefaultSink));
     451
     452            pThis->fEnumOpSuccess = false;
     453            rc = drvHostAudioPaWaitFor(pThis, pa_context_get_sink_info_by_name(pThis->pContext, CbCtx.pszDefaultSink,
     454                                                                   drvHostAudioPaEnumSinkCallback, &CbCtx));
     455            if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess)
     456                rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */
     457            if (   RT_FAILURE(rc)
     458                && fLog)
     459            {
     460                LogRel(("PulseAudio: Error enumerating properties for default output sink '%s'\n", CbCtx.pszDefaultSink));
     461            }
     462        }
     463        else if (fLog)
     464            LogRel2(("PulseAudio: No default output sink found\n"));
     465
     466        if (RT_SUCCESS(rc))
     467        {
     468            if (CbCtx.pszDefaultSource)
     469            {
     470                if (fLog)
     471                    LogRel2(("PulseAudio: Default input source is '%s'\n", CbCtx.pszDefaultSource));
     472
     473                pThis->fEnumOpSuccess = false;
     474                rc = drvHostAudioPaWaitFor(pThis, pa_context_get_source_info_by_name(pThis->pContext, CbCtx.pszDefaultSource,
     475                                                                         drvHostAudioPaEnumSourceCallback, &CbCtx));
     476                if (   (RT_FAILURE(rc) || !pThis->fEnumOpSuccess)
     477                    && fLog)
     478                {
     479                    LogRel(("PulseAudio: Error enumerating properties for default input source '%s'\n", CbCtx.pszDefaultSource));
     480                }
     481            }
     482            else if (fLog)
     483                LogRel2(("PulseAudio: No default input source found\n"));
     484        }
     485
     486        if (RT_SUCCESS(rc))
     487        {
     488            if (fLog)
     489            {
     490                LogRel2(("PulseAudio: Found %RU8 host playback device(s)\n",  CbCtx.cDevOut));
     491                LogRel2(("PulseAudio: Found %RU8 host capturing device(s)\n", CbCtx.cDevIn));
     492            }
     493
     494            if (pCfg)
     495                memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
     496        }
     497
     498        if (CbCtx.pszDefaultSink)
     499        {
     500            RTStrFree(CbCtx.pszDefaultSink);
     501            CbCtx.pszDefaultSink = NULL;
     502        }
     503
     504        if (CbCtx.pszDefaultSource)
     505        {
     506            RTStrFree(CbCtx.pszDefaultSource);
     507            CbCtx.pszDefaultSource = NULL;
     508        }
     509    }
     510    else if (fLog)
     511        LogRel(("PulseAudio: Error enumerating PulseAudio server properties\n"));
     512
     513    pa_threaded_mainloop_unlock(pThis->pMainLoop);
     514
     515    LogFlowFuncLeaveRC(rc);
     516    return rc;
     517}
     518
     519
     520/**
     521 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
     522 */
     523static DECLCALLBACK(int) drvHostAudioPaHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
     524{
     525    AssertPtrReturn(pInterface,  VERR_INVALID_POINTER);
     526    AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
     527    PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     528
     529    return drvHostAudioPaEnumerate(pThis, pBackendCfg, PULSEAUDIOENUMCBFLAGS_LOG /* fEnum */);
     530}
     531
     532
     533/**
     534 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
     535 */
     536static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAudioPaHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
     537{
     538    RT_NOREF(enmDir);
     539    AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
     540
     541    return PDMAUDIOBACKENDSTS_RUNNING;
     542}
     543
     544
     545static pa_sample_format_t drvHostAudioPaPropsToPulse(PPDMAUDIOPCMPROPS pProps)
     546{
     547    switch (PDMAudioPropsSampleSize(pProps))
     548    {
     549        case 1:
     550            if (!pProps->fSigned)
     551                return PA_SAMPLE_U8;
     552            break;
     553
     554        case 2:
     555            if (pProps->fSigned)
     556                return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
     557            break;
     558
     559#ifdef PA_SAMPLE_S32LE
     560        case 4:
     561            if (pProps->fSigned)
     562                return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S32LE : PA_SAMPLE_S32BE;
     563            break;
     564#endif
     565    }
     566
     567    AssertMsgFailed(("%RU8%s not supported\n", PDMAudioPropsSampleSize(pProps), pProps->fSigned ? "S" : "U"));
     568    return PA_SAMPLE_INVALID;
     569}
     570
     571
     572static int drvHostAudioPaToAudioProps(PPDMAUDIOPCMPROPS pProps, pa_sample_format_t pulsefmt, uint8_t cChannels, uint32_t uHz)
     573{
     574    AssertReturn(cChannels > 0, VERR_INVALID_PARAMETER);
     575    AssertReturn(cChannels < 16, VERR_INVALID_PARAMETER);
     576
     577    switch (pulsefmt)
     578    {
     579        case PA_SAMPLE_U8:
     580            PDMAudioPropsInit(pProps, 1 /*8-bit*/, false /*signed*/, cChannels, uHz);
     581            break;
     582
     583        case PA_SAMPLE_S16LE:
     584            PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
     585            break;
     586
     587        case PA_SAMPLE_S16BE:
     588            PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
     589            break;
     590
     591#ifdef PA_SAMPLE_S32LE
     592        case PA_SAMPLE_S32LE:
     593            PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
     594            break;
     595#endif
     596
     597#ifdef PA_SAMPLE_S32BE
     598        case PA_SAMPLE_S32BE:
     599            PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
     600            break;
     601#endif
    349602
    350603        default:
    351             break;
    352     }
    353 }
    354 
    355 
    356 /**
    357  * Context status changed.
    358  */
    359 static void paContextCbStateChanged(pa_context *pCtx, void *pvUser)
    360 {
    361     AssertPtrReturnVoid(pCtx);
    362 
    363     PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser;
    364     AssertPtrReturnVoid(pThis);
    365 
    366     switch (pa_context_get_state(pCtx))
    367     {
    368         case PA_CONTEXT_READY:
    369         case PA_CONTEXT_TERMINATED:
    370         case PA_CONTEXT_FAILED:
    371             paSignalWaiter(pThis);
    372             break;
    373 
    374         default:
    375             break;
    376     }
    377 }
    378 
    379 
    380 /**
    381  * Callback called when our pa_stream_drain operation was completed.
    382  */
    383 static void paStreamCbDrain(pa_stream *pStream, int fSuccess, void *pvUser)
    384 {
    385     AssertPtrReturnVoid(pStream);
    386 
    387     PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser;
    388     AssertPtrReturnVoid(pStreamPA);
    389 
    390     pStreamPA->fOpSuccess = fSuccess;
    391     if (fSuccess)
    392     {
    393         pa_operation_unref(pa_stream_cork(pStream, 1,
    394                                           paStreamCbSuccess, pvUser));
    395     }
    396     else
    397         paError(pStreamPA->pDrv, "Failed to drain stream");
    398 
    399     if (pStreamPA->pDrainOp)
    400     {
    401         pa_operation_unref(pStreamPA->pDrainOp);
    402         pStreamPA->pDrainOp = NULL;
    403     }
     604            AssertLogRelMsgFailed(("PulseAudio: Format (%d) not supported\n", pulsefmt));
     605            return VERR_NOT_SUPPORTED;
     606    }
     607
     608    return VINF_SUCCESS;
    404609}
    405610
     
    408613 * Stream status changed.
    409614 */
    410 static void paStreamCbStateChanged(pa_stream *pStream, void *pvUser)
     615static void drvHostAudioPaStreamStateChangedCallback(pa_stream *pStream, void *pvUser)
    411616{
    412617    AssertPtrReturnVoid(pStream);
     
    420625        case PA_STREAM_FAILED:
    421626        case PA_STREAM_TERMINATED:
    422             paSignalWaiter(pThis);
     627            drvHostAudioPaSignalWaiter(pThis);
    423628            break;
    424629
     
    430635#ifdef DEBUG
    431636
    432 static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext)
     637static void drvHostAudioPaStreamReqWriteDebugCallback(pa_stream *pStream, size_t cbLen, void *pvContext)
    433638{
    434639    RT_NOREF(cbLen, pvContext);
     
    445650
    446651
    447 static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext)
     652static void drvHostAudioPaStreamUnderflowDebugCallback(pa_stream *pStream, void *pvContext)
    448653{
    449654    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
     
    489694
    490695
    491 static void paStreamCbOverflow(pa_stream *pStream, void *pvContext)
     696static void drvHostAudioPaStreamOverflowDebugCallback(pa_stream *pStream, void *pvContext)
    492697{
    493698    RT_NOREF(pStream, pvContext);
     
    498703#endif /* DEBUG */
    499704
    500 static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvUser)
    501 {
    502     AssertPtrReturnVoid(pStream);
    503 
    504     PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser;
    505     AssertPtrReturnVoid(pStrm);
    506 
    507     pStrm->fOpSuccess = fSuccess;
    508 
    509     if (fSuccess)
    510         paSignalWaiter(pStrm->pDrv);
    511     else
    512         paError(pStrm->pDrv, "Failed to finish stream operation");
    513 }
    514 
    515 
    516 static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, bool fIn, const char *pszName)
     705
     706static int drvHostAudioPaStreamOpen(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, bool fIn, const char *pszName)
    517707{
    518708    AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
     
    550740
    551741#ifdef DEBUG
    552         pa_stream_set_write_callback       (pStream, paStreamCbReqWrite,     pStreamPA);
    553         pa_stream_set_underflow_callback   (pStream, paStreamCbUnderflow,    pStreamPA);
     742        pa_stream_set_write_callback(       pStream, drvHostAudioPaStreamReqWriteDebugCallback,  pStreamPA);
     743        pa_stream_set_underflow_callback(   pStream, drvHostAudioPaStreamUnderflowDebugCallback, pStreamPA);
    554744        if (!fIn) /* Only for output streams. */
    555             pa_stream_set_overflow_callback(pStream, paStreamCbOverflow,     pStreamPA);
     745            pa_stream_set_overflow_callback(pStream, drvHostAudioPaStreamOverflowDebugCallback,  pStreamPA);
    556746#endif
    557         pa_stream_set_state_callback       (pStream, paStreamCbStateChanged, pThis);
     747        pa_stream_set_state_callback(       pStream, drvHostAudioPaStreamStateChangedCallback,  pThis);
    558748
    559749        uint32_t flags = PA_STREAM_NOFLAGS;
     
    645835
    646836
    647 static int paCreateStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA,
    648                              PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     837static int drvHostAudioPaCreateStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA,
     838                                         PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    649839{
    650840    pStreamPA->pDrainOp            = NULL;
    651841
    652     pStreamPA->SampleSpec.format   = paAudioPropsToPulse(&pCfgReq->Props);
     842    pStreamPA->SampleSpec.format   = drvHostAudioPaPropsToPulse(&pCfgReq->Props);
    653843    pStreamPA->SampleSpec.rate     = PDMAudioPropsHz(&pCfgReq->Props);
    654844    pStreamPA->SampleSpec.channels = PDMAudioPropsChannels(&pCfgReq->Props);
     
    674864
    675865    /* Note that the struct BufAttr is updated to the obtained values after this call! */
    676     int rc = paStreamOpen(pThis, pStreamPA, false /* fIn */, szName);
     866    int rc = drvHostAudioPaStreamOpen(pThis, pStreamPA, false /* fIn */, szName);
    677867    if (RT_FAILURE(rc))
    678868        return rc;
    679869
    680     rc = paPulseToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,
    681                              pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);
     870    rc = drvHostAudioPaToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,
     871                                  pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);
    682872    if (RT_FAILURE(rc))
    683873    {
     
    699889
    700890
    701 static int paCreateStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM  pStreamPA,
    702                             PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    703 {
    704     pStreamPA->SampleSpec.format   = paAudioPropsToPulse(&pCfgReq->Props);
     891static int drvHostAudioPaCreateStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM  pStreamPA,
     892                                        PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     893{
     894    pStreamPA->SampleSpec.format   = drvHostAudioPaPropsToPulse(&pCfgReq->Props);
    705895    pStreamPA->SampleSpec.rate     = PDMAudioPropsHz(&pCfgReq->Props);
    706896    pStreamPA->SampleSpec.channels = PDMAudioPropsChannels(&pCfgReq->Props);
     
    715905
    716906    /* Note: Other members of BufAttr are ignored for record streams. */
    717     int rc = paStreamOpen(pThis, pStreamPA, true /* fIn */, szName);
     907    int rc = drvHostAudioPaStreamOpen(pThis, pStreamPA, true /* fIn */, szName);
    718908    if (RT_FAILURE(rc))
    719909        return rc;
    720910
    721     rc = paPulseToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,
    722                              pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);
     911    rc = drvHostAudioPaToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,
     912                                  pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);
    723913    if (RT_FAILURE(rc))
    724914    {
     
    742932
    743933/**
     934 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
     935 */
     936static DECLCALLBACK(int) drvHostAudioPaHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     937                                                       PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     938{
     939    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     940    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
     941    AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
     942    AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
     943
     944    PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     945    PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
     946
     947    int rc;
     948    if (pCfgReq->enmDir == PDMAUDIODIR_IN)
     949        rc = drvHostAudioPaCreateStreamIn (pThis, pStreamPA, pCfgReq, pCfgAcq);
     950    else if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
     951        rc = drvHostAudioPaCreateStreamOut(pThis, pStreamPA, pCfgReq, pCfgAcq);
     952    else
     953        AssertFailedReturn(VERR_NOT_IMPLEMENTED);
     954
     955    if (RT_SUCCESS(rc))
     956    {
     957        pStreamPA->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
     958        if (!pStreamPA->pCfg)
     959            rc = VERR_NO_MEMORY;
     960    }
     961
     962    return rc;
     963}
     964
     965
     966/**
     967 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
     968 */
     969static DECLCALLBACK(int) drvHostAudioPaHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     970{
     971    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     972    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
     973
     974    PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     975    PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
     976
     977    if (pStreamPA->pStream)
     978    {
     979        pa_threaded_mainloop_lock(pThis->pMainLoop);
     980
     981        /* Make sure to cancel a pending draining operation, if any. */
     982        if (pStreamPA->pDrainOp)
     983        {
     984            pa_operation_cancel(pStreamPA->pDrainOp);
     985            pStreamPA->pDrainOp = NULL;
     986        }
     987
     988        pa_stream_disconnect(pStreamPA->pStream);
     989        pa_stream_unref(pStreamPA->pStream);
     990
     991        pStreamPA->pStream = NULL;
     992
     993        pa_threaded_mainloop_unlock(pThis->pMainLoop);
     994    }
     995
     996    if (pStreamPA->pCfg)
     997    {
     998        PDMAudioStrmCfgFree(pStreamPA->pCfg);
     999        pStreamPA->pCfg = NULL;
     1000    }
     1001
     1002    return VINF_SUCCESS;
     1003}
     1004
     1005
     1006/**
     1007 * Pulse audio pa_stream_drain() completion callback.
     1008 */
     1009static void drvHostAudioPaStreamDrainCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser)
     1010{
     1011    AssertPtrReturnVoid(pStream);
     1012    PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser;
     1013    AssertPtrReturnVoid(pStreamPA);
     1014
     1015    pStreamPA->fOpSuccess = fSuccess;
     1016    if (fSuccess)
     1017        pa_operation_unref(pa_stream_cork(pStream, 1, drvHostAudioPaStreamSuccessCallback, pvUser));
     1018    else
     1019        drvHostAudioPaError(pStreamPA->pDrv, "Failed to drain stream");
     1020
     1021    if (pStreamPA->pDrainOp)
     1022    {
     1023        pa_operation_unref(pStreamPA->pDrainOp);
     1024        pStreamPA->pDrainOp = NULL;
     1025    }
     1026}
     1027
     1028
     1029static int drvHostAudioPaStreamControlOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd)
     1030{
     1031    int rc = VINF_SUCCESS;
     1032
     1033    switch (enmStreamCmd)
     1034    {
     1035        case PDMAUDIOSTREAMCMD_ENABLE:
     1036        case PDMAUDIOSTREAMCMD_RESUME:
     1037        {
     1038            pa_threaded_mainloop_lock(pThis->pMainLoop);
     1039
     1040            if (   pStreamPA->pDrainOp
     1041                && pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_DONE)
     1042            {
     1043                pa_operation_cancel(pStreamPA->pDrainOp);
     1044                pa_operation_unref(pStreamPA->pDrainOp);
     1045
     1046                pStreamPA->pDrainOp = NULL;
     1047            }
     1048            else
     1049            {
     1050                /* Uncork (resume) stream. */
     1051                rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Uncork */, drvHostAudioPaStreamSuccessCallback, pStreamPA));
     1052            }
     1053
     1054            pa_threaded_mainloop_unlock(pThis->pMainLoop);
     1055            break;
     1056        }
     1057
     1058        case PDMAUDIOSTREAMCMD_DISABLE:
     1059        case PDMAUDIOSTREAMCMD_PAUSE:
     1060        {
     1061            /* Pause audio output (the Pause bit of the AC97 x_CR register is set).
     1062             * Note that we must return immediately from here! */
     1063            pa_threaded_mainloop_lock(pThis->pMainLoop);
     1064            if (!pStreamPA->pDrainOp)
     1065            {
     1066                rc = drvHostAudioPaWaitFor(pThis, pa_stream_trigger(pStreamPA->pStream, drvHostAudioPaStreamSuccessCallback, pStreamPA));
     1067                if (RT_SUCCESS(rc))
     1068                    pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, drvHostAudioPaStreamDrainCompletionCallback, pStreamPA);
     1069            }
     1070            pa_threaded_mainloop_unlock(pThis->pMainLoop);
     1071            break;
     1072        }
     1073
     1074        default:
     1075            rc = VERR_NOT_SUPPORTED;
     1076            break;
     1077    }
     1078
     1079    LogFlowFuncLeaveRC(rc);
     1080    return rc;
     1081}
     1082
     1083
     1084static int drvHostAudioPaStreamControlIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd)
     1085{
     1086    int rc = VINF_SUCCESS;
     1087
     1088    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
     1089
     1090    switch (enmStreamCmd)
     1091    {
     1092        case PDMAUDIOSTREAMCMD_ENABLE:
     1093        case PDMAUDIOSTREAMCMD_RESUME:
     1094        {
     1095            pa_threaded_mainloop_lock(pThis->pMainLoop);
     1096            rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Play / resume */, drvHostAudioPaStreamSuccessCallback, pStreamPA));
     1097            pa_threaded_mainloop_unlock(pThis->pMainLoop);
     1098            break;
     1099        }
     1100
     1101        case PDMAUDIOSTREAMCMD_DISABLE:
     1102        case PDMAUDIOSTREAMCMD_PAUSE:
     1103        {
     1104            pa_threaded_mainloop_lock(pThis->pMainLoop);
     1105            if (pStreamPA->pu8PeekBuf) /* Do we need to drop the peek buffer?*/
     1106            {
     1107                pa_stream_drop(pStreamPA->pStream);
     1108                pStreamPA->pu8PeekBuf = NULL;
     1109            }
     1110
     1111            rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 1 /* Stop / pause */, drvHostAudioPaStreamSuccessCallback, pStreamPA));
     1112            pa_threaded_mainloop_unlock(pThis->pMainLoop);
     1113            break;
     1114        }
     1115
     1116        default:
     1117            rc = VERR_NOT_SUPPORTED;
     1118            break;
     1119    }
     1120
     1121    return rc;
     1122}
     1123
     1124
     1125/**
     1126 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
     1127 */
     1128static DECLCALLBACK(int) drvHostAudioPaHA_StreamControl(PPDMIHOSTAUDIO pInterface,
     1129                                                        PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
     1130{
     1131    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     1132    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
     1133
     1134    PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     1135    PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
     1136
     1137    if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */
     1138        return VINF_SUCCESS;
     1139
     1140    int rc;
     1141    if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
     1142        rc = drvHostAudioPaStreamControlIn (pThis, pStreamPA, enmStreamCmd);
     1143    else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
     1144        rc = drvHostAudioPaStreamControlOut(pThis, pStreamPA, enmStreamCmd);
     1145    else
     1146        AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
     1147
     1148    return rc;
     1149}
     1150
     1151
     1152static uint32_t drvHostAudioPaStreamGetAvailable(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA)
     1153{
     1154    pa_threaded_mainloop_lock(pThis->pMainLoop);
     1155
     1156    uint32_t cbAvail = 0;
     1157
     1158    if (PA_STREAM_IS_GOOD(pa_stream_get_state(pStreamPA->pStream)))
     1159    {
     1160        if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
     1161        {
     1162            cbAvail = (uint32_t)pa_stream_readable_size(pStreamPA->pStream);
     1163            Log3Func(("cbReadable=%RU32\n", cbAvail));
     1164        }
     1165        else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
     1166        {
     1167            size_t cbWritable = pa_stream_writable_size(pStreamPA->pStream);
     1168
     1169            Log3Func(("cbWritable=%zu, maxLength=%RU32, minReq=%RU32\n",
     1170                      cbWritable, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
     1171
     1172            /* Don't report more writable than the PA server can handle. */
     1173            if (cbWritable > pStreamPA->BufAttr.maxlength)
     1174                cbWritable = pStreamPA->BufAttr.maxlength;
     1175
     1176            cbAvail = (uint32_t)cbWritable;
     1177        }
     1178        else
     1179            AssertFailed();
     1180    }
     1181
     1182    pa_threaded_mainloop_unlock(pThis->pMainLoop);
     1183
     1184    return cbAvail;
     1185}
     1186
     1187
     1188/**
     1189 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
     1190 */
     1191static DECLCALLBACK(uint32_t) drvHostAudioPaHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1192{
     1193    PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     1194    PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
     1195
     1196    return drvHostAudioPaStreamGetAvailable(pThis, pStreamPA);
     1197}
     1198
     1199
     1200/**
     1201 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
     1202 */
     1203static DECLCALLBACK(uint32_t) drvHostAudioPaHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1204{
     1205    PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     1206    PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
     1207
     1208    return drvHostAudioPaStreamGetAvailable(pThis, pStreamPA);
     1209}
     1210
     1211
     1212/**
     1213 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
     1214 */
     1215static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAudioPaHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1216{
     1217    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     1218    RT_NOREF(pStream);
     1219
     1220    PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     1221
     1222    PDMAUDIOSTREAMSTS fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_NONE;
     1223
     1224    /* Check PulseAudio's general status. */
     1225    if (   pThis->pContext
     1226        && PA_CONTEXT_IS_GOOD(pa_context_get_state(pThis->pContext)))
     1227       fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
     1228
     1229    return fStrmSts;
     1230}
     1231
     1232
     1233/**
     1234 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
     1235 */
     1236static DECLCALLBACK(int) drvHostAudioPaHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     1237                                                        const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
     1238{
     1239    PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
     1240    AssertPtr(pThis);
     1241    PPULSEAUDIOSTREAM pPAStream = (PPULSEAUDIOSTREAM)pStream;
     1242    AssertPtrReturn(pPAStream, VERR_INVALID_POINTER);
     1243    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     1244    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     1245    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
     1246
     1247    pa_threaded_mainloop_lock(pThis->pMainLoop);
     1248
     1249#ifdef LOG_ENABLED
     1250    const pa_usec_t tsNowUs         = pa_rtclock_now();
     1251    const pa_usec_t tsDeltaPlayedUs = tsNowUs - pPAStream->tsLastReadWrittenUs;
     1252    pPAStream->tsLastReadWrittenUs  = tsNowUs;
     1253    Log3Func(("tsDeltaPlayedMs=%RU64\n", tsDeltaPlayedUs / RT_US_1MS));
     1254#endif
     1255
     1256    int          rc;
     1257    size_t const cbWriteable = pa_stream_writable_size(pPAStream->pStream);
     1258    if (cbWriteable != (size_t)-1)
     1259    {
     1260        size_t cbLeft = RT_MIN(cbWriteable, cbBuf);
     1261        Assert(cbLeft > 0 /* At this point we better have *something* to write (DrvAudio checked before calling). */);
     1262        if (pa_stream_write(pPAStream->pStream, pvBuf, cbLeft, NULL /*pfnFree*/, 0 /*offset*/, PA_SEEK_RELATIVE) >= 0)
     1263        {
     1264            *pcbWritten = (uint32_t)cbLeft;
     1265            rc = VINF_SUCCESS;
     1266        }
     1267        else
     1268            rc = drvHostAudioPaError(pPAStream->pDrv, "Failed to write to output stream");
     1269    }
     1270    else
     1271        rc = drvHostAudioPaError(pPAStream->pDrv, "Failed to determine output data size");
     1272
     1273    pa_threaded_mainloop_unlock(pThis->pMainLoop);
     1274    return rc;
     1275}
     1276
     1277
     1278/**
    7441279 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
    7451280 */
    746 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     1281static DECLCALLBACK(int) drvHostAudioPaHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    7471282                                                           void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
    7481283{
     
    7621297
    7631298    if (cbAvail == (size_t)-1)
    764         return paError(pStreamPA->pDrv, "Failed to determine input data size");
     1299        return drvHostAudioPaError(pStreamPA->pDrv, "Failed to determine input data size");
    7651300
    7661301    /* If the buffer was not dropped last call, add what remains. */
     
    8581393
    8591394
    860 /**
    861  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
    862  */
    863 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    864                                                         const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
    865 {
    866     PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    867     AssertPtr(pThis);
    868     PPULSEAUDIOSTREAM pPAStream = (PPULSEAUDIOSTREAM)pStream;
    869     AssertPtrReturn(pPAStream, VERR_INVALID_POINTER);
    870     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    871     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    872     AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
    873 
    874     pa_threaded_mainloop_lock(pThis->pMainLoop);
    875 
    876 #ifdef LOG_ENABLED
    877     const pa_usec_t tsNowUs         = pa_rtclock_now();
    878     const pa_usec_t tsDeltaPlayedUs = tsNowUs - pPAStream->tsLastReadWrittenUs;
    879     pPAStream->tsLastReadWrittenUs  = tsNowUs;
    880     Log3Func(("tsDeltaPlayedMs=%RU64\n", tsDeltaPlayedUs / RT_US_1MS));
    881 #endif
    882 
    883     int          rc;
    884     size_t const cbWriteable = pa_stream_writable_size(pPAStream->pStream);
    885     if (cbWriteable != (size_t)-1)
    886     {
    887         size_t cbLeft = RT_MIN(cbWriteable, cbBuf);
    888         Assert(cbLeft > 0 /* At this point we better have *something* to write (DrvAudio checked before calling). */);
    889         if (pa_stream_write(pPAStream->pStream, pvBuf, cbLeft, NULL /*pfnFree*/, 0 /*offset*/, PA_SEEK_RELATIVE) >= 0)
    890         {
    891             *pcbWritten = (uint32_t)cbLeft;
    892             rc = VINF_SUCCESS;
    893         }
    894         else
    895             rc = paError(pPAStream->pDrv, "Failed to write to output stream");
    896     }
    897     else
    898         rc = paError(pPAStream->pDrv, "Failed to determine output data size");
    899 
    900     pa_threaded_mainloop_unlock(pThis->pMainLoop);
    901     return rc;
    902 }
    903 
    904 
    905 /** @todo Implement va handling. */
    906 static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg)
    907 {
    908     AssertPtrReturn(pThis, VERR_INVALID_POINTER);
    909     AssertPtrReturn(szMsg, VERR_INVALID_POINTER);
    910 
    911     if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)
    912     {
    913         int rc2 = pa_context_errno(pThis->pContext);
    914         LogRel2(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2)));
    915     }
    916 
    917     /** @todo Implement some PulseAudio -> IPRT mapping here. */
    918     return VERR_GENERAL_FAILURE;
    919 }
    920 
    921 
    922 static void paEnumSinkCb(pa_context *pCtx, const pa_sink_info *pInfo, int eol, void *pvUserData)
    923 {
    924     if (eol > 0)
    925         return;
    926 
    927     PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
    928     AssertPtrReturnVoid(pCbCtx);
    929     PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
    930     AssertPtrReturnVoid(pThis);
    931     if (eol < 0)
    932     {
    933         pThis->fEnumOpSuccess = false;
    934         pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
    935         return;
    936     }
    937 
    938     AssertPtrReturnVoid(pCtx);
    939     AssertPtrReturnVoid(pInfo);
    940 
    941     LogRel2(("PulseAudio: Using output sink '%s'\n", pInfo->name));
    942 
    943     /** @todo Store sinks + channel mapping in callback context as soon as we have surround support. */
    944     pCbCtx->cDevOut++;
    945 
    946     pThis->fEnumOpSuccess = true;
    947     pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
    948 }
    949 
    950 
    951 static void paEnumSourceCb(pa_context *pCtx, const pa_source_info *pInfo, int eol, void *pvUserData)
    952 {
    953     if (eol > 0)
    954         return;
    955 
    956     PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
    957     AssertPtrReturnVoid(pCbCtx);
    958     PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
    959     AssertPtrReturnVoid(pThis);
    960     if (eol < 0)
    961     {
    962         pThis->fEnumOpSuccess = false;
    963         pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
    964         return;
    965     }
    966 
    967     AssertPtrReturnVoid(pCtx);
    968     AssertPtrReturnVoid(pInfo);
    969 
    970     LogRel2(("PulseAudio: Using input source '%s'\n", pInfo->name));
    971 
    972     /** @todo Store sources + channel mapping in callback context as soon as we have surround support. */
    973     pCbCtx->cDevIn++;
    974 
    975     pThis->fEnumOpSuccess = true;
    976     pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
    977 }
    978 
    979 
    980 static void paEnumServerCb(pa_context *pCtx, const pa_server_info *pInfo, void *pvUserData)
    981 {
    982     AssertPtrReturnVoid(pCtx);
    983     PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
    984     AssertPtrReturnVoid(pCbCtx);
    985     PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
    986     AssertPtrReturnVoid(pThis);
    987 
    988     if (!pInfo)
    989     {
    990         pThis->fEnumOpSuccess = false;
    991         pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
    992         return;
    993     }
    994 
    995     if (pInfo->default_sink_name)
    996     {
    997         Assert(RTStrIsValidEncoding(pInfo->default_sink_name));
    998         pCbCtx->pszDefaultSink   = RTStrDup(pInfo->default_sink_name);
    999     }
    1000 
    1001     if (pInfo->default_sink_name)
    1002     {
    1003         Assert(RTStrIsValidEncoding(pInfo->default_source_name));
    1004         pCbCtx->pszDefaultSource = RTStrDup(pInfo->default_source_name);
    1005     }
    1006 
    1007     pThis->fEnumOpSuccess = true;
    1008     pa_threaded_mainloop_signal(pThis->pMainLoop, 0);
    1009 }
    1010 
    1011 
    1012 static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
    1013 {
    1014     AssertPtrReturn(pThis, VERR_INVALID_POINTER);
    1015     AssertPtrReturn(pCfg,  VERR_INVALID_POINTER);
    1016 
    1017     PDMAUDIOBACKENDCFG Cfg;
    1018     RT_ZERO(Cfg);
    1019 
    1020     RTStrPrintf2(Cfg.szName, sizeof(Cfg.szName), "PulseAudio");
    1021 
    1022     Cfg.cbStreamOut    = sizeof(PULSEAUDIOSTREAM);
    1023     Cfg.cbStreamIn     = sizeof(PULSEAUDIOSTREAM);
    1024     Cfg.cMaxStreamsOut = UINT32_MAX;
    1025     Cfg.cMaxStreamsIn  = UINT32_MAX;
    1026 
    1027     PULSEAUDIOENUMCBCTX CbCtx;
    1028     RT_ZERO(CbCtx);
    1029 
    1030     CbCtx.pDrv   = pThis;
    1031     CbCtx.fFlags = fEnum;
    1032 
    1033     bool fLog = (fEnum & PULSEAUDIOENUMCBFLAGS_LOG);
    1034 
    1035     pa_threaded_mainloop_lock(pThis->pMainLoop);
    1036 
    1037     pThis->fEnumOpSuccess = false;
    1038 
    1039     LogRel(("PulseAudio: Retrieving server information ...\n"));
    1040 
    1041     /* Check if server information is available and bail out early if it isn't. */
    1042     pa_operation *paOpServerInfo = pa_context_get_server_info(pThis->pContext, paEnumServerCb, &CbCtx);
    1043     if (!paOpServerInfo)
    1044     {
    1045         pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1046 
    1047         LogRel(("PulseAudio: Server information not available, skipping enumeration\n"));
    1048         return VINF_SUCCESS;
    1049     }
    1050 
    1051     int rc = paWaitFor(pThis, paOpServerInfo);
    1052     if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess)
    1053         rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */
    1054     if (RT_SUCCESS(rc))
    1055     {
    1056         if (CbCtx.pszDefaultSink)
    1057         {
    1058             if (fLog)
    1059                 LogRel2(("PulseAudio: Default output sink is '%s'\n", CbCtx.pszDefaultSink));
    1060 
    1061             pThis->fEnumOpSuccess = false;
    1062             rc = paWaitFor(pThis, pa_context_get_sink_info_by_name(pThis->pContext, CbCtx.pszDefaultSink,
    1063                                                                    paEnumSinkCb, &CbCtx));
    1064             if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess)
    1065                 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */
    1066             if (   RT_FAILURE(rc)
    1067                 && fLog)
    1068             {
    1069                 LogRel(("PulseAudio: Error enumerating properties for default output sink '%s'\n", CbCtx.pszDefaultSink));
    1070             }
    1071         }
    1072         else if (fLog)
    1073             LogRel2(("PulseAudio: No default output sink found\n"));
    1074 
    1075         if (RT_SUCCESS(rc))
    1076         {
    1077             if (CbCtx.pszDefaultSource)
    1078             {
    1079                 if (fLog)
    1080                     LogRel2(("PulseAudio: Default input source is '%s'\n", CbCtx.pszDefaultSource));
    1081 
    1082                 pThis->fEnumOpSuccess = false;
    1083                 rc = paWaitFor(pThis, pa_context_get_source_info_by_name(pThis->pContext, CbCtx.pszDefaultSource,
    1084                                                                          paEnumSourceCb, &CbCtx));
    1085                 if (   (RT_FAILURE(rc) || !pThis->fEnumOpSuccess)
    1086                     && fLog)
    1087                 {
    1088                     LogRel(("PulseAudio: Error enumerating properties for default input source '%s'\n", CbCtx.pszDefaultSource));
    1089                 }
    1090             }
    1091             else if (fLog)
    1092                 LogRel2(("PulseAudio: No default input source found\n"));
    1093         }
    1094 
    1095         if (RT_SUCCESS(rc))
    1096         {
    1097             if (fLog)
    1098             {
    1099                 LogRel2(("PulseAudio: Found %RU8 host playback device(s)\n",  CbCtx.cDevOut));
    1100                 LogRel2(("PulseAudio: Found %RU8 host capturing device(s)\n", CbCtx.cDevIn));
    1101             }
    1102 
    1103             if (pCfg)
    1104                 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
    1105         }
    1106 
    1107         if (CbCtx.pszDefaultSink)
    1108         {
    1109             RTStrFree(CbCtx.pszDefaultSink);
    1110             CbCtx.pszDefaultSink = NULL;
    1111         }
    1112 
    1113         if (CbCtx.pszDefaultSource)
    1114         {
    1115             RTStrFree(CbCtx.pszDefaultSource);
    1116             CbCtx.pszDefaultSource = NULL;
    1117         }
    1118     }
    1119     else if (fLog)
    1120         LogRel(("PulseAudio: Error enumerating PulseAudio server properties\n"));
    1121 
    1122     pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1123 
    1124     LogFlowFuncLeaveRC(rc);
    1125     return rc;
    1126 }
    1127 
    1128 
    1129 static int paDestroyStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA)
    1130 {
    1131     LogFlowFuncEnter();
    1132 
    1133     if (pStreamPA->pStream)
    1134     {
    1135         pa_threaded_mainloop_lock(pThis->pMainLoop);
    1136 
    1137         pa_stream_disconnect(pStreamPA->pStream);
    1138         pa_stream_unref(pStreamPA->pStream);
    1139 
    1140         pStreamPA->pStream = NULL;
    1141 
    1142         pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1143     }
    1144 
    1145     return VINF_SUCCESS;
    1146 }
    1147 
    1148 
    1149 static int paDestroyStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA)
    1150 {
    1151     if (pStreamPA->pStream)
    1152     {
    1153         pa_threaded_mainloop_lock(pThis->pMainLoop);
    1154 
    1155         /* Make sure to cancel a pending draining operation, if any. */
    1156         if (pStreamPA->pDrainOp)
    1157         {
    1158             pa_operation_cancel(pStreamPA->pDrainOp);
    1159             pStreamPA->pDrainOp = NULL;
    1160         }
    1161 
    1162         pa_stream_disconnect(pStreamPA->pStream);
    1163         pa_stream_unref(pStreamPA->pStream);
    1164 
    1165         pStreamPA->pStream = NULL;
    1166 
    1167         pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1168     }
    1169 
    1170     return VINF_SUCCESS;
    1171 }
    1172 
    1173 
    1174 static int paControlStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd)
    1175 {
    1176     int rc = VINF_SUCCESS;
    1177 
    1178     switch (enmStreamCmd)
    1179     {
    1180         case PDMAUDIOSTREAMCMD_ENABLE:
    1181         case PDMAUDIOSTREAMCMD_RESUME:
    1182         {
    1183             pa_threaded_mainloop_lock(pThis->pMainLoop);
    1184 
    1185             if (   pStreamPA->pDrainOp
    1186                 && pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_DONE)
    1187             {
    1188                 pa_operation_cancel(pStreamPA->pDrainOp);
    1189                 pa_operation_unref(pStreamPA->pDrainOp);
    1190 
    1191                 pStreamPA->pDrainOp = NULL;
    1192             }
    1193             else
    1194             {
    1195                 /* Uncork (resume) stream. */
    1196                 rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Uncork */, paStreamCbSuccess, pStreamPA));
    1197             }
    1198 
    1199             pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1200             break;
    1201         }
    1202 
    1203         case PDMAUDIOSTREAMCMD_DISABLE:
    1204         case PDMAUDIOSTREAMCMD_PAUSE:
    1205         {
    1206             /* Pause audio output (the Pause bit of the AC97 x_CR register is set).
    1207              * Note that we must return immediately from here! */
    1208             pa_threaded_mainloop_lock(pThis->pMainLoop);
    1209             if (!pStreamPA->pDrainOp)
    1210             {
    1211                 rc = paWaitFor(pThis, pa_stream_trigger(pStreamPA->pStream, paStreamCbSuccess, pStreamPA));
    1212                 if (RT_SUCCESS(rc))
    1213                     pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, paStreamCbDrain, pStreamPA);
    1214             }
    1215             pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1216             break;
    1217         }
    1218 
    1219         default:
    1220             rc = VERR_NOT_SUPPORTED;
    1221             break;
    1222     }
    1223 
    1224     LogFlowFuncLeaveRC(rc);
    1225     return rc;
    1226 }
    1227 
    1228 
    1229 static int paControlStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd)
    1230 {
    1231     int rc = VINF_SUCCESS;
    1232 
    1233     LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
    1234 
    1235     switch (enmStreamCmd)
    1236     {
    1237         case PDMAUDIOSTREAMCMD_ENABLE:
    1238         case PDMAUDIOSTREAMCMD_RESUME:
    1239         {
    1240             pa_threaded_mainloop_lock(pThis->pMainLoop);
    1241             rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Play / resume */, paStreamCbSuccess, pStreamPA));
    1242             pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1243             break;
    1244         }
    1245 
    1246         case PDMAUDIOSTREAMCMD_DISABLE:
    1247         case PDMAUDIOSTREAMCMD_PAUSE:
    1248         {
    1249             pa_threaded_mainloop_lock(pThis->pMainLoop);
    1250             if (pStreamPA->pu8PeekBuf) /* Do we need to drop the peek buffer?*/
    1251             {
    1252                 pa_stream_drop(pStreamPA->pStream);
    1253                 pStreamPA->pu8PeekBuf = NULL;
    1254             }
    1255 
    1256             rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 1 /* Stop / pause */, paStreamCbSuccess, pStreamPA));
    1257             pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1258             break;
    1259         }
    1260 
    1261         default:
    1262             rc = VERR_NOT_SUPPORTED;
    1263             break;
    1264     }
    1265 
    1266     return rc;
    1267 }
    1268 
    1269 
    1270 /**
    1271  * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
    1272  */
    1273 static DECLCALLBACK(int) drvHostPulseAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
    1274 {
    1275     AssertPtrReturn(pInterface,  VERR_INVALID_POINTER);
    1276     AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
    1277 
    1278     PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    1279 
    1280     return paEnumerate(pThis, pBackendCfg, PULSEAUDIOENUMCBFLAGS_LOG /* fEnum */);
    1281 }
    1282 
    1283 
    1284 /**
    1285  * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
    1286  */
    1287 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostPulseAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
    1288 {
    1289     RT_NOREF(enmDir);
    1290     AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
    1291 
    1292     return PDMAUDIOBACKENDSTS_RUNNING;
    1293 }
    1294 
    1295 
    1296 /**
    1297  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
    1298  */
    1299 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    1300                                                           PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    1301 {
    1302     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1303     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    1304     AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
    1305     AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
    1306 
    1307     PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    1308     PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
    1309 
    1310     int rc;
    1311     if (pCfgReq->enmDir == PDMAUDIODIR_IN)
    1312         rc = paCreateStreamIn (pThis, pStreamPA, pCfgReq, pCfgAcq);
    1313     else if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
    1314         rc = paCreateStreamOut(pThis, pStreamPA, pCfgReq, pCfgAcq);
    1315     else
    1316         AssertFailedReturn(VERR_NOT_IMPLEMENTED);
    1317 
    1318     if (RT_SUCCESS(rc))
    1319     {
    1320         pStreamPA->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
    1321         if (!pStreamPA->pCfg)
    1322             rc = VERR_NO_MEMORY;
    1323     }
    1324 
    1325     return rc;
    1326 }
    1327 
    1328 
    1329 /**
    1330  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
    1331  */
    1332 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1333 {
    1334     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1335     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    1336 
    1337     PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    1338     PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
    1339 
    1340     if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */
    1341         return VINF_SUCCESS;
    1342 
    1343     int rc;
    1344     if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
    1345         rc = paDestroyStreamIn (pThis, pStreamPA);
    1346     else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
    1347         rc = paDestroyStreamOut(pThis, pStreamPA);
    1348     else
    1349         AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
    1350 
    1351     if (RT_SUCCESS(rc))
    1352     {
    1353         PDMAudioStrmCfgFree(pStreamPA->pCfg);
    1354         pStreamPA->pCfg = NULL;
    1355     }
    1356 
    1357     return rc;
    1358 }
    1359 
    1360 
    1361 /**
    1362  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
    1363  */
    1364 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
    1365                                                            PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
    1366 {
    1367     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1368     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    1369 
    1370     PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    1371     PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
    1372 
    1373     if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */
    1374         return VINF_SUCCESS;
    1375 
    1376     int rc;
    1377     if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
    1378         rc = paControlStreamIn (pThis, pStreamPA, enmStreamCmd);
    1379     else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
    1380         rc = paControlStreamOut(pThis, pStreamPA, enmStreamCmd);
    1381     else
    1382         AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
    1383 
    1384     return rc;
    1385 }
    1386 
    1387 
    1388 static uint32_t paStreamGetAvail(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA)
    1389 {
    1390     pa_threaded_mainloop_lock(pThis->pMainLoop);
    1391 
    1392     uint32_t cbAvail = 0;
    1393 
    1394     if (PA_STREAM_IS_GOOD(pa_stream_get_state(pStreamPA->pStream)))
    1395     {
    1396         if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
    1397         {
    1398             cbAvail = (uint32_t)pa_stream_readable_size(pStreamPA->pStream);
    1399             Log3Func(("cbReadable=%RU32\n", cbAvail));
    1400         }
    1401         else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
    1402         {
    1403             size_t cbWritable = pa_stream_writable_size(pStreamPA->pStream);
    1404 
    1405             Log3Func(("cbWritable=%zu, maxLength=%RU32, minReq=%RU32\n",
    1406                       cbWritable, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
    1407 
    1408             /* Don't report more writable than the PA server can handle. */
    1409             if (cbWritable > pStreamPA->BufAttr.maxlength)
    1410                 cbWritable = pStreamPA->BufAttr.maxlength;
    1411 
    1412             cbAvail = (uint32_t)cbWritable;
    1413         }
    1414         else
    1415             AssertFailed();
    1416     }
    1417 
    1418     pa_threaded_mainloop_unlock(pThis->pMainLoop);
    1419 
    1420     return cbAvail;
    1421 }
    1422 
    1423 
    1424 /**
    1425  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
    1426  */
    1427 static DECLCALLBACK(uint32_t) drvHostPulseAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1428 {
    1429     PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    1430     PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
    1431 
    1432     return paStreamGetAvail(pThis, pStreamPA);
    1433 }
    1434 
    1435 
    1436 /**
    1437  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
    1438  */
    1439 static DECLCALLBACK(uint32_t) drvHostPulseAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1440 {
    1441     PDRVHOSTPULSEAUDIO pThis     = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    1442     PPULSEAUDIOSTREAM  pStreamPA = (PPULSEAUDIOSTREAM)pStream;
    1443 
    1444     return paStreamGetAvail(pThis, pStreamPA);
    1445 }
    1446 
    1447 
    1448 /**
    1449  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
    1450  */
    1451 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostPulseAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1452 {
    1453     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1454     RT_NOREF(pStream);
    1455 
    1456     PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
    1457 
    1458     PDMAUDIOSTREAMSTS fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_NONE;
    1459 
    1460     /* Check PulseAudio's general status. */
    1461     if (   pThis->pContext
    1462         && PA_CONTEXT_IS_GOOD(pa_context_get_state(pThis->pContext)))
    1463        fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
    1464 
    1465     return fStrmSts;
    1466 }
    1467 
     1395/*********************************************************************************************************************************
     1396*   PDMIBASE                                                                                                                     *
     1397*********************************************************************************************************************************/
    14681398
    14691399/**
    14701400 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
    14711401 */
    1472 static DECLCALLBACK(void *) drvHostPulseAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
     1402static DECLCALLBACK(void *) drvHostAudioPaQueryInterface(PPDMIBASE pInterface, const char *pszIID)
    14731403{
    14741404    AssertPtrReturn(pInterface, NULL);
     
    14841414
    14851415
     1416/*********************************************************************************************************************************
     1417*   PDMDRVREG                                                                                                                    *
     1418*********************************************************************************************************************************/
     1419
    14861420/**
    14871421 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
    14881422 */
    1489 static DECLCALLBACK(void) drvHostPulseAudioPowerOff(PPDMDRVINS pDrvIns)
     1423static DECLCALLBACK(void) drvHostAudioPaPowerOff(PPDMDRVINS pDrvIns)
    14901424{
    14911425    PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
     
    15171451 * @copydoc FNPDMDRVDESTRUCT
    15181452 */
    1519 static DECLCALLBACK(void) drvHostPulseAudioDestruct(PPDMDRVINS pDrvIns)
     1453static DECLCALLBACK(void) drvHostAudioPaDestruct(PPDMDRVINS pDrvIns)
    15201454{
    15211455    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
    15221456    LogFlowFuncEnter();
    1523     drvHostPulseAudioPowerOff(pDrvIns);
     1457    drvHostAudioPaPowerOff(pDrvIns);
    15241458    LogFlowFuncLeave();
     1459}
     1460
     1461
     1462/**
     1463 * Pulse audio callback for context status changes, init variant.
     1464 *
     1465 * Signalls our event semaphore so we can do a timed wait from
     1466 * drvHostAudioPaConstruct().
     1467 */
     1468static void drvHostAudioPaCtxCallbackStateChangedInit(pa_context *pCtx, void *pvUser)
     1469{
     1470    AssertPtrReturnVoid(pCtx);
     1471    PPULSEAUDIOSTATECHGCTX pStateChgCtx = (PPULSEAUDIOSTATECHGCTX)pvUser;
     1472    pa_context_state_t     enmCtxState  = pa_context_get_state(pCtx);
     1473    switch (enmCtxState)
     1474    {
     1475        case PA_CONTEXT_READY:
     1476        case PA_CONTEXT_TERMINATED:
     1477        case PA_CONTEXT_FAILED:
     1478            AssertPtrReturnVoid(pStateChgCtx);
     1479            pStateChgCtx->enmCtxState = enmCtxState;
     1480            RTSemEventSignal(pStateChgCtx->hEvtInit);
     1481            break;
     1482
     1483        default:
     1484            break;
     1485    }
    15251486}
    15261487
     
    15311492 * @copydoc FNPDMDRVCONSTRUCT
    15321493 */
    1533 static DECLCALLBACK(int) drvHostPulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
     1494static DECLCALLBACK(int) drvHostAudioPaConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
    15341495{
    15351496    RT_NOREF(pCfg, fFlags);
     
    15451506    pThis->pDrvIns                   = pDrvIns;
    15461507    /* IBase */
    1547     pDrvIns->IBase.pfnQueryInterface = drvHostPulseAudioQueryInterface;
     1508    pDrvIns->IBase.pfnQueryInterface = drvHostAudioPaQueryInterface;
    15481509    /* IHostAudio */
    1549     pThis->IHostAudio.pfnGetConfig          = drvHostPulseAudioHA_GetConfig;
    1550     pThis->IHostAudio.pfnGetStatus          = drvHostPulseAudioHA_GetStatus;
    1551     pThis->IHostAudio.pfnStreamCreate       = drvHostPulseAudioHA_StreamCreate;
    1552     pThis->IHostAudio.pfnStreamDestroy      = drvHostPulseAudioHA_StreamDestroy;
    1553     pThis->IHostAudio.pfnStreamControl      = drvHostPulseAudioHA_StreamControl;
    1554     pThis->IHostAudio.pfnStreamGetReadable  = drvHostPulseAudioHA_StreamGetReadable;
    1555     pThis->IHostAudio.pfnStreamGetWritable  = drvHostPulseAudioHA_StreamGetWritable;
    1556     pThis->IHostAudio.pfnStreamGetStatus    = drvHostPulseAudioHA_StreamGetStatus;
    1557     pThis->IHostAudio.pfnStreamPlay         = drvHostPulseAudioHA_StreamPlay;
    1558     pThis->IHostAudio.pfnStreamCapture      = drvHostPulseAudioHA_StreamCapture;
     1510    pThis->IHostAudio.pfnGetConfig          = drvHostAudioPaHA_GetConfig;
    15591511    pThis->IHostAudio.pfnGetDevices         = NULL;
     1512    pThis->IHostAudio.pfnGetStatus          = drvHostAudioPaHA_GetStatus;
     1513    pThis->IHostAudio.pfnStreamCreate       = drvHostAudioPaHA_StreamCreate;
     1514    pThis->IHostAudio.pfnStreamDestroy      = drvHostAudioPaHA_StreamDestroy;
     1515    pThis->IHostAudio.pfnStreamControl      = drvHostAudioPaHA_StreamControl;
     1516    pThis->IHostAudio.pfnStreamGetReadable  = drvHostAudioPaHA_StreamGetReadable;
     1517    pThis->IHostAudio.pfnStreamGetWritable  = drvHostAudioPaHA_StreamGetWritable;
    15601518    pThis->IHostAudio.pfnStreamGetPending   = NULL;
     1519    pThis->IHostAudio.pfnStreamGetStatus    = drvHostAudioPaHA_StreamGetStatus;
     1520    pThis->IHostAudio.pfnStreamPlay         = drvHostAudioPaHA_StreamPlay;
     1521    pThis->IHostAudio.pfnStreamCapture      = drvHostAudioPaHA_StreamCapture;
    15611522
    15621523    /*
     
    16141575
    16151576    pa_threaded_mainloop_lock(pThis->pMainLoop);
    1616     pa_context_set_state_callback(pThis->pContext, paContextCbStateChangedInit, &pThis->InitStateChgCtx);
     1577    pa_context_set_state_callback(pThis->pContext, drvHostAudioPaCtxCallbackStateChangedInit, &pThis->InitStateChgCtx);
    16171578    if (!pa_context_connect(pThis->pContext, NULL /* pszServer */, PA_CONTEXT_NOFLAGS, NULL))
    16181579    {
     
    16261587                /* Install the main state changed callback to know if something happens to our acquired context. */
    16271588                pa_threaded_mainloop_lock(pThis->pMainLoop);
    1628                 pa_context_set_state_callback(pThis->pContext, paContextCbStateChanged, pThis /* pvUserData */);
     1589                pa_context_set_state_callback(pThis->pContext, drvHostAudioPaCtxCallbackStateChanged, pThis /* pvUserData */);
    16291590                pa_threaded_mainloop_unlock(pThis->pMainLoop);
    16301591            }
     
    16791640    sizeof(DRVHOSTPULSEAUDIO),
    16801641    /* pfnConstruct */
    1681     drvHostPulseAudioConstruct,
     1642    drvHostAudioPaConstruct,
    16821643    /* pfnDestruct */
    1683     drvHostPulseAudioDestruct,
     1644    drvHostAudioPaDestruct,
    16841645    /* pfnRelocate */
    16851646    NULL,
     
    16991660    NULL,
    17001661    /* pfnPowerOff */
    1701     drvHostPulseAudioPowerOff,
     1662    drvHostAudioPaPowerOff,
    17021663    /* pfnSoftReset */
    17031664    NULL,
     
    17061667};
    17071668
    1708 #if 0 /* unused */
    1709 static struct audio_option pulse_options[] =
    1710 {
    1711     {"DAC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_out,
    1712      "DAC period size in milliseconds", NULL, 0},
    1713     {"ADC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_in,
    1714      "ADC period size in milliseconds", NULL, 0}
    1715 };
    1716 #endif
    1717 
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette