VirtualBox

Changeset 88715 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Apr 26, 2021 8:13:59 PM (4 years ago)
Author:
vboxsync
Message:

DrvAudio: Shuffling functions more into interface order. bugref:9890

File:
1 edited

Legend:

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

    r88714 r88715  
    398398#endif /* !VBOX_AUDIO_TESTCASE */
    399399
    400 /**
    401  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
    402  */
    403 static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
    404                                                PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
    405 {
    406     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    407     AssertPtr(pThis);
    408 
    409     /** @todo r=bird: why?  It's not documented to ignore NULL streams.   */
    410     if (!pStream)
    411         return VINF_SUCCESS;
    412     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    413     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    414     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    415     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    416 
    417     int rc = RTCritSectEnter(&pThis->CritSect);
    418     AssertRCReturn(rc, rc);
    419 
    420     LogFlowFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, PDMAudioStrmCmdGetName(enmStreamCmd)));
    421 
    422     rc = drvAudioStreamControlInternal(pThis, pStreamEx, enmStreamCmd);
    423 
    424     RTCritSectLeave(&pThis->CritSect);
    425     return rc;
    426 }
    427 
    428 /**
    429  * Controls an audio stream.
    430  *
    431  * @returns VBox status code.
    432  * @param   pThis           Pointer to driver instance.
    433  * @param   pStreamEx       Stream to control.
    434  * @param   enmStreamCmd    Control command.
    435  */
    436 static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
    437 {
    438     AssertPtr(pThis);
    439     AssertPtr(pStreamEx);
    440 
    441 #ifdef LOG_ENABLED
    442     char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
    443 #endif
    444     LogFunc(("[%s] enmStreamCmd=%s fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
    445              dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
    446 
    447     int rc = VINF_SUCCESS;
    448 
    449     switch (enmStreamCmd)
    450     {
    451         case PDMAUDIOSTREAMCMD_ENABLE:
    452         {
    453             if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
    454             {
    455                 /* Is a pending disable outstanding? Then disable first. */
    456                 if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
    457                     rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    458 
    459                 if (RT_SUCCESS(rc))
    460                     rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
    461 
    462                 if (RT_SUCCESS(rc))
    463                     pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
    464             }
    465             break;
    466         }
    467 
    468         case PDMAUDIOSTREAMCMD_DISABLE:
    469         {
    470             if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED)
    471             {
    472                 /*
    473                  * For playback (output) streams first mark the host stream as pending disable,
    474                  * so that the rest of the remaining audio data will be played first before
    475                  * closing the stream.
    476                  */
    477                 if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
    478                 {
    479                     LogFunc(("[%s] Pending disable/pause\n", pStreamEx->Core.szName));
    480                     pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE;
    481 
    482                     /* Schedule a follow up timer to the pending-disable state.  We cannot rely
    483                        on the device to provide further callouts to finish the state transition.
    484                        10ms is taking out of thin air and may be too course grained, we should
    485                        really consider the amount of unplayed buffer in the backend and what not... */
    486                     if (!pThis->fTimerArmed)
    487                     {
    488                         LogFlowFunc(("Arming emergency pending-disable hack...\n"));
    489                         int rc2 = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimer, 10 /*ms*/);
    490                         AssertRC(rc2);
    491                         pThis->fTimerArmed = true;
    492                     }
    493                 }
    494 
    495                 /* Can we close the host stream as well (not in pending disable mode)? */
    496                 if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
    497                 {
    498                     rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    499                     if (RT_SUCCESS(rc))
    500                         drvAudioStreamResetInternal(pThis, pStreamEx);
    501                 }
    502             }
    503             break;
    504         }
    505 
    506         case PDMAUDIOSTREAMCMD_PAUSE:
    507         {
    508             if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED))
    509             {
    510                 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_PAUSE);
    511                 if (RT_SUCCESS(rc))
    512                     pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
    513             }
    514             break;
    515         }
    516 
    517         case PDMAUDIOSTREAMCMD_RESUME:
    518         {
    519             if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
    520             {
    521                 rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_RESUME);
    522                 if (RT_SUCCESS(rc))
    523                     pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
    524             }
    525             break;
    526         }
    527 
    528         default:
    529             rc = VERR_NOT_IMPLEMENTED;
    530             break;
    531     }
    532 
    533     if (RT_FAILURE(rc))
    534         LogFunc(("[%s] Failed with %Rrc\n", pStreamEx->Core.szName, rc));
    535 
    536     return rc;
    537 }
    538 
    539 /**
    540  * Controls a stream's backend.
    541  *
    542  * If the stream has no backend available, VERR_NOT_FOUND is returned
    543  * (bird: actually the code returns VINF_SUCCESS).
    544  *
    545  * @returns VBox status code.
    546  * @param   pThis           Pointer to driver instance.
    547  * @param   pStreamEx       Stream to control.
    548  * @param   enmStreamCmd    Control command.
    549  */
    550 static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
    551 {
    552     AssertPtr(pThis);
    553     AssertPtr(pStreamEx);
    554 
    555 #ifdef LOG_ENABLED
    556     char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
    557 #endif
    558     LogFlowFunc(("[%s] enmStreamCmd=%s, fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
    559                  dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
    560 
    561     if (!pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
    562         return VINF_SUCCESS;
    563 
    564 
    565     /*
    566      * Whether to propagate commands down to the backend.
    567      *
    568      * This is needed for critical operations like recording audio if audio input is disabled on a per-driver level.
    569      *
    570      * Note that not all commands will be covered by this, such as operations like stopping, draining and droppping,
    571      * which are considered uncritical and sometimes even are required for certain backends (like DirectSound on Windows).
    572      *
    573      * The actual stream state will be untouched to not make the state machine handling more complicated than
    574      * it already is.
    575      *
    576      * See @bugref{9882}.
    577      */
    578     const bool fEnabled =    (   pStreamEx->Core.enmDir == PDMAUDIODIR_IN
    579                               && pThis->In.fEnabled)
    580                           || (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
    581                               && pThis->Out.fEnabled);
    582 
    583     LogRel2(("Audio: %s stream '%s' in backend (%s is %s)\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName,
    584                                                               PDMAudioDirGetName(pStreamEx->Core.enmDir),
    585                                                               fEnabled ? "enabled" : "disabled"));
    586     int rc = VINF_SUCCESS;
    587     switch (enmStreamCmd)
    588     {
    589         case PDMAUDIOSTREAMCMD_ENABLE:
    590         {
    591             if (fEnabled)
    592                 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_ENABLE);
    593             break;
    594         }
    595 
    596         case PDMAUDIOSTREAMCMD_DISABLE:
    597         {
    598             rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DISABLE);
    599             break;
    600         }
    601 
    602         case PDMAUDIOSTREAMCMD_PAUSE:
    603         {
    604             if (fEnabled) /* Needed, as resume below also is being checked for. */
    605                 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_PAUSE);
    606             break;
    607         }
    608 
    609         case PDMAUDIOSTREAMCMD_RESUME:
    610         {
    611             if (fEnabled)
    612                 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_RESUME);
    613             break;
    614         }
    615 
    616         case PDMAUDIOSTREAMCMD_DRAIN:
    617         {
    618             rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DRAIN);
    619             break;
    620         }
    621 
    622         default:
    623             AssertMsgFailedReturn(("Command %RU32 not implemented\n", enmStreamCmd), VERR_INTERNAL_ERROR_2);
    624     }
    625 
    626     if (RT_FAILURE(rc))
    627     {
    628         if (   rc != VERR_NOT_IMPLEMENTED
    629             && rc != VERR_NOT_SUPPORTED
    630             && rc != VERR_AUDIO_STREAM_NOT_READY)
    631         {
    632             LogRel(("Audio: %s stream '%s' failed with %Rrc\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName, rc));
    633         }
    634 
    635         LogFunc(("[%s] %s failed with %Rrc\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd), rc));
    636     }
    637 
    638     return rc;
    639 }
    640400
    641401/**
     
    999759
    1000760/**
    1001  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
    1002  */
    1003 static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
    1004                                              const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
    1005 {
    1006     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    1007     AssertPtr(pThis);
    1008 
    1009     /*
    1010      * Check input and sanity.
    1011      */
    1012     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    1013     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    1014     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    1015     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    1016     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    1017     uint32_t uTmp;
    1018     if (!pcbWritten)
    1019         pcbWritten = &uTmp;
    1020     AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
    1021 
    1022     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    1023     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    1024     AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT,
    1025                     ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n",
    1026                      pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED);
    1027     Assert(pStreamEx->fNoMixBufs);
    1028 
    1029     AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf),
    1030               ("Stream '%s' got a non-frame-aligned write (%RU32 bytes)\n", pStreamEx->Core.szName, cbBuf));
    1031 
    1032 /// @todo    STAM_PROFILE_ADV_START(&pThis->Stats.DelayOut, out); /* (stopped in drvAudioStreamPlayLocked) */
    1033 
    1034     int rc = RTCritSectEnter(&pThis->CritSect);
    1035     AssertRCReturn(rc, rc);
    1036 
    1037     /*
    1038      * First check that we can write to the stream, and if not,
    1039      * whether to just drop the input into the bit bucket.
    1040      */
    1041     if (PDMAudioStrmStatusIsReady(pStreamEx->Core.fStatus))
    1042     {
    1043         if (   !pThis->Out.fEnabled         /* (see @bugref{9882}) */
    1044             || pThis->pHostDrvAudio == NULL /* (we used to work this condition differently) */
    1045             || !PDMAudioStrmStatusCanWrite(pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend)))
    1046         {
    1047             Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.szName,
    1048                       !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
    1049             *pcbWritten = cbBuf;
    1050             pStreamEx->offInternal += cbBuf;
    1051         }
    1052         /*
    1053          * No-mixing buffer mode:  Write the data directly to the backend, unless
    1054          * we're prebuffering.  There will be no pfnStreamPlay call in this mode.
    1055          */
    1056         else
    1057         {
    1058             uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore);
    1059             rc = drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
    1060             Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal);
    1061             if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc))
    1062             { /* likely */ }
    1063             else
    1064                 AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */);
    1065         }
    1066     }
    1067     else
    1068         rc = VERR_AUDIO_STREAM_NOT_READY;
    1069 
    1070     RTCritSectLeave(&pThis->CritSect);
    1071     return rc;
    1072 }
    1073 
    1074 /**
    1075  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
    1076  */
    1077 static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
    1078 {
    1079    AssertPtrReturn(pInterface, UINT32_MAX);
    1080    AssertPtrReturn(pStream,    UINT32_MAX);
    1081    AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
    1082    AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
    1083    RT_NOREF(pInterface);
    1084 
    1085    uint32_t const cRefs = ASMAtomicIncU32(&pStream->cRefs);
    1086    Assert(cRefs > 1);
    1087    Assert(cRefs < _1K);
    1088 
    1089    return cRefs;
    1090 }
    1091 
    1092 /**
    1093  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
    1094  */
    1095 static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
    1096 {
    1097    AssertPtrReturn(pInterface, UINT32_MAX);
    1098    AssertPtrReturn(pStream,    UINT32_MAX);
    1099    AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
    1100    AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
    1101    RT_NOREF(pInterface);
    1102 
    1103    uint32_t cRefs = ASMAtomicDecU32(&pStream->cRefs);
    1104    AssertStmt(cRefs >= 1, cRefs = ASMAtomicIncU32(&pStream->cRefs));
    1105    Assert(cRefs < _1K);
    1106 
    1107    return cRefs;
    1108 }
    1109 
    1110 /**
    1111  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
    1112  */
    1113 static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
    1114 {
    1115     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    1116     AssertPtr(pThis);
    1117     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    1118     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    1119     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    1120     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    1121 
    1122     int rc = RTCritSectEnter(&pThis->CritSect);
    1123     AssertRCReturn(rc, rc);
    1124 
    1125     rc = drvAudioStreamIterateInternal(pThis, pStreamEx);
    1126 
    1127     RTCritSectLeave(&pThis->CritSect);
    1128 
    1129     if (RT_FAILURE(rc))
    1130         LogFlowFuncLeaveRC(rc);
    1131     return rc;
    1132 }
    1133 
    1134 /**
    1135761 * Re-initializes the given stream if it is scheduled for this operation.
    1136762 *
     
    1204830    }
    1205831}
     832
     833
     834/**
     835 * @callback_method_impl{FNTMTIMERDRV}
     836 */
     837static DECLCALLBACK(void) drvAudioEmergencyIterateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
     838{
     839    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
     840    RT_NOREF(hTimer, pvUser);
     841    RTCritSectEnter(&pThis->CritSect);
     842
     843    /*
     844     * Iterate any stream with the pending-disable flag set.
     845     */
     846    uint32_t        cMilliesToNext = 0;
     847    PDRVAUDIOSTREAM pStreamEx, pStreamExNext;
     848    RTListForEachSafe(&pThis->lstStreams, pStreamEx, pStreamExNext, DRVAUDIOSTREAM, ListEntry)
     849    {
     850        if (   pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC
     851            && pStreamEx->Core.cRefs >= 1)
     852        {
     853            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
     854            {
     855                drvAudioStreamIterateInternal(pThis, pStreamEx);
     856
     857                if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
     858                    cMilliesToNext = 10;
     859            }
     860        }
     861    }
     862
     863    /*
     864     * Re-arm the timer if we still got streams in the pending state.
     865     */
     866    if (cMilliesToNext)
     867    {
     868        pThis->fTimerArmed = true;
     869        PDMDrvHlpTimerSetMillies(pDrvIns, pThis->hTimer, cMilliesToNext);
     870    }
     871    else
     872        pThis->fTimerArmed = false;
     873
     874    RTCritSectLeave(&pThis->CritSect);
     875}
     876
     877
     878#ifdef VBOX_WITH_AUDIO_ENUM
     879/**
     880 * Enumerates all host audio devices.
     881 *
     882 * This functionality might not be implemented by all backends and will return
     883 * VERR_NOT_SUPPORTED if not being supported.
     884 *
     885 * @note Must not hold the driver's critical section!
     886 *
     887 * @returns VBox status code.
     888 * @param   pThis               Driver instance to be called.
     889 * @param   fLog                Whether to print the enumerated device to the release log or not.
     890 * @param   pDevEnum            Where to store the device enumeration.
     891 *
     892 * @remarks This is currently ONLY used for release logging.
     893 */
     894static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIOHOSTENUM pDevEnum)
     895{
     896    AssertReturn(!RTCritSectIsOwner(&pThis->CritSect), VERR_WRONG_ORDER);
     897
     898    int rc;
     899
     900    /*
     901     * If the backend supports it, do a device enumeration.
     902     */
     903    if (pThis->pHostDrvAudio->pfnGetDevices)
     904    {
     905        PDMAUDIOHOSTENUM DevEnum;
     906        rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
     907        if (RT_SUCCESS(rc))
     908        {
     909            if (fLog)
     910                LogRel(("Audio: Found %RU16 devices for driver '%s'\n", DevEnum.cDevices, pThis->szName));
     911
     912            PPDMAUDIOHOSTDEV pDev;
     913            RTListForEach(&DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
     914            {
     915                if (fLog)
     916                {
     917                    char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
     918                    LogRel(("Audio: Device '%s':\n", pDev->szName));
     919                    LogRel(("Audio:   Usage           = %s\n",   PDMAudioDirGetName(pDev->enmUsage)));
     920                    LogRel(("Audio:   Flags           = %s\n",   PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags)));
     921                    LogRel(("Audio:   Input channels  = %RU8\n", pDev->cMaxInputChannels));
     922                    LogRel(("Audio:   Output channels = %RU8\n", pDev->cMaxOutputChannels));
     923                }
     924            }
     925
     926            if (pDevEnum)
     927                rc = PDMAudioHostEnumCopy(pDevEnum, &DevEnum, PDMAUDIODIR_INVALID /*all*/, true /*fOnlyCoreData*/);
     928
     929            PDMAudioHostEnumDelete(&DevEnum);
     930        }
     931        else
     932        {
     933            if (fLog)
     934                LogRel(("Audio: Device enumeration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
     935            /* Not fatal. */
     936        }
     937    }
     938    else
     939    {
     940        rc = VERR_NOT_SUPPORTED;
     941
     942        if (fLog)
     943            LogRel2(("Audio: Host driver '%s' does not support audio device enumeration, skipping\n", pThis->szName));
     944    }
     945
     946    LogFunc(("Returning %Rrc\n", rc));
     947    return rc;
     948}
     949#endif /* VBOX_WITH_AUDIO_ENUM */
     950
     951/**
     952 * Initializes the host backend and queries its initial configuration.
     953 *
     954 * @returns VBox status code.
     955 * @param   pThis               Driver instance to be called.
     956 */
     957static int drvAudioHostInit(PDRVAUDIO pThis)
     958{
     959    LogFlowFuncEnter();
     960
     961    /*
     962     * Check the function pointers, make sure the ones we define as
     963     * mandatory are present.
     964     */
     965    PPDMIHOSTAUDIO pHostDrvAudio = pThis->pHostDrvAudio;
     966    AssertPtrReturn(pHostDrvAudio, VERR_INVALID_POINTER);
     967    AssertPtrReturn(pHostDrvAudio->pfnGetConfig, VERR_INVALID_POINTER);
     968    AssertPtrNullReturn(pHostDrvAudio->pfnGetDevices, VERR_INVALID_POINTER);
     969    AssertPtrNullReturn(pHostDrvAudio->pfnGetStatus, VERR_INVALID_POINTER);
     970    AssertPtrNullReturn(pHostDrvAudio->pfnStreamConfigHint, VERR_INVALID_POINTER);
     971    AssertPtrReturn(pHostDrvAudio->pfnStreamCreate, VERR_INVALID_POINTER);
     972    AssertPtrReturn(pHostDrvAudio->pfnStreamDestroy, VERR_INVALID_POINTER);
     973    AssertPtrReturn(pHostDrvAudio->pfnStreamControl, VERR_INVALID_POINTER);
     974    AssertPtrReturn(pHostDrvAudio->pfnStreamGetReadable, VERR_INVALID_POINTER);
     975    AssertPtrReturn(pHostDrvAudio->pfnStreamGetWritable, VERR_INVALID_POINTER);
     976    AssertPtrNullReturn(pHostDrvAudio->pfnStreamGetPending, VERR_INVALID_POINTER);
     977    AssertPtrReturn(pHostDrvAudio->pfnStreamGetStatus, VERR_INVALID_POINTER);
     978    AssertPtrReturn(pHostDrvAudio->pfnStreamPlay, VERR_INVALID_POINTER);
     979    AssertPtrReturn(pHostDrvAudio->pfnStreamCapture, VERR_INVALID_POINTER);
     980
     981    /*
     982     * Get the backend configuration.
     983     */
     984    int rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
     985    if (RT_FAILURE(rc))
     986    {
     987        LogRel(("Audio: Getting configuration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
     988        return VERR_AUDIO_BACKEND_INIT_FAILED;
     989    }
     990
     991    pThis->In.cStreamsFree  = pThis->BackendCfg.cMaxStreamsIn;
     992    pThis->Out.cStreamsFree = pThis->BackendCfg.cMaxStreamsOut;
     993
     994    LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
     995
     996    LogRel2(("Audio: Host driver '%s' supports %RU32 input streams and %RU32 output streams at once.\n",
     997             pThis->szName, pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
     998
     999#ifdef VBOX_WITH_AUDIO_ENUM
     1000    int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
     1001    if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
     1002        AssertRC(rc2);
     1003
     1004    RT_NOREF(rc2);
     1005    /* Ignore rc. */
     1006#endif
     1007
     1008    LogFlowFuncLeave();
     1009    return VINF_SUCCESS;
     1010}
     1011
     1012
     1013/*********************************************************************************************************************************
     1014*   PDMIAUDIOCONNECTOR                                                                                                           *
     1015*********************************************************************************************************************************/
     1016
     1017/**
     1018 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnEnable}
     1019 */
     1020static DECLCALLBACK(int) drvAudioEnable(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir, bool fEnable)
     1021{
     1022    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     1023    AssertPtr(pThis);
     1024
     1025    bool *pfEnabled;
     1026    if (enmDir == PDMAUDIODIR_IN)
     1027        pfEnabled = &pThis->In.fEnabled;
     1028    else if (enmDir == PDMAUDIODIR_OUT)
     1029        pfEnabled = &pThis->Out.fEnabled;
     1030    else
     1031        AssertFailedReturn(VERR_INVALID_PARAMETER);
     1032
     1033    int rc = RTCritSectEnter(&pThis->CritSect);
     1034    AssertRCReturn(rc, rc);
     1035
     1036    if (fEnable != *pfEnabled)
     1037    {
     1038        LogRel(("Audio: %s %s for driver '%s'\n",
     1039                fEnable ? "Enabling" : "Disabling", enmDir == PDMAUDIODIR_IN ? "input" : "output", pThis->szName));
     1040
     1041        /* Update the status first, as this will be checked for in drvAudioStreamControlInternalBackend() below. */
     1042        *pfEnabled = fEnable;
     1043
     1044        PDRVAUDIOSTREAM pStreamEx;
     1045        RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
     1046        {
     1047            if (pStreamEx->Core.enmDir != enmDir) /* Skip unwanted streams. */
     1048                continue;
     1049
     1050            /* Note: Only enable / disable the backend, do *not* change the stream's internal status.
     1051             *       Callers (device emulation, mixer, ...) from outside will not see any status or behavior change,
     1052             *       to not confuse the rest of the state machine.
     1053             *
     1054             *       When disabling:
     1055             *          - playing back audo data would go to /dev/null
     1056             *          - recording audio data would return silence instead
     1057             *
     1058             * See @bugref{9882}.
     1059             */
     1060            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx,
     1061                                                           fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
     1062            if (RT_FAILURE(rc2))
     1063            {
     1064                if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
     1065                    LogRel(("Audio: Stream '%s' not available\n", pStreamEx->Core.szName));
     1066                else
     1067                    LogRel(("Audio: Failed to %s %s stream '%s', rc=%Rrc\n", fEnable ? "enable" : "disable",
     1068                            enmDir == PDMAUDIODIR_IN ? "input" : "output", pStreamEx->Core.szName, rc2));
     1069            }
     1070            else
     1071            {
     1072                /* When (re-)enabling a stream, clear the disabled warning bit again. */
     1073                if (fEnable)
     1074                    pStreamEx->Core.fWarningsShown &= ~PDMAUDIOSTREAM_WARN_FLAGS_DISABLED;
     1075            }
     1076
     1077            if (RT_SUCCESS(rc))
     1078                rc = rc2;
     1079
     1080            /* Keep going. */
     1081        }
     1082    }
     1083
     1084    RTCritSectLeave(&pThis->CritSect);
     1085    LogFlowFuncLeaveRC(rc);
     1086    return rc;
     1087}
     1088
     1089
     1090/**
     1091 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
     1092 */
     1093static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
     1094{
     1095    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     1096    AssertPtr(pThis);
     1097    int rc = RTCritSectEnter(&pThis->CritSect);
     1098    AssertRCReturn(rc, false);
     1099
     1100    bool fEnabled;
     1101    if (enmDir == PDMAUDIODIR_IN)
     1102        fEnabled = pThis->In.fEnabled;
     1103    else if (enmDir == PDMAUDIODIR_OUT)
     1104        fEnabled = pThis->Out.fEnabled;
     1105    else
     1106        AssertFailedStmt(fEnabled = false);
     1107
     1108    RTCritSectLeave(&pThis->CritSect);
     1109    return fEnabled;
     1110}
     1111
     1112
     1113/**
     1114 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
     1115 */
     1116static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
     1117{
     1118    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     1119    AssertPtr(pThis);
     1120    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
     1121    int rc = RTCritSectEnter(&pThis->CritSect);
     1122    AssertRCReturn(rc, rc);
     1123
     1124    if (pThis->pHostDrvAudio)
     1125    {
     1126        if (pThis->pHostDrvAudio->pfnGetConfig)
     1127            rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
     1128        else
     1129            rc = VERR_NOT_SUPPORTED;
     1130    }
     1131    else
     1132        rc = VERR_PDM_NO_ATTACHED_DRIVER;
     1133
     1134    RTCritSectLeave(&pThis->CritSect);
     1135    LogFlowFuncLeaveRC(rc);
     1136    return rc;
     1137}
     1138
     1139
     1140/**
     1141 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
     1142 */
     1143static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
     1144{
     1145    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     1146    AssertPtr(pThis);
     1147    int rc = RTCritSectEnter(&pThis->CritSect);
     1148    AssertRCReturn(rc, PDMAUDIOBACKENDSTS_UNKNOWN);
     1149
     1150    PDMAUDIOBACKENDSTS fBackendStatus;
     1151    if (pThis->pHostDrvAudio)
     1152    {
     1153        if (pThis->pHostDrvAudio->pfnGetStatus)
     1154            fBackendStatus = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
     1155        else
     1156            fBackendStatus = PDMAUDIOBACKENDSTS_UNKNOWN;
     1157    }
     1158    else
     1159        fBackendStatus = PDMAUDIOBACKENDSTS_NOT_ATTACHED;
     1160
     1161    RTCritSectLeave(&pThis->CritSect);
     1162    LogFlowFunc(("LEAVE - %#x\n", fBackendStatus));
     1163    return fBackendStatus;
     1164}
     1165
     1166
     1167/**
     1168 * Adjusts the request stream configuration, applying our settings.
     1169 *
     1170 * This also does some basic validations.
     1171 *
     1172 * Used by both the stream creation and stream configuration hinting code.
     1173 *
     1174 * @returns VBox status code.
     1175 * @param   pThis       Pointer to the DrvAudio instance data.
     1176 * @param   pCfgReq     The request configuration that should be adjusted.
     1177 * @param   pszName     Stream name to use when logging warnings and errors.
     1178 */
     1179static int drvAudioStreamAdjustConfig(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfgReq, const char *pszName)
     1180{
     1181    /* Get the right configuration for the stream to be created. */
     1182    PDRVAUDIOCFG pDrvCfg = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.Cfg : &pThis->Out.Cfg;
     1183
     1184    /* Fill in the tweakable parameters into the requested host configuration.
     1185     * All parameters in principle can be changed and returned by the backend via the acquired configuration. */
     1186
     1187    /*
     1188     * PCM
     1189     */
     1190    if (PDMAudioPropsSampleSize(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
     1191    {
     1192        PDMAudioPropsSetSampleSize(&pCfgReq->Props, PDMAudioPropsSampleSize(&pDrvCfg->Props));
     1193        LogRel2(("Audio: Using custom sample size of %RU8 bytes for stream '%s'\n",
     1194                 PDMAudioPropsSampleSize(&pCfgReq->Props), pszName));
     1195    }
     1196
     1197    if (pDrvCfg->Props.uHz) /* Anything set via custom extra-data? */
     1198    {
     1199        pCfgReq->Props.uHz = pDrvCfg->Props.uHz;
     1200        LogRel2(("Audio: Using custom Hz rate %RU32 for stream '%s'\n", pCfgReq->Props.uHz, pszName));
     1201    }
     1202
     1203    if (pDrvCfg->uSigned != UINT8_MAX) /* Anything set via custom extra-data? */
     1204    {
     1205        pCfgReq->Props.fSigned = RT_BOOL(pDrvCfg->uSigned);
     1206        LogRel2(("Audio: Using custom %s sample format for stream '%s'\n",
     1207                 pCfgReq->Props.fSigned ? "signed" : "unsigned", pszName));
     1208    }
     1209
     1210    if (pDrvCfg->uSwapEndian != UINT8_MAX) /* Anything set via custom extra-data? */
     1211    {
     1212        pCfgReq->Props.fSwapEndian = RT_BOOL(pDrvCfg->uSwapEndian);
     1213        LogRel2(("Audio: Using custom %s endianess for samples of stream '%s'\n",
     1214                 pCfgReq->Props.fSwapEndian ? "swapped" : "original", pszName));
     1215    }
     1216
     1217    if (PDMAudioPropsChannels(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
     1218    {
     1219        PDMAudioPropsSetChannels(&pCfgReq->Props, PDMAudioPropsChannels(&pDrvCfg->Props));
     1220        LogRel2(("Audio: Using custom %RU8 channel(s) for stream '%s'\n", PDMAudioPropsChannels(&pDrvCfg->Props), pszName));
     1221    }
     1222
     1223    /* Validate PCM properties. */
     1224    if (!AudioHlpPcmPropsAreValid(&pCfgReq->Props))
     1225    {
     1226        LogRel(("Audio: Invalid custom PCM properties set for stream '%s', cannot create stream\n", pszName));
     1227        return VERR_INVALID_PARAMETER;
     1228    }
     1229
     1230    /*
     1231     * Period size
     1232     */
     1233    const char *pszWhat = "device-specific";
     1234    if (pDrvCfg->uPeriodSizeMs)
     1235    {
     1236        pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPeriodSizeMs);
     1237        pszWhat = "custom";
     1238    }
     1239
     1240    if (!pCfgReq->Backend.cFramesPeriod) /* Set default period size if nothing explicitly is set. */
     1241    {
     1242        pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 150 /*ms*/);
     1243        pszWhat = "default";
     1244    }
     1245
     1246    LogRel2(("Audio: Using %s period size %RU64 ms / %RU32 frames for stream '%s'\n",
     1247             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod),
     1248             pCfgReq->Backend.cFramesPeriod, pszName));
     1249
     1250    /*
     1251     * Buffer size
     1252     */
     1253    pszWhat = "device-specific";
     1254    if (pDrvCfg->uBufferSizeMs)
     1255    {
     1256        pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uBufferSizeMs);
     1257        pszWhat = "custom";
     1258    }
     1259
     1260    if (!pCfgReq->Backend.cFramesBufferSize) /* Set default buffer size if nothing explicitly is set. */
     1261    {
     1262        pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 300 /*ms*/);
     1263        pszWhat = "default";
     1264    }
     1265
     1266    LogRel2(("Audio: Using %s buffer size %RU64 ms / %RU32 frames for stream '%s'\n",
     1267             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
     1268             pCfgReq->Backend.cFramesBufferSize, pszName));
     1269
     1270    /*
     1271     * Pre-buffering size
     1272     */
     1273    pszWhat = "device-specific";
     1274    if (pDrvCfg->uPreBufSizeMs != UINT32_MAX) /* Anything set via global / per-VM extra-data? */
     1275    {
     1276        pCfgReq->Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPreBufSizeMs);
     1277        pszWhat = "custom";
     1278    }
     1279    else /* No, then either use the default or device-specific settings (if any). */
     1280    {
     1281        if (pCfgReq->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */
     1282        {
     1283            /* Pre-buffer 66% of the buffer. */
     1284            pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize * 2 / 3;
     1285            pszWhat = "default";
     1286        }
     1287    }
     1288
     1289    LogRel2(("Audio: Using %s pre-buffering size %RU64 ms / %RU32 frames for stream '%s'\n",
     1290             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
     1291             pCfgReq->Backend.cFramesPreBuffering, pszName));
     1292
     1293    /*
     1294     * Validate input.
     1295     */
     1296    if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPeriod)
     1297    {
     1298        LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the period size (%RU64ms)\n",
     1299                pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
     1300                PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod)));
     1301        return VERR_INVALID_PARAMETER;
     1302    }
     1303
     1304    if (   pCfgReq->Backend.cFramesPreBuffering != UINT32_MAX /* Custom pre-buffering set? */
     1305        && pCfgReq->Backend.cFramesPreBuffering)
     1306    {
     1307        if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPreBuffering)
     1308        {
     1309            LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the pre-buffering size (%RU64ms)\n",
     1310                    pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
     1311                    PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize)));
     1312            return VERR_INVALID_PARAMETER;
     1313        }
     1314    }
     1315
     1316    return VINF_SUCCESS;
     1317}
     1318
     1319
     1320/**
     1321 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamConfigHint}
     1322 */
     1323static DECLCALLBACK(void) drvAudioStreamConfigHint(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg)
     1324{
     1325    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     1326    AssertReturnVoid(pCfg->enmDir == PDMAUDIODIR_IN || pCfg->enmDir == PDMAUDIODIR_OUT);
     1327
     1328    int rc = RTCritSectEnter(&pThis->CritSect); /** @todo Reconsider the locking for DrvAudio */
     1329    AssertRCReturnVoid(rc);
     1330
     1331    /*
     1332     * Don't do anything unless the backend has a pfnStreamConfigHint method
     1333     * and the direction is currently enabled.
     1334     */
     1335    if (   pThis->pHostDrvAudio
     1336        && pThis->pHostDrvAudio->pfnStreamConfigHint)
     1337    {
     1338        if (pCfg->enmDir == PDMAUDIODIR_OUT ? pThis->Out.fEnabled : pThis->In.fEnabled)
     1339        {
     1340            /*
     1341             * Adjust the configuration (applying out settings) then call the backend driver.
     1342             */
     1343            rc = drvAudioStreamAdjustConfig(pThis, pCfg, pCfg->szName);
     1344            AssertLogRelRC(rc);
     1345            if (RT_SUCCESS(rc))
     1346                pThis->pHostDrvAudio->pfnStreamConfigHint(pThis->pHostDrvAudio, pCfg);
     1347        }
     1348        else
     1349            LogFunc(("Ignoring hint because direction is not currently enabled\n"));
     1350    }
     1351    else
     1352        LogFlowFunc(("Ignoring hint because backend has no pfnStreamConfigHint method.\n"));
     1353
     1354    RTCritSectLeave(&pThis->CritSect);
     1355}
     1356
     1357
     1358/**
     1359 * Worker for drvAudioStreamInitInternal and drvAudioStreamReInitInternal that
     1360 * creates the backend (host driver) side of an audio stream.
     1361 *
     1362 * @returns VBox status code.
     1363 * @param   pThis       Pointer to driver instance.
     1364 * @param   pStreamEx   Audio stream to create the backend side for.
     1365 * @param   pCfgReq     Requested audio stream configuration to use for
     1366 *                      stream creation.
     1367 * @param   pCfgAcq     Acquired audio stream configuration returned by
     1368 *                      the backend.
     1369 *
     1370 * @note    Configuration precedence for requested audio stream configuration (first has highest priority, if set):
     1371 *          - per global extra-data
     1372 *          - per-VM extra-data
     1373 *          - requested configuration (by pCfgReq)
     1374 *          - default value
     1375 */
     1376static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
     1377                                               PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     1378{
     1379    AssertMsg((pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED) == 0,
     1380              ("Stream '%s' already initialized in backend\n", pStreamEx->Core.szName));
     1381
     1382    /*
     1383     * Adjust the requested stream config, applying our settings.
     1384     */
     1385    int rc = drvAudioStreamAdjustConfig(pThis, pCfgReq, pStreamEx->Core.szName);
     1386    if (RT_FAILURE(rc))
     1387        return rc;
     1388
     1389    /*
     1390     * Make the acquired host configuration the requested host configuration initially,
     1391     * in case the backend does not report back an acquired configuration.
     1392     */
     1393    /** @todo r=bird: This is conveniently not documented in the interface... */
     1394    rc = PDMAudioStrmCfgCopy(pCfgAcq, pCfgReq);
     1395    if (RT_FAILURE(rc))
     1396    {
     1397        LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
     1398                pStreamEx->Core.szName));
     1399        return rc;
     1400    }
     1401
     1402    /*
     1403     * Call the host driver to create the stream.
     1404     */
     1405    AssertPtr(pThis->pHostDrvAudio);
     1406    if (pThis->pHostDrvAudio)
     1407        rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pStreamEx->pBackend, pCfgReq, pCfgAcq);
     1408    else
     1409        rc = VERR_PDM_NO_ATTACHED_DRIVER;
     1410    if (RT_FAILURE(rc))
     1411    {
     1412        if (rc == VERR_NOT_SUPPORTED)
     1413            LogRel2(("Audio: Creating stream '%s' in backend not supported\n", pStreamEx->Core.szName));
     1414        else if (rc == VERR_AUDIO_STREAM_COULD_NOT_CREATE)
     1415            LogRel2(("Audio: Stream '%s' could not be created in backend because of missing hardware / drivers\n", pStreamEx->Core.szName));
     1416        else
     1417            LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pStreamEx->Core.szName, rc));
     1418        return rc;
     1419    }
     1420
     1421    /* Validate acquired configuration. */
     1422    char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
     1423    AssertLogRelMsgReturn(AudioHlpStreamCfgIsValid(pCfgAcq),
     1424                          ("Audio: Creating stream '%s' returned an invalid backend configuration (%s), skipping\n",
     1425                           pStreamEx->Core.szName, PDMAudioPropsToString(&pCfgAcq->Props, szTmp, sizeof(szTmp))),
     1426                          VERR_INVALID_PARAMETER);
     1427
     1428    /* Let the user know that the backend changed one of the values requested above. */
     1429    if (pCfgAcq->Backend.cFramesBufferSize != pCfgReq->Backend.cFramesBufferSize)
     1430        LogRel2(("Audio: Buffer size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
     1431                 pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesBufferSize), pCfgAcq->Backend.cFramesBufferSize));
     1432
     1433    if (pCfgAcq->Backend.cFramesPeriod != pCfgReq->Backend.cFramesPeriod)
     1434        LogRel2(("Audio: Period size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
     1435                 pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPeriod), pCfgAcq->Backend.cFramesPeriod));
     1436
     1437    /* Was pre-buffering requested, but the acquired configuration from the backend told us something else? */
     1438    if (pCfgReq->Backend.cFramesPreBuffering)
     1439    {
     1440        if (pCfgAcq->Backend.cFramesPreBuffering != pCfgReq->Backend.cFramesPreBuffering)
     1441            LogRel2(("Audio: Pre-buffering size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
     1442                     pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
     1443
     1444        if (pCfgAcq->Backend.cFramesPreBuffering > pCfgAcq->Backend.cFramesBufferSize)
     1445        {
     1446            pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesBufferSize;
     1447            LogRel2(("Audio: Pre-buffering size bigger than buffer size for stream '%s', adjusting to %RU64ms (%RU32 frames)\n",
     1448                     pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
     1449        }
     1450    }
     1451    else if (pCfgReq->Backend.cFramesPreBuffering == 0) /* Was the pre-buffering requested as being disabeld? Tell the users. */
     1452    {
     1453        LogRel2(("Audio: Pre-buffering is disabled for stream '%s'\n", pStreamEx->Core.szName));
     1454        pCfgAcq->Backend.cFramesPreBuffering = 0;
     1455    }
     1456
     1457    /* Sanity for detecting buggy backends. */
     1458    AssertMsgReturn(pCfgAcq->Backend.cFramesPeriod < pCfgAcq->Backend.cFramesBufferSize,
     1459                    ("Acquired period size must be smaller than buffer size\n"),
     1460                    VERR_INVALID_PARAMETER);
     1461    AssertMsgReturn(pCfgAcq->Backend.cFramesPreBuffering <= pCfgAcq->Backend.cFramesBufferSize,
     1462                    ("Acquired pre-buffering size must be smaller or as big as the buffer size\n"),
     1463                    VERR_INVALID_PARAMETER);
     1464
     1465    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
     1466
     1467    return VINF_SUCCESS;
     1468}
     1469
     1470
     1471/**
     1472 * Worker for drvAudioStreamCreate that initializes the audio stream.
     1473 *
     1474 * @returns VBox status code.
     1475 * @param   pThis       Pointer to driver instance.
     1476 * @param   pStreamEx   Stream to initialize.
     1477 * @param   fFlags      PDMAUDIOSTREAM_CREATE_F_XXX.
     1478 * @param   pCfgHost    Stream configuration to use for the host side (backend).
     1479 * @param   pCfgGuest   Stream configuration to use for the guest side.
     1480 */
     1481static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t fFlags,
     1482                                      PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
     1483{
     1484    /*
     1485     * Init host stream.
     1486     */
     1487    pStreamEx->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
     1488
     1489    /* Set the host's default audio data layout. */
     1490/** @todo r=bird: Why, oh why? OTOH, the layout stuff is non-sense anyway. */
     1491    pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
     1492
     1493#ifdef LOG_ENABLED
     1494    LogFunc(("[%s] Requested host format:\n", pStreamEx->Core.szName));
     1495    PDMAudioStrmCfgLog(pCfgHost);
     1496#endif
     1497
     1498    LogRel2(("Audio: Creating stream '%s'\n", pStreamEx->Core.szName));
     1499    LogRel2(("Audio: Guest %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
     1500             pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
     1501             pCfgGuest->Props.uHz, PDMAudioPropsSampleBits(&pCfgGuest->Props), pCfgGuest->Props.fSigned ? "S" : "U",
     1502             PDMAudioPropsChannels(&pCfgGuest->Props), PDMAudioPropsChannels(&pCfgGuest->Props) == 1 ? "" : "s"));
     1503    LogRel2(("Audio: Requested host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
     1504             pCfgHost->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
     1505             pCfgHost->Props.uHz, PDMAudioPropsSampleBits(&pCfgHost->Props), pCfgHost->Props.fSigned ? "S" : "U",
     1506             PDMAudioPropsChannels(&pCfgHost->Props), PDMAudioPropsChannels(&pCfgHost->Props) == 1 ? "" : "s"));
     1507
     1508    PDMAUDIOSTREAMCFG CfgHostAcq;
     1509    int rc = drvAudioStreamCreateInternalBackend(pThis, pStreamEx, pCfgHost, &CfgHostAcq);
     1510    if (RT_FAILURE(rc))
     1511        return rc;
     1512
     1513    LogFunc(("[%s] Acquired host format:\n",  pStreamEx->Core.szName));
     1514    PDMAudioStrmCfgLog(&CfgHostAcq);
     1515    LogRel2(("Audio: Acquired host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
     1516             CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback",  pStreamEx->Core.szName,
     1517             CfgHostAcq.Props.uHz, PDMAudioPropsSampleBits(&CfgHostAcq.Props), CfgHostAcq.Props.fSigned ? "S" : "U",
     1518             PDMAudioPropsChannels(&CfgHostAcq.Props), PDMAudioPropsChannels(&CfgHostAcq.Props) == 1 ? "" : "s"));
     1519    Assert(PDMAudioPropsAreValid(&CfgHostAcq.Props));
     1520
     1521    /* Set the stream properties (currently guest side, when DevSB16 is
     1522       converted to mixer and PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF becomes
     1523       default, this will just be the stream properties). */
     1524    if (fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF)
     1525        pStreamEx->Core.Props = CfgHostAcq.Props;
     1526    else
     1527        pStreamEx->Core.Props = pCfgGuest->Props;
     1528
     1529    /* Let the user know if the backend changed some of the tweakable values. */
     1530    if (CfgHostAcq.Backend.cFramesBufferSize != pCfgHost->Backend.cFramesBufferSize)
     1531        LogRel2(("Audio: Backend changed buffer size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
     1532                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesBufferSize), pCfgHost->Backend.cFramesBufferSize,
     1533                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
     1534
     1535    if (CfgHostAcq.Backend.cFramesPeriod != pCfgHost->Backend.cFramesPeriod)
     1536        LogRel2(("Audio: Backend changed period size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
     1537                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPeriod), pCfgHost->Backend.cFramesPeriod,
     1538                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod), CfgHostAcq.Backend.cFramesPeriod));
     1539
     1540    if (CfgHostAcq.Backend.cFramesPreBuffering != pCfgHost->Backend.cFramesPreBuffering)
     1541        LogRel2(("Audio: Backend changed pre-buffering size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
     1542                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPreBuffering), pCfgHost->Backend.cFramesPreBuffering,
     1543                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
     1544
     1545    /*
     1546     * Check if the backend did return sane values and correct if necessary.
     1547     * Should never happen with our own backends, but you never know ...
     1548     */
     1549    uint32_t const cFramesPreBufferingMax = CfgHostAcq.Backend.cFramesBufferSize - RT_MIN(16, CfgHostAcq.Backend.cFramesBufferSize);
     1550    if (CfgHostAcq.Backend.cFramesPreBuffering > cFramesPreBufferingMax)
     1551    {
     1552        LogRel2(("Audio: Warning: Pre-buffering size of %RU32 frames for stream '%s' is too close to or larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n",
     1553                 CfgHostAcq.Backend.cFramesPreBuffering, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, cFramesPreBufferingMax));
     1554        AssertFailed();
     1555        CfgHostAcq.Backend.cFramesPreBuffering = cFramesPreBufferingMax;
     1556    }
     1557
     1558    if (CfgHostAcq.Backend.cFramesPeriod > CfgHostAcq.Backend.cFramesBufferSize)
     1559    {
     1560        LogRel2(("Audio: Warning: Period size of %RU32 frames for stream '%s' is larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n",
     1561                 CfgHostAcq.Backend.cFramesPeriod, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, CfgHostAcq.Backend.cFramesBufferSize / 2));
     1562        AssertFailed();
     1563        CfgHostAcq.Backend.cFramesPeriod = CfgHostAcq.Backend.cFramesBufferSize / 2;
     1564    }
     1565
     1566    LogRel2(("Audio: Buffer size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
     1567             PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
     1568    LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
     1569             PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
     1570
     1571    /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
     1572    const uint32_t msPeriod = PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod);
     1573    LogRel2(("Audio: Period size of stream '%s' is %RU64 ms / %RU32 frames\n",
     1574             pStreamEx->Core.szName, msPeriod, CfgHostAcq.Backend.cFramesPeriod));
     1575
     1576    if (   pCfgGuest->Device.cMsSchedulingHint             /* Any scheduling hint set? */
     1577        && pCfgGuest->Device.cMsSchedulingHint > msPeriod) /* This might lead to buffer underflows. */
     1578        LogRel(("Audio: Warning: Scheduling hint of stream '%s' is bigger (%RU64ms) than used period size (%RU64ms)\n",
     1579                pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint, msPeriod));
     1580
     1581    /*
     1582     * Make a copy of the acquired host stream configuration and the guest side one.
     1583     */
     1584    rc = PDMAudioStrmCfgCopy(&pStreamEx->Host.Cfg, &CfgHostAcq);
     1585    AssertRC(rc);
     1586
     1587    /* Set the guests's default audio data layout. */
     1588    pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; /** @todo r=bird: WTF DO WE DO THIS?  It's input and probably should've been const... */
     1589    rc = PDMAudioStrmCfgCopy(&pStreamEx->Guest.Cfg, pCfgGuest);
     1590    AssertRC(rc);
     1591
     1592    /*
     1593     * Configure host buffers.
     1594     */
     1595
     1596    /* Destroy any former mixing buffer. */
     1597    AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
     1598
     1599    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
     1600    {
     1601        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
     1602        rc = AudioMixBufInit(&pStreamEx->Host.MixBuf, pStreamEx->Core.szName, &CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize);
     1603        AssertRCReturn(rc, rc);
     1604    }
     1605    /* Allocate space for pre-buffering of output stream w/o mixing buffers. */
     1606    else if (pCfgHost->enmDir == PDMAUDIODIR_OUT)
     1607    {
     1608        Assert(pStreamEx->Out.cbPreBufAlloc == 0);
     1609        Assert(pStreamEx->Out.cbPreBufThreshold == 0);
     1610        Assert(pStreamEx->Out.cbPreBuffered == 0);
     1611        if (CfgHostAcq.Backend.cFramesPreBuffering != 0)
     1612        {
     1613            pStreamEx->Out.cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering);
     1614            pStreamEx->Out.cbPreBufAlloc = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize - 2);
     1615            pStreamEx->Out.cbPreBufAlloc = RT_MIN(RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K),
     1616                                                  pStreamEx->Out.cbPreBufAlloc);
     1617            pStreamEx->Out.pbPreBuf = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
     1618            AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY);
     1619        }
     1620    }
     1621
     1622    /*
     1623     * Init guest stream.
     1624     */
     1625    if (pCfgGuest->Device.cMsSchedulingHint)
     1626        LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms (%RU32 bytes)\n",
     1627                 pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint,
     1628                 PDMAudioPropsMilliToBytes(&pCfgGuest->Props, pCfgGuest->Device.cMsSchedulingHint)));
     1629
     1630    /* Destroy any former mixing buffer. */
     1631    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
     1632
     1633    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
     1634    {
     1635        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
     1636        rc = AudioMixBufInit(&pStreamEx->Guest.MixBuf, pStreamEx->Core.szName, &pCfgGuest->Props, CfgHostAcq.Backend.cFramesBufferSize);
     1637        AssertRCReturn(rc, rc);
     1638    }
     1639
     1640    if (RT_FAILURE(rc))
     1641        LogRel(("Audio: Creating stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
     1642
     1643    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
     1644    {
     1645        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
     1646        /* Host (Parent) -> Guest (Child). */
     1647        rc = AudioMixBufLinkTo(&pStreamEx->Host.MixBuf, &pStreamEx->Guest.MixBuf);
     1648        AssertRC(rc);
     1649    }
     1650
     1651    /*
     1652     * Register statistics.
     1653     */
     1654    PPDMDRVINS const pDrvIns = pThis->pDrvIns;
     1655    /** @todo expose config and more. */
     1656    PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.Cfg.Backend.cFramesBufferSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1657                           "Host side: The size of the backend buffer (in frames)", "%s/0-HostBackendBufSize", pStreamEx->Core.szName);
     1658    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
     1659    {
     1660        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
     1661        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1662                               "Host side: The size of the mixer buffer (in frames)",   "%s/1-HostMixBufSize", pStreamEx->Core.szName);
     1663        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1664                               "Guest side: The size of the mixer buffer (in frames)",  "%s/2-GuestMixBufSize", pStreamEx->Core.szName);
     1665        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cMixed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1666                               "Host side: Number of frames in the mixer buffer",   "%s/1-HostMixBufUsed", pStreamEx->Core.szName);
     1667        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1668                               "Guest side: Number of frames in the mixer buffer",  "%s/2-GuestMixBufUsed", pStreamEx->Core.szName);
     1669    }
     1670    if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
     1671    {
     1672        /** @todo later? */
     1673    }
     1674    else
     1675    {
     1676        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableBefore, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1677                               "Host side: Free space in backend buffer before play", "%s/0-HostBackendBufFreeBefore", pStreamEx->Core.szName);
     1678        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableAfter, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1679                               "Host side: Free space in backend buffer after play",  "%s/0-HostBackendBufFreeAfter", pStreamEx->Core.szName);
     1680    }
     1681
     1682#ifdef VBOX_WITH_STATISTICS
     1683    if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
     1684    {
     1685        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1686                               "Total frames played.", "%s/TotalFramesCaptured", pStreamEx->Core.szName);
     1687        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1688                               "Total number of playbacks.", "%s/TotalTimesCaptured", pStreamEx->Core.szName);
     1689        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1690                               "Total frames read.", "%s/TotalFramesRead", pStreamEx->Core.szName);
     1691        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1692                               "Total number of reads.", "%s/TotalTimesRead", pStreamEx->Core.szName);
     1693    }
     1694    else
     1695    {
     1696        Assert(pCfgGuest->enmDir == PDMAUDIODIR_OUT);
     1697        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1698                               "Total frames played.", "%s/TotalFramesPlayed", pStreamEx->Core.szName);
     1699        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1700                               "Total number of playbacks.", "%s/TotalTimesPlayed", pStreamEx->Core.szName);
     1701        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1702                               "Total frames written.", "%s/TotalFramesWritten", pStreamEx->Core.szName);
     1703        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
     1704                               "Total number of writes.", "%s/TotalTimesWritten", pStreamEx->Core.szName);
     1705    }
     1706#endif /* VBOX_WITH_STATISTICS */
     1707
     1708    LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
     1709    return rc;
     1710}
     1711
     1712
     1713/**
     1714 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
     1715 */
     1716static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface, uint32_t fFlags, PPDMAUDIOSTREAMCFG pCfgHost,
     1717                                              PPDMAUDIOSTREAMCFG pCfgGuest, PPDMAUDIOSTREAM *ppStream)
     1718{
     1719    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     1720    AssertPtr(pThis);
     1721
     1722    /*
     1723     * Assert sanity.
     1724     */
     1725    AssertReturn(!(fFlags & ~PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);
     1726    AssertPtrReturn(pCfgHost,   VERR_INVALID_POINTER);
     1727    AssertPtrReturn(pCfgGuest,  VERR_INVALID_POINTER);
     1728    AssertPtrReturn(ppStream,   VERR_INVALID_POINTER);
     1729    LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
     1730#ifdef LOG_ENABLED
     1731    PDMAudioStrmCfgLog(pCfgHost);
     1732    PDMAudioStrmCfgLog(pCfgGuest);
     1733#endif
     1734    AssertReturn(AudioHlpStreamCfgIsValid(pCfgHost), VERR_INVALID_PARAMETER);
     1735    AssertReturn(AudioHlpStreamCfgIsValid(pCfgGuest), VERR_INVALID_PARAMETER);
     1736    AssertReturn(pCfgHost->enmDir == pCfgGuest->enmDir, VERR_MISMATCH);
     1737    AssertReturn(pCfgHost->enmDir == PDMAUDIODIR_IN || pCfgHost->enmDir == PDMAUDIODIR_OUT, VERR_NOT_SUPPORTED);
     1738    /* Require PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF for output streams: */
     1739    AssertReturn((fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF) || pCfgHost->enmDir == PDMAUDIODIR_IN, VERR_INVALID_FLAGS);
     1740
     1741    /*
     1742     * Lock the whole driver instance.
     1743     */
     1744    int rc = RTCritSectEnter(&pThis->CritSect);
     1745    AssertRCReturn(rc, rc);
     1746
     1747    /*
     1748     * Check that we have free streams in the backend and get the
     1749     * size of the backend specific stream data.
     1750     */
     1751    uint32_t *pcFreeStreams;
     1752    if (pCfgHost->enmDir == PDMAUDIODIR_IN)
     1753    {
     1754        if (!pThis->In.cStreamsFree)
     1755        {
     1756            LogFlowFunc(("Maximum number of host input streams reached\n"));
     1757            rc = VERR_AUDIO_NO_FREE_INPUT_STREAMS;
     1758        }
     1759        pcFreeStreams = &pThis->In.cStreamsFree;
     1760    }
     1761    else /* Out */
     1762    {
     1763        if (!pThis->Out.cStreamsFree)
     1764        {
     1765            LogFlowFunc(("Maximum number of host output streams reached\n"));
     1766            rc = VERR_AUDIO_NO_FREE_OUTPUT_STREAMS;
     1767        }
     1768        pcFreeStreams = &pThis->Out.cStreamsFree;
     1769    }
     1770    size_t const cbHstStrm = pThis->BackendCfg.cbStream;
     1771    AssertStmt(cbHstStrm < _16M, rc = VERR_OUT_OF_RANGE);
     1772    if (RT_SUCCESS(rc))
     1773    {
     1774        /*
     1775         * Allocate and initialize common state.
     1776         */
     1777        PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)RTMemAllocZ(sizeof(DRVAUDIOSTREAM) + RT_ALIGN_Z(cbHstStrm, 64));
     1778        if (pStreamEx)
     1779        {
     1780            /* Retrieve host driver name for easier identification. */
     1781            AssertPtr(pThis->pHostDrvAudio);
     1782            RTStrPrintf(pStreamEx->Core.szName, RT_ELEMENTS(pStreamEx->Core.szName), "[%s] %s",
     1783                        pThis->BackendCfg.szName, pCfgHost->szName[0] != '\0' ? pCfgHost->szName : "<Untitled>");
     1784
     1785            pStreamEx->Core.enmDir    = pCfgHost->enmDir;
     1786            pStreamEx->Core.cbBackend = (uint32_t)cbHstStrm;
     1787            if (cbHstStrm)
     1788                pStreamEx->pBackend   = (PPDMAUDIOBACKENDSTREAM)(pStreamEx + 1);
     1789            pStreamEx->fNoMixBufs     = RT_BOOL(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF);
     1790            pStreamEx->uMagic         = DRVAUDIOSTREAM_MAGIC;
     1791
     1792            /*
     1793             * Try to init the rest.
     1794             */
     1795            rc = drvAudioStreamInitInternal(pThis, pStreamEx, fFlags, pCfgHost, pCfgGuest);
     1796            if (RT_SUCCESS(rc))
     1797            {
     1798                /* Set initial reference counts. */
     1799                pStreamEx->Core.cRefs = 1;
     1800
     1801                /* Decrement the free stream counter. */
     1802                Assert(*pcFreeStreams > 0);
     1803                *pcFreeStreams -= 1;
     1804
     1805                /*
     1806                 * We're good.
     1807                 */
     1808                RTListAppend(&pThis->lstStreams, &pStreamEx->ListEntry);
     1809                STAM_COUNTER_INC(&pThis->Stats.TotalStreamsCreated);
     1810                *ppStream = &pStreamEx->Core;
     1811
     1812                /*
     1813                 * Init debug stuff if enabled (ignore failures).
     1814                 */
     1815                if (pCfgHost->enmDir == PDMAUDIODIR_IN)
     1816                {
     1817                    if (pThis->In.Cfg.Dbg.fEnabled)
     1818                    {
     1819                        AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileCaptureNonInterleaved, pThis->In.Cfg.Dbg.szPathOut,
     1820                                                  "DrvAudioCapNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
     1821                        AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileStreamRead, pThis->In.Cfg.Dbg.szPathOut,
     1822                                                  "DrvAudioRead", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
     1823                    }
     1824                }
     1825                else /* Out */
     1826                {
     1827                    if (pThis->Out.Cfg.Dbg.fEnabled)
     1828                    {
     1829                        AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pThis->Out.Cfg.Dbg.szPathOut,
     1830                                                  "DrvAudioPlayNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
     1831                        AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFileStreamWrite, pThis->Out.Cfg.Dbg.szPathOut,
     1832                                                  "DrvAudioWrite", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
     1833                    }
     1834                }
     1835            }
     1836            else
     1837            {
     1838                LogFunc(("drvAudioStreamInitInternal failed: %Rrc\n", rc));
     1839                int rc2 = drvAudioStreamUninitInternal(pThis, pStreamEx);
     1840                AssertRC(rc2);
     1841                drvAudioStreamFree(pStreamEx);
     1842            }
     1843        }
     1844        else
     1845            rc = VERR_NO_MEMORY;
     1846    }
     1847
     1848    RTCritSectLeave(&pThis->CritSect);
     1849    LogFlowFuncLeaveRC(rc);
     1850    return rc;
     1851}
     1852
     1853
     1854/**
     1855 * Calls the backend to give it the chance to destroy its part of the audio stream.
     1856 *
     1857 * Called from drvAudioPowerOff, drvAudioStreamUninitInternal and
     1858 * drvAudioStreamReInitInternal.
     1859 *
     1860 * @returns VBox status code.
     1861 * @param   pThis       Pointer to driver instance.
     1862 * @param   pStreamEx   Audio stream destruct backend for.
     1863 */
     1864static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
     1865{
     1866    AssertPtr(pThis);
     1867    AssertPtr(pStreamEx);
     1868
     1869    int rc = VINF_SUCCESS;
     1870
     1871#ifdef LOG_ENABLED
     1872    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
     1873#endif
     1874    LogFunc(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
     1875
     1876    if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED)
     1877    {
     1878        AssertPtr(pStreamEx->pBackend);
     1879
     1880        /* Check if the pointer to  the host audio driver is still valid.
     1881         * It can be NULL if we were called in drvAudioDestruct, for example. */
     1882        if (pThis->pHostDrvAudio)
     1883            rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pStreamEx->pBackend);
     1884
     1885        pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
     1886    }
     1887
     1888    LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
     1889    return rc;
     1890}
     1891
     1892
     1893/**
     1894 * Uninitializes an audio stream - worker for drvAudioStreamDestroy,
     1895 * drvAudioDestruct and drvAudioStreamCreate.
     1896 *
     1897 * @returns VBox status code.
     1898 * @param   pThis       Pointer to driver instance.
     1899 * @param   pStreamEx   Pointer to audio stream to uninitialize.
     1900 *
     1901 * @note    Caller owns the critical section.
     1902 */
     1903static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
     1904{
     1905    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
     1906    AssertMsgReturn(pStreamEx->Core.cRefs <= 1,
     1907                    ("Stream '%s' still has %RU32 references held when uninitializing\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs),
     1908                    VERR_WRONG_ORDER);
     1909    LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
     1910
     1911    /*
     1912     * ...
     1913     */
     1914    int rc = drvAudioStreamControlInternal(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     1915    if (RT_SUCCESS(rc))
     1916        rc = drvAudioStreamDestroyInternalBackend(pThis, pStreamEx);
     1917
     1918    /* Destroy mixing buffers. */
     1919    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
     1920    AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
     1921
     1922    /* Free pre-buffer space. */
     1923    if (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
     1924        && pStreamEx->Out.pbPreBuf)
     1925    {
     1926        RTMemFree(pStreamEx->Out.pbPreBuf);
     1927        pStreamEx->Out.pbPreBuf      = NULL;
     1928        pStreamEx->Out.cbPreBufAlloc = 0;
     1929        pStreamEx->Out.cbPreBuffered = 0;
     1930    }
     1931
     1932    if (RT_SUCCESS(rc))
     1933    {
     1934#ifdef LOG_ENABLED
     1935        if (pStreamEx->Core.fStatus != PDMAUDIOSTREAMSTS_FLAGS_NONE)
     1936        {
     1937            char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
     1938            LogFunc(("[%s] Warning: Still has %s set when uninitializing\n",
     1939                     pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
     1940        }
     1941#endif
     1942        pStreamEx->Core.fStatus = PDMAUDIOSTREAMSTS_FLAGS_NONE;
     1943    }
     1944
     1945    PPDMDRVINS const pDrvIns = pThis->pDrvIns;
     1946    PDMDrvHlpSTAMDeregisterByPrefix(pDrvIns, pStreamEx->Core.szName);
     1947
     1948    if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
     1949    {
     1950        if (pThis->In.Cfg.Dbg.fEnabled)
     1951        {
     1952            AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileCaptureNonInterleaved);
     1953            pStreamEx->In.Dbg.pFileCaptureNonInterleaved = NULL;
     1954
     1955            AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileStreamRead);
     1956            pStreamEx->In.Dbg.pFileStreamRead = NULL;
     1957        }
     1958    }
     1959    else
     1960    {
     1961        Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT);
     1962        if (pThis->Out.Cfg.Dbg.fEnabled)
     1963        {
     1964            AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFilePlayNonInterleaved);
     1965            pStreamEx->Out.Dbg.pFilePlayNonInterleaved = NULL;
     1966
     1967            AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFileStreamWrite);
     1968            pStreamEx->Out.Dbg.pFileStreamWrite = NULL;
     1969        }
     1970    }
     1971    LogFlowFunc(("Returning %Rrc\n", rc));
     1972    return rc;
     1973}
     1974
     1975
     1976/**
     1977 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
     1978 */
     1979static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
     1980{
     1981    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     1982    AssertPtr(pThis);
     1983
     1984    if (!pStream)
     1985        return VINF_SUCCESS;
     1986    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     1987    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;   /* Note! Do not touch pStream after this! */
     1988    Assert(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
     1989    Assert(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
     1990
     1991    int rc = RTCritSectEnter(&pThis->CritSect);
     1992    AssertRCReturn(rc, rc);
     1993
     1994    LogRel2(("Audio: Destroying stream '%s'\n", pStreamEx->Core.szName));
     1995
     1996    LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
     1997    AssertMsg(pStreamEx->Core.cRefs <= 1, ("%u %s\n", pStreamEx->Core.cRefs, pStreamEx->Core.szName));
     1998    if (pStreamEx->Core.cRefs <= 1)
     1999    {
     2000        rc = drvAudioStreamUninitInternal(pThis, pStreamEx);
     2001        if (RT_SUCCESS(rc))
     2002        {
     2003            if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
     2004                pThis->In.cStreamsFree++;
     2005            else /* Out */
     2006                pThis->Out.cStreamsFree++;
     2007
     2008            RTListNodeRemove(&pStreamEx->ListEntry);
     2009
     2010            drvAudioStreamFree(pStreamEx);
     2011            pStreamEx = NULL;
     2012            pStream = NULL;
     2013        }
     2014        else
     2015            LogRel(("Audio: Uninitializing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
     2016    }
     2017    else
     2018        rc = VERR_WRONG_ORDER;
     2019
     2020    RTCritSectLeave(&pThis->CritSect);
     2021    LogFlowFuncLeaveRC(rc);
     2022    return rc;
     2023}
     2024
     2025
     2026/**
     2027 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
     2028 */
     2029static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
     2030{
     2031   AssertPtrReturn(pInterface, UINT32_MAX);
     2032   AssertPtrReturn(pStream,    UINT32_MAX);
     2033   AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
     2034   AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
     2035   RT_NOREF(pInterface);
     2036
     2037   uint32_t const cRefs = ASMAtomicIncU32(&pStream->cRefs);
     2038   Assert(cRefs > 1);
     2039   Assert(cRefs < _1K);
     2040
     2041   return cRefs;
     2042}
     2043
     2044
     2045/**
     2046 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
     2047 */
     2048static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
     2049{
     2050   AssertPtrReturn(pInterface, UINT32_MAX);
     2051   AssertPtrReturn(pStream,    UINT32_MAX);
     2052   AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
     2053   AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
     2054   RT_NOREF(pInterface);
     2055
     2056   uint32_t cRefs = ASMAtomicDecU32(&pStream->cRefs);
     2057   AssertStmt(cRefs >= 1, cRefs = ASMAtomicIncU32(&pStream->cRefs));
     2058   Assert(cRefs < _1K);
     2059
     2060   return cRefs;
     2061}
     2062
     2063
     2064/**
     2065 * Controls a stream's backend.
     2066 *
     2067 * If the stream has no backend available, VERR_NOT_FOUND is returned
     2068 * (bird: actually the code returns VINF_SUCCESS).
     2069 *
     2070 * @returns VBox status code.
     2071 * @param   pThis           Pointer to driver instance.
     2072 * @param   pStreamEx       Stream to control.
     2073 * @param   enmStreamCmd    Control command.
     2074 */
     2075static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
     2076{
     2077    AssertPtr(pThis);
     2078    AssertPtr(pStreamEx);
     2079
     2080#ifdef LOG_ENABLED
     2081    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
     2082#endif
     2083    LogFlowFunc(("[%s] enmStreamCmd=%s, fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
     2084                 dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
     2085
     2086    if (!pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
     2087        return VINF_SUCCESS;
     2088
     2089
     2090    /*
     2091     * Whether to propagate commands down to the backend.
     2092     *
     2093     * This is needed for critical operations like recording audio if audio input is disabled on a per-driver level.
     2094     *
     2095     * Note that not all commands will be covered by this, such as operations like stopping, draining and droppping,
     2096     * which are considered uncritical and sometimes even are required for certain backends (like DirectSound on Windows).
     2097     *
     2098     * The actual stream state will be untouched to not make the state machine handling more complicated than
     2099     * it already is.
     2100     *
     2101     * See @bugref{9882}.
     2102     */
     2103    const bool fEnabled =    (   pStreamEx->Core.enmDir == PDMAUDIODIR_IN
     2104                              && pThis->In.fEnabled)
     2105                          || (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
     2106                              && pThis->Out.fEnabled);
     2107
     2108    LogRel2(("Audio: %s stream '%s' in backend (%s is %s)\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName,
     2109                                                              PDMAudioDirGetName(pStreamEx->Core.enmDir),
     2110                                                              fEnabled ? "enabled" : "disabled"));
     2111    int rc = VINF_SUCCESS;
     2112    switch (enmStreamCmd)
     2113    {
     2114        case PDMAUDIOSTREAMCMD_ENABLE:
     2115        {
     2116            if (fEnabled)
     2117                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_ENABLE);
     2118            break;
     2119        }
     2120
     2121        case PDMAUDIOSTREAMCMD_DISABLE:
     2122        {
     2123            rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DISABLE);
     2124            break;
     2125        }
     2126
     2127        case PDMAUDIOSTREAMCMD_PAUSE:
     2128        {
     2129            if (fEnabled) /* Needed, as resume below also is being checked for. */
     2130                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_PAUSE);
     2131            break;
     2132        }
     2133
     2134        case PDMAUDIOSTREAMCMD_RESUME:
     2135        {
     2136            if (fEnabled)
     2137                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_RESUME);
     2138            break;
     2139        }
     2140
     2141        case PDMAUDIOSTREAMCMD_DRAIN:
     2142        {
     2143            rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DRAIN);
     2144            break;
     2145        }
     2146
     2147        default:
     2148            AssertMsgFailedReturn(("Command %RU32 not implemented\n", enmStreamCmd), VERR_INTERNAL_ERROR_2);
     2149    }
     2150
     2151    if (RT_FAILURE(rc))
     2152    {
     2153        if (   rc != VERR_NOT_IMPLEMENTED
     2154            && rc != VERR_NOT_SUPPORTED
     2155            && rc != VERR_AUDIO_STREAM_NOT_READY)
     2156        {
     2157            LogRel(("Audio: %s stream '%s' failed with %Rrc\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName, rc));
     2158        }
     2159
     2160        LogFunc(("[%s] %s failed with %Rrc\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd), rc));
     2161    }
     2162
     2163    return rc;
     2164}
     2165
     2166/**
     2167 * Controls an audio stream.
     2168 *
     2169 * @returns VBox status code.
     2170 * @param   pThis           Pointer to driver instance.
     2171 * @param   pStreamEx       Stream to control.
     2172 * @param   enmStreamCmd    Control command.
     2173 */
     2174static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
     2175{
     2176    AssertPtr(pThis);
     2177    AssertPtr(pStreamEx);
     2178
     2179#ifdef LOG_ENABLED
     2180    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
     2181#endif
     2182    LogFunc(("[%s] enmStreamCmd=%s fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
     2183             dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
     2184
     2185    int rc = VINF_SUCCESS;
     2186
     2187    switch (enmStreamCmd)
     2188    {
     2189        case PDMAUDIOSTREAMCMD_ENABLE:
     2190        {
     2191            if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
     2192            {
     2193                /* Is a pending disable outstanding? Then disable first. */
     2194                if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
     2195                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     2196
     2197                if (RT_SUCCESS(rc))
     2198                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
     2199
     2200                if (RT_SUCCESS(rc))
     2201                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
     2202            }
     2203            break;
     2204        }
     2205
     2206        case PDMAUDIOSTREAMCMD_DISABLE:
     2207        {
     2208            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED)
     2209            {
     2210                /*
     2211                 * For playback (output) streams first mark the host stream as pending disable,
     2212                 * so that the rest of the remaining audio data will be played first before
     2213                 * closing the stream.
     2214                 */
     2215                if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
     2216                {
     2217                    LogFunc(("[%s] Pending disable/pause\n", pStreamEx->Core.szName));
     2218                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE;
     2219
     2220                    /* Schedule a follow up timer to the pending-disable state.  We cannot rely
     2221                       on the device to provide further callouts to finish the state transition.
     2222                       10ms is taking out of thin air and may be too course grained, we should
     2223                       really consider the amount of unplayed buffer in the backend and what not... */
     2224                    if (!pThis->fTimerArmed)
     2225                    {
     2226                        LogFlowFunc(("Arming emergency pending-disable hack...\n"));
     2227                        int rc2 = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimer, 10 /*ms*/);
     2228                        AssertRC(rc2);
     2229                        pThis->fTimerArmed = true;
     2230                    }
     2231                }
     2232
     2233                /* Can we close the host stream as well (not in pending disable mode)? */
     2234                if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
     2235                {
     2236                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     2237                    if (RT_SUCCESS(rc))
     2238                        drvAudioStreamResetInternal(pThis, pStreamEx);
     2239                }
     2240            }
     2241            break;
     2242        }
     2243
     2244        case PDMAUDIOSTREAMCMD_PAUSE:
     2245        {
     2246            if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED))
     2247            {
     2248                rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_PAUSE);
     2249                if (RT_SUCCESS(rc))
     2250                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
     2251            }
     2252            break;
     2253        }
     2254
     2255        case PDMAUDIOSTREAMCMD_RESUME:
     2256        {
     2257            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
     2258            {
     2259                rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_RESUME);
     2260                if (RT_SUCCESS(rc))
     2261                    pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
     2262            }
     2263            break;
     2264        }
     2265
     2266        default:
     2267            rc = VERR_NOT_IMPLEMENTED;
     2268            break;
     2269    }
     2270
     2271    if (RT_FAILURE(rc))
     2272        LogFunc(("[%s] Failed with %Rrc\n", pStreamEx->Core.szName, rc));
     2273
     2274    return rc;
     2275}
     2276
     2277
     2278/**
     2279 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
     2280 */
     2281static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
     2282                                               PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
     2283{
     2284    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     2285    AssertPtr(pThis);
     2286
     2287    /** @todo r=bird: why?  It's not documented to ignore NULL streams.   */
     2288    if (!pStream)
     2289        return VINF_SUCCESS;
     2290    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
     2291    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
     2292    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     2293    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     2294
     2295    int rc = RTCritSectEnter(&pThis->CritSect);
     2296    AssertRCReturn(rc, rc);
     2297
     2298    LogFlowFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, PDMAudioStrmCmdGetName(enmStreamCmd)));
     2299
     2300    rc = drvAudioStreamControlInternal(pThis, pStreamEx, enmStreamCmd);
     2301
     2302    RTCritSectLeave(&pThis->CritSect);
     2303    return rc;
     2304}
     2305
     2306
     2307/**
     2308 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
     2309 */
     2310static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
     2311                                            void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
     2312{
     2313    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     2314    AssertPtr(pThis);
     2315    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
     2316    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
     2317    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     2318    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     2319    AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER);
     2320    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     2321    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     2322    AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
     2323              ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
     2324               pStreamEx->Core.szName, pStreamEx->Core.enmDir));
     2325
     2326    int rc = RTCritSectEnter(&pThis->CritSect);
     2327    AssertRCReturn(rc, rc);
     2328
     2329    /*
     2330     * ...
     2331     */
     2332    uint32_t cbReadTotal = 0;
     2333
     2334    do
     2335    {
     2336        uint32_t cfReadTotal = 0;
     2337
     2338        const uint32_t cfBuf = AUDIOMIXBUF_B2F(&pStreamEx->Guest.MixBuf, cbBuf);
     2339
     2340        if (pThis->In.fEnabled) /* Input for this audio driver enabled? See #9822. */
     2341        {
     2342            if (!PDMAudioStrmStatusCanRead(pStream->fStatus))
     2343            {
     2344                rc = VERR_AUDIO_STREAM_NOT_READY;
     2345                break;
     2346            }
     2347
     2348            /*
     2349             * Read from the parent buffer (that is, the guest buffer) which
     2350             * should have the audio data in the format the guest needs.
     2351             */
     2352            uint32_t cfToRead = RT_MIN(cfBuf, AudioMixBufLive(&pStreamEx->Guest.MixBuf));
     2353            while (cfToRead)
     2354            {
     2355                uint32_t cfRead;
     2356                rc = AudioMixBufAcquireReadBlock(&pStreamEx->Guest.MixBuf,
     2357                                                 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
     2358                                                 AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfToRead), &cfRead);
     2359                if (RT_FAILURE(rc))
     2360                    break;
     2361
     2362#ifdef VBOX_WITH_STATISTICS
     2363                const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfRead);
     2364                STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead,    cbRead);
     2365                STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesRead, cfRead);
     2366                STAM_COUNTER_INC(&pStreamEx->In.Stats.TotalTimesRead);
     2367#endif
     2368                Assert(cfToRead >= cfRead);
     2369                cfToRead -= cfRead;
     2370
     2371                cfReadTotal += cfRead;
     2372
     2373                AudioMixBufReleaseReadBlock(&pStreamEx->Guest.MixBuf, cfRead);
     2374            }
     2375
     2376            if (cfReadTotal)
     2377            {
     2378                if (pThis->In.Cfg.Dbg.fEnabled)
     2379                    AudioHlpFileWrite(pStreamEx->In.Dbg.pFileStreamRead,
     2380                                      pvBuf, AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 0 /* fFlags */);
     2381
     2382                AudioMixBufFinish(&pStreamEx->Guest.MixBuf, cfReadTotal);
     2383            }
     2384        }
     2385
     2386        /* If we were not able to read as much data as requested, fill up the returned
     2387         * data with silence.
     2388         *
     2389         * This is needed to keep the device emulation DMA transfers up and running at a constant rate. */
     2390        if (cfReadTotal < cfBuf)
     2391        {
     2392            Log3Func(("[%s] Filling in silence (%RU64ms / %RU64ms)\n", pStream->szName,
     2393                      PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf - cfReadTotal),
     2394                      PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf)));
     2395
     2396            PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props,
     2397                                     (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
     2398                                     AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfBuf - cfReadTotal),
     2399                                     cfBuf - cfReadTotal);
     2400
     2401            cfReadTotal = cfBuf;
     2402        }
     2403
     2404        cbReadTotal = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal);
     2405
     2406        pStreamEx->nsLastReadWritten = RTTimeNanoTS();
     2407
     2408        Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc));
     2409
     2410    } while (0);
     2411
     2412    RTCritSectLeave(&pThis->CritSect);
     2413
     2414    if (RT_SUCCESS(rc) && pcbRead)
     2415        *pcbRead = cbReadTotal;
     2416    return rc;
     2417}
     2418
     2419
     2420/**
     2421 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
     2422 */
     2423static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
     2424                                             const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
     2425{
     2426    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     2427    AssertPtr(pThis);
     2428
     2429    /*
     2430     * Check input and sanity.
     2431     */
     2432    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     2433    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
     2434    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
     2435    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     2436    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     2437    uint32_t uTmp;
     2438    if (!pcbWritten)
     2439        pcbWritten = &uTmp;
     2440    AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
     2441
     2442    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     2443    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     2444    AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT,
     2445                    ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n",
     2446                     pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED);
     2447    Assert(pStreamEx->fNoMixBufs);
     2448
     2449    AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf),
     2450              ("Stream '%s' got a non-frame-aligned write (%RU32 bytes)\n", pStreamEx->Core.szName, cbBuf));
     2451
     2452/// @todo    STAM_PROFILE_ADV_START(&pThis->Stats.DelayOut, out); /* (stopped in drvAudioStreamPlayLocked) */
     2453
     2454    int rc = RTCritSectEnter(&pThis->CritSect);
     2455    AssertRCReturn(rc, rc);
     2456
     2457    /*
     2458     * First check that we can write to the stream, and if not,
     2459     * whether to just drop the input into the bit bucket.
     2460     */
     2461    if (PDMAudioStrmStatusIsReady(pStreamEx->Core.fStatus))
     2462    {
     2463        if (   !pThis->Out.fEnabled         /* (see @bugref{9882}) */
     2464            || pThis->pHostDrvAudio == NULL /* (we used to work this condition differently) */
     2465            || !PDMAudioStrmStatusCanWrite(pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend)))
     2466        {
     2467            Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.szName,
     2468                      !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
     2469            *pcbWritten = cbBuf;
     2470            pStreamEx->offInternal += cbBuf;
     2471        }
     2472        /*
     2473         * No-mixing buffer mode:  Write the data directly to the backend, unless
     2474         * we're prebuffering.  There will be no pfnStreamPlay call in this mode.
     2475         */
     2476        else
     2477        {
     2478            uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore);
     2479            rc = drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
     2480            Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal);
     2481            if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc))
     2482            { /* likely */ }
     2483            else
     2484                AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */);
     2485        }
     2486    }
     2487    else
     2488        rc = VERR_AUDIO_STREAM_NOT_READY;
     2489
     2490    RTCritSectLeave(&pThis->CritSect);
     2491    return rc;
     2492}
     2493
    12062494
    12072495/**
     
    13252613}
    13262614
    1327 /**
    1328  * @callback_method_impl{FNTMTIMERDRV}
    1329  */
    1330 static DECLCALLBACK(void) drvAudioEmergencyIterateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
    1331 {
    1332     PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
    1333     RT_NOREF(hTimer, pvUser);
    1334     RTCritSectEnter(&pThis->CritSect);
    1335 
    1336     /*
    1337      * Iterate any stream with the pending-disable flag set.
    1338      */
    1339     uint32_t        cMilliesToNext = 0;
    1340     PDRVAUDIOSTREAM pStreamEx, pStreamExNext;
    1341     RTListForEachSafe(&pThis->lstStreams, pStreamEx, pStreamExNext, DRVAUDIOSTREAM, ListEntry)
    1342     {
    1343         if (   pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC
    1344             && pStreamEx->Core.cRefs >= 1)
    1345         {
    1346             if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
    1347             {
    1348                 drvAudioStreamIterateInternal(pThis, pStreamEx);
    1349 
    1350                 if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
    1351                     cMilliesToNext = 10;
    1352             }
    1353         }
    1354     }
    1355 
    1356     /*
    1357      * Re-arm the timer if we still got streams in the pending state.
    1358      */
    1359     if (cMilliesToNext)
    1360     {
    1361         pThis->fTimerArmed = true;
    1362         PDMDrvHlpTimerSetMillies(pDrvIns, pThis->hTimer, cMilliesToNext);
    1363     }
    1364     else
    1365         pThis->fTimerArmed = false;
    1366 
    1367     RTCritSectLeave(&pThis->CritSect);
    1368 }
    1369 
    1370 
    1371 /**
    1372  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
    1373  */
    1374 static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed)
    1375 {
    1376     RT_NOREF(pInterface, pStream, pcFramesPlayed);
    1377     AssertFailed(/* OBSOLETE! */);
    1378     return VERR_NOT_SUPPORTED;
    1379 }
    1380 
    1381 
    1382 /**
    1383  * Captures non-interleaved input from a host stream.
    1384  *
    1385  * @returns VBox status code.
    1386  * @param   pThis       Driver instance.
    1387  * @param   pStreamEx   Stream to capture from.
    1388  * @param   pcfCaptured Number of (host) audio frames captured.
    1389  */
    1390 static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
    1391 {
    1392     Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
    1393     Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
    1394 
    1395     /*
    1396      * ...
    1397      */
    1398     AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
    1399     uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
    1400     if (!cbReadable)
    1401         Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
    1402 
    1403     uint32_t cbFree = AudioMixBufFreeBytes(&pStreamEx->Guest.MixBuf); /* Parent */
    1404     if (!cbFree)
    1405         Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
    1406 
    1407     if (cbReadable > cbFree) /* More data readable than we can store at the moment? Limit. */
    1408         cbReadable = cbFree;
    1409 
    1410     /*
    1411      * ...
    1412      */
    1413     int      rc = VINF_SUCCESS;
    1414     uint32_t cfCapturedTotal = 0;
    1415     while (cbReadable)
    1416     {
    1417         uint8_t  abChunk[_4K];
    1418         uint32_t cbCaptured;
    1419         rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
    1420                                                     abChunk, RT_MIN(cbReadable, (uint32_t)sizeof(abChunk)), &cbCaptured);
    1421         if (RT_FAILURE(rc))
    1422         {
    1423             int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    1424             AssertRC(rc2);
    1425             break;
    1426         }
    1427 
    1428         Assert(cbCaptured <= sizeof(abChunk));
    1429         if (cbCaptured > sizeof(abChunk)) /* Paranoia. */
    1430             cbCaptured = (uint32_t)sizeof(abChunk);
    1431 
    1432         if (!cbCaptured) /* Nothing captured? Take a shortcut. */
    1433             break;
    1434 
    1435         /* We use the host side mixing buffer as an intermediate buffer to do some
    1436          * (first) processing (if needed), so always write the incoming data at offset 0. */
    1437         uint32_t cfHstWritten = 0;
    1438         rc = AudioMixBufWriteAt(&pStreamEx->Host.MixBuf, 0 /* offFrames */, abChunk, cbCaptured, &cfHstWritten);
    1439         if (   RT_FAILURE(rc)
    1440             || !cfHstWritten)
    1441         {
    1442             AssertMsgFailed(("[%s] Write failed: cbCaptured=%RU32, cfHstWritten=%RU32, rc=%Rrc\n",
    1443                              pStreamEx->Core.szName, cbCaptured, cfHstWritten, rc));
    1444             break;
    1445         }
    1446 
    1447         if (pThis->In.Cfg.Dbg.fEnabled)
    1448             AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCaptureNonInterleaved, abChunk, cbCaptured, 0 /* fFlags */);
    1449 
    1450         uint32_t cfHstMixed = 0;
    1451         if (cfHstWritten)
    1452         {
    1453             int rc2 = AudioMixBufMixToParentEx(&pStreamEx->Host.MixBuf, 0 /* cSrcOffset */, cfHstWritten /* cSrcFrames */,
    1454                                                &cfHstMixed /* pcSrcMixed */);
    1455             Log3Func(("[%s] cbCaptured=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
    1456                       pStreamEx->Core.szName, cbCaptured, cfHstWritten, cfHstMixed, rc2));
    1457             AssertRC(rc2);
    1458         }
    1459 
    1460         Assert(cbReadable >= cbCaptured);
    1461         cbReadable      -= cbCaptured;
    1462         cfCapturedTotal += cfHstMixed;
    1463     }
    1464 
    1465     if (RT_SUCCESS(rc))
    1466     {
    1467         if (cfCapturedTotal)
    1468             Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
    1469     }
    1470     else
    1471         LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pStreamEx->Core.szName, rc));
    1472 
    1473     if (pcfCaptured)
    1474         *pcfCaptured = cfCapturedTotal;
    1475 
    1476     return rc;
    1477 }
    1478 
    1479 /**
    1480  * Captures raw input from a host stream.
    1481  * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
    1482  * no data layout processing done in between.
    1483  *
    1484  * Needed for e.g. the VRDP audio backend (in Main).
    1485  *
    1486  * @returns VBox status code.
    1487  * @param   pThis       Driver instance.
    1488  * @param   pStreamEx   Stream to capture from.
    1489  * @param   pcfCaptured Number of (host) audio frames captured.
    1490  */
    1491 static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
    1492 {
    1493     Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
    1494     Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
    1495     AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
    1496 
    1497     /*
    1498      * ...
    1499      */
    1500     /* Note: Raw means *audio frames*, not bytes! */
    1501     uint32_t cfReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
    1502     if (!cfReadable)
    1503         Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
    1504 
    1505     const uint32_t cfFree = AudioMixBufFree(&pStreamEx->Guest.MixBuf); /* Parent */
    1506     if (!cfFree)
    1507         Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
    1508 
    1509     if (cfReadable > cfFree) /* More data readable than we can store at the moment? Limit. */
    1510         cfReadable = cfFree;
    1511 
    1512     /*
    1513      * ...
    1514      */
    1515     int      rc              = VINF_SUCCESS;
    1516     uint32_t cfCapturedTotal = 0;
    1517     while (cfReadable)
    1518     {
    1519         PPDMAUDIOFRAME paFrames;
    1520         uint32_t cfWritable;
    1521         rc = AudioMixBufPeekMutable(&pStreamEx->Host.MixBuf, cfReadable, &paFrames, &cfWritable);
    1522         if (   RT_FAILURE(rc)
    1523             || !cfWritable)
    1524             break;
    1525 
    1526         uint32_t cfCaptured;
    1527         rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
    1528                                                     paFrames, cfWritable, &cfCaptured);
    1529         if (RT_FAILURE(rc))
    1530         {
    1531             int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    1532             AssertRC(rc2);
    1533             break;
    1534         }
    1535 
    1536         Assert(cfCaptured <= cfWritable);
    1537         if (cfCaptured > cfWritable) /* Paranoia. */
    1538             cfCaptured = cfWritable;
    1539 
    1540         Assert(cfReadable >= cfCaptured);
    1541         cfReadable      -= cfCaptured;
    1542         cfCapturedTotal += cfCaptured;
    1543     }
    1544 
    1545     if (pcfCaptured)
    1546         *pcfCaptured = cfCapturedTotal;
    1547     Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
    1548     return rc;
    1549 }
    1550 
    1551 /**
    1552  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
    1553  */
    1554 static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
    1555                                                PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
     2615
     2616/**
     2617 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
     2618 */
     2619static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
    15562620{
    15572621    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     
    15592623    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    15602624    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    1561     AssertPtrNull(pcFramesCaptured);
    15622625    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    15632626    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    1564     AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
    1565               ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
    1566                pStreamEx->Core.szName, pStreamEx->Core.enmDir));
     2627
    15672628    int rc = RTCritSectEnter(&pThis->CritSect);
    15682629    AssertRCReturn(rc, rc);
    15692630
    1570 #ifdef LOG_ENABLED
    1571     char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
    1572 #endif
    1573     Log3Func(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
    1574 
    1575     /*
    1576      * ...
    1577      */
    1578     uint32_t cfCaptured = 0;
    1579     do
    1580     {
    1581         if (!pThis->pHostDrvAudio)
    1582         {
    1583             rc = VERR_PDM_NO_ATTACHED_DRIVER;
    1584             break;
    1585         }
    1586 
    1587         if (   !pThis->In.fEnabled
    1588             || !PDMAudioStrmStatusCanRead(pStreamEx->Core.fStatus))
    1589         {
    1590             rc = VERR_AUDIO_STREAM_NOT_READY;
    1591             break;
    1592         }
    1593 
    1594         /*
    1595          * Do the actual capturing.
    1596          */
    1597         if (RT_LIKELY(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
    1598             rc = drvAudioStreamCaptureNonInterleaved(pThis, pStreamEx, &cfCaptured);
    1599         else if (pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
    1600             rc = drvAudioStreamCaptureRaw(pThis, pStreamEx, &cfCaptured);
    1601         else
    1602             AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
    1603 
    1604         if (RT_SUCCESS(rc))
    1605         {
    1606             Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCaptured, rc));
    1607 
    1608             STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn,              cfCaptured);
    1609             STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesCaptured, cfCaptured);
    1610         }
    1611         else if (RT_UNLIKELY(RT_FAILURE(rc)))
    1612             LogRel(("Audio: Capturing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
    1613     } while (0);
     2631    rc = drvAudioStreamIterateInternal(pThis, pStreamEx);
    16142632
    16152633    RTCritSectLeave(&pThis->CritSect);
    1616 
    1617     if (pcFramesCaptured)
    1618         *pcFramesCaptured = cfCaptured;
    16192634
    16202635    if (RT_FAILURE(rc))
     
    16232638}
    16242639
    1625 #ifdef VBOX_WITH_AUDIO_ENUM
    1626 /**
    1627  * Enumerates all host audio devices.
    1628  *
    1629  * This functionality might not be implemented by all backends and will return
    1630  * VERR_NOT_SUPPORTED if not being supported.
    1631  *
    1632  * @note Must not hold the driver's critical section!
    1633  *
    1634  * @returns VBox status code.
    1635  * @param   pThis               Driver instance to be called.
    1636  * @param   fLog                Whether to print the enumerated device to the release log or not.
    1637  * @param   pDevEnum            Where to store the device enumeration.
    1638  *
    1639  * @remarks This is currently ONLY used for release logging.
    1640  */
    1641 static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIOHOSTENUM pDevEnum)
    1642 {
    1643     AssertReturn(!RTCritSectIsOwner(&pThis->CritSect), VERR_WRONG_ORDER);
    1644 
    1645     int rc;
    1646 
    1647     /*
    1648      * If the backend supports it, do a device enumeration.
    1649      */
    1650     if (pThis->pHostDrvAudio->pfnGetDevices)
    1651     {
    1652         PDMAUDIOHOSTENUM DevEnum;
    1653         rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
    1654         if (RT_SUCCESS(rc))
    1655         {
    1656             if (fLog)
    1657                 LogRel(("Audio: Found %RU16 devices for driver '%s'\n", DevEnum.cDevices, pThis->szName));
    1658 
    1659             PPDMAUDIOHOSTDEV pDev;
    1660             RTListForEach(&DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
    1661             {
    1662                 if (fLog)
    1663                 {
    1664                     char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
    1665                     LogRel(("Audio: Device '%s':\n", pDev->szName));
    1666                     LogRel(("Audio:   Usage           = %s\n",   PDMAudioDirGetName(pDev->enmUsage)));
    1667                     LogRel(("Audio:   Flags           = %s\n",   PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags)));
    1668                     LogRel(("Audio:   Input channels  = %RU8\n", pDev->cMaxInputChannels));
    1669                     LogRel(("Audio:   Output channels = %RU8\n", pDev->cMaxOutputChannels));
    1670                 }
    1671             }
    1672 
    1673             if (pDevEnum)
    1674                 rc = PDMAudioHostEnumCopy(pDevEnum, &DevEnum, PDMAUDIODIR_INVALID /*all*/, true /*fOnlyCoreData*/);
    1675 
    1676             PDMAudioHostEnumDelete(&DevEnum);
    1677         }
    1678         else
    1679         {
    1680             if (fLog)
    1681                 LogRel(("Audio: Device enumeration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
    1682             /* Not fatal. */
    1683         }
    1684     }
    1685     else
    1686     {
    1687         rc = VERR_NOT_SUPPORTED;
    1688 
    1689         if (fLog)
    1690             LogRel2(("Audio: Host driver '%s' does not support audio device enumeration, skipping\n", pThis->szName));
    1691     }
    1692 
    1693     LogFunc(("Returning %Rrc\n", rc));
    1694     return rc;
    1695 }
    1696 #endif /* VBOX_WITH_AUDIO_ENUM */
    1697 
    1698 /**
    1699  * Initializes the host backend and queries its initial configuration.
    1700  *
    1701  * @returns VBox status code.
    1702  * @param   pThis               Driver instance to be called.
    1703  */
    1704 static int drvAudioHostInit(PDRVAUDIO pThis)
    1705 {
    1706     LogFlowFuncEnter();
    1707 
    1708     /*
    1709      * Check the function pointers, make sure the ones we define as
    1710      * mandatory are present.
    1711      */
    1712     PPDMIHOSTAUDIO pHostDrvAudio = pThis->pHostDrvAudio;
    1713     AssertPtrReturn(pHostDrvAudio, VERR_INVALID_POINTER);
    1714     AssertPtrReturn(pHostDrvAudio->pfnGetConfig, VERR_INVALID_POINTER);
    1715     AssertPtrNullReturn(pHostDrvAudio->pfnGetDevices, VERR_INVALID_POINTER);
    1716     AssertPtrNullReturn(pHostDrvAudio->pfnGetStatus, VERR_INVALID_POINTER);
    1717     AssertPtrNullReturn(pHostDrvAudio->pfnStreamConfigHint, VERR_INVALID_POINTER);
    1718     AssertPtrReturn(pHostDrvAudio->pfnStreamCreate, VERR_INVALID_POINTER);
    1719     AssertPtrReturn(pHostDrvAudio->pfnStreamDestroy, VERR_INVALID_POINTER);
    1720     AssertPtrReturn(pHostDrvAudio->pfnStreamControl, VERR_INVALID_POINTER);
    1721     AssertPtrReturn(pHostDrvAudio->pfnStreamGetReadable, VERR_INVALID_POINTER);
    1722     AssertPtrReturn(pHostDrvAudio->pfnStreamGetWritable, VERR_INVALID_POINTER);
    1723     AssertPtrNullReturn(pHostDrvAudio->pfnStreamGetPending, VERR_INVALID_POINTER);
    1724     AssertPtrReturn(pHostDrvAudio->pfnStreamGetStatus, VERR_INVALID_POINTER);
    1725     AssertPtrReturn(pHostDrvAudio->pfnStreamPlay, VERR_INVALID_POINTER);
    1726     AssertPtrReturn(pHostDrvAudio->pfnStreamCapture, VERR_INVALID_POINTER);
    1727 
    1728     /*
    1729      * Get the backend configuration.
    1730      */
    1731     int rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
    1732     if (RT_FAILURE(rc))
    1733     {
    1734         LogRel(("Audio: Getting configuration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
    1735         return VERR_AUDIO_BACKEND_INIT_FAILED;
    1736     }
    1737 
    1738     pThis->In.cStreamsFree  = pThis->BackendCfg.cMaxStreamsIn;
    1739     pThis->Out.cStreamsFree = pThis->BackendCfg.cMaxStreamsOut;
    1740 
    1741     LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
    1742 
    1743     LogRel2(("Audio: Host driver '%s' supports %RU32 input streams and %RU32 output streams at once.\n",
    1744              pThis->szName, pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
    1745 
    1746 #ifdef VBOX_WITH_AUDIO_ENUM
    1747     int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
    1748     if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
    1749         AssertRC(rc2);
    1750 
    1751     RT_NOREF(rc2);
    1752     /* Ignore rc. */
    1753 #endif
    1754 
    1755     LogFlowFuncLeave();
    1756     return VINF_SUCCESS;
    1757 }
    1758 
    1759 /**
    1760  * Handles state changes for all audio streams.
    1761  *
    1762  * @param   pDrvIns             Pointer to driver instance.
    1763  * @param   enmCmd              Stream command to set for all streams.
    1764  */
    1765 static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
    1766 {
    1767     PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
    1768     PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
    1769     LogFlowFunc(("enmCmd=%s\n", PDMAudioStrmCmdGetName(enmCmd)));
    1770 
    1771     int rc2 = RTCritSectEnter(&pThis->CritSect);
    1772     AssertRCReturnVoid(rc2);
    1773 
    1774     if (pThis->pHostDrvAudio)
    1775     {
    1776         PDRVAUDIOSTREAM pStreamEx;
    1777         RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
    1778         {
    1779             drvAudioStreamControlInternal(pThis, pStreamEx, enmCmd);
    1780         }
    1781     }
    1782 
    1783     rc2 = RTCritSectLeave(&pThis->CritSect);
    1784     AssertRC(rc2);
    1785 }
    1786 
    1787 /**
    1788  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
    1789  */
    1790 static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
    1791                                             void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
    1792 {
    1793     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    1794     AssertPtr(pThis);
    1795     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    1796     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    1797     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    1798     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    1799     AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER);
    1800     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    1801     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    1802     AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
    1803               ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
    1804                pStreamEx->Core.szName, pStreamEx->Core.enmDir));
    1805 
    1806     int rc = RTCritSectEnter(&pThis->CritSect);
    1807     AssertRCReturn(rc, rc);
    1808 
    1809     /*
    1810      * ...
    1811      */
    1812     uint32_t cbReadTotal = 0;
    1813 
    1814     do
    1815     {
    1816         uint32_t cfReadTotal = 0;
    1817 
    1818         const uint32_t cfBuf = AUDIOMIXBUF_B2F(&pStreamEx->Guest.MixBuf, cbBuf);
    1819 
    1820         if (pThis->In.fEnabled) /* Input for this audio driver enabled? See #9822. */
    1821         {
    1822             if (!PDMAudioStrmStatusCanRead(pStream->fStatus))
    1823             {
    1824                 rc = VERR_AUDIO_STREAM_NOT_READY;
    1825                 break;
    1826             }
    1827 
    1828             /*
    1829              * Read from the parent buffer (that is, the guest buffer) which
    1830              * should have the audio data in the format the guest needs.
    1831              */
    1832             uint32_t cfToRead = RT_MIN(cfBuf, AudioMixBufLive(&pStreamEx->Guest.MixBuf));
    1833             while (cfToRead)
    1834             {
    1835                 uint32_t cfRead;
    1836                 rc = AudioMixBufAcquireReadBlock(&pStreamEx->Guest.MixBuf,
    1837                                                  (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
    1838                                                  AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfToRead), &cfRead);
    1839                 if (RT_FAILURE(rc))
    1840                     break;
    1841 
    1842 #ifdef VBOX_WITH_STATISTICS
    1843                 const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfRead);
    1844                 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead,    cbRead);
    1845                 STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesRead, cfRead);
    1846                 STAM_COUNTER_INC(&pStreamEx->In.Stats.TotalTimesRead);
    1847 #endif
    1848                 Assert(cfToRead >= cfRead);
    1849                 cfToRead -= cfRead;
    1850 
    1851                 cfReadTotal += cfRead;
    1852 
    1853                 AudioMixBufReleaseReadBlock(&pStreamEx->Guest.MixBuf, cfRead);
    1854             }
    1855 
    1856             if (cfReadTotal)
    1857             {
    1858                 if (pThis->In.Cfg.Dbg.fEnabled)
    1859                     AudioHlpFileWrite(pStreamEx->In.Dbg.pFileStreamRead,
    1860                                       pvBuf, AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 0 /* fFlags */);
    1861 
    1862                 AudioMixBufFinish(&pStreamEx->Guest.MixBuf, cfReadTotal);
    1863             }
    1864         }
    1865 
    1866         /* If we were not able to read as much data as requested, fill up the returned
    1867          * data with silence.
    1868          *
    1869          * This is needed to keep the device emulation DMA transfers up and running at a constant rate. */
    1870         if (cfReadTotal < cfBuf)
    1871         {
    1872             Log3Func(("[%s] Filling in silence (%RU64ms / %RU64ms)\n", pStream->szName,
    1873                       PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf - cfReadTotal),
    1874                       PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf)));
    1875 
    1876             PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props,
    1877                                      (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
    1878                                      AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfBuf - cfReadTotal),
    1879                                      cfBuf - cfReadTotal);
    1880 
    1881             cfReadTotal = cfBuf;
    1882         }
    1883 
    1884         cbReadTotal = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal);
    1885 
    1886         pStreamEx->nsLastReadWritten = RTTimeNanoTS();
    1887 
    1888         Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc));
    1889 
    1890     } while (0);
    1891 
    1892     RTCritSectLeave(&pThis->CritSect);
    1893 
    1894     if (RT_SUCCESS(rc) && pcbRead)
    1895         *pcbRead = cbReadTotal;
    1896     return rc;
    1897 }
    1898 
    1899 /**
    1900  * Adjusts the request stream configuration, applying our settings.
    1901  *
    1902  * This also does some basic validations.
    1903  *
    1904  * Used by both the stream creation and stream configuration hinting code.
    1905  *
    1906  * @returns VBox status code.
    1907  * @param   pThis       Pointer to the DrvAudio instance data.
    1908  * @param   pCfgReq     The request configuration that should be adjusted.
    1909  * @param   pszName     Stream name to use when logging warnings and errors.
    1910  */
    1911 static int drvAudioStreamAdjustConfig(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfgReq, const char *pszName)
    1912 {
    1913     /* Get the right configuration for the stream to be created. */
    1914     PDRVAUDIOCFG pDrvCfg = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.Cfg : &pThis->Out.Cfg;
    1915 
    1916     /* Fill in the tweakable parameters into the requested host configuration.
    1917      * All parameters in principle can be changed and returned by the backend via the acquired configuration. */
    1918 
    1919     /*
    1920      * PCM
    1921      */
    1922     if (PDMAudioPropsSampleSize(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
    1923     {
    1924         PDMAudioPropsSetSampleSize(&pCfgReq->Props, PDMAudioPropsSampleSize(&pDrvCfg->Props));
    1925         LogRel2(("Audio: Using custom sample size of %RU8 bytes for stream '%s'\n",
    1926                  PDMAudioPropsSampleSize(&pCfgReq->Props), pszName));
    1927     }
    1928 
    1929     if (pDrvCfg->Props.uHz) /* Anything set via custom extra-data? */
    1930     {
    1931         pCfgReq->Props.uHz = pDrvCfg->Props.uHz;
    1932         LogRel2(("Audio: Using custom Hz rate %RU32 for stream '%s'\n", pCfgReq->Props.uHz, pszName));
    1933     }
    1934 
    1935     if (pDrvCfg->uSigned != UINT8_MAX) /* Anything set via custom extra-data? */
    1936     {
    1937         pCfgReq->Props.fSigned = RT_BOOL(pDrvCfg->uSigned);
    1938         LogRel2(("Audio: Using custom %s sample format for stream '%s'\n",
    1939                  pCfgReq->Props.fSigned ? "signed" : "unsigned", pszName));
    1940     }
    1941 
    1942     if (pDrvCfg->uSwapEndian != UINT8_MAX) /* Anything set via custom extra-data? */
    1943     {
    1944         pCfgReq->Props.fSwapEndian = RT_BOOL(pDrvCfg->uSwapEndian);
    1945         LogRel2(("Audio: Using custom %s endianess for samples of stream '%s'\n",
    1946                  pCfgReq->Props.fSwapEndian ? "swapped" : "original", pszName));
    1947     }
    1948 
    1949     if (PDMAudioPropsChannels(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
    1950     {
    1951         PDMAudioPropsSetChannels(&pCfgReq->Props, PDMAudioPropsChannels(&pDrvCfg->Props));
    1952         LogRel2(("Audio: Using custom %RU8 channel(s) for stream '%s'\n", PDMAudioPropsChannels(&pDrvCfg->Props), pszName));
    1953     }
    1954 
    1955     /* Validate PCM properties. */
    1956     if (!AudioHlpPcmPropsAreValid(&pCfgReq->Props))
    1957     {
    1958         LogRel(("Audio: Invalid custom PCM properties set for stream '%s', cannot create stream\n", pszName));
    1959         return VERR_INVALID_PARAMETER;
    1960     }
    1961 
    1962     /*
    1963      * Period size
    1964      */
    1965     const char *pszWhat = "device-specific";
    1966     if (pDrvCfg->uPeriodSizeMs)
    1967     {
    1968         pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPeriodSizeMs);
    1969         pszWhat = "custom";
    1970     }
    1971 
    1972     if (!pCfgReq->Backend.cFramesPeriod) /* Set default period size if nothing explicitly is set. */
    1973     {
    1974         pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 150 /*ms*/);
    1975         pszWhat = "default";
    1976     }
    1977 
    1978     LogRel2(("Audio: Using %s period size %RU64 ms / %RU32 frames for stream '%s'\n",
    1979              pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod),
    1980              pCfgReq->Backend.cFramesPeriod, pszName));
    1981 
    1982     /*
    1983      * Buffer size
    1984      */
    1985     pszWhat = "device-specific";
    1986     if (pDrvCfg->uBufferSizeMs)
    1987     {
    1988         pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uBufferSizeMs);
    1989         pszWhat = "custom";
    1990     }
    1991 
    1992     if (!pCfgReq->Backend.cFramesBufferSize) /* Set default buffer size if nothing explicitly is set. */
    1993     {
    1994         pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 300 /*ms*/);
    1995         pszWhat = "default";
    1996     }
    1997 
    1998     LogRel2(("Audio: Using %s buffer size %RU64 ms / %RU32 frames for stream '%s'\n",
    1999              pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
    2000              pCfgReq->Backend.cFramesBufferSize, pszName));
    2001 
    2002     /*
    2003      * Pre-buffering size
    2004      */
    2005     pszWhat = "device-specific";
    2006     if (pDrvCfg->uPreBufSizeMs != UINT32_MAX) /* Anything set via global / per-VM extra-data? */
    2007     {
    2008         pCfgReq->Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPreBufSizeMs);
    2009         pszWhat = "custom";
    2010     }
    2011     else /* No, then either use the default or device-specific settings (if any). */
    2012     {
    2013         if (pCfgReq->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */
    2014         {
    2015             /* Pre-buffer 66% of the buffer. */
    2016             pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize * 2 / 3;
    2017             pszWhat = "default";
    2018         }
    2019     }
    2020 
    2021     LogRel2(("Audio: Using %s pre-buffering size %RU64 ms / %RU32 frames for stream '%s'\n",
    2022              pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
    2023              pCfgReq->Backend.cFramesPreBuffering, pszName));
    2024 
    2025     /*
    2026      * Validate input.
    2027      */
    2028     if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPeriod)
    2029     {
    2030         LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the period size (%RU64ms)\n",
    2031                 pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
    2032                 PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod)));
    2033         return VERR_INVALID_PARAMETER;
    2034     }
    2035 
    2036     if (   pCfgReq->Backend.cFramesPreBuffering != UINT32_MAX /* Custom pre-buffering set? */
    2037         && pCfgReq->Backend.cFramesPreBuffering)
    2038     {
    2039         if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPreBuffering)
    2040         {
    2041             LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the pre-buffering size (%RU64ms)\n",
    2042                     pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
    2043                     PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize)));
    2044             return VERR_INVALID_PARAMETER;
    2045         }
    2046     }
    2047 
    2048     return VINF_SUCCESS;
    2049 }
    2050 
    2051 /**
    2052  * Worker for drvAudioStreamInitInternal and drvAudioStreamReInitInternal that
    2053  * creates the backend (host driver) side of an audio stream.
    2054  *
    2055  * @returns VBox status code.
    2056  * @param   pThis       Pointer to driver instance.
    2057  * @param   pStreamEx   Audio stream to create the backend side for.
    2058  * @param   pCfgReq     Requested audio stream configuration to use for
    2059  *                      stream creation.
    2060  * @param   pCfgAcq     Acquired audio stream configuration returned by
    2061  *                      the backend.
    2062  *
    2063  * @note    Configuration precedence for requested audio stream configuration (first has highest priority, if set):
    2064  *          - per global extra-data
    2065  *          - per-VM extra-data
    2066  *          - requested configuration (by pCfgReq)
    2067  *          - default value
    2068  */
    2069 static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
    2070                                                PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    2071 {
    2072     AssertMsg((pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED) == 0,
    2073               ("Stream '%s' already initialized in backend\n", pStreamEx->Core.szName));
    2074 
    2075     /*
    2076      * Adjust the requested stream config, applying our settings.
    2077      */
    2078     int rc = drvAudioStreamAdjustConfig(pThis, pCfgReq, pStreamEx->Core.szName);
    2079     if (RT_FAILURE(rc))
    2080         return rc;
    2081 
    2082     /*
    2083      * Make the acquired host configuration the requested host configuration initially,
    2084      * in case the backend does not report back an acquired configuration.
    2085      */
    2086     /** @todo r=bird: This is conveniently not documented in the interface... */
    2087     rc = PDMAudioStrmCfgCopy(pCfgAcq, pCfgReq);
    2088     if (RT_FAILURE(rc))
    2089     {
    2090         LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
    2091                 pStreamEx->Core.szName));
    2092         return rc;
    2093     }
    2094 
    2095     /*
    2096      * Call the host driver to create the stream.
    2097      */
    2098     AssertPtr(pThis->pHostDrvAudio);
    2099     if (pThis->pHostDrvAudio)
    2100         rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pStreamEx->pBackend, pCfgReq, pCfgAcq);
    2101     else
    2102         rc = VERR_PDM_NO_ATTACHED_DRIVER;
    2103     if (RT_FAILURE(rc))
    2104     {
    2105         if (rc == VERR_NOT_SUPPORTED)
    2106             LogRel2(("Audio: Creating stream '%s' in backend not supported\n", pStreamEx->Core.szName));
    2107         else if (rc == VERR_AUDIO_STREAM_COULD_NOT_CREATE)
    2108             LogRel2(("Audio: Stream '%s' could not be created in backend because of missing hardware / drivers\n", pStreamEx->Core.szName));
    2109         else
    2110             LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pStreamEx->Core.szName, rc));
    2111         return rc;
    2112     }
    2113 
    2114     /* Validate acquired configuration. */
    2115     char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
    2116     AssertLogRelMsgReturn(AudioHlpStreamCfgIsValid(pCfgAcq),
    2117                           ("Audio: Creating stream '%s' returned an invalid backend configuration (%s), skipping\n",
    2118                            pStreamEx->Core.szName, PDMAudioPropsToString(&pCfgAcq->Props, szTmp, sizeof(szTmp))),
    2119                           VERR_INVALID_PARAMETER);
    2120 
    2121     /* Let the user know that the backend changed one of the values requested above. */
    2122     if (pCfgAcq->Backend.cFramesBufferSize != pCfgReq->Backend.cFramesBufferSize)
    2123         LogRel2(("Audio: Buffer size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
    2124                  pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesBufferSize), pCfgAcq->Backend.cFramesBufferSize));
    2125 
    2126     if (pCfgAcq->Backend.cFramesPeriod != pCfgReq->Backend.cFramesPeriod)
    2127         LogRel2(("Audio: Period size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
    2128                  pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPeriod), pCfgAcq->Backend.cFramesPeriod));
    2129 
    2130     /* Was pre-buffering requested, but the acquired configuration from the backend told us something else? */
    2131     if (pCfgReq->Backend.cFramesPreBuffering)
    2132     {
    2133         if (pCfgAcq->Backend.cFramesPreBuffering != pCfgReq->Backend.cFramesPreBuffering)
    2134             LogRel2(("Audio: Pre-buffering size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
    2135                      pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
    2136 
    2137         if (pCfgAcq->Backend.cFramesPreBuffering > pCfgAcq->Backend.cFramesBufferSize)
    2138         {
    2139             pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesBufferSize;
    2140             LogRel2(("Audio: Pre-buffering size bigger than buffer size for stream '%s', adjusting to %RU64ms (%RU32 frames)\n",
    2141                      pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
    2142         }
    2143     }
    2144     else if (pCfgReq->Backend.cFramesPreBuffering == 0) /* Was the pre-buffering requested as being disabeld? Tell the users. */
    2145     {
    2146         LogRel2(("Audio: Pre-buffering is disabled for stream '%s'\n", pStreamEx->Core.szName));
    2147         pCfgAcq->Backend.cFramesPreBuffering = 0;
    2148     }
    2149 
    2150     /* Sanity for detecting buggy backends. */
    2151     AssertMsgReturn(pCfgAcq->Backend.cFramesPeriod < pCfgAcq->Backend.cFramesBufferSize,
    2152                     ("Acquired period size must be smaller than buffer size\n"),
    2153                     VERR_INVALID_PARAMETER);
    2154     AssertMsgReturn(pCfgAcq->Backend.cFramesPreBuffering <= pCfgAcq->Backend.cFramesBufferSize,
    2155                     ("Acquired pre-buffering size must be smaller or as big as the buffer size\n"),
    2156                     VERR_INVALID_PARAMETER);
    2157 
    2158     pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
    2159 
    2160     return VINF_SUCCESS;
    2161 }
    2162 
    2163 
    2164 /**
    2165  * Worker for drvAudioStreamCreate that initializes the audio stream.
    2166  *
    2167  * @returns VBox status code.
    2168  * @param   pThis       Pointer to driver instance.
    2169  * @param   pStreamEx   Stream to initialize.
    2170  * @param   fFlags      PDMAUDIOSTREAM_CREATE_F_XXX.
    2171  * @param   pCfgHost    Stream configuration to use for the host side (backend).
    2172  * @param   pCfgGuest   Stream configuration to use for the guest side.
    2173  */
    2174 static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t fFlags,
    2175                                       PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
    2176 {
    2177     /*
    2178      * Init host stream.
    2179      */
    2180     pStreamEx->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
    2181 
    2182     /* Set the host's default audio data layout. */
    2183 /** @todo r=bird: Why, oh why? OTOH, the layout stuff is non-sense anyway. */
    2184     pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
    2185 
    2186 #ifdef LOG_ENABLED
    2187     LogFunc(("[%s] Requested host format:\n", pStreamEx->Core.szName));
    2188     PDMAudioStrmCfgLog(pCfgHost);
    2189 #endif
    2190 
    2191     LogRel2(("Audio: Creating stream '%s'\n", pStreamEx->Core.szName));
    2192     LogRel2(("Audio: Guest %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
    2193              pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
    2194              pCfgGuest->Props.uHz, PDMAudioPropsSampleBits(&pCfgGuest->Props), pCfgGuest->Props.fSigned ? "S" : "U",
    2195              PDMAudioPropsChannels(&pCfgGuest->Props), PDMAudioPropsChannels(&pCfgGuest->Props) == 1 ? "" : "s"));
    2196     LogRel2(("Audio: Requested host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
    2197              pCfgHost->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
    2198              pCfgHost->Props.uHz, PDMAudioPropsSampleBits(&pCfgHost->Props), pCfgHost->Props.fSigned ? "S" : "U",
    2199              PDMAudioPropsChannels(&pCfgHost->Props), PDMAudioPropsChannels(&pCfgHost->Props) == 1 ? "" : "s"));
    2200 
    2201     PDMAUDIOSTREAMCFG CfgHostAcq;
    2202     int rc = drvAudioStreamCreateInternalBackend(pThis, pStreamEx, pCfgHost, &CfgHostAcq);
    2203     if (RT_FAILURE(rc))
    2204         return rc;
    2205 
    2206     LogFunc(("[%s] Acquired host format:\n",  pStreamEx->Core.szName));
    2207     PDMAudioStrmCfgLog(&CfgHostAcq);
    2208     LogRel2(("Audio: Acquired host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
    2209              CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback",  pStreamEx->Core.szName,
    2210              CfgHostAcq.Props.uHz, PDMAudioPropsSampleBits(&CfgHostAcq.Props), CfgHostAcq.Props.fSigned ? "S" : "U",
    2211              PDMAudioPropsChannels(&CfgHostAcq.Props), PDMAudioPropsChannels(&CfgHostAcq.Props) == 1 ? "" : "s"));
    2212     Assert(PDMAudioPropsAreValid(&CfgHostAcq.Props));
    2213 
    2214     /* Set the stream properties (currently guest side, when DevSB16 is
    2215        converted to mixer and PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF becomes
    2216        default, this will just be the stream properties). */
    2217     if (fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF)
    2218         pStreamEx->Core.Props = CfgHostAcq.Props;
    2219     else
    2220         pStreamEx->Core.Props = pCfgGuest->Props;
    2221 
    2222     /* Let the user know if the backend changed some of the tweakable values. */
    2223     if (CfgHostAcq.Backend.cFramesBufferSize != pCfgHost->Backend.cFramesBufferSize)
    2224         LogRel2(("Audio: Backend changed buffer size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
    2225                  PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesBufferSize), pCfgHost->Backend.cFramesBufferSize,
    2226                  PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
    2227 
    2228     if (CfgHostAcq.Backend.cFramesPeriod != pCfgHost->Backend.cFramesPeriod)
    2229         LogRel2(("Audio: Backend changed period size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
    2230                  PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPeriod), pCfgHost->Backend.cFramesPeriod,
    2231                  PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod), CfgHostAcq.Backend.cFramesPeriod));
    2232 
    2233     if (CfgHostAcq.Backend.cFramesPreBuffering != pCfgHost->Backend.cFramesPreBuffering)
    2234         LogRel2(("Audio: Backend changed pre-buffering size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
    2235                  PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPreBuffering), pCfgHost->Backend.cFramesPreBuffering,
    2236                  PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
    2237 
    2238     /*
    2239      * Check if the backend did return sane values and correct if necessary.
    2240      * Should never happen with our own backends, but you never know ...
    2241      */
    2242     uint32_t const cFramesPreBufferingMax = CfgHostAcq.Backend.cFramesBufferSize - RT_MIN(16, CfgHostAcq.Backend.cFramesBufferSize);
    2243     if (CfgHostAcq.Backend.cFramesPreBuffering > cFramesPreBufferingMax)
    2244     {
    2245         LogRel2(("Audio: Warning: Pre-buffering size of %RU32 frames for stream '%s' is too close to or larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n",
    2246                  CfgHostAcq.Backend.cFramesPreBuffering, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, cFramesPreBufferingMax));
    2247         AssertFailed();
    2248         CfgHostAcq.Backend.cFramesPreBuffering = cFramesPreBufferingMax;
    2249     }
    2250 
    2251     if (CfgHostAcq.Backend.cFramesPeriod > CfgHostAcq.Backend.cFramesBufferSize)
    2252     {
    2253         LogRel2(("Audio: Warning: Period size of %RU32 frames for stream '%s' is larger than the %RU32 frames buffer size, reducing it to %RU32 frames!\n",
    2254                  CfgHostAcq.Backend.cFramesPeriod, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, CfgHostAcq.Backend.cFramesBufferSize / 2));
    2255         AssertFailed();
    2256         CfgHostAcq.Backend.cFramesPeriod = CfgHostAcq.Backend.cFramesBufferSize / 2;
    2257     }
    2258 
    2259     LogRel2(("Audio: Buffer size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
    2260              PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
    2261     LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
    2262              PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
    2263 
    2264     /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
    2265     const uint32_t msPeriod = PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod);
    2266     LogRel2(("Audio: Period size of stream '%s' is %RU64 ms / %RU32 frames\n",
    2267              pStreamEx->Core.szName, msPeriod, CfgHostAcq.Backend.cFramesPeriod));
    2268 
    2269     if (   pCfgGuest->Device.cMsSchedulingHint             /* Any scheduling hint set? */
    2270         && pCfgGuest->Device.cMsSchedulingHint > msPeriod) /* This might lead to buffer underflows. */
    2271         LogRel(("Audio: Warning: Scheduling hint of stream '%s' is bigger (%RU64ms) than used period size (%RU64ms)\n",
    2272                 pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint, msPeriod));
    2273 
    2274     /*
    2275      * Make a copy of the acquired host stream configuration and the guest side one.
    2276      */
    2277     rc = PDMAudioStrmCfgCopy(&pStreamEx->Host.Cfg, &CfgHostAcq);
    2278     AssertRC(rc);
    2279 
    2280     /* Set the guests's default audio data layout. */
    2281     pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; /** @todo r=bird: WTF DO WE DO THIS?  It's input and probably should've been const... */
    2282     rc = PDMAudioStrmCfgCopy(&pStreamEx->Guest.Cfg, pCfgGuest);
    2283     AssertRC(rc);
    2284 
    2285     /*
    2286      * Configure host buffers.
    2287      */
    2288 
    2289     /* Destroy any former mixing buffer. */
    2290     AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
    2291 
    2292     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    2293     {
    2294         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    2295         rc = AudioMixBufInit(&pStreamEx->Host.MixBuf, pStreamEx->Core.szName, &CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize);
    2296         AssertRCReturn(rc, rc);
    2297     }
    2298     /* Allocate space for pre-buffering of output stream w/o mixing buffers. */
    2299     else if (pCfgHost->enmDir == PDMAUDIODIR_OUT)
    2300     {
    2301         Assert(pStreamEx->Out.cbPreBufAlloc == 0);
    2302         Assert(pStreamEx->Out.cbPreBufThreshold == 0);
    2303         Assert(pStreamEx->Out.cbPreBuffered == 0);
    2304         if (CfgHostAcq.Backend.cFramesPreBuffering != 0)
    2305         {
    2306             pStreamEx->Out.cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering);
    2307             pStreamEx->Out.cbPreBufAlloc = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize - 2);
    2308             pStreamEx->Out.cbPreBufAlloc = RT_MIN(RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K),
    2309                                                   pStreamEx->Out.cbPreBufAlloc);
    2310             pStreamEx->Out.pbPreBuf = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
    2311             AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY);
    2312         }
    2313     }
    2314 
    2315     /*
    2316      * Init guest stream.
    2317      */
    2318     if (pCfgGuest->Device.cMsSchedulingHint)
    2319         LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms (%RU32 bytes)\n",
    2320                  pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint,
    2321                  PDMAudioPropsMilliToBytes(&pCfgGuest->Props, pCfgGuest->Device.cMsSchedulingHint)));
    2322 
    2323     /* Destroy any former mixing buffer. */
    2324     AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
    2325 
    2326     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    2327     {
    2328         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    2329         rc = AudioMixBufInit(&pStreamEx->Guest.MixBuf, pStreamEx->Core.szName, &pCfgGuest->Props, CfgHostAcq.Backend.cFramesBufferSize);
    2330         AssertRCReturn(rc, rc);
    2331     }
    2332 
    2333     if (RT_FAILURE(rc))
    2334         LogRel(("Audio: Creating stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
    2335 
    2336     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    2337     {
    2338         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    2339         /* Host (Parent) -> Guest (Child). */
    2340         rc = AudioMixBufLinkTo(&pStreamEx->Host.MixBuf, &pStreamEx->Guest.MixBuf);
    2341         AssertRC(rc);
    2342     }
    2343 
    2344     /*
    2345      * Register statistics.
    2346      */
    2347     PPDMDRVINS const pDrvIns = pThis->pDrvIns;
    2348     /** @todo expose config and more. */
    2349     PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.Cfg.Backend.cFramesBufferSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2350                            "Host side: The size of the backend buffer (in frames)", "%s/0-HostBackendBufSize", pStreamEx->Core.szName);
    2351     if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
    2352     {
    2353         Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
    2354         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2355                                "Host side: The size of the mixer buffer (in frames)",   "%s/1-HostMixBufSize", pStreamEx->Core.szName);
    2356         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2357                                "Guest side: The size of the mixer buffer (in frames)",  "%s/2-GuestMixBufSize", pStreamEx->Core.szName);
    2358         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cMixed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2359                                "Host side: Number of frames in the mixer buffer",   "%s/1-HostMixBufUsed", pStreamEx->Core.szName);
    2360         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2361                                "Guest side: Number of frames in the mixer buffer",  "%s/2-GuestMixBufUsed", pStreamEx->Core.szName);
    2362     }
    2363     if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
    2364     {
    2365         /** @todo later? */
    2366     }
    2367     else
    2368     {
    2369         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableBefore, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2370                                "Host side: Free space in backend buffer before play", "%s/0-HostBackendBufFreeBefore", pStreamEx->Core.szName);
    2371         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableAfter, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2372                                "Host side: Free space in backend buffer after play",  "%s/0-HostBackendBufFreeAfter", pStreamEx->Core.szName);
    2373     }
    2374 
    2375 #ifdef VBOX_WITH_STATISTICS
    2376     if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
    2377     {
    2378         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2379                                "Total frames played.", "%s/TotalFramesCaptured", pStreamEx->Core.szName);
    2380         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2381                                "Total number of playbacks.", "%s/TotalTimesCaptured", pStreamEx->Core.szName);
    2382         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2383                                "Total frames read.", "%s/TotalFramesRead", pStreamEx->Core.szName);
    2384         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2385                                "Total number of reads.", "%s/TotalTimesRead", pStreamEx->Core.szName);
    2386     }
    2387     else
    2388     {
    2389         Assert(pCfgGuest->enmDir == PDMAUDIODIR_OUT);
    2390         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2391                                "Total frames played.", "%s/TotalFramesPlayed", pStreamEx->Core.szName);
    2392         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2393                                "Total number of playbacks.", "%s/TotalTimesPlayed", pStreamEx->Core.szName);
    2394         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2395                                "Total frames written.", "%s/TotalFramesWritten", pStreamEx->Core.szName);
    2396         PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
    2397                                "Total number of writes.", "%s/TotalTimesWritten", pStreamEx->Core.szName);
    2398     }
    2399 #endif /* VBOX_WITH_STATISTICS */
    2400 
    2401     LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
    2402     return rc;
    2403 }
    2404 
    2405 /**
    2406  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
    2407  */
    2408 static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface, uint32_t fFlags, PPDMAUDIOSTREAMCFG pCfgHost,
    2409                                               PPDMAUDIOSTREAMCFG pCfgGuest, PPDMAUDIOSTREAM *ppStream)
    2410 {
    2411     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    2412     AssertPtr(pThis);
    2413 
    2414     /*
    2415      * Assert sanity.
    2416      */
    2417     AssertReturn(!(fFlags & ~PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);
    2418     AssertPtrReturn(pCfgHost,   VERR_INVALID_POINTER);
    2419     AssertPtrReturn(pCfgGuest,  VERR_INVALID_POINTER);
    2420     AssertPtrReturn(ppStream,   VERR_INVALID_POINTER);
    2421     LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
    2422 #ifdef LOG_ENABLED
    2423     PDMAudioStrmCfgLog(pCfgHost);
    2424     PDMAudioStrmCfgLog(pCfgGuest);
    2425 #endif
    2426     AssertReturn(AudioHlpStreamCfgIsValid(pCfgHost), VERR_INVALID_PARAMETER);
    2427     AssertReturn(AudioHlpStreamCfgIsValid(pCfgGuest), VERR_INVALID_PARAMETER);
    2428     AssertReturn(pCfgHost->enmDir == pCfgGuest->enmDir, VERR_MISMATCH);
    2429     AssertReturn(pCfgHost->enmDir == PDMAUDIODIR_IN || pCfgHost->enmDir == PDMAUDIODIR_OUT, VERR_NOT_SUPPORTED);
    2430     /* Require PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF for output streams: */
    2431     AssertReturn((fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF) || pCfgHost->enmDir == PDMAUDIODIR_IN, VERR_INVALID_FLAGS);
    2432 
    2433     /*
    2434      * Lock the whole driver instance.
    2435      */
    2436     int rc = RTCritSectEnter(&pThis->CritSect);
    2437     AssertRCReturn(rc, rc);
    2438 
    2439     /*
    2440      * Check that we have free streams in the backend and get the
    2441      * size of the backend specific stream data.
    2442      */
    2443     uint32_t *pcFreeStreams;
    2444     if (pCfgHost->enmDir == PDMAUDIODIR_IN)
    2445     {
    2446         if (!pThis->In.cStreamsFree)
    2447         {
    2448             LogFlowFunc(("Maximum number of host input streams reached\n"));
    2449             rc = VERR_AUDIO_NO_FREE_INPUT_STREAMS;
    2450         }
    2451         pcFreeStreams = &pThis->In.cStreamsFree;
    2452     }
    2453     else /* Out */
    2454     {
    2455         if (!pThis->Out.cStreamsFree)
    2456         {
    2457             LogFlowFunc(("Maximum number of host output streams reached\n"));
    2458             rc = VERR_AUDIO_NO_FREE_OUTPUT_STREAMS;
    2459         }
    2460         pcFreeStreams = &pThis->Out.cStreamsFree;
    2461     }
    2462     size_t const cbHstStrm = pThis->BackendCfg.cbStream;
    2463     AssertStmt(cbHstStrm < _16M, rc = VERR_OUT_OF_RANGE);
    2464     if (RT_SUCCESS(rc))
    2465     {
    2466         /*
    2467          * Allocate and initialize common state.
    2468          */
    2469         PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)RTMemAllocZ(sizeof(DRVAUDIOSTREAM) + RT_ALIGN_Z(cbHstStrm, 64));
    2470         if (pStreamEx)
    2471         {
    2472             /* Retrieve host driver name for easier identification. */
    2473             AssertPtr(pThis->pHostDrvAudio);
    2474             RTStrPrintf(pStreamEx->Core.szName, RT_ELEMENTS(pStreamEx->Core.szName), "[%s] %s",
    2475                         pThis->BackendCfg.szName, pCfgHost->szName[0] != '\0' ? pCfgHost->szName : "<Untitled>");
    2476 
    2477             pStreamEx->Core.enmDir    = pCfgHost->enmDir;
    2478             pStreamEx->Core.cbBackend = (uint32_t)cbHstStrm;
    2479             if (cbHstStrm)
    2480                 pStreamEx->pBackend   = (PPDMAUDIOBACKENDSTREAM)(pStreamEx + 1);
    2481             pStreamEx->fNoMixBufs     = RT_BOOL(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF);
    2482             pStreamEx->uMagic         = DRVAUDIOSTREAM_MAGIC;
    2483 
    2484             /*
    2485              * Try to init the rest.
    2486              */
    2487             rc = drvAudioStreamInitInternal(pThis, pStreamEx, fFlags, pCfgHost, pCfgGuest);
    2488             if (RT_SUCCESS(rc))
    2489             {
    2490                 /* Set initial reference counts. */
    2491                 pStreamEx->Core.cRefs = 1;
    2492 
    2493                 /* Decrement the free stream counter. */
    2494                 Assert(*pcFreeStreams > 0);
    2495                 *pcFreeStreams -= 1;
    2496 
    2497                 /*
    2498                  * We're good.
    2499                  */
    2500                 RTListAppend(&pThis->lstStreams, &pStreamEx->ListEntry);
    2501                 STAM_COUNTER_INC(&pThis->Stats.TotalStreamsCreated);
    2502                 *ppStream = &pStreamEx->Core;
    2503 
    2504                 /*
    2505                  * Init debug stuff if enabled (ignore failures).
    2506                  */
    2507                 if (pCfgHost->enmDir == PDMAUDIODIR_IN)
    2508                 {
    2509                     if (pThis->In.Cfg.Dbg.fEnabled)
    2510                     {
    2511                         AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileCaptureNonInterleaved, pThis->In.Cfg.Dbg.szPathOut,
    2512                                                   "DrvAudioCapNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    2513                         AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileStreamRead, pThis->In.Cfg.Dbg.szPathOut,
    2514                                                   "DrvAudioRead", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    2515                     }
    2516                 }
    2517                 else /* Out */
    2518                 {
    2519                     if (pThis->Out.Cfg.Dbg.fEnabled)
    2520                     {
    2521                         AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pThis->Out.Cfg.Dbg.szPathOut,
    2522                                                   "DrvAudioPlayNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    2523                         AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFileStreamWrite, pThis->Out.Cfg.Dbg.szPathOut,
    2524                                                   "DrvAudioWrite", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
    2525                     }
    2526                 }
    2527             }
    2528             else
    2529             {
    2530                 LogFunc(("drvAudioStreamInitInternal failed: %Rrc\n", rc));
    2531                 int rc2 = drvAudioStreamUninitInternal(pThis, pStreamEx);
    2532                 AssertRC(rc2);
    2533                 drvAudioStreamFree(pStreamEx);
    2534             }
    2535         }
    2536         else
    2537             rc = VERR_NO_MEMORY;
    2538     }
    2539 
    2540     RTCritSectLeave(&pThis->CritSect);
    2541     LogFlowFuncLeaveRC(rc);
    2542     return rc;
    2543 }
    2544 
    2545 /**
    2546  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnEnable}
    2547  */
    2548 static DECLCALLBACK(int) drvAudioEnable(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir, bool fEnable)
    2549 {
    2550     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    2551     AssertPtr(pThis);
    2552 
    2553     bool *pfEnabled;
    2554     if (enmDir == PDMAUDIODIR_IN)
    2555         pfEnabled = &pThis->In.fEnabled;
    2556     else if (enmDir == PDMAUDIODIR_OUT)
    2557         pfEnabled = &pThis->Out.fEnabled;
    2558     else
    2559         AssertFailedReturn(VERR_INVALID_PARAMETER);
    2560 
    2561     int rc = RTCritSectEnter(&pThis->CritSect);
    2562     AssertRCReturn(rc, rc);
    2563 
    2564     if (fEnable != *pfEnabled)
    2565     {
    2566         LogRel(("Audio: %s %s for driver '%s'\n",
    2567                 fEnable ? "Enabling" : "Disabling", enmDir == PDMAUDIODIR_IN ? "input" : "output", pThis->szName));
    2568 
    2569         /* Update the status first, as this will be checked for in drvAudioStreamControlInternalBackend() below. */
    2570         *pfEnabled = fEnable;
    2571 
    2572         PDRVAUDIOSTREAM pStreamEx;
    2573         RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
    2574         {
    2575             if (pStreamEx->Core.enmDir != enmDir) /* Skip unwanted streams. */
    2576                 continue;
    2577 
    2578             /* Note: Only enable / disable the backend, do *not* change the stream's internal status.
    2579              *       Callers (device emulation, mixer, ...) from outside will not see any status or behavior change,
    2580              *       to not confuse the rest of the state machine.
    2581              *
    2582              *       When disabling:
    2583              *          - playing back audo data would go to /dev/null
    2584              *          - recording audio data would return silence instead
    2585              *
    2586              * See @bugref{9882}.
    2587              */
    2588             int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx,
    2589                                                            fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
    2590             if (RT_FAILURE(rc2))
    2591             {
    2592                 if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
    2593                     LogRel(("Audio: Stream '%s' not available\n", pStreamEx->Core.szName));
    2594                 else
    2595                     LogRel(("Audio: Failed to %s %s stream '%s', rc=%Rrc\n", fEnable ? "enable" : "disable",
    2596                             enmDir == PDMAUDIODIR_IN ? "input" : "output", pStreamEx->Core.szName, rc2));
    2597             }
    2598             else
    2599             {
    2600                 /* When (re-)enabling a stream, clear the disabled warning bit again. */
    2601                 if (fEnable)
    2602                     pStreamEx->Core.fWarningsShown &= ~PDMAUDIOSTREAM_WARN_FLAGS_DISABLED;
    2603             }
    2604 
    2605             if (RT_SUCCESS(rc))
    2606                 rc = rc2;
    2607 
    2608             /* Keep going. */
    2609         }
    2610     }
    2611 
    2612     RTCritSectLeave(&pThis->CritSect);
    2613     LogFlowFuncLeaveRC(rc);
    2614     return rc;
    2615 }
    2616 
    2617 /**
    2618  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
    2619  */
    2620 static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
    2621 {
    2622     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    2623     AssertPtr(pThis);
    2624     int rc = RTCritSectEnter(&pThis->CritSect);
    2625     AssertRCReturn(rc, false);
    2626 
    2627     bool fEnabled;
    2628     if (enmDir == PDMAUDIODIR_IN)
    2629         fEnabled = pThis->In.fEnabled;
    2630     else if (enmDir == PDMAUDIODIR_OUT)
    2631         fEnabled = pThis->Out.fEnabled;
    2632     else
    2633         AssertFailedStmt(fEnabled = false);
    2634 
    2635     RTCritSectLeave(&pThis->CritSect);
    2636     return fEnabled;
    2637 }
    2638 
    2639 /**
    2640  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
    2641  */
    2642 static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
    2643 {
    2644     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    2645     AssertPtr(pThis);
    2646     AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
    2647     int rc = RTCritSectEnter(&pThis->CritSect);
    2648     AssertRCReturn(rc, rc);
    2649 
    2650     if (pThis->pHostDrvAudio)
    2651     {
    2652         if (pThis->pHostDrvAudio->pfnGetConfig)
    2653             rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
    2654         else
    2655             rc = VERR_NOT_SUPPORTED;
    2656     }
    2657     else
    2658         rc = VERR_PDM_NO_ATTACHED_DRIVER;
    2659 
    2660     RTCritSectLeave(&pThis->CritSect);
    2661     LogFlowFuncLeaveRC(rc);
    2662     return rc;
    2663 }
    2664 
    2665 /**
    2666  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
    2667  */
    2668 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
    2669 {
    2670     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    2671     AssertPtr(pThis);
    2672     int rc = RTCritSectEnter(&pThis->CritSect);
    2673     AssertRCReturn(rc, PDMAUDIOBACKENDSTS_UNKNOWN);
    2674 
    2675     PDMAUDIOBACKENDSTS fBackendStatus;
    2676     if (pThis->pHostDrvAudio)
    2677     {
    2678         if (pThis->pHostDrvAudio->pfnGetStatus)
    2679             fBackendStatus = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
    2680         else
    2681             fBackendStatus = PDMAUDIOBACKENDSTS_UNKNOWN;
    2682     }
    2683     else
    2684         fBackendStatus = PDMAUDIOBACKENDSTS_NOT_ATTACHED;
    2685 
    2686     RTCritSectLeave(&pThis->CritSect);
    2687     LogFlowFunc(("LEAVE - %#x\n", fBackendStatus));
    2688     return fBackendStatus;
    2689 }
    2690 
    2691 /**
    2692  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamConfigHint}
    2693  */
    2694 static DECLCALLBACK(void) drvAudioStreamConfigHint(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg)
    2695 {
    2696     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    2697     AssertReturnVoid(pCfg->enmDir == PDMAUDIODIR_IN || pCfg->enmDir == PDMAUDIODIR_OUT);
    2698 
    2699     int rc = RTCritSectEnter(&pThis->CritSect); /** @todo Reconsider the locking for DrvAudio */
    2700     AssertRCReturnVoid(rc);
    2701 
    2702     /*
    2703      * Don't do anything unless the backend has a pfnStreamConfigHint method
    2704      * and the direction is currently enabled.
    2705      */
    2706     if (   pThis->pHostDrvAudio
    2707         && pThis->pHostDrvAudio->pfnStreamConfigHint)
    2708     {
    2709         if (pCfg->enmDir == PDMAUDIODIR_OUT ? pThis->Out.fEnabled : pThis->In.fEnabled)
    2710         {
    2711             /*
    2712              * Adjust the configuration (applying out settings) then call the backend driver.
    2713              */
    2714             rc = drvAudioStreamAdjustConfig(pThis, pCfg, pCfg->szName);
    2715             AssertLogRelRC(rc);
    2716             if (RT_SUCCESS(rc))
    2717                 pThis->pHostDrvAudio->pfnStreamConfigHint(pThis->pHostDrvAudio, pCfg);
    2718         }
    2719         else
    2720             LogFunc(("Ignoring hint because direction is not currently enabled\n"));
    2721     }
    2722     else
    2723         LogFlowFunc(("Ignoring hint because backend has no pfnStreamConfigHint method.\n"));
    2724 
    2725     RTCritSectLeave(&pThis->CritSect);
    2726 }
    27272640
    27282641/**
     
    28042717    return cbReadable;
    28052718}
     2719
    28062720
    28072721/**
     
    28722786}
    28732787
     2788
    28742789/**
    28752790 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
     
    29052820}
    29062821
     2822
    29072823/**
    29082824 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
     
    29262842}
    29272843
    2928 /**
    2929  * Calls the backend to give it the chance to destroy its part of the audio stream.
    2930  *
    2931  * Called from drvAudioPowerOff, drvAudioStreamUninitInternal and
    2932  * drvAudioStreamReInitInternal.
     2844
     2845/**
     2846 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
     2847 */
     2848static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed)
     2849{
     2850    RT_NOREF(pInterface, pStream, pcFramesPlayed);
     2851    AssertFailed(/* OBSOLETE! */);
     2852    return VERR_NOT_SUPPORTED;
     2853}
     2854
     2855
     2856/**
     2857 * Captures non-interleaved input from a host stream.
    29332858 *
    29342859 * @returns VBox status code.
    2935  * @param   pThis       Pointer to driver instance.
    2936  * @param   pStreamEx   Audio stream destruct backend for.
    2937  */
    2938 static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
    2939 {
     2860 * @param   pThis       Driver instance.
     2861 * @param   pStreamEx   Stream to capture from.
     2862 * @param   pcfCaptured Number of (host) audio frames captured.
     2863 */
     2864static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
     2865{
     2866    Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
     2867    Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
     2868
     2869    /*
     2870     * ...
     2871     */
     2872    AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
     2873    uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     2874    if (!cbReadable)
     2875        Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
     2876
     2877    uint32_t cbFree = AudioMixBufFreeBytes(&pStreamEx->Guest.MixBuf); /* Parent */
     2878    if (!cbFree)
     2879        Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
     2880
     2881    if (cbReadable > cbFree) /* More data readable than we can store at the moment? Limit. */
     2882        cbReadable = cbFree;
     2883
     2884    /*
     2885     * ...
     2886     */
     2887    int      rc = VINF_SUCCESS;
     2888    uint32_t cfCapturedTotal = 0;
     2889    while (cbReadable)
     2890    {
     2891        uint8_t  abChunk[_4K];
     2892        uint32_t cbCaptured;
     2893        rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
     2894                                                    abChunk, RT_MIN(cbReadable, (uint32_t)sizeof(abChunk)), &cbCaptured);
     2895        if (RT_FAILURE(rc))
     2896        {
     2897            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     2898            AssertRC(rc2);
     2899            break;
     2900        }
     2901
     2902        Assert(cbCaptured <= sizeof(abChunk));
     2903        if (cbCaptured > sizeof(abChunk)) /* Paranoia. */
     2904            cbCaptured = (uint32_t)sizeof(abChunk);
     2905
     2906        if (!cbCaptured) /* Nothing captured? Take a shortcut. */
     2907            break;
     2908
     2909        /* We use the host side mixing buffer as an intermediate buffer to do some
     2910         * (first) processing (if needed), so always write the incoming data at offset 0. */
     2911        uint32_t cfHstWritten = 0;
     2912        rc = AudioMixBufWriteAt(&pStreamEx->Host.MixBuf, 0 /* offFrames */, abChunk, cbCaptured, &cfHstWritten);
     2913        if (   RT_FAILURE(rc)
     2914            || !cfHstWritten)
     2915        {
     2916            AssertMsgFailed(("[%s] Write failed: cbCaptured=%RU32, cfHstWritten=%RU32, rc=%Rrc\n",
     2917                             pStreamEx->Core.szName, cbCaptured, cfHstWritten, rc));
     2918            break;
     2919        }
     2920
     2921        if (pThis->In.Cfg.Dbg.fEnabled)
     2922            AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCaptureNonInterleaved, abChunk, cbCaptured, 0 /* fFlags */);
     2923
     2924        uint32_t cfHstMixed = 0;
     2925        if (cfHstWritten)
     2926        {
     2927            int rc2 = AudioMixBufMixToParentEx(&pStreamEx->Host.MixBuf, 0 /* cSrcOffset */, cfHstWritten /* cSrcFrames */,
     2928                                               &cfHstMixed /* pcSrcMixed */);
     2929            Log3Func(("[%s] cbCaptured=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
     2930                      pStreamEx->Core.szName, cbCaptured, cfHstWritten, cfHstMixed, rc2));
     2931            AssertRC(rc2);
     2932        }
     2933
     2934        Assert(cbReadable >= cbCaptured);
     2935        cbReadable      -= cbCaptured;
     2936        cfCapturedTotal += cfHstMixed;
     2937    }
     2938
     2939    if (RT_SUCCESS(rc))
     2940    {
     2941        if (cfCapturedTotal)
     2942            Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
     2943    }
     2944    else
     2945        LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pStreamEx->Core.szName, rc));
     2946
     2947    if (pcfCaptured)
     2948        *pcfCaptured = cfCapturedTotal;
     2949
     2950    return rc;
     2951}
     2952
     2953
     2954/**
     2955 * Captures raw input from a host stream.
     2956 *
     2957 * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
     2958 * no data layout processing done in between.
     2959 *
     2960 * Needed for e.g. the VRDP audio backend (in Main).
     2961 *
     2962 * @returns VBox status code.
     2963 * @param   pThis       Driver instance.
     2964 * @param   pStreamEx   Stream to capture from.
     2965 * @param   pcfCaptured Number of (host) audio frames captured.
     2966 */
     2967static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
     2968{
     2969    Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
     2970    Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
     2971    AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
     2972
     2973    /*
     2974     * ...
     2975     */
     2976    /* Note: Raw means *audio frames*, not bytes! */
     2977    uint32_t cfReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
     2978    if (!cfReadable)
     2979        Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
     2980
     2981    const uint32_t cfFree = AudioMixBufFree(&pStreamEx->Guest.MixBuf); /* Parent */
     2982    if (!cfFree)
     2983        Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
     2984
     2985    if (cfReadable > cfFree) /* More data readable than we can store at the moment? Limit. */
     2986        cfReadable = cfFree;
     2987
     2988    /*
     2989     * ...
     2990     */
     2991    int      rc              = VINF_SUCCESS;
     2992    uint32_t cfCapturedTotal = 0;
     2993    while (cfReadable)
     2994    {
     2995        PPDMAUDIOFRAME paFrames;
     2996        uint32_t cfWritable;
     2997        rc = AudioMixBufPeekMutable(&pStreamEx->Host.MixBuf, cfReadable, &paFrames, &cfWritable);
     2998        if (   RT_FAILURE(rc)
     2999            || !cfWritable)
     3000            break;
     3001
     3002        uint32_t cfCaptured;
     3003        rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
     3004                                                    paFrames, cfWritable, &cfCaptured);
     3005        if (RT_FAILURE(rc))
     3006        {
     3007            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
     3008            AssertRC(rc2);
     3009            break;
     3010        }
     3011
     3012        Assert(cfCaptured <= cfWritable);
     3013        if (cfCaptured > cfWritable) /* Paranoia. */
     3014            cfCaptured = cfWritable;
     3015
     3016        Assert(cfReadable >= cfCaptured);
     3017        cfReadable      -= cfCaptured;
     3018        cfCapturedTotal += cfCaptured;
     3019    }
     3020
     3021    if (pcfCaptured)
     3022        *pcfCaptured = cfCapturedTotal;
     3023    Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
     3024    return rc;
     3025}
     3026
     3027
     3028/**
     3029 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
     3030 */
     3031static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
     3032                                               PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
     3033{
     3034    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    29403035    AssertPtr(pThis);
    2941     AssertPtr(pStreamEx);
    2942 
    2943     int rc = VINF_SUCCESS;
     3036    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
     3037    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
     3038    AssertPtrNull(pcFramesCaptured);
     3039    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     3040    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     3041    AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
     3042              ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
     3043               pStreamEx->Core.szName, pStreamEx->Core.enmDir));
     3044    int rc = RTCritSectEnter(&pThis->CritSect);
     3045    AssertRCReturn(rc, rc);
    29443046
    29453047#ifdef LOG_ENABLED
    29463048    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
    29473049#endif
    2948     LogFunc(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
    2949 
    2950     if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED)
    2951     {
    2952         AssertPtr(pStreamEx->pBackend);
    2953 
    2954         /* Check if the pointer to  the host audio driver is still valid.
    2955          * It can be NULL if we were called in drvAudioDestruct, for example. */
    2956         if (pThis->pHostDrvAudio)
    2957             rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pStreamEx->pBackend);
    2958 
    2959         pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
    2960     }
    2961 
    2962     LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
    2963     return rc;
    2964 }
    2965 
    2966 /**
    2967  * Uninitializes an audio stream - worker for drvAudioStreamDestroy,
    2968  * drvAudioDestruct and drvAudioStreamCreate.
    2969  *
    2970  * @returns VBox status code.
    2971  * @param   pThis       Pointer to driver instance.
    2972  * @param   pStreamEx   Pointer to audio stream to uninitialize.
    2973  *
    2974  * @note    Caller owns the critical section.
    2975  */
    2976 static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
    2977 {
    2978     AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
    2979     AssertMsgReturn(pStreamEx->Core.cRefs <= 1,
    2980                     ("Stream '%s' still has %RU32 references held when uninitializing\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs),
    2981                     VERR_WRONG_ORDER);
    2982     LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
     3050    Log3Func(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
    29833051
    29843052    /*
    29853053     * ...
    29863054     */
    2987     int rc = drvAudioStreamControlInternal(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    2988     if (RT_SUCCESS(rc))
    2989         rc = drvAudioStreamDestroyInternalBackend(pThis, pStreamEx);
    2990 
    2991     /* Destroy mixing buffers. */
    2992     AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
    2993     AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
    2994 
    2995     /* Free pre-buffer space. */
    2996     if (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
    2997         && pStreamEx->Out.pbPreBuf)
    2998     {
    2999         RTMemFree(pStreamEx->Out.pbPreBuf);
    3000         pStreamEx->Out.pbPreBuf      = NULL;
    3001         pStreamEx->Out.cbPreBufAlloc = 0;
    3002         pStreamEx->Out.cbPreBuffered = 0;
    3003     }
    3004 
    3005     if (RT_SUCCESS(rc))
    3006     {
    3007 #ifdef LOG_ENABLED
    3008         if (pStreamEx->Core.fStatus != PDMAUDIOSTREAMSTS_FLAGS_NONE)
    3009         {
    3010             char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
    3011             LogFunc(("[%s] Warning: Still has %s set when uninitializing\n",
    3012                      pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
    3013         }
    3014 #endif
    3015         pStreamEx->Core.fStatus = PDMAUDIOSTREAMSTS_FLAGS_NONE;
    3016     }
    3017 
    3018     PPDMDRVINS const pDrvIns = pThis->pDrvIns;
    3019     PDMDrvHlpSTAMDeregisterByPrefix(pDrvIns, pStreamEx->Core.szName);
    3020 
    3021     if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
    3022     {
    3023         if (pThis->In.Cfg.Dbg.fEnabled)
    3024         {
    3025             AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileCaptureNonInterleaved);
    3026             pStreamEx->In.Dbg.pFileCaptureNonInterleaved = NULL;
    3027 
    3028             AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileStreamRead);
    3029             pStreamEx->In.Dbg.pFileStreamRead = NULL;
    3030         }
    3031     }
    3032     else
    3033     {
    3034         Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT);
    3035         if (pThis->Out.Cfg.Dbg.fEnabled)
    3036         {
    3037             AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFilePlayNonInterleaved);
    3038             pStreamEx->Out.Dbg.pFilePlayNonInterleaved = NULL;
    3039 
    3040             AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFileStreamWrite);
    3041             pStreamEx->Out.Dbg.pFileStreamWrite = NULL;
    3042         }
    3043     }
    3044     LogFlowFunc(("Returning %Rrc\n", rc));
    3045     return rc;
    3046 }
    3047 
    3048 /**
    3049  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
    3050  */
    3051 static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
    3052 {
    3053     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    3054     AssertPtr(pThis);
    3055 
    3056     if (!pStream)
    3057         return VINF_SUCCESS;
    3058     AssertPtrReturn(pStream, VERR_INVALID_POINTER);
    3059     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;   /* Note! Do not touch pStream after this! */
    3060     Assert(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
    3061     Assert(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
    3062 
    3063     int rc = RTCritSectEnter(&pThis->CritSect);
    3064     AssertRCReturn(rc, rc);
    3065 
    3066     LogRel2(("Audio: Destroying stream '%s'\n", pStreamEx->Core.szName));
    3067 
    3068     LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
    3069     AssertMsg(pStreamEx->Core.cRefs <= 1, ("%u %s\n", pStreamEx->Core.cRefs, pStreamEx->Core.szName));
    3070     if (pStreamEx->Core.cRefs <= 1)
    3071     {
    3072         rc = drvAudioStreamUninitInternal(pThis, pStreamEx);
     3055    uint32_t cfCaptured = 0;
     3056    do
     3057    {
     3058        if (!pThis->pHostDrvAudio)
     3059        {
     3060            rc = VERR_PDM_NO_ATTACHED_DRIVER;
     3061            break;
     3062        }
     3063
     3064        if (   !pThis->In.fEnabled
     3065            || !PDMAudioStrmStatusCanRead(pStreamEx->Core.fStatus))
     3066        {
     3067            rc = VERR_AUDIO_STREAM_NOT_READY;
     3068            break;
     3069        }
     3070
     3071        /*
     3072         * Do the actual capturing.
     3073         */
     3074        if (RT_LIKELY(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
     3075            rc = drvAudioStreamCaptureNonInterleaved(pThis, pStreamEx, &cfCaptured);
     3076        else if (pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
     3077            rc = drvAudioStreamCaptureRaw(pThis, pStreamEx, &cfCaptured);
     3078        else
     3079            AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
     3080
    30733081        if (RT_SUCCESS(rc))
    30743082        {
    3075             if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
    3076                 pThis->In.cStreamsFree++;
    3077             else /* Out */
    3078                 pThis->Out.cStreamsFree++;
    3079 
    3080             RTListNodeRemove(&pStreamEx->ListEntry);
    3081 
    3082             drvAudioStreamFree(pStreamEx);
    3083             pStreamEx = NULL;
    3084             pStream = NULL;
    3085         }
    3086         else
    3087             LogRel(("Audio: Uninitializing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
    3088     }
    3089     else
    3090         rc = VERR_WRONG_ORDER;
     3083            Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCaptured, rc));
     3084
     3085            STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn,              cfCaptured);
     3086            STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesCaptured, cfCaptured);
     3087        }
     3088        else if (RT_UNLIKELY(RT_FAILURE(rc)))
     3089            LogRel(("Audio: Capturing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
     3090    } while (0);
    30913091
    30923092    RTCritSectLeave(&pThis->CritSect);
    3093     LogFlowFuncLeaveRC(rc);
     3093
     3094    if (pcFramesCaptured)
     3095        *pcFramesCaptured = cfCaptured;
     3096
     3097    if (RT_FAILURE(rc))
     3098        LogFlowFuncLeaveRC(rc);
    30943099    return rc;
    30953100}
     
    32863291    RTCritSectLeave(&pThis->CritSect);
    32873292    return rc;
     3293}
     3294
     3295
     3296/**
     3297 * Handles state changes for all audio streams.
     3298 *
     3299 * @param   pDrvIns             Pointer to driver instance.
     3300 * @param   enmCmd              Stream command to set for all streams.
     3301 */
     3302static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
     3303{
     3304    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
     3305    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
     3306    LogFlowFunc(("enmCmd=%s\n", PDMAudioStrmCmdGetName(enmCmd)));
     3307
     3308    int rc2 = RTCritSectEnter(&pThis->CritSect);
     3309    AssertRCReturnVoid(rc2);
     3310
     3311    if (pThis->pHostDrvAudio)
     3312    {
     3313        PDRVAUDIOSTREAM pStreamEx;
     3314        RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
     3315        {
     3316            drvAudioStreamControlInternal(pThis, pStreamEx, enmCmd);
     3317        }
     3318    }
     3319
     3320    rc2 = RTCritSectLeave(&pThis->CritSect);
     3321    AssertRC(rc2);
    32883322}
    32893323
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