VirtualBox

Changeset 89327 in vbox for trunk/src/VBox/Devices/Audio


Ignore:
Timestamp:
May 28, 2021 12:22:27 AM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
144676
Message:

Audio: Reworking the capture (recording) code path, part 4: Combine PDMIAUDIOCONNECTOR::pfnStreamCapture and PDMIAUDIOCONNECTOR::pfnStreamRead, remove PDMIAUDIOCONNECTOR::pfnStreamSetVoplume, eliminate mixer buffers in DrvAudio. Added pre-buffering of input streams (delay fetching samples from the backend till we've reached the desired buffer fill there). bugref:9890

Location:
trunk/src/VBox/Devices/Audio
Files:
2 edited

Legend:

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

    r89314 r89327  
    611611                 */
    612612                PPDMAUDIOSTREAM pStream;
    613                 rc = pConn->pfnStreamCreate(pConn, pSink->enmDir == PDMAUDIODIR_OUT ? PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF : 0,
    614                                             &CfgHost, pCfg, &pStream);
     613                rc = pConn->pfnStreamCreate(pConn, 0 /*fFlags*/, &CfgHost, pCfg, &pStream);
    615614                if (RT_SUCCESS(rc))
    616615                {
     
    14451444            PPDMIAUDIOCONNECTOR const pIConnector = pMixStream->pConn;
    14461445            PPDMAUDIOSTREAM const     pStream     = pMixStream->pStream;
    1447             uint32_t                  cIgnored    = 0;
    1448             pIConnector->pfnStreamCapture(pIConnector, pStream, &cIgnored);
    14491446            pIConnector->pfnStreamIterate(pIConnector, pStream);
    14501447
     
    14611458            if (cFramesToRead > cFrames && !pMixStream->fUnreliable)
    14621459            {
    1463                 Log4Func(("%s: cFramesToRead %u -> %u; %s (%u bytes writable)\n",
     1460                Log4Func(("%s: cFramesToRead %u -> %u; %s (%u bytes readable)\n",
    14641461                          pSink->pszName, cFramesToRead, cFrames, pMixStream->pszName, cbReadable));
    14651462                cFramesToRead = cFrames;
     
    16061603                        if (cbSrcToRead > 0)
    16071604                        {
    1608                             int rc2 = pIConnector->pfnStreamRead(pIConnector, pStream, pvBuf, cbSrcToRead, &cbSrcRead);
     1605                            int rc2 = pIConnector->pfnStreamCapture(pIConnector, pStream, pvBuf, cbSrcToRead, &cbSrcRead);
    16091606                            Log3Func(("%s: %#x L %#x => %#x bytes; rc2=%Rrc %s\n",
    16101607                                      pSink->pszName, offSrc, cbSrcToRead, cbSrcRead, rc2, pMixStream->pszName));
  • trunk/src/VBox/Devices/Audio/DrvAudio.cpp

    r89282 r89327  
    103103    /** The stream's audio configuration. */
    104104    PDMAUDIOSTREAMCFG   Cfg;
    105     /** This stream's mixing buffer. */
    106     AUDIOMIXBUF         MixBuf;
    107105} DRVAUDIOSTREAMCTX;
     106
     107/**
     108 * Capture state of a stream wrt backend.
     109 */
     110typedef enum DRVAUDIOCAPTURESTATE
     111{
     112    /** Invalid zero value.   */
     113    DRVAUDIOCAPTURESTATE_INVALID = 0,
     114    /** No capturing or pre-buffering.   */
     115    DRVAUDIOCAPTURESTATE_NO_CAPTURE,
     116    /** Regular capturing. */
     117    DRVAUDIOCAPTURESTATE_CAPTURING,
     118    /** Returning silence till the backend buffer has reched the configured
     119     *  pre-buffering level. */
     120    DRVAUDIOCAPTURESTATE_PREBUF,
     121    /** End of valid values. */
     122    DRVAUDIOCAPTURESTATE_END
     123} DRVAUDIOCAPTURESTATE;
    108124
    109125/**
     
    166182    PPDMAUDIOBACKENDSTREAM  pBackend;
    167183
    168     /** Do not use the mixing buffers (Guest::MixBuf, Host::MixBuf). */
    169     bool                    fNoMixBufs;
    170184    /** Set if pfnStreamCreate returned VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED. */
    171185    bool                    fNeedAsyncInit;
    172186    /** The fImmediate parameter value for pfnStreamDestroy. */
    173187    bool                    fDestroyImmediate;
    174     bool                    fPadding;
     188    bool                    afPadding[2];
    175189
    176190    /** Number of (re-)tries while re-initializing the stream. */
     
    181195    PDMHOSTAUDIOSTREAMSTATE enmLastBackendState;
    182196
     197    /** The pre-buffering threshold expressed in bytes. */
     198    uint32_t                cbPreBufThreshold;
     199
    183200    /** The pfnStreamInitAsync request handle. */
    184201    PRTREQ                  hReqInitAsync;
    185202
     203    /** @todo The guest and host fields only contains the stream config now that
     204     *        the mixing buffer is gone, so we can probably combine them into a
     205     *        single Cfg member. */
    186206    /** The guest side of the stream. */
    187207    DRVAUDIOSTREAMCTX       Guest;
     
    189209    DRVAUDIOSTREAMCTX       Host;
    190210
     211
     212    /** The nanosecond timestamp when the stream was started. */
     213    uint64_t                nsStarted;
     214    /** Internal stream position (as per pfnStreamPlay/pfnStreamCapture). */
     215    uint64_t                offInternal;
    191216
    192217    /** Timestamp (in ns) since last trying to re-initialize.
     
    200225     *  write (output streams). */
    201226    uint64_t                nsLastReadWritten;
    202     /** Internal stream position (as per pfnStreamWrite/Read). */
    203     uint64_t                offInternal;
     227
    204228
    205229    /** Union for input/output specifics depending on enmDir. */
     
    211235        struct
    212236        {
     237            /** The capture state. */
     238            DRVAUDIOCAPTURESTATE enmCaptureState;
     239
    213240            struct
    214241            {
    215                 /** File for writing stream reads. */
    216                 PAUDIOHLPFILE   pFileStreamRead;
    217242                /** File for writing non-interleaved captures. */
    218                 PAUDIOHLPFILE   pFileCaptureNonInterleaved;
     243                PAUDIOHLPFILE   pFileCapture;
    219244            } Dbg;
    220245            struct
    221246            {
     247                uint32_t        cbBackendReadableBefore;
     248                uint32_t        cbBackendReadableAfter;
     249
    222250                STAMCOUNTER     TotalFramesCaptured;
    223251                STAMCOUNTER     AvgFramesCaptured;
     
    236264            struct
    237265            {
    238                 /** File for writing stream writes. */
    239                 PAUDIOHLPFILE   pFileStreamWrite;
    240266                /** File for writing stream playback. */
    241                 PAUDIOHLPFILE   pFilePlayNonInterleaved;
     267                PAUDIOHLPFILE   pFilePlay;
    242268            } Dbg;
    243269            struct
     
    255281            /** Number of bytes we've pre-buffered. */
    256282            uint32_t            cbPreBuffered;
    257             /** The pre-buffering threshold expressed in bytes. */
    258             uint32_t            cbPreBufThreshold;
    259283            /** The play state. */
    260284            DRVAUDIOPLAYSTATE   enmPlayState;
     
    467491
    468492/**
    469  * Get pre-buffer state name string.
     493 * Get play state name string.
    470494 */
    471495static const char *drvAudioPlayStateName(DRVAUDIOPLAYSTATE enmState)
     
    487511}
    488512
     513
     514/**
     515 * Get capture state name string.
     516 */
     517static const char *drvAudioCaptureStateName(DRVAUDIOCAPTURESTATE enmState)
     518{
     519    switch (enmState)
     520    {
     521        case DRVAUDIOCAPTURESTATE_INVALID:          return "INVALID";
     522        case DRVAUDIOCAPTURESTATE_NO_CAPTURE:       return "NO_CAPTURE";
     523        case DRVAUDIOCAPTURESTATE_CAPTURING:        return "CAPTURING";
     524        case DRVAUDIOCAPTURESTATE_PREBUF:           return "PREBUF";
     525        case DRVAUDIOCAPTURESTATE_END:
     526            break;
     527    }
     528    return "BAD";
     529}
     530
     531
    489532/**
    490533 * Checks if the stream status is one that can be read from.
     
    588631                                                                       PDMHOSTAUDIOSTREAMSTATE enmOldState)
    589632{
    590     PDMAUDIODIR const       enmDir       = pStreamEx->Guest.Cfg.enmDir;
     633    PDMAUDIODIR const           enmDir          = pStreamEx->Guest.Cfg.enmDir;
    591634#ifdef LOG_ENABLED
    592     DRVAUDIOPLAYSTATE const enmPlayState = enmDir == PDMAUDIODIR_OUT ? pStreamEx->Out.enmPlayState : DRVAUDIOPLAYSTATE_INVALID;
     635    DRVAUDIOPLAYSTATE const     enmPlayState    = enmDir == PDMAUDIODIR_OUT
     636                                                ? pStreamEx->Out.enmPlayState   : DRVAUDIOPLAYSTATE_INVALID;
     637    DRVAUDIOCAPTURESTATE const  enmCaptureState = enmDir == PDMAUDIODIR_OUT
     638                                                ? pStreamEx->In.enmCaptureState : DRVAUDIOCAPTURESTATE_INVALID;
    593639#endif
    594640    Assert(enmNewState != enmOldState);
     
    611657                drvAudioStreamProcessBackendStateChangeWasDraining(pStreamEx);
    612658            if (enmDir == PDMAUDIODIR_OUT)
    613                 pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_NOPLAY;
     659                pStreamEx->Out.enmPlayState   = DRVAUDIOPLAYSTATE_NOPLAY;
     660            else
     661                pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_NO_CAPTURE;
    614662            break;
    615663
     
    660708                 PDMHostAudioStreamStateGetName(enmNewState), drvAudioPlayStateName(pStreamEx->Out.enmPlayState) ));
    661709    else
    662         LogFunc(("Input stream '%s': %s -> %s\n", pStreamEx->Core.szName,
    663                  PDMHostAudioStreamStateGetName(enmOldState), PDMHostAudioStreamStateGetName(enmNewState) ));
     710        LogFunc(("Input stream '%s': %s/%s -> %s/%s\n", pStreamEx->Core.szName,
     711                 PDMHostAudioStreamStateGetName(enmOldState), drvAudioCaptureStateName(enmCaptureState),
     712                 PDMHostAudioStreamStateGetName(enmNewState), drvAudioCaptureStateName(pStreamEx->In.enmCaptureState) ));
    664713
    665714    pStreamEx->enmLastBackendState = enmNewState;
     
    10981147        if (pCfgReq->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */
    10991148        {
    1100             /* Pre-buffer 66% of the buffer. */
    1101             pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize * 2 / 3;
     1149            /* Pre-buffer 66% of the buffer for output streams, but only 50% for input. Capping both at 200ms. */
     1150            if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
     1151                pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize * 2 / 3;
     1152            else
     1153                pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize / 2;
     1154            uint32_t const cFramesMax = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 200);
     1155            pCfgReq->Backend.cFramesPreBuffering = RT_MIN(pCfgReq->Backend.cFramesPreBuffering, cFramesMax);
    11021156            pszWhat = "default";
    11031157        }
     
    15071561 * @param   pThis       Pointer to driver instance.
    15081562 * @param   pStreamEx   Stream to initialize.
    1509  * @param   fFlags      PDMAUDIOSTREAM_CREATE_F_XXX.
    15101563 * @param   pCfgHost    Stream configuration to use for the host side (backend).
    15111564 * @param   pCfgGuest   Stream configuration to use for the guest side.
    15121565 */
    1513 static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t fFlags,
     1566static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
    15141567                                      PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
    15151568{
     
    15511604    Assert(PDMAudioPropsAreValid(&CfgHostAcq.Props));
    15521605
    1553     /* Set the stream properties (currently guest side, when DevSB16 is
    1554        converted to mixer and PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF becomes
    1555        default, this will just be the stream properties). */
    1556     if (fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF)
    1557         pStreamEx->Core.Props = CfgHostAcq.Props;
    1558     else
    1559         pStreamEx->Core.Props = pCfgGuest->Props;
     1606    /* Set the stream properties. */
     1607    pStreamEx->Core.Props = CfgHostAcq.Props;
    15601608
    15611609    /* Let the user know if the backend changed some of the tweakable values. */
     
    16251673     * Configure host buffers.
    16261674     */
    1627 
    1628     /* Destroy any former mixing buffer. */
    1629     AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
    1630 
    1631     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    1632     {
    1633         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    1634         rc = AudioMixBufInit(&pStreamEx->Host.MixBuf, pStreamEx->Core.szName, &CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize);
    1635         AssertRCReturn(rc, rc);
    1636     }
     1675    Assert(pStreamEx->cbPreBufThreshold == 0);
     1676    if (CfgHostAcq.Backend.cFramesPreBuffering != 0)
     1677        pStreamEx->cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering);
     1678
    16371679    /* Allocate space for pre-buffering of output stream w/o mixing buffers. */
    1638     else if (pCfgHost->enmDir == PDMAUDIODIR_OUT)
     1680    if (pCfgHost->enmDir == PDMAUDIODIR_OUT)
    16391681    {
    16401682        Assert(pStreamEx->Out.cbPreBufAlloc == 0);
    1641         Assert(pStreamEx->Out.cbPreBufThreshold == 0);
    16421683        Assert(pStreamEx->Out.cbPreBuffered == 0);
    16431684        Assert(pStreamEx->Out.offPreBuf == 0);
    16441685        if (CfgHostAcq.Backend.cFramesPreBuffering != 0)
    16451686        {
    1646             pStreamEx->Out.cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering);
    16471687            pStreamEx->Out.cbPreBufAlloc     = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props,
    16481688                                                                          CfgHostAcq.Backend.cFramesBufferSize - 2);
    1649             pStreamEx->Out.cbPreBufAlloc     = RT_MIN(RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K),
    1650                                                      pStreamEx->Out.cbPreBufAlloc);
     1689            pStreamEx->Out.cbPreBufAlloc     = RT_MIN(RT_ALIGN_32(pStreamEx->cbPreBufThreshold + _8K, _4K),
     1690                                                      pStreamEx->Out.cbPreBufAlloc);
    16511691            pStreamEx->Out.pbPreBuf          = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
    16521692            AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY);
     
    16631703                 PDMAudioPropsMilliToBytes(&pCfgGuest->Props, pCfgGuest->Device.cMsSchedulingHint)));
    16641704
    1665     /* Destroy any former mixing buffer. */
    1666     AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
    1667 
    1668     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    1669     {
    1670         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    1671         rc = AudioMixBufInit(&pStreamEx->Guest.MixBuf, pStreamEx->Core.szName, &pCfgGuest->Props, CfgHostAcq.Backend.cFramesBufferSize);
    1672         AssertRCReturn(rc, rc);
    1673     }
    1674 
    1675     if (RT_FAILURE(rc))
    1676         LogRel(("Audio: Creating stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
    1677 
    1678     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    1679     {
    1680         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    1681         /* Host (Parent) -> Guest (Child). */
    1682         rc = AudioMixBufLinkTo(&pStreamEx->Host.MixBuf, &pStreamEx->Guest.MixBuf);
    1683         AssertRC(rc);
    1684     }
    1685 
    16861705    /*
    16871706     * Register statistics.
     
    16911710    PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.Cfg.Backend.cFramesBufferSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    16921711                           "Host side: The size of the backend buffer (in frames)", "%s/0-HostBackendBufSize", pStreamEx->Core.szName);
    1693     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    1694     {
    1695         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    1696         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    1697                                "Host side: The size of the mixer buffer (in frames)",   "%s/1-HostMixBufSize", pStreamEx->Core.szName);
    1698         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    1699                                "Guest side: The size of the mixer buffer (in frames)",  "%s/2-GuestMixBufSize", pStreamEx->Core.szName);
    1700         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cMixed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    1701                                "Host side: Number of frames in the mixer buffer",   "%s/1-HostMixBufUsed", pStreamEx->Core.szName);
    1702         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    1703                                "Guest side: Number of frames in the mixer buffer",  "%s/2-GuestMixBufUsed", pStreamEx->Core.szName);
    1704     }
    17051712    if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
    17061713    {
     
    17611768    AssertReturn(pCfgHost->enmDir == pCfgGuest->enmDir, VERR_MISMATCH);
    17621769    AssertReturn(pCfgHost->enmDir == PDMAUDIODIR_IN || pCfgHost->enmDir == PDMAUDIODIR_OUT, VERR_NOT_SUPPORTED);
    1763     /* Require PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF for output streams: */
    1764     AssertReturn((fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF) || pCfgHost->enmDir == PDMAUDIODIR_IN, VERR_INVALID_FLAGS);
    17651770
    17661771    /*
     
    18131818                pStreamEx->Core.enmDir          = pCfgHost->enmDir;
    18141819                pStreamEx->Core.cbBackend       = (uint32_t)cbHstStrm;
    1815                 pStreamEx->fNoMixBufs           = RT_BOOL(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF);
    18161820                pStreamEx->fDestroyImmediate    = true;
    18171821                pStreamEx->hReqInitAsync        = NIL_RTREQ;
     
    18491853                 * Try to init the rest.
    18501854                 */
    1851                 rc = drvAudioStreamInitInternal(pThis, pStreamEx, fFlags, pCfgHost, pCfgGuest);
     1855                rc = drvAudioStreamInitInternal(pThis, pStreamEx, pCfgHost, pCfgGuest);
    18521856                if (RT_SUCCESS(rc))
    18531857                {
     
    18701874                    {
    18711875                        if (pThis->CfgIn.Dbg.fEnabled)
    1872                         {
    1873                             AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileCaptureNonInterleaved, pThis->CfgIn.Dbg.szPathOut,
    1874                                                       "DrvAudioCapNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    1875                             AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileStreamRead, pThis->CfgIn.Dbg.szPathOut,
    1876                                                       "DrvAudioRead", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    1877                         }
     1876                            AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileCapture, pThis->CfgIn.Dbg.szPathOut,
     1877                                                      "DrvAudioCapture", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    18781878                    }
    18791879                    else /* Out */
    18801880                    {
    18811881                        if (pThis->CfgOut.Dbg.fEnabled)
    1882                         {
    1883                             AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pThis->CfgOut.Dbg.szPathOut,
    1884                                                       "DrvAudioPlayNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    1885                             AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFileStreamWrite, pThis->CfgOut.Dbg.szPathOut,
    1886                                                       "DrvAudioWrite", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    1887                         }
     1882                            AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFilePlay, pThis->CfgOut.Dbg.szPathOut,
     1883                                                      "DrvAudioPlay", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    18881884                    }
    18891885
     
    20132009    int rc = drvAudioStreamDestroyInternalBackend(pThis, pStreamEx);
    20142010
    2015     /* Destroy mixing buffers. */
    2016     AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
    2017     AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
    2018 
    20192011    /* Free pre-buffer space. */
    20202012    if (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
     
    20482040        if (pThis->CfgIn.Dbg.fEnabled)
    20492041        {
    2050             AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileCaptureNonInterleaved);
    2051             pStreamEx->In.Dbg.pFileCaptureNonInterleaved = NULL;
    2052 
    2053             AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileStreamRead);
    2054             pStreamEx->In.Dbg.pFileStreamRead = NULL;
     2042            AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileCapture);
     2043            pStreamEx->In.Dbg.pFileCapture = NULL;
    20552044        }
    20562045    }
     
    20602049        if (pThis->CfgOut.Dbg.fEnabled)
    20612050        {
    2062             AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFilePlayNonInterleaved);
    2063             pStreamEx->Out.Dbg.pFilePlayNonInterleaved = NULL;
    2064 
    2065             AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFileStreamWrite);
    2066             pStreamEx->Out.Dbg.pFileStreamWrite = NULL;
     2051            AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFilePlay);
     2052            pStreamEx->Out.Dbg.pFilePlay = NULL;
    20672053        }
    20682054    }
     
    22852271    Assert(RTCritSectIsOwner(&pStreamEx->Core.CritSect));
    22862272
    2287     if (pStreamEx->fNoMixBufs)
    2288     {
    2289         AudioMixBufReset(&pStreamEx->Guest.MixBuf);
    2290         AudioMixBufReset(&pStreamEx->Host.MixBuf);
    2291     }
    2292 
    22932273    pStreamEx->nsLastIterated       = 0;
    22942274    pStreamEx->nsLastPlayedCaptured = 0;
     
    22982278        pStreamEx->Out.cbPreBuffered = 0;
    22992279        pStreamEx->Out.offPreBuf     = 0;
    2300         pStreamEx->Out.enmPlayState  = pStreamEx->Out.cbPreBufThreshold > 0
     2280        pStreamEx->Out.enmPlayState  = pStreamEx->cbPreBufThreshold > 0
    23012281                                     ? DRVAUDIOPLAYSTATE_PREBUF : DRVAUDIOPLAYSTATE_PLAY;
    23022282    }
     2283    else
     2284        pStreamEx->In.enmCaptureState = pStreamEx->cbPreBufThreshold > 0
     2285                                      ? DRVAUDIOCAPTURESTATE_PREBUF : DRVAUDIOCAPTURESTATE_CAPTURING;
    23032286}
    23042287
     
    27172700                if (RT_SUCCESS(rc))
    27182701                {
    2719                     /* Reset the play state before we try to start. */
     2702                    /* Reset the state before we try to start. */
    27202703                    PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
    27212704                    pStreamEx->enmLastBackendState = enmBackendState;
     2705                    pStreamEx->offInternal         = 0;
     2706
    27222707                    if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
    27232708                    {
     
    27282713                        {
    27292714                            case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
    2730                                 if (pStreamEx->Out.cbPreBufThreshold > 0)
     2715                                if (pStreamEx->cbPreBufThreshold > 0)
    27312716                                    pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF;
    27322717                                break;
     
    27352720                                RT_FALL_THROUGH();
    27362721                            case PDMHOSTAUDIOSTREAMSTATE_OKAY:
    2737                                 pStreamEx->Out.enmPlayState = pStreamEx->Out.cbPreBufThreshold > 0
     2722                                pStreamEx->Out.enmPlayState = pStreamEx->cbPreBufThreshold > 0
    27382723                                                            ? DRVAUDIOPLAYSTATE_PREBUF : DRVAUDIOPLAYSTATE_PLAY;
    27392724                                break;
     
    27512736                    }
    27522737                    else
    2753                         LogFunc(("ENABLE: enmBackendState=%s\n", PDMHostAudioStreamStateGetName(enmBackendState)));
     2738                    {
     2739                        pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_NO_CAPTURE;
     2740                        switch (enmBackendState)
     2741                        {
     2742                            case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
     2743                                pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_PREBUF;
     2744                                break;
     2745                            case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
     2746                                AssertFailed();
     2747                                RT_FALL_THROUGH();
     2748                            case PDMHOSTAUDIOSTREAMSTATE_OKAY:
     2749                                pStreamEx->In.enmCaptureState = pStreamEx->cbPreBufThreshold > 0
     2750                                                              ? DRVAUDIOCAPTURESTATE_PREBUF : DRVAUDIOCAPTURESTATE_CAPTURING;
     2751                                break;
     2752                            case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
     2753                            case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
     2754                                break;
     2755                            /* no default */
     2756                            case PDMHOSTAUDIOSTREAMSTATE_INVALID:
     2757                            case PDMHOSTAUDIOSTREAMSTATE_END:
     2758                            case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
     2759                                break;
     2760                        }
     2761                        LogFunc(("ENABLE: enmBackendState=%s enmCaptureState=%s\n", PDMHostAudioStreamStateGetName(enmBackendState),
     2762                                 drvAudioCaptureStateName(pStreamEx->In.enmCaptureState)));
     2763                    }
    27542764
    27552765                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
    27562766                    if (RT_SUCCESS(rc))
    27572767                    {
     2768                        pStreamEx->nsStarted = RTTimeNanoTS();
    27582769                        pStreamEx->fStatus |= PDMAUDIOSTREAM_STS_ENABLED;
    27592770                        PDMAUDIOSTREAM_STS_ASSERT_VALID(pStreamEx->fStatus);
     
    29923003        {
    29933004            if (cbWrittenNow != cbToWrite)
    2994                 Log3Func(("%s: @%#RX64: Wrote less bytes than requested: %#x, requested %#x\n",
     3005                Log3Func(("%s: @%#RX64: Wrote fewer bytes than requested: %#x, requested %#x\n",
    29953006                          pStreamEx->Core.szName, pStreamEx->offInternal, cbWrittenNow, cbToWrite));
    29963007#ifdef DEBUG_bird
     
    30373048        Log3Func(("[%s] Pre-buffering (%s): wrote %#x bytes => %#x bytes / %u%%\n",
    30383049                  pStreamEx->Core.szName, drvAudioPlayStateName(pStreamEx->Out.enmPlayState), cbBuf, pStreamEx->Out.cbPreBuffered,
    3039                   pStreamEx->Out.cbPreBuffered * 100 / RT_MAX(pStreamEx->Out.cbPreBufThreshold, 1)));
     3050                  pStreamEx->Out.cbPreBuffered * 100 / RT_MAX(pStreamEx->cbPreBufThreshold, 1)));
    30403051
    30413052    }
     
    32693280
    32703281/**
    3271  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
    3272  */
    3273 static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
    3274 {
    3275     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    3276     AssertPtr(pThis);
     3282 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetState}
     3283 */
     3284static DECLCALLBACK(PDMAUDIOSTREAMSTATE) drvAudioStreamGetState(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
     3285{
     3286    PDRVAUDIO       pThis     = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    32773287    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    3278     AssertPtrReturn(pStreamEx, 0);
    3279     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, 0);
    3280     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, 0);
    3281     AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
    3282 
     3288    AssertPtrReturn(pStreamEx, PDMAUDIOSTREAMSTATE_INVALID);
     3289    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, PDMAUDIOSTREAMSTATE_INVALID);
     3290    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, PDMAUDIOSTREAMSTATE_INVALID);
     3291
     3292    /*
     3293     * Get the status mask.
     3294     */
    32833295    int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
    3284     AssertRCReturn(rc, 0);
     3296    AssertRCReturn(rc, PDMAUDIOSTREAMSTATE_INVALID);
    32853297    RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
    32863298
    3287     /*
    3288      * ...
    3289      */
    3290     uint32_t cbReadable = 0;
    3291 
    3292     /* All input streams for this driver disabled? See @bugref{9882}. */
    3293     const bool fDisabled = !pThis->In.fEnabled;
    3294 
    3295     if (   pThis->pHostDrvAudio
    3296         && (   PDMAudioStrmStatusCanRead(pStreamEx->fStatus)
    3297             || fDisabled)
    3298        )
    3299     {
    3300         PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
    3301         if (pStreamEx->fNoMixBufs)
    3302             cbReadable =    pThis->pHostDrvAudio
    3303                          && (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY)
    3304                          && enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
    3305                          && !fDisabled
    3306                        ? pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend)
    3307                        : 0;
    3308         else
    3309         {
    3310             const uint32_t cfReadable = AudioMixBufLive(&pStreamEx->Guest.MixBuf);
    3311             cbReadable = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadable);
    3312         }
    3313         if (!cbReadable)
    3314         {
    3315             /*
    3316              * If nothing is readable, check if the stream on the backend side is ready to be read from.
    3317              * If it isn't, return the number of bytes readable since the last read from this stream.
    3318              *
    3319              * This is needed for backends (e.g. VRDE) which do not provide any input data in certain
    3320              * situations, but the device emulation needs input data to keep the DMA transfers moving.
    3321              * Reading the actual data from a stream then will return silence then.
    3322              */
    3323             if (   enmBackendState != PDMHOSTAUDIOSTREAMSTATE_OKAY /** @todo probably not correct.  OTOH, I'm not sure if we do what the above comment claims either. */
    3324                 || fDisabled)
    3325             {
    3326                 cbReadable = PDMAudioPropsNanoToBytes(&pStreamEx->Host.Cfg.Props,
    3327                                                       RTTimeNanoTS() - pStreamEx->nsLastReadWritten);
    3328                 if (!(pStreamEx->Core.fWarningsShown & PDMAUDIOSTREAM_WARN_FLAGS_DISABLED))
    3329                 {
    3330                     if (fDisabled)
    3331                         LogRel(("Audio: Input for driver '%s' has been disabled, returning silence\n", pThis->BackendCfg.szName));
    3332                     else
    3333                         LogRel(("Audio: Warning: Input for stream '%s' of driver '%s' not ready (current input status is %s), returning silence\n",
    3334                                 pStreamEx->Core.szName, pThis->BackendCfg.szName, PDMHostAudioStreamStateGetName(enmBackendState) ));
    3335 
    3336                     pStreamEx->Core.fWarningsShown |= PDMAUDIOSTREAM_WARN_FLAGS_DISABLED;
    3337                 }
    3338             }
    3339         }
    3340 
    3341         /* Make sure to align the readable size to the guest's frame size. */
    3342         if (cbReadable)
    3343             cbReadable = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Guest.Cfg.Props, cbReadable);
    3344     }
     3299    PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendStateAndProcessChanges(pThis, pStreamEx);
     3300    uint32_t const                fStrmStatus     = pStreamEx->fStatus;
     3301    PDMAUDIODIR const             enmDir          = pStreamEx->Guest.Cfg.enmDir;
     3302    Assert(enmDir == PDMAUDIODIR_IN || enmDir == PDMAUDIODIR_OUT);
    33453303
    33463304    RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
    33473305    RTCritSectLeave(&pStreamEx->Core.CritSect);
    3348     Log3Func(("[%s] cbReadable=%#RX32 (%RU64ms)\n",
    3349               pStreamEx->Core.szName, cbReadable, PDMAudioPropsBytesToMilli(&pStreamEx->Host.Cfg.Props, cbReadable)));
    3350     return cbReadable;
     3306
     3307    /*
     3308     * Translate it to state enum value.
     3309     */
     3310    PDMAUDIOSTREAMSTATE enmState;
     3311    if (!(fStrmStatus & PDMAUDIOSTREAM_STS_NEED_REINIT))
     3312    {
     3313        if (fStrmStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED)
     3314        {
     3315            if (   (fStrmStatus & PDMAUDIOSTREAM_STS_ENABLED)
     3316                && (enmDir == PDMAUDIODIR_IN ? pThis->In.fEnabled : pThis->Out.fEnabled)
     3317                && (   enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
     3318                    || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING
     3319                    || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_INITIALIZING ))
     3320                enmState = enmDir == PDMAUDIODIR_IN ? PDMAUDIOSTREAMSTATE_ENABLED_READABLE : PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE;
     3321            else
     3322                enmState = PDMAUDIOSTREAMSTATE_INACTIVE;
     3323        }
     3324        else
     3325            enmState = PDMAUDIOSTREAMSTATE_NOT_WORKING;
     3326    }
     3327    else
     3328        enmState = PDMAUDIOSTREAMSTATE_NEED_REINIT;
     3329
     3330#ifdef LOG_ENABLED
     3331    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
     3332#endif
     3333    Log3Func(("[%s] returns %s (status: %s)\n", pStreamEx->Core.szName, PDMAudioStreamStateGetName(enmState),
     3334              drvAudioStreamStatusToStr(szStreamSts, fStrmStatus)));
     3335    return enmState;
    33513336}
    33523337
     
    33703355
    33713356    /*
    3372      * ...
    3373      *
    3374      * Note: We don't propagate the backend stream's status to the outside -- it's the job of this
    3375      *       audio connector to make sense of it.
     3357     * Use the playback and backend states to determin how much can be written, if anything.
    33763358     */
    33773359    uint32_t cbWritable = 0;
     
    34613443
    34623444/**
    3463  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetState}
    3464  */
    3465 static DECLCALLBACK(PDMAUDIOSTREAMSTATE) drvAudioStreamGetState(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
    3466 {
    3467     PDRVAUDIO       pThis     = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    3468     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    3469     AssertPtrReturn(pStreamEx, PDMAUDIOSTREAMSTATE_INVALID);
    3470     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, PDMAUDIOSTREAMSTATE_INVALID);
    3471     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, PDMAUDIOSTREAMSTATE_INVALID);
    3472 
    3473     /*
    3474      * Get the status mask.
    3475      */
    3476     int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
    3477     AssertRCReturn(rc, PDMAUDIOSTREAMSTATE_INVALID);
    3478     RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
    3479 
    3480     PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendStateAndProcessChanges(pThis, pStreamEx);
    3481     uint32_t const                fStrmStatus     = pStreamEx->fStatus;
    3482     PDMAUDIODIR const             enmDir          = pStreamEx->Guest.Cfg.enmDir;
    3483     Assert(enmDir == PDMAUDIODIR_IN || enmDir == PDMAUDIODIR_OUT);
    3484 
    3485     RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
    3486     RTCritSectLeave(&pStreamEx->Core.CritSect);
    3487 
    3488     /*
    3489      * Translate it to state enum value.
    3490      */
    3491     PDMAUDIOSTREAMSTATE enmState;
    3492     if (!(fStrmStatus & PDMAUDIOSTREAM_STS_NEED_REINIT))
    3493     {
    3494         if (fStrmStatus & PDMAUDIOSTREAM_STS_BACKEND_CREATED)
    3495         {
    3496             if (   (fStrmStatus & PDMAUDIOSTREAM_STS_ENABLED)
    3497                 && (enmDir == PDMAUDIODIR_IN ? pThis->In.fEnabled : pThis->Out.fEnabled)
    3498                 && (   enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
    3499                     || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_DRAINING
    3500                     || enmBackendState == PDMHOSTAUDIOSTREAMSTATE_INITIALIZING ))
    3501                 enmState = enmDir == PDMAUDIODIR_IN ? PDMAUDIOSTREAMSTATE_ENABLED_READABLE : PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE;
    3502             else
    3503                 enmState = PDMAUDIOSTREAMSTATE_INACTIVE;
    3504         }
    3505         else
    3506             enmState = PDMAUDIOSTREAMSTATE_NOT_WORKING;
    3507     }
    3508     else
    3509         enmState = PDMAUDIOSTREAMSTATE_NEED_REINIT;
    3510 
    3511 #ifdef LOG_ENABLED
    3512     char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
    3513 #endif
    3514     Log3Func(("[%s] returns %s (status: %s)\n", pStreamEx->Core.szName, PDMAudioStreamStateGetName(enmState),
    3515               drvAudioStreamStatusToStr(szStreamSts, fStrmStatus)));
    3516     return enmState;
    3517 }
    3518 
    3519 
    3520 /**
    3521  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
    3522  */
    3523 static DECLCALLBACK(int) drvAudioStreamSetVolume(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOVOLUME pVol)
    3524 {
    3525     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    3526     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    3527     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    3528     AssertPtrReturn(pVol, VERR_INVALID_POINTER);
    3529     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    3530     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    3531     AssertReturn(!pStreamEx->fNoMixBufs, VWRN_INVALID_STATE);
    3532 
    3533     LogFlowFunc(("[%s] volL=%RU32, volR=%RU32, fMute=%RTbool\n", pStreamEx->Core.szName, pVol->uLeft, pVol->uRight, pVol->fMuted));
    3534 
    3535     int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
    3536     AssertRCReturn(rc, rc);
    3537 
    3538     AudioMixBufSetVolume(&pStreamEx->Guest.MixBuf, pVol);
    3539     AudioMixBufSetVolume(&pStreamEx->Host.MixBuf,  pVol);
    3540 
    3541     RTCritSectLeave(&pStreamEx->Core.CritSect);
    3542     return VINF_SUCCESS;
    3543 }
    3544 
    3545 
    3546 /**
    35473445 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
    35483446 */
     
    35623460    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    35633461    uint32_t uTmp;
    3564     if (!pcbWritten)
     3462    if (pcbWritten)
     3463        AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
     3464    else
    35653465        pcbWritten = &uTmp;
    3566     AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
    35673466
    35683467    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     
    35713470                    ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n",
    35723471                     pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED);
    3573     Assert(pStreamEx->fNoMixBufs);
    35743472
    35753473    AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf),
     
    36093507                    Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY);
    36103508                    rc = drvAudioStreamPlayLocked(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
    3611                     drvAudioStreamPreBuffer(pStreamEx, (uint8_t const *)pvBuf, *pcbWritten, pStreamEx->Out.cbPreBufThreshold);
     3509                    drvAudioStreamPreBuffer(pStreamEx, (uint8_t const *)pvBuf, *pcbWritten, pStreamEx->cbPreBufThreshold);
    36123510                    break;
    36133511
    36143512                case DRVAUDIOPLAYSTATE_PREBUF:
    3615                     if (cbBuf + pStreamEx->Out.cbPreBuffered < pStreamEx->Out.cbPreBufThreshold)
    3616                         rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->Out.cbPreBufThreshold, pcbWritten);
     3513                    if (cbBuf + pStreamEx->Out.cbPreBuffered < pStreamEx->cbPreBufThreshold)
     3514                        rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->cbPreBufThreshold, pcbWritten);
    36173515                    else if (   enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY
    36183516                             && (pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY))
     
    36203518                        Log3Func(("[%s] Pre-buffering completing: cbBuf=%#x cbPreBuffered=%#x => %#x vs cbPreBufThreshold=%#x\n",
    36213519                                  pStreamEx->Core.szName, cbBuf, pStreamEx->Out.cbPreBuffered,
    3622                                   cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->Out.cbPreBufThreshold));
     3520                                  cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->cbPreBufThreshold));
    36233521                        rc = drvAudioStreamPreBufComitting(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
    36243522                    }
     
    36273525                        Log3Func(("[%s] Pre-buffering completing but device not ready: cbBuf=%#x cbPreBuffered=%#x => %#x vs cbPreBufThreshold=%#x; PREBUF -> PREBUF_OVERDUE\n",
    36283526                                  pStreamEx->Core.szName, cbBuf, pStreamEx->Out.cbPreBuffered,
    3629                                   cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->Out.cbPreBufThreshold));
     3527                                  cbBuf + pStreamEx->Out.cbPreBuffered, pStreamEx->cbPreBufThreshold));
    36303528                        pStreamEx->Out.enmPlayState = DRVAUDIOPLAYSTATE_PREBUF_OVERDUE;
    3631                         rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->Out.cbPreBufThreshold, pcbWritten);
     3529                        rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->cbPreBufThreshold, pcbWritten);
    36323530                    }
    36333531                    break;
     
    36383536                    RT_FALL_THRU();
    36393537                case DRVAUDIOPLAYSTATE_PREBUF_SWITCHING:
    3640                     rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->Out.cbPreBufThreshold, pcbWritten);
     3538                    rc = drvAudioStreamPlayToPreBuffer(pStreamEx, pvBuf, cbBuf, pStreamEx->cbPreBufThreshold, pcbWritten);
    36413539                    break;
    36423540
     
    36623560            { /* likely */ }
    36633561            else
    3664                 AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */);
     3562                AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlay, pvBuf, *pcbWritten, 0 /* fFlags */);
    36653563        }
    36663564        else
     
    36813579
    36823580
     3581/**
     3582 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetReadable}
     3583 */
     3584static DECLCALLBACK(uint32_t) drvAudioStreamGetReadable(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
     3585{
     3586    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     3587    AssertPtr(pThis);
     3588    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
     3589    AssertPtrReturn(pStreamEx, 0);
     3590    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, 0);
     3591    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, 0);
     3592    AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN, ("Can't read from a non-input stream\n"));
     3593
     3594
     3595    int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
     3596    AssertRCReturn(rc, 0);
     3597    RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
     3598
     3599    /*
     3600     * Use the capture state to determin how much can be written, if anything.
     3601     */
     3602    uint32_t cbReadable = 0;
     3603    DRVAUDIOCAPTURESTATE const    enmCaptureState = pStreamEx->In.enmCaptureState;
     3604    PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx); RT_NOREF(enmBackendState);
     3605    if (   PDMAudioStrmStatusCanRead(pStreamEx->fStatus)
     3606        && pThis->pHostDrvAudio != NULL)
     3607    {
     3608        switch (enmCaptureState)
     3609        {
     3610            /*
     3611             * Whatever the backend has to offer when in capture mode.
     3612             */
     3613            case DRVAUDIOCAPTURESTATE_CAPTURING:
     3614                Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
     3615                Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY /* potential unplug race */);
     3616                cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     3617                break;
     3618
     3619            /*
     3620             * Same calculation as in drvAudioStreamCaptureSilence, only we cap it
     3621             * at the pre-buffering threshold so we don't get into trouble when we
     3622             * switch to capture mode between now and pfnStreamCapture.
     3623             */
     3624            case DRVAUDIOCAPTURESTATE_PREBUF:
     3625            {
     3626                uint64_t const cNsStream = RTTimeNanoTS() - pStreamEx->nsStarted;
     3627                uint64_t const offCur    = PDMAudioPropsNanoToBytes64(&pStreamEx->Guest.Cfg.Props, cNsStream);
     3628                if (offCur > pStreamEx->offInternal)
     3629                {
     3630                    uint64_t const cbUnread = offCur - pStreamEx->offInternal;
     3631                    cbReadable = (uint32_t)RT_MIN(pStreamEx->cbPreBufThreshold, cbUnread);
     3632                }
     3633                break;
     3634            }
     3635
     3636            case DRVAUDIOCAPTURESTATE_NO_CAPTURE:
     3637                break;
     3638
     3639            case DRVAUDIOCAPTURESTATE_INVALID:
     3640            case DRVAUDIOCAPTURESTATE_END:
     3641                AssertFailed();
     3642                break;
     3643        }
     3644
     3645        /* Make sure to align the readable size to the host's frame size. */
     3646        cbReadable = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Host.Cfg.Props, cbReadable);
     3647    }
     3648
     3649    RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
     3650    RTCritSectLeave(&pStreamEx->Core.CritSect);
     3651    Log3Func(("[%s] cbReadable=%#RX32 (%RU64ms) enmCaptureMode=%s enmBackendState=%s\n",
     3652              pStreamEx->Core.szName, cbReadable, PDMAudioPropsBytesToMilli(&pStreamEx->Host.Cfg.Props, cbReadable),
     3653              drvAudioCaptureStateName(enmCaptureState), PDMHostAudioStreamStateGetName(enmBackendState) ));
     3654    return cbReadable;
     3655}
     3656
     3657
     3658/**
     3659 * Worker for drvAudioStreamCapture that returns silence.
     3660 *
     3661 * The amount of silence returned is a function of how long the stream has been
     3662 * enabled.
     3663 *
     3664 * @returns VINF_SUCCESS
     3665 * @param   pStreamEx   The stream to commit the pre-buffering for.
     3666 * @param   pbBuf       The output buffer.
     3667 * @param   cbBuf       The size of the output buffer.
     3668 * @param   pcbRead     Where to return the number of bytes actually read.
     3669 */
     3670static int drvAudioStreamCaptureSilence(PDRVAUDIOSTREAM pStreamEx, uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbRead)
     3671{
     3672    /** @todo  Does not take paused time into account...  */
     3673    uint64_t const cNsStream = RTTimeNanoTS() - pStreamEx->nsStarted;
     3674    uint64_t const offCur    = PDMAudioPropsNanoToBytes64(&pStreamEx->Guest.Cfg.Props, cNsStream);
     3675    if (offCur > pStreamEx->offInternal)
     3676    {
     3677        uint64_t const cbUnread  = offCur - pStreamEx->offInternal;
     3678        uint32_t const cbToClear = (uint32_t)RT_MIN(cbBuf, cbUnread);
     3679        *pcbRead                 = cbToClear;
     3680        pStreamEx->offInternal  += cbToClear;
     3681        cbBuf                   -= cbToClear;
     3682        PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props, pbBuf, cbToClear,
     3683                                 PDMAudioPropsBytesToFrames(&pStreamEx->Guest.Cfg.Props, cbToClear));
     3684    }
     3685    else
     3686        *pcbRead = 0;
     3687    Log4Func(("%s: @%#RX64: Read %#x bytes of silence (%#x bytes left)\n",
     3688              pStreamEx->Core.szName, pStreamEx->offInternal, *pcbRead, cbBuf));
     3689    return VINF_SUCCESS;
     3690}
     3691
     3692
     3693/**
     3694 * Worker for drvAudioStreamCapture.
     3695 */
     3696static int drvAudioStreamCaptureLocked(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
     3697                                       uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbRead)
     3698{
     3699    Log4Func(("%s: @%#RX64: cbBuf=%#x\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbBuf));
     3700
     3701    uint32_t      cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     3702    pStreamEx->In.Stats.cbBackendReadableBefore = cbReadable;
     3703
     3704    uint32_t      cbRead     = 0;
     3705    int           rc         = VINF_SUCCESS;
     3706    uint8_t const cbFrame    = PDMAudioPropsFrameSize(&pStreamEx->Core.Props);
     3707    while (cbBuf >= cbFrame && cbReadable >= cbFrame)
     3708    {
     3709        uint32_t const cbToRead  = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, RT_MIN(cbBuf, cbReadable));
     3710        uint32_t       cbReadNow = 0;
     3711        rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend, pbBuf, cbToRead, &cbReadNow);
     3712        if (RT_SUCCESS(rc))
     3713        {
     3714            if (cbReadNow != cbToRead)
     3715                Log4Func(("%s: @%#RX64: Read fewer bytes than requested: %#x, requested %#x\n",
     3716                          pStreamEx->Core.szName, pStreamEx->offInternal, cbReadNow, cbToRead));
     3717#ifdef DEBUG_bird
     3718            Assert(cbReadNow == cbToRead);
     3719#endif
     3720            AssertStmt(cbReadNow <= cbToRead, cbReadNow = cbToRead);
     3721            cbRead                 += cbReadNow;
     3722            cbBuf                  -= cbReadNow;
     3723            pbBuf                  += cbReadNow;
     3724            pStreamEx->offInternal += cbReadNow;
     3725        }
     3726        else
     3727        {
     3728            *pcbRead = cbRead;
     3729            LogFunc(("%s: @%#RX64: pfnStreamCapture failed read %#x bytes (%#x previous read, %#x readable): %Rrc\n",
     3730                     pStreamEx->Core.szName, pStreamEx->offInternal, cbToRead, cbRead, cbReadable, rc));
     3731            return cbRead ? VINF_SUCCESS : rc;
     3732        }
     3733
     3734        cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     3735    }
     3736
     3737    *pcbRead = cbRead;
     3738    pStreamEx->In.Stats.cbBackendReadableAfter = cbReadable;
     3739    if (cbRead)
     3740        pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();
     3741
     3742    Log4Func(("%s: @%#RX64: Read %#x bytes (%#x bytes left)\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbRead, cbBuf));
     3743    return rc;
     3744}
     3745
     3746
     3747/**
     3748 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
     3749 */
     3750static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
     3751                                               void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
     3752{
     3753    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     3754    AssertPtr(pThis);
     3755
     3756    /*
     3757     * Check input and sanity.
     3758     */
     3759    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     3760    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
     3761    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
     3762    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     3763    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     3764    uint32_t uTmp;
     3765    if (pcbRead)
     3766        AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
     3767    else
     3768        pcbRead = &uTmp;
     3769
     3770    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     3771    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     3772    AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
     3773                    ("Stream '%s' is not an input stream and therefore cannot be read from (direction is '%s')\n",
     3774                     pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED);
     3775
     3776    AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf),
     3777              ("Stream '%s' got a non-frame-aligned write (%#RX32 bytes)\n", pStreamEx->Core.szName, cbBuf));
     3778
     3779    int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
     3780    AssertRCReturn(rc, rc);
     3781
     3782    /*
     3783     * First check that we can read from the stream, and if not,
     3784     * whether to just drop the input into the bit bucket.
     3785     */
     3786    if (PDMAudioStrmStatusIsReady(pStreamEx->fStatus))
     3787    {
     3788        RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
     3789        if (   pThis->In.fEnabled /* (see @bugref{9882}) */
     3790            && pThis->pHostDrvAudio != NULL)
     3791        {
     3792            /*
     3793             * Get the backend state and process changes to it since last time we checked.
     3794             */
     3795            PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendStateAndProcessChanges(pThis, pStreamEx);
     3796
     3797            /*
     3798             * Do the transfering.
     3799             */
     3800            switch (pStreamEx->In.enmCaptureState)
     3801            {
     3802                case DRVAUDIOCAPTURESTATE_CAPTURING:
     3803                    Assert(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY);
     3804                    Assert(enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY);
     3805                    rc = drvAudioStreamCaptureLocked(pThis, pStreamEx, (uint8_t *)pvBuf, cbBuf, pcbRead);
     3806                    break;
     3807
     3808                case DRVAUDIOCAPTURESTATE_PREBUF:
     3809                    if (enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY)
     3810                    {
     3811                        uint32_t const cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio,
     3812                                                                                               pStreamEx->pBackend);
     3813                        if (cbReadable >= pStreamEx->cbPreBufThreshold)
     3814                        {
     3815                            Log4Func(("[%s] Pre-buffering completed: cbReadable=%#x vs cbPreBufThreshold=%#x (cbBuf=%#x)\n",
     3816                                      pStreamEx->Core.szName, cbReadable, pStreamEx->cbPreBufThreshold, cbBuf));
     3817                            pStreamEx->In.enmCaptureState = DRVAUDIOCAPTURESTATE_CAPTURING;
     3818                            rc = drvAudioStreamCaptureLocked(pThis, pStreamEx, (uint8_t *)pvBuf, cbBuf, pcbRead);
     3819                            break;
     3820                        }
     3821                        pStreamEx->In.Stats.cbBackendReadableBefore = cbReadable;
     3822                        pStreamEx->In.Stats.cbBackendReadableAfter  = cbReadable;
     3823                        Log4Func(("[%s] Pre-buffering: Got %#x out of %#x\n",
     3824                                  pStreamEx->Core.szName, cbReadable, pStreamEx->cbPreBufThreshold));
     3825                    }
     3826                    else
     3827                        Log4Func(("[%s] Pre-buffering: Backend status %s\n",
     3828                                  pStreamEx->Core.szName, PDMHostAudioStreamStateGetName(enmBackendState) ));
     3829                    drvAudioStreamCaptureSilence(pStreamEx, (uint8_t *)pvBuf, cbBuf, pcbRead);
     3830                    break;
     3831
     3832                case DRVAUDIOCAPTURESTATE_NO_CAPTURE:
     3833                    *pcbRead = 0;
     3834                    Log4Func(("[%s] Not capturing - backend state: %s\n",
     3835                              pStreamEx->Core.szName, PDMHostAudioStreamStateGetName(enmBackendState) ));
     3836                    break;
     3837
     3838                default:
     3839                    *pcbRead = 0;
     3840                    AssertMsgFailedBreak(("%d; cbBuf=%#x\n", pStreamEx->In.enmCaptureState, cbBuf));
     3841            }
     3842
     3843            if (!pThis->CfgIn.Dbg.fEnabled || RT_FAILURE(rc))
     3844            { /* likely */ }
     3845            else
     3846                AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCapture, pvBuf, *pcbRead, 0 /* fFlags */);
     3847        }
     3848        else
     3849        {
     3850            *pcbRead = 0;
     3851            Log4Func(("[%s] Backend stream %s, returning no data\n", pStreamEx->Core.szName,
     3852                      !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
     3853        }
     3854        RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
     3855    }
     3856    else
     3857        rc = VERR_AUDIO_STREAM_NOT_READY;
     3858
     3859    RTCritSectLeave(&pStreamEx->Core.CritSect);
     3860    return rc;
     3861}
     3862
     3863
     3864#ifdef OLD_CAPTURE_CODE
    36833865/**
    36843866 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
     
    38594041
    38604042        if (pThis->CfgIn.Dbg.fEnabled)
    3861             AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCaptureNonInterleaved, abChunk, cbCaptured, 0 /* fFlags */);
     4043            AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCapture, abChunk, cbCaptured, 0 /* fFlags */);
    38624044
    38634045        uint32_t cfHstMixed = 0;
     
    40474229    return rc;
    40484230}
     4231#endif
    40494232
    40504233
     
    42684451    if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
    42694452    {
    4270         if (pStreamEx->Out.cbPreBufThreshold > 0)
     4453        if (pStreamEx->cbPreBufThreshold > 0)
    42714454        {
    42724455            DRVAUDIOPLAYSTATE const enmPlayState = pStreamEx->Out.enmPlayState;
     
    50705253    pThis->IAudioConnector.pfnStreamControl     = drvAudioStreamControl;
    50715254    pThis->IAudioConnector.pfnStreamIterate     = drvAudioStreamIterate;
     5255    pThis->IAudioConnector.pfnStreamGetState    = drvAudioStreamGetState;
     5256    pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
     5257    pThis->IAudioConnector.pfnStreamPlay        = drvAudioStreamPlay;
    50725258    pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
    5073     pThis->IAudioConnector.pfnStreamGetWritable = drvAudioStreamGetWritable;
    5074     pThis->IAudioConnector.pfnStreamGetState    = drvAudioStreamGetState;
    5075     pThis->IAudioConnector.pfnStreamSetVolume   = drvAudioStreamSetVolume;
    5076     pThis->IAudioConnector.pfnStreamPlay        = drvAudioStreamPlay;
    5077     pThis->IAudioConnector.pfnStreamRead        = drvAudioStreamRead;
    50785259    pThis->IAudioConnector.pfnStreamCapture     = drvAudioStreamCapture;
    50795260    /* IHostAudioPort */
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