Index: trunk/src/VBox/Devices/Audio/AudioMixBuffer.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/AudioMixBuffer.cpp	(revision 67363)
+++ trunk/src/VBox/Devices/Audio/AudioMixBuffer.cpp	(revision 67365)
@@ -62,5 +62,6 @@
 
 #ifdef DEBUG
-DECLINLINE(void) audioMixBufDbgPrintInternal(PPDMAUDIOMIXBUF pMixBuf);
+DECLINLINE(void)        audioMixBufDbgPrintInternal(PPDMAUDIOMIXBUF pMixBuf, const char *pszFunc);
+DECL_FORCE_INLINE(bool) audioMixBufDbgValidate(PPDMAUDIOMIXBUF pMixBuf);
 #endif
 
@@ -134,8 +135,4 @@
 AssertCompile(AUDIOMIXBUF_VOL_0DB == 0x40000000);   /* For now -- when only attenuation is used. */
 
-#ifdef DEBUG
-static uint64_t s_cSamplesMixedTotal = 0;
-#endif
-
 
 /**
@@ -288,5 +285,5 @@
 
         pIter->cMixed -= RT_MIN(pIter->cMixed, cSamplesToClear);
-        pIter->cUsed  -= RT_MIN(pIter->cUsed,  AUDIOMIXBUF_S2S_RATIO(pMixBuf, cSamplesToClear));
+        /* Note: Do not increment pIter->cUsed here, as this gets done when reading from that buffer using AudioMixBufReadXXX. */
     }
 
@@ -822,5 +819,7 @@
 
     pMixBuf->pParent = NULL;
+
     RTListInit(&pMixBuf->lstChildren);
+    pMixBuf->cChildren = 0;
 
     pMixBuf->pSamples = NULL;
@@ -883,4 +882,22 @@
 
 /**
+ * Calculates the frequency (sample rate) ratio of mixing buffer A in relation to mixing buffer B.
+ *
+ * @returns Calculated frequency ratio.
+ * @param   pMixBufA            First mixing buffer.
+ * @param   pMixBufB            Second mixing buffer.
+ */
+static int64_t audioMixBufCalcFreqRatio(PPDMAUDIOMIXBUF pMixBufA, PPDMAUDIOMIXBUF pMixBufB)
+{
+    int64_t iRatio = ((int64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBufA->AudioFmt) << 32)
+                   /           AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBufB->AudioFmt);
+
+    if (iRatio == 0)      /* Catch division by zero. */
+        iRatio = 1 << 20; /* Do a 1:1 conversion instead. */
+
+    return iRatio;
+}
+
+/**
  * Links an audio mixing buffer to a parent mixing buffer. A parent mixing
  * buffer can have multiple children mixing buffers [1:N], whereas a child only can
@@ -922,12 +939,11 @@
 
     RTListAppend(&pParent->lstChildren, &pMixBuf->Node);
+    pParent->cChildren++;
+
+    /* Set the parent. */
     pMixBuf->pParent = pParent;
 
-    /* Calculate the frequency ratio. */
-    pMixBuf->iFreqRatio = ((int64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt) << 32)
-                        /           AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt);
-
-    if (pMixBuf->iFreqRatio == 0) /* Catch division by zero. */
-        pMixBuf->iFreqRatio = 1 << 20; /* Do a 1:1 conversion instead. */
+    /* Calculate the frequency ratios. */
+    pMixBuf->iFreqRatio = audioMixBufCalcFreqRatio(pParent, pMixBuf);
 
     int rc = VINF_SUCCESS;
@@ -1044,12 +1060,14 @@
  * @param   pDst                    Destination mixing buffer.
  * @param   pSrc                    Source mixing buffer.
+ * @param   cSrcOff                 Offset of source audio samples to mix.
  * @param   cSrcSamples             Number of source audio samples to mix.
- * @param   pcProcessed             Number of audio samples successfully mixed.
- */
-static int audioMixBufMixTo(PPDMAUDIOMIXBUF pDst, PPDMAUDIOMIXBUF pSrc, uint32_t cSrcSamples, uint32_t *pcProcessed)
+ * @param   pcSrcMixed              Number of source audio samples successfully mixed. Optional.
+ */
+static int audioMixBufMixTo(PPDMAUDIOMIXBUF pDst, PPDMAUDIOMIXBUF pSrc, uint32_t cSrcOff, uint32_t cSrcSamples,
+                            uint32_t *pcSrcMixed)
 {
     AssertPtrReturn(pDst,  VERR_INVALID_POINTER);
     AssertPtrReturn(pSrc,  VERR_INVALID_POINTER);
-    /* pcProcessed is optional. */
+    /* pcSrcMixed is optional. */
 
     AssertMsgReturn(pDst == pSrc->pParent, ("Source buffer '%s' is not a child of destination '%s'\n",
@@ -1058,36 +1076,28 @@
     uint32_t cWrittenTotal = 0;
 
-    if (pSrc->cMixed >= pDst->cSamples)
-    {
-        AUDMIXBUF_LOG(("Warning: Destination buffer '%s' full (%RU32 samples max), got %RU32 mixed samples\n",
-                       pDst->pszName, pDst->cSamples, pSrc->cMixed));
-        if (pcProcessed)
-            *pcProcessed = 0;
+    Assert(pSrc->cMixed <= pDst->cSamples);
+
+    Assert(pSrc->cUsed >= pDst->cMixed);
+    Assert(pDst->cUsed <= pDst->cSamples);
+
+    uint32_t offSrcRead  = cSrcOff;
+
+    uint32_t offDstWrite = pDst->offWrite;
+    uint32_t cDstMixed   = pSrc->cMixed;
+
+    uint32_t cSrcAvail   = RT_MIN(cSrcSamples, pSrc->cUsed);
+    uint32_t cDstAvail   = pDst->cSamples - pDst->cUsed; /** @todo Use pDst->cMixed later? */
+
+    AUDMIXBUF_LOG(("%s (%RU32 available) -> %s (%RU32 available)\n",
+                   pSrc->pszName, cSrcAvail, pDst->pszName, cDstAvail));
+#ifdef DEBUG
+    audioMixBufDbgPrintInternal(pDst, __FUNCTION__);
+#endif
+
+    if (!cSrcAvail)
+        return VERR_BUFFER_UNDERFLOW;
+
+    if (!cDstAvail)
         return VERR_BUFFER_OVERFLOW;
-    }
-
-    Assert(pSrc->cUsed >= pDst->cMixed);
-
-    uint32_t cSrcAvail  = RT_MIN(cSrcSamples, pSrc->cUsed - pDst->cMixed);
-    uint32_t offSrcRead = pSrc->offRead;
-    uint32_t cDstMixed  = pSrc->cMixed;
-
-    Assert(pDst->cUsed <= pDst->cSamples);
-    uint32_t cDstAvail    = pDst->cSamples - pDst->cUsed;
-    uint32_t offDstWrite  = pDst->offWrite;
-
-    AUDMIXBUF_LOG(("cSrcSamples=%RU32, cSrcAvail=%RU32 -> cDstAvail=%RU32\n", cSrcSamples,  cSrcAvail, cDstAvail));
-
-    if (   !cSrcAvail
-        || !cDstAvail)
-    {
-        if (pcProcessed)
-            *pcProcessed = 0;
-        return VINF_SUCCESS;
-    }
-
-#ifdef DEBUG
-    audioMixBufDbgPrintInternal(pDst);
-#endif
 
     uint32_t cSrcToRead = 0;
@@ -1099,28 +1109,29 @@
     int rc = VINF_SUCCESS;
 
-    while (   cSrcAvail
-           && cDstAvail)
+    while (cSrcAvail && cDstAvail)
     {
         cSrcToRead  = RT_MIN(cSrcAvail, pSrc->cSamples - offSrcRead);
         cDstToWrite = RT_MIN(cDstAvail, pDst->cSamples - offDstWrite);
 
-        AUDMIXBUF_LOG(("\tSource: %RU32 samples available, %RU32 @ %RU32 -> reading %RU32\n", cSrcAvail, offSrcRead, pSrc->cSamples, cSrcToRead));
-        AUDMIXBUF_LOG(("\tDest  : %RU32 samples available, %RU32 @ %RU32 -> writing %RU32\n", cDstAvail, offDstWrite, pDst->cSamples, cDstToWrite));
+        AUDMIXBUF_LOG(("\tSource: %RU32 @ %RU32 -> reading %RU32\n", cSrcAvail, offSrcRead, cSrcToRead));
+        AUDMIXBUF_LOG(("\tDest  : %RU32 @ %RU32 -> writing %RU32\n", cDstAvail, offDstWrite, cDstToWrite));
+
+        if (   !cDstToWrite
+            || !cSrcToRead)
+        {
+            break;
+        }
 
         cDstWritten = cSrcRead = 0;
 
-        if (   cDstToWrite
-            && cSrcToRead)
-        {
-            Assert(offSrcRead < pSrc->cSamples);
-            Assert(offSrcRead + cSrcToRead <= pSrc->cSamples);
-
-            Assert(offDstWrite < pDst->cSamples);
-            Assert(offDstWrite + cDstToWrite <= pDst->cSamples);
-
-            audioMixBufOpAssign(pDst->pSamples + offDstWrite, cDstToWrite,
-                                pSrc->pSamples + offSrcRead,  cSrcToRead,
-                                pSrc->pRate, &cDstWritten, &cSrcRead);
-        }
+        Assert(offSrcRead < pSrc->cSamples);
+        Assert(offSrcRead + cSrcToRead <= pSrc->cSamples);
+
+        Assert(offDstWrite < pDst->cSamples);
+        Assert(offDstWrite + cDstToWrite <= pDst->cSamples);
+
+        audioMixBufOpAssign(pDst->pSamples + offDstWrite, cDstToWrite,
+                            pSrc->pSamples + offSrcRead,  cSrcToRead,
+                            pSrc->pRate, &cDstWritten, &cSrcRead);
 
         cReadTotal    += cSrcRead;
@@ -1133,14 +1144,17 @@
 
         Assert(cSrcAvail >= cSrcRead);
-        cSrcAvail     -= cSrcRead;
+        cSrcAvail        -= cSrcRead;
+
         Assert(cDstAvail >= cDstWritten);
-        cDstAvail     -= cDstWritten;
-
-        AUDMIXBUF_LOG(("\t%RU32 read (%RU32 left), %RU32 written (%RU32 left)\n", cSrcRead, cSrcAvail, cDstWritten, cDstAvail));
+        cDstAvail        -= cDstWritten;
+
+        AUDMIXBUF_LOG(("\t%RU32 read (%RU32 left @ %RU32), %RU32 written (%RU32 left @ %RU32)\n",
+                       cSrcRead, cSrcAvail, offSrcRead,
+                       cDstWritten, cDstAvail, offDstWrite));
     }
 
     pSrc->offRead     = offSrcRead;
     Assert(pSrc->cUsed >= cReadTotal);
-    pSrc->cUsed      -= cReadTotal;
+    pSrc->cUsed      -= RT_MIN(pSrc->cUsed, cReadTotal);
 
     /* Note: Always count in parent samples, as the rate can differ! */
@@ -1159,5 +1173,5 @@
     if (pDst->cUsed > pDst->cSamples)
     {
-        LogFunc(("Warning: Destination buffer used %RU32 / %RU32 samples\n", pDst->cUsed, pDst->cSamples));
+        LogFunc(("%s: Warning: Destination buffer used %RU32 / %RU32 samples\n", pDst->pszName, pDst->cUsed, pDst->cSamples));
         pDst->offWrite     = 0;
         pDst->cUsed        = pDst->cSamples;
@@ -1165,24 +1179,55 @@
         rc = VERR_BUFFER_OVERFLOW;
     }
-    else if (!cSrcToRead && cDstAvail)
-    {
-        LogFunc(("Warning: Source buffer '%s' ran out of data\n", pSrc->pszName));
-        rc = VERR_BUFFER_UNDERFLOW;
-    }
-    else if (cSrcAvail && !cDstAvail)
-    {
-        LogFunc(("Warning: Destination buffer '%s' full (%RU32 source samples left)\n", pDst->pszName, cSrcAvail));
-        rc = VERR_BUFFER_OVERFLOW;
-    }
 
 #ifdef DEBUG
-    s_cSamplesMixedTotal += cWrittenTotal;
-    audioMixBufDbgPrintInternal(pDst);
-#endif
-
-    if (pcProcessed)
-        *pcProcessed = cReadTotal;
-
-    AUDMIXBUF_LOG(("cReadTotal=%RU32 (pcProcessed), cWrittenTotal=%RU32, cSrcMixed=%RU32, cDstUsed=%RU32, rc=%Rrc\n",
+    audioMixBufDbgValidate(pSrc);
+    audioMixBufDbgValidate(pDst);
+
+    Assert(pSrc->cMixed <= pDst->cSamples);
+#endif
+
+#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
+    uint32_t offRead = pDst->offRead;
+
+    uint32_t cLeft = cWrittenTotal;
+    while (cLeft)
+    {
+        uint8_t auBuf[256];
+        RT_ZERO(auBuf);
+
+        Assert(sizeof(auBuf) >= 4);
+        Assert(sizeof(auBuf) % 4 == 0);
+
+        uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2S(pDst, sizeof(auBuf)), RT_MIN(cLeft, pDst->cSamples - offRead));
+        Assert(cToRead <= pDst->cUsed);
+
+        PDMAUDMIXBUFCONVOPTS convOpts;
+        RT_ZERO(convOpts);
+        convOpts.cSamples = cToRead;
+
+        pDst->pfnConvTo(auBuf, pDst->pSamples + offRead, &convOpts);
+
+        RTFILE fh;
+        int rc2 = RTFileOpen(&fh, AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "mixbuf_mixto.pcm",
+                             RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+        if (RT_SUCCESS(rc2))
+        {
+            RTFileWrite(fh, auBuf, AUDIOMIXBUF_S2B(pDst, cToRead), NULL);
+            RTFileClose(fh);
+        }
+
+        offRead  = (offRead + cToRead) % pDst->cSamples;
+        cLeft   -= cToRead;
+    }
+#endif /* AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA */
+
+#ifdef DEBUG
+    audioMixBufDbgPrintInternal(pDst, __FUNCTION__);
+#endif
+
+    if (pcSrcMixed)
+        *pcSrcMixed = cReadTotal;
+
+    AUDMIXBUF_LOG(("cReadTotal=%RU32, cWrittenTotal=%RU32, cSrcMixed=%RU32, cDstUsed=%RU32, rc=%Rrc\n",
                    cReadTotal, cWrittenTotal, pSrc->cMixed, pDst->cUsed, rc));
     return rc;
@@ -1190,14 +1235,13 @@
 
 /**
- * Mixes audio samples down to the parent mixing buffer.
+ * Mixes audio samples down to the parent mixing buffer, extended version.
  *
  * @return  IPRT status code. See audioMixBufMixTo() for a more detailed explanation.
- * @param   pMixBuf                 Mixing buffer to mix samples down to parent.
- * @param   cSamples                Number of audio samples of specified mixing buffer to to mix
- *                                  to its attached parent mixing buffer (if any).
- * @param   pcProcessed             Number of audio samples successfully processed. Optional.
- */
-int AudioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples,
-                           uint32_t *pcProcessed)
+ * @param   pMixBuf                 Source mixing buffer to mix to its parent.
+ * @param   cSrcOffset              Offset (in samples) of source mixing buffer.
+ * @param   cSrcSamples             Number of source audio samples to mix to its parent.
+ * @param   pcSrcMixed              Number of source audio samples successfully mixed. Optional.
+ */
+int AudioMixBufMixToParentEx(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSrcOffset, uint32_t cSrcSamples, uint32_t *pcSrcMixed)
 {
     AssertMsgReturn(VALID_PTR(pMixBuf->pParent),
@@ -1205,9 +1249,21 @@
                     VERR_INVALID_PARAMETER);
 
-    return audioMixBufMixTo(pMixBuf->pParent, pMixBuf, cSamples, pcProcessed);
+    return audioMixBufMixTo(pMixBuf->pParent, pMixBuf, cSrcOffset, cSrcSamples, pcSrcMixed);
+}
+
+/**
+ * Mixes audio samples down to the parent mixing buffer.
+ *
+ * @return  IPRT status code. See audioMixBufMixTo() for a more detailed explanation.
+ * @param   pMixBuf                 Source mixing buffer to mix to its parent.
+ * @param   cSrcSamples             Number of source audio samples to mix to its parent.
+ * @param   pcSrcMixed              Number of source audio samples successfully mixed. Optional.
+ */
+int AudioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSrcSamples, uint32_t *pcSrcMixed)
+{
+    return audioMixBufMixTo(pMixBuf->pParent, pMixBuf, pMixBuf->offRead, cSrcSamples, pcSrcMixed);
 }
 
 #ifdef DEBUG
-
 /**
  * Prints a single mixing buffer.
@@ -1216,12 +1272,50 @@
  * @return  IPRT status code.
  * @param   pMixBuf                 Mixing buffer to print.
+ * @param   pszFunc                 Function name to log this for.
  * @param   fIsParent               Whether this is a parent buffer or not.
  * @param   uIdtLvl                 Indention level to use.
  */
-DECL_FORCE_INLINE(void) audioMixBufDbgPrintSingle(PPDMAUDIOMIXBUF pMixBuf, bool fIsParent, uint16_t uIdtLvl)
-{
-    LogFunc(("%*s[%s] %s: offRead=%RU32, offWrite=%RU32, cMixed=%RU32 -> %RU32/%RU32\n",
-             uIdtLvl * 4, "", fIsParent ? "PARENT" : "CHILD",
-             pMixBuf->pszName, pMixBuf->offRead, pMixBuf->offWrite, pMixBuf->cMixed, pMixBuf->cUsed, pMixBuf->cSamples));
+DECL_FORCE_INLINE(void) audioMixBufDbgPrintSingle(PPDMAUDIOMIXBUF pMixBuf, const char *pszFunc, bool fIsParent, uint16_t uIdtLvl)
+{
+    Log(("%s: %*s[%s] %s: offRead=%RU32, offWrite=%RU32, cMixed=%RU32 -> %RU32/%RU32\n",
+         pszFunc, uIdtLvl * 4, "", fIsParent ? "PARENT" : "CHILD",
+         pMixBuf->pszName, pMixBuf->offRead, pMixBuf->offWrite, pMixBuf->cMixed, pMixBuf->cUsed, pMixBuf->cSamples));
+}
+
+/**
+ * Validates a single mixing buffer.
+ *
+ * @return  @true if the buffer state is valid or @false if not.
+ * @param   pMixBuf                 Mixing buffer to validate.
+ */
+DECL_FORCE_INLINE(bool) audioMixBufDbgValidate(PPDMAUDIOMIXBUF pMixBuf)
+{
+    //const uint32_t offReadEnd  = (pMixBuf->offRead + pMixBuf->cUsed) % pMixBuf->cSamples;
+    //const uint32_t offWriteEnd = (pMixBuf->offWrite + (pMixBuf->cSamples - pMixBuf->cUsed)) % pMixBuf->cSamples;
+
+    bool fValid = true;
+
+    AssertStmt(pMixBuf->offRead  <= pMixBuf->cSamples, fValid = false);
+    AssertStmt(pMixBuf->offWrite <= pMixBuf->cSamples, fValid = false);
+    AssertStmt(pMixBuf->cUsed    <= pMixBuf->cSamples, fValid = false);
+
+    if (pMixBuf->offWrite > pMixBuf->offRead)
+    {
+        if (pMixBuf->offWrite - pMixBuf->offRead != pMixBuf->cUsed)
+            fValid = false;
+    }
+    else if (pMixBuf->offWrite < pMixBuf->offRead)
+    {
+        if (pMixBuf->offWrite + pMixBuf->cSamples - pMixBuf->offRead != pMixBuf->cUsed)
+            fValid = false;
+    }
+
+    if (!fValid)
+    {
+        audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__);
+        AssertFailed();
+    }
+
+    return fValid;
 }
 
@@ -1232,18 +1326,20 @@
  * @return  IPRT status code.
  * @param   pMixBuf                 Mixing buffer to print.
+ * @param   pszFunc                 Function name to print the chain for.
  * @param   uIdtLvl                 Indention level to use.
  * @param   pcChildren              Pointer to children counter.
  */
-DECL_FORCE_INLINE(void) audioMixBufDbgPrintChainHelper(PPDMAUDIOMIXBUF pMixBuf, uint16_t uIdtLvl, size_t *pcChildren)
+DECL_FORCE_INLINE(void) audioMixBufDbgPrintChainHelper(PPDMAUDIOMIXBUF pMixBuf, const char *pszFunc, uint16_t uIdtLvl,
+                                                       size_t *pcChildren)
 {
     PPDMAUDIOMIXBUF pIter;
     RTListForEach(&pMixBuf->lstChildren, pIter, PDMAUDIOMIXBUF, Node)
     {
-        audioMixBufDbgPrintSingle(pIter, false /* ifIsParent */, uIdtLvl + 1);
+        audioMixBufDbgPrintSingle(pIter, pszFunc, false /* ifIsParent */, uIdtLvl + 1);
         *pcChildren++;
     }
 }
 
-DECL_FORCE_INLINE(void) audioMixBufDbgPrintChainInternal(PPDMAUDIOMIXBUF pMixBuf)
+DECL_FORCE_INLINE(void) audioMixBufDbgPrintChainInternal(PPDMAUDIOMIXBUF pMixBuf, const char *pszFunc)
 {
     PPDMAUDIOMIXBUF pParent = pMixBuf->pParent;
@@ -1259,14 +1355,11 @@
         pParent = pMixBuf;
 
-    AUDMIXBUF_LOG(("********************************************\n"));
-
-    audioMixBufDbgPrintSingle(pParent, true /* fIsParent */, 0 /* uIdtLvl */);
+    audioMixBufDbgPrintSingle(pParent, pszFunc, true /* fIsParent */, 0 /* uIdtLvl */);
 
     /* Recursively iterate children. */
     size_t cChildren = 0;
-    audioMixBufDbgPrintChainHelper(pParent, 0 /* uIdtLvl */, &cChildren);
-
-    AUDMIXBUF_LOG(("Children: %zu - Total samples mixed: %RU64\n", cChildren, s_cSamplesMixedTotal));
-    AUDMIXBUF_LOG(("********************************************\n"));
+    audioMixBufDbgPrintChainHelper(pParent, pszFunc, 0 /* uIdtLvl */, &cChildren);
+
+    Log(("%s: Children: %zu\n", pszFunc, cChildren));
 }
 
@@ -1281,8 +1374,8 @@
 void AudioMixBufDbgPrintChain(PPDMAUDIOMIXBUF pMixBuf)
 {
-    audioMixBufDbgPrintChainInternal(pMixBuf);
-}
-
-DECL_FORCE_INLINE(void) audioMixBufDbgPrintInternal(PPDMAUDIOMIXBUF pMixBuf)
+    audioMixBufDbgPrintChainInternal(pMixBuf, __FUNCTION__);
+}
+
+DECL_FORCE_INLINE(void) audioMixBufDbgPrintInternal(PPDMAUDIOMIXBUF pMixBuf, const char *pszFunc)
 {
     PPDMAUDIOMIXBUF pParent = pMixBuf;
@@ -1290,17 +1383,13 @@
         pParent = pMixBuf->pParent;
 
-    LogFunc(("***************************************************************************************\n"));
-
-    audioMixBufDbgPrintSingle(pMixBuf, pParent == pMixBuf /* fIsParent */, 0 /* iIdtLevel */);
+    audioMixBufDbgPrintSingle(pMixBuf, pszFunc, pParent == pMixBuf /* fIsParent */, 0 /* iIdtLevel */);
 
     PPDMAUDIOMIXBUF pIter;
-    RTListForEach(&pParent->lstChildren, pIter, PDMAUDIOMIXBUF, Node)
+    RTListForEach(&pMixBuf->lstChildren, pIter, PDMAUDIOMIXBUF, Node)
     {
         if (pIter == pMixBuf)
             continue;
-        audioMixBufDbgPrintSingle(pIter, false /* fIsParent */, 1 /* iIdtLevel */);
-    }
-
-    LogFunc(("***************************************************************************************\n"));
+        audioMixBufDbgPrintSingle(pIter, pszFunc, false /* fIsParent */, 1 /* iIdtLevel */);
+    }
 }
 
@@ -1314,7 +1403,6 @@
 void AudioMixBufDbgPrint(PPDMAUDIOMIXBUF pMixBuf)
 {
-    audioMixBufDbgPrintInternal(pMixBuf);
-}
-
+    audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__);
+}
 #endif /* DEBUG */
 
@@ -1454,15 +1542,12 @@
 {
     AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf,      VERR_INVALID_PARAMETER);
     AssertPtrReturn(pvBuf,   VERR_INVALID_POINTER);
-    /* pcbRead is optional. */
-
-    if (!cbBuf)
-    {
-        if (pcRead)
-            *pcRead = 0;
-        return VINF_SUCCESS;
-    }
-
-    uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), pMixBuf->cUsed);
+    /* pcRead is optional. */
+
+    /* Make sure that we at least have space for a full audio sample. */
+    AssertReturn(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), VERR_INVALID_PARAMETER);
+
+    uint32_t cToRead = RT_MIN(pMixBuf->cUsed, AUDIOMIXBUF_B2S(pMixBuf, cbBuf));
 
     AUDMIXBUF_LOG(("%s: cbBuf=%RU32 (%RU32 samples), cToRead=%RU32, fmtSrc=0x%x, fmtDst=0x%x\n",
@@ -1472,5 +1557,5 @@
     {
 #ifdef DEBUG
-        audioMixBufDbgPrintInternal(pMixBuf);
+        audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__);
 #endif
         if (pcRead)
@@ -1491,80 +1576,52 @@
     }
 
-    PPDMAUDIOSAMPLE pSamplesSrc1 = pMixBuf->pSamples + pMixBuf->offRead;
-    uint32_t cLenSrc1 = cToRead;
-
-    PPDMAUDIOSAMPLE pSamplesSrc2 = NULL;
-    uint32_t cLenSrc2 = 0;
-
-    /*
-     * Do we need to wrap around to read all requested data, that is,
-     * starting at the beginning of our circular buffer? This then will
-     * be the optional second part to do.
-     */
-    if ((pMixBuf->offRead + cToRead) > pMixBuf->cSamples)
-    {
-        Assert(pMixBuf->offRead <= pMixBuf->cSamples);
-        cLenSrc1 = pMixBuf->cSamples - pMixBuf->offRead;
-
-        pSamplesSrc2 = pMixBuf->pSamples;
-        Assert(cToRead >= cLenSrc1);
-        cLenSrc2 = RT_MIN(cToRead - cLenSrc1, pMixBuf->cSamples);
-    }
-
-    PDMAUDMIXBUFCONVOPTS convOpts;
-    RT_ZERO(convOpts);
-    /* Note: No volume handling/conversion done in the conversion-to macros (yet). */
-
-    /* Anything to do at all? */
-    int rc = VINF_SUCCESS;
-    if (cLenSrc1)
-    {
-        AssertPtr(pSamplesSrc1);
-
-        convOpts.cSamples = cLenSrc1;
-
-        AUDMIXBUF_LOG(("P1: offRead=%RU32, cToRead=%RU32\n", pMixBuf->offRead, cLenSrc1));
-        pfnConvTo(pvBuf, pSamplesSrc1, &convOpts);
-    }
-
-    /* Second part present? */
-    if (   RT_LIKELY(RT_SUCCESS(rc))
-        && cLenSrc2)
-    {
-        AssertPtr(pSamplesSrc2);
-
-        convOpts.cSamples = cLenSrc2;
-
-        AUDMIXBUF_LOG(("P2: cToRead=%RU32, offWrite=%RU32 (%zu bytes)\n", cLenSrc2, cLenSrc1,
-                       AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1)));
-        pfnConvTo((uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1), pSamplesSrc2, &convOpts);
-    }
-
-    if (RT_SUCCESS(rc))
-    {
+    cToRead = RT_MIN(cToRead, pMixBuf->cSamples - pMixBuf->offRead);
+    if (cToRead)
+    {
+        PDMAUDMIXBUFCONVOPTS convOpts;
+        RT_ZERO(convOpts);
+        convOpts.cSamples = cToRead;
+
+        AUDMIXBUF_LOG(("cToRead=%RU32\n", cToRead));
+
+        pfnConvTo(pvBuf, pMixBuf->pSamples + pMixBuf->offRead, &convOpts);
+
 #ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
         RTFILE fh;
-        rc = RTFileOpen(&fh, AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "mixbuf_readcirc.pcm",
-                        RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
-        if (RT_SUCCESS(rc))
+        int rc2 = RTFileOpen(&fh, AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "mixbuf_readcirc.pcm",
+                             RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+        if (RT_SUCCESS(rc2))
         {
-            RTFileWrite(fh, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1 + cLenSrc2), NULL);
+            RTFileWrite(fh, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cToRead), NULL);
             RTFileClose(fh);
         }
 #endif
         pMixBuf->offRead  = (pMixBuf->offRead + cToRead) % pMixBuf->cSamples;
-        Assert(cToRead <= pMixBuf->cUsed);
-        pMixBuf->cUsed   -= RT_MIN(cToRead, pMixBuf->cUsed);
-
-        if (pcRead)
-            *pcRead = cToRead;
-    }
+        Assert(pMixBuf->cUsed >= cToRead);
+        pMixBuf->cUsed   -= cToRead;
+    }
+
+    if (pcRead)
+        *pcRead = cToRead;
 
 #ifdef DEBUG
-    audioMixBufDbgPrintInternal(pMixBuf);
-#endif
-
-    AUDMIXBUF_LOG(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n", cToRead, AUDIOMIXBUF_S2B(pMixBuf, cToRead), rc));
-    return rc;
+    audioMixBufDbgValidate(pMixBuf);
+#endif
+
+    AUDMIXBUF_LOG(("cRead=%RU32 (%RU32 bytes)\n", cToRead, AUDIOMIXBUF_S2B(pMixBuf, cToRead)));
+    return VINF_SUCCESS;
+}
+
+/**
+ * Returns the current read position of a mixing buffer.
+ *
+ * @returns IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to return position for.
+ */
+uint32_t AudioMixBufReadPos(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, 0);
+
+    return pMixBuf->offRead;
 }
 
@@ -1642,5 +1699,5 @@
     AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
 
-    if (pMixBuf->pParent)
+    if (pMixBuf->pParent) /* IS this a children buffer? */
     {
         AUDMIXBUF_LOG(("%s: Unlinking from parent \"%s\"\n",
@@ -1648,4 +1705,8 @@
 
         RTListNodeRemove(&pMixBuf->Node);
+
+        /* Decrease the paren't children count. */
+        Assert(pMixBuf->pParent->cChildren);
+        pMixBuf->pParent->cChildren--;
 
         /* Make sure to reset the parent mixing buffer each time it gets linked
@@ -1666,7 +1727,12 @@
 
         RTListNodeRemove(&pChild->Node);
+
+        /* Decrease the children count. */
+        Assert(pMixBuf->cChildren);
+        pMixBuf->cChildren--;
     }
 
     Assert(RTListIsEmpty(&pMixBuf->lstChildren));
+    Assert(pMixBuf->cChildren == 0);
 
     AudioMixBufReset(pMixBuf);
@@ -1699,4 +1765,7 @@
 /**
  * Writes audio samples at a specific offset.
+ *
+ * Note that this operation also modifies the current read and write position
+ * to \a offSamples + written samples on success.
  *
  * The audio sample format to be written can be different from the audio format
@@ -1712,32 +1781,23 @@
  */
 int AudioMixBufWriteAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
-                         uint32_t offSamples,
-                         const void *pvBuf, uint32_t cbBuf,
+                         uint32_t offSamples, const void *pvBuf, uint32_t cbBuf,
                          uint32_t *pcWritten)
 {
     AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf,      VERR_INVALID_PARAMETER);
     AssertPtrReturn(pvBuf,   VERR_INVALID_POINTER);
-    /* pcWritten is optional. */
+    /* pcbWritten is optional. */
+
+    if (offSamples >= pMixBuf->cSamples)
+    {
+        if (pcWritten)
+            *pcWritten = 0;
+        return VERR_BUFFER_OVERFLOW;
+    }
 
     /*
      * Adjust cToWrite so we don't overflow our buffers.
      */
-    int rc;
-    uint32_t cToWrite = AUDIOMIXBUF_B2S(pMixBuf, cbBuf);
-    if (offSamples <= pMixBuf->cSamples)
-    {
-        if (offSamples + cToWrite <= pMixBuf->cSamples)
-            rc = VINF_SUCCESS;
-        else
-        {
-            rc = VINF_BUFFER_OVERFLOW;
-            cToWrite = pMixBuf->cSamples - offSamples;
-        }
-    }
-    else
-    {
-        rc = VINF_BUFFER_OVERFLOW;
-        cToWrite = 0;
-    }
+    uint32_t cToWrite = RT_MIN(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), pMixBuf->cSamples - offSamples);
 
 #ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
@@ -1769,4 +1829,6 @@
         pfnConvFrom = &audioMixBufConvFromSilence;
 
+    int rc = VINF_SUCCESS;
+
     uint32_t cWritten;
     if (   pfnConvFrom
@@ -1792,8 +1854,4 @@
     }
 
-#ifdef DEBUG
-    audioMixBufDbgPrintInternal(pMixBuf);
-#endif
-
     AUDMIXBUF_LOG(("%s: offSamples=%RU32, cbBuf=%RU32, cToWrite=%RU32 (%zu bytes), cWritten=%RU32 (%zu bytes), rc=%Rrc\n",
                    pMixBuf->pszName, offSamples, cbBuf,
@@ -1801,6 +1859,19 @@
                    cWritten, AUDIOMIXBUF_S2B(pMixBuf, cWritten), rc));
 
-    if (RT_SUCCESS(rc) && pcWritten)
-        *pcWritten = cWritten;
+    if (RT_SUCCESS(rc))
+    {
+        pMixBuf->offRead  = offSamples % pMixBuf->cSamples;
+        pMixBuf->offWrite = (offSamples + cWritten) % pMixBuf->cSamples;
+        pMixBuf->cUsed    = cWritten;
+        pMixBuf->cMixed   = 0;
+
+#ifdef DEBUG
+        audioMixBufDbgValidate(pMixBuf);
+#endif
+        if (pcWritten)
+            *pcWritten = cWritten;
+    }
+    else
+        AUDMIXBUF_LOG(("%s: Failed with %Rrc\n", pMixBuf->pszName, rc));
 
     return rc;
@@ -1828,7 +1899,7 @@
 /**
  * Writes audio samples of a specific format.
- *
- * @return  IPRT status code, or VERR_BUFFER_OVERFLOW if samples which not have
- *          been processed yet have been overwritten (due to cyclic buffer).
+ * This function might write less data at once than requested.
+ *
+ * @return  IPRT status code, or VERR_BUFFER_OVERFLOW no space is available for writing anymore.
  * @param   pMixBuf                 Pointer to mixing buffer to write to.
  * @param   enmFmt                  Audio format supplied in the buffer.
@@ -1851,20 +1922,9 @@
     }
 
-    PPDMAUDIOMIXBUF pParent = pMixBuf->pParent;
-
-    AUDMIXBUF_LOG(("%s: enmFmt=0x%x, cbBuf=%RU32 (%RU32 samples)\n",
-                   pMixBuf->pszName, enmFmt, cbBuf, AUDIOMIXBUF_B2S(pMixBuf, cbBuf)));
-
-    if (   pParent
-        && pParent->cSamples < pMixBuf->cMixed)
-    {
-        if (pcWritten)
-            *pcWritten = 0;
-
-        AUDMIXBUF_LOG(("%s: Parent buffer '%s' is full\n",
-                       pMixBuf->pszName, pMixBuf->pParent->pszName));
-
-        return VERR_BUFFER_OVERFLOW;
-    }
+    /* Make sure that we at least write a full audio sample. */
+    AssertReturn(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), VERR_INVALID_PARAMETER);
+
+    Assert(pMixBuf->cSamples);
+    AssertPtr(pMixBuf->pSamples);
 
     PFNPDMAUDIOMIXBUFCONVFROM pfnConvFrom = NULL;
@@ -1885,104 +1945,70 @@
     }
 
-    uint32_t cToWrite = AUDIOMIXBUF_B2S(pMixBuf, cbBuf);
-    AssertMsg(cToWrite, ("cToWrite is 0 (cbBuf=%zu)\n", cbBuf));
-
-    PPDMAUDIOSAMPLE pSamplesDst1 = pMixBuf->pSamples + pMixBuf->offWrite;
-    uint32_t cLenDst1 = cToWrite;
-
-    PPDMAUDIOSAMPLE pSamplesDst2 = NULL;
-    uint32_t cLenDst2 = 0;
-
-    uint32_t cOffWrite = pMixBuf->offWrite + cToWrite;
-
-    /*
-     * Do we need to wrap around to write all requested data, that is,
-     * starting at the beginning of our circular buffer? This then will
-     * be the optional second part to do.
-     */
-    if (cOffWrite >= pMixBuf->cSamples)
-    {
+    int rc = VINF_SUCCESS;
+
+    uint32_t cWritten = 0;
+
+    uint32_t cFree = pMixBuf->cSamples - pMixBuf->cUsed;
+    if (cFree)
+    {
+        if ((pMixBuf->cSamples - pMixBuf->offWrite) == 0)
+            pMixBuf->offWrite = 0;
+
+        uint32_t cToWrite = RT_MIN(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), RT_MIN(pMixBuf->cSamples - pMixBuf->offWrite, cFree));
+        Assert(cToWrite);
+
+        PDMAUDMIXBUFCONVOPTS convOpts;
+        RT_ZERO(convOpts);
+
+        convOpts.From.Volume.fMuted = pMixBuf->Volume.fMuted;
+        convOpts.From.Volume.uLeft  = pMixBuf->Volume.uLeft;
+        convOpts.From.Volume.uRight = pMixBuf->Volume.uRight;
+
+        convOpts.cSamples = cToWrite;
+
+        cWritten = pfnConvFrom(pMixBuf->pSamples + pMixBuf->offWrite,
+                               pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cToWrite), &convOpts);
+        Assert(cWritten == cToWrite);
+
+#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
+        RTFILE fh;
+        RTFileOpen(&fh, AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "mixbuf_writecirc_ex.pcm",
+                   RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+        RTFileWrite(fh, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cToWrite), NULL);
+        RTFileClose(fh);
+#endif
+        pMixBuf->cUsed   += cWritten;
+        Assert(pMixBuf->cUsed <= pMixBuf->cSamples);
+
+        pMixBuf->offWrite = (pMixBuf->offWrite + cWritten) % pMixBuf->cSamples;
         Assert(pMixBuf->offWrite <= pMixBuf->cSamples);
-        cLenDst1 = pMixBuf->cSamples - pMixBuf->offWrite;
-
-        pSamplesDst2 = pMixBuf->pSamples;
-        Assert(cToWrite >= cLenDst1);
-        cLenDst2 = RT_MIN(cToWrite - cLenDst1, pMixBuf->cSamples);
-
-        /* Save new read offset. */
-        cOffWrite = cLenDst2;
-    }
-
-#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
-    RTFILE fh;
-    RTFileOpen(&fh, AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA_PATH "mixbuf_writecirc_ex.pcm",
-               RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
-#endif
-
-    uint32_t cWrittenTotal = 0;
-
-    PDMAUDMIXBUFCONVOPTS convOpts;
-    convOpts.From.Volume.fMuted = pMixBuf->Volume.fMuted;
-    convOpts.From.Volume.uLeft  = pMixBuf->Volume.uLeft;
-    convOpts.From.Volume.uRight = pMixBuf->Volume.uRight;
-
-    /* Anything to do at all? */
-    if (cLenDst1)
-    {
-        convOpts.cSamples = cLenDst1;
-        cWrittenTotal = pfnConvFrom(pSamplesDst1, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), &convOpts);
-        Assert(cWrittenTotal == cLenDst1);
-
-#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
-        RTFileWrite(fh, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), NULL);
-#endif
-    }
-
-    /* Second part present? */
-    if (cLenDst2)
-    {
-        AssertPtr(pSamplesDst2);
-
-        convOpts.cSamples = cLenDst2;
-        cWrittenTotal += pfnConvFrom(pSamplesDst2,
-                                     (uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenDst1),
-                                     cbBuf - AUDIOMIXBUF_S2B(pMixBuf, cLenDst1),
-                                     &convOpts);
-        Assert(cWrittenTotal == cLenDst1 + cLenDst2);
-
-#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
-        RTFileWrite(fh, (uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenDst1),
-                    cbBuf - AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), NULL);
-#endif
-    }
-
-#ifdef AUDIOMIXBUF_DEBUG_DUMP_PCM_DATA
-    RTFileClose(fh);
-#endif
-
-    pMixBuf->offWrite = (pMixBuf->offWrite + cWrittenTotal) % pMixBuf->cSamples;
-    pMixBuf->cUsed   += cWrittenTotal;
-
-    int rc = VINF_SUCCESS;
-
-    if (pMixBuf->cUsed > pMixBuf->cSamples)
-    {
-        AUDMIXBUF_LOG(("Warning: %RU32 unprocessed samples overwritten\n", pMixBuf->cUsed - pMixBuf->cSamples));
-        pMixBuf->cUsed = pMixBuf->cSamples;
-
+    }
+    else
         rc = VERR_BUFFER_OVERFLOW;
-    }
+
+#ifdef DEBUG
+    audioMixBufDbgPrintInternal(pMixBuf, __FUNCTION__);
+    audioMixBufDbgValidate(pMixBuf);
+#endif
 
     if (pcWritten)
-        *pcWritten = cWrittenTotal;
-
-#ifdef DEBUG
-    audioMixBufDbgPrintInternal(pMixBuf);
-#endif
-
-    AUDMIXBUF_LOG(("%s: offWrite=%RU32, cLenDst1=%RU32, cLenDst2=%RU32, cTotal=%RU32 (%zu bytes), rc=%Rrc\n",
-                   pMixBuf->pszName, pMixBuf->offWrite, cLenDst1, cLenDst2, cLenDst1 + cLenDst2,
-                   AUDIOMIXBUF_S2B(pMixBuf, cLenDst1 + cLenDst2), rc));
+        *pcWritten = cWritten;
+
+    AUDMIXBUF_LOG(("%s: enmFmt=0x%x, cbBuf=%RU32 (%RU32 samples), cWritten=%RU32, rc=%Rrc\n",
+                   pMixBuf->pszName, enmFmt, cbBuf, AUDIOMIXBUF_B2S(pMixBuf, cbBuf), cWritten, rc));
     return rc;
 }
 
+/**
+ * Returns the current write position of a mixing buffer.
+ *
+ * @returns IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to return position for.
+ */
+uint32_t AudioMixBufWritePos(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, 0);
+
+    return pMixBuf->offWrite;
+}
+
Index: trunk/src/VBox/Devices/Audio/AudioMixBuffer.h
===================================================================
--- trunk/src/VBox/Devices/Audio/AudioMixBuffer.h	(revision 67363)
+++ trunk/src/VBox/Devices/Audio/AudioMixBuffer.h	(revision 67365)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2014-2016 Oracle Corporation
+ * Copyright (C) 2014-2017 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -59,5 +59,6 @@
 int AudioMixBufLinkTo(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOMIXBUF pParent);
 uint32_t AudioMixBufLive(PPDMAUDIOMIXBUF pMixBuf);
-int AudioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples, uint32_t *pcProcessed);
+int AudioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSrcSamples, uint32_t *pcSrcMixed);
+int AudioMixBufMixToParentEx(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSrcOffset, uint32_t cSrcSamples, uint32_t *pcSrcMixed);
 int AudioMixBufPeek(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToRead, PPDMAUDIOSAMPLE paSampleBuf, uint32_t cSampleBuf, uint32_t *pcSamplesRead);
 int AudioMixBufPeekMutable(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToRead, PPDMAUDIOSAMPLE *ppvSamples, uint32_t *pcSamplesRead);
@@ -67,4 +68,5 @@
 int AudioMixBufReadCirc(PPDMAUDIOMIXBUF pMixBuf, void *pvBuf, uint32_t cbBuf, uint32_t *pcRead);
 int AudioMixBufReadCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt, void *pvBuf, uint32_t cbBuf, uint32_t *pcRead);
+uint32_t AudioMixBufReadPos(PPDMAUDIOMIXBUF pMixBuf);
 void AudioMixBufReset(PPDMAUDIOMIXBUF pMixBuf);
 void AudioMixBufSetVolume(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOVOLUME pVol);
@@ -76,4 +78,5 @@
 int AudioMixBufWriteCirc(PPDMAUDIOMIXBUF pMixBuf, const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten);
 int AudioMixBufWriteCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt, const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten);
+uint32_t AudioMixBufWritePos(PPDMAUDIOMIXBUF pMixBuf);
 
 #ifdef DEBUG
