Fix a couple of problems with underruns and stopping/restarting.

This commit is contained in:
Ove Kaaven 2000-07-08 11:45:39 +00:00 committed by Alexandre Julliard
parent 257ca83d04
commit 9405793109
1 changed files with 52 additions and 29 deletions

View File

@ -111,7 +111,7 @@ struct IDirectSoundBufferImpl
LPBYTE buffer; LPBYTE buffer;
IDirectSound3DBufferImpl* ds3db; IDirectSound3DBufferImpl* ds3db;
DWORD playflags,state,leadin; DWORD playflags,state,leadin;
DWORD playpos,mixpos,writelead,buflen; DWORD playpos,mixpos,startpos,writelead,buflen;
DWORD nAvgBytesPerSec; DWORD nAvgBytesPerSec;
DWORD freq; DWORD freq;
ULONG freqAdjust; ULONG freqAdjust;
@ -1150,8 +1150,9 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Play(
); );
This->playflags = flags; This->playflags = flags;
if (This->state == STATE_STOPPED) { if (This->state == STATE_STOPPED) {
This->state = STATE_STARTING;
This->leadin = TRUE; This->leadin = TRUE;
This->startpos = This->mixpos;
This->state = STATE_STARTING;
} else if (This->state == STATE_STOPPING) } else if (This->state == STATE_STOPPING)
This->state = STATE_PLAYING; This->state = STATE_PLAYING;
if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) { if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
@ -1274,10 +1275,10 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
} else { } else {
if (playpos && (This->state != STATE_PLAYING)) { if (playpos && (This->state != STATE_PLAYING)) {
/* we haven't been merged into the primary buffer (yet) */ /* we haven't been merged into the primary buffer (yet) */
*playpos = 0; *playpos = This->mixpos;
} }
else if (playpos) { else if (playpos) {
DWORD pplay, lplay, splay, pstate; DWORD pplay, lplay, splay, tplay, pstate;
/* let's get this exact; first, recursively call GetPosition on the primary */ /* let's get this exact; first, recursively call GetPosition on the primary */
EnterCriticalSection(&(primarybuf->lock)); EnterCriticalSection(&(primarybuf->lock));
if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) { if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) {
@ -1290,14 +1291,23 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
/* get last mixed primary play position */ /* get last mixed primary play position */
lplay = primarybuf->mixpos; lplay = primarybuf->mixpos;
pstate = primarybuf->state; pstate = primarybuf->state;
/* detect HEL mode underrun */
if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
TRACE("detected an underrun\n");
pplay = lplay;
if (pstate == STATE_PLAYING)
pstate = STATE_STARTING;
else if (pstate == STATE_STOPPING)
pstate = STATE_STOPPED;
}
/* get our own last mixed position while we still have the lock */ /* get our own last mixed position while we still have the lock */
splay = This->mixpos; splay = This->mixpos;
LeaveCriticalSection(&(primarybuf->lock)); LeaveCriticalSection(&(primarybuf->lock));
TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, lplay); TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, lplay);
TRACE("this mixpos=%ld\n", splay); TRACE("this mixpos=%ld\n", splay);
/* the actual primary play position (pplay) is always behind last mixed (lplay) /* the actual primary play position (pplay) is always behind last mixed (lplay),
* (unless the computer is too slow, which we can't fix anyway) */ * unless the computer is too slow or something */
/* we need to know how far away we are from there */ /* we need to know how far away we are from there */
if (lplay == pplay) { if (lplay == pplay) {
if ((pstate == STATE_PLAYING) || (pstate == STATE_STOPPING)) { if ((pstate == STATE_PLAYING) || (pstate == STATE_STOPPING)) {
@ -1309,6 +1319,12 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
} }
if (lplay < pplay) lplay += primarybuf->buflen; /* wraparound */ if (lplay < pplay) lplay += primarybuf->buflen; /* wraparound */
lplay -= pplay; lplay -= pplay;
/* detect HAL mode underrun */
if (primarybuf->hwbuf &&
(lplay > ((DS_HAL_QUEUE + 1) * primarybuf->dsound->fraglen + primarybuf->writelead))) {
TRACE("detected an underrun: primary queue was %ld\n",lplay);
lplay = 0;
}
/* divide the offset by its sample size */ /* divide the offset by its sample size */
lplay /= primarybuf->wfx.nChannels * (primarybuf->wfx.wBitsPerSample / 8); lplay /= primarybuf->wfx.nChannels * (primarybuf->wfx.wBitsPerSample / 8);
TRACE("primary back-samples=%ld\n",lplay); TRACE("primary back-samples=%ld\n",lplay);
@ -1318,24 +1334,26 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
lplay *= This->wfx.nChannels * (This->wfx.wBitsPerSample / 8); lplay *= This->wfx.nChannels * (This->wfx.wBitsPerSample / 8);
TRACE("this back-offset=%ld\n", lplay); TRACE("this back-offset=%ld\n", lplay);
/* subtract from our last mixed position */ /* subtract from our last mixed position */
if ((splay < lplay) && This->leadin) { tplay = splay;
while (tplay < lplay) tplay += This->buflen; /* wraparound */
tplay -= lplay;
if (This->leadin && ((tplay < This->startpos) || (tplay > splay))) {
/* seems we haven't started playing yet */ /* seems we haven't started playing yet */
splay = 0; TRACE("this still in lead-in phase\n");
} else { tplay = This->startpos;
while (splay < lplay) splay += This->buflen; /* wraparound */
splay -= lplay;
} }
/* return the result */ /* return the result */
*playpos = splay; *playpos = tplay;
} }
if (writepos) *writepos = This->mixpos; if (writepos) *writepos = This->mixpos;
} }
/* apply the documented 10ms lead to writepos */
if (writepos) { if (writepos) {
if (This->state != STATE_STOPPED)
/* apply the documented 10ms lead to writepos */
*writepos += This->writelead; *writepos += This->writelead;
while (*writepos >= This->buflen) *writepos -= This->buflen; while (*writepos >= This->buflen) *writepos -= This->buflen;
} }
TRACE("playpos = %ld, writepos = %ld\n", playpos?*playpos:0, writepos?*writepos:0); TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
return DS_OK; return DS_OK;
} }
@ -2321,7 +2339,7 @@ static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
ibp = dsb->buffer + dsb->mixpos; ibp = dsb->buffer + dsb->mixpos;
obp = buf; obp = buf;
TRACE("(%p, %p, %p), mixpos=%8.8lx\n", dsb, ibp, obp, dsb->mixpos); TRACE("(%p, %p, %p), mixpos=%ld\n", dsb, ibp, obp, dsb->mixpos);
/* Check for the best case */ /* Check for the best case */
if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) && if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
(dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) && (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
@ -2492,6 +2510,7 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
dsb->state = STATE_STOPPED; dsb->state = STATE_STOPPED;
dsb->playpos = 0; dsb->playpos = 0;
dsb->mixpos = 0; dsb->mixpos = 0;
dsb->leadin = FALSE;
/* Check for DSBPN_OFFSETSTOP */ /* Check for DSBPN_OFFSETSTOP */
DSOUND_CheckEvent(dsb, 0); DSOUND_CheckEvent(dsb, 0);
return 0; return 0;
@ -2538,28 +2557,32 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY) if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
DSOUND_CheckEvent(dsb, ilen); DSOUND_CheckEvent(dsb, ilen);
if (dsb->leadin && (dsb->startpos > dsb->mixpos) && (dsb->startpos <= dsb->mixpos + ilen)) {
/* HACK... leadin should be reset when the PLAY position reaches the startpos,
* not the MIX position... but if the sound buffer is bigger than our prebuffering
* (which must be the case for the streaming buffers that need this hack anyway)
* plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
dsb->leadin = FALSE;
}
dsb->mixpos += ilen; dsb->mixpos += ilen;
/* dsb->writepos = dsb->playpos + ilen; */
if (dsb->mixpos >= dsb->buflen) { if (dsb->mixpos >= dsb->buflen) {
if (!(dsb->playflags & DSBPLAY_LOOPING)) { if (!(dsb->playflags & DSBPLAY_LOOPING)) {
dsb->state = STATE_STOPPED; dsb->state = STATE_STOPPED;
dsb->playpos = 0; dsb->playpos = 0;
dsb->mixpos = 0; dsb->mixpos = 0;
dsb->leadin = FALSE;
DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */ DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
} else { } else {
dsb->mixpos %= dsb->buflen; /* wrap */ /* wrap */
/* HACK... leadin should be reset when the PLAY position reaches the wrap, while (dsb->mixpos >= dsb->buflen)
* not the MIX position... but if the sound buffer is bigger than our prebuffering dsb->mixpos -= dsb->buflen;
* (which must be the case for the streaming buffers that need this hack anyway) if (dsb->leadin && (dsb->startpos <= dsb->mixpos))
* plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */ dsb->leadin = FALSE; /* HACK: see above */
dsb->leadin = FALSE;
} }
} }
/* if (dsb->writepos >= dsb->buflen)
dsb->writepos %= dsb->buflen; */
return len; return len;
} }
@ -2675,6 +2698,8 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
frag = DS_HAL_QUEUE * dsound->fraglen; frag = DS_HAL_QUEUE * dsound->fraglen;
if (maxq > frag) maxq = frag; if (maxq > frag) maxq = frag;
EnterCriticalSection(&(primarybuf->lock));
/* check for consistency */ /* check for consistency */
if (inq > maxq) { if (inq > maxq) {
/* the playback position must have passed our last /* the playback position must have passed our last
@ -2706,8 +2731,6 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
paused = TRUE; paused = TRUE;
} }
EnterCriticalSection(&(primarybuf->lock));
/* see if some new buffers have been started that we want to merge into our prebuffer; /* see if some new buffers have been started that we want to merge into our prebuffer;
* this should minimize latency even when we have a large prebuffer */ * this should minimize latency even when we have a large prebuffer */
if (!paused) { if (!paused) {
@ -2809,6 +2832,7 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
} }
primarybuf->playpos = dsound->pwplay * dsound->fraglen; primarybuf->playpos = dsound->pwplay * dsound->fraglen;
TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->mixpos); TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->mixpos);
EnterCriticalSection(&(primarybuf->lock));
if (!dsound->pwqueue) { if (!dsound->pwqueue) {
/* this is either an underrun or we have nothing more to play... /* this is either an underrun or we have nothing more to play...
* since playback has already stopped now, we can enter pause mode, * since playback has already stopped now, we can enter pause mode,
@ -2827,7 +2851,6 @@ static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw
writepos = primarybuf->playpos + DS_HEL_MARGIN * dsound->fraglen; writepos = primarybuf->playpos + DS_HEL_MARGIN * dsound->fraglen;
while (writepos >= primarybuf->buflen) writepos -= primarybuf->buflen; while (writepos >= primarybuf->buflen) writepos -= primarybuf->buflen;
EnterCriticalSection(&(primarybuf->lock));
/* see if some new buffers have been started that we want to merge into our prebuffer; /* see if some new buffers have been started that we want to merge into our prebuffer;
* this should minimize latency even when we have a large prebuffer */ * this should minimize latency even when we have a large prebuffer */
if (dsound->priolevel != DSSCL_WRITEPRIMARY) { if (dsound->priolevel != DSSCL_WRITEPRIMARY) {