Index: trunk/src/VBox/Devices/Audio/AudioMixer.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/AudioMixer.cpp	(revision 73524)
+++ trunk/src/VBox/Devices/Audio/AudioMixer.cpp	(revision 73525)
@@ -480,85 +480,29 @@
     LogFlowFuncEnter();
 
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-    /* Make sure only compatible streams are added. */
-    if (pStream->enmDir == PDMAUDIODIR_IN)
-    {
-        if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pIn->Props))
-        {
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-            /* Chain: Stream (Child) -> Sink (Child) -> Guest (Parent). */
-            PPDMAUDIOMIXBUF pHstIn = &pStream->InOut.pIn->pHstStrmIn->MixBuf;
-            PPDMAUDIOMIXBUF pGstIn = &pStream->InOut.pIn->MixBuf;
-
-            /* Unlink any former parent from host input. */
-            AudioMixBufUnlink(pHstIn);
-
-            /* Link host input to this sink as a parent. */
-            rc = AudioMixBufLinkTo(pHstIn, &pSink->MixBuf);
-            AssertRC(rc);
-
-            /* Unlink any former parent from this sink. */
-            AudioMixBufUnlink(&pSink->MixBuf);
-
-            /* Link guest input to this sink as a parent. */
-            rc = AudioMixBufLinkTo(&pSink->MixBuf, pGstIn);
-            AssertRC(rc);
-# ifdef DEBUG
-            AudioMixBufDbgPrintChain(&pStream->InOut.pIn->MixBuf);
-# endif
-#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
-        }
-        else
-            AssertFailedStmt(rc = VERR_WRONG_TYPE);
-    }
-    else if (pStream->enmDir == PDMAUDIODIR_OUT)
-    {
-        if (DrvAudioHlpPCMPropsAreEqual(&pSink->PCMProps, &pStream->InOut.pOut->Props))
-        {
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-            /* Chain: Guest (Child) -> Sink (Child) -> Stream (Parent). */
-            rc = AudioMixBufLinkTo(&pStream->InOut.pOut->pHstStrmOut->MixBuf, &pSink->MixBuf);
-# ifdef DEBUG
-            AudioMixBufDbgPrintChain(&pSink->MixBuf);
-# endif
-#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
-        }
-        else
-            AssertFailedStmt(rc = VERR_WRONG_TYPE);
-    }
-    else
-        AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
-#else
-    rc = VINF_SUCCESS;
-#endif
+    /** @todo Check if stream already is assigned to (another) sink. */
+
+    /* If the sink is running and not in pending disable mode,
+     * make sure that the added stream also is enabled. */
+    if (    (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
+        && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
+    {
+        rc = audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE, AUDMIXSTRMCTL_FLAG_NONE);
+    }
 
     if (RT_SUCCESS(rc))
     {
-        /** @todo Check if stream already is assigned to (another) sink. */
-
-        /* If the sink is running and not in pending disable mode,
-         * make sure that the added stream also is enabled. */
-        if (    (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
-            && !(pSink->fStatus & AUDMIXSINK_STS_PENDING_DISABLE))
-        {
-            rc = audioMixerStreamCtlInternal(pStream, PDMAUDIOSTREAMCMD_ENABLE, AUDMIXSTRMCTL_FLAG_NONE);
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Apply the sink's combined volume to the stream. */
-            rc = pStream->pConn->pfnStreamSetVolume(pStream->pConn, pStream->pStream, &pSink->VolumeCombined);
-            AssertRC(rc);
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Save pointer to sink the stream is attached to. */
-            pStream->pSink = pSink;
-
-            /* Append stream to sink's list. */
-            RTListAppend(&pSink->lstStreams, &pStream->Node);
-            pSink->cStreams++;
-        }
+        /* Apply the sink's combined volume to the stream. */
+        rc = pStream->pConn->pfnStreamSetVolume(pStream->pConn, pStream->pStream, &pSink->VolumeCombined);
+        AssertRC(rc);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        /* Save pointer to sink the stream is attached to. */
+        pStream->pSink = pSink;
+
+        /* Append stream to sink's list. */
+        RTListAppend(&pSink->lstStreams, &pStream->Node);
+        pSink->cStreams++;
     }
 
@@ -859,5 +803,5 @@
     if (pSink->fStatus & AUDMIXSINK_STS_RUNNING)
     {
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
+#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
 # error "Implement me!"
 #else
@@ -922,6 +866,4 @@
         return 0;
 
-    const uint64_t deltaLastReadWriteNs = RTTimeNanoTS() - pSink->tsLastReadWrittenNs;
-
     uint32_t cbWritable = 0;
 
@@ -930,15 +872,15 @@
     {
 #ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-# error "Implement me!"
+        cbWritable = AudioMixBufFreeBytes(&pSink->MixBuf);
 #else
         /* Return how much data we expect since the last write. */
-        cbWritable = DrvAudioHlpNanoToBytes(deltaLastReadWriteNs, &pSink->PCMProps);
-
+        cbWritable = DrvAudioHlpMilliToBytes(10 /* ms */, &pSink->PCMProps); /** @todo Make this configurable! */
+#endif
         /* Make sure to align the writable size to the stream's frame size. */
         cbWritable = DrvAudioHlpBytesAlign(cbWritable, &pSink->PCMProps);
-#endif
-    }
-
-    Log3Func(("Mixer: [%s] cbWritable=%RU32 (%RU64ms)\n", pSink->pszName, cbWritable, deltaLastReadWriteNs / RT_NS_1MS_64));
+    }
+
+    Log3Func(("Mixer: [%s] cbWritable=%RU32 (%RU64ms)\n",
+              pSink->pszName, cbWritable, DrvAudioHlpBytesToMilli(cbWritable, &pSink->PCMProps)));
 
     int rc2 = RTCritSectLeave(&pSink->CritSect);
@@ -1131,5 +1073,5 @@
             uint32_t cbReadStrm;
             AssertPtr(pStreamRecSource->pConn);
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
+#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF_IN
 # error "Implement me!"
 #else
@@ -1209,9 +1151,4 @@
                  pSink->pszName, pStream->pStream->szName, pSink->cStreams));
 
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-    /* Unlink mixing buffer. */
-    AudioMixBufUnlink(&pStream->pStream->MixBuf);
-#endif
-
     /* Remove stream from sink. */
     RTListNodeRemove(&pStream->Node);
@@ -1284,23 +1221,10 @@
     LogFunc(("[%s]\n", pSink->pszName));
 
-    if (pSink->enmDir == AUDMIXSINKDIR_INPUT)
-    {
 #ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-        AudioMixBufReset(&pSink->MixBuf);
-#else
-        pSink->In.cbReadable = 0;
+    AudioMixBufReset(&pSink->MixBuf);
 #endif
-    }
-    else if (pSink->enmDir == AUDMIXSINKDIR_OUTPUT)
-    {
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-        AudioMixBufReset(&pSink->MixBuf);
-#else
-        pSink->Out.cbWritable = 0;
-#endif
-    }
 
     /* Update last updated timestamp. */
-    pSink->tsLastUpdatedMs = RTTimeMilliTS();
+    pSink->tsLastUpdatedMs = 0;
 
     /* Reset status. */
@@ -1408,5 +1332,6 @@
     /* Also update the sink's mixing buffer format. */
     AudioMixBufDestroy(&pSink->MixBuf);
-    rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, pPCMProps, _4K /** @todo Make configurable? */);
+    rc = AudioMixBufInit(&pSink->MixBuf, pSink->pszName, &pSink->PCMProps,
+                         DrvAudioHlpMilliToFrames(100 /* ms */, &pSink->PCMProps)); /** @todo Make this configurable? */
     if (RT_SUCCESS(rc))
     {
@@ -1632,7 +1557,4 @@
     }
 
-    Log3Func(("[%s] cbReadable=%RU32, cbWritable=%RU32, rc=%Rrc\n",
-              pSink->pszName, pSink->In.cbReadable, pSink->Out.cbWritable, rc));
-
     return rc;
 }
@@ -1702,29 +1624,20 @@
 
 /**
- * Writes data to a mixer sink.
+ * Writes audio output data to all connected mixer streams (multiplex).
  *
  * @returns IPRT status code.
- * @param   pSink               Sink to write data to.
- * @param   enmOp               Mixer operation to use when writing data to the sink.
- * @param   pvBuf               Buffer containing the audio data to write.
- * @param   cbBuf               Size (in bytes) of the buffer containing the audio data.
- * @param   pcbWritten          Number of bytes written. Optional.
- */
-int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
-{
-    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
+ * @param   pSink               Sink to write audio output to.
+ * @param   enmOp               What mixing operation to use. Currently not implemented.
+ * @param   pvBuf               Pointer to audio data to write.
+ * @param   cbBuf               Size (in bytes) of audio data to write.
+ * @param   pcbWrittenMin       Returns minimum size (in bytes) successfully written by all mixer streams. Optional.
+ */
+int audioMixerSinkWriteToStreams(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWrittenMin)
+{
     RT_NOREF(enmOp);
-    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
-    AssertReturn   (cbBuf, VERR_INVALID_PARAMETER);
-    /* pcbWritten is optional. */
-
-    int rc = RTCritSectEnter(&pSink->CritSect);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
-              ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
-    AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
-              ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
+
+    int rc = VINF_SUCCESS;
+
+    uint32_t cbWrittenMin = UINT32_MAX;
 
     PAUDMIXSTREAM pMixStream;
@@ -1760,9 +1673,11 @@
             LogFunc(("[%s] Warning: Only written %RU32/%RU32 bytes for stream '%s'\n",
                      pSink->pszName, cbWritten, cbBuf, pMixStream->pszName));
-            LogRel2(("Audio: Buffer overrun for mixer sink '%s', stream '%s'\n", pSink->pszName, pMixStream->pszName));
+            LogRel2(("Audio: Buffer overrun for mixer stream '%s' (sink '%s')\n", pMixStream->pszName, pSink->pszName));
 #ifdef DEBUG_andy
             AssertFailed();
 #endif
         }
+
+        cbWrittenMin = RT_MIN(cbWrittenMin, cbWritten);
 
         if (cbWritten) /* Update the mixer stream's last written time stamp. */
@@ -1804,11 +1719,100 @@
             cbToWrite -= (uint32_t)cbChunk;
         }
-
-        if (RTCircBufUsed(pCircBuf))
-        {
-            /* Set dirty bit. */
-            pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
-        }
-    }
+    }
+
+    if (cbWrittenMin == UINT32_MAX) /* Nothing written? */
+        cbWrittenMin = 0;
+
+    if (pcbWrittenMin)
+        *pcbWrittenMin = cbWrittenMin;
+
+    return rc;
+}
+
+/**
+ * Writes data to a mixer sink.
+ *
+ * @returns IPRT status code.
+ * @param   pSink               Sink to write data to.
+ * @param   enmOp               Mixer operation to use when writing data to the sink.
+ * @param   pvBuf               Buffer containing the audio data to write.
+ * @param   cbBuf               Size (in bytes) of the buffer containing the audio data.
+ * @param   pcbWritten          Number of bytes written. Optional.
+ */
+int AudioMixerSinkWrite(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
+{
+    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
+    RT_NOREF(enmOp);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    AssertReturn   (cbBuf, VERR_INVALID_PARAMETER);
+    /* pcbWritten is optional. */
+
+    int rc = RTCritSectEnter(&pSink->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    AssertMsg(pSink->fStatus & AUDMIXSINK_STS_RUNNING,
+              ("%s: Can't write to a sink which is not running (anymore) (status 0x%x)\n", pSink->pszName, pSink->fStatus));
+    AssertMsg(pSink->enmDir == AUDMIXSINKDIR_OUTPUT,
+              ("%s: Can't write to a sink which is not an output sink\n", pSink->pszName));
+
+#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
+    uint32_t cbWritten = 0;
+    uint32_t cbToWrite = cbBuf;
+    while (cbToWrite)
+    {
+        /* First, write the data to the mixer sink's own mixing buffer.
+         * Here the audio data can be transformed into the mixer sink's format. */
+        uint32_t cfWritten = 0;
+        rc = AudioMixBufWriteCirc(&pSink->MixBuf, (uint8_t *)pvBuf + cbWritten, cbToWrite, &cfWritten);
+        if (RT_FAILURE(rc))
+            break;
+
+        const uint32_t cbWrittenChunk = DrvAudioHlpFramesToBytes(cfWritten, &pSink->PCMProps);
+
+        Assert(cbToWrite >= cbWrittenChunk);
+        cbToWrite -= cbWrittenChunk;
+        cbWritten += cbWrittenChunk;
+    }
+
+    Assert(cbWritten <= cbBuf);
+
+    if (   RT_FAILURE(rc)
+        || cbWritten < cbBuf)
+    {
+        LogRel2(("Audio: Buffer overrun for mixer sink '%s' (only %RU32/%RU32 bytes written)\n",
+                 pSink->pszName, cbWritten, cbBuf));
+# ifdef DEBUG_andy
+        AssertFailed();
+# endif
+    }
+
+    /* Next, try to write (multiplex) as much audio data as possible to all connected mixer streams. */
+    uint8_t arrChunkBuf[_1K]; /** @todo Hm ... some zero copy / shared buffers would be nice! */
+    while (cbWritten)
+    {
+        uint32_t cfChunk;
+        rc  = AudioMixBufAcquireReadBlock(&pSink->MixBuf, arrChunkBuf, sizeof(arrChunkBuf), &cfChunk);
+        if (RT_FAILURE(rc))
+            break;
+
+        const uint32_t cbChunk = DrvAudioHlpFramesToBytes(cfChunk, &pSink->PCMProps);
+        Assert(cbChunk <= sizeof(arrChunkBuf));
+        rc = audioMixerSinkWriteToStreams(pSink, enmOp, arrChunkBuf, cbChunk, NULL /* pcbWrittenMin */);
+        AudioMixBufReleaseReadBlock(&pSink->MixBuf, cfChunk);
+
+        Assert(cbWritten >= cbChunk);
+        cbWritten -= cbChunk;
+    }
+
+    if (   !(pSink->fStatus & AUDMIXSINK_STS_DIRTY)
+        && AudioMixBufUsed(&pSink->MixBuf)) /* Still audio output data left? Consider the sink as being "dirty" then. */
+    {
+        /* Set dirty bit. */
+        pSink->fStatus |= AUDMIXSINK_STS_DIRTY;
+    }
+#else
+    rc = audioMixerSinkWriteToStreams(pSink, enmOp, pvBuf, cbBuf, NULL /* pcbWrittenMin */);
+#endif /* VBOX_AUDIO_MIXER_WITH_MIXBUF */
 
     /* Update the sink's last written time stamp. */
Index: trunk/src/VBox/Devices/Audio/AudioMixer.h
===================================================================
--- trunk/src/VBox/Devices/Audio/AudioMixer.h	(revision 73524)
+++ trunk/src/VBox/Devices/Audio/AudioMixer.h	(revision 73525)
@@ -26,4 +26,7 @@
 
 #include <VBox/vmm/pdmaudioifs.h>
+
+/* Use a mixer sink's mixing buffer for multiplexing. */
+#define VBOX_AUDIO_MIXER_WITH_MIXBUF
 
 /**
@@ -136,12 +139,4 @@
 typedef struct AUDMIXSINKIN
 {
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-    /** This sink's mixing buffer, acting as
-     * a parent buffer for all streams this sink owns. */
-    PDMAUDIOMIXBUF MixBuf;
-#else
-    /** Number of bytes available to read from the sink. */
-    uint32_t       cbReadable;
-#endif
     /** The current recording source. Can be NULL if not set. */
     PAUDMIXSTREAM  pStreamRecSource;
@@ -154,12 +149,4 @@
 typedef struct AUDMIXSINKOUT
 {
-#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
-    /** This sink's mixing buffer, acting as
-     * a parent buffer for all streams this sink owns. */
-    PDMAUDIOMIXBUF MixBuf;
-#else
-    /** Number of bytes available to write to the sink. */
-    uint32_t       cbWritable;
-#endif
 } AUDMIXSINKOUT;
 
@@ -179,4 +166,9 @@
     /** The sink's critical section. */
     RTCRITSECT              CritSect;
+#ifdef VBOX_AUDIO_MIXER_WITH_MIXBUF
+    /** This sink's mixing buffer, acting as
+     * a parent buffer for all streams this sink owns. */
+    PDMAUDIOMIXBUF          MixBuf;
+#endif
     /** Union for input/output specifics. */
     union
