Index: /trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp	(revision 88565)
+++ /trunk/src/VBox/Devices/Audio/DrvHostAudioDSound.cpp	(revision 88566)
@@ -108,4 +108,6 @@
 typedef struct DSOUNDSTREAM
 {
+    /** Entry in DRVHOSTDSOUND::HeadStreams. */
+    RTLISTNODE          ListEntry;
     /** The stream's acquired configuration. */
     PDMAUDIOSTREAMCFG   Cfg;
@@ -155,4 +157,6 @@
              *  to the DirectSound buffer was. */
             uint32_t                    cbLastTransferred;
+            /** The RTTimeMilliTS() deadline for the draining of this stream. */
+            uint64_t                    msDrainDeadline;
         } Out;
     };
@@ -165,5 +169,5 @@
     char                szStatus[127];
     /** Fixed zero terminator. */
-    char const         chStateZero;
+    char const          chStateZero;
 } DSOUNDSTREAM, *PDSOUNDSTREAM;
 
@@ -213,4 +217,11 @@
     /** The Direct Sound capturing interface. */
     LPDIRECTSOUNDCAPTURE8       pDSC;
+    /** A drain stop timer that makes sure a draining stream will be
+     *  properly stopped (seem the non-loop mode is a bit buggy). */
+    TMTIMERHANDLE               hDrainTimer;
+    /** List of streams (DSOUNDSTREAM).
+     * Requires CritSect ownership.  */
+    RTLISTANCHOR                HeadStreams;
+
 #ifdef VBOX_WITH_AUDIO_MMNOTIFICATION_CLIENT
     DrvHostAudioDSoundMMNotifClient *m_pNotificationClient;
@@ -1641,4 +1652,5 @@
                  pCfgReq->enmDir == PDMAUDIODIR_IN ? PDMAudioRecSrcGetName(pCfgReq->u.enmSrc)
                  : PDMAudioPlaybackDstGetName(pCfgReq->u.enmDst), pCfgReq->szName));
+    RTListInit(&pStreamDS->ListEntry); /* paranoia */
 
     /* For whatever reason: */
@@ -1693,4 +1705,9 @@
         PDMAudioStrmCfgCopy(&pStreamDS->Cfg, pCfgAcq);
         drvHostDSoundStreamReset(pThis, pStreamDS);
+
+        RTCritSectEnter(&pThis->CritSect);
+        RTListAppend(&pThis->HeadStreams, &pStreamDS->ListEntry);
+        RTCritSectLeave(&pThis->CritSect);
+
         rc = VINF_SUCCESS;
     }
@@ -1712,4 +1729,8 @@
     AssertPtrReturn(pStreamDS, VERR_INVALID_POINTER);
     LogFlowFunc(("Stream '%s' {%s}\n", pStreamDS->Cfg.szName, drvHostDSoundStreamStatusString(pStreamDS)));
+
+    RTCritSectEnter(&pThis->CritSect);
+    RTListNodeRemove(&pStreamDS->ListEntry);
+    RTCritSectLeave(&pThis->CritSect);
 
     if (pStreamDS->Cfg.enmDir == PDMAUDIODIR_IN)
@@ -2069,4 +2090,65 @@
 
 /**
+ * This is used by the timer function as well as when arming the timer.
+ *
+ * @param   pThis       The DSound host audio driver instance data.
+ * @param   msNow       A current RTTimeMilliTS() value.
+ */
+static void drvHostDSoundDrainTimerWorker(PDRVHOSTDSOUND pThis, uint64_t msNow)
+{
+    /*
+     * Go thru the stream list and look at draining streams.
+     */
+    uint64_t        msNext = UINT64_MAX;
+    RTCritSectEnter(&pThis->CritSect);
+    PDSOUNDSTREAM   pCur;
+    RTListForEach(&pThis->HeadStreams, pCur, DSOUNDSTREAM, ListEntry)
+    {
+        if (   pCur->Cfg.enmDir == PDMAUDIODIR_OUT
+            && pCur->Out.fDrain)
+        {
+            uint64_t const msCurDeadline = pCur->Out.msDrainDeadline;
+            if (msCurDeadline > 0 && msCurDeadline < msNext)
+            {
+                if (msCurDeadline > msNow)
+                    msNext = pCur->Out.msDrainDeadline;
+                else
+                {
+                    LogRel2(("DSound: Stopping draining of '%s' {%s} ...\n",
+                             pCur->Cfg.szName, drvHostDSoundStreamStatusString(pCur)));
+                    if (pCur->Out.pDSB)
+                    {
+                        HRESULT hrc = IDirectSoundBuffer8_Stop(pCur->Out.pDSB);
+                        if (FAILED(hrc))
+                            LogRelMax(64, ("DSound: Failed to stop draining stream '%s': %Rhrc\n", pCur->Cfg.szName, hrc));
+                    }
+                    pCur->Out.fDrain = false;
+                }
+            }
+        }
+    }
+
+    /*
+     * Re-arm the timer if necessary.
+     */
+    if (msNext != UINT64_MAX)
+        PDMDrvHlpTimerSetMillies(pThis->pDrvIns, pThis->hDrainTimer, msNext - msNow);
+    RTCritSectLeave(&pThis->CritSect);
+}
+
+
+/**
+ * @callback_method_impl{FNTMTIMERDRV,
+ * This is a hack to ensure that draining streams stop playing.}
+ */
+static DECLCALLBACK(void) drvHostDSoundDrainStopHackTimer(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+    PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
+    RT_NOREF(hTimer, pvUser);
+    drvHostDSoundDrainTimerWorker(pThis, RTTimeMilliTS());
+}
+
+
+/**
  * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
  */
@@ -2092,5 +2174,10 @@
             hrc = IDirectSoundBuffer8_Play(pStreamDS->Out.pDSB, 0, 0, 0);
             if (SUCCEEDED(hrc))
-                pStreamDS->Out.fDrain = true;
+            {
+                uint64_t const msNow = RTTimeMilliTS();
+                pStreamDS->Out.msDrainDeadline = PDMAudioPropsBytesToMilli(&pStreamDS->Cfg.Props,  pStreamDS->cbBufSize) + msNow;
+                pStreamDS->Out.fDrain          = true;
+                drvHostDSoundDrainTimerWorker(pThis, msNow);
+            }
             else
                 LogRelMax(64, ("DSound: Failed to restart '%s' in drain mode: %Rhrc\n", pStreamDS->Cfg.szName, hrc));
@@ -2115,4 +2202,5 @@
     return rc;
 }
+
 
 /**
@@ -2665,5 +2753,7 @@
      * Init basic data members and interfaces.
      */
+    RTListInit(&pThis->HeadStreams);
     pThis->pDrvIns                   = pDrvIns;
+    pThis->hDrainTimer               = NIL_TMTIMERHANDLE;
     /* IBase */
     pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
@@ -2754,4 +2844,12 @@
         LogRel2(("DSound: Notification client is disabled (ver %#RX64)\n", RTSystemGetNtVersion()));
 #endif
+
+    /*
+     * Create a timer that helps making sure draining streams actually stop playing.
+     * (Seem dsound doesn't stop by itself in many cases.)
+     */
+    int rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_REAL, drvHostDSoundDrainStopHackTimer, NULL /*pvUser*/, 0 /*fFlags*/,
+                                    "DSound drain", &pThis->hDrainTimer);
+    AssertRCReturn(rc, rc);
 
     /*
