Index: trunk/src/VBox/Devices/Audio/DrvAudio.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/DrvAudio.cpp	(revision 88714)
+++ trunk/src/VBox/Devices/Audio/DrvAudio.cpp	(revision 88715)
@@ -398,244 +398,4 @@
 #endif /* !VBOX_AUDIO_TESTCASE */
 
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
- */
-static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
-                                               PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-
-    /** @todo r=bird: why?  It's not documented to ignore NULL streams.   */
-    if (!pStream)
-        return VINF_SUCCESS;
-    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
-    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
-    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    LogFlowFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, PDMAudioStrmCmdGetName(enmStreamCmd)));
-
-    rc = drvAudioStreamControlInternal(pThis, pStreamEx, enmStreamCmd);
-
-    RTCritSectLeave(&pThis->CritSect);
-    return rc;
-}
-
-/**
- * Controls an audio stream.
- *
- * @returns VBox status code.
- * @param   pThis           Pointer to driver instance.
- * @param   pStreamEx       Stream to control.
- * @param   enmStreamCmd    Control command.
- */
-static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
-{
-    AssertPtr(pThis);
-    AssertPtr(pStreamEx);
-
-#ifdef LOG_ENABLED
-    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
-#endif
-    LogFunc(("[%s] enmStreamCmd=%s fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
-             dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
-
-    int rc = VINF_SUCCESS;
-
-    switch (enmStreamCmd)
-    {
-        case PDMAUDIOSTREAMCMD_ENABLE:
-        {
-            if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
-            {
-                /* Is a pending disable outstanding? Then disable first. */
-                if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
-                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
-
-                if (RT_SUCCESS(rc))
-                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
-
-                if (RT_SUCCESS(rc))
-                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
-            }
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_DISABLE:
-        {
-            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED)
-            {
-                /*
-                 * For playback (output) streams first mark the host stream as pending disable,
-                 * so that the rest of the remaining audio data will be played first before
-                 * closing the stream.
-                 */
-                if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
-                {
-                    LogFunc(("[%s] Pending disable/pause\n", pStreamEx->Core.szName));
-                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE;
-
-                    /* Schedule a follow up timer to the pending-disable state.  We cannot rely
-                       on the device to provide further callouts to finish the state transition.
-                       10ms is taking out of thin air and may be too course grained, we should
-                       really consider the amount of unplayed buffer in the backend and what not... */
-                    if (!pThis->fTimerArmed)
-                    {
-                        LogFlowFunc(("Arming emergency pending-disable hack...\n"));
-                        int rc2 = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimer, 10 /*ms*/);
-                        AssertRC(rc2);
-                        pThis->fTimerArmed = true;
-                    }
-                }
-
-                /* Can we close the host stream as well (not in pending disable mode)? */
-                if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
-                {
-                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
-                    if (RT_SUCCESS(rc))
-                        drvAudioStreamResetInternal(pThis, pStreamEx);
-                }
-            }
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_PAUSE:
-        {
-            if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED))
-            {
-                rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_PAUSE);
-                if (RT_SUCCESS(rc))
-                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
-            }
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_RESUME:
-        {
-            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
-            {
-                rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_RESUME);
-                if (RT_SUCCESS(rc))
-                    pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
-            }
-            break;
-        }
-
-        default:
-            rc = VERR_NOT_IMPLEMENTED;
-            break;
-    }
-
-    if (RT_FAILURE(rc))
-        LogFunc(("[%s] Failed with %Rrc\n", pStreamEx->Core.szName, rc));
-
-    return rc;
-}
-
-/**
- * Controls a stream's backend.
- *
- * If the stream has no backend available, VERR_NOT_FOUND is returned
- * (bird: actually the code returns VINF_SUCCESS).
- *
- * @returns VBox status code.
- * @param   pThis           Pointer to driver instance.
- * @param   pStreamEx       Stream to control.
- * @param   enmStreamCmd    Control command.
- */
-static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
-{
-    AssertPtr(pThis);
-    AssertPtr(pStreamEx);
-
-#ifdef LOG_ENABLED
-    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
-#endif
-    LogFlowFunc(("[%s] enmStreamCmd=%s, fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
-                 dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
-
-    if (!pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
-        return VINF_SUCCESS;
-
-
-    /*
-     * Whether to propagate commands down to the backend.
-     *
-     * This is needed for critical operations like recording audio if audio input is disabled on a per-driver level.
-     *
-     * Note that not all commands will be covered by this, such as operations like stopping, draining and droppping,
-     * which are considered uncritical and sometimes even are required for certain backends (like DirectSound on Windows).
-     *
-     * The actual stream state will be untouched to not make the state machine handling more complicated than
-     * it already is.
-     *
-     * See @bugref{9882}.
-     */
-    const bool fEnabled =    (   pStreamEx->Core.enmDir == PDMAUDIODIR_IN
-                              && pThis->In.fEnabled)
-                          || (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
-                              && pThis->Out.fEnabled);
-
-    LogRel2(("Audio: %s stream '%s' in backend (%s is %s)\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName,
-                                                              PDMAudioDirGetName(pStreamEx->Core.enmDir),
-                                                              fEnabled ? "enabled" : "disabled"));
-    int rc = VINF_SUCCESS;
-    switch (enmStreamCmd)
-    {
-        case PDMAUDIOSTREAMCMD_ENABLE:
-        {
-            if (fEnabled)
-                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_ENABLE);
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_DISABLE:
-        {
-            rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DISABLE);
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_PAUSE:
-        {
-            if (fEnabled) /* Needed, as resume below also is being checked for. */
-                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_PAUSE);
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_RESUME:
-        {
-            if (fEnabled)
-                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_RESUME);
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_DRAIN:
-        {
-            rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DRAIN);
-            break;
-        }
-
-        default:
-            AssertMsgFailedReturn(("Command %RU32 not implemented\n", enmStreamCmd), VERR_INTERNAL_ERROR_2);
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        if (   rc != VERR_NOT_IMPLEMENTED
-            && rc != VERR_NOT_SUPPORTED
-            && rc != VERR_AUDIO_STREAM_NOT_READY)
-        {
-            LogRel(("Audio: %s stream '%s' failed with %Rrc\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName, rc));
-        }
-
-        LogFunc(("[%s] %s failed with %Rrc\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd), rc));
-    }
-
-    return rc;
-}
 
 /**
@@ -999,138 +759,4 @@
 
 /**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
- */
-static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
-                                             const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-
-    /*
-     * Check input and sanity.
-     */
-    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
-    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
-    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
-    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
-    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
-    uint32_t uTmp;
-    if (!pcbWritten)
-        pcbWritten = &uTmp;
-    AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
-
-    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-    AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT,
-                    ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n",
-                     pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED);
-    Assert(pStreamEx->fNoMixBufs);
-
-    AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf),
-              ("Stream '%s' got a non-frame-aligned write (%RU32 bytes)\n", pStreamEx->Core.szName, cbBuf));
-
-/// @todo    STAM_PROFILE_ADV_START(&pThis->Stats.DelayOut, out); /* (stopped in drvAudioStreamPlayLocked) */
-
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    /*
-     * First check that we can write to the stream, and if not,
-     * whether to just drop the input into the bit bucket.
-     */
-    if (PDMAudioStrmStatusIsReady(pStreamEx->Core.fStatus))
-    {
-        if (   !pThis->Out.fEnabled         /* (see @bugref{9882}) */
-            || pThis->pHostDrvAudio == NULL /* (we used to work this condition differently) */
-            || !PDMAudioStrmStatusCanWrite(pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend)))
-        {
-            Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.szName,
-                      !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
-            *pcbWritten = cbBuf;
-            pStreamEx->offInternal += cbBuf;
-        }
-        /*
-         * No-mixing buffer mode:  Write the data directly to the backend, unless
-         * we're prebuffering.  There will be no pfnStreamPlay call in this mode.
-         */
-        else
-        {
-            uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore);
-            rc = drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
-            Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal);
-            if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc))
-            { /* likely */ }
-            else
-                AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */);
-        }
-    }
-    else
-        rc = VERR_AUDIO_STREAM_NOT_READY;
-
-    RTCritSectLeave(&pThis->CritSect);
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
- */
-static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
-{
-   AssertPtrReturn(pInterface, UINT32_MAX);
-   AssertPtrReturn(pStream,    UINT32_MAX);
-   AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
-   AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
-   RT_NOREF(pInterface);
-
-   uint32_t const cRefs = ASMAtomicIncU32(&pStream->cRefs);
-   Assert(cRefs > 1);
-   Assert(cRefs < _1K);
-
-   return cRefs;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
- */
-static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
-{
-   AssertPtrReturn(pInterface, UINT32_MAX);
-   AssertPtrReturn(pStream,    UINT32_MAX);
-   AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
-   AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
-   RT_NOREF(pInterface);
-
-   uint32_t cRefs = ASMAtomicDecU32(&pStream->cRefs);
-   AssertStmt(cRefs >= 1, cRefs = ASMAtomicIncU32(&pStream->cRefs));
-   Assert(cRefs < _1K);
-
-   return cRefs;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
- */
-static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
-    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
-    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    rc = drvAudioStreamIterateInternal(pThis, pStreamEx);
-
-    RTCritSectLeave(&pThis->CritSect);
-
-    if (RT_FAILURE(rc))
-        LogFlowFuncLeaveRC(rc);
-    return rc;
-}
-
-/**
  * Re-initializes the given stream if it is scheduled for this operation.
  *
@@ -1204,4 +830,1666 @@
     }
 }
+
+
+/**
+ * @callback_method_impl{FNTMTIMERDRV}
+ */
+static DECLCALLBACK(void) drvAudioEmergencyIterateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+    RT_NOREF(hTimer, pvUser);
+    RTCritSectEnter(&pThis->CritSect);
+
+    /*
+     * Iterate any stream with the pending-disable flag set.
+     */
+    uint32_t        cMilliesToNext = 0;
+    PDRVAUDIOSTREAM pStreamEx, pStreamExNext;
+    RTListForEachSafe(&pThis->lstStreams, pStreamEx, pStreamExNext, DRVAUDIOSTREAM, ListEntry)
+    {
+        if (   pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC
+            && pStreamEx->Core.cRefs >= 1)
+        {
+            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
+            {
+                drvAudioStreamIterateInternal(pThis, pStreamEx);
+
+                if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
+                    cMilliesToNext = 10;
+            }
+        }
+    }
+
+    /*
+     * Re-arm the timer if we still got streams in the pending state.
+     */
+    if (cMilliesToNext)
+    {
+        pThis->fTimerArmed = true;
+        PDMDrvHlpTimerSetMillies(pDrvIns, pThis->hTimer, cMilliesToNext);
+    }
+    else
+        pThis->fTimerArmed = false;
+
+    RTCritSectLeave(&pThis->CritSect);
+}
+
+
+#ifdef VBOX_WITH_AUDIO_ENUM
+/**
+ * Enumerates all host audio devices.
+ *
+ * This functionality might not be implemented by all backends and will return
+ * VERR_NOT_SUPPORTED if not being supported.
+ *
+ * @note Must not hold the driver's critical section!
+ *
+ * @returns VBox status code.
+ * @param   pThis               Driver instance to be called.
+ * @param   fLog                Whether to print the enumerated device to the release log or not.
+ * @param   pDevEnum            Where to store the device enumeration.
+ *
+ * @remarks This is currently ONLY used for release logging.
+ */
+static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIOHOSTENUM pDevEnum)
+{
+    AssertReturn(!RTCritSectIsOwner(&pThis->CritSect), VERR_WRONG_ORDER);
+
+    int rc;
+
+    /*
+     * If the backend supports it, do a device enumeration.
+     */
+    if (pThis->pHostDrvAudio->pfnGetDevices)
+    {
+        PDMAUDIOHOSTENUM DevEnum;
+        rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
+        if (RT_SUCCESS(rc))
+        {
+            if (fLog)
+                LogRel(("Audio: Found %RU16 devices for driver '%s'\n", DevEnum.cDevices, pThis->szName));
+
+            PPDMAUDIOHOSTDEV pDev;
+            RTListForEach(&DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
+            {
+                if (fLog)
+                {
+                    char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
+                    LogRel(("Audio: Device '%s':\n", pDev->szName));
+                    LogRel(("Audio:   Usage           = %s\n",   PDMAudioDirGetName(pDev->enmUsage)));
+                    LogRel(("Audio:   Flags           = %s\n",   PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags)));
+                    LogRel(("Audio:   Input channels  = %RU8\n", pDev->cMaxInputChannels));
+                    LogRel(("Audio:   Output channels = %RU8\n", pDev->cMaxOutputChannels));
+                }
+            }
+
+            if (pDevEnum)
+                rc = PDMAudioHostEnumCopy(pDevEnum, &DevEnum, PDMAUDIODIR_INVALID /*all*/, true /*fOnlyCoreData*/);
+
+            PDMAudioHostEnumDelete(&DevEnum);
+        }
+        else
+        {
+            if (fLog)
+                LogRel(("Audio: Device enumeration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
+            /* Not fatal. */
+        }
+    }
+    else
+    {
+        rc = VERR_NOT_SUPPORTED;
+
+        if (fLog)
+            LogRel2(("Audio: Host driver '%s' does not support audio device enumeration, skipping\n", pThis->szName));
+    }
+
+    LogFunc(("Returning %Rrc\n", rc));
+    return rc;
+}
+#endif /* VBOX_WITH_AUDIO_ENUM */
+
+/**
+ * Initializes the host backend and queries its initial configuration.
+ *
+ * @returns VBox status code.
+ * @param   pThis               Driver instance to be called.
+ */
+static int drvAudioHostInit(PDRVAUDIO pThis)
+{
+    LogFlowFuncEnter();
+
+    /*
+     * Check the function pointers, make sure the ones we define as
+     * mandatory are present.
+     */
+    PPDMIHOSTAUDIO pHostDrvAudio = pThis->pHostDrvAudio;
+    AssertPtrReturn(pHostDrvAudio, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnGetConfig, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pHostDrvAudio->pfnGetDevices, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pHostDrvAudio->pfnGetStatus, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pHostDrvAudio->pfnStreamConfigHint, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamCreate, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamDestroy, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamControl, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamGetReadable, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamGetWritable, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pHostDrvAudio->pfnStreamGetPending, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamGetStatus, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamPlay, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostDrvAudio->pfnStreamCapture, VERR_INVALID_POINTER);
+
+    /*
+     * Get the backend configuration.
+     */
+    int rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("Audio: Getting configuration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
+        return VERR_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    pThis->In.cStreamsFree  = pThis->BackendCfg.cMaxStreamsIn;
+    pThis->Out.cStreamsFree = pThis->BackendCfg.cMaxStreamsOut;
+
+    LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
+
+    LogRel2(("Audio: Host driver '%s' supports %RU32 input streams and %RU32 output streams at once.\n",
+             pThis->szName, pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
+
+#ifdef VBOX_WITH_AUDIO_ENUM
+    int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
+    if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
+        AssertRC(rc2);
+
+    RT_NOREF(rc2);
+    /* Ignore rc. */
+#endif
+
+    LogFlowFuncLeave();
+    return VINF_SUCCESS;
+}
+
+
+/*********************************************************************************************************************************
+*   PDMIAUDIOCONNECTOR                                                                                                           *
+*********************************************************************************************************************************/
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnEnable}
+ */
+static DECLCALLBACK(int) drvAudioEnable(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir, bool fEnable)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+
+    bool *pfEnabled;
+    if (enmDir == PDMAUDIODIR_IN)
+        pfEnabled = &pThis->In.fEnabled;
+    else if (enmDir == PDMAUDIODIR_OUT)
+        pfEnabled = &pThis->Out.fEnabled;
+    else
+        AssertFailedReturn(VERR_INVALID_PARAMETER);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
+
+    if (fEnable != *pfEnabled)
+    {
+        LogRel(("Audio: %s %s for driver '%s'\n",
+                fEnable ? "Enabling" : "Disabling", enmDir == PDMAUDIODIR_IN ? "input" : "output", pThis->szName));
+
+        /* Update the status first, as this will be checked for in drvAudioStreamControlInternalBackend() below. */
+        *pfEnabled = fEnable;
+
+        PDRVAUDIOSTREAM pStreamEx;
+        RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
+        {
+            if (pStreamEx->Core.enmDir != enmDir) /* Skip unwanted streams. */
+                continue;
+
+            /* Note: Only enable / disable the backend, do *not* change the stream's internal status.
+             *       Callers (device emulation, mixer, ...) from outside will not see any status or behavior change,
+             *       to not confuse the rest of the state machine.
+             *
+             *       When disabling:
+             *          - playing back audo data would go to /dev/null
+             *          - recording audio data would return silence instead
+             *
+             * See @bugref{9882}.
+             */
+            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx,
+                                                           fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
+            if (RT_FAILURE(rc2))
+            {
+                if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
+                    LogRel(("Audio: Stream '%s' not available\n", pStreamEx->Core.szName));
+                else
+                    LogRel(("Audio: Failed to %s %s stream '%s', rc=%Rrc\n", fEnable ? "enable" : "disable",
+                            enmDir == PDMAUDIODIR_IN ? "input" : "output", pStreamEx->Core.szName, rc2));
+            }
+            else
+            {
+                /* When (re-)enabling a stream, clear the disabled warning bit again. */
+                if (fEnable)
+                    pStreamEx->Core.fWarningsShown &= ~PDMAUDIOSTREAM_WARN_FLAGS_DISABLED;
+            }
+
+            if (RT_SUCCESS(rc))
+                rc = rc2;
+
+            /* Keep going. */
+        }
+    }
+
+    RTCritSectLeave(&pThis->CritSect);
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
+ */
+static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, false);
+
+    bool fEnabled;
+    if (enmDir == PDMAUDIODIR_IN)
+        fEnabled = pThis->In.fEnabled;
+    else if (enmDir == PDMAUDIODIR_OUT)
+        fEnabled = pThis->Out.fEnabled;
+    else
+        AssertFailedStmt(fEnabled = false);
+
+    RTCritSectLeave(&pThis->CritSect);
+    return fEnabled;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
+ */
+static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
+
+    if (pThis->pHostDrvAudio)
+    {
+        if (pThis->pHostDrvAudio->pfnGetConfig)
+            rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
+        else
+            rc = VERR_NOT_SUPPORTED;
+    }
+    else
+        rc = VERR_PDM_NO_ATTACHED_DRIVER;
+
+    RTCritSectLeave(&pThis->CritSect);
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
+ */
+static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, PDMAUDIOBACKENDSTS_UNKNOWN);
+
+    PDMAUDIOBACKENDSTS fBackendStatus;
+    if (pThis->pHostDrvAudio)
+    {
+        if (pThis->pHostDrvAudio->pfnGetStatus)
+            fBackendStatus = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
+        else
+            fBackendStatus = PDMAUDIOBACKENDSTS_UNKNOWN;
+    }
+    else
+        fBackendStatus = PDMAUDIOBACKENDSTS_NOT_ATTACHED;
+
+    RTCritSectLeave(&pThis->CritSect);
+    LogFlowFunc(("LEAVE - %#x\n", fBackendStatus));
+    return fBackendStatus;
+}
+
+
+/**
+ * Adjusts the request stream configuration, applying our settings.
+ *
+ * This also does some basic validations.
+ *
+ * Used by both the stream creation and stream configuration hinting code.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Pointer to the DrvAudio instance data.
+ * @param   pCfgReq     The request configuration that should be adjusted.
+ * @param   pszName     Stream name to use when logging warnings and errors.
+ */
+static int drvAudioStreamAdjustConfig(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfgReq, const char *pszName)
+{
+    /* Get the right configuration for the stream to be created. */
+    PDRVAUDIOCFG pDrvCfg = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.Cfg : &pThis->Out.Cfg;
+
+    /* Fill in the tweakable parameters into the requested host configuration.
+     * All parameters in principle can be changed and returned by the backend via the acquired configuration. */
+
+    /*
+     * PCM
+     */
+    if (PDMAudioPropsSampleSize(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
+    {
+        PDMAudioPropsSetSampleSize(&pCfgReq->Props, PDMAudioPropsSampleSize(&pDrvCfg->Props));
+        LogRel2(("Audio: Using custom sample size of %RU8 bytes for stream '%s'\n",
+                 PDMAudioPropsSampleSize(&pCfgReq->Props), pszName));
+    }
+
+    if (pDrvCfg->Props.uHz) /* Anything set via custom extra-data? */
+    {
+        pCfgReq->Props.uHz = pDrvCfg->Props.uHz;
+        LogRel2(("Audio: Using custom Hz rate %RU32 for stream '%s'\n", pCfgReq->Props.uHz, pszName));
+    }
+
+    if (pDrvCfg->uSigned != UINT8_MAX) /* Anything set via custom extra-data? */
+    {
+        pCfgReq->Props.fSigned = RT_BOOL(pDrvCfg->uSigned);
+        LogRel2(("Audio: Using custom %s sample format for stream '%s'\n",
+                 pCfgReq->Props.fSigned ? "signed" : "unsigned", pszName));
+    }
+
+    if (pDrvCfg->uSwapEndian != UINT8_MAX) /* Anything set via custom extra-data? */
+    {
+        pCfgReq->Props.fSwapEndian = RT_BOOL(pDrvCfg->uSwapEndian);
+        LogRel2(("Audio: Using custom %s endianess for samples of stream '%s'\n",
+                 pCfgReq->Props.fSwapEndian ? "swapped" : "original", pszName));
+    }
+
+    if (PDMAudioPropsChannels(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
+    {
+        PDMAudioPropsSetChannels(&pCfgReq->Props, PDMAudioPropsChannels(&pDrvCfg->Props));
+        LogRel2(("Audio: Using custom %RU8 channel(s) for stream '%s'\n", PDMAudioPropsChannels(&pDrvCfg->Props), pszName));
+    }
+
+    /* Validate PCM properties. */
+    if (!AudioHlpPcmPropsAreValid(&pCfgReq->Props))
+    {
+        LogRel(("Audio: Invalid custom PCM properties set for stream '%s', cannot create stream\n", pszName));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Period size
+     */
+    const char *pszWhat = "device-specific";
+    if (pDrvCfg->uPeriodSizeMs)
+    {
+        pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPeriodSizeMs);
+        pszWhat = "custom";
+    }
+
+    if (!pCfgReq->Backend.cFramesPeriod) /* Set default period size if nothing explicitly is set. */
+    {
+        pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 150 /*ms*/);
+        pszWhat = "default";
+    }
+
+    LogRel2(("Audio: Using %s period size %RU64 ms / %RU32 frames for stream '%s'\n",
+             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod),
+             pCfgReq->Backend.cFramesPeriod, pszName));
+
+    /*
+     * Buffer size
+     */
+    pszWhat = "device-specific";
+    if (pDrvCfg->uBufferSizeMs)
+    {
+        pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uBufferSizeMs);
+        pszWhat = "custom";
+    }
+
+    if (!pCfgReq->Backend.cFramesBufferSize) /* Set default buffer size if nothing explicitly is set. */
+    {
+        pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 300 /*ms*/);
+        pszWhat = "default";
+    }
+
+    LogRel2(("Audio: Using %s buffer size %RU64 ms / %RU32 frames for stream '%s'\n",
+             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
+             pCfgReq->Backend.cFramesBufferSize, pszName));
+
+    /*
+     * Pre-buffering size
+     */
+    pszWhat = "device-specific";
+    if (pDrvCfg->uPreBufSizeMs != UINT32_MAX) /* Anything set via global / per-VM extra-data? */
+    {
+        pCfgReq->Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPreBufSizeMs);
+        pszWhat = "custom";
+    }
+    else /* No, then either use the default or device-specific settings (if any). */
+    {
+        if (pCfgReq->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */
+        {
+            /* Pre-buffer 66% of the buffer. */
+            pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize * 2 / 3;
+            pszWhat = "default";
+        }
+    }
+
+    LogRel2(("Audio: Using %s pre-buffering size %RU64 ms / %RU32 frames for stream '%s'\n",
+             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
+             pCfgReq->Backend.cFramesPreBuffering, pszName));
+
+    /*
+     * Validate input.
+     */
+    if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPeriod)
+    {
+        LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the period size (%RU64ms)\n",
+                pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
+                PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod)));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    if (   pCfgReq->Backend.cFramesPreBuffering != UINT32_MAX /* Custom pre-buffering set? */
+        && pCfgReq->Backend.cFramesPreBuffering)
+    {
+        if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPreBuffering)
+        {
+            LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the pre-buffering size (%RU64ms)\n",
+                    pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
+                    PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize)));
+            return VERR_INVALID_PARAMETER;
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamConfigHint}
+ */
+static DECLCALLBACK(void) drvAudioStreamConfigHint(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertReturnVoid(pCfg->enmDir == PDMAUDIODIR_IN || pCfg->enmDir == PDMAUDIODIR_OUT);
+
+    int rc = RTCritSectEnter(&pThis->CritSect); /** @todo Reconsider the locking for DrvAudio */
+    AssertRCReturnVoid(rc);
+
+    /*
+     * Don't do anything unless the backend has a pfnStreamConfigHint method
+     * and the direction is currently enabled.
+     */
+    if (   pThis->pHostDrvAudio
+        && pThis->pHostDrvAudio->pfnStreamConfigHint)
+    {
+        if (pCfg->enmDir == PDMAUDIODIR_OUT ? pThis->Out.fEnabled : pThis->In.fEnabled)
+        {
+            /*
+             * Adjust the configuration (applying out settings) then call the backend driver.
+             */
+            rc = drvAudioStreamAdjustConfig(pThis, pCfg, pCfg->szName);
+            AssertLogRelRC(rc);
+            if (RT_SUCCESS(rc))
+                pThis->pHostDrvAudio->pfnStreamConfigHint(pThis->pHostDrvAudio, pCfg);
+        }
+        else
+            LogFunc(("Ignoring hint because direction is not currently enabled\n"));
+    }
+    else
+        LogFlowFunc(("Ignoring hint because backend has no pfnStreamConfigHint method.\n"));
+
+    RTCritSectLeave(&pThis->CritSect);
+}
+
+
+/**
+ * Worker for drvAudioStreamInitInternal and drvAudioStreamReInitInternal that
+ * creates the backend (host driver) side of an audio stream.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Pointer to driver instance.
+ * @param   pStreamEx   Audio stream to create the backend side for.
+ * @param   pCfgReq     Requested audio stream configuration to use for
+ *                      stream creation.
+ * @param   pCfgAcq     Acquired audio stream configuration returned by
+ *                      the backend.
+ *
+ * @note    Configuration precedence for requested audio stream configuration (first has highest priority, if set):
+ *          - per global extra-data
+ *          - per-VM extra-data
+ *          - requested configuration (by pCfgReq)
+ *          - default value
+ */
+static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
+                                               PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
+{
+    AssertMsg((pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED) == 0,
+              ("Stream '%s' already initialized in backend\n", pStreamEx->Core.szName));
+
+    /*
+     * Adjust the requested stream config, applying our settings.
+     */
+    int rc = drvAudioStreamAdjustConfig(pThis, pCfgReq, pStreamEx->Core.szName);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Make the acquired host configuration the requested host configuration initially,
+     * in case the backend does not report back an acquired configuration.
+     */
+    /** @todo r=bird: This is conveniently not documented in the interface... */
+    rc = PDMAudioStrmCfgCopy(pCfgAcq, pCfgReq);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
+                pStreamEx->Core.szName));
+        return rc;
+    }
+
+    /*
+     * Call the host driver to create the stream.
+     */
+    AssertPtr(pThis->pHostDrvAudio);
+    if (pThis->pHostDrvAudio)
+        rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pStreamEx->pBackend, pCfgReq, pCfgAcq);
+    else
+        rc = VERR_PDM_NO_ATTACHED_DRIVER;
+    if (RT_FAILURE(rc))
+    {
+        if (rc == VERR_NOT_SUPPORTED)
+            LogRel2(("Audio: Creating stream '%s' in backend not supported\n", pStreamEx->Core.szName));
+        else if (rc == VERR_AUDIO_STREAM_COULD_NOT_CREATE)
+            LogRel2(("Audio: Stream '%s' could not be created in backend because of missing hardware / drivers\n", pStreamEx->Core.szName));
+        else
+            LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pStreamEx->Core.szName, rc));
+        return rc;
+    }
+
+    /* Validate acquired configuration. */
+    char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
+    AssertLogRelMsgReturn(AudioHlpStreamCfgIsValid(pCfgAcq),
+                          ("Audio: Creating stream '%s' returned an invalid backend configuration (%s), skipping\n",
+                           pStreamEx->Core.szName, PDMAudioPropsToString(&pCfgAcq->Props, szTmp, sizeof(szTmp))),
+                          VERR_INVALID_PARAMETER);
+
+    /* Let the user know that the backend changed one of the values requested above. */
+    if (pCfgAcq->Backend.cFramesBufferSize != pCfgReq->Backend.cFramesBufferSize)
+        LogRel2(("Audio: Buffer size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
+                 pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesBufferSize), pCfgAcq->Backend.cFramesBufferSize));
+
+    if (pCfgAcq->Backend.cFramesPeriod != pCfgReq->Backend.cFramesPeriod)
+        LogRel2(("Audio: Period size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
+                 pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPeriod), pCfgAcq->Backend.cFramesPeriod));
+
+    /* Was pre-buffering requested, but the acquired configuration from the backend told us something else? */
+    if (pCfgReq->Backend.cFramesPreBuffering)
+    {
+        if (pCfgAcq->Backend.cFramesPreBuffering != pCfgReq->Backend.cFramesPreBuffering)
+            LogRel2(("Audio: Pre-buffering size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
+                     pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
+
+        if (pCfgAcq->Backend.cFramesPreBuffering > pCfgAcq->Backend.cFramesBufferSize)
+        {
+            pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesBufferSize;
+            LogRel2(("Audio: Pre-buffering size bigger than buffer size for stream '%s', adjusting to %RU64ms (%RU32 frames)\n",
+                     pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
+        }
+    }
+    else if (pCfgReq->Backend.cFramesPreBuffering == 0) /* Was the pre-buffering requested as being disabeld? Tell the users. */
+    {
+        LogRel2(("Audio: Pre-buffering is disabled for stream '%s'\n", pStreamEx->Core.szName));
+        pCfgAcq->Backend.cFramesPreBuffering = 0;
+    }
+
+    /* Sanity for detecting buggy backends. */
+    AssertMsgReturn(pCfgAcq->Backend.cFramesPeriod < pCfgAcq->Backend.cFramesBufferSize,
+                    ("Acquired period size must be smaller than buffer size\n"),
+                    VERR_INVALID_PARAMETER);
+    AssertMsgReturn(pCfgAcq->Backend.cFramesPreBuffering <= pCfgAcq->Backend.cFramesBufferSize,
+                    ("Acquired pre-buffering size must be smaller or as big as the buffer size\n"),
+                    VERR_INVALID_PARAMETER);
+
+    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for drvAudioStreamCreate that initializes the audio stream.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Pointer to driver instance.
+ * @param   pStreamEx   Stream to initialize.
+ * @param   fFlags      PDMAUDIOSTREAM_CREATE_F_XXX.
+ * @param   pCfgHost    Stream configuration to use for the host side (backend).
+ * @param   pCfgGuest   Stream configuration to use for the guest side.
+ */
+static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t fFlags,
+                                      PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
+{
+    /*
+     * Init host stream.
+     */
+    pStreamEx->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
+
+    /* Set the host's default audio data layout. */
+/** @todo r=bird: Why, oh why? OTOH, the layout stuff is non-sense anyway. */
+    pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
+
+#ifdef LOG_ENABLED
+    LogFunc(("[%s] Requested host format:\n", pStreamEx->Core.szName));
+    PDMAudioStrmCfgLog(pCfgHost);
+#endif
+
+    LogRel2(("Audio: Creating stream '%s'\n", pStreamEx->Core.szName));
+    LogRel2(("Audio: Guest %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
+             pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
+             pCfgGuest->Props.uHz, PDMAudioPropsSampleBits(&pCfgGuest->Props), pCfgGuest->Props.fSigned ? "S" : "U",
+             PDMAudioPropsChannels(&pCfgGuest->Props), PDMAudioPropsChannels(&pCfgGuest->Props) == 1 ? "" : "s"));
+    LogRel2(("Audio: Requested host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
+             pCfgHost->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
+             pCfgHost->Props.uHz, PDMAudioPropsSampleBits(&pCfgHost->Props), pCfgHost->Props.fSigned ? "S" : "U",
+             PDMAudioPropsChannels(&pCfgHost->Props), PDMAudioPropsChannels(&pCfgHost->Props) == 1 ? "" : "s"));
+
+    PDMAUDIOSTREAMCFG CfgHostAcq;
+    int rc = drvAudioStreamCreateInternalBackend(pThis, pStreamEx, pCfgHost, &CfgHostAcq);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    LogFunc(("[%s] Acquired host format:\n",  pStreamEx->Core.szName));
+    PDMAudioStrmCfgLog(&CfgHostAcq);
+    LogRel2(("Audio: Acquired host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
+             CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback",  pStreamEx->Core.szName,
+             CfgHostAcq.Props.uHz, PDMAudioPropsSampleBits(&CfgHostAcq.Props), CfgHostAcq.Props.fSigned ? "S" : "U",
+             PDMAudioPropsChannels(&CfgHostAcq.Props), PDMAudioPropsChannels(&CfgHostAcq.Props) == 1 ? "" : "s"));
+    Assert(PDMAudioPropsAreValid(&CfgHostAcq.Props));
+
+    /* Set the stream properties (currently guest side, when DevSB16 is
+       converted to mixer and PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF becomes
+       default, this will just be the stream properties). */
+    if (fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF)
+        pStreamEx->Core.Props = CfgHostAcq.Props;
+    else
+        pStreamEx->Core.Props = pCfgGuest->Props;
+
+    /* Let the user know if the backend changed some of the tweakable values. */
+    if (CfgHostAcq.Backend.cFramesBufferSize != pCfgHost->Backend.cFramesBufferSize)
+        LogRel2(("Audio: Backend changed buffer size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
+                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesBufferSize), pCfgHost->Backend.cFramesBufferSize,
+                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
+
+    if (CfgHostAcq.Backend.cFramesPeriod != pCfgHost->Backend.cFramesPeriod)
+        LogRel2(("Audio: Backend changed period size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
+                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPeriod), pCfgHost->Backend.cFramesPeriod,
+                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod), CfgHostAcq.Backend.cFramesPeriod));
+
+    if (CfgHostAcq.Backend.cFramesPreBuffering != pCfgHost->Backend.cFramesPreBuffering)
+        LogRel2(("Audio: Backend changed pre-buffering size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
+                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPreBuffering), pCfgHost->Backend.cFramesPreBuffering,
+                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
+
+    /*
+     * Check if the backend did return sane values and correct if necessary.
+     * Should never happen with our own backends, but you never know ...
+     */
+    uint32_t const cFramesPreBufferingMax = CfgHostAcq.Backend.cFramesBufferSize - RT_MIN(16, CfgHostAcq.Backend.cFramesBufferSize);
+    if (CfgHostAcq.Backend.cFramesPreBuffering > cFramesPreBufferingMax)
+    {
+        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",
+                 CfgHostAcq.Backend.cFramesPreBuffering, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, cFramesPreBufferingMax));
+        AssertFailed();
+        CfgHostAcq.Backend.cFramesPreBuffering = cFramesPreBufferingMax;
+    }
+
+    if (CfgHostAcq.Backend.cFramesPeriod > CfgHostAcq.Backend.cFramesBufferSize)
+    {
+        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",
+                 CfgHostAcq.Backend.cFramesPeriod, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, CfgHostAcq.Backend.cFramesBufferSize / 2));
+        AssertFailed();
+        CfgHostAcq.Backend.cFramesPeriod = CfgHostAcq.Backend.cFramesBufferSize / 2;
+    }
+
+    LogRel2(("Audio: Buffer size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
+             PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
+    LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
+             PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
+
+    /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
+    const uint32_t msPeriod = PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod);
+    LogRel2(("Audio: Period size of stream '%s' is %RU64 ms / %RU32 frames\n",
+             pStreamEx->Core.szName, msPeriod, CfgHostAcq.Backend.cFramesPeriod));
+
+    if (   pCfgGuest->Device.cMsSchedulingHint             /* Any scheduling hint set? */
+        && pCfgGuest->Device.cMsSchedulingHint > msPeriod) /* This might lead to buffer underflows. */
+        LogRel(("Audio: Warning: Scheduling hint of stream '%s' is bigger (%RU64ms) than used period size (%RU64ms)\n",
+                pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint, msPeriod));
+
+    /*
+     * Make a copy of the acquired host stream configuration and the guest side one.
+     */
+    rc = PDMAudioStrmCfgCopy(&pStreamEx->Host.Cfg, &CfgHostAcq);
+    AssertRC(rc);
+
+    /* Set the guests's default audio data layout. */
+    pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; /** @todo r=bird: WTF DO WE DO THIS?  It's input and probably should've been const... */
+    rc = PDMAudioStrmCfgCopy(&pStreamEx->Guest.Cfg, pCfgGuest);
+    AssertRC(rc);
+
+    /*
+     * Configure host buffers.
+     */
+
+    /* Destroy any former mixing buffer. */
+    AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
+
+    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
+    {
+        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
+        rc = AudioMixBufInit(&pStreamEx->Host.MixBuf, pStreamEx->Core.szName, &CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize);
+        AssertRCReturn(rc, rc);
+    }
+    /* Allocate space for pre-buffering of output stream w/o mixing buffers. */
+    else if (pCfgHost->enmDir == PDMAUDIODIR_OUT)
+    {
+        Assert(pStreamEx->Out.cbPreBufAlloc == 0);
+        Assert(pStreamEx->Out.cbPreBufThreshold == 0);
+        Assert(pStreamEx->Out.cbPreBuffered == 0);
+        if (CfgHostAcq.Backend.cFramesPreBuffering != 0)
+        {
+            pStreamEx->Out.cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering);
+            pStreamEx->Out.cbPreBufAlloc = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize - 2);
+            pStreamEx->Out.cbPreBufAlloc = RT_MIN(RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K),
+                                                  pStreamEx->Out.cbPreBufAlloc);
+            pStreamEx->Out.pbPreBuf = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
+            AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY);
+        }
+    }
+
+    /*
+     * Init guest stream.
+     */
+    if (pCfgGuest->Device.cMsSchedulingHint)
+        LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms (%RU32 bytes)\n",
+                 pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint,
+                 PDMAudioPropsMilliToBytes(&pCfgGuest->Props, pCfgGuest->Device.cMsSchedulingHint)));
+
+    /* Destroy any former mixing buffer. */
+    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
+
+    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
+    {
+        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
+        rc = AudioMixBufInit(&pStreamEx->Guest.MixBuf, pStreamEx->Core.szName, &pCfgGuest->Props, CfgHostAcq.Backend.cFramesBufferSize);
+        AssertRCReturn(rc, rc);
+    }
+
+    if (RT_FAILURE(rc))
+        LogRel(("Audio: Creating stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
+
+    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
+    {
+        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
+        /* Host (Parent) -> Guest (Child). */
+        rc = AudioMixBufLinkTo(&pStreamEx->Host.MixBuf, &pStreamEx->Guest.MixBuf);
+        AssertRC(rc);
+    }
+
+    /*
+     * Register statistics.
+     */
+    PPDMDRVINS const pDrvIns = pThis->pDrvIns;
+    /** @todo expose config and more. */
+    PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.Cfg.Backend.cFramesBufferSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                           "Host side: The size of the backend buffer (in frames)", "%s/0-HostBackendBufSize", pStreamEx->Core.szName);
+    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
+    {
+        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Host side: The size of the mixer buffer (in frames)",   "%s/1-HostMixBufSize", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Guest side: The size of the mixer buffer (in frames)",  "%s/2-GuestMixBufSize", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cMixed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Host side: Number of frames in the mixer buffer",   "%s/1-HostMixBufUsed", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Guest side: Number of frames in the mixer buffer",  "%s/2-GuestMixBufUsed", pStreamEx->Core.szName);
+    }
+    if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
+    {
+        /** @todo later? */
+    }
+    else
+    {
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableBefore, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Host side: Free space in backend buffer before play", "%s/0-HostBackendBufFreeBefore", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableAfter, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Host side: Free space in backend buffer after play",  "%s/0-HostBackendBufFreeAfter", pStreamEx->Core.szName);
+    }
+
+#ifdef VBOX_WITH_STATISTICS
+    if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
+    {
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total frames played.", "%s/TotalFramesCaptured", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total number of playbacks.", "%s/TotalTimesCaptured", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total frames read.", "%s/TotalFramesRead", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total number of reads.", "%s/TotalTimesRead", pStreamEx->Core.szName);
+    }
+    else
+    {
+        Assert(pCfgGuest->enmDir == PDMAUDIODIR_OUT);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total frames played.", "%s/TotalFramesPlayed", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total number of playbacks.", "%s/TotalTimesPlayed", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total frames written.", "%s/TotalFramesWritten", pStreamEx->Core.szName);
+        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
+                               "Total number of writes.", "%s/TotalTimesWritten", pStreamEx->Core.szName);
+    }
+#endif /* VBOX_WITH_STATISTICS */
+
+    LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
+ */
+static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface, uint32_t fFlags, PPDMAUDIOSTREAMCFG pCfgHost,
+                                              PPDMAUDIOSTREAMCFG pCfgGuest, PPDMAUDIOSTREAM *ppStream)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+
+    /*
+     * Assert sanity.
+     */
+    AssertReturn(!(fFlags & ~PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);
+    AssertPtrReturn(pCfgHost,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfgGuest,  VERR_INVALID_POINTER);
+    AssertPtrReturn(ppStream,   VERR_INVALID_POINTER);
+    LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
+#ifdef LOG_ENABLED
+    PDMAudioStrmCfgLog(pCfgHost);
+    PDMAudioStrmCfgLog(pCfgGuest);
+#endif
+    AssertReturn(AudioHlpStreamCfgIsValid(pCfgHost), VERR_INVALID_PARAMETER);
+    AssertReturn(AudioHlpStreamCfgIsValid(pCfgGuest), VERR_INVALID_PARAMETER);
+    AssertReturn(pCfgHost->enmDir == pCfgGuest->enmDir, VERR_MISMATCH);
+    AssertReturn(pCfgHost->enmDir == PDMAUDIODIR_IN || pCfgHost->enmDir == PDMAUDIODIR_OUT, VERR_NOT_SUPPORTED);
+    /* Require PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF for output streams: */
+    AssertReturn((fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF) || pCfgHost->enmDir == PDMAUDIODIR_IN, VERR_INVALID_FLAGS);
+
+    /*
+     * Lock the whole driver instance.
+     */
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Check that we have free streams in the backend and get the
+     * size of the backend specific stream data.
+     */
+    uint32_t *pcFreeStreams;
+    if (pCfgHost->enmDir == PDMAUDIODIR_IN)
+    {
+        if (!pThis->In.cStreamsFree)
+        {
+            LogFlowFunc(("Maximum number of host input streams reached\n"));
+            rc = VERR_AUDIO_NO_FREE_INPUT_STREAMS;
+        }
+        pcFreeStreams = &pThis->In.cStreamsFree;
+    }
+    else /* Out */
+    {
+        if (!pThis->Out.cStreamsFree)
+        {
+            LogFlowFunc(("Maximum number of host output streams reached\n"));
+            rc = VERR_AUDIO_NO_FREE_OUTPUT_STREAMS;
+        }
+        pcFreeStreams = &pThis->Out.cStreamsFree;
+    }
+    size_t const cbHstStrm = pThis->BackendCfg.cbStream;
+    AssertStmt(cbHstStrm < _16M, rc = VERR_OUT_OF_RANGE);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Allocate and initialize common state.
+         */
+        PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)RTMemAllocZ(sizeof(DRVAUDIOSTREAM) + RT_ALIGN_Z(cbHstStrm, 64));
+        if (pStreamEx)
+        {
+            /* Retrieve host driver name for easier identification. */
+            AssertPtr(pThis->pHostDrvAudio);
+            RTStrPrintf(pStreamEx->Core.szName, RT_ELEMENTS(pStreamEx->Core.szName), "[%s] %s",
+                        pThis->BackendCfg.szName, pCfgHost->szName[0] != '\0' ? pCfgHost->szName : "<Untitled>");
+
+            pStreamEx->Core.enmDir    = pCfgHost->enmDir;
+            pStreamEx->Core.cbBackend = (uint32_t)cbHstStrm;
+            if (cbHstStrm)
+                pStreamEx->pBackend   = (PPDMAUDIOBACKENDSTREAM)(pStreamEx + 1);
+            pStreamEx->fNoMixBufs     = RT_BOOL(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF);
+            pStreamEx->uMagic         = DRVAUDIOSTREAM_MAGIC;
+
+            /*
+             * Try to init the rest.
+             */
+            rc = drvAudioStreamInitInternal(pThis, pStreamEx, fFlags, pCfgHost, pCfgGuest);
+            if (RT_SUCCESS(rc))
+            {
+                /* Set initial reference counts. */
+                pStreamEx->Core.cRefs = 1;
+
+                /* Decrement the free stream counter. */
+                Assert(*pcFreeStreams > 0);
+                *pcFreeStreams -= 1;
+
+                /*
+                 * We're good.
+                 */
+                RTListAppend(&pThis->lstStreams, &pStreamEx->ListEntry);
+                STAM_COUNTER_INC(&pThis->Stats.TotalStreamsCreated);
+                *ppStream = &pStreamEx->Core;
+
+                /*
+                 * Init debug stuff if enabled (ignore failures).
+                 */
+                if (pCfgHost->enmDir == PDMAUDIODIR_IN)
+                {
+                    if (pThis->In.Cfg.Dbg.fEnabled)
+                    {
+                        AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileCaptureNonInterleaved, pThis->In.Cfg.Dbg.szPathOut,
+                                                  "DrvAudioCapNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
+                        AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileStreamRead, pThis->In.Cfg.Dbg.szPathOut,
+                                                  "DrvAudioRead", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
+                    }
+                }
+                else /* Out */
+                {
+                    if (pThis->Out.Cfg.Dbg.fEnabled)
+                    {
+                        AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pThis->Out.Cfg.Dbg.szPathOut,
+                                                  "DrvAudioPlayNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
+                        AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFileStreamWrite, pThis->Out.Cfg.Dbg.szPathOut,
+                                                  "DrvAudioWrite", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
+                    }
+                }
+            }
+            else
+            {
+                LogFunc(("drvAudioStreamInitInternal failed: %Rrc\n", rc));
+                int rc2 = drvAudioStreamUninitInternal(pThis, pStreamEx);
+                AssertRC(rc2);
+                drvAudioStreamFree(pStreamEx);
+            }
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+
+    RTCritSectLeave(&pThis->CritSect);
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+
+/**
+ * Calls the backend to give it the chance to destroy its part of the audio stream.
+ *
+ * Called from drvAudioPowerOff, drvAudioStreamUninitInternal and
+ * drvAudioStreamReInitInternal.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Pointer to driver instance.
+ * @param   pStreamEx   Audio stream destruct backend for.
+ */
+static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
+{
+    AssertPtr(pThis);
+    AssertPtr(pStreamEx);
+
+    int rc = VINF_SUCCESS;
+
+#ifdef LOG_ENABLED
+    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
+#endif
+    LogFunc(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
+
+    if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED)
+    {
+        AssertPtr(pStreamEx->pBackend);
+
+        /* Check if the pointer to  the host audio driver is still valid.
+         * It can be NULL if we were called in drvAudioDestruct, for example. */
+        if (pThis->pHostDrvAudio)
+            rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pStreamEx->pBackend);
+
+        pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
+    }
+
+    LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
+    return rc;
+}
+
+
+/**
+ * Uninitializes an audio stream - worker for drvAudioStreamDestroy,
+ * drvAudioDestruct and drvAudioStreamCreate.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Pointer to driver instance.
+ * @param   pStreamEx   Pointer to audio stream to uninitialize.
+ *
+ * @note    Caller owns the critical section.
+ */
+static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertMsgReturn(pStreamEx->Core.cRefs <= 1,
+                    ("Stream '%s' still has %RU32 references held when uninitializing\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs),
+                    VERR_WRONG_ORDER);
+    LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
+
+    /*
+     * ...
+     */
+    int rc = drvAudioStreamControlInternal(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
+    if (RT_SUCCESS(rc))
+        rc = drvAudioStreamDestroyInternalBackend(pThis, pStreamEx);
+
+    /* Destroy mixing buffers. */
+    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
+    AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
+
+    /* Free pre-buffer space. */
+    if (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
+        && pStreamEx->Out.pbPreBuf)
+    {
+        RTMemFree(pStreamEx->Out.pbPreBuf);
+        pStreamEx->Out.pbPreBuf      = NULL;
+        pStreamEx->Out.cbPreBufAlloc = 0;
+        pStreamEx->Out.cbPreBuffered = 0;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+#ifdef LOG_ENABLED
+        if (pStreamEx->Core.fStatus != PDMAUDIOSTREAMSTS_FLAGS_NONE)
+        {
+            char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
+            LogFunc(("[%s] Warning: Still has %s set when uninitializing\n",
+                     pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
+        }
+#endif
+        pStreamEx->Core.fStatus = PDMAUDIOSTREAMSTS_FLAGS_NONE;
+    }
+
+    PPDMDRVINS const pDrvIns = pThis->pDrvIns;
+    PDMDrvHlpSTAMDeregisterByPrefix(pDrvIns, pStreamEx->Core.szName);
+
+    if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
+    {
+        if (pThis->In.Cfg.Dbg.fEnabled)
+        {
+            AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileCaptureNonInterleaved);
+            pStreamEx->In.Dbg.pFileCaptureNonInterleaved = NULL;
+
+            AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileStreamRead);
+            pStreamEx->In.Dbg.pFileStreamRead = NULL;
+        }
+    }
+    else
+    {
+        Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT);
+        if (pThis->Out.Cfg.Dbg.fEnabled)
+        {
+            AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFilePlayNonInterleaved);
+            pStreamEx->Out.Dbg.pFilePlayNonInterleaved = NULL;
+
+            AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFileStreamWrite);
+            pStreamEx->Out.Dbg.pFileStreamWrite = NULL;
+        }
+    }
+    LogFlowFunc(("Returning %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
+ */
+static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+
+    if (!pStream)
+        return VINF_SUCCESS;
+    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;   /* Note! Do not touch pStream after this! */
+    Assert(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
+    Assert(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
+
+    LogRel2(("Audio: Destroying stream '%s'\n", pStreamEx->Core.szName));
+
+    LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
+    AssertMsg(pStreamEx->Core.cRefs <= 1, ("%u %s\n", pStreamEx->Core.cRefs, pStreamEx->Core.szName));
+    if (pStreamEx->Core.cRefs <= 1)
+    {
+        rc = drvAudioStreamUninitInternal(pThis, pStreamEx);
+        if (RT_SUCCESS(rc))
+        {
+            if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
+                pThis->In.cStreamsFree++;
+            else /* Out */
+                pThis->Out.cStreamsFree++;
+
+            RTListNodeRemove(&pStreamEx->ListEntry);
+
+            drvAudioStreamFree(pStreamEx);
+            pStreamEx = NULL;
+            pStream = NULL;
+        }
+        else
+            LogRel(("Audio: Uninitializing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
+    }
+    else
+        rc = VERR_WRONG_ORDER;
+
+    RTCritSectLeave(&pThis->CritSect);
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRetain}
+ */
+static DECLCALLBACK(uint32_t) drvAudioStreamRetain(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
+{
+   AssertPtrReturn(pInterface, UINT32_MAX);
+   AssertPtrReturn(pStream,    UINT32_MAX);
+   AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
+   AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
+   RT_NOREF(pInterface);
+
+   uint32_t const cRefs = ASMAtomicIncU32(&pStream->cRefs);
+   Assert(cRefs > 1);
+   Assert(cRefs < _1K);
+
+   return cRefs;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRelease}
+ */
+static DECLCALLBACK(uint32_t) drvAudioStreamRelease(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
+{
+   AssertPtrReturn(pInterface, UINT32_MAX);
+   AssertPtrReturn(pStream,    UINT32_MAX);
+   AssertReturn(pStream->uMagic == PDMAUDIOSTREAM_MAGIC, UINT32_MAX);
+   AssertReturn(((PDRVAUDIOSTREAM)pStream)->uMagic == DRVAUDIOSTREAM_MAGIC, UINT32_MAX);
+   RT_NOREF(pInterface);
+
+   uint32_t cRefs = ASMAtomicDecU32(&pStream->cRefs);
+   AssertStmt(cRefs >= 1, cRefs = ASMAtomicIncU32(&pStream->cRefs));
+   Assert(cRefs < _1K);
+
+   return cRefs;
+}
+
+
+/**
+ * Controls a stream's backend.
+ *
+ * If the stream has no backend available, VERR_NOT_FOUND is returned
+ * (bird: actually the code returns VINF_SUCCESS).
+ *
+ * @returns VBox status code.
+ * @param   pThis           Pointer to driver instance.
+ * @param   pStreamEx       Stream to control.
+ * @param   enmStreamCmd    Control command.
+ */
+static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    AssertPtr(pThis);
+    AssertPtr(pStreamEx);
+
+#ifdef LOG_ENABLED
+    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
+#endif
+    LogFlowFunc(("[%s] enmStreamCmd=%s, fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
+                 dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
+
+    if (!pThis->pHostDrvAudio) /* If not lower driver is configured, bail out. */
+        return VINF_SUCCESS;
+
+
+    /*
+     * Whether to propagate commands down to the backend.
+     *
+     * This is needed for critical operations like recording audio if audio input is disabled on a per-driver level.
+     *
+     * Note that not all commands will be covered by this, such as operations like stopping, draining and droppping,
+     * which are considered uncritical and sometimes even are required for certain backends (like DirectSound on Windows).
+     *
+     * The actual stream state will be untouched to not make the state machine handling more complicated than
+     * it already is.
+     *
+     * See @bugref{9882}.
+     */
+    const bool fEnabled =    (   pStreamEx->Core.enmDir == PDMAUDIODIR_IN
+                              && pThis->In.fEnabled)
+                          || (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
+                              && pThis->Out.fEnabled);
+
+    LogRel2(("Audio: %s stream '%s' in backend (%s is %s)\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName,
+                                                              PDMAudioDirGetName(pStreamEx->Core.enmDir),
+                                                              fEnabled ? "enabled" : "disabled"));
+    int rc = VINF_SUCCESS;
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            if (fEnabled)
+                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_ENABLE);
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DISABLE);
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            if (fEnabled) /* Needed, as resume below also is being checked for. */
+                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_PAUSE);
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            if (fEnabled)
+                rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_RESUME);
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DRAIN:
+        {
+            rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pStreamEx->pBackend, PDMAUDIOSTREAMCMD_DRAIN);
+            break;
+        }
+
+        default:
+            AssertMsgFailedReturn(("Command %RU32 not implemented\n", enmStreamCmd), VERR_INTERNAL_ERROR_2);
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        if (   rc != VERR_NOT_IMPLEMENTED
+            && rc != VERR_NOT_SUPPORTED
+            && rc != VERR_AUDIO_STREAM_NOT_READY)
+        {
+            LogRel(("Audio: %s stream '%s' failed with %Rrc\n", PDMAudioStrmCmdGetName(enmStreamCmd), pStreamEx->Core.szName, rc));
+        }
+
+        LogFunc(("[%s] %s failed with %Rrc\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd), rc));
+    }
+
+    return rc;
+}
+
+/**
+ * Controls an audio stream.
+ *
+ * @returns VBox status code.
+ * @param   pThis           Pointer to driver instance.
+ * @param   pStreamEx       Stream to control.
+ * @param   enmStreamCmd    Control command.
+ */
+static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    AssertPtr(pThis);
+    AssertPtr(pStreamEx);
+
+#ifdef LOG_ENABLED
+    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
+#endif
+    LogFunc(("[%s] enmStreamCmd=%s fStatus=%s\n", pStreamEx->Core.szName, PDMAudioStrmCmdGetName(enmStreamCmd),
+             dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
+
+    int rc = VINF_SUCCESS;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED))
+            {
+                /* Is a pending disable outstanding? Then disable first. */
+                if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
+                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
+
+                if (RT_SUCCESS(rc))
+                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_ENABLE);
+
+                if (RT_SUCCESS(rc))
+                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
+            }
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_ENABLED)
+            {
+                /*
+                 * For playback (output) streams first mark the host stream as pending disable,
+                 * so that the rest of the remaining audio data will be played first before
+                 * closing the stream.
+                 */
+                if (pStreamEx->Core.enmDir == PDMAUDIODIR_OUT)
+                {
+                    LogFunc(("[%s] Pending disable/pause\n", pStreamEx->Core.szName));
+                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE;
+
+                    /* Schedule a follow up timer to the pending-disable state.  We cannot rely
+                       on the device to provide further callouts to finish the state transition.
+                       10ms is taking out of thin air and may be too course grained, we should
+                       really consider the amount of unplayed buffer in the backend and what not... */
+                    if (!pThis->fTimerArmed)
+                    {
+                        LogFlowFunc(("Arming emergency pending-disable hack...\n"));
+                        int rc2 = PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hTimer, 10 /*ms*/);
+                        AssertRC(rc2);
+                        pThis->fTimerArmed = true;
+                    }
+                }
+
+                /* Can we close the host stream as well (not in pending disable mode)? */
+                if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE))
+                {
+                    rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
+                    if (RT_SUCCESS(rc))
+                        drvAudioStreamResetInternal(pThis, pStreamEx);
+                }
+            }
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            if (!(pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED))
+            {
+                rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_PAUSE);
+                if (RT_SUCCESS(rc))
+                    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
+            }
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PAUSED)
+            {
+                rc = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_RESUME);
+                if (RT_SUCCESS(rc))
+                    pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_PAUSED;
+            }
+            break;
+        }
+
+        default:
+            rc = VERR_NOT_IMPLEMENTED;
+            break;
+    }
+
+    if (RT_FAILURE(rc))
+        LogFunc(("[%s] Failed with %Rrc\n", pStreamEx->Core.szName, rc));
+
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamControl}
+ */
+static DECLCALLBACK(int) drvAudioStreamControl(PPDMIAUDIOCONNECTOR pInterface,
+                                               PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+
+    /** @todo r=bird: why?  It's not documented to ignore NULL streams.   */
+    if (!pStream)
+        return VINF_SUCCESS;
+    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
+    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
+    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
+
+    LogFlowFunc(("[%s] enmStreamCmd=%s\n", pStream->szName, PDMAudioStrmCmdGetName(enmStreamCmd)));
+
+    rc = drvAudioStreamControlInternal(pThis, pStreamEx, enmStreamCmd);
+
+    RTCritSectLeave(&pThis->CritSect);
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
+ */
+static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
+                                            void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
+    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+    AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER);
+    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+    AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
+              ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
+               pStreamEx->Core.szName, pStreamEx->Core.enmDir));
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * ...
+     */
+    uint32_t cbReadTotal = 0;
+
+    do
+    {
+        uint32_t cfReadTotal = 0;
+
+        const uint32_t cfBuf = AUDIOMIXBUF_B2F(&pStreamEx->Guest.MixBuf, cbBuf);
+
+        if (pThis->In.fEnabled) /* Input for this audio driver enabled? See #9822. */
+        {
+            if (!PDMAudioStrmStatusCanRead(pStream->fStatus))
+            {
+                rc = VERR_AUDIO_STREAM_NOT_READY;
+                break;
+            }
+
+            /*
+             * Read from the parent buffer (that is, the guest buffer) which
+             * should have the audio data in the format the guest needs.
+             */
+            uint32_t cfToRead = RT_MIN(cfBuf, AudioMixBufLive(&pStreamEx->Guest.MixBuf));
+            while (cfToRead)
+            {
+                uint32_t cfRead;
+                rc = AudioMixBufAcquireReadBlock(&pStreamEx->Guest.MixBuf,
+                                                 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
+                                                 AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfToRead), &cfRead);
+                if (RT_FAILURE(rc))
+                    break;
+
+#ifdef VBOX_WITH_STATISTICS
+                const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfRead);
+                STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead,    cbRead);
+                STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesRead, cfRead);
+                STAM_COUNTER_INC(&pStreamEx->In.Stats.TotalTimesRead);
+#endif
+                Assert(cfToRead >= cfRead);
+                cfToRead -= cfRead;
+
+                cfReadTotal += cfRead;
+
+                AudioMixBufReleaseReadBlock(&pStreamEx->Guest.MixBuf, cfRead);
+            }
+
+            if (cfReadTotal)
+            {
+                if (pThis->In.Cfg.Dbg.fEnabled)
+                    AudioHlpFileWrite(pStreamEx->In.Dbg.pFileStreamRead,
+                                      pvBuf, AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 0 /* fFlags */);
+
+                AudioMixBufFinish(&pStreamEx->Guest.MixBuf, cfReadTotal);
+            }
+        }
+
+        /* If we were not able to read as much data as requested, fill up the returned
+         * data with silence.
+         *
+         * This is needed to keep the device emulation DMA transfers up and running at a constant rate. */
+        if (cfReadTotal < cfBuf)
+        {
+            Log3Func(("[%s] Filling in silence (%RU64ms / %RU64ms)\n", pStream->szName,
+                      PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf - cfReadTotal),
+                      PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf)));
+
+            PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props,
+                                     (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
+                                     AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfBuf - cfReadTotal),
+                                     cfBuf - cfReadTotal);
+
+            cfReadTotal = cfBuf;
+        }
+
+        cbReadTotal = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal);
+
+        pStreamEx->nsLastReadWritten = RTTimeNanoTS();
+
+        Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc));
+
+    } while (0);
+
+    RTCritSectLeave(&pThis->CritSect);
+
+    if (RT_SUCCESS(rc) && pcbRead)
+        *pcbRead = cbReadTotal;
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite}
+ */
+static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
+                                             const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
+    AssertPtr(pThis);
+
+    /*
+     * Check input and sanity.
+     */
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
+    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+    uint32_t uTmp;
+    if (!pcbWritten)
+        pcbWritten = &uTmp;
+    AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
+
+    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+    AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT,
+                    ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n",
+                     pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED);
+    Assert(pStreamEx->fNoMixBufs);
+
+    AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf),
+              ("Stream '%s' got a non-frame-aligned write (%RU32 bytes)\n", pStreamEx->Core.szName, cbBuf));
+
+/// @todo    STAM_PROFILE_ADV_START(&pThis->Stats.DelayOut, out); /* (stopped in drvAudioStreamPlayLocked) */
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * First check that we can write to the stream, and if not,
+     * whether to just drop the input into the bit bucket.
+     */
+    if (PDMAudioStrmStatusIsReady(pStreamEx->Core.fStatus))
+    {
+        if (   !pThis->Out.fEnabled         /* (see @bugref{9882}) */
+            || pThis->pHostDrvAudio == NULL /* (we used to work this condition differently) */
+            || !PDMAudioStrmStatusCanWrite(pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend)))
+        {
+            Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.szName,
+                      !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet"));
+            *pcbWritten = cbBuf;
+            pStreamEx->offInternal += cbBuf;
+        }
+        /*
+         * No-mixing buffer mode:  Write the data directly to the backend, unless
+         * we're prebuffering.  There will be no pfnStreamPlay call in this mode.
+         */
+        else
+        {
+            uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore);
+            rc = drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten);
+            Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal);
+            if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc))
+            { /* likely */ }
+            else
+                AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */);
+        }
+    }
+    else
+        rc = VERR_AUDIO_STREAM_NOT_READY;
+
+    RTCritSectLeave(&pThis->CritSect);
+    return rc;
+}
+
 
 /**
@@ -1325,233 +2613,9 @@
 }
 
-/**
- * @callback_method_impl{FNTMTIMERDRV}
- */
-static DECLCALLBACK(void) drvAudioEmergencyIterateTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
-{
-    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
-    RT_NOREF(hTimer, pvUser);
-    RTCritSectEnter(&pThis->CritSect);
-
-    /*
-     * Iterate any stream with the pending-disable flag set.
-     */
-    uint32_t        cMilliesToNext = 0;
-    PDRVAUDIOSTREAM pStreamEx, pStreamExNext;
-    RTListForEachSafe(&pThis->lstStreams, pStreamEx, pStreamExNext, DRVAUDIOSTREAM, ListEntry)
-    {
-        if (   pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC
-            && pStreamEx->Core.cRefs >= 1)
-        {
-            if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
-            {
-                drvAudioStreamIterateInternal(pThis, pStreamEx);
-
-                if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)
-                    cMilliesToNext = 10;
-            }
-        }
-    }
-
-    /*
-     * Re-arm the timer if we still got streams in the pending state.
-     */
-    if (cMilliesToNext)
-    {
-        pThis->fTimerArmed = true;
-        PDMDrvHlpTimerSetMillies(pDrvIns, pThis->hTimer, cMilliesToNext);
-    }
-    else
-        pThis->fTimerArmed = false;
-
-    RTCritSectLeave(&pThis->CritSect);
-}
-
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
- */
-static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed)
-{
-    RT_NOREF(pInterface, pStream, pcFramesPlayed);
-    AssertFailed(/* OBSOLETE! */);
-    return VERR_NOT_SUPPORTED;
-}
-
-
-/**
- * Captures non-interleaved input from a host stream.
- *
- * @returns VBox status code.
- * @param   pThis       Driver instance.
- * @param   pStreamEx   Stream to capture from.
- * @param   pcfCaptured Number of (host) audio frames captured.
- */
-static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
-{
-    Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
-    Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
-
-    /*
-     * ...
-     */
-    AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
-    uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
-    if (!cbReadable)
-        Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
-
-    uint32_t cbFree = AudioMixBufFreeBytes(&pStreamEx->Guest.MixBuf); /* Parent */
-    if (!cbFree)
-        Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
-
-    if (cbReadable > cbFree) /* More data readable than we can store at the moment? Limit. */
-        cbReadable = cbFree;
-
-    /*
-     * ...
-     */
-    int      rc = VINF_SUCCESS;
-    uint32_t cfCapturedTotal = 0;
-    while (cbReadable)
-    {
-        uint8_t  abChunk[_4K];
-        uint32_t cbCaptured;
-        rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
-                                                    abChunk, RT_MIN(cbReadable, (uint32_t)sizeof(abChunk)), &cbCaptured);
-        if (RT_FAILURE(rc))
-        {
-            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
-            AssertRC(rc2);
-            break;
-        }
-
-        Assert(cbCaptured <= sizeof(abChunk));
-        if (cbCaptured > sizeof(abChunk)) /* Paranoia. */
-            cbCaptured = (uint32_t)sizeof(abChunk);
-
-        if (!cbCaptured) /* Nothing captured? Take a shortcut. */
-            break;
-
-        /* We use the host side mixing buffer as an intermediate buffer to do some
-         * (first) processing (if needed), so always write the incoming data at offset 0. */
-        uint32_t cfHstWritten = 0;
-        rc = AudioMixBufWriteAt(&pStreamEx->Host.MixBuf, 0 /* offFrames */, abChunk, cbCaptured, &cfHstWritten);
-        if (   RT_FAILURE(rc)
-            || !cfHstWritten)
-        {
-            AssertMsgFailed(("[%s] Write failed: cbCaptured=%RU32, cfHstWritten=%RU32, rc=%Rrc\n",
-                             pStreamEx->Core.szName, cbCaptured, cfHstWritten, rc));
-            break;
-        }
-
-        if (pThis->In.Cfg.Dbg.fEnabled)
-            AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCaptureNonInterleaved, abChunk, cbCaptured, 0 /* fFlags */);
-
-        uint32_t cfHstMixed = 0;
-        if (cfHstWritten)
-        {
-            int rc2 = AudioMixBufMixToParentEx(&pStreamEx->Host.MixBuf, 0 /* cSrcOffset */, cfHstWritten /* cSrcFrames */,
-                                               &cfHstMixed /* pcSrcMixed */);
-            Log3Func(("[%s] cbCaptured=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
-                      pStreamEx->Core.szName, cbCaptured, cfHstWritten, cfHstMixed, rc2));
-            AssertRC(rc2);
-        }
-
-        Assert(cbReadable >= cbCaptured);
-        cbReadable      -= cbCaptured;
-        cfCapturedTotal += cfHstMixed;
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        if (cfCapturedTotal)
-            Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
-    }
-    else
-        LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pStreamEx->Core.szName, rc));
-
-    if (pcfCaptured)
-        *pcfCaptured = cfCapturedTotal;
-
-    return rc;
-}
-
-/**
- * Captures raw input from a host stream.
- * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
- * no data layout processing done in between.
- *
- * Needed for e.g. the VRDP audio backend (in Main).
- *
- * @returns VBox status code.
- * @param   pThis       Driver instance.
- * @param   pStreamEx   Stream to capture from.
- * @param   pcfCaptured Number of (host) audio frames captured.
- */
-static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
-{
-    Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
-    Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
-    AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
-
-    /*
-     * ...
-     */
-    /* Note: Raw means *audio frames*, not bytes! */
-    uint32_t cfReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
-    if (!cfReadable)
-        Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
-
-    const uint32_t cfFree = AudioMixBufFree(&pStreamEx->Guest.MixBuf); /* Parent */
-    if (!cfFree)
-        Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
-
-    if (cfReadable > cfFree) /* More data readable than we can store at the moment? Limit. */
-        cfReadable = cfFree;
-
-    /*
-     * ...
-     */
-    int      rc              = VINF_SUCCESS;
-    uint32_t cfCapturedTotal = 0;
-    while (cfReadable)
-    {
-        PPDMAUDIOFRAME paFrames;
-        uint32_t cfWritable;
-        rc = AudioMixBufPeekMutable(&pStreamEx->Host.MixBuf, cfReadable, &paFrames, &cfWritable);
-        if (   RT_FAILURE(rc)
-            || !cfWritable)
-            break;
-
-        uint32_t cfCaptured;
-        rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
-                                                    paFrames, cfWritable, &cfCaptured);
-        if (RT_FAILURE(rc))
-        {
-            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
-            AssertRC(rc2);
-            break;
-        }
-
-        Assert(cfCaptured <= cfWritable);
-        if (cfCaptured > cfWritable) /* Paranoia. */
-            cfCaptured = cfWritable;
-
-        Assert(cfReadable >= cfCaptured);
-        cfReadable      -= cfCaptured;
-        cfCapturedTotal += cfCaptured;
-    }
-
-    if (pcfCaptured)
-        *pcfCaptured = cfCapturedTotal;
-    Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
- */
-static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
-                                               PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamIterate}
+ */
+static DECLCALLBACK(int) drvAudioStreamIterate(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
 {
     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
@@ -1559,62 +2623,13 @@
     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
-    AssertPtrNull(pcFramesCaptured);
     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-    AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
-              ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
-               pStreamEx->Core.szName, pStreamEx->Core.enmDir));
+
     int rc = RTCritSectEnter(&pThis->CritSect);
     AssertRCReturn(rc, rc);
 
-#ifdef LOG_ENABLED
-    char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
-#endif
-    Log3Func(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
-
-    /*
-     * ...
-     */
-    uint32_t cfCaptured = 0;
-    do
-    {
-        if (!pThis->pHostDrvAudio)
-        {
-            rc = VERR_PDM_NO_ATTACHED_DRIVER;
-            break;
-        }
-
-        if (   !pThis->In.fEnabled
-            || !PDMAudioStrmStatusCanRead(pStreamEx->Core.fStatus))
-        {
-            rc = VERR_AUDIO_STREAM_NOT_READY;
-            break;
-        }
-
-        /*
-         * Do the actual capturing.
-         */
-        if (RT_LIKELY(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
-            rc = drvAudioStreamCaptureNonInterleaved(pThis, pStreamEx, &cfCaptured);
-        else if (pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
-            rc = drvAudioStreamCaptureRaw(pThis, pStreamEx, &cfCaptured);
-        else
-            AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
-
-        if (RT_SUCCESS(rc))
-        {
-            Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCaptured, rc));
-
-            STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn,              cfCaptured);
-            STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesCaptured, cfCaptured);
-        }
-        else if (RT_UNLIKELY(RT_FAILURE(rc)))
-            LogRel(("Audio: Capturing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
-    } while (0);
+    rc = drvAudioStreamIterateInternal(pThis, pStreamEx);
 
     RTCritSectLeave(&pThis->CritSect);
-
-    if (pcFramesCaptured)
-        *pcFramesCaptured = cfCaptured;
 
     if (RT_FAILURE(rc))
@@ -1623,1106 +2638,4 @@
 }
 
-#ifdef VBOX_WITH_AUDIO_ENUM
-/**
- * Enumerates all host audio devices.
- *
- * This functionality might not be implemented by all backends and will return
- * VERR_NOT_SUPPORTED if not being supported.
- *
- * @note Must not hold the driver's critical section!
- *
- * @returns VBox status code.
- * @param   pThis               Driver instance to be called.
- * @param   fLog                Whether to print the enumerated device to the release log or not.
- * @param   pDevEnum            Where to store the device enumeration.
- *
- * @remarks This is currently ONLY used for release logging.
- */
-static int drvAudioDevicesEnumerateInternal(PDRVAUDIO pThis, bool fLog, PPDMAUDIOHOSTENUM pDevEnum)
-{
-    AssertReturn(!RTCritSectIsOwner(&pThis->CritSect), VERR_WRONG_ORDER);
-
-    int rc;
-
-    /*
-     * If the backend supports it, do a device enumeration.
-     */
-    if (pThis->pHostDrvAudio->pfnGetDevices)
-    {
-        PDMAUDIOHOSTENUM DevEnum;
-        rc = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum);
-        if (RT_SUCCESS(rc))
-        {
-            if (fLog)
-                LogRel(("Audio: Found %RU16 devices for driver '%s'\n", DevEnum.cDevices, pThis->szName));
-
-            PPDMAUDIOHOSTDEV pDev;
-            RTListForEach(&DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
-            {
-                if (fLog)
-                {
-                    char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
-                    LogRel(("Audio: Device '%s':\n", pDev->szName));
-                    LogRel(("Audio:   Usage           = %s\n",   PDMAudioDirGetName(pDev->enmUsage)));
-                    LogRel(("Audio:   Flags           = %s\n",   PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags)));
-                    LogRel(("Audio:   Input channels  = %RU8\n", pDev->cMaxInputChannels));
-                    LogRel(("Audio:   Output channels = %RU8\n", pDev->cMaxOutputChannels));
-                }
-            }
-
-            if (pDevEnum)
-                rc = PDMAudioHostEnumCopy(pDevEnum, &DevEnum, PDMAUDIODIR_INVALID /*all*/, true /*fOnlyCoreData*/);
-
-            PDMAudioHostEnumDelete(&DevEnum);
-        }
-        else
-        {
-            if (fLog)
-                LogRel(("Audio: Device enumeration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
-            /* Not fatal. */
-        }
-    }
-    else
-    {
-        rc = VERR_NOT_SUPPORTED;
-
-        if (fLog)
-            LogRel2(("Audio: Host driver '%s' does not support audio device enumeration, skipping\n", pThis->szName));
-    }
-
-    LogFunc(("Returning %Rrc\n", rc));
-    return rc;
-}
-#endif /* VBOX_WITH_AUDIO_ENUM */
-
-/**
- * Initializes the host backend and queries its initial configuration.
- *
- * @returns VBox status code.
- * @param   pThis               Driver instance to be called.
- */
-static int drvAudioHostInit(PDRVAUDIO pThis)
-{
-    LogFlowFuncEnter();
-
-    /*
-     * Check the function pointers, make sure the ones we define as
-     * mandatory are present.
-     */
-    PPDMIHOSTAUDIO pHostDrvAudio = pThis->pHostDrvAudio;
-    AssertPtrReturn(pHostDrvAudio, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnGetConfig, VERR_INVALID_POINTER);
-    AssertPtrNullReturn(pHostDrvAudio->pfnGetDevices, VERR_INVALID_POINTER);
-    AssertPtrNullReturn(pHostDrvAudio->pfnGetStatus, VERR_INVALID_POINTER);
-    AssertPtrNullReturn(pHostDrvAudio->pfnStreamConfigHint, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamCreate, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamDestroy, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamControl, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamGetReadable, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamGetWritable, VERR_INVALID_POINTER);
-    AssertPtrNullReturn(pHostDrvAudio->pfnStreamGetPending, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamGetStatus, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamPlay, VERR_INVALID_POINTER);
-    AssertPtrReturn(pHostDrvAudio->pfnStreamCapture, VERR_INVALID_POINTER);
-
-    /*
-     * Get the backend configuration.
-     */
-    int rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);
-    if (RT_FAILURE(rc))
-    {
-        LogRel(("Audio: Getting configuration for driver '%s' failed with %Rrc\n", pThis->szName, rc));
-        return VERR_AUDIO_BACKEND_INIT_FAILED;
-    }
-
-    pThis->In.cStreamsFree  = pThis->BackendCfg.cMaxStreamsIn;
-    pThis->Out.cStreamsFree = pThis->BackendCfg.cMaxStreamsOut;
-
-    LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
-
-    LogRel2(("Audio: Host driver '%s' supports %RU32 input streams and %RU32 output streams at once.\n",
-             pThis->szName, pThis->In.cStreamsFree, pThis->Out.cStreamsFree));
-
-#ifdef VBOX_WITH_AUDIO_ENUM
-    int rc2 = drvAudioDevicesEnumerateInternal(pThis, true /* fLog */, NULL /* pDevEnum */);
-    if (rc2 != VERR_NOT_SUPPORTED) /* Some backends don't implement device enumeration. */
-        AssertRC(rc2);
-
-    RT_NOREF(rc2);
-    /* Ignore rc. */
-#endif
-
-    LogFlowFuncLeave();
-    return VINF_SUCCESS;
-}
-
-/**
- * Handles state changes for all audio streams.
- *
- * @param   pDrvIns             Pointer to driver instance.
- * @param   enmCmd              Stream command to set for all streams.
- */
-static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
-{
-    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
-    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
-    LogFlowFunc(("enmCmd=%s\n", PDMAudioStrmCmdGetName(enmCmd)));
-
-    int rc2 = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturnVoid(rc2);
-
-    if (pThis->pHostDrvAudio)
-    {
-        PDRVAUDIOSTREAM pStreamEx;
-        RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
-        {
-            drvAudioStreamControlInternal(pThis, pStreamEx, enmCmd);
-        }
-    }
-
-    rc2 = RTCritSectLeave(&pThis->CritSect);
-    AssertRC(rc2);
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
- */
-static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
-                                            void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
-    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
-    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
-    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
-    AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER);
-    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
-    AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
-              ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
-               pStreamEx->Core.szName, pStreamEx->Core.enmDir));
-
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    /*
-     * ...
-     */
-    uint32_t cbReadTotal = 0;
-
-    do
-    {
-        uint32_t cfReadTotal = 0;
-
-        const uint32_t cfBuf = AUDIOMIXBUF_B2F(&pStreamEx->Guest.MixBuf, cbBuf);
-
-        if (pThis->In.fEnabled) /* Input for this audio driver enabled? See #9822. */
-        {
-            if (!PDMAudioStrmStatusCanRead(pStream->fStatus))
-            {
-                rc = VERR_AUDIO_STREAM_NOT_READY;
-                break;
-            }
-
-            /*
-             * Read from the parent buffer (that is, the guest buffer) which
-             * should have the audio data in the format the guest needs.
-             */
-            uint32_t cfToRead = RT_MIN(cfBuf, AudioMixBufLive(&pStreamEx->Guest.MixBuf));
-            while (cfToRead)
-            {
-                uint32_t cfRead;
-                rc = AudioMixBufAcquireReadBlock(&pStreamEx->Guest.MixBuf,
-                                                 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
-                                                 AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfToRead), &cfRead);
-                if (RT_FAILURE(rc))
-                    break;
-
-#ifdef VBOX_WITH_STATISTICS
-                const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfRead);
-                STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead,    cbRead);
-                STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesRead, cfRead);
-                STAM_COUNTER_INC(&pStreamEx->In.Stats.TotalTimesRead);
-#endif
-                Assert(cfToRead >= cfRead);
-                cfToRead -= cfRead;
-
-                cfReadTotal += cfRead;
-
-                AudioMixBufReleaseReadBlock(&pStreamEx->Guest.MixBuf, cfRead);
-            }
-
-            if (cfReadTotal)
-            {
-                if (pThis->In.Cfg.Dbg.fEnabled)
-                    AudioHlpFileWrite(pStreamEx->In.Dbg.pFileStreamRead,
-                                      pvBuf, AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 0 /* fFlags */);
-
-                AudioMixBufFinish(&pStreamEx->Guest.MixBuf, cfReadTotal);
-            }
-        }
-
-        /* If we were not able to read as much data as requested, fill up the returned
-         * data with silence.
-         *
-         * This is needed to keep the device emulation DMA transfers up and running at a constant rate. */
-        if (cfReadTotal < cfBuf)
-        {
-            Log3Func(("[%s] Filling in silence (%RU64ms / %RU64ms)\n", pStream->szName,
-                      PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf - cfReadTotal),
-                      PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf)));
-
-            PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props,
-                                     (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
-                                     AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfBuf - cfReadTotal),
-                                     cfBuf - cfReadTotal);
-
-            cfReadTotal = cfBuf;
-        }
-
-        cbReadTotal = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal);
-
-        pStreamEx->nsLastReadWritten = RTTimeNanoTS();
-
-        Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc));
-
-    } while (0);
-
-    RTCritSectLeave(&pThis->CritSect);
-
-    if (RT_SUCCESS(rc) && pcbRead)
-        *pcbRead = cbReadTotal;
-    return rc;
-}
-
-/**
- * Adjusts the request stream configuration, applying our settings.
- *
- * This also does some basic validations.
- *
- * Used by both the stream creation and stream configuration hinting code.
- *
- * @returns VBox status code.
- * @param   pThis       Pointer to the DrvAudio instance data.
- * @param   pCfgReq     The request configuration that should be adjusted.
- * @param   pszName     Stream name to use when logging warnings and errors.
- */
-static int drvAudioStreamAdjustConfig(PDRVAUDIO pThis, PPDMAUDIOSTREAMCFG pCfgReq, const char *pszName)
-{
-    /* Get the right configuration for the stream to be created. */
-    PDRVAUDIOCFG pDrvCfg = pCfgReq->enmDir == PDMAUDIODIR_IN ? &pThis->In.Cfg : &pThis->Out.Cfg;
-
-    /* Fill in the tweakable parameters into the requested host configuration.
-     * All parameters in principle can be changed and returned by the backend via the acquired configuration. */
-
-    /*
-     * PCM
-     */
-    if (PDMAudioPropsSampleSize(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
-    {
-        PDMAudioPropsSetSampleSize(&pCfgReq->Props, PDMAudioPropsSampleSize(&pDrvCfg->Props));
-        LogRel2(("Audio: Using custom sample size of %RU8 bytes for stream '%s'\n",
-                 PDMAudioPropsSampleSize(&pCfgReq->Props), pszName));
-    }
-
-    if (pDrvCfg->Props.uHz) /* Anything set via custom extra-data? */
-    {
-        pCfgReq->Props.uHz = pDrvCfg->Props.uHz;
-        LogRel2(("Audio: Using custom Hz rate %RU32 for stream '%s'\n", pCfgReq->Props.uHz, pszName));
-    }
-
-    if (pDrvCfg->uSigned != UINT8_MAX) /* Anything set via custom extra-data? */
-    {
-        pCfgReq->Props.fSigned = RT_BOOL(pDrvCfg->uSigned);
-        LogRel2(("Audio: Using custom %s sample format for stream '%s'\n",
-                 pCfgReq->Props.fSigned ? "signed" : "unsigned", pszName));
-    }
-
-    if (pDrvCfg->uSwapEndian != UINT8_MAX) /* Anything set via custom extra-data? */
-    {
-        pCfgReq->Props.fSwapEndian = RT_BOOL(pDrvCfg->uSwapEndian);
-        LogRel2(("Audio: Using custom %s endianess for samples of stream '%s'\n",
-                 pCfgReq->Props.fSwapEndian ? "swapped" : "original", pszName));
-    }
-
-    if (PDMAudioPropsChannels(&pDrvCfg->Props) != 0) /* Anything set via custom extra-data? */
-    {
-        PDMAudioPropsSetChannels(&pCfgReq->Props, PDMAudioPropsChannels(&pDrvCfg->Props));
-        LogRel2(("Audio: Using custom %RU8 channel(s) for stream '%s'\n", PDMAudioPropsChannels(&pDrvCfg->Props), pszName));
-    }
-
-    /* Validate PCM properties. */
-    if (!AudioHlpPcmPropsAreValid(&pCfgReq->Props))
-    {
-        LogRel(("Audio: Invalid custom PCM properties set for stream '%s', cannot create stream\n", pszName));
-        return VERR_INVALID_PARAMETER;
-    }
-
-    /*
-     * Period size
-     */
-    const char *pszWhat = "device-specific";
-    if (pDrvCfg->uPeriodSizeMs)
-    {
-        pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPeriodSizeMs);
-        pszWhat = "custom";
-    }
-
-    if (!pCfgReq->Backend.cFramesPeriod) /* Set default period size if nothing explicitly is set. */
-    {
-        pCfgReq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 150 /*ms*/);
-        pszWhat = "default";
-    }
-
-    LogRel2(("Audio: Using %s period size %RU64 ms / %RU32 frames for stream '%s'\n",
-             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod),
-             pCfgReq->Backend.cFramesPeriod, pszName));
-
-    /*
-     * Buffer size
-     */
-    pszWhat = "device-specific";
-    if (pDrvCfg->uBufferSizeMs)
-    {
-        pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uBufferSizeMs);
-        pszWhat = "custom";
-    }
-
-    if (!pCfgReq->Backend.cFramesBufferSize) /* Set default buffer size if nothing explicitly is set. */
-    {
-        pCfgReq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 300 /*ms*/);
-        pszWhat = "default";
-    }
-
-    LogRel2(("Audio: Using %s buffer size %RU64 ms / %RU32 frames for stream '%s'\n",
-             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
-             pCfgReq->Backend.cFramesBufferSize, pszName));
-
-    /*
-     * Pre-buffering size
-     */
-    pszWhat = "device-specific";
-    if (pDrvCfg->uPreBufSizeMs != UINT32_MAX) /* Anything set via global / per-VM extra-data? */
-    {
-        pCfgReq->Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(&pCfgReq->Props, pDrvCfg->uPreBufSizeMs);
-        pszWhat = "custom";
-    }
-    else /* No, then either use the default or device-specific settings (if any). */
-    {
-        if (pCfgReq->Backend.cFramesPreBuffering == UINT32_MAX) /* Set default pre-buffering size if nothing explicitly is set. */
-        {
-            /* Pre-buffer 66% of the buffer. */
-            pCfgReq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesBufferSize * 2 / 3;
-            pszWhat = "default";
-        }
-    }
-
-    LogRel2(("Audio: Using %s pre-buffering size %RU64 ms / %RU32 frames for stream '%s'\n",
-             pszWhat, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
-             pCfgReq->Backend.cFramesPreBuffering, pszName));
-
-    /*
-     * Validate input.
-     */
-    if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPeriod)
-    {
-        LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the period size (%RU64ms)\n",
-                pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize),
-                PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod)));
-        return VERR_INVALID_PARAMETER;
-    }
-
-    if (   pCfgReq->Backend.cFramesPreBuffering != UINT32_MAX /* Custom pre-buffering set? */
-        && pCfgReq->Backend.cFramesPreBuffering)
-    {
-        if (pCfgReq->Backend.cFramesBufferSize < pCfgReq->Backend.cFramesPreBuffering)
-        {
-            LogRel(("Audio: Error for stream '%s': Buffering size (%RU64ms) must not be smaller than the pre-buffering size (%RU64ms)\n",
-                    pszName, PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesPreBuffering),
-                    PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize)));
-            return VERR_INVALID_PARAMETER;
-        }
-    }
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Worker for drvAudioStreamInitInternal and drvAudioStreamReInitInternal that
- * creates the backend (host driver) side of an audio stream.
- *
- * @returns VBox status code.
- * @param   pThis       Pointer to driver instance.
- * @param   pStreamEx   Audio stream to create the backend side for.
- * @param   pCfgReq     Requested audio stream configuration to use for
- *                      stream creation.
- * @param   pCfgAcq     Acquired audio stream configuration returned by
- *                      the backend.
- *
- * @note    Configuration precedence for requested audio stream configuration (first has highest priority, if set):
- *          - per global extra-data
- *          - per-VM extra-data
- *          - requested configuration (by pCfgReq)
- *          - default value
- */
-static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,
-                                               PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
-{
-    AssertMsg((pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED) == 0,
-              ("Stream '%s' already initialized in backend\n", pStreamEx->Core.szName));
-
-    /*
-     * Adjust the requested stream config, applying our settings.
-     */
-    int rc = drvAudioStreamAdjustConfig(pThis, pCfgReq, pStreamEx->Core.szName);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /*
-     * Make the acquired host configuration the requested host configuration initially,
-     * in case the backend does not report back an acquired configuration.
-     */
-    /** @todo r=bird: This is conveniently not documented in the interface... */
-    rc = PDMAudioStrmCfgCopy(pCfgAcq, pCfgReq);
-    if (RT_FAILURE(rc))
-    {
-        LogRel(("Audio: Creating stream '%s' with an invalid backend configuration not possible, skipping\n",
-                pStreamEx->Core.szName));
-        return rc;
-    }
-
-    /*
-     * Call the host driver to create the stream.
-     */
-    AssertPtr(pThis->pHostDrvAudio);
-    if (pThis->pHostDrvAudio)
-        rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pStreamEx->pBackend, pCfgReq, pCfgAcq);
-    else
-        rc = VERR_PDM_NO_ATTACHED_DRIVER;
-    if (RT_FAILURE(rc))
-    {
-        if (rc == VERR_NOT_SUPPORTED)
-            LogRel2(("Audio: Creating stream '%s' in backend not supported\n", pStreamEx->Core.szName));
-        else if (rc == VERR_AUDIO_STREAM_COULD_NOT_CREATE)
-            LogRel2(("Audio: Stream '%s' could not be created in backend because of missing hardware / drivers\n", pStreamEx->Core.szName));
-        else
-            LogRel(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pStreamEx->Core.szName, rc));
-        return rc;
-    }
-
-    /* Validate acquired configuration. */
-    char szTmp[PDMAUDIOPROPSTOSTRING_MAX];
-    AssertLogRelMsgReturn(AudioHlpStreamCfgIsValid(pCfgAcq),
-                          ("Audio: Creating stream '%s' returned an invalid backend configuration (%s), skipping\n",
-                           pStreamEx->Core.szName, PDMAudioPropsToString(&pCfgAcq->Props, szTmp, sizeof(szTmp))),
-                          VERR_INVALID_PARAMETER);
-
-    /* Let the user know that the backend changed one of the values requested above. */
-    if (pCfgAcq->Backend.cFramesBufferSize != pCfgReq->Backend.cFramesBufferSize)
-        LogRel2(("Audio: Buffer size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
-                 pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesBufferSize), pCfgAcq->Backend.cFramesBufferSize));
-
-    if (pCfgAcq->Backend.cFramesPeriod != pCfgReq->Backend.cFramesPeriod)
-        LogRel2(("Audio: Period size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
-                 pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPeriod), pCfgAcq->Backend.cFramesPeriod));
-
-    /* Was pre-buffering requested, but the acquired configuration from the backend told us something else? */
-    if (pCfgReq->Backend.cFramesPreBuffering)
-    {
-        if (pCfgAcq->Backend.cFramesPreBuffering != pCfgReq->Backend.cFramesPreBuffering)
-            LogRel2(("Audio: Pre-buffering size overwritten by backend for stream '%s' (now %RU64ms, %RU32 frames)\n",
-                     pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
-
-        if (pCfgAcq->Backend.cFramesPreBuffering > pCfgAcq->Backend.cFramesBufferSize)
-        {
-            pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesBufferSize;
-            LogRel2(("Audio: Pre-buffering size bigger than buffer size for stream '%s', adjusting to %RU64ms (%RU32 frames)\n",
-                     pStreamEx->Core.szName, PDMAudioPropsFramesToMilli(&pCfgAcq->Props, pCfgAcq->Backend.cFramesPreBuffering), pCfgAcq->Backend.cFramesPreBuffering));
-        }
-    }
-    else if (pCfgReq->Backend.cFramesPreBuffering == 0) /* Was the pre-buffering requested as being disabeld? Tell the users. */
-    {
-        LogRel2(("Audio: Pre-buffering is disabled for stream '%s'\n", pStreamEx->Core.szName));
-        pCfgAcq->Backend.cFramesPreBuffering = 0;
-    }
-
-    /* Sanity for detecting buggy backends. */
-    AssertMsgReturn(pCfgAcq->Backend.cFramesPeriod < pCfgAcq->Backend.cFramesBufferSize,
-                    ("Acquired period size must be smaller than buffer size\n"),
-                    VERR_INVALID_PARAMETER);
-    AssertMsgReturn(pCfgAcq->Backend.cFramesPreBuffering <= pCfgAcq->Backend.cFramesBufferSize,
-                    ("Acquired pre-buffering size must be smaller or as big as the buffer size\n"),
-                    VERR_INVALID_PARAMETER);
-
-    pStreamEx->Core.fStatus |= PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Worker for drvAudioStreamCreate that initializes the audio stream.
- *
- * @returns VBox status code.
- * @param   pThis       Pointer to driver instance.
- * @param   pStreamEx   Stream to initialize.
- * @param   fFlags      PDMAUDIOSTREAM_CREATE_F_XXX.
- * @param   pCfgHost    Stream configuration to use for the host side (backend).
- * @param   pCfgGuest   Stream configuration to use for the guest side.
- */
-static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t fFlags,
-                                      PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest)
-{
-    /*
-     * Init host stream.
-     */
-    pStreamEx->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
-
-    /* Set the host's default audio data layout. */
-/** @todo r=bird: Why, oh why? OTOH, the layout stuff is non-sense anyway. */
-    pCfgHost->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
-
-#ifdef LOG_ENABLED
-    LogFunc(("[%s] Requested host format:\n", pStreamEx->Core.szName));
-    PDMAudioStrmCfgLog(pCfgHost);
-#endif
-
-    LogRel2(("Audio: Creating stream '%s'\n", pStreamEx->Core.szName));
-    LogRel2(("Audio: Guest %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
-             pCfgGuest->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
-             pCfgGuest->Props.uHz, PDMAudioPropsSampleBits(&pCfgGuest->Props), pCfgGuest->Props.fSigned ? "S" : "U",
-             PDMAudioPropsChannels(&pCfgGuest->Props), PDMAudioPropsChannels(&pCfgGuest->Props) == 1 ? "" : "s"));
-    LogRel2(("Audio: Requested host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
-             pCfgHost->enmDir == PDMAUDIODIR_IN ? "recording" : "playback", pStreamEx->Core.szName,
-             pCfgHost->Props.uHz, PDMAudioPropsSampleBits(&pCfgHost->Props), pCfgHost->Props.fSigned ? "S" : "U",
-             PDMAudioPropsChannels(&pCfgHost->Props), PDMAudioPropsChannels(&pCfgHost->Props) == 1 ? "" : "s"));
-
-    PDMAUDIOSTREAMCFG CfgHostAcq;
-    int rc = drvAudioStreamCreateInternalBackend(pThis, pStreamEx, pCfgHost, &CfgHostAcq);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    LogFunc(("[%s] Acquired host format:\n",  pStreamEx->Core.szName));
-    PDMAudioStrmCfgLog(&CfgHostAcq);
-    LogRel2(("Audio: Acquired host %s format for '%s': %RU32Hz, %u%s, %RU8 channel%s\n",
-             CfgHostAcq.enmDir == PDMAUDIODIR_IN ? "recording" : "playback",  pStreamEx->Core.szName,
-             CfgHostAcq.Props.uHz, PDMAudioPropsSampleBits(&CfgHostAcq.Props), CfgHostAcq.Props.fSigned ? "S" : "U",
-             PDMAudioPropsChannels(&CfgHostAcq.Props), PDMAudioPropsChannels(&CfgHostAcq.Props) == 1 ? "" : "s"));
-    Assert(PDMAudioPropsAreValid(&CfgHostAcq.Props));
-
-    /* Set the stream properties (currently guest side, when DevSB16 is
-       converted to mixer and PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF becomes
-       default, this will just be the stream properties). */
-    if (fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF)
-        pStreamEx->Core.Props = CfgHostAcq.Props;
-    else
-        pStreamEx->Core.Props = pCfgGuest->Props;
-
-    /* Let the user know if the backend changed some of the tweakable values. */
-    if (CfgHostAcq.Backend.cFramesBufferSize != pCfgHost->Backend.cFramesBufferSize)
-        LogRel2(("Audio: Backend changed buffer size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
-                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesBufferSize), pCfgHost->Backend.cFramesBufferSize,
-                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
-
-    if (CfgHostAcq.Backend.cFramesPeriod != pCfgHost->Backend.cFramesPeriod)
-        LogRel2(("Audio: Backend changed period size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
-                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPeriod), pCfgHost->Backend.cFramesPeriod,
-                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod), CfgHostAcq.Backend.cFramesPeriod));
-
-    if (CfgHostAcq.Backend.cFramesPreBuffering != pCfgHost->Backend.cFramesPreBuffering)
-        LogRel2(("Audio: Backend changed pre-buffering size from %RU64ms (%RU32 frames) to %RU64ms (%RU32 frames)\n",
-                 PDMAudioPropsFramesToMilli(&pCfgHost->Props, pCfgHost->Backend.cFramesPreBuffering), pCfgHost->Backend.cFramesPreBuffering,
-                 PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
-
-    /*
-     * Check if the backend did return sane values and correct if necessary.
-     * Should never happen with our own backends, but you never know ...
-     */
-    uint32_t const cFramesPreBufferingMax = CfgHostAcq.Backend.cFramesBufferSize - RT_MIN(16, CfgHostAcq.Backend.cFramesBufferSize);
-    if (CfgHostAcq.Backend.cFramesPreBuffering > cFramesPreBufferingMax)
-    {
-        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",
-                 CfgHostAcq.Backend.cFramesPreBuffering, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, cFramesPreBufferingMax));
-        AssertFailed();
-        CfgHostAcq.Backend.cFramesPreBuffering = cFramesPreBufferingMax;
-    }
-
-    if (CfgHostAcq.Backend.cFramesPeriod > CfgHostAcq.Backend.cFramesBufferSize)
-    {
-        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",
-                 CfgHostAcq.Backend.cFramesPeriod, pStreamEx->Core.szName, CfgHostAcq.Backend.cFramesBufferSize, CfgHostAcq.Backend.cFramesBufferSize / 2));
-        AssertFailed();
-        CfgHostAcq.Backend.cFramesPeriod = CfgHostAcq.Backend.cFramesBufferSize / 2;
-    }
-
-    LogRel2(("Audio: Buffer size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
-             PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize), CfgHostAcq.Backend.cFramesBufferSize));
-    LogRel2(("Audio: Pre-buffering size of stream '%s' is %RU64 ms / %RU32 frames\n", pStreamEx->Core.szName,
-             PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering), CfgHostAcq.Backend.cFramesPreBuffering));
-
-    /* Make sure the configured buffer size by the backend at least can hold the configured latency. */
-    const uint32_t msPeriod = PDMAudioPropsFramesToMilli(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPeriod);
-    LogRel2(("Audio: Period size of stream '%s' is %RU64 ms / %RU32 frames\n",
-             pStreamEx->Core.szName, msPeriod, CfgHostAcq.Backend.cFramesPeriod));
-
-    if (   pCfgGuest->Device.cMsSchedulingHint             /* Any scheduling hint set? */
-        && pCfgGuest->Device.cMsSchedulingHint > msPeriod) /* This might lead to buffer underflows. */
-        LogRel(("Audio: Warning: Scheduling hint of stream '%s' is bigger (%RU64ms) than used period size (%RU64ms)\n",
-                pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint, msPeriod));
-
-    /*
-     * Make a copy of the acquired host stream configuration and the guest side one.
-     */
-    rc = PDMAudioStrmCfgCopy(&pStreamEx->Host.Cfg, &CfgHostAcq);
-    AssertRC(rc);
-
-    /* Set the guests's default audio data layout. */
-    pCfgGuest->enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED; /** @todo r=bird: WTF DO WE DO THIS?  It's input and probably should've been const... */
-    rc = PDMAudioStrmCfgCopy(&pStreamEx->Guest.Cfg, pCfgGuest);
-    AssertRC(rc);
-
-    /*
-     * Configure host buffers.
-     */
-
-    /* Destroy any former mixing buffer. */
-    AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
-
-    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
-    {
-        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
-        rc = AudioMixBufInit(&pStreamEx->Host.MixBuf, pStreamEx->Core.szName, &CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize);
-        AssertRCReturn(rc, rc);
-    }
-    /* Allocate space for pre-buffering of output stream w/o mixing buffers. */
-    else if (pCfgHost->enmDir == PDMAUDIODIR_OUT)
-    {
-        Assert(pStreamEx->Out.cbPreBufAlloc == 0);
-        Assert(pStreamEx->Out.cbPreBufThreshold == 0);
-        Assert(pStreamEx->Out.cbPreBuffered == 0);
-        if (CfgHostAcq.Backend.cFramesPreBuffering != 0)
-        {
-            pStreamEx->Out.cbPreBufThreshold = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesPreBuffering);
-            pStreamEx->Out.cbPreBufAlloc = PDMAudioPropsFramesToBytes(&CfgHostAcq.Props, CfgHostAcq.Backend.cFramesBufferSize - 2);
-            pStreamEx->Out.cbPreBufAlloc = RT_MIN(RT_ALIGN_32(pStreamEx->Out.cbPreBufThreshold + _8K, _4K),
-                                                  pStreamEx->Out.cbPreBufAlloc);
-            pStreamEx->Out.pbPreBuf = (uint8_t *)RTMemAllocZ(pStreamEx->Out.cbPreBufAlloc);
-            AssertReturn(pStreamEx->Out.pbPreBuf, VERR_NO_MEMORY);
-        }
-    }
-
-    /*
-     * Init guest stream.
-     */
-    if (pCfgGuest->Device.cMsSchedulingHint)
-        LogRel2(("Audio: Stream '%s' got a scheduling hint of %RU32ms (%RU32 bytes)\n",
-                 pStreamEx->Core.szName, pCfgGuest->Device.cMsSchedulingHint,
-                 PDMAudioPropsMilliToBytes(&pCfgGuest->Props, pCfgGuest->Device.cMsSchedulingHint)));
-
-    /* Destroy any former mixing buffer. */
-    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
-
-    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
-    {
-        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
-        rc = AudioMixBufInit(&pStreamEx->Guest.MixBuf, pStreamEx->Core.szName, &pCfgGuest->Props, CfgHostAcq.Backend.cFramesBufferSize);
-        AssertRCReturn(rc, rc);
-    }
-
-    if (RT_FAILURE(rc))
-        LogRel(("Audio: Creating stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
-
-    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
-    {
-        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
-        /* Host (Parent) -> Guest (Child). */
-        rc = AudioMixBufLinkTo(&pStreamEx->Host.MixBuf, &pStreamEx->Guest.MixBuf);
-        AssertRC(rc);
-    }
-
-    /*
-     * Register statistics.
-     */
-    PPDMDRVINS const pDrvIns = pThis->pDrvIns;
-    /** @todo expose config and more. */
-    PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.Cfg.Backend.cFramesBufferSize, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                           "Host side: The size of the backend buffer (in frames)", "%s/0-HostBackendBufSize", pStreamEx->Core.szName);
-    if (!(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF))
-    {
-        Assert(pCfgHost->enmDir == PDMAUDIODIR_IN);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Host side: The size of the mixer buffer (in frames)",   "%s/1-HostMixBufSize", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cFrames, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Guest side: The size of the mixer buffer (in frames)",  "%s/2-GuestMixBufSize", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Host.MixBuf.cMixed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Host side: Number of frames in the mixer buffer",   "%s/1-HostMixBufUsed", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Guest.MixBuf.cUsed, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Guest side: Number of frames in the mixer buffer",  "%s/2-GuestMixBufUsed", pStreamEx->Core.szName);
-    }
-    if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
-    {
-        /** @todo later? */
-    }
-    else
-    {
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableBefore, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Host side: Free space in backend buffer before play", "%s/0-HostBackendBufFreeBefore", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.cbBackendWritableAfter, STAMTYPE_U32, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Host side: Free space in backend buffer after play",  "%s/0-HostBackendBufFreeAfter", pStreamEx->Core.szName);
-    }
-
-#ifdef VBOX_WITH_STATISTICS
-    if (pCfgGuest->enmDir == PDMAUDIODIR_IN)
-    {
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total frames played.", "%s/TotalFramesCaptured", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesCaptured, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total number of playbacks.", "%s/TotalTimesCaptured", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalFramesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total frames read.", "%s/TotalFramesRead", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->In.Stats.TotalTimesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total number of reads.", "%s/TotalTimesRead", pStreamEx->Core.szName);
-    }
-    else
-    {
-        Assert(pCfgGuest->enmDir == PDMAUDIODIR_OUT);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total frames played.", "%s/TotalFramesPlayed", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesPlayed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total number of playbacks.", "%s/TotalTimesPlayed", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalFramesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total frames written.", "%s/TotalFramesWritten", pStreamEx->Core.szName);
-        PDMDrvHlpSTAMRegisterF(pDrvIns, &pStreamEx->Out.Stats.TotalTimesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_NONE,
-                               "Total number of writes.", "%s/TotalTimesWritten", pStreamEx->Core.szName);
-    }
-#endif /* VBOX_WITH_STATISTICS */
-
-    LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCreate}
- */
-static DECLCALLBACK(int) drvAudioStreamCreate(PPDMIAUDIOCONNECTOR pInterface, uint32_t fFlags, PPDMAUDIOSTREAMCFG pCfgHost,
-                                              PPDMAUDIOSTREAMCFG pCfgGuest, PPDMAUDIOSTREAM *ppStream)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-
-    /*
-     * Assert sanity.
-     */
-    AssertReturn(!(fFlags & ~PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF), VERR_INVALID_FLAGS);
-    AssertPtrReturn(pCfgHost,   VERR_INVALID_POINTER);
-    AssertPtrReturn(pCfgGuest,  VERR_INVALID_POINTER);
-    AssertPtrReturn(ppStream,   VERR_INVALID_POINTER);
-    LogFlowFunc(("Host=%s, Guest=%s\n", pCfgHost->szName, pCfgGuest->szName));
-#ifdef LOG_ENABLED
-    PDMAudioStrmCfgLog(pCfgHost);
-    PDMAudioStrmCfgLog(pCfgGuest);
-#endif
-    AssertReturn(AudioHlpStreamCfgIsValid(pCfgHost), VERR_INVALID_PARAMETER);
-    AssertReturn(AudioHlpStreamCfgIsValid(pCfgGuest), VERR_INVALID_PARAMETER);
-    AssertReturn(pCfgHost->enmDir == pCfgGuest->enmDir, VERR_MISMATCH);
-    AssertReturn(pCfgHost->enmDir == PDMAUDIODIR_IN || pCfgHost->enmDir == PDMAUDIODIR_OUT, VERR_NOT_SUPPORTED);
-    /* Require PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF for output streams: */
-    AssertReturn((fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF) || pCfgHost->enmDir == PDMAUDIODIR_IN, VERR_INVALID_FLAGS);
-
-    /*
-     * Lock the whole driver instance.
-     */
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    /*
-     * Check that we have free streams in the backend and get the
-     * size of the backend specific stream data.
-     */
-    uint32_t *pcFreeStreams;
-    if (pCfgHost->enmDir == PDMAUDIODIR_IN)
-    {
-        if (!pThis->In.cStreamsFree)
-        {
-            LogFlowFunc(("Maximum number of host input streams reached\n"));
-            rc = VERR_AUDIO_NO_FREE_INPUT_STREAMS;
-        }
-        pcFreeStreams = &pThis->In.cStreamsFree;
-    }
-    else /* Out */
-    {
-        if (!pThis->Out.cStreamsFree)
-        {
-            LogFlowFunc(("Maximum number of host output streams reached\n"));
-            rc = VERR_AUDIO_NO_FREE_OUTPUT_STREAMS;
-        }
-        pcFreeStreams = &pThis->Out.cStreamsFree;
-    }
-    size_t const cbHstStrm = pThis->BackendCfg.cbStream;
-    AssertStmt(cbHstStrm < _16M, rc = VERR_OUT_OF_RANGE);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * Allocate and initialize common state.
-         */
-        PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)RTMemAllocZ(sizeof(DRVAUDIOSTREAM) + RT_ALIGN_Z(cbHstStrm, 64));
-        if (pStreamEx)
-        {
-            /* Retrieve host driver name for easier identification. */
-            AssertPtr(pThis->pHostDrvAudio);
-            RTStrPrintf(pStreamEx->Core.szName, RT_ELEMENTS(pStreamEx->Core.szName), "[%s] %s",
-                        pThis->BackendCfg.szName, pCfgHost->szName[0] != '\0' ? pCfgHost->szName : "<Untitled>");
-
-            pStreamEx->Core.enmDir    = pCfgHost->enmDir;
-            pStreamEx->Core.cbBackend = (uint32_t)cbHstStrm;
-            if (cbHstStrm)
-                pStreamEx->pBackend   = (PPDMAUDIOBACKENDSTREAM)(pStreamEx + 1);
-            pStreamEx->fNoMixBufs     = RT_BOOL(fFlags & PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF);
-            pStreamEx->uMagic         = DRVAUDIOSTREAM_MAGIC;
-
-            /*
-             * Try to init the rest.
-             */
-            rc = drvAudioStreamInitInternal(pThis, pStreamEx, fFlags, pCfgHost, pCfgGuest);
-            if (RT_SUCCESS(rc))
-            {
-                /* Set initial reference counts. */
-                pStreamEx->Core.cRefs = 1;
-
-                /* Decrement the free stream counter. */
-                Assert(*pcFreeStreams > 0);
-                *pcFreeStreams -= 1;
-
-                /*
-                 * We're good.
-                 */
-                RTListAppend(&pThis->lstStreams, &pStreamEx->ListEntry);
-                STAM_COUNTER_INC(&pThis->Stats.TotalStreamsCreated);
-                *ppStream = &pStreamEx->Core;
-
-                /*
-                 * Init debug stuff if enabled (ignore failures).
-                 */
-                if (pCfgHost->enmDir == PDMAUDIODIR_IN)
-                {
-                    if (pThis->In.Cfg.Dbg.fEnabled)
-                    {
-                        AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileCaptureNonInterleaved, pThis->In.Cfg.Dbg.szPathOut,
-                                                  "DrvAudioCapNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
-                        AudioHlpFileCreateAndOpen(&pStreamEx->In.Dbg.pFileStreamRead, pThis->In.Cfg.Dbg.szPathOut,
-                                                  "DrvAudioRead", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
-                    }
-                }
-                else /* Out */
-                {
-                    if (pThis->Out.Cfg.Dbg.fEnabled)
-                    {
-                        AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pThis->Out.Cfg.Dbg.szPathOut,
-                                                  "DrvAudioPlayNonInt", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
-                        AudioHlpFileCreateAndOpen(&pStreamEx->Out.Dbg.pFileStreamWrite, pThis->Out.Cfg.Dbg.szPathOut,
-                                                  "DrvAudioWrite", pThis->pDrvIns->iInstance, &pStreamEx->Host.Cfg.Props);
-                    }
-                }
-            }
-            else
-            {
-                LogFunc(("drvAudioStreamInitInternal failed: %Rrc\n", rc));
-                int rc2 = drvAudioStreamUninitInternal(pThis, pStreamEx);
-                AssertRC(rc2);
-                drvAudioStreamFree(pStreamEx);
-            }
-        }
-        else
-            rc = VERR_NO_MEMORY;
-    }
-
-    RTCritSectLeave(&pThis->CritSect);
-    LogFlowFuncLeaveRC(rc);
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnEnable}
- */
-static DECLCALLBACK(int) drvAudioEnable(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir, bool fEnable)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-
-    bool *pfEnabled;
-    if (enmDir == PDMAUDIODIR_IN)
-        pfEnabled = &pThis->In.fEnabled;
-    else if (enmDir == PDMAUDIODIR_OUT)
-        pfEnabled = &pThis->Out.fEnabled;
-    else
-        AssertFailedReturn(VERR_INVALID_PARAMETER);
-
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    if (fEnable != *pfEnabled)
-    {
-        LogRel(("Audio: %s %s for driver '%s'\n",
-                fEnable ? "Enabling" : "Disabling", enmDir == PDMAUDIODIR_IN ? "input" : "output", pThis->szName));
-
-        /* Update the status first, as this will be checked for in drvAudioStreamControlInternalBackend() below. */
-        *pfEnabled = fEnable;
-
-        PDRVAUDIOSTREAM pStreamEx;
-        RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
-        {
-            if (pStreamEx->Core.enmDir != enmDir) /* Skip unwanted streams. */
-                continue;
-
-            /* Note: Only enable / disable the backend, do *not* change the stream's internal status.
-             *       Callers (device emulation, mixer, ...) from outside will not see any status or behavior change,
-             *       to not confuse the rest of the state machine.
-             *
-             *       When disabling:
-             *          - playing back audo data would go to /dev/null
-             *          - recording audio data would return silence instead
-             *
-             * See @bugref{9882}.
-             */
-            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx,
-                                                           fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
-            if (RT_FAILURE(rc2))
-            {
-                if (rc2 == VERR_AUDIO_STREAM_NOT_READY)
-                    LogRel(("Audio: Stream '%s' not available\n", pStreamEx->Core.szName));
-                else
-                    LogRel(("Audio: Failed to %s %s stream '%s', rc=%Rrc\n", fEnable ? "enable" : "disable",
-                            enmDir == PDMAUDIODIR_IN ? "input" : "output", pStreamEx->Core.szName, rc2));
-            }
-            else
-            {
-                /* When (re-)enabling a stream, clear the disabled warning bit again. */
-                if (fEnable)
-                    pStreamEx->Core.fWarningsShown &= ~PDMAUDIOSTREAM_WARN_FLAGS_DISABLED;
-            }
-
-            if (RT_SUCCESS(rc))
-                rc = rc2;
-
-            /* Keep going. */
-        }
-    }
-
-    RTCritSectLeave(&pThis->CritSect);
-    LogFlowFuncLeaveRC(rc);
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnIsEnabled}
- */
-static DECLCALLBACK(bool) drvAudioIsEnabled(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, false);
-
-    bool fEnabled;
-    if (enmDir == PDMAUDIODIR_IN)
-        fEnabled = pThis->In.fEnabled;
-    else if (enmDir == PDMAUDIODIR_OUT)
-        fEnabled = pThis->Out.fEnabled;
-    else
-        AssertFailedStmt(fEnabled = false);
-
-    RTCritSectLeave(&pThis->CritSect);
-    return fEnabled;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetConfig}
- */
-static DECLCALLBACK(int) drvAudioGetConfig(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    if (pThis->pHostDrvAudio)
-    {
-        if (pThis->pHostDrvAudio->pfnGetConfig)
-            rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, pCfg);
-        else
-            rc = VERR_NOT_SUPPORTED;
-    }
-    else
-        rc = VERR_PDM_NO_ATTACHED_DRIVER;
-
-    RTCritSectLeave(&pThis->CritSect);
-    LogFlowFuncLeaveRC(rc);
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnGetStatus}
- */
-static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioGetStatus(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIODIR enmDir)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, PDMAUDIOBACKENDSTS_UNKNOWN);
-
-    PDMAUDIOBACKENDSTS fBackendStatus;
-    if (pThis->pHostDrvAudio)
-    {
-        if (pThis->pHostDrvAudio->pfnGetStatus)
-            fBackendStatus = pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, enmDir);
-        else
-            fBackendStatus = PDMAUDIOBACKENDSTS_UNKNOWN;
-    }
-    else
-        fBackendStatus = PDMAUDIOBACKENDSTS_NOT_ATTACHED;
-
-    RTCritSectLeave(&pThis->CritSect);
-    LogFlowFunc(("LEAVE - %#x\n", fBackendStatus));
-    return fBackendStatus;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamConfigHint}
- */
-static DECLCALLBACK(void) drvAudioStreamConfigHint(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAMCFG pCfg)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertReturnVoid(pCfg->enmDir == PDMAUDIODIR_IN || pCfg->enmDir == PDMAUDIODIR_OUT);
-
-    int rc = RTCritSectEnter(&pThis->CritSect); /** @todo Reconsider the locking for DrvAudio */
-    AssertRCReturnVoid(rc);
-
-    /*
-     * Don't do anything unless the backend has a pfnStreamConfigHint method
-     * and the direction is currently enabled.
-     */
-    if (   pThis->pHostDrvAudio
-        && pThis->pHostDrvAudio->pfnStreamConfigHint)
-    {
-        if (pCfg->enmDir == PDMAUDIODIR_OUT ? pThis->Out.fEnabled : pThis->In.fEnabled)
-        {
-            /*
-             * Adjust the configuration (applying out settings) then call the backend driver.
-             */
-            rc = drvAudioStreamAdjustConfig(pThis, pCfg, pCfg->szName);
-            AssertLogRelRC(rc);
-            if (RT_SUCCESS(rc))
-                pThis->pHostDrvAudio->pfnStreamConfigHint(pThis->pHostDrvAudio, pCfg);
-        }
-        else
-            LogFunc(("Ignoring hint because direction is not currently enabled\n"));
-    }
-    else
-        LogFlowFunc(("Ignoring hint because backend has no pfnStreamConfigHint method.\n"));
-
-    RTCritSectLeave(&pThis->CritSect);
-}
 
 /**
@@ -2804,4 +2717,5 @@
     return cbReadable;
 }
+
 
 /**
@@ -2872,4 +2786,5 @@
 }
 
+
 /**
  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamGetStatus}
@@ -2905,4 +2820,5 @@
 }
 
+
 /**
  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamSetVolume}
@@ -2926,170 +2842,259 @@
 }
 
-/**
- * Calls the backend to give it the chance to destroy its part of the audio stream.
- *
- * Called from drvAudioPowerOff, drvAudioStreamUninitInternal and
- * drvAudioStreamReInitInternal.
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
+ */
+static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed)
+{
+    RT_NOREF(pInterface, pStream, pcFramesPlayed);
+    AssertFailed(/* OBSOLETE! */);
+    return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * Captures non-interleaved input from a host stream.
  *
  * @returns VBox status code.
- * @param   pThis       Pointer to driver instance.
- * @param   pStreamEx   Audio stream destruct backend for.
- */
-static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
-{
+ * @param   pThis       Driver instance.
+ * @param   pStreamEx   Stream to capture from.
+ * @param   pcfCaptured Number of (host) audio frames captured.
+ */
+static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
+{
+    Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
+    Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
+
+    /*
+     * ...
+     */
+    AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
+    uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
+    if (!cbReadable)
+        Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
+
+    uint32_t cbFree = AudioMixBufFreeBytes(&pStreamEx->Guest.MixBuf); /* Parent */
+    if (!cbFree)
+        Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
+
+    if (cbReadable > cbFree) /* More data readable than we can store at the moment? Limit. */
+        cbReadable = cbFree;
+
+    /*
+     * ...
+     */
+    int      rc = VINF_SUCCESS;
+    uint32_t cfCapturedTotal = 0;
+    while (cbReadable)
+    {
+        uint8_t  abChunk[_4K];
+        uint32_t cbCaptured;
+        rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
+                                                    abChunk, RT_MIN(cbReadable, (uint32_t)sizeof(abChunk)), &cbCaptured);
+        if (RT_FAILURE(rc))
+        {
+            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
+            AssertRC(rc2);
+            break;
+        }
+
+        Assert(cbCaptured <= sizeof(abChunk));
+        if (cbCaptured > sizeof(abChunk)) /* Paranoia. */
+            cbCaptured = (uint32_t)sizeof(abChunk);
+
+        if (!cbCaptured) /* Nothing captured? Take a shortcut. */
+            break;
+
+        /* We use the host side mixing buffer as an intermediate buffer to do some
+         * (first) processing (if needed), so always write the incoming data at offset 0. */
+        uint32_t cfHstWritten = 0;
+        rc = AudioMixBufWriteAt(&pStreamEx->Host.MixBuf, 0 /* offFrames */, abChunk, cbCaptured, &cfHstWritten);
+        if (   RT_FAILURE(rc)
+            || !cfHstWritten)
+        {
+            AssertMsgFailed(("[%s] Write failed: cbCaptured=%RU32, cfHstWritten=%RU32, rc=%Rrc\n",
+                             pStreamEx->Core.szName, cbCaptured, cfHstWritten, rc));
+            break;
+        }
+
+        if (pThis->In.Cfg.Dbg.fEnabled)
+            AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCaptureNonInterleaved, abChunk, cbCaptured, 0 /* fFlags */);
+
+        uint32_t cfHstMixed = 0;
+        if (cfHstWritten)
+        {
+            int rc2 = AudioMixBufMixToParentEx(&pStreamEx->Host.MixBuf, 0 /* cSrcOffset */, cfHstWritten /* cSrcFrames */,
+                                               &cfHstMixed /* pcSrcMixed */);
+            Log3Func(("[%s] cbCaptured=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
+                      pStreamEx->Core.szName, cbCaptured, cfHstWritten, cfHstMixed, rc2));
+            AssertRC(rc2);
+        }
+
+        Assert(cbReadable >= cbCaptured);
+        cbReadable      -= cbCaptured;
+        cfCapturedTotal += cfHstMixed;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (cfCapturedTotal)
+            Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
+    }
+    else
+        LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pStreamEx->Core.szName, rc));
+
+    if (pcfCaptured)
+        *pcfCaptured = cfCapturedTotal;
+
+    return rc;
+}
+
+
+/**
+ * Captures raw input from a host stream.
+ *
+ * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
+ * no data layout processing done in between.
+ *
+ * Needed for e.g. the VRDP audio backend (in Main).
+ *
+ * @returns VBox status code.
+ * @param   pThis       Driver instance.
+ * @param   pStreamEx   Stream to capture from.
+ * @param   pcfCaptured Number of (host) audio frames captured.
+ */
+static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
+{
+    Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
+    Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
+    AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
+
+    /*
+     * ...
+     */
+    /* Note: Raw means *audio frames*, not bytes! */
+    uint32_t cfReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
+    if (!cfReadable)
+        Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
+
+    const uint32_t cfFree = AudioMixBufFree(&pStreamEx->Guest.MixBuf); /* Parent */
+    if (!cfFree)
+        Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
+
+    if (cfReadable > cfFree) /* More data readable than we can store at the moment? Limit. */
+        cfReadable = cfFree;
+
+    /*
+     * ...
+     */
+    int      rc              = VINF_SUCCESS;
+    uint32_t cfCapturedTotal = 0;
+    while (cfReadable)
+    {
+        PPDMAUDIOFRAME paFrames;
+        uint32_t cfWritable;
+        rc = AudioMixBufPeekMutable(&pStreamEx->Host.MixBuf, cfReadable, &paFrames, &cfWritable);
+        if (   RT_FAILURE(rc)
+            || !cfWritable)
+            break;
+
+        uint32_t cfCaptured;
+        rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
+                                                    paFrames, cfWritable, &cfCaptured);
+        if (RT_FAILURE(rc))
+        {
+            int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
+            AssertRC(rc2);
+            break;
+        }
+
+        Assert(cfCaptured <= cfWritable);
+        if (cfCaptured > cfWritable) /* Paranoia. */
+            cfCaptured = cfWritable;
+
+        Assert(cfReadable >= cfCaptured);
+        cfReadable      -= cfCaptured;
+        cfCapturedTotal += cfCaptured;
+    }
+
+    if (pcfCaptured)
+        *pcfCaptured = cfCapturedTotal;
+    Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
+ */
+static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
+                                               PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
+{
+    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
     AssertPtr(pThis);
-    AssertPtr(pStreamEx);
-
-    int rc = VINF_SUCCESS;
+    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
+    AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
+    AssertPtrNull(pcFramesCaptured);
+    AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+    AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
+    AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
+              ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
+               pStreamEx->Core.szName, pStreamEx->Core.enmDir));
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturn(rc, rc);
 
 #ifdef LOG_ENABLED
     char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
 #endif
-    LogFunc(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
-
-    if (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED)
-    {
-        AssertPtr(pStreamEx->pBackend);
-
-        /* Check if the pointer to  the host audio driver is still valid.
-         * It can be NULL if we were called in drvAudioDestruct, for example. */
-        if (pThis->pHostDrvAudio)
-            rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pStreamEx->pBackend);
-
-        pStreamEx->Core.fStatus &= ~PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
-    }
-
-    LogFlowFunc(("[%s] Returning %Rrc\n", pStreamEx->Core.szName, rc));
-    return rc;
-}
-
-/**
- * Uninitializes an audio stream - worker for drvAudioStreamDestroy,
- * drvAudioDestruct and drvAudioStreamCreate.
- *
- * @returns VBox status code.
- * @param   pThis       Pointer to driver instance.
- * @param   pStreamEx   Pointer to audio stream to uninitialize.
- *
- * @note    Caller owns the critical section.
- */
-static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx)
-{
-    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
-    AssertMsgReturn(pStreamEx->Core.cRefs <= 1,
-                    ("Stream '%s' still has %RU32 references held when uninitializing\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs),
-                    VERR_WRONG_ORDER);
-    LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
+    Log3Func(("[%s] fStatus=%s\n", pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
 
     /*
      * ...
      */
-    int rc = drvAudioStreamControlInternal(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
-    if (RT_SUCCESS(rc))
-        rc = drvAudioStreamDestroyInternalBackend(pThis, pStreamEx);
-
-    /* Destroy mixing buffers. */
-    AudioMixBufDestroy(&pStreamEx->Guest.MixBuf);
-    AudioMixBufDestroy(&pStreamEx->Host.MixBuf);
-
-    /* Free pre-buffer space. */
-    if (   pStreamEx->Core.enmDir == PDMAUDIODIR_OUT
-        && pStreamEx->Out.pbPreBuf)
-    {
-        RTMemFree(pStreamEx->Out.pbPreBuf);
-        pStreamEx->Out.pbPreBuf      = NULL;
-        pStreamEx->Out.cbPreBufAlloc = 0;
-        pStreamEx->Out.cbPreBuffered = 0;
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-#ifdef LOG_ENABLED
-        if (pStreamEx->Core.fStatus != PDMAUDIOSTREAMSTS_FLAGS_NONE)
-        {
-            char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
-            LogFunc(("[%s] Warning: Still has %s set when uninitializing\n",
-                     pStreamEx->Core.szName, dbgAudioStreamStatusToStr(szStreamSts, pStreamEx->Core.fStatus)));
-        }
-#endif
-        pStreamEx->Core.fStatus = PDMAUDIOSTREAMSTS_FLAGS_NONE;
-    }
-
-    PPDMDRVINS const pDrvIns = pThis->pDrvIns;
-    PDMDrvHlpSTAMDeregisterByPrefix(pDrvIns, pStreamEx->Core.szName);
-
-    if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
-    {
-        if (pThis->In.Cfg.Dbg.fEnabled)
-        {
-            AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileCaptureNonInterleaved);
-            pStreamEx->In.Dbg.pFileCaptureNonInterleaved = NULL;
-
-            AudioHlpFileDestroy(pStreamEx->In.Dbg.pFileStreamRead);
-            pStreamEx->In.Dbg.pFileStreamRead = NULL;
-        }
-    }
-    else
-    {
-        Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT);
-        if (pThis->Out.Cfg.Dbg.fEnabled)
-        {
-            AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFilePlayNonInterleaved);
-            pStreamEx->Out.Dbg.pFilePlayNonInterleaved = NULL;
-
-            AudioHlpFileDestroy(pStreamEx->Out.Dbg.pFileStreamWrite);
-            pStreamEx->Out.Dbg.pFileStreamWrite = NULL;
-        }
-    }
-    LogFlowFunc(("Returning %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamDestroy}
- */
-static DECLCALLBACK(int) drvAudioStreamDestroy(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream)
-{
-    PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
-    AssertPtr(pThis);
-
-    if (!pStream)
-        return VINF_SUCCESS;
-    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
-    PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;   /* Note! Do not touch pStream after this! */
-    Assert(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC);
-    Assert(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC);
-
-    int rc = RTCritSectEnter(&pThis->CritSect);
-    AssertRCReturn(rc, rc);
-
-    LogRel2(("Audio: Destroying stream '%s'\n", pStreamEx->Core.szName));
-
-    LogFlowFunc(("[%s] cRefs=%RU32\n", pStreamEx->Core.szName, pStreamEx->Core.cRefs));
-    AssertMsg(pStreamEx->Core.cRefs <= 1, ("%u %s\n", pStreamEx->Core.cRefs, pStreamEx->Core.szName));
-    if (pStreamEx->Core.cRefs <= 1)
-    {
-        rc = drvAudioStreamUninitInternal(pThis, pStreamEx);
+    uint32_t cfCaptured = 0;
+    do
+    {
+        if (!pThis->pHostDrvAudio)
+        {
+            rc = VERR_PDM_NO_ATTACHED_DRIVER;
+            break;
+        }
+
+        if (   !pThis->In.fEnabled
+            || !PDMAudioStrmStatusCanRead(pStreamEx->Core.fStatus))
+        {
+            rc = VERR_AUDIO_STREAM_NOT_READY;
+            break;
+        }
+
+        /*
+         * Do the actual capturing.
+         */
+        if (RT_LIKELY(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
+            rc = drvAudioStreamCaptureNonInterleaved(pThis, pStreamEx, &cfCaptured);
+        else if (pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
+            rc = drvAudioStreamCaptureRaw(pThis, pStreamEx, &cfCaptured);
+        else
+            AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
         if (RT_SUCCESS(rc))
         {
-            if (pStreamEx->Core.enmDir == PDMAUDIODIR_IN)
-                pThis->In.cStreamsFree++;
-            else /* Out */
-                pThis->Out.cStreamsFree++;
-
-            RTListNodeRemove(&pStreamEx->ListEntry);
-
-            drvAudioStreamFree(pStreamEx);
-            pStreamEx = NULL;
-            pStream = NULL;
-        }
-        else
-            LogRel(("Audio: Uninitializing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
-    }
-    else
-        rc = VERR_WRONG_ORDER;
+            Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCaptured, rc));
+
+            STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn,              cfCaptured);
+            STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesCaptured, cfCaptured);
+        }
+        else if (RT_UNLIKELY(RT_FAILURE(rc)))
+            LogRel(("Audio: Capturing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
+    } while (0);
 
     RTCritSectLeave(&pThis->CritSect);
-    LogFlowFuncLeaveRC(rc);
+
+    if (pcFramesCaptured)
+        *pcFramesCaptured = cfCaptured;
+
+    if (RT_FAILURE(rc))
+        LogFlowFuncLeaveRC(rc);
     return rc;
 }
@@ -3286,4 +3291,33 @@
     RTCritSectLeave(&pThis->CritSect);
     return rc;
+}
+
+
+/**
+ * Handles state changes for all audio streams.
+ *
+ * @param   pDrvIns             Pointer to driver instance.
+ * @param   enmCmd              Stream command to set for all streams.
+ */
+static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
+{
+    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+    LogFlowFunc(("enmCmd=%s\n", PDMAudioStrmCmdGetName(enmCmd)));
+
+    int rc2 = RTCritSectEnter(&pThis->CritSect);
+    AssertRCReturnVoid(rc2);
+
+    if (pThis->pHostDrvAudio)
+    {
+        PDRVAUDIOSTREAM pStreamEx;
+        RTListForEach(&pThis->lstStreams, pStreamEx, DRVAUDIOSTREAM, ListEntry)
+        {
+            drvAudioStreamControlInternal(pThis, pStreamEx, enmCmd);
+        }
+    }
+
+    rc2 = RTCritSectLeave(&pThis->CritSect);
+    AssertRC(rc2);
 }
 
