[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