Index: trunk/src/VBox/Devices/Audio/DevHDA.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/DevHDA.cpp	(revision 88097)
+++ trunk/src/VBox/Devices/Audio/DevHDA.cpp	(revision 88112)
@@ -5200,5 +5200,7 @@
                                "The stream frequency.",                     "Stream%u/Cfg/Hz", idxStream);
         PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.Cfg.Props.cChannels, STAMTYPE_U8, STAMVISIBILITY_USED, STAMUNIT_BYTES,
-                               "The number of channels.",                   "Stream%u/Cfg/Channels", idxStream);
+                               "The number of channels.",                   "Stream%u/Cfg/Channels-Host", idxStream);
+        PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aStreams[idxStream].State.Mapping.GuestProps.cChannels, STAMTYPE_U8, STAMVISIBILITY_USED, STAMUNIT_BYTES,
+                               "The number of channels.",                   "Stream%u/Cfg/Channels-Guest", idxStream);
         PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStreams[idxStream].State.Cfg.Props.cbSample, STAMTYPE_U8, STAMVISIBILITY_USED, STAMUNIT_BYTES,
                                "The size of a sample (per channel).",       "Stream%u/Cfg/cbSample", idxStream);
Index: trunk/src/VBox/Devices/Audio/HDAStream.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/HDAStream.cpp	(revision 88097)
+++ trunk/src/VBox/Devices/Audio/HDAStream.cpp	(revision 88112)
@@ -265,6 +265,6 @@
     }
 
-    PDMAUDIOPCMPROPS Props;
-    int rc = hdaR3SDFMTToPCMProps(u16FMT, &Props);
+    PDMAUDIOPCMPROPS HostProps;
+    int rc = hdaR3SDFMTToPCMProps(u16FMT, &HostProps);
     if (RT_FAILURE(rc))
     {
@@ -272,7 +272,4 @@
         return rc;
     }
-
-    /* Reset (any former) stream map. */
-    hdaR3StreamMapReset(&pStreamR3->State.Mapping);
 
     /*
@@ -284,28 +281,20 @@
      * number of channels in a single audio stream.
      */
-    rc = hdaR3StreamMapInit(&pStreamR3->State.Mapping, &Props);
+    /** @todo r=bird: this is not done at the wrong time.  We don't have the host
+     * output side set up yet, so we cannot really do proper mapping setup.
+     * However, we really need this further down when we configure the internal DMA
+     * buffer size.  For now we just assume it's all stereo on the host side.
+     * This is not compatible with microphone support. */
+# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
+#  error "Implement me!"
+# endif
+    rc = hdaR3StreamMapInit(&pStreamR3->State.Mapping, 2 /*cHostChannels*/, &HostProps);
     AssertRCReturn(rc, rc);
 
-    ASSERT_GUEST_LOGREL_MSG_RETURN(   pStreamR3->State.Mapping.cbFrameSize > 0
-                                   && u32CBL % pStreamR3->State.Mapping.cbFrameSize == 0,
+    ASSERT_GUEST_LOGREL_MSG_RETURN(   pStreamR3->State.Mapping.cbGuestFrame > 0
+                                   && u32CBL % pStreamR3->State.Mapping.cbGuestFrame == 0,
                                    ("CBL for stream #%RU8 does not align to frame size (u32CBL=%u cbFrameSize=%u)\n",
-                                    uSD, u32CBL, pStreamR3->State.Mapping.cbFrameSize),
+                                    uSD, u32CBL, pStreamR3->State.Mapping.cbGuestFrame),
                                    VERR_INVALID_PARAMETER);
-
-#ifndef VBOX_WITH_AUDIO_HDA_51_SURROUND
-    if (Props.cChannels > 2)
-    {
-        /*
-         * When not running with surround support enabled, override the audio channel count
-         * with stereo (2) channels so that we at least can properly work with those.
-         *
-         * Note: This also involves dealing with surround setups the guest might has set up for us.
-         */
-        LogRelMax(32, ("HDA: Warning: More than stereo (2) channels are not supported (%RU8 requested), falling back to stereo channels for stream #%RU8\n",
-                       Props.cChannels, uSD));
-        Props.cChannels = 2;
-        Props.cShift    = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(Props.cbSample, Props.cChannels);
-    }
-#endif
 
     /* Make sure the guest behaves regarding the stream's FIFO. */
@@ -325,5 +314,5 @@
 
     PPDMAUDIOSTREAMCFG pCfg = &pStreamShared->State.Cfg;
-    pCfg->Props = Props;
+    pCfg->Props = HostProps;
 
     /* Set the stream's direction. */
@@ -344,5 +333,5 @@
 
         case PDMAUDIODIR_OUT:
-            /* Destination(s) will be set in hdaAddStreamOut(),
+            /* Destination(s) will be set in hdaR3AddStreamOut(),
              * based on the channels / stream layout. */
             break;
@@ -355,5 +344,5 @@
     LogRel2(("HDA: Stream #%RU8 DMA @ 0x%x (%RU32 bytes = %RU64ms total)\n",
              uSD, pStreamShared->u64BDLBase, pStreamShared->u32CBL,
-             PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.PCMProps, pStreamShared->u32CBL)));
+             PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->u32CBL)));
 
     /* Figure out how many transfer fragments we're going to use for this stream. */
@@ -396,10 +385,10 @@
 #ifdef VBOX_WITH_INTEL_HDA
             /* Intel ICH / PCH: 1 frame. */
-            if (BDLE.Desc.u32BufSize == (uint32_t)(1 * pStreamR3->State.Mapping.cbFrameSize))
+            if (BDLE.Desc.u32BufSize == (uint32_t)(1 * pStreamR3->State.Mapping.cbGuestFrame))
             {
                 cfPosAdjust = 1;
             }
             /* Intel Baytrail / Braswell: 32 frames. */
-            else if (BDLE.Desc.u32BufSize == (uint32_t)(32 * pStreamR3->State.Mapping.cbFrameSize))
+            else if (BDLE.Desc.u32BufSize == (uint32_t)(32 * pStreamR3->State.Mapping.cbGuestFrame))
             {
                 cfPosAdjust = 32;
@@ -422,5 +411,5 @@
              * position adjustment.
              */
-            if ((cfPosAdjust * pStreamR3->State.Mapping.cbFrameSize) == BDLE.Desc.u32BufSize)
+            if ((cfPosAdjust * pStreamR3->State.Mapping.cbGuestFrame) == BDLE.Desc.u32BufSize)
                 cTransferFragments--;
 
@@ -457,5 +446,5 @@
 
     /* Audio data per second the stream needs. */
-    const uint32_t cbDataPerSec = PDMAudioPropsMilliToBytes(&pStreamR3->State.Mapping.PCMProps, RT_MS_1SEC);
+    const uint32_t cbDataPerSec = PDMAudioPropsMilliToBytes(&pStreamR3->State.Mapping.GuestProps, RT_MS_1SEC);
 
     /* This is used to indicate whether we're done or should the uTimerIoHz as fallback. */
@@ -469,10 +458,10 @@
     unsigned uTransferHz = pStreamShared->State.uTimerIoHz;
 
-    LogRel2(("HDA: Stream #%RU8 needs %RU32 bytes/s audio data\n", uSD, cbDataPerSec));
+    LogRel2(("HDA: Stream #%RU8 needs %RU32 bytes/s audio data (from the guest).\n", uSD, cbDataPerSec));
 
     if (pThis->fTransferHeuristicsEnabled) /* Are data transfer heuristics enabled? */
     {
         /* Don't take frames (as bytes) into account which are part of the position adjustment. */
-        uint32_t cbTransferHeuristicsPosAdjust = pStreamShared->State.cfPosAdjustDefault * pStreamR3->State.Mapping.cbFrameSize;
+        uint32_t cbTransferHeuristicsPosAdjust = pStreamShared->State.cfPosAdjustDefault * pStreamR3->State.Mapping.cbGuestFrame;
         uint32_t cbTransferHeuristics          = 0;
         uint32_t cbTransferHeuristicsCur       = 0;
@@ -533,10 +522,15 @@
         if (cbTransferHeuristics >= 8)
         {
-            ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pStreamR3->State.Mapping.PCMProps, cbTransferHeuristics),
+            ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pStreamR3->State.Mapping.GuestProps, cbTransferHeuristics),
                                     ("We arrived at a misaligned transfer size for stream #%RU8: %#x (%u)\n",
                                      uSD, cbTransferHeuristics, cbTransferHeuristics));
 
             uint64_t const cTimerTicksPerSec = PDMDevHlpTimerGetFreq(pDevIns, pStreamShared->hTimer);
-            uint64_t const cbTransferPerSec  = RT_MAX(pStreamR3->State.Mapping.PCMProps.uHz * pStreamR3->State.Mapping.cbFrameSize,
+
+            /* Convert the heuristics value to host side bytes and that's what we're calculating. */
+            cbTransferHeuristics = PDMAudioPropsBytesToFrames(&pStreamR3->State.Mapping.GuestProps, cbTransferHeuristics);
+            cbTransferHeuristics = PDMAudioPropsFramesToBytes(&pCfg->Props, cbTransferHeuristics);
+
+            uint64_t const cbTransferPerSec  = RT_MAX(PDMAudioPropsFramesToBytes(&pCfg->Props, pCfg->Props.uHz),
                                                       4096 /* zero div prevention: min is 6kHz, picked 4k in case I'm mistaken */);
 
@@ -545,10 +539,10 @@
                halve the buffer till we get there. */
             while (cbTransferHeuristics > 1024 && cbTransferHeuristics > cbTransferPerSec / 4)
-                cbTransferHeuristics = PDMAudioPropsFloorBytesToFrame(&pStreamR3->State.Mapping.PCMProps, cbTransferHeuristics / 2);
+                cbTransferHeuristics = PDMAudioPropsFloorBytesToFrame(&pCfg->Props, cbTransferHeuristics / 2);
 
             /* Set the transfer size per timer callout. (No chunking, so same.) */
             pStreamShared->State.cbTransferSize  = cbTransferHeuristics;
             pStreamShared->State.cbTransferChunk = cbTransferHeuristics;
-            ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pStreamR3->State.Mapping.PCMProps, cbTransferHeuristics),
+            ASSERT_GUEST_LOGREL_MSG(PDMAudioPropsIsSizeAligned(&pCfg->Props, cbTransferHeuristics),
                                     ("We arrived at a misaligned transfer size for stream #%RU8: %#x (%u)\n",
                                      uSD, cbTransferHeuristics, cbTransferHeuristics));
@@ -594,5 +588,5 @@
                                      uTransferHz = HDA_TIMER_HZ_DEFAULT);
 
-        pStreamShared->State.cbTransferSize = (pStreamR3->State.Mapping.PCMProps.uHz * pStreamR3->State.Mapping.cbFrameSize)
+        pStreamShared->State.cbTransferSize = (pStreamR3->State.Mapping.GuestProps.uHz * pStreamR3->State.Mapping.cbGuestFrame)
                                             / uTransferHz;
         ASSERT_GUEST_LOGREL_MSG_STMT(pStreamShared->State.cbTransferSize,
@@ -641,8 +635,8 @@
 
                 LogRel2(("HDA: Stream #%RU8 is using %uHz I/O timer (%RU64 virtual ticks / Hz), stream Hz=%RU32, cTicksPerByte=%RU64, cTransferTicks=%RU64 -> cbTransferChunk=%RU32 (%RU64ms), cbTransferSize=%RU32 (%RU64ms)\n",
-                         uSD, pStreamShared->State.uTimerIoHz, (uint64_t)cTicksPerHz, pStreamR3->State.Mapping.PCMProps.uHz,
+                         uSD, pStreamShared->State.uTimerIoHz, (uint64_t)cTicksPerHz, pStreamR3->State.Mapping.GuestProps.uHz,
                          pStreamShared->State.cTicksPerByte, pStreamShared->State.cTransferTicks,
-                         pStreamShared->State.cbTransferChunk, PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.PCMProps, pStreamShared->State.cbTransferChunk),
-                         pStreamShared->State.cbTransferSize,  PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.PCMProps, pStreamShared->State.cbTransferSize)));
+                         pStreamShared->State.cbTransferChunk, PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->State.cbTransferChunk),
+                         pStreamShared->State.cbTransferSize,  PDMAudioPropsBytesToMilli(&pStreamR3->State.Mapping.GuestProps, pStreamShared->State.cbTransferSize)));
             }
         }
@@ -979,13 +973,15 @@
  * @param   pDevIns             The device instance.
  * @param   pThis               The shared HDA device state.
- * @param   uToAdd              Position (in bytes) to add to the current read / write position.
- */
-void hdaR3StreamSetPositionAdd(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t uToAdd)
-{
-    if (!uToAdd) /* No need to update anything if 0. */
-        return;
-
-    hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis,
-                              (HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) + uToAdd) % pStreamShared->u32CBL);
+ * @param   cbToAdd             Position (in bytes) to add to the current read / write position.
+ */
+void hdaR3StreamSetPositionAdd(PHDASTREAM pStreamShared, PPDMDEVINS pDevIns, PHDASTATE pThis, uint32_t cbToAdd)
+{
+    if (cbToAdd) /* No need to update anything if 0. */
+    {
+        uint32_t const uCBL = pStreamShared->u32CBL;
+        if (uCBL) /* paranoia */
+            hdaR3StreamSetPositionAbs(pStreamShared, pDevIns, pThis,
+                                      (HDA_STREAM_REG(pThis, LPIB, pStreamShared->u8SD) + cbToAdd) % uCBL);
+    }
 }
 
@@ -1296,5 +1292,5 @@
          * make sure that we process them first as a whole. */
         if (pStreamShared->State.cfPosAdjustLeft)
-            cbChunk = RT_MIN(cbChunk, (uint32_t)pStreamShared->State.cfPosAdjustLeft * pStreamR3->State.Mapping.cbFrameSize);
+            cbChunk = RT_MIN(cbChunk, (uint32_t)pStreamShared->State.cfPosAdjustLeft * pStreamR3->State.Mapping.cbGuestFrame);
 
         if (!cbChunk)
@@ -1368,5 +1364,5 @@
                  * Only macOS guests need the frame extraction branch below at the moment AFAIK.
                  */
-                if (pStreamR3->State.Mapping.cbFrameSize == HDA_FRAME_SIZE_DEFAULT)
+                if (pStreamR3->State.Mapping.cbGuestFrame == HDA_FRAME_SIZE_DEFAULT)
                 {
                     uint32_t cbDMARead = 0;
@@ -1410,5 +1406,5 @@
                     for (unsigned m = 0; m < pStreamR3->State.Mapping.cMappings; m++)
                     {
-                        const uint32_t cbFrame  = pStreamR3->State.Mapping.cbFrameSize;
+                        const uint32_t cbFrame  = pStreamR3->State.Mapping.cbGuestFrame;
 
                         Assert(cbFree >= cbDMA);
@@ -1546,5 +1542,5 @@
                            + hdaR3StreamPeriodFramesToWalClk(pPeriod,
                                                                pBDLE->Desc.u32BufSize
-                                                             / pStreamR3->State.Mapping.cbFrameSize),
+                                                             / pStreamR3->State.Mapping.cbGuestFrame),
                            false /* fForce */);
 
@@ -1619,5 +1615,5 @@
         /* Do the position adjustment accounting. */
         pStreamShared->State.cfPosAdjustLeft -=
-            RT_MIN(pStreamShared->State.cfPosAdjustLeft, cbDMA / pStreamR3->State.Mapping.cbFrameSize);
+            RT_MIN(pStreamShared->State.cfPosAdjustLeft, cbDMA / pStreamR3->State.Mapping.cbGuestFrame);
 
         if (RT_FAILURE(rc))
@@ -1636,5 +1632,5 @@
     if (pStreamShared->State.cfPosAdjustLeft == 0)
     {
-        hdaR3StreamPeriodInc(pPeriod, RT_MIN(cbProcessed / pStreamR3->State.Mapping.cbFrameSize,
+        hdaR3StreamPeriodInc(pPeriod, RT_MIN(cbProcessed / pStreamR3->State.Mapping.cbGuestFrame,
                                              hdaR3StreamPeriodGetRemainingFrames(pPeriod)));
     }
@@ -1659,5 +1655,5 @@
                                                       + hdaR3StreamPeriodFramesToWalClk(pPeriod,
                                                                                           cbProcessed
-                                                                                        / pStreamR3->State.Mapping.cbFrameSize),
+                                                                                        / pStreamR3->State.Mapping.cbGuestFrame),
                                                       hdaR3WalClkGetMax(pThis, pThisCC)),
                                                false /* fForce */);
@@ -1700,15 +1696,19 @@
  * @param   pStreamShared       HDA stream to update (shared).
  * @param   pStreamR3           HDA stream to update (ring-3).
- * @param   cbToProcessMax      How much data (in bytes) to process as maximum.
- *                              Caller should already have made sure this is at
- *                              least one
+ * @param   cbToProduce         The max amount of data to produce (i.e. put into
+ *                              the circular buffer).  (Caller should already
+ *                              have made sure this is at least the size of one
+ *                              DMA timer period, so this function doesn't need
+ *                              to do any extra underflow handling.)
+ * @param   tsNowNs             The current RTTimeNano() value.
+ *
  * @remarks Caller owns the stream lock.
  */
 static int hdaR3StreamDoDmaOutput(PPDMDEVINS pDevIns, PHDASTATE pThis, PHDASTATER3 pThisCC, PHDASTREAM pStreamShared,
-                                  PHDASTREAMR3 pStreamR3, uint32_t cbToProcessMax, uint64_t tsNowNs)
+                                  PHDASTREAMR3 pStreamR3, uint32_t cbToProduce, uint64_t tsNowNs)
 {
     PHDASTREAMPERIOD const  pPeriod = &pStreamShared->State.Period;
     uint8_t const           uSD     = pStreamShared->u8SD;
-    LogFlowFunc(("ENTER - #%u cbToProcessMax=%#x\n", uSD, cbToProcessMax));
+    LogFlowFunc(("ENTER - #%u cbToProduce=%#x\n", uSD, cbToProduce));
 
 
@@ -1778,9 +1778,31 @@
      * of buffer available.   We will not transfer more than that.
      */
+    uint8_t cSuppressIocs = 0;
     Assert(pStreamShared->State.cbTransferChunk == pStreamShared->State.cbTransferSize);
-    Assert(cbToProcessMax >= pStreamShared->State.cbTransferSize);
-    if (cbToProcessMax > pStreamShared->State.cbTransferSize)
-        cbToProcessMax = pStreamShared->State.cbTransferSize;
-    uint32_t cbLeft = cbToProcessMax;
+    if (!pStreamShared->State.cfPosAdjustLeft)
+    {
+        Assert(cbToProduce >= pStreamShared->State.cbTransferSize);
+        if (cbToProduce > pStreamShared->State.cbTransferSize)
+            cbToProduce = pStreamShared->State.cbTransferSize;
+    }
+    else
+    {
+        /* We currently process the position adjustment BLDE0 and the whole BLDE1
+           in one DMA timer callout, ignoring BLDE0.IOC. */
+        /** @todo Process the tiny BLDE0 seperatly. */
+        uint32_t const cbPosAdjust = PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props,
+                                                                pStreamShared->State.cfPosAdjustLeft);
+        Assert(cbToProduce >= pStreamShared->State.cbTransferSize + cbPosAdjust);
+        if (cbToProduce >= pStreamShared->State.cbTransferSize + cbPosAdjust)
+        {
+            cbToProduce = pStreamShared->State.cbTransferSize + cbPosAdjust;
+            pStreamShared->State.cfPosAdjustLeft = 0;
+            cSuppressIocs = pBDLE->Desc.fFlags & HDA_BDLE_F_IOC ? 1 : 0;
+        }
+        else
+            pStreamShared->State.cfPosAdjustLeft -= PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props, cbToProduce);
+    }
+    uint32_t cbLeft = cbToProduce;
+    Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
 
     /*
@@ -1796,5 +1818,6 @@
      *        Unless the buffers are really small, this is stuff that won't happen
      *        on real hardware.  */
-    /** @todo Disallow non-heuristics approach!  It only complicates the code. */
+    /** @todo Disallow non-heuristics approach!  It only complicates the code
+     *        and messes with the guest if we cover more than one IOC! */
     bool fInterruptSent = false;
 
@@ -1807,201 +1830,155 @@
     HDA_STREAM_REG(pThis, STS, uSD) |= HDA_SDSTS_FIFORDY;
 
-    while (cbLeft)
-    {
-        /* Limit the chunk to the stream's FIFO size and what's left to process. */
-        uint32_t cbChunk = RT_MIN(cbLeft, pStreamShared->u8FIFOS);
-
-        /* Limit the chunk to the remaining data of the current BDLE. */
-        cbChunk = RT_MIN(cbChunk, pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff);
-
-        /* If there are position adjustment frames left to be processed,
-         * make sure that we process them first as a whole. */
-        if (pStreamShared->State.cfPosAdjustLeft)
-            cbChunk = RT_MIN(cbChunk, (uint32_t)pStreamShared->State.cfPosAdjustLeft * pStreamR3->State.Mapping.cbFrameSize);
-
-        /** @todo wtf is this for? Move on to the next BDLE! */
-        if (!cbChunk)
-            break;
-
-        uint32_t   cbDMA    = 0;
-        PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
-        uint8_t   *pabFIFO  = pStreamShared->abFIFO;
-
+    /*
+     *
+     * The DMA copy loop.
+     *
+     */
+    uint8_t    abBounce[4096 + 128];    /* Most guest does at most 4KB BDLE. So, 4KB + space for a partial frame to reduce loops. */
+    uint32_t   cbBounce = 0;            /* in case of incomplete frames between buffer segments */
+    PRTCIRCBUF pCircBuf = pStreamR3->State.pCircBuf;
+    while (cbLeft > 0)
+    {
         STAM_PROFILE_START(&pThis->StatOut, a);
 
-        rc = hdaR3DMARead(pDevIns, pThis, pStreamShared, pStreamR3, pabFIFO, cbChunk, &cbDMA /* pcbRead */);
-        if (RT_SUCCESS(rc))
+        /*
+         * Figure out how much we can read & write in this iteration.
+         */
+        uint32_t cbChunk = pBDLE->Desc.u32BufSize - pBDLE->State.u32BufOff;
+        AssertStmt(cbChunk <= pBDLE->Desc.u32BufSize, cbChunk = 0);
+
+        /* Need to diverge if the frame format differs.  */
+        if (   !pStreamR3->State.Mapping.fMappingNeeded
+            /** @todo && pStreamShared->State.fFrameAlignedBuffers */)
         {
-            /** @todo wtf? Caller passed us the RTCircBufFree value via cbToProcessMax.   */
-            const uint32_t cbFree = (uint32_t)RTCircBufFree(pCircBuf);
+            if (cbChunk <= cbLeft)
+            { /* very likely */ }
+            else
+                cbChunk = cbLeft;
 
             /*
-             * Most guests don't use different stream frame sizes than
-             * the default one, so save a bit of CPU time and don't go into
-             * the frame extraction code below.
-             *
-             * Only macOS guests need the frame extraction branch below at the moment AFAIK.
+             * Read the guest data directly into the internal DMA buffer.
              */
-            if (pStreamR3->State.Mapping.cbFrameSize == HDA_FRAME_SIZE_DEFAULT)
+            RTGCPHYS GCPhys = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
+            while (cbChunk > 0)
             {
-                uint32_t cbDMARead = 0;
-                uint32_t cbDMALeft = RT_MIN(cbDMA, cbFree);
-
-                while (cbDMALeft)
-                {
-                    void *pvBuf; size_t cbBuf;
-                    RTCircBufAcquireWriteBlock(pCircBuf, cbDMALeft, &pvBuf, &cbBuf);
-
-                    if (cbBuf)
-                    {
-                        memcpy(pvBuf, pabFIFO + cbDMARead, cbBuf);
-                        cbDMARead += (uint32_t)cbBuf;
-                        cbDMALeft -= (uint32_t)cbBuf;
-#ifdef VBOX_WITH_DTRACE
-                        VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbBuf, pStreamR3->State.offWrite);
-#endif
-                        pStreamR3->State.offWrite += cbBuf;
-                    }
-
-                    RTCircBufReleaseWriteBlock(pCircBuf, cbBuf);
-                }
-            }
-            else
-            {
-                /*
-                 * The following code extracts the required audio stream (channel) data
-                 * of non-interleaved *and* interleaved audio streams.
-                 *
-                 * We by default only support 2 channels with 16-bit samples (HDA_FRAME_SIZE),
-                 * but an HDA audio stream can have interleaved audio data of multiple audio
-                 * channels in such a single stream ("AA,AA,AA vs. AA,BB,AA,BB").
-                 *
-                 * So take this into account by just handling the first channel in such a stream ("A")
-                 * and just discard the other channel's data.
-                 *
-                 * I know, the following code is horribly slow, but seems to work for now.
-                 */
-                /** @todo Optimize channel data extraction! Use some SSE(3) / intrinsics? */
-                for (unsigned m = 0; m < pStreamR3->State.Mapping.cMappings; m++)
-                {
-                    const uint32_t cbFrame  = pStreamR3->State.Mapping.cbFrameSize;
-
-                    Assert(cbFree >= cbDMA);
-
-                    PPDMAUDIOSTREAMMAP pMap = &pStreamR3->State.Mapping.paMappings[m];
-                    AssertPtr(pMap);
-
-                    Log3Func(("Mapping #%u: Start (cbDMA=%RU32, cbFrame=%RU32, offNext=%RU32)\n",
-                              m, cbDMA, cbFrame, pMap->offNext));
-
-
-                    /* Skip the current DMA chunk if the chunk is smaller than what the current stream mapping needs to read
-                     * the next associated frame (pointed to at pMap->cbOff).
-                     *
-                     * This can happen if the guest did not come up with enough data within a certain time period, especially
-                     * when using multi-channel speaker (> 2 channels [stereo]) setups. */
-                    if (pMap->offNext > cbChunk)
-                    {
-                        Log2Func(("Mapping #%u: Skipped (cbChunk=%RU32, cbMapOff=%RU32)\n", m, cbChunk, pMap->offNext));
-                        continue;
-                    }
-
-                    uint8_t *pbSrcBuf = pabFIFO;
-                    size_t cbSrcOff   = pMap->offNext;
-
-                    for (unsigned i = 0; i < cbDMA / cbFrame; i++)
-                    {
-                        void *pvDstBuf; size_t cbDstBuf;
-                        RTCircBufAcquireWriteBlock(pCircBuf, pMap->cbStep, &pvDstBuf, &cbDstBuf);
-
-                        Assert(cbDstBuf >= pMap->cbStep);
-
-                        if (cbDstBuf)
-                        {
-                            Log3Func(("Mapping #%u: Frame #%02u:    cbStep=%u, offFirst=%u, offNext=%u, cbDstBuf=%u, cbSrcOff=%u\n",
-                                      m, i, pMap->cbStep, pMap->offFirst, pMap->offNext, cbDstBuf, cbSrcOff));
-
-                            memcpy(pvDstBuf, pbSrcBuf + cbSrcOff, cbDstBuf);
-
-#if 0 /* Too slow, even for release builds, so disabled it. */
-                            if (pStreamR3->Dbg.Runtime.fEnabled)
-                                DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMAMapped, pvDstBuf, cbDstBuf,
-                                                     0 /* fFlags */);
-#endif
-                            Assert(cbSrcOff <= cbDMA);
-                            if (cbSrcOff + cbFrame + pMap->offFirst<= cbDMA)
-                                cbSrcOff += cbFrame + pMap->offFirst;
-
-#ifdef VBOX_WITH_DTRACE
-                            VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbDstBuf, pStreamR3->State.offWrite);
-#endif
-                            Log3Func(("Mapping #%u: Frame #%02u:    -> cbSrcOff=%zu\n", m, i, cbSrcOff));
-                            pStreamR3->State.offWrite += cbDstBuf;
-                        }
-
-                        RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
-                    }
-
-                    Log3Func(("Mapping #%u: End cbSize=%u, cbDMA=%RU32, cbSrcOff=%zu\n",
-                              m, pMap->cbStep, cbDMA, cbSrcOff));
-
-                    Assert(cbSrcOff <= cbDMA);
-
-                    const uint32_t cbSrcLeft = cbDMA - (uint32_t)cbSrcOff;
-                    if (cbSrcLeft)
-                    {
-                        Log3Func(("Mapping #%u: cbSrcLeft=%RU32\n", m, cbSrcLeft));
-
-                        if (cbSrcLeft >= pMap->cbStep)
-                        {
-                            void *pvDstBuf; size_t cbDstBuf;
-                            RTCircBufAcquireWriteBlock(pCircBuf, pMap->cbStep, &pvDstBuf, &cbDstBuf);
-
-                            Assert(cbDstBuf >= pMap->cbStep);
-
-                            if (cbDstBuf)
-                            {
-                                memcpy(pvDstBuf, pbSrcBuf + cbSrcOff, cbDstBuf);
-#ifdef VBOX_WITH_DTRACE
-                                VBOXDD_HDA_STREAM_DMA_OUT((uint32_t)uSD, (uint32_t)cbDstBuf, pStreamR3->State.offWrite);
-#endif
-                                pStreamR3->State.offWrite += cbDstBuf;
-                            }
-
-                            RTCircBufReleaseWriteBlock(pCircBuf, cbDstBuf);
-                        }
-
-                        Assert(pMap->cbFrame >= cbSrcLeft);
-                        pMap->offNext = pMap->cbFrame - cbSrcLeft;
-                    }
-                    else
-                        pMap->offNext = 0;
-
-                    Log3Func(("Mapping #%u finish (cbSrcOff=%zu, offNext=%zu)\n", m, cbSrcOff, pMap->offNext));
-                }
+                /* Grab internal DMA buffer space and read into it. */
+                void  *pvBufDst;
+                size_t cbBufDst;
+                RTCircBufAcquireWriteBlock(pCircBuf, cbChunk, &pvBufDst, &cbBufDst);
+                AssertBreakStmt(cbBufDst, RTCircBufReleaseWriteBlock(pCircBuf, 0));
+
+                int rc2 = PDMDevHlpPhysRead(pDevIns, GCPhys, pvBufDst, cbBufDst);
+                AssertRC(rc2);
+
+# ifdef HDA_DEBUG_SILENCE
+                fix me if relevant;
+# endif
+                if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
+                { /* likely */ }
+                else
+                    DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pvBufDst, cbBufDst, 0 /* fFlags */);
+
+                pStreamR3->State.offWrite += cbBufDst;
+                RTCircBufReleaseWriteBlock(pCircBuf, cbBufDst);
+                STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBufDst);
+
+                /* advance */
+                cbChunk                -= cbBufDst;
+                GCPhys                 += cbBufDst;
+                cbLeft                 -= cbBufDst;
+                pBDLE->State.u32BufOff += cbBufDst;
+                Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
             }
         }
+        /*
+         * Need to map the frame content, so we need to read the guest data
+         * into a temporary buffer, though the output can be directly written
+         * into the internal buffer as it is assumed to be frame aligned.
+         *
+         * Note! cbLeft is relative to the output frame size.
+         *       cbChunk OTOH is relative to input size.
+         */
         else
-            LogRel(("HDA: Reading from stream #%RU8 DMA failed with %Rrc\n", uSD, rc));
+        {
+            Assert(PDMAudioPropsIsSizeAligned(&pStreamShared->State.Cfg.Props, cbLeft));
+            uint32_t const cbLeftInput = PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps,
+                                                                    PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props,
+                                                                                               cbLeft));
+            if (cbChunk <= cbLeftInput)
+            { /* very likely */ }
+            else
+                cbChunk = cbLeftInput;
+
+            /*
+             * Loop till we've covered the chunk.
+             */
+            RTGCPHYS GCPhys = pBDLE->Desc.u64BufAddr + pBDLE->State.u32BufOff;
+            Log5Func(("loop0:  GCPhys=%RGp cbChunk=%#x + cbBounce=%#x\n", GCPhys, cbChunk, cbBounce));
+            while (cbChunk > 0)
+            {
+                /* Read into the bounce buffer. */
+                uint32_t const cbToRead = RT_MIN(cbChunk, sizeof(abBounce) - cbBounce);
+                int rc2 = PDMDevHlpPhysRead(pDevIns, GCPhys, &abBounce[cbBounce], cbToRead);
+                AssertRC(rc2);
+                cbBounce += cbToRead;
+
+                /* Convert the size to whole frames and a remainder. */
+                uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStreamR3->State.Mapping.GuestProps, cbBounce);
+                uint32_t const cbRemainder = cbBounce - PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps, cFrames);
+                Log5Func((" loop1: GCPhys=%RGp cbToRead=%#x cbBounce=%#x cFrames=%#x\n", GCPhys, cbToRead, cbBounce, cFrames));
+
+                /*
+                 * Convert from the bounce buffer and into the internal DMA buffer.
+                 */
+                uint32_t offBounce = 0;
+                while (cFrames > 0)
+                {
+                    void  *pvBufDst;
+                    size_t cbBufDst;
+                    RTCircBufAcquireWriteBlock(pCircBuf, PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFrames),
+                                               &pvBufDst, &cbBufDst);
+
+                    uint32_t const cFramesToConvert = PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props, cbBufDst);
+                    Assert(PDMAudioPropsFramesToBytes(&pStreamShared->State.Cfg.Props, cFramesToConvert) == cbBufDst);
+                    Assert(cFramesToConvert > 0);
+                    Assert(cFramesToConvert <= cFrames);
+
+                    pStreamR3->State.Mapping.pfnGuestToHost(pvBufDst, &abBounce[offBounce], cFramesToConvert,
+                                                            &pStreamR3->State.Mapping);
+                    Log5Func(("  loop2: offBounce=%#05x cFramesToConvert=%#05x cbBufDst=%#x%s\n",
+                              offBounce, cFramesToConvert, cbBufDst, ASMMemIsZero(pvBufDst, cbBufDst) ? " all zero" : ""));
+
+# ifdef HDA_DEBUG_SILENCE
+                    fix me if relevant;
+# endif
+                    if (RT_LIKELY(!pStreamR3->Dbg.Runtime.fEnabled))
+                    { /* likely */ }
+                    else
+                        DrvAudioHlpFileWrite(pStreamR3->Dbg.Runtime.pFileDMARaw, pvBufDst, cbBufDst, 0 /* fFlags */);
+
+                    pStreamR3->State.offWrite += cbBufDst;
+                    RTCircBufReleaseWriteBlock(pCircBuf, cbBufDst);
+                    STAM_COUNTER_ADD(&pThis->StatBytesRead, cbBufDst);
+
+                    /* advance */
+                    cbLeft    -= cbBufDst;
+                    cFrames   -= cFramesToConvert;
+                    offBounce += PDMAudioPropsFramesToBytes(&pStreamR3->State.Mapping.GuestProps, cFramesToConvert);
+                }
+
+                /* advance */
+                cbChunk                -= cbToRead;
+                GCPhys                 += cbToRead;
+                pBDLE->State.u32BufOff += cbToRead;
+                Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
+                if (cbRemainder)
+                    memmove(&abBounce[0], &abBounce[cbBounce - cbRemainder], cbRemainder);
+                cbBounce = cbRemainder;
+            }
+            Log5Func(("loop0: GCPhys=%RGp cbBounce=%#x cbLeft=%#x\n", GCPhys, cbBounce, cbLeft));
+        }
 
         STAM_PROFILE_STOP(&pThis->StatOut, a);
-
-        if (cbDMA)
-        {
-            /* We always increment the position of DMA buffer counter because we're always reading
-             * into an intermediate DMA buffer. */
-            pBDLE->State.u32BufOff += (uint32_t)cbDMA;
-            Assert(pBDLE->State.u32BufOff <= pBDLE->Desc.u32BufSize);
-
-            /* Are we done doing the position adjustment?
-             * Only then do the transfer accounting .*/
-            if (pStreamShared->State.cfPosAdjustLeft == 0)
-            {
-                Assert(cbLeft >= cbDMA);
-                cbLeft        -= cbDMA;
-            }
-
-            Log3Func(("[SD%RU8] cbDMA=%RU32 -> %R[bdle]\n", uSD, cbDMA, pBDLE));
-        }
 
         /*
@@ -2014,9 +1991,10 @@
             /* Make sure to also update the wall clock when a BDLE is complete.
              * Needed for Windows 10 guests. */
+            /** @todo there is a rounding error here.   */
             hdaR3WalClkSet(pThis, pThisCC,
                              hdaWalClkGetCurrent(pThis)
                            + hdaR3StreamPeriodFramesToWalClk(pPeriod,
                                                                pBDLE->Desc.u32BufSize
-                                                             / pStreamR3->State.Mapping.cbFrameSize),
+                                                             / pStreamR3->State.Mapping.cbGuestFrame),
                            false /* fForce */);
 
@@ -2033,55 +2011,33 @@
 
             /* Does the current BDLE require an interrupt to be sent? */
-            if (   hdaR3BDLENeedsInterrupt(pBDLE)
-                /* Are we done doing the position adjustment?
-                 * It can happen that a BDLE which is handled while doing the
-                 * position adjustment requires an interrupt on completion (IOC) being set.
-                 *
-                 * In such a case we need to skip such an interrupt and just move on. */
-                && pStreamShared->State.cfPosAdjustLeft == 0)
+            if (hdaR3BDLENeedsInterrupt(pBDLE))
             {
-                /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL register is set
-                 * we need to generate an interrupt.
-                 */
-                if (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_IOCE)
+                if (cSuppressIocs == 0)
                 {
-                    /* Assert the interrupt before actually fetching the next BDLE below. */
-                    if (!fInterruptSent)
+                    /* If the IOCE ("Interrupt On Completion Enable") bit of the SDCTL
+                       register is set we need to generate an interrupt. */
+                    if (HDA_STREAM_REG(pThis, CTL, uSD) & HDA_SDCTL_IOCE)
                     {
-#if 1
-                        pStreamShared->State.cTransferPendingInterrupts = 1;
-                        Log3Func(("[SD%RU8] Scheduling interrupt\n", uSD));
-#else
-                        /* ??*/
-                        AssertMsg(pStreamShared->State.cTransferPendingInterrupts <= 32, ("Too many pending interrupts (%RU8) for stream #%RU8\n", pStreamShared->State.cTransferPendingInterrupts, uSD));
-                        Log3Func(("[SD%RU8] Scheduling interrupt (now %RU8 total)\n", uSD, pStreamShared->State.cTransferPendingInterrupts));
-#endif
-
-                        /*
-                         * Set the stream's BCIS bit.
-                         *
-                         * Note: This only must be done if the whole period is complete, and not if only
-                         * one specific BDL entry is complete (if it has the IOC bit set).
-                         *
-                         * This will otherwise confuses the guest when it 1) deasserts the interrupt,
-                         * 2) reads SDSTS (with BCIS set) and then 3) too early reads a (wrong) WALCLK value.
-                         *
-                         * snd_hda_intel on Linux will tell.
-                         */
-                        /** @todo r=bird: The above comment does not match what we're doing.  This is
-                         *        just a flag indicating that the IOC is why the driver got an IRQ. */
-                        HDA_STREAM_REG(pThis, STS, uSD) |= HDA_SDSTS_BCIS;
-
-                        /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
-                         * ending / beginning a period. */
-                        HDA_PROCESS_INTERRUPT(pDevIns, pThis);
-
-                        fInterruptSent = true;
+                        /* Assert the interrupt before actually fetching the next BDLE below. */
+                        if (!fInterruptSent)
+                        {
+                            pStreamShared->State.cTransferPendingInterrupts = 1;
+                            Log3Func(("[SD%RU8] Scheduling interrupt\n", uSD));
+
+                            /* Trigger an interrupt first and let hdaRegWriteSDSTS() deal with
+                             * ending / beginning of a period. */
+                            /** @todo r=bird: What does the above comment mean? */
+                            HDA_STREAM_REG(pThis, STS, uSD) |= HDA_SDSTS_BCIS;
+                            HDA_PROCESS_INTERRUPT(pDevIns, pThis);
+                            fInterruptSent = true;
+                        }
                     }
                 }
+                else
+                    cSuppressIocs--;
             }
 
             /*
-             * Advance.
+             * Advance to the next BDLE.
              */
             if (pStreamShared->State.uCurBDLE >= pStreamShared->u16LVI)
@@ -2089,59 +2045,47 @@
             else
                 pStreamShared->State.uCurBDLE++;
-
-            /* Fetch the next BDLE entry. */
             hdaR3BDLEFetch(pDevIns, pBDLE, pStreamShared->u64BDLBase, pStreamShared->State.uCurBDLE);
         }
-
-        /* Do the position adjustment accounting. */
-        pStreamShared->State.cfPosAdjustLeft -= RT_MIN(pStreamShared->State.cfPosAdjustLeft, cbDMA / pStreamR3->State.Mapping.cbFrameSize);
-
-        if (RT_FAILURE(rc))
-            break;
-    }
-
-    /* Remove the FIFORDY bit again. */
+        else
+            Log3Func(("[SD%RU8] Not complete %R[bdle]\n", uSD, pBDLE));
+    }
+
+    Assert(cbLeft == 0); /* There shall be no break statements in the above loop, so cbLeft is always zero here! */
+    AssertMsg(cbBounce == 0, ("%#x\n", cbBounce));
+
+    /*
+     * Clear the (pointless) FIFORDY bit again.
+     */
     HDA_STREAM_REG(pThis, STS, uSD) &= ~HDA_SDSTS_FIFORDY;
 
-    /* Sanity. */
-    Assert(cbLeft == 0);
-    uint32_t const cbProcessed = cbToProcessMax - cbLeft;
-
-    /* Only do the data accounting if we don't have to do any position
-     * adjustment anymore. */
-    if (pStreamShared->State.cfPosAdjustLeft == 0)
-    {
-        hdaR3StreamPeriodInc(pPeriod, RT_MIN(cbProcessed / pStreamR3->State.Mapping.cbFrameSize,
-                                             hdaR3StreamPeriodGetRemainingFrames(pPeriod)));
-    }
-
-    const bool fTransferComplete = cbLeft == 0;
-    if (fTransferComplete)
-    {
-        /*
-         * Try updating the wall clock.
-         *
-         * Note 1) Only certain guests (like Linux' snd_hda_intel) rely on the WALCLK register
-         *         in order to determine the correct timing of the sound device. Other guests
-         *         like Windows 7 + 10 (or even more exotic ones like Haiku) will completely
-         *         ignore this.
-         *
-         * Note 2) When updating the WALCLK register too often / early (or even in a non-monotonic
-         *         fashion) this *will* upset guest device drivers and will completely fuck up the
-         *         sound output. Running VLC on the guest will tell!
-         */
-        const bool fWalClkSet = hdaR3WalClkSet(pThis, pThisCC,
-                                               RT_MIN(  hdaWalClkGetCurrent(pThis)
-                                                      + hdaR3StreamPeriodFramesToWalClk(pPeriod,
-                                                                                          cbProcessed
-                                                                                        / pStreamR3->State.Mapping.cbFrameSize),
-                                                      hdaR3WalClkGetMax(pThis, pThisCC)),
-                                               false /* fForce */);
-        RT_NOREF(fWalClkSet);
-    }
-
-    Log3Func(("[SD%RU8] %R[bdle] -- %#RX32/%#RX32 @ %#RX64 - fTransferComplete=%d (%d) cTransferPendingInterrupts=%RU8\n",
-              uSD, pBDLE, cbProcessed, pStreamShared->State.cbTransferSize, pStreamR3->State.offWrite - cbProcessed,
-              fTransferComplete, cbLeft, pStreamShared->State.cTransferPendingInterrupts));
+    /*
+     * Try updating the wall clock.
+     *
+     * Note 1) Only certain guests (like Linux' snd_hda_intel) rely on the WALCLK register
+     *         in order to determine the correct timing of the sound device. Other guests
+     *         like Windows 7 + 10 (or even more exotic ones like Haiku) will completely
+     *         ignore this.
+     *
+     * Note 2) When updating the WALCLK register too often / early (or even in a non-monotonic
+     *         fashion) this *will* upset guest device drivers and will completely fuck up the
+     *         sound output. Running VLC on the guest will tell!
+     */
+    uint32_t const cFramesProcessed = PDMAudioPropsBytesToFrames(&pStreamShared->State.Cfg.Props, cbToProduce);
+    /** @todo this needs to go, but we need it for hdaR3WalClkGetMax below. */
+    hdaR3StreamPeriodInc(&pStreamShared->State.Period,
+                         RT_MIN(cFramesProcessed, hdaR3StreamPeriodGetRemainingFrames(&pStreamShared->State.Period)));
+
+    uint64_t const cWallTicks = hdaR3StreamPeriodFramesToWalClk(pPeriod, cFramesProcessed);
+    uint64_t const uWallNew   = hdaWalClkGetCurrent(pThis) + cWallTicks;
+    uint64_t const uWallMax   = hdaR3WalClkGetMax(pThis, pThisCC);
+    bool const     fWalClkSet = hdaR3WalClkSet(pThis, pThisCC, RT_MIN(uWallNew, uWallMax), false /* fForce */);
+    RT_NOREF(fWalClkSet);
+
+    /*
+     * Log and leave.
+     */
+    Log3Func(("[SD%RU8] %R[bdle] -- %#RX32/%#RX32 @ %#RX64 - cTransferPendingInterrupts=%RU8\n",
+              uSD, pBDLE, cbToProduce, pStreamShared->State.cbTransferSize, pStreamR3->State.offWrite - cbToProduce,
+              pStreamShared->State.cTransferPendingInterrupts));
 
     LogFlowFuncLeave();
@@ -2164,5 +2108,5 @@
     uint32_t       cbToReadFromStream = RT_MIN(cbStreamReadable, cbSinkWritable);
     /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
-    cbToReadFromStream = PDMAudioPropsFloorBytesToFrame(&pStreamR3->State.Mapping.PCMProps, cbToReadFromStream);
+    cbToReadFromStream = PDMAudioPropsFloorBytesToFrame(&pStreamShared->State.Cfg.Props, cbToReadFromStream);
 
     Assert(nsNow >= pStreamShared->State.tsLastReadNs);
@@ -2205,5 +2149,6 @@
 
     /* Re-arm the timer if the sink is still active: */
-    if (   pStreamR3->pMixSink
+    if (   pStreamShared->State.fRunning
+        && pStreamR3->pMixSink
         && AudioMixerSinkIsActive(pStreamR3->pMixSink->pMixSink))
     {
Index: trunk/src/VBox/Devices/Audio/HDAStream.h
===================================================================
--- trunk/src/VBox/Devices/Audio/HDAStream.h	(revision 88097)
+++ trunk/src/VBox/Devices/Audio/HDAStream.h	(revision 88112)
@@ -132,7 +132,8 @@
      *  Can be 0 if no next transfer is scheduled. */
     uint64_t                tsTransferNext;
-    /** Total transfer size (in bytes) of a transfer period. */
+    /** Total transfer size (in bytes) of a transfer period.
+     * @note This is in host side frames, in case we're doing any mapping. */
     uint32_t                cbTransferSize;
-    /** Transfer chunk size (in bytes) of a transfer period. */
+    /** Unused, same as cbTransferSize. */
     uint32_t                cbTransferChunk;
     /** How many interrupts are pending due to
@@ -157,6 +158,8 @@
     /** The stream's period. Need for timing. */
     HDASTREAMPERIOD         Period;
-    /** The stream's current configuration.
-     *  Should match SDFMT. */
+    /** The stream's current host side configuration.
+     * This should match the SDnFMT in all respects but maybe the channel count as
+     * we may need to expand mono or into/from into stereo.  The unmodified SDnFMT
+     * properties can be found in HDASTREAMR3::Mapping::PCMProps. */
     PDMAUDIOSTREAMCFG       Cfg;
     /** Timestamp (real time, in ns) of last DMA transfer. */
Index: trunk/src/VBox/Devices/Audio/HDAStreamMap.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/HDAStreamMap.cpp	(revision 88097)
+++ trunk/src/VBox/Devices/Audio/HDAStreamMap.cpp	(revision 88112)
@@ -23,10 +23,11 @@
 #include <VBox/log.h>
 
-#include <iprt/mem.h>
-
 #include <VBox/vmm/pdmdev.h>
 #include <VBox/vmm/pdmaudioifs.h>
 #include <VBox/vmm/pdmaudioinline.h>
 
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
 #include "DrvAudio.h"
 
@@ -34,96 +35,155 @@
 #include "HDAStreamMap.h"
 
-#ifdef IN_RING3
-
-static int hdaR3StreamMapSetup(PHDASTREAMMAP pMap, PPDMAUDIOPCMPROPS pProps);
-
-/**
- * Initializes a stream mapping structure according to the given PCM properties.
- *
- * @return  IPRT status code.
- * @param   pMap                Pointer to mapping to initialize.
- * @param   pProps              Pointer to PCM properties to use for initialization.
- */
-int hdaR3StreamMapInit(PHDASTREAMMAP pMap, PPDMAUDIOPCMPROPS pProps)
-{
-    AssertPtrReturn(pMap, VERR_INVALID_POINTER);
-    AssertPtrReturn(pProps,   VERR_INVALID_POINTER);
-
-    if (!DrvAudioHlpPcmPropsAreValid(pProps))
-        return VERR_INVALID_PARAMETER;
-
-    hdaR3StreamMapReset(pMap);
-
-    int rc = hdaR3StreamMapSetup(pMap, pProps);
-    if (RT_FAILURE(rc))
-        return rc;
-
-#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
-    if (   RT_SUCCESS(rc)
-        /* Create circular buffer if not created yet. */
-        && !pMap->pCircBuf)
-    {
-        rc = RTCircBufCreate(&pMap->pCircBuf, _4K); /** @todo Make size configurable? */
-    }
-#endif
-
-    if (RT_SUCCESS(rc))
-    {
-        pMap->cbFrameSize = pProps->cChannels * pProps->cbSample;
-
-        LogFunc(("cChannels=%RU8, cBytes=%RU8 -> cbFrameSize=%RU32\n",
-                 pProps->cChannels, pProps->cbSample, pMap->cbFrameSize));
-
-        Assert(pMap->cbFrameSize); /* Frame size must not be 0. */
-
-        pMap->enmLayout = PDMAUDIOSTREAMLAYOUT_INTERLEAVED;
-    }
-
-    return rc;
-}
-
-
-/**
- * Destroys a given stream mapping.
- *
- * @param   pMap            Pointer to mapping to destroy.
- */
-void hdaR3StreamMapDestroy(PHDASTREAMMAP pMap)
-{
-    hdaR3StreamMapReset(pMap);
-
-#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
-    if (pMap->pCircBuf)
-    {
-        RTCircBufDestroy(pMap->pCircBuf);
-        pMap->pCircBuf = NULL;
-    }
-#endif
-}
-
-
-/**
- * Resets a given stream mapping.
- *
- * @param   pMap            Pointer to mapping to reset.
- */
-void hdaR3StreamMapReset(PHDASTREAMMAP pMap)
-{
-    AssertPtrReturnVoid(pMap);
-
-    pMap->enmLayout = PDMAUDIOSTREAMLAYOUT_UNKNOWN;
-
-    if (pMap->paMappings)
-    {
-        for (uint8_t i = 0; i < pMap->cMappings; i++)
-            hdaR3StreamChannelDataDestroy(&pMap->paMappings[i].Data);
-
-        RTMemFree(pMap->paMappings);
-        pMap->paMappings = NULL;
-
-        pMap->cMappings = 0;
-    }
-
-    RT_ZERO(pMap->PCMProps);
+
+
+/** Convert host stereo to mono guest, signed 16-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_H2G_GenericS16_Stereo2Mono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    /** @todo this doesn't merge the two, it just picks the first one. */
+    uint16_t       *pu16Dst    = (uint16_t *)pvDst;
+    uint16_t const *pu16Src    = (uint16_t const *)pvSrc;
+    size_t const    cbDstFrame = pMap->cbGuestFrame;
+
+    if (cbDstFrame != sizeof(*pu16Dst))
+        RT_BZERO(pvDst, cbDstFrame * cFrames);
+
+    while (cFrames-- > 0)
+    {
+        *pu16Dst = *pu16Src;
+        pu16Src += 2;
+        pu16Dst  = (uint16_t *)((uintptr_t)pu16Dst + cbDstFrame);
+    }
+}
+
+
+/** Convert mono guest channel to host stereo, signed 16-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_G2H_GenericS16_Mono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    uint32_t       *pu32Dst    = (uint32_t *)pvDst;
+    uint16_t const *pu16Src    = (uint16_t const *)pvSrc;
+    size_t const    cbSrcFrame = pMap->cbGuestFrame;
+
+    while (cFrames-- > 0)
+    {
+        uint16_t uSample = *pu16Src;
+        *pu32Dst++ = RT_MAKE_U32(uSample, uSample);
+        pu16Src = (uint16_t const *)((uintptr_t)pu16Src + cbSrcFrame);
+    }
+}
+
+
+/** Convert host stereo to mono guest, signed 32-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_H2G_GenericS32_Stereo2Mono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    /** @todo this doesn't merge the two, it just picks the first one. */
+    uint32_t       *pu32Dst    = (uint32_t *)pvDst;
+    uint32_t const *pu32Src    = (uint32_t const *)pvSrc;
+    size_t const    cbDstFrame = pMap->cbGuestFrame;
+
+    if (cbDstFrame != sizeof(*pu32Dst))
+        RT_BZERO(pvDst, cbDstFrame * cFrames);
+
+    while (cFrames-- > 0)
+    {
+        *pu32Dst = *pu32Src;
+        pu32Src += 2;
+        pu32Dst  = (uint32_t *)((uintptr_t)pu32Dst + cbDstFrame);
+    }
+}
+
+
+/** Convert mono guest channel to host stereo, signed 32-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_G2H_GenericS32_Mono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    uint64_t       *pu64Dst    = (uint64_t *)pvDst;
+    uint32_t const *pu32Src    = (uint32_t const *)pvSrc;
+    size_t const    cbSrcFrame = pMap->cbGuestFrame;
+
+    while (cFrames-- > 0)
+    {
+        uint32_t uSample = *pu32Src;
+        *pu64Dst++ = RT_MAKE_U64(uSample, uSample);
+        pu32Src = (uint32_t const *)((uintptr_t)pu32Src + cbSrcFrame);
+    }
+}
+
+
+/** Convert stereo host to 2 or more guest channels, signed 16-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_H2G_GenericS16_Stereo2NonMono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    uint32_t       *pu32Dst    = (uint32_t *)pvDst;
+    uint32_t const *pu32Src    = (uint32_t const *)pvSrc; /** @todo could be misaligned */
+    size_t const    cbDstFrame = pMap->cbGuestFrame;
+
+    RT_BZERO(pvDst, cbDstFrame * cFrames);
+
+    while (cFrames-- > 0)
+    {
+        *pu32Dst = *pu32Src++;
+        pu32Dst = (uint32_t *)((uintptr_t)pu32Dst + cbDstFrame);
+    }
+}
+
+
+/** Convert 2 or more guest channels to host stereo, signed 16-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_G2H_GenericS16_NonMono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    uint32_t       *pu32Dst    = (uint32_t *)pvDst;
+    uint32_t const *pu32Src    = (uint32_t const *)pvSrc; /** @todo could be misaligned */
+    size_t const    cbSrcFrame = pMap->cbGuestFrame;
+
+    while (cFrames-- > 0)
+    {
+        *pu32Dst++ = *pu32Src;
+        pu32Src = (uint32_t const *)((uintptr_t)pu32Src + cbSrcFrame);
+    }
+}
+
+
+/** Convert stereo host to 2 or more guest channels, signed 32-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_H2G_GenericS32_Stereo2NonMono(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    uint64_t       *pu64Dst    = (uint64_t *)pvDst;         /** @todo could be misaligned. */
+    uint64_t const *pu64Src    = (uint64_t const *)pvSrc;
+    size_t const    cbDstFrame = pMap->cbGuestFrame;
+
+    RT_BZERO(pvDst, cbDstFrame * cFrames);
+
+    while (cFrames-- > 0)
+    {
+        *pu64Dst = *pu64Src++;
+        pu64Dst = (uint64_t *)((uintptr_t)pu64Dst + cbDstFrame);
+    }
+}
+
+
+/** Convert 2 or more guest channels to host stereo, signed 32-bit edition. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_G2H_GenericS32_NonMono2Stereo(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    uint64_t       *pu64Dst    = (uint64_t *)pvDst;
+    uint64_t const *pu64Src    = (uint64_t const *)pvSrc; /** @todo could be misaligned */
+    size_t const    cbSrcFrame = pMap->cbGuestFrame;
+
+    while (cFrames-- > 0)
+    {
+        *pu64Dst++ = *pu64Src;
+        pu64Src = (uint64_t const *)((uintptr_t)pu64Src + cbSrcFrame);
+    }
+}
+
+
+/** No conversion needed, just copy the data. */
+static DECLCALLBACK(void)
+hdaR3StreamMap_GenericCopy(void *pvDst, void const *pvSrc, uint32_t cFrames, HDASTREAMMAP const *pMap)
+{
+    memcpy(pvDst, pvSrc, cFrames * pMap->cbGuestFrame);
 }
 
@@ -132,9 +192,13 @@
  * Sets up a stream mapping according to the given properties / configuration.
  *
- * @return VBox status code, or VERR_NOT_SUPPORTED if the channel setup is not supported (yet).
- * @param  pMap                 Pointer to mapping to set up.
- * @param  pProps               PCM audio properties to use for lookup.
- */
-static int hdaR3StreamMapSetup(PHDASTREAMMAP pMap, PPDMAUDIOPCMPROPS pProps)
+ * @return  VBox status code.
+ * @retval  VERR_NOT_SUPPORTED if the channel setup is not supported.
+ *
+ * @param   pMap            Pointer to mapping to set up.
+ * @param   pProps          Input: The stream PCM properties from the guest.
+ *                          Output: Properties for the host stream.
+ * @param   cHostChannels   The number of host channels to map to.
+ */
+static int hdaR3StreamMapSetup(PHDASTREAMMAP pMap, PPDMAUDIOPCMPROPS pProps, uint8_t cHostChannels)
 {
     int rc;
@@ -150,24 +214,97 @@
          || pProps->cChannels == 6) /* Surround (5.1) */
     {
+        /*
+         * Copy the guest stream properties and see if we need to change the host properties.
+         */
+        memcpy(&pMap->GuestProps, pProps, sizeof(PDMAUDIOPCMPROPS));
+        if (pProps->cChannels != cHostChannels)
+        {
+            if (pProps->cChannels == 1)
+                LogRelMax(32, ("HDA: Warning: Guest mono, host stereo.\n"));
+            else if (cHostChannels == 1 && pProps->cChannels == 2)
+                LogRelMax(32, ("HDA: Warning: Host mono, guest stereo.\n"));
+            else
+#ifndef VBOX_WITH_AUDIO_HDA_51_SURROUND
+                LogRelMax(32, ("HDA: Warning: Guest configured %u channels, host only supports %u. Ignoring additional channels.\n",
+                               pProps->cChannels, cHostChannels));
+#else
+# error reconsider the above logic
+#endif
+            pProps->cChannels = cHostChannels;
+            pProps->cShift    = PDMAUDIOPCMPROPS_MAKE_SHIFT(pProps);
+        }
+
+        /*
+         * Pick conversion functions.
+         */
+        Assert(pMap->GuestProps.cbSample == pProps->cbSample);
+
+        /* If the channel count matches, we can use the memcpy converters: */
+        if (pProps->cChannels == pMap->GuestProps.cChannels)
+        {
+            pMap->pfnGuestToHost = hdaR3StreamMap_GenericCopy;
+            pMap->pfnHostToGuest = hdaR3StreamMap_GenericCopy;
+            pMap->fMappingNeeded = false;
+        }
+        else
+        {
+            /* For multi-speaker guest configs, we currently just pick the
+               first two channels and map this onto the host stereo ones. */
+            AssertReturn(cHostChannels == 2, VERR_NOT_SUPPORTED);
+            switch (pMap->GuestProps.cbSample)
+            {
+                case 2:
+                    if (pMap->GuestProps.cChannels > 1)
+                    {
+                        pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS16_NonMono2Stereo;
+                        pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS16_Stereo2NonMono;
+                    }
+                    else
+                    {
+                        pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS16_Mono2Stereo;
+                        pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS16_Stereo2Mono;
+                    }
+                    break;
+
+                case 4:
+                    if (pMap->GuestProps.cChannels > 1)
+                    {
+                        pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS32_NonMono2Stereo;
+                        pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS32_Stereo2NonMono;
+                    }
+                    else
+                    {
+                        pMap->pfnGuestToHost = hdaR3StreamMap_G2H_GenericS32_Mono2Stereo;
+                        pMap->pfnHostToGuest = hdaR3StreamMap_H2G_GenericS32_Stereo2Mono;
+                    }
+                    break;
+
+                default:
+                    AssertMsgFailedReturn(("cbSample=%u\n", pMap->GuestProps.cbSample), VERR_NOT_SUPPORTED);
+            }
+            pMap->fMappingNeeded = true;
+        }
+
+        /** @todo bird: Not sure if this is really needed any more. */
+
         /* For now we don't have anything other as mono / stereo channels being covered by the backends.
          * So just set up one channel covering those and skipping the rest (like configured rear or center/LFE outputs). */
         pMap->cMappings  = 1;
         pMap->paMappings = (PPDMAUDIOSTREAMMAP)RTMemAlloc(sizeof(PDMAUDIOSTREAMMAP) * pMap->cMappings);
-        if (!pMap->paMappings)
-            return VERR_NO_MEMORY;
-
-        PPDMAUDIOSTREAMMAP pMapLR = &pMap->paMappings[0];
-
-        pMapLR->aenmIDs[0]  = PDMAUDIOSTREAMCHANNELID_FRONT_LEFT;
-        pMapLR->aenmIDs[1]  = PDMAUDIOSTREAMCHANNELID_FRONT_RIGHT;
-        pMapLR->cbFrame     = pProps->cbSample * pProps->cChannels;
-        pMapLR->cbStep      = pProps->cbSample * 2 /* Front left + Front right channels */;
-        pMapLR->offFirst    = 0;
-        pMapLR->offNext     = pMapLR->offFirst;
-
-        rc = hdaR3StreamChannelDataInit(&pMapLR->Data, PDMAUDIOSTREAMCHANNELDATA_FLAGS_NONE);
-        AssertRC(rc);
-
-        memcpy(&pMap->PCMProps, pProps, sizeof(PDMAUDIOPCMPROPS));
+        if (pMap->paMappings)
+        {
+            PPDMAUDIOSTREAMMAP pMapLR = &pMap->paMappings[0];
+            pMapLR->aenmIDs[0]  = PDMAUDIOSTREAMCHANNELID_FRONT_LEFT;
+            pMapLR->aenmIDs[1]  = PDMAUDIOSTREAMCHANNELID_FRONT_RIGHT;
+            pMapLR->cbFrame     = pProps->cbSample * pProps->cChannels;
+            pMapLR->cbStep      = pProps->cbSample * 2 /* Front left + Front right channels */;
+            pMapLR->offFirst    = 0;
+            pMapLR->offNext     = pMapLR->offFirst;
+
+            rc = hdaR3StreamChannelDataInit(&pMapLR->Data, PDMAUDIOSTREAMCHANNELDATA_FLAGS_NONE);
+            AssertRC(rc);
+        }
+        else
+            rc = VERR_NO_MEMORY;
     }
     else
@@ -176,4 +313,94 @@
     return rc;
 }
-#endif /* IN_RING3 */
-
+
+
+/**
+ * Initializes a stream mapping structure according to the given PCM properties.
+ *
+ * @return  IPRT status code.
+ * @param   pMap            Pointer to mapping to initialize.
+ * @param   cHostChannels   The number of host channels to map to.
+ * @param   pProps          Input: The stream PCM properties from the guest.
+ *                          Output: Properties for the host side.
+ */
+int hdaR3StreamMapInit(PHDASTREAMMAP pMap, uint8_t cHostChannels, PPDMAUDIOPCMPROPS pProps)
+{
+    AssertPtrReturn(pMap, VERR_INVALID_POINTER);
+    AssertPtrReturn(pProps, VERR_INVALID_POINTER);
+
+    if (!DrvAudioHlpPcmPropsAreValid(pProps))
+        return VERR_INVALID_PARAMETER;
+
+    hdaR3StreamMapReset(pMap);
+
+    pMap->cbGuestFrame = pProps->cChannels * pProps->cbSample;
+    int rc = hdaR3StreamMapSetup(pMap, pProps, cHostChannels);
+    if (RT_SUCCESS(rc))
+    {
+#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
+        /* Create the circular buffer if not created yet. */
+        if (!pMap->pCircBuf)
+            rc = RTCircBufCreate(&pMap->pCircBuf, _4K); /** @todo Make size configurable? */
+        if (RT_SUCCESS(rc))
+#endif
+        {
+            LogFunc(("cChannels=%RU8, cBytes=%RU8 -> cbGuestFrame=%RU32\n",
+                     pProps->cChannels, pProps->cbSample, pMap->cbGuestFrame));
+
+            Assert(pMap->cbGuestFrame); /* Frame size must not be 0. */
+            pMap->enmLayout = PDMAUDIOSTREAMLAYOUT_INTERLEAVED;
+            return VINF_SUCCESS;
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Destroys a given stream mapping.
+ *
+ * @param   pMap            Pointer to mapping to destroy.
+ */
+void hdaR3StreamMapDestroy(PHDASTREAMMAP pMap)
+{
+    hdaR3StreamMapReset(pMap);
+
+#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
+    if (pMap->pCircBuf)
+    {
+        RTCircBufDestroy(pMap->pCircBuf);
+        pMap->pCircBuf = NULL;
+    }
+#endif
+}
+
+
+/**
+ * Resets a given stream mapping.
+ *
+ * @param   pMap            Pointer to mapping to reset.
+ */
+void hdaR3StreamMapReset(PHDASTREAMMAP pMap)
+{
+    AssertPtrReturnVoid(pMap);
+
+    pMap->enmLayout = PDMAUDIOSTREAMLAYOUT_UNKNOWN;
+
+    if (pMap->paMappings)
+    {
+        for (uint8_t i = 0; i < pMap->cMappings; i++)
+            hdaR3StreamChannelDataDestroy(&pMap->paMappings[i].Data);
+
+        RTMemFree(pMap->paMappings);
+        pMap->paMappings = NULL;
+
+        pMap->cMappings = 0;
+    }
+
+    pMap->fMappingNeeded = false;
+    pMap->pfnGuestToHost = hdaR3StreamMap_GenericCopy;
+    pMap->pfnHostToGuest = hdaR3StreamMap_GenericCopy;
+    RT_ZERO(pMap->GuestProps);
+}
+
Index: trunk/src/VBox/Devices/Audio/HDAStreamMap.h
===================================================================
--- trunk/src/VBox/Devices/Audio/HDAStreamMap.h	(revision 88097)
+++ trunk/src/VBox/Devices/Audio/HDAStreamMap.h	(revision 88112)
@@ -27,14 +27,16 @@
 typedef struct HDASTREAMMAP
 {
-    /** The PCM properties which have been used. */
-    PDMAUDIOPCMPROPS                PCMProps;
+    /** The guest stream properties that is being mapped from/to.
+     * The host properties are found in HDASTREAMSTATE::Cfg::Props.  */
+    PDMAUDIOPCMPROPS                GuestProps;
     /** The stream's layout. */
     PDMAUDIOSTREAMLAYOUT            enmLayout;
-    /** The mapping's overall audio frame size (in bytes).
-     *  This includes all mappings in \a paMappings. */
-    uint8_t                         cbFrameSize;
+    /** The guest side frame size in bytes. */
+    uint8_t                         cbGuestFrame;
+    /** Set if mapping is needed. */
+    bool                            fMappingNeeded;
     /** Number of mappings in paMappings. */
     uint8_t                         cMappings;
-    uint8_t                         aPadding[2];
+    uint8_t                         aPadding[1];
     /** Array of stream mappings.
      *  Note: The mappings *must* be layed out in an increasing order, e.g.
@@ -48,4 +50,24 @@
     R3PTRTYPE(PRTCIRCBUF)           pCircBuf;
 #endif
+    /**
+     * Converts guest data to host data.
+     *
+     * @param   pvDst       The destination (host) buffer.
+     * @param   pvSrc       The source (guest) data.  Does not overlap @a pvDst.
+     * @param   cFrames     Number of frames to convert.
+     * @param   pMapping    Pointer to this structure.
+     */
+    DECLCALLBACKMEMBER(void, pfnGuestToHost,(void *pvDst, void const *pvSrc, uint32_t cFrames,
+                                             struct HDASTREAMMAP const *pMapping));
+    /**
+     * Converts host data to guest data.
+     *
+     * @param   pvDst       The destination (guest) buffer.
+     * @param   pvSrc       The source (host) data.  Does not overlap @a pvDst.
+     * @param   cFrames     Number of frames to convert.
+     * @param   pMapping    Pointer to this structure.
+     */
+    DECLCALLBACKMEMBER(void, pfnHostToGuest,(void *pvDst, void const *pvSrc, uint32_t cFrames,
+                                             struct HDASTREAMMAP const *pMapping));
 } HDASTREAMMAP;
 AssertCompileSizeAlignment(HDASTREAMMAP, 8);
@@ -57,5 +79,5 @@
  */
 #ifdef IN_RING3
-int  hdaR3StreamMapInit(PHDASTREAMMAP pMapping, PPDMAUDIOPCMPROPS pProps);
+int  hdaR3StreamMapInit(PHDASTREAMMAP pMapping, uint8_t cHostChannels, PPDMAUDIOPCMPROPS pProps);
 void hdaR3StreamMapDestroy(PHDASTREAMMAP pMapping);
 void hdaR3StreamMapReset(PHDASTREAMMAP pMapping);
Index: trunk/src/VBox/Devices/Audio/testcase/tstAudioMixBuffer.cpp
===================================================================
--- trunk/src/VBox/Devices/Audio/testcase/tstAudioMixBuffer.cpp	(revision 88097)
+++ trunk/src/VBox/Devices/Audio/testcase/tstAudioMixBuffer.cpp	(revision 88112)
@@ -38,5 +38,5 @@
     RTTestSub(hTest, "Basics");
 
-    static const PDMAUDIOPCMPROPS s_Cfg441StereoS16 = PDMAUDIOPCMPROPS_INITIALIZOR(
+    const PDMAUDIOPCMPROPS Cfg441StereoS16 = PDMAUDIOPCMPROPS_INITIALIZOR(
         /* a_cb: */             2,
         /* a_fSigned: */        true,
@@ -46,5 +46,5 @@
         /* a_fSwapEndian: */    false
     );
-    static const PDMAUDIOPCMPROPS s_Cfg441StereoU16 = PDMAUDIOPCMPROPS_INITIALIZOR(
+    const PDMAUDIOPCMPROPS Cfg441StereoU16 = PDMAUDIOPCMPROPS_INITIALIZOR(
         /* a_cb: */             2,
         /* a_fSigned: */        false,
@@ -54,5 +54,5 @@
         /* a_fSwapEndian: */    false
     );
-    static const PDMAUDIOPCMPROPS s_Cfg441StereoU32 = PDMAUDIOPCMPROPS_INITIALIZOR(
+    const PDMAUDIOPCMPROPS Cfg441StereoU32 = PDMAUDIOPCMPROPS_INITIALIZOR(
         /* a_cb: */             4,
         /* a_fSigned: */        false,
@@ -63,86 +63,86 @@
     );
 
-    RTTESTI_CHECK(PDMAudioPropsGetBitrate(&s_Cfg441StereoS16) == 44100*4*8);
-    RTTESTI_CHECK(PDMAudioPropsGetBitrate(&s_Cfg441StereoU16) == 44100*4*8);
-    RTTESTI_CHECK(PDMAudioPropsGetBitrate(&s_Cfg441StereoU32) == 44100*8*8);
-
-    RTTESTI_CHECK(DrvAudioHlpPcmPropsAreValid(&s_Cfg441StereoS16));
-    RTTESTI_CHECK(DrvAudioHlpPcmPropsAreValid(&s_Cfg441StereoU16) == false); /* go figure */
-    RTTESTI_CHECK(DrvAudioHlpPcmPropsAreValid(&s_Cfg441StereoU32) == false); /* go figure */
-
-
-    RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&s_Cfg441StereoS16, 1) == 4,
-                      ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&s_Cfg441StereoS16, 1)));
-    RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&s_Cfg441StereoU16, 1) == 4,
-                      ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&s_Cfg441StereoU16, 1)));
-    RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&s_Cfg441StereoU32, 1) == 8,
-                      ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&s_Cfg441StereoU32, 1)));
-
-    RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&s_Cfg441StereoS16) == 4,
-                      ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&s_Cfg441StereoS16)));
-    RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&s_Cfg441StereoU16) == 4,
-                      ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&s_Cfg441StereoU16)));
-    RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&s_Cfg441StereoU32) == 8,
-                      ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&s_Cfg441StereoU32)));
+    RTTESTI_CHECK(PDMAudioPropsGetBitrate(&Cfg441StereoS16) == 44100*4*8);
+    RTTESTI_CHECK(PDMAudioPropsGetBitrate(&Cfg441StereoU16) == 44100*4*8);
+    RTTESTI_CHECK(PDMAudioPropsGetBitrate(&Cfg441StereoU32) == 44100*8*8);
+
+    RTTESTI_CHECK(DrvAudioHlpPcmPropsAreValid(&Cfg441StereoS16));
+    RTTESTI_CHECK(DrvAudioHlpPcmPropsAreValid(&Cfg441StereoU16) == false); /* go figure */
+    RTTESTI_CHECK(DrvAudioHlpPcmPropsAreValid(&Cfg441StereoU32) == false); /* go figure */
+
+
+    RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&Cfg441StereoS16, 1) == 4,
+                      ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&Cfg441StereoS16, 1)));
+    RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU16, 1) == 4,
+                      ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU16, 1)));
+    RTTESTI_CHECK_MSG(PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU32, 1) == 8,
+                      ("got %x, expected 4\n", PDMAUDIOPCMPROPS_F2B(&Cfg441StereoU32, 1)));
+
+    RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&Cfg441StereoS16) == 4,
+                      ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&Cfg441StereoS16)));
+    RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&Cfg441StereoU16) == 4,
+                      ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&Cfg441StereoU16)));
+    RTTESTI_CHECK_MSG(PDMAudioPropsBytesPerFrame(&Cfg441StereoU32) == 8,
+                      ("got %x, expected 4\n", PDMAudioPropsBytesPerFrame(&Cfg441StereoU32)));
 
     uint32_t u32;
     for (uint32_t i = 0; i < 256; i += 8)
     {
-        RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&s_Cfg441StereoU32, i) == true);
+        RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoU32, i) == true);
         for (uint32_t j = 1; j < 8; j++)
-            RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&s_Cfg441StereoU32, i + j) == false);
+            RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoU32, i + j) == false);
         for (uint32_t j = 0; j < 8; j++)
-            RTTESTI_CHECK(PDMAudioPropsFloorBytesToFrame(&s_Cfg441StereoU32, i + j) == i);
+            RTTESTI_CHECK(PDMAudioPropsFloorBytesToFrame(&Cfg441StereoU32, i + j) == i);
     }
     for (uint32_t i = 0; i < 4096; i += 4)
     {
-        RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&s_Cfg441StereoS16, i) == true);
+        RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoS16, i) == true);
         for (uint32_t j = 1; j < 4; j++)
-            RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&s_Cfg441StereoS16, i + j) == false);
+            RTTESTI_CHECK(PDMAudioPropsIsSizeAligned(&Cfg441StereoS16, i + j) == false);
         for (uint32_t j = 0; j < 4; j++)
-            RTTESTI_CHECK(PDMAudioPropsFloorBytesToFrame(&s_Cfg441StereoS16, i + j) == i);
-    }
-
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&s_Cfg441StereoS16, 44100)) == 44100 * 2 * 2,
+            RTTESTI_CHECK(PDMAudioPropsFloorBytesToFrame(&Cfg441StereoS16, i + j) == i);
+    }
+
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoS16, 44100)) == 44100 * 2 * 2,
                       ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&s_Cfg441StereoS16, 2)) == 2 * 2 * 2,
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoS16, 2)) == 2 * 2 * 2,
                       ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&s_Cfg441StereoS16, 1)) == 4,
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoS16, 1)) == 4,
                       ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&s_Cfg441StereoU16, 1)) == 4,
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoU16, 1)) == 4,
                       ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&s_Cfg441StereoU32, 1)) == 8,
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsFramesToBytes(&Cfg441StereoU32, 1)) == 8,
                       ("cb=%RU32\n", u32));
 
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&s_Cfg441StereoS16, 4)) == 1, ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&s_Cfg441StereoU16, 4)) == 1, ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&s_Cfg441StereoU32, 8)) == 1, ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&Cfg441StereoS16, 4)) == 1, ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&Cfg441StereoU16, 4)) == 1, ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsBytesToFrames(&Cfg441StereoU32, 8)) == 1, ("cb=%RU32\n", u32));
 
     uint64_t u64;
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToNano(&s_Cfg441StereoS16, 44100 * 2 * 2)) == RT_NS_1SEC,
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToNano(&Cfg441StereoS16, 44100 * 2 * 2)) == RT_NS_1SEC,
                       ("ns=%RU64\n", u64));
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToMicro(&s_Cfg441StereoS16, 44100 * 2 * 2)) == RT_US_1SEC,
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToMicro(&Cfg441StereoS16, 44100 * 2 * 2)) == RT_US_1SEC,
                       ("us=%RU64\n", u64));
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToMilli(&s_Cfg441StereoS16, 44100 * 2 * 2)) == RT_MS_1SEC,
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsBytesToMilli(&Cfg441StereoS16, 44100 * 2 * 2)) == RT_MS_1SEC,
                       ("ms=%RU64\n", u64));
 
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&s_Cfg441StereoS16, 44100)) == RT_NS_1SEC, ("ns=%RU64\n", u64));
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&s_Cfg441StereoS16,     1)) == 22675,      ("ns=%RU64\n", u64));
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&s_Cfg441StereoS16,    31)) == 702947,     ("ns=%RU64\n", u64));
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&s_Cfg441StereoS16,   255)) == 5782312,    ("ns=%RU64\n", u64));
-    //RTTESTI_CHECK_MSG((u64 = DrvAudioHlpFramesToMicro(&s_Cfg441StereoS16, 44100)) == RT_US_1SEC,
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16, 44100)) == RT_NS_1SEC, ("ns=%RU64\n", u64));
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16,     1)) == 22675,      ("ns=%RU64\n", u64));
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16,    31)) == 702947,     ("ns=%RU64\n", u64));
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToNano(&Cfg441StereoS16,   255)) == 5782312,    ("ns=%RU64\n", u64));
+    //RTTESTI_CHECK_MSG((u64 = DrvAudioHlpFramesToMicro(&Cfg441StereoS16, 44100)) == RT_US_1SEC,
     //                  ("us=%RU64\n", u64));
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToMilli(&s_Cfg441StereoS16, 44100)) == RT_MS_1SEC, ("ms=%RU64\n", u64));
-    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToMilli(&s_Cfg441StereoS16,   255)) == 5,          ("ms=%RU64\n", u64));
-
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToFrames(&s_Cfg441StereoS16,  RT_NS_1SEC)) == 44100, ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToFrames(&s_Cfg441StereoS16,      215876)) == 10,    ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToFrames(&s_Cfg441StereoS16, RT_MS_1SEC)) == 44100, ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToFrames(&s_Cfg441StereoU32,          6)) == 265,   ("cb=%RU32\n", u32));
-
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToBytes(&s_Cfg441StereoS16,  RT_NS_1SEC)) == 44100*2*2, ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToBytes(&s_Cfg441StereoS16,      702947)) == 31*2*2,    ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToBytes(&s_Cfg441StereoS16, RT_MS_1SEC)) == 44100*2*2, ("cb=%RU32\n", u32));
-    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToBytes(&s_Cfg441StereoS16,          5)) == 884,       ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToMilli(&Cfg441StereoS16, 44100)) == RT_MS_1SEC, ("ms=%RU64\n", u64));
+    RTTESTI_CHECK_MSG((u64 = PDMAudioPropsFramesToMilli(&Cfg441StereoS16,   255)) == 5,          ("ms=%RU64\n", u64));
+
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToFrames(&Cfg441StereoS16,  RT_NS_1SEC)) == 44100, ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToFrames(&Cfg441StereoS16,      215876)) == 10,    ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToFrames(&Cfg441StereoS16, RT_MS_1SEC)) == 44100, ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToFrames(&Cfg441StereoU32,          6)) == 265,   ("cb=%RU32\n", u32));
+
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToBytes(&Cfg441StereoS16,  RT_NS_1SEC)) == 44100*2*2, ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsNanoToBytes(&Cfg441StereoS16,      702947)) == 31*2*2,    ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToBytes(&Cfg441StereoS16, RT_MS_1SEC)) == 44100*2*2, ("cb=%RU32\n", u32));
+    RTTESTI_CHECK_MSG((u32 = PDMAudioPropsMilliToBytes(&Cfg441StereoS16,          5)) == 884,       ("cb=%RU32\n", u32));
 
     /* DrvAudioHlpClearBuf: */
@@ -152,14 +152,14 @@
 
     memset(pbPage, 0x42, PAGE_SIZE);
-    PDMAudioPropsClearBuffer(&s_Cfg441StereoS16, pbPage, PAGE_SIZE, PAGE_SIZE / 4);
+    PDMAudioPropsClearBuffer(&Cfg441StereoS16, pbPage, PAGE_SIZE, PAGE_SIZE / 4);
     RTTESTI_CHECK(ASMMemIsZero(pbPage, PAGE_SIZE));
 
     memset(pbPage, 0x42, PAGE_SIZE);
-    PDMAudioPropsClearBuffer(&s_Cfg441StereoU16, pbPage, PAGE_SIZE, PAGE_SIZE / 4);
+    PDMAudioPropsClearBuffer(&Cfg441StereoU16, pbPage, PAGE_SIZE, PAGE_SIZE / 4);
     for (uint32_t off = 0; off < PAGE_SIZE; off += 2)
         RTTESTI_CHECK_MSG(pbPage[off] == 0x80 && pbPage[off + 1] == 0, ("off=%#x: %#x %x\n", off, pbPage[off], pbPage[off + 1]));
 
     memset(pbPage, 0x42, PAGE_SIZE);
-    PDMAudioPropsClearBuffer(&s_Cfg441StereoU32, pbPage, PAGE_SIZE, PAGE_SIZE / 8);
+    PDMAudioPropsClearBuffer(&Cfg441StereoU32, pbPage, PAGE_SIZE, PAGE_SIZE / 8);
     for (uint32_t off = 0; off < PAGE_SIZE; off += 4)
         RTTESTI_CHECK(pbPage[off] == 0x80 && pbPage[off + 1] == 0 && pbPage[off + 2] == 0 && pbPage[off + 3] == 0);
@@ -168,14 +168,14 @@
     RTTestDisableAssertions(hTest);
     memset(pbPage, 0x42, PAGE_SIZE);
-    PDMAudioPropsClearBuffer(&s_Cfg441StereoS16, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
+    PDMAudioPropsClearBuffer(&Cfg441StereoS16, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
     RTTESTI_CHECK(ASMMemIsZero(pbPage, PAGE_SIZE));
 
     memset(pbPage, 0x42, PAGE_SIZE);
-    PDMAudioPropsClearBuffer(&s_Cfg441StereoU16, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
+    PDMAudioPropsClearBuffer(&Cfg441StereoU16, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
     for (uint32_t off = 0; off < PAGE_SIZE; off += 2)
         RTTESTI_CHECK_MSG(pbPage[off] == 0x80 && pbPage[off + 1] == 0, ("off=%#x: %#x %x\n", off, pbPage[off], pbPage[off + 1]));
 
     memset(pbPage, 0x42, PAGE_SIZE);
-    PDMAudioPropsClearBuffer(&s_Cfg441StereoU32, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
+    PDMAudioPropsClearBuffer(&Cfg441StereoU32, pbPage, PAGE_SIZE, PAGE_SIZE); /* should adjust down the frame count. */
     for (uint32_t off = 0; off < PAGE_SIZE; off += 4)
         RTTESTI_CHECK(pbPage[off] == 0x80 && pbPage[off + 1] == 0 && pbPage[off + 2] == 0 && pbPage[off + 3] == 0);
