Fix a couple of problems with underruns and stopping/restarting.
This commit is contained in:
parent
257ca83d04
commit
9405793109
|
@ -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) {
|
||||||
*writepos += This->writelead;
|
if (This->state != STATE_STOPPED)
|
||||||
|
/* apply the documented 10ms lead to writepos */
|
||||||
|
*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,27 +2557,31 @@ 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) {
|
||||||
|
|
Loading…
Reference in New Issue