[PATCH] dsound: Report buffer notifications in offset order

Andrew Eikum aeikum at codeweavers.com
Wed Dec 3 14:06:38 CST 2014


---
 dlls/dsound/buffer.c       |  22 ++++++++++
 dlls/dsound/mixer.c        | 103 +++++++++++++++++++++++++++------------------
 dlls/dsound/tests/dsound.c |  84 ++++++++++++++++++++++++++++++++++++
 3 files changed, 167 insertions(+), 42 deletions(-)

diff --git a/dlls/dsound/buffer.c b/dlls/dsound/buffer.c
index e8fc5f3..9794b84 100644
--- a/dlls/dsound/buffer.c
+++ b/dlls/dsound/buffer.c
@@ -81,6 +81,27 @@ static ULONG WINAPI IDirectSoundNotifyImpl_Release(IDirectSoundNotify *iface)
     return ref;
 }
 
+static int notify_compar(const void *l, const void *r)
+{
+    const DSBPOSITIONNOTIFY *left = l;
+    const DSBPOSITIONNOTIFY *right = r;
+
+    /* place DSBPN_OFFSETSTOP at the start of the sorted array */
+    if(left->dwOffset == DSBPN_OFFSETSTOP){
+        if(right->dwOffset != DSBPN_OFFSETSTOP)
+            return -1;
+    }else if(right->dwOffset == DSBPN_OFFSETSTOP)
+        return 1;
+
+    if(left->dwOffset == right->dwOffset)
+        return 0;
+
+    if(left->dwOffset < right->dwOffset)
+        return -1;
+
+    return 1;
+}
+
 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(IDirectSoundNotify *iface,
         DWORD howmuch, const DSBPOSITIONNOTIFY *notify)
 {
@@ -113,6 +134,7 @@ static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(IDirectSou
 	    }
             CopyMemory(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
             This->nrofnotifies = howmuch;
+            qsort(This->notifies, howmuch, sizeof(DSBPOSITIONNOTIFY), notify_compar);
 	} else {
            HeapFree(GetProcessHeap(), 0, This->notifies);
            This->notifies = NULL;
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 43b7dca..8d6b379 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -183,48 +183,67 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
  */
 void DSOUND_CheckEvent(const IDirectSoundBufferImpl *dsb, DWORD playpos, int len)
 {
-	int			i;
-	DWORD			offset;
-	LPDSBPOSITIONNOTIFY	event;
-	TRACE("(%p,%d)\n",dsb,len);
-
-	if (dsb->nrofnotifies == 0)
-		return;
-
-	TRACE("(%p) buflen = %d, playpos = %d, len = %d\n",
-		dsb, dsb->buflen, playpos, len);
-	for (i = 0; i < dsb->nrofnotifies ; i++) {
-		event = dsb->notifies + i;
-		offset = event->dwOffset;
-		TRACE("checking %d, position %d, event = %p\n",
-			i, offset, event->hEventNotify);
-		/* DSBPN_OFFSETSTOP has to be the last element. So this is */
-		/* OK. [Inside DirectX, p274] */
-		/* Windows does not seem to enforce this, and some apps rely */
-		/* on that, so we can't stop there. */
-		/*  */
-		/* This also means we can't sort the entries by offset, */
-		/* because DSBPN_OFFSETSTOP == -1 */
-		if (offset == DSBPN_OFFSETSTOP) {
-			if (dsb->state == STATE_STOPPED) {
-				SetEvent(event->hEventNotify);
-				TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
-			}
-                        continue;
-		}
-		if ((playpos + len) >= dsb->buflen) {
-			if ((offset < ((playpos + len) % dsb->buflen)) ||
-			    (offset >= playpos)) {
-				TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
-				SetEvent(event->hEventNotify);
-			}
-		} else {
-			if ((offset >= playpos) && (offset < (playpos + len))) {
-				TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
-				SetEvent(event->hEventNotify);
-			}
-		}
-	}
+    int first, left, right, check;
+
+    if(dsb->nrofnotifies == 0)
+        return;
+
+    if(dsb->state == STATE_STOPPED){
+        TRACE("Stopped...\n");
+        /* DSBPN_OFFSETSTOP notifies are always at the start of the sorted array */
+        for(left = 0; left < dsb->nrofnotifies; ++left){
+            if(dsb->notifies[left].dwOffset != DSBPN_OFFSETSTOP)
+                break;
+
+            TRACE("Signalling %p\n", dsb->notifies[left].hEventNotify);
+            SetEvent(dsb->notifies[left].hEventNotify);
+        }
+        return;
+    }
+
+    for(first = 0; first < dsb->nrofnotifies && dsb->notifies[first].dwOffset == DSBPN_OFFSETSTOP; ++first)
+        ;
+
+    if(first == dsb->nrofnotifies)
+        return;
+
+    check = left = first;
+    right = dsb->nrofnotifies - 1;
+
+    /* find leftmost notify that is greater than playpos */
+    while(left != right){
+        check = left + (right - left) / 2;
+        if(dsb->notifies[check].dwOffset < playpos)
+            left = check + 1;
+        else if(dsb->notifies[check].dwOffset > playpos)
+            right = check;
+        else{
+            left = check;
+            break;
+        }
+    }
+
+    TRACE("Not stopped: first notify: %u (%u), range: [%u,%u)\n", first,
+            dsb->notifies[check].dwOffset, playpos, (playpos + len) % dsb->buflen);
+
+    /* send notifications in range */
+    for(check = left; check < dsb->nrofnotifies; ++check){
+        if(dsb->notifies[check].dwOffset >= playpos + len)
+            break;
+
+        TRACE("Signalling %p (%u)\n", dsb->notifies[check].hEventNotify, dsb->notifies[check].dwOffset);
+        SetEvent(dsb->notifies[check].hEventNotify);
+    }
+
+    if(playpos + len > dsb->buflen){
+        for(check = first; check < left; ++check){
+            if(dsb->notifies[check].dwOffset >= (playpos + len) % dsb->buflen)
+                break;
+
+            TRACE("Signalling %p (%u)\n", dsb->notifies[check].hEventNotify, dsb->notifies[check].dwOffset);
+            SetEvent(dsb->notifies[check].hEventNotify);
+        }
+    }
 }
 
 static inline float get_current_sample(const IDirectSoundBufferImpl *dsb,
diff --git a/dlls/dsound/tests/dsound.c b/dlls/dsound/tests/dsound.c
index 10a1032..ad01809 100644
--- a/dlls/dsound/tests/dsound.c
+++ b/dlls/dsound/tests/dsound.c
@@ -1486,6 +1486,89 @@ done:
     return S_OK;
 }
 
+static void test_notifications(LPGUID lpGuid)
+{
+    HRESULT rc;
+    IDirectSound *dso;
+    IDirectSoundBuffer *buf;
+    IDirectSoundNotify *buf_notif;
+    DSBUFFERDESC bufdesc;
+    WAVEFORMATEX wfx;
+    DSBPOSITIONNOTIFY notifies[2];
+    HANDLE handles[2];
+    DWORD expect;
+    int cycles;
+
+    rc = pDirectSoundCreate(lpGuid, &dso, NULL);
+    ok(rc == DS_OK || rc == DSERR_NODRIVER || rc == DSERR_ALLOCATED,
+           "DirectSoundCreate() failed: %08x\n", rc);
+    if(rc != DS_OK)
+        return;
+
+    rc = IDirectSound_SetCooperativeLevel(dso, get_hwnd(), DSSCL_PRIORITY);
+    ok(rc == DS_OK, "IDirectSound_SetCooperativeLevel() failed: %08x\n", rc);
+    if(rc != DS_OK){
+        IDirectSound_Release(dso);
+        return;
+    }
+
+    wfx.wFormatTag = WAVE_FORMAT_PCM;
+    wfx.nChannels = 1;
+    wfx.nSamplesPerSec = 44100;
+    wfx.wBitsPerSample = 16;
+    wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
+    wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
+    wfx.cbSize = 0;
+
+    ZeroMemory(&bufdesc, sizeof(bufdesc));
+    bufdesc.dwSize = sizeof(bufdesc);
+    bufdesc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY;
+    bufdesc.dwBufferBytes = wfx.nSamplesPerSec * wfx.nBlockAlign / 2; /* 0.5s */
+    bufdesc.lpwfxFormat = &wfx;
+    rc = IDirectSound_CreateSoundBuffer(dso, &bufdesc, &buf, NULL);
+    ok(rc == DS_OK && buf != NULL, "IDirectSound_CreateSoundBuffer() failed "
+           "to create a buffer %08x\n", rc);
+
+    rc = IDirectSoundBuffer_QueryInterface(buf, &IID_IDirectSoundNotify, (void**)&buf_notif);
+    ok(rc == DS_OK, "QueryInterface(IID_IDirectSoundNotify): %08x\n", rc);
+
+    /* create notifications at each end of the buffer */
+    notifies[0].dwOffset = 0;
+    handles[0] = notifies[0].hEventNotify = CreateEventW(NULL, FALSE, FALSE, NULL);
+    notifies[1].dwOffset = bufdesc.dwBufferBytes - 1;
+    handles[1] = notifies[1].hEventNotify = CreateEventW(NULL, FALSE, FALSE, NULL);
+
+    rc = IDirectSoundNotify_SetNotificationPositions(buf_notif, 2, notifies);
+    ok(rc == DS_OK, "SetNotificationPositions: %08x\n", rc);
+
+    IDirectSoundNotify_Release(buf_notif);
+
+    rc = IDirectSoundBuffer_Play(buf, 0, 0, DSBPLAY_LOOPING);
+    ok(rc == DS_OK, "Play: %08x\n", rc);
+
+    expect = 0;
+    for(cycles = 0; cycles < 6 /* 1.5s */; ++cycles){
+        DWORD wait;
+
+        /* since the notifications are on opposite ends of the entire buffer,
+         * they should arrive well-ordered in an alternating sequence. */
+        wait = WaitForMultipleObjects(2, handles, FALSE, 1000);
+        ok(wait >= WAIT_OBJECT_0 && wait <= WAIT_OBJECT_0 + 1 &&
+                wait - WAIT_OBJECT_0 == expect, "Got unexpected notification order or timeout: %u\n", wait);
+
+        expect = !expect;
+    }
+
+    rc = IDirectSoundBuffer_Stop(buf);
+    ok(rc == DS_OK, "Stop: %08x\n", rc);
+
+    CloseHandle(notifies[0].hEventNotify);
+    CloseHandle(notifies[1].hEventNotify);
+
+    IDirectSoundBuffer_Release(buf);
+    IDirectSound_Release(dso);
+}
+
 static unsigned int number;
 
 static BOOL WINAPI dsenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
@@ -1516,6 +1599,7 @@ static BOOL WINAPI dsenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
         test_frequency(lpGuid);
         test_duplicate(lpGuid);
         test_invalid_fmts(lpGuid);
+        test_notifications(lpGuid);
     }
 
     return TRUE;
-- 
2.1.3




More information about the wine-patches mailing list