VirtualBox

Changeset 88450 in vbox


Ignore:
Timestamp:
Apr 9, 2021 8:00:48 PM (4 years ago)
Author:
vboxsync
Message:

DrvHostAudioAlsa: Put the functions in a more logical order, with workers closer to their callers. bugref:9890

File:
1 edited

Legend:

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

    r88449 r88450  
    6464
    6565/*********************************************************************************************************************************
    66 *   Defines                                                                                                                      *
    67 *********************************************************************************************************************************/
    68 
    69 /** Makes DRVHOSTALSAAUDIO out of PDMIHOSTAUDIO. */
    70 #define PDMIHOSTAUDIO_2_DRVHOSTALSAAUDIO(pInterface) \
    71     ( (PDRVHOSTALSAAUDIO)((uintptr_t)pInterface - RT_UOFFSETOF(DRVHOSTALSAAUDIO, IHostAudio)) )
    72 
    73 
    74 /*********************************************************************************************************************************
    7566*   Structures                                                                                                                   *
    7667*********************************************************************************************************************************/
    77 
    7868/**
    7969 * Structure for maintaining an ALSA audio stream.
     
    139129
    140130
     131
     132/**
     133 * Closes an ALSA stream
     134 *
     135 * @returns VBox status code.
     136 * @param   pphPCM              ALSA stream to close.
     137 */
     138static int alsaStreamClose(snd_pcm_t **pphPCM)
     139{
     140    if (!pphPCM || !*pphPCM)
     141        return VINF_SUCCESS;
     142
     143    int rc;
     144    int rc2 = snd_pcm_close(*pphPCM);
     145    if (rc2)
     146    {
     147        LogRel(("ALSA: Closing PCM descriptor failed: %s\n", snd_strerror(rc2)));
     148        rc = VERR_GENERAL_FAILURE; /** @todo */
     149    }
     150    else
     151    {
     152        *pphPCM = NULL;
     153        rc = VINF_SUCCESS;
     154    }
     155
     156    LogFlowFuncLeaveRC(rc);
     157    return rc;
     158}
     159
     160
     161#ifdef DEBUG
     162static void alsaDbgErrorHandler(const char *file, int line, const char *function,
     163                                int err, const char *fmt, ...)
     164{
     165    /** @todo Implement me! */
     166    RT_NOREF(file, line, function, err, fmt);
     167}
     168#endif
     169
     170
     171/**
     172 * Tries to recover an ALSA stream.
     173 *
     174 * @returns VBox status code.
     175 * @param   phPCM               ALSA stream handle.
     176 */
     177static int alsaStreamRecover(snd_pcm_t *phPCM)
     178{
     179    AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
     180
     181    int err = snd_pcm_prepare(phPCM);
     182    if (err < 0)
     183    {
     184        LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err)));
     185        return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     186    }
     187
     188    return VINF_SUCCESS;
     189}
     190
     191/**
     192 * Resumes an ALSA stream.
     193 *
     194 * @returns VBox status code.
     195 * @param   phPCM               ALSA stream to resume.
     196 */
     197static int alsaStreamResume(snd_pcm_t *phPCM)
     198{
     199    AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
     200
     201    int err = snd_pcm_resume(phPCM);
     202    if (err < 0)
     203    {
     204        LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err)));
     205        return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     206    }
     207
     208    return VINF_SUCCESS;
     209}
     210
     211
     212/**
     213 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
     214 */
     215static DECLCALLBACK(int) drvHostAlsaAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
     216{
     217    RT_NOREF(pInterface);
     218    AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
     219
     220    RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "ALSA");
     221
     222    pBackendCfg->cbStreamIn  = sizeof(ALSAAUDIOSTREAM);
     223    pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAM);
     224
     225    /* Enumerate sound devices. */
     226    char **pszHints;
     227    int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints);
     228    if (err == 0)
     229    {
     230        char** pszHintCur = pszHints;
     231        while (*pszHintCur != NULL)
     232        {
     233            char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME");
     234            bool fSkip =    !pszDev
     235                         || !RTStrICmp("null", pszDev);
     236            if (fSkip)
     237            {
     238                if (pszDev)
     239                    free(pszDev);
     240                pszHintCur++;
     241                continue;
     242            }
     243
     244            char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID");
     245            if (pszIOID)
     246            {
     247#if 0
     248                if (!RTStrICmp("input", pszIOID))
     249
     250                else if (!RTStrICmp("output", pszIOID))
     251#endif
     252            }
     253            else /* NULL means bidirectional, input + output. */
     254            {
     255            }
     256
     257            LogRel2(("ALSA: Found %s device: %s\n", pszIOID ?  RTStrToLower(pszIOID) : "bidirectional", pszDev));
     258
     259            /* Special case for ALSAAudio. */
     260            if (   pszDev
     261                && RTStrIStr("pulse", pszDev) != NULL)
     262                LogRel2(("ALSA: ALSAAudio plugin in use\n"));
     263
     264            if (pszIOID)
     265                free(pszIOID);
     266
     267            if (pszDev)
     268                free(pszDev);
     269
     270            pszHintCur++;
     271        }
     272
     273        snd_device_name_free_hint((void **)pszHints);
     274        pszHints = NULL;
     275    }
     276    else
     277        LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err));
     278
     279    /* ALSA allows exactly one input and one output used at a time for the selected device(s). */
     280    pBackendCfg->cMaxStreamsIn  = 1;
     281    pBackendCfg->cMaxStreamsOut = 1;
     282
     283    return VINF_SUCCESS;
     284}
     285
     286
     287/**
     288 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
     289 */
     290static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAlsaAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
     291{
     292    RT_NOREF(enmDir);
     293    AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
     294
     295    return PDMAUDIOBACKENDSTS_RUNNING;
     296}
     297
     298
    141299/**
    142300 * Converts internal audio PCM properties to an ALSA PCM format.
     
    228386    }
    229387    return VINF_SUCCESS;
    230 }
    231 
    232 
    233 /**
    234  * Closes an ALSA stream
    235  *
    236  * @returns VBox status code.
    237  * @param   pphPCM              ALSA stream to close.
    238  */
    239 static int alsaStreamClose(snd_pcm_t **pphPCM)
    240 {
    241     if (!pphPCM || !*pphPCM)
    242         return VINF_SUCCESS;
    243 
    244     int rc;
    245     int rc2 = snd_pcm_close(*pphPCM);
    246     if (rc2)
    247     {
    248         LogRel(("ALSA: Closing PCM descriptor failed: %s\n", snd_strerror(rc2)));
    249         rc = VERR_GENERAL_FAILURE; /** @todo */
    250     }
    251     else
    252     {
    253         *pphPCM = NULL;
    254         rc = VINF_SUCCESS;
    255     }
    256 
    257     LogFlowFuncLeaveRC(rc);
    258     return rc;
    259388}
    260389
     
    476605
    477606
    478 #ifdef DEBUG
    479 static void alsaDbgErrorHandler(const char *file, int line, const char *function,
    480                                 int err, const char *fmt, ...)
    481 {
    482     /** @todo Implement me! */
    483     RT_NOREF(file, line, function, err, fmt);
    484 }
    485 #endif
     607/**
     608 * Creates an ALSA output stream.
     609 *
     610 * @returns VBox status code.
     611 * @param   pThis       The ALSA driver instance data.
     612 * @param   pStreamALSA ALSA output stream to create.
     613 * @param   pCfgReq     Requested configuration to create stream with.
     614 * @param   pCfgAcq     Obtained configuration the stream got created
     615 *                      with on success.
     616 */
     617static int alsaCreateStreamOut(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,
     618                               PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     619{
     620    snd_pcm_t *phPCM = NULL;
     621
     622    int rc;
     623
     624    do
     625    {
     626        ALSAAUDIOSTREAMCFG req;
     627        req.fmt         = alsaAudioPropsToALSA(&pCfgReq->Props);
     628        req.freq        = PDMAudioPropsHz(&pCfgReq->Props);
     629        req.nchannels   = PDMAudioPropsChannels(&pCfgReq->Props);
     630        req.period_size = pCfgReq->Backend.cFramesPeriod;
     631        req.buffer_size = pCfgReq->Backend.cFramesBufferSize;
     632        req.threshold   = pCfgReq->Backend.cFramesPreBuffering;
     633
     634        ALSAAUDIOSTREAMCFG obt;
     635        rc = alsaStreamOpen(pThis->szDefaultOut, false /* fIn */, &req, &obt, &phPCM);
     636        if (RT_FAILURE(rc))
     637            break;
     638
     639        rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq);
     640        if (RT_FAILURE(rc))
     641            break;
     642
     643        pCfgAcq->Backend.cFramesPeriod     = obt.period_size;
     644        pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
     645        pCfgAcq->Backend.cFramesPreBuffering     = obt.threshold;
     646
     647        pStreamALSA->phPCM = phPCM;
     648    }
     649    while (0);
     650
     651    if (RT_FAILURE(rc))
     652        alsaStreamClose(&phPCM);
     653
     654    LogFlowFuncLeaveRC(rc);
     655    return rc;
     656}
     657
     658
     659/**
     660 * Creates an ALSA input stream.
     661 *
     662 * @returns VBox status code.
     663 * @param   pThis       The ALSA driver instance data.
     664 * @param   pStreamALSA ALSA input stream to create.
     665 * @param   pCfgReq     Requested configuration to create stream with.
     666 * @param   pCfgAcq     Obtained configuration the stream got created
     667 *                      with on success.
     668 */
     669static int alsaCreateStreamIn(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,
     670                              PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     671{
     672    int rc;
     673
     674    snd_pcm_t *phPCM = NULL;
     675
     676    do
     677    {
     678        ALSAAUDIOSTREAMCFG req;
     679        req.fmt         = alsaAudioPropsToALSA(&pCfgReq->Props);
     680        req.freq        = PDMAudioPropsHz(&pCfgReq->Props);
     681        req.nchannels   = PDMAudioPropsChannels(&pCfgReq->Props);
     682        req.period_size = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 50 /*ms*/); /** @todo Make this configurable. */
     683        req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */
     684        req.threshold   = req.period_size;
     685
     686        ALSAAUDIOSTREAMCFG obt;
     687        rc = alsaStreamOpen(pThis->szDefaultIn, true /* fIn */, &req, &obt, &phPCM);
     688        if (RT_FAILURE(rc))
     689            break;
     690
     691        rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq);
     692        if (RT_FAILURE(rc))
     693            break;
     694
     695        pCfgAcq->Backend.cFramesPeriod     = obt.period_size;
     696        pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
     697        pCfgAcq->Backend.cFramesPreBuffering = 0; /* No pre-buffering. */
     698
     699        pStreamALSA->phPCM = phPCM;
     700    }
     701    while (0);
     702
     703    if (RT_FAILURE(rc))
     704        alsaStreamClose(&phPCM);
     705
     706    LogFlowFuncLeaveRC(rc);
     707    return rc;
     708}
     709
     710
     711/**
     712 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
     713 */
     714static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     715                                                         PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     716{
     717    PDRVHOSTALSAAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTALSAAUDIO, IHostAudio);
     718    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     719    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
     720    AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
     721    AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
     722
     723    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     724
     725    int rc;
     726    if (pCfgReq->enmDir == PDMAUDIODIR_IN)
     727        rc = alsaCreateStreamIn( pThis, pStreamALSA, pCfgReq, pCfgAcq);
     728    else
     729        rc = alsaCreateStreamOut(pThis, pStreamALSA, pCfgReq, pCfgAcq);
     730    if (RT_SUCCESS(rc))
     731    {
     732        pStreamALSA->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
     733        if (!pStreamALSA->pCfg)
     734            rc = VERR_NO_MEMORY;
     735    }
     736
     737    return rc;
     738}
     739
     740
     741/**
     742 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
     743 */
     744static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     745{
     746    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     747    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
     748
     749    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     750
     751    if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
     752        return VINF_SUCCESS;
     753
     754    int rc = alsaStreamClose(&pStreamALSA->phPCM);
     755    /** @todo r=bird: It's not like we can do much with a bad status... Check
     756     *        what the caller does... */
     757    if (RT_SUCCESS(rc))
     758    {
     759        PDMAudioStrmCfgFree(pStreamALSA->pCfg);
     760        pStreamALSA->pCfg = NULL;
     761    }
     762
     763    return rc;
     764}
     765
     766
     767/**
     768 * Controls an ALSA input stream.
     769 *
     770 * @returns VBox status code.
     771 * @param   pStreamALSA         ALSA input stream to control.
     772 * @param   enmStreamCmd        Stream command to issue.
     773 */
     774static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
     775{
     776    int rc = VINF_SUCCESS;
     777
     778    int err;
     779
     780    switch (enmStreamCmd)
     781    {
     782        case PDMAUDIOSTREAMCMD_ENABLE:
     783        case PDMAUDIOSTREAMCMD_RESUME:
     784        {
     785            err = snd_pcm_prepare(pStreamALSA->phPCM);
     786            if (err < 0)
     787            {
     788                LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err)));
     789                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     790            }
     791            else
     792            {
     793                Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
     794
     795                /* Only start the PCM stream for input streams. */
     796                err = snd_pcm_start(pStreamALSA->phPCM);
     797                if (err < 0)
     798                {
     799                    LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));
     800                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     801                }
     802            }
     803
     804            break;
     805        }
     806
     807        case PDMAUDIOSTREAMCMD_DISABLE:
     808        {
     809            err = snd_pcm_drop(pStreamALSA->phPCM);
     810            if (err < 0)
     811            {
     812                LogRel(("ALSA: Error disabling input stream: %s\n", snd_strerror(err)));
     813                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     814            }
     815            break;
     816        }
     817
     818        case PDMAUDIOSTREAMCMD_PAUSE:
     819        {
     820            err = snd_pcm_drop(pStreamALSA->phPCM);
     821            if (err < 0)
     822            {
     823                LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err)));
     824                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     825            }
     826            break;
     827        }
     828
     829        default:
     830            rc = VERR_NOT_SUPPORTED;
     831            break;
     832    }
     833
     834    LogFlowFuncLeaveRC(rc);
     835    return rc;
     836}
     837
     838
     839/**
     840 * Controls an ALSA output stream.
     841 *
     842 * @returns VBox status code.
     843 * @param   pStreamALSA         ALSA output stream to control.
     844 * @param   enmStreamCmd        Stream command to issue.
     845 */
     846static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
     847{
     848    int rc = VINF_SUCCESS;
     849
     850    int err;
     851
     852    switch (enmStreamCmd)
     853    {
     854        case PDMAUDIOSTREAMCMD_ENABLE:
     855        case PDMAUDIOSTREAMCMD_RESUME:
     856        {
     857            err = snd_pcm_prepare(pStreamALSA->phPCM);
     858            if (err < 0)
     859            {
     860                LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err)));
     861                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     862            }
     863            else
     864            {
     865                Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
     866            }
     867
     868            break;
     869        }
     870
     871        case PDMAUDIOSTREAMCMD_DISABLE:
     872        {
     873            err = snd_pcm_drop(pStreamALSA->phPCM);
     874            if (err < 0)
     875            {
     876                LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err)));
     877                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     878            }
     879            break;
     880        }
     881
     882        case PDMAUDIOSTREAMCMD_PAUSE:
     883        {
     884            /** @todo shouldn't this try snd_pcm_pause first? */
     885            err = snd_pcm_drop(pStreamALSA->phPCM);
     886            if (err < 0)
     887            {
     888                LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err)));
     889                rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     890            }
     891            break;
     892        }
     893
     894        case PDMAUDIOSTREAMCMD_DRAIN:
     895        {
     896            snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM);
     897            Log2Func(("Stream state is: %d\n", streamState));
     898
     899            if (   streamState == SND_PCM_STATE_PREPARED
     900                || streamState == SND_PCM_STATE_RUNNING)
     901            {
     902                /** @todo r=bird: You want EMT to block here for potentially 200-300ms worth
     903                 *        of buffer to be drained?  That's a certifiably bad idea.  */
     904                err = snd_pcm_nonblock(pStreamALSA->phPCM, 0);
     905                if (err < 0)
     906                {
     907                    LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err)));
     908                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     909                    break;
     910                }
     911
     912                err = snd_pcm_drain(pStreamALSA->phPCM);
     913                if (err < 0)
     914                {
     915                    LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err)));
     916                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     917                    break;
     918                }
     919
     920                err = snd_pcm_nonblock(pStreamALSA->phPCM, 1);
     921                if (err < 0)
     922                {
     923                    LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err)));
     924                    rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
     925                }
     926            }
     927            break;
     928        }
     929
     930        default:
     931            rc = VERR_NOT_SUPPORTED;
     932            break;
     933    }
     934
     935    LogFlowFuncLeaveRC(rc);
     936    return rc;
     937}
     938
     939
     940/**
     941 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
     942 */
     943static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
     944                                                          PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
     945{
     946    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     947    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
     948
     949    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     950
     951    if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
     952        return VINF_SUCCESS;
     953
     954    int rc;
     955    if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
     956        rc = alsaControlStreamIn (pStreamALSA, enmStreamCmd);
     957    else
     958        rc = alsaControlStreamOut(pStreamALSA, enmStreamCmd);
     959
     960    return rc;
     961}
     962
    486963
    487964/**
     
    5321009}
    5331010
    534 /**
    535  * Tries to recover an ALSA stream.
    536  *
    537  * @returns VBox status code.
    538  * @param   phPCM               ALSA stream handle.
    539  */
    540 static int alsaStreamRecover(snd_pcm_t *phPCM)
    541 {
    542     AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
    543 
    544     int err = snd_pcm_prepare(phPCM);
    545     if (err < 0)
    546     {
    547         LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err)));
    548         return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    549     }
    550 
    551     return VINF_SUCCESS;
    552 }
    553 
    554 /**
    555  * Resumes an ALSA stream.
    556  *
    557  * @returns VBox status code.
    558  * @param   phPCM               ALSA stream to resume.
    559  */
    560 static int alsaStreamResume(snd_pcm_t *phPCM)
    561 {
    562     AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
    563 
    564     int err = snd_pcm_resume(phPCM);
    565     if (err < 0)
    566     {
    567         LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err)));
    568         return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    569     }
    570 
    571     return VINF_SUCCESS;
    572 }
     1011
     1012/**
     1013 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
     1014 */
     1015static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1016{
     1017    RT_NOREF(pInterface);
     1018
     1019    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     1020
     1021    uint32_t cbAvail = 0;
     1022
     1023    snd_pcm_sframes_t cFramesAvail;
     1024    int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
     1025    if (RT_SUCCESS(rc))
     1026        cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
     1027
     1028    return cbAvail;
     1029}
     1030
     1031
     1032/**
     1033 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
     1034 */
     1035static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1036{
     1037    RT_NOREF(pInterface);
     1038
     1039    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     1040
     1041    uint32_t cbAvail = 0;
     1042
     1043    snd_pcm_sframes_t cFramesAvail;
     1044    int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
     1045    if (RT_SUCCESS(rc))
     1046        cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
     1047
     1048    return cbAvail;
     1049}
     1050
     1051
     1052/**
     1053 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
     1054 */
     1055static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1056{
     1057    RT_NOREF(pInterface);
     1058    PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
     1059    AssertPtrReturn(pStreamALSA, 0);
     1060    AssertPtr(pStreamALSA->pCfg);
     1061
     1062    /*
     1063     * This is only relevant to output streams (input streams can't have
     1064     * any pending, unplayed data).
     1065     */
     1066    uint32_t cbPending = 0;
     1067    if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT)
     1068    {
     1069        /*
     1070         * Getting the delay (in audio frames) reports the time it will take
     1071         * to hear a new sample after all queued samples have been played out.
     1072         *
     1073         * We use snd_pcm_avail_delay instead of snd_pcm_delay here as it will
     1074         * update the buffer positions, and we can use the extra value against
     1075         * the buffer size to double check since the delay value may include
     1076         * fixed built-in delays in the processing chain and hardware.
     1077         */
     1078        snd_pcm_sframes_t cFramesAvail = 0;
     1079        snd_pcm_sframes_t cFramesDelay = 0;
     1080        int rc = snd_pcm_avail_delay(pStreamALSA->phPCM, &cFramesAvail, &cFramesDelay);
     1081
     1082        /*
     1083         * We now also get the state as the pending value should be zero when
     1084         * we're not in a playing state.
     1085         */
     1086        snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM);
     1087        switch (enmState)
     1088        {
     1089            case SND_PCM_STATE_RUNNING:
     1090            case SND_PCM_STATE_DRAINING:
     1091                if (rc >= 0)
     1092                {
     1093                    if (cFramesAvail >= pStreamALSA->pCfg->Backend.cFramesBufferSize)
     1094                        cbPending = 0;
     1095                    else
     1096                        cbPending = PDMAudioPropsFramesToBytes(&pStreamALSA->pCfg->Props, cFramesDelay);
     1097                }
     1098                break;
     1099
     1100            default:
     1101                break;
     1102        }
     1103        Log2Func(("returns %u (%#x) - cFramesBufferSize=%RU32 cFramesAvail=%ld cFramesDelay=%ld rc=%d; enmState=%s (%d) \n",
     1104                  cbPending, cbPending, pStreamALSA->pCfg->Backend.cFramesBufferSize, cFramesAvail, cFramesDelay, rc,
     1105                  snd_pcm_state_name(enmState), enmState));
     1106    }
     1107    return cbPending;
     1108}
     1109
     1110
     1111/**
     1112 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
     1113 */
     1114static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAlsaAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1115{
     1116    RT_NOREF(pInterface, pStream);
     1117
     1118    return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
     1119}
     1120
    5731121
    5741122/**
     
    8141362}
    8151363
    816 /**
    817  * Creates an ALSA output stream.
    818  *
    819  * @returns VBox status code.
    820  * @param   pThis       The ALSA driver instance data.
    821  * @param   pStreamALSA ALSA output stream to create.
    822  * @param   pCfgReq     Requested configuration to create stream with.
    823  * @param   pCfgAcq     Obtained configuration the stream got created
    824  *                      with on success.
    825  */
    826 static int alsaCreateStreamOut(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,
    827                                PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    828 {
    829     snd_pcm_t *phPCM = NULL;
    830 
    831     int rc;
    832 
    833     do
    834     {
    835         ALSAAUDIOSTREAMCFG req;
    836         req.fmt         = alsaAudioPropsToALSA(&pCfgReq->Props);
    837         req.freq        = PDMAudioPropsHz(&pCfgReq->Props);
    838         req.nchannels   = PDMAudioPropsChannels(&pCfgReq->Props);
    839         req.period_size = pCfgReq->Backend.cFramesPeriod;
    840         req.buffer_size = pCfgReq->Backend.cFramesBufferSize;
    841         req.threshold   = pCfgReq->Backend.cFramesPreBuffering;
    842 
    843         ALSAAUDIOSTREAMCFG obt;
    844         rc = alsaStreamOpen(pThis->szDefaultOut, false /* fIn */, &req, &obt, &phPCM);
    845         if (RT_FAILURE(rc))
    846             break;
    847 
    848         rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq);
    849         if (RT_FAILURE(rc))
    850             break;
    851 
    852         pCfgAcq->Backend.cFramesPeriod     = obt.period_size;
    853         pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
    854         pCfgAcq->Backend.cFramesPreBuffering     = obt.threshold;
    855 
    856         pStreamALSA->phPCM = phPCM;
    857     }
    858     while (0);
    859 
    860     if (RT_FAILURE(rc))
    861         alsaStreamClose(&phPCM);
    862 
    863     LogFlowFuncLeaveRC(rc);
    864     return rc;
    865 }
    866 
    867 /**
    868  * Creates an ALSA input stream.
    869  *
    870  * @returns VBox status code.
    871  * @param   pThis       The ALSA driver instance data.
    872  * @param   pStreamALSA ALSA input stream to create.
    873  * @param   pCfgReq     Requested configuration to create stream with.
    874  * @param   pCfgAcq     Obtained configuration the stream got created
    875  *                      with on success.
    876  */
    877 static int alsaCreateStreamIn(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,
    878                               PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    879 {
    880     int rc;
    881 
    882     snd_pcm_t *phPCM = NULL;
    883 
    884     do
    885     {
    886         ALSAAUDIOSTREAMCFG req;
    887         req.fmt         = alsaAudioPropsToALSA(&pCfgReq->Props);
    888         req.freq        = PDMAudioPropsHz(&pCfgReq->Props);
    889         req.nchannels   = PDMAudioPropsChannels(&pCfgReq->Props);
    890         req.period_size = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 50 /*ms*/); /** @todo Make this configurable. */
    891         req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */
    892         req.threshold   = req.period_size;
    893 
    894         ALSAAUDIOSTREAMCFG obt;
    895         rc = alsaStreamOpen(pThis->szDefaultIn, true /* fIn */, &req, &obt, &phPCM);
    896         if (RT_FAILURE(rc))
    897             break;
    898 
    899         rc = alsaALSAToAudioProps(&pCfgAcq->Props, obt.fmt, obt.nchannels, obt.freq);
    900         if (RT_FAILURE(rc))
    901             break;
    902 
    903         pCfgAcq->Backend.cFramesPeriod     = obt.period_size;
    904         pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
    905         pCfgAcq->Backend.cFramesPreBuffering = 0; /* No pre-buffering. */
    906 
    907         pStreamALSA->phPCM = phPCM;
    908     }
    909     while (0);
    910 
    911     if (RT_FAILURE(rc))
    912         alsaStreamClose(&phPCM);
    913 
    914     LogFlowFuncLeaveRC(rc);
    915     return rc;
    916 }
    917 
    918 /**
    919  * Controls an ALSA input stream.
    920  *
    921  * @returns VBox status code.
    922  * @param   pStreamALSA         ALSA input stream to control.
    923  * @param   enmStreamCmd        Stream command to issue.
    924  */
    925 static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
    926 {
    927     int rc = VINF_SUCCESS;
    928 
    929     int err;
    930 
    931     switch (enmStreamCmd)
    932     {
    933         case PDMAUDIOSTREAMCMD_ENABLE:
    934         case PDMAUDIOSTREAMCMD_RESUME:
    935         {
    936             err = snd_pcm_prepare(pStreamALSA->phPCM);
    937             if (err < 0)
    938             {
    939                 LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err)));
    940                 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    941             }
    942             else
    943             {
    944                 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
    945 
    946                 /* Only start the PCM stream for input streams. */
    947                 err = snd_pcm_start(pStreamALSA->phPCM);
    948                 if (err < 0)
    949                 {
    950                     LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));
    951                     rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    952                 }
    953             }
    954 
    955             break;
    956         }
    957 
    958         case PDMAUDIOSTREAMCMD_DISABLE:
    959         {
    960             err = snd_pcm_drop(pStreamALSA->phPCM);
    961             if (err < 0)
    962             {
    963                 LogRel(("ALSA: Error disabling input stream: %s\n", snd_strerror(err)));
    964                 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    965             }
    966             break;
    967         }
    968 
    969         case PDMAUDIOSTREAMCMD_PAUSE:
    970         {
    971             err = snd_pcm_drop(pStreamALSA->phPCM);
    972             if (err < 0)
    973             {
    974                 LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err)));
    975                 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    976             }
    977             break;
    978         }
    979 
    980         default:
    981             rc = VERR_NOT_SUPPORTED;
    982             break;
    983     }
    984 
    985     LogFlowFuncLeaveRC(rc);
    986     return rc;
    987 }
    988 
    989 /**
    990  * Controls an ALSA output stream.
    991  *
    992  * @returns VBox status code.
    993  * @param   pStreamALSA         ALSA output stream to control.
    994  * @param   enmStreamCmd        Stream command to issue.
    995  */
    996 static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
    997 {
    998     int rc = VINF_SUCCESS;
    999 
    1000     int err;
    1001 
    1002     switch (enmStreamCmd)
    1003     {
    1004         case PDMAUDIOSTREAMCMD_ENABLE:
    1005         case PDMAUDIOSTREAMCMD_RESUME:
    1006         {
    1007             err = snd_pcm_prepare(pStreamALSA->phPCM);
    1008             if (err < 0)
    1009             {
    1010                 LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err)));
    1011                 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    1012             }
    1013             else
    1014             {
    1015                 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
    1016             }
    1017 
    1018             break;
    1019         }
    1020 
    1021         case PDMAUDIOSTREAMCMD_DISABLE:
    1022         {
    1023             err = snd_pcm_drop(pStreamALSA->phPCM);
    1024             if (err < 0)
    1025             {
    1026                 LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err)));
    1027                 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    1028             }
    1029             break;
    1030         }
    1031 
    1032         case PDMAUDIOSTREAMCMD_PAUSE:
    1033         {
    1034             /** @todo shouldn't this try snd_pcm_pause first? */
    1035             err = snd_pcm_drop(pStreamALSA->phPCM);
    1036             if (err < 0)
    1037             {
    1038                 LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err)));
    1039                 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    1040             }
    1041             break;
    1042         }
    1043 
    1044         case PDMAUDIOSTREAMCMD_DRAIN:
    1045         {
    1046             snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM);
    1047             Log2Func(("Stream state is: %d\n", streamState));
    1048 
    1049             if (   streamState == SND_PCM_STATE_PREPARED
    1050                 || streamState == SND_PCM_STATE_RUNNING)
    1051             {
    1052                 /** @todo r=bird: You want EMT to block here for potentially 200-300ms worth
    1053                  *        of buffer to be drained?  That's a certifiably bad idea.  */
    1054                 err = snd_pcm_nonblock(pStreamALSA->phPCM, 0);
    1055                 if (err < 0)
    1056                 {
    1057                     LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err)));
    1058                     rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    1059                     break;
    1060                 }
    1061 
    1062                 err = snd_pcm_drain(pStreamALSA->phPCM);
    1063                 if (err < 0)
    1064                 {
    1065                     LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err)));
    1066                     rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    1067                     break;
    1068                 }
    1069 
    1070                 err = snd_pcm_nonblock(pStreamALSA->phPCM, 1);
    1071                 if (err < 0)
    1072                 {
    1073                     LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err)));
    1074                     rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
    1075                 }
    1076             }
    1077             break;
    1078         }
    1079 
    1080         default:
    1081             rc = VERR_NOT_SUPPORTED;
    1082             break;
    1083     }
    1084 
    1085     LogFlowFuncLeaveRC(rc);
    1086     return rc;
    1087 }
    1088 
    1089 
    1090 /**
    1091  * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
    1092  */
    1093 static DECLCALLBACK(int) drvHostAlsaAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
    1094 {
    1095     RT_NOREF(pInterface);
    1096     AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
    1097 
    1098     RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "ALSA");
    1099 
    1100     pBackendCfg->cbStreamIn  = sizeof(ALSAAUDIOSTREAM);
    1101     pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAM);
    1102 
    1103     /* Enumerate sound devices. */
    1104     char **pszHints;
    1105     int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints);
    1106     if (err == 0)
    1107     {
    1108         char** pszHintCur = pszHints;
    1109         while (*pszHintCur != NULL)
    1110         {
    1111             char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME");
    1112             bool fSkip =    !pszDev
    1113                          || !RTStrICmp("null", pszDev);
    1114             if (fSkip)
    1115             {
    1116                 if (pszDev)
    1117                     free(pszDev);
    1118                 pszHintCur++;
    1119                 continue;
    1120             }
    1121 
    1122             char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID");
    1123             if (pszIOID)
    1124             {
    1125 #if 0
    1126                 if (!RTStrICmp("input", pszIOID))
    1127 
    1128                 else if (!RTStrICmp("output", pszIOID))
    1129 #endif
    1130             }
    1131             else /* NULL means bidirectional, input + output. */
    1132             {
    1133             }
    1134 
    1135             LogRel2(("ALSA: Found %s device: %s\n", pszIOID ?  RTStrToLower(pszIOID) : "bidirectional", pszDev));
    1136 
    1137             /* Special case for ALSAAudio. */
    1138             if (   pszDev
    1139                 && RTStrIStr("pulse", pszDev) != NULL)
    1140                 LogRel2(("ALSA: ALSAAudio plugin in use\n"));
    1141 
    1142             if (pszIOID)
    1143                 free(pszIOID);
    1144 
    1145             if (pszDev)
    1146                 free(pszDev);
    1147 
    1148             pszHintCur++;
    1149         }
    1150 
    1151         snd_device_name_free_hint((void **)pszHints);
    1152         pszHints = NULL;
    1153     }
    1154     else
    1155         LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err));
    1156 
    1157     /* ALSA allows exactly one input and one output used at a time for the selected device(s). */
    1158     pBackendCfg->cMaxStreamsIn  = 1;
    1159     pBackendCfg->cMaxStreamsOut = 1;
    1160 
    1161     return VINF_SUCCESS;
    1162 }
    1163 
    1164 
    1165 /**
    1166  * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
    1167  */
    1168 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAlsaAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
    1169 {
    1170     RT_NOREF(enmDir);
    1171     AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
    1172 
    1173     return PDMAUDIOBACKENDSTS_RUNNING;
    1174 }
    1175 
    1176 
    1177 /**
    1178  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
    1179  */
    1180 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    1181                                                          PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    1182 {
    1183     PDRVHOSTALSAAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTALSAAUDIO, IHostAudio);
    1184     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1185     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    1186     AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
    1187     AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
    1188 
    1189     PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
    1190 
    1191     int rc;
    1192     if (pCfgReq->enmDir == PDMAUDIODIR_IN)
    1193         rc = alsaCreateStreamIn( pThis, pStreamALSA, pCfgReq, pCfgAcq);
    1194     else
    1195         rc = alsaCreateStreamOut(pThis, pStreamALSA, pCfgReq, pCfgAcq);
    1196 
    1197     if (RT_SUCCESS(rc))
    1198     {
    1199         pStreamALSA->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
    1200         if (!pStreamALSA->pCfg)
    1201             rc = VERR_NO_MEMORY;
    1202     }
    1203 
    1204     return rc;
    1205 }
    1206 
    1207 
    1208 /**
    1209  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
    1210  */
    1211 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1212 {
    1213     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1214     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    1215 
    1216     PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
    1217 
    1218     if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
    1219         return VINF_SUCCESS;
    1220 
    1221     int rc = alsaStreamClose(&pStreamALSA->phPCM);
    1222     /** @todo r=bird: It's not like we can do much with a bad status... Check
    1223      *        what the caller does... */
    1224     if (RT_SUCCESS(rc))
    1225     {
    1226         PDMAudioStrmCfgFree(pStreamALSA->pCfg);
    1227         pStreamALSA->pCfg = NULL;
    1228     }
    1229 
    1230     return rc;
    1231 }
    1232 
    1233 
    1234 /**
    1235  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
    1236  */
    1237 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
    1238                                                           PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
    1239 {
    1240     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1241     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    1242 
    1243     PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
    1244 
    1245     if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
    1246         return VINF_SUCCESS;
    1247 
    1248     int rc;
    1249     if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
    1250         rc = alsaControlStreamIn (pStreamALSA, enmStreamCmd);
    1251     else
    1252         rc = alsaControlStreamOut(pStreamALSA, enmStreamCmd);
    1253 
    1254     return rc;
    1255 }
    1256 
    1257 
    1258 /**
    1259  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
    1260  */
    1261 static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1262 {
    1263     RT_NOREF(pInterface);
    1264 
    1265     PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
    1266 
    1267     uint32_t cbAvail = 0;
    1268 
    1269     snd_pcm_sframes_t cFramesAvail;
    1270     int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
    1271     if (RT_SUCCESS(rc))
    1272         cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
    1273 
    1274     return cbAvail;
    1275 }
    1276 
    1277 
    1278 /**
    1279  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
    1280  */
    1281 static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1282 {
    1283     RT_NOREF(pInterface);
    1284 
    1285     PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
    1286 
    1287     uint32_t cbAvail = 0;
    1288 
    1289     snd_pcm_sframes_t cFramesAvail;
    1290     int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
    1291     if (RT_SUCCESS(rc))
    1292         cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
    1293 
    1294     return cbAvail;
    1295 }
    1296 
    1297 
    1298 /**
    1299  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
    1300  */
    1301 static DECLCALLBACK(uint32_t) drvHostALSAStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1302 {
    1303     RT_NOREF(pInterface);
    1304     PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
    1305     AssertPtrReturn(pStreamALSA, 0);
    1306     AssertPtr(pStreamALSA->pCfg);
    1307 
    1308     /*
    1309      * This is only relevant to output streams (input streams can't have
    1310      * any pending, unplayed data).
    1311      */
    1312     uint32_t cbPending = 0;
    1313     if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT)
    1314     {
    1315         /*
    1316          * Getting the delay (in audio frames) reports the time it will take
    1317          * to hear a new sample after all queued samples have been played out.
    1318          *
    1319          * We use snd_pcm_avail_delay instead of snd_pcm_delay here as it will
    1320          * update the buffer positions, and we can use the extra value against
    1321          * the buffer size to double check since the delay value may include
    1322          * fixed built-in delays in the processing chain and hardware.
    1323          */
    1324         snd_pcm_sframes_t cFramesAvail = 0;
    1325         snd_pcm_sframes_t cFramesDelay = 0;
    1326         int rc = snd_pcm_avail_delay(pStreamALSA->phPCM, &cFramesAvail, &cFramesDelay);
    1327 
    1328         /*
    1329          * We now also get the state as the pending value should be zero when
    1330          * we're not in a playing state.
    1331          */
    1332         snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM);
    1333         switch (enmState)
    1334         {
    1335             case SND_PCM_STATE_RUNNING:
    1336             case SND_PCM_STATE_DRAINING:
    1337                 if (rc >= 0)
    1338                 {
    1339                     if (cFramesAvail >= pStreamALSA->pCfg->Backend.cFramesBufferSize)
    1340                         cbPending = 0;
    1341                     else
    1342                         cbPending = PDMAudioPropsFramesToBytes(&pStreamALSA->pCfg->Props, cFramesDelay);
    1343                 }
    1344                 break;
    1345 
    1346             default:
    1347                 break;
    1348         }
    1349         Log2Func(("returns %u (%#x) - cFramesBufferSize=%RU32 cFramesAvail=%ld cFramesDelay=%ld rc=%d; enmState=%s (%d) \n",
    1350                   cbPending, cbPending, pStreamALSA->pCfg->Backend.cFramesBufferSize, cFramesAvail, cFramesDelay, rc,
    1351                   snd_pcm_state_name(enmState), enmState));
    1352     }
    1353     return cbPending;
    1354 }
    1355 
    1356 
    1357 /**
    1358  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
    1359  */
    1360 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAlsaAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    1361 {
    1362     RT_NOREF(pInterface, pStream);
    1363 
    1364     return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
    1365 }
    1366 
    13671364
    13681365/**
     
    14001397    /* IHostAudio */
    14011398    pThis->IHostAudio.pfnGetConfig          = drvHostAlsaAudioHA_GetConfig;
     1399    pThis->IHostAudio.pfnGetDevices         = NULL;
    14021400    pThis->IHostAudio.pfnGetStatus          = drvHostAlsaAudioHA_GetStatus;
    14031401    pThis->IHostAudio.pfnStreamCreate       = drvHostAlsaAudioHA_StreamCreate;
     
    14061404    pThis->IHostAudio.pfnStreamGetReadable  = drvHostAlsaAudioHA_StreamGetReadable;
    14071405    pThis->IHostAudio.pfnStreamGetWritable  = drvHostAlsaAudioHA_StreamGetWritable;
     1406    pThis->IHostAudio.pfnStreamGetPending   = drvHostAlsaAudioHA_StreamGetPending;
    14081407    pThis->IHostAudio.pfnStreamGetStatus    = drvHostAlsaAudioHA_StreamGetStatus;
    14091408    pThis->IHostAudio.pfnStreamPlay         = drvHostAlsaAudioHA_StreamPlay;
    14101409    pThis->IHostAudio.pfnStreamCapture      = drvHostAlsaAudioHA_StreamCapture;
    1411     pThis->IHostAudio.pfnGetDevices         = NULL;
    1412     pThis->IHostAudio.pfnStreamGetPending   = drvHostALSAStreamGetPending;
    14131410
    14141411    /*
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