Index: /trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp	(revision 58732)
+++ /trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp	(revision 58733)
@@ -67,4 +67,15 @@
 typedef FNDIRECTSOUNDCAPTUREENUMERATEW *PFNDIRECTSOUNDCAPTUREENUMERATEW;
 
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+# define VBOX_DSOUND_MAX_EVENTS 3
+
+typedef enum DSOUNDEVENT
+{
+    DSOUNDEVENT_NOTIFY = 0,
+    DSOUNDEVENT_INPUT,
+    DSOUNDEVENT_OUTPUT,
+ } DSOUNDEVENT;
+#endif /* VBOX_WITH_AUDIO_CALLBACKS */
+
 typedef struct DSOUNDHOSTCFG
 {
@@ -76,4 +87,27 @@
     LPCGUID pGuidCapture;
 } DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
+
+typedef struct DSOUNDSTREAMOUT
+{
+    PDMAUDIOHSTSTRMOUT   strmOut; /* Always must come first! */
+    LPDIRECTSOUND8       pDS;
+    LPDIRECTSOUNDBUFFER8 pDSB;
+    DWORD                cbPlayWritePos;
+    DWORD                csPlaybackBufferSize;
+    bool                 fRestartPlayback;
+    PDMAUDIOSTREAMCFG    streamCfg;
+} DSOUNDSTREAMOUT, *PDSOUNDSTREAMOUT;
+
+typedef struct DSOUNDSTREAMIN
+{
+    PDMAUDIOHSTSTRMIN           strmIn; /* Always must come first! */
+    LPDIRECTSOUNDCAPTURE8       pDSC;
+    LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
+    DWORD                       csCaptureReadPos;
+    DWORD                       csCaptureBufferSize;
+    HRESULT                     hrLastCaptureIn;
+    PDMAUDIORECSOURCE           enmRecSource;
+    PDMAUDIOSTREAMCFG           streamCfg;
+} DSOUNDSTREAMIN, *PDSOUNDSTREAMIN;
 
 typedef struct DRVHOSTDSOUND
@@ -89,28 +123,20 @@
     /** DirectSound configuration options. */
     DSOUNDHOSTCFG cfg;
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    /** Stopped indicator. */
+    bool          fStopped;
+    /** Shutdown indicator. */
+    bool          fShutdown;
+    /** Notification thread. */
+    RTTHREAD      Thread;
+    /** Array of events to wait for in notification thread. */
+    HANDLE        aEvents[VBOX_DSOUND_MAX_EVENTS];
+    /** Number of events to wait for in notification thread.
+     *  Must not exceed VBOX_DSOUND_MAX_EVENTS. */
+    uint8_t       cEvents;
+    PDSOUNDSTREAMIN pStrmIn;
+    PDSOUNDSTREAMOUT pStrmOut;
+#endif
 } DRVHOSTDSOUND, *PDRVHOSTDSOUND;
-
-typedef struct DSOUNDSTREAMOUT
-{
-    PDMAUDIOHSTSTRMOUT   strmOut; /* Always must come first! */
-    LPDIRECTSOUND8       pDS;
-    LPDIRECTSOUNDBUFFER8 pDSB;
-    DWORD                cbPlayWritePos;
-    DWORD                csPlaybackBufferSize;
-    bool                 fRestartPlayback;
-    PDMAUDIOSTREAMCFG    streamCfg;
-} DSOUNDSTREAMOUT, *PDSOUNDSTREAMOUT;
-
-typedef struct DSOUNDSTREAMIN
-{
-    PDMAUDIOHSTSTRMIN           strmIn; /* Always must come first! */
-    LPDIRECTSOUNDCAPTURE8       pDSC;
-    LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
-    DWORD                       csCaptureReadPos;
-    DWORD                       csCaptureBufferSize;
-    HRESULT                     hrLastCaptureIn;
-    PDMAUDIORECSOURCE           enmRecSource;
-    PDMAUDIOSTREAMCFG           streamCfg;
-} DSOUNDSTREAMIN, *PDSOUNDSTREAMIN;
 
 /**
@@ -135,4 +161,7 @@
 
 static void dsoundDevRemove(PDSOUNDDEV pDev);
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown);
+#endif
 
 static DWORD dsoundRingDistance(DWORD offEnd, DWORD offBegin, DWORD cSize)
@@ -376,6 +405,9 @@
 }
 
-static void directSoundPlayClose(PDSOUNDSTREAMOUT pDSoundStrmOut)
-{
+static void directSoundPlayClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    AssertPtrReturnVoid(pThis);
+    AssertPtrReturnVoid(pDSoundStrmOut);
+
     DSLOG(("DSound: Closing playback stream %p, buffer %p\n", pDSoundStrmOut, pDSoundStrmOut->pDSB));
 
@@ -386,4 +418,16 @@
             DSLOGREL(("DSound: Stop playback stream %p when closing %Rhrc\n", pDSoundStrmOut, hr));
 
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+        if (pThis->aEvents[DSOUNDEVENT_OUTPUT] != NULL)
+        {
+            CloseHandle(pThis->aEvents[DSOUNDEVENT_OUTPUT]);
+            pThis->aEvents[DSOUNDEVENT_OUTPUT] = NULL;
+
+            if (pThis->cEvents)
+                pThis->cEvents--;
+
+            pThis->pStrmOut = NULL;
+        }
+#endif
         IDirectSoundBuffer8_Release(pDSoundStrmOut->pDSB);
         pDSoundStrmOut->pDSB = NULL;
@@ -410,5 +454,5 @@
         /* Should not happen but be forgiving. */
         DSLOGREL(("DSound: DirectSoundBuffer already exists\n"));
-        directSoundPlayClose(pDSoundStrmOut);
+        directSoundPlayClose(pThis, pDSoundStrmOut);
     }
 
@@ -425,12 +469,16 @@
     {
         LPDIRECTSOUNDBUFFER pDSB = NULL;
+
         DSBUFFERDESC bd;
         RT_ZERO(bd);
-        bd.dwSize = sizeof(bd);
-        bd.lpwfxFormat = &wfx;
-        bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+        bd.dwSize        = sizeof(bd);
+        bd.lpwfxFormat   = &wfx;
+        bd.dwFlags       = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+        bd.dwFlags      |= DSBCAPS_CTRLPOSITIONNOTIFY;
+#endif
         bd.dwBufferBytes = pThis->cfg.cbBufferOut;
-        hr = IDirectSound8_CreateSoundBuffer(pDSoundStrmOut->pDS,
-                                             &bd, &pDSB, NULL);
+
+        hr = IDirectSound8_CreateSoundBuffer(pDSoundStrmOut->pDS, &bd, &pDSB, NULL);
         if (FAILED(hr))
         {
@@ -439,7 +487,7 @@
         }
 
-        /* "Upgrade" to IDirectSoundBuffer8 intarface. */
-        hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (void **)&pDSoundStrmOut->pDSB);
-        pDSB->Release();
+        /* "Upgrade" to IDirectSoundBuffer8 interface. */
+        hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (LPVOID *)&pDSoundStrmOut->pDSB);
+        IDirectSoundBuffer_Release(pDSB);
         if (FAILED(hr))
         {
@@ -502,8 +550,56 @@
         DSLOG(("DSound: csPlaybackBufferSize=%RU32\n", pDSoundStrmOut->csPlaybackBufferSize));
 
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+        /*
+         * Install notification.
+         */
+        pThis->aEvents[DSOUNDEVENT_OUTPUT] = CreateEvent(NULL /* Security attribute */,
+                                                         FALSE /* bManualReset */, FALSE /* bInitialState */,
+                                                         NULL /* lpName */);
+        if (pThis->aEvents[DSOUNDEVENT_OUTPUT] == NULL)
+        {
+            hr = HRESULT_FROM_WIN32(GetLastError());
+            DSLOGREL(("DSound: CreateEvent for output failed with hr=%Rhrc\n", hr));
+            break;
+        }
+
+        LPDIRECTSOUNDNOTIFY8 pNotify;
+        hr = IDirectSoundNotify_QueryInterface(pDSoundStrmOut->pDSB, IID_IDirectSoundNotify8, (LPVOID *)&pNotify);
+        if (SUCCEEDED(hr))
+        {
+            DSBPOSITIONNOTIFY dsBufPosNotify;
+            RT_ZERO(dsBufPosNotify);
+            dsBufPosNotify.dwOffset     = DSBPN_OFFSETSTOP;
+            dsBufPosNotify.hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT];
+
+            hr = IDirectSoundNotify_SetNotificationPositions(pNotify, 1 /* Count */, &dsBufPosNotify);
+            if (FAILED(hr))
+                DSLOGREL(("DSound: IDirectSoundNotify_SetNotificationPositions failed with hr=%Rhrc\n", hr));
+
+            IDirectSoundNotify_Release(pNotify);
+        }
+        else
+            DSLOGREL(("DSound: IDirectSoundNotify_QueryInterface failed with hr=%Rhrc\n", hr));
+
+        if (FAILED(hr))
+            break;
+
+        pThis->pStrmOut = pDSoundStrmOut;
+
+        Assert(pThis->cEvents < VBOX_DSOUND_MAX_EVENTS);
+        pThis->cEvents++;
+
+        /* Let the thread know. */
+        dsoundNotifyThread(pThis, false /* fShutdown */);
+
+        /* Trigger the just installed output notification. */
+        hr = IDirectSoundBuffer8_Play(pDSoundStrmOut->pDSB, 0, 0, 0);
+
+#endif /* VBOX_WITH_AUDIO_CALLBACKS */
+
     } while (0);
 
     if (FAILED(hr))
-        directSoundPlayClose(pDSoundStrmOut);
+        directSoundPlayClose(pThis, pDSoundStrmOut);
 
     return hr;
@@ -1071,5 +1167,5 @@
             if (FAILED(hr))
             {
-                directSoundPlayClose(pDSoundStrmOut);
+                directSoundPlayClose(pThis, pDSoundStrmOut);
                 directSoundPlayOpen(pThis, pDSoundStrmOut);
 
@@ -1131,7 +1227,5 @@
             hr = directSoundPlayRestore(pDSB);
             if (SUCCEEDED(hr))
-            {
                 hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayPos, NULL);
-            }
         }
 
@@ -1241,9 +1335,10 @@
     PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
 
-    directSoundPlayClose(pDSoundStrmOut);
-
-    pDSoundStrmOut->cbPlayWritePos = 0;
-    pDSoundStrmOut->fRestartPlayback = true;
+    directSoundPlayClose(pThis, pDSoundStrmOut);
+
+    pDSoundStrmOut->cbPlayWritePos       = 0;
+    pDSoundStrmOut->fRestartPlayback     = true;
     pDSoundStrmOut->csPlaybackBufferSize = 0;
+
     RT_ZERO(pDSoundStrmOut->streamCfg);
 
@@ -1555,7 +1650,120 @@
 }
 
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    if (fShutdown)
+    {
+        LogFlowFunc(("Shutting down thread ...\n"));
+        pThis->fShutdown = fShutdown;
+    }
+
+    /* Set the notification event so that the thread is being notified. */
+    BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
+    Assert(fRc);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostDSoundThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+    PDRVHOSTDSOUND pThis = (PDRVHOSTDSOUND)pvUser;
+    AssertPtr(pThis);
+
+    LogFlowFuncEnter();
+
+    /* Let caller know that we're done initializing, regardless of the result. */
+    int rc = RTThreadUserSignal(hThreadSelf);
+    AssertRC(rc);
+
+    HRESULT hr;
+
+    do
+    {
+        HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];
+        DWORD  cEvents = 0;
+        for (uint8_t i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
+        {
+            if (pThis->aEvents[i])
+                aEvents[cEvents++] = pThis->aEvents[i];
+        }
+        Assert(cEvents);
+
+        LogFlowFunc(("Waiting: cEvents=%ld\n", cEvents));
+
+        DWORD dwObj = WaitForMultipleObjects(cEvents, aEvents, FALSE /* bWaitAll */, INFINITE);
+        switch (dwObj)
+        {
+            case WAIT_FAILED:
+            {
+                rc = VERR_CANCELLED;
+                break;
+            }
+
+            case WAIT_TIMEOUT:
+            {
+                rc = VERR_TIMEOUT;
+                break;
+            }
+
+            default:
+            {
+                dwObj = WAIT_OBJECT_0 + cEvents - 1;
+                if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_NOTIFY])
+                {
+                    LogFlowFunc(("Notify\n"));
+                }
+                else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_INPUT])
+                {
+
+                }
+                else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_OUTPUT])
+                {
+                    DWORD cbPlayPos;
+                    hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pThis->pStrmOut->pDSB, NULL, &cbPlayPos);
+                    LogFlowFunc(("Output: hr=%Rhrc, dwPlayPos=%ld\n", hr, cbPlayPos));
+                }
+                break;
+            }
+        }
+
+        if (pThis->fShutdown)
+            break;
+
+    } while (RT_SUCCESS(rc));
+
+    pThis->fStopped = true;
+
+    LogFlowFunc(("Exited with fShutdown=%RTbool, rc=%Rrc\n", pThis->fShutdown, rc));
+    return rc;
+}
+#endif /* VBOX_WITH_AUDIO_CALLBACKS */
+
 static DECLCALLBACK(void) drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
 {
-    NOREF(pInterface);
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+
+    LogFlowFuncEnter();
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    int rc = dsoundNotifyThread(pThis, true /* fShutdown */);
+    AssertRC(rc);
+
+    int rcThread;
+    rc = RTThreadWait(pThis->Thread,  15 * 1000 /* 15s timeout */, &rcThread);
+    LogFlowFunc(("rc=%Rrc, rcThread=%Rrc\n", rc, rcThread));
+
+    Assert(pThis->fStopped);
+
+    if (pThis->aEvents[DSOUNDEVENT_NOTIFY])
+    {
+        CloseHandle(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
+        pThis->aEvents[DSOUNDEVENT_NOTIFY] = NULL;
+    }
+#endif
+
+    LogFlowFuncLeave();
 }
 
@@ -1565,4 +1773,6 @@
 
     LogFlowFuncEnter();
+
+    int rc;
 
     /* Verify that IDirectSound is available. */
@@ -1571,9 +1781,34 @@
                                   IID_IDirectSound, (void **)&pDirectSound);
     if (SUCCEEDED(hr))
+    {
         IDirectSound_Release(pDirectSound);
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+        /* Create notification event. */
+        pThis->aEvents[DSOUNDEVENT_NOTIFY] = CreateEvent(NULL /* Security attribute */,
+                                                         FALSE /* bManualReset */, FALSE /* bInitialState */,
+                                                         NULL /* lpName */);
+        Assert(pThis->aEvents[DSOUNDEVENT_NOTIFY] != NULL);
+
+        /* Start notification thread. */
+        rc = RTThreadCreate(&pThis->Thread, drvHostDSoundThread,
+                            pThis /*pvUser*/, 0 /*cbStack*/,
+                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "dSoundNtfy");
+        if (RT_SUCCESS(rc))
+        {
+            /* Wait for the thread to initialize. */
+            rc = RTThreadUserWait(pThis->Thread, RT_MS_1MIN);
+            if (RT_FAILURE(rc))
+                DSLOGREL(("DSound: Waiting for thread to initialize failed with rc=%Rrc\n", rc));
+        }
+        else
+            DSLOGREL(("DSound: Creating thread failed with rc=%Rrc\n", rc));
+#endif
+    }
     else
+    {
         DSLOGREL(("DSound: DirectSound not available %Rhrc\n", hr));
-
-    int rc = SUCCEEDED(hr) ? VINF_SUCCESS: VERR_NOT_SUPPORTED;
+        rc = VERR_NOT_SUPPORTED;
+    }
 
     LogFlowFuncLeaveRC(rc);
@@ -1673,4 +1908,12 @@
     RTListInit(&pThis->lstDevInput);
     RTListInit(&pThis->lstDevOutput);
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    pThis->fStopped  = false;
+    pThis->fShutdown = false;
+
+    RT_ZERO(pThis->aEvents);
+    pThis->cEvents = 0;
+#endif
 
     /*
@@ -1698,5 +1941,5 @@
     "DirectSound Audio host driver",
     /* fFlags */
-     PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
     /* fClass. */
     PDM_DRVREG_CLASS_AUDIO,
