diff --git a/dlls/dsound/buffer.c b/dlls/dsound/buffer.c index e8fc5f30b44..9794b8434a6 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 43b7dcaf069..8d6b379a9fe 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); + int first, left, right, check; - if (dsb->nrofnotifies == 0) - return; + 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); - } - } - } + 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 10a1032e319..1df7ce4e30e 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 + 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;