1. the improper play position return by DSOUND_PrimaryGetPosition - beffer underrun,
2. and the notify mechanics we used - sound data skipped.
Please refer to the comment for the first problem.
DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len) original use current play position and the count of data TO BE played to check if a notify should be generated. That's to say, an application may be notified before the data at the desired offset really played. Since the application got the notify that the data in it's demand region is useless (played), it might call IDirectSoundBufferImpl_Lock to lock this region, which contains useful data, IDirectSoundBufferImpl_Lock will handle the conflict by cancelling the region by default.
NOTE:
There are still problems with DSBPN_OFFSETSTOP event. If the buffer's state is STATE_STOPPED, my patch will set the event each time DSOUND_PerformMix called by the timer! I know it's a bad idea, so if anyone have a better solution, please re-patch it.
ChangeLog: - Fixed buffer underrun & sound chunks left out.
Index: dlls/dsound/buffer.c =================================================================== RCS file: /home/wine/wine/dlls/dsound/buffer.c,v retrieving revision 1.7 diff -u -r1.7 buffer.c --- dlls/dsound/buffer.c 15 Mar 2003 00:54:12 -0000 1.7 +++ dlls/dsound/buffer.c 16 Mar 2003 15:27:35 -0000 @@ -463,7 +463,10 @@ *writepos += This->writelead; while (*writepos >= This->buflen) *writepos -= This->buflen; } - if (playpos) This->last_playpos = *playpos; + if (playpos) { + This->last_playpos = This->playpos; + This->playpos = *playpos; + } TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount()); return DS_OK; } @@ -586,19 +589,19 @@ /* if the segment between playpos and buf_mixpos is touched, * we need to cancel some mixing */ /* we'll assume that the app always calls GetCurrentPosition before - * locking a playing buffer, so that last_playpos is up-to-date */ - if (This->buf_mixpos >= This->last_playpos) { + * locking a playing buffer, so that laypos is up-to-date */ + if (This->buf_mixpos >= This->playpos) {/* PPPPMMMMMPP */ if (This->buf_mixpos > writecursor && - This->last_playpos < writecursor+writebytes) - remix = TRUE; + This->playpos < writecursor+writebytes)/* PPPPMMMMMPP */ + remix = TRUE; /* WWWW */ } - else { + else {/* MMMMPPPPMMM */ if (This->buf_mixpos > writecursor || - This->last_playpos < writecursor+writebytes) - remix = TRUE; + This->playpos < writecursor+writebytes)/* MMMMPPPPMMM */ + remix = TRUE; /* WWWWWW */ } if (remix) { - TRACE("locking prebuffered region, ouch\n"); + FIXME("locking prebuffered region, ouch\n"); DSOUND_MixCancelAt(This, writecursor); } } Index: dlls/dsound/mixer.c =================================================================== RCS file: /home/wine/wine/dlls/dsound/mixer.c,v retrieving revision 1.9 diff -u -r1.9 mixer.c --- dlls/dsound/mixer.c 15 Mar 2003 00:54:12 -0000 1.9 +++ dlls/dsound/mixer.c 16 Mar 2003 15:27:52 -0000 @@ -98,10 +98,12 @@ if (dsb->state == STATE_STOPPED) { SetEvent(event->hEventNotify); TRACE("signalled event %p (%d)\n", event->hEventNotify, i); - return; + break; } else - return; + break; } +#if 0 /* "len" is the count of data TO BE played, not the count of data that played. It'll + * make IDirectSoundBufferImpl_Lock to lock a prebuffered region. */ if ((dsb->playpos + len) >= dsb->buflen) { if ((offset < ((dsb->playpos + len) % dsb->buflen)) || (offset >= dsb->playpos)) { @@ -114,7 +116,22 @@ SetEvent(event->hEventNotify); } } +#else /* #if 0 */ + if (dsb->last_playpos > dsb->playpos) { /* PPPPMMMMPPP P-played data M-mixed data */ + if (offset < dsb->playpos || offset >= dsb->last_playpos) {/* POPPMMMPPP || PPPPMMMMPOP */ + SetEvent(event->hEventNotify); + } + } + else { /* MMMMPPPPPMMM */ + if (offset >= dsb->last_playpos && offset < dsb->playpos) {/* MMMMPOPPPMMM */ + SetEvent(event->hEventNotify); + } + } +#endif /* #if 0 #else */ } + + // Make sure that the event won't be triger twice. + dsb->last_playpos = dsb->playpos; } /* WAV format info can be found at: @@ -569,8 +586,15 @@ mixlen); TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin); +#if 0 /* Seems no one used this value for non-GETCURRENTPOSITION2, and it makes DSOUND_CheckEvent + * trigger notifies ahead of scheeule. */ /* save write position for non-GETCURRENTPOSITION2... */ dsb->playpos = buf_writepos; +#else // Update play position for DSOUND_CheckEvent + dsb->last_playpos = dsb->playpos; + dsb->playpos = DSOUND_CalcPlayPosition(dsb, dsb->state & dsb->dsound->state, playpos, + writepos, dsb->primary_mixpos, dsb->buf_mixpos); +#endif /* #if 0 #else */ /* check whether CalcPlayPosition detected a mixing underrun */ if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) { @@ -688,7 +712,13 @@ if (!dsb || !(ICOM_VTBL(dsb))) continue; - if (dsb->buflen && dsb->state && !dsb->hwbuf) { + if (STATE_STOPPED == dsb->state) { + /* I reeally appreciate that if someone can move the code to a better place or + * find another way to set DSBPN_OFFSETSTOP event. */ + DSOUND_CheckEvent(dsb, 0); + } + else if (dsb->buflen && !dsb->hwbuf) { + /* if (dsb->buflen && dsb->state && !dsb->hwbuf) { */ TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen); EnterCriticalSection(&(dsb->lock)); if (dsb->state == STATE_STOPPING) { Index: dlls/dsound/primary.c =================================================================== RCS file: /home/wine/wine/dlls/dsound/primary.c,v retrieving revision 1.9 diff -u -r1.9 primary.c --- dlls/dsound/primary.c 15 Mar 2003 00:54:12 -0000 1.9 +++ dlls/dsound/primary.c 16 Mar 2003 15:28:07 -0000 @@ -245,11 +245,19 @@ } else { if (playpos) { +#if 0 /* Well, it apears that waveOutGetPosition can't return the correct value, + * it returns 0, 4096, 8192, etc, and far away from the real play position. + * The code here to caculate the primary play position will be similar with + * the that in DSOUND_PerformMix, IDirectSoundBufferImpl_GetCurrentPosition, etc. */ MMTIME mtime; mtime.wType = TIME_BYTES; waveOutGetPosition(This->hwo, &mtime, sizeof(mtime)); mtime.u.cb = mtime.u.cb % This->buflen; *playpos = mtime.u.cb; +#else /* #if 0 */ + *playpos = This->pwplay * This->fraglen; +#endif /* #if 0 #else */ } if (writepos) { /* the writepos should only be used by apps with WRITEPRIMARY priority,