dsound: Report buffer notifications in offset order.

This commit is contained in:
Andrew Eikum 2014-12-03 14:06:38 -06:00 committed by Alexandre Julliard
parent 5b7e49e84a
commit ee126c96f1
3 changed files with 165 additions and 40 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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;