[prev in list] [next in list] [prev in thread] [next in thread] 

List:       wine-patches
Subject:    dsound bugfixes
From:       Ove Kaaven <ovek () arcticnet ! no>
Date:       2000-06-28 21:14:28
[Download RAW message or body]

Curse of Monkey Island gets really confused if a buffer underrun occurs
during video sequences (the rest of the video sequence gets played in
"skip-motion" since DirectSoundBuffer::Stop currently "stops immediately"  
at the last prebuffered position (300ms ahead of the playing position),
which forces the game to stop again to skip ahead those 300ms as soon as
it tries to restart playback, and so it keeps skipping ahead for the rest
of the sequence).

This patch lets DirectSoundBuffer::GetCurrentPosition detect underruns
itself and treat them sensibly (by reporting last mixed position, so the
game doesn't get confused), so a buffer underrun will not cause anything
more serious than an ordinary sound glitch.

(Of course, it would also be good if we could keep the underruns from
happening in the first place... it looks like in CMI, the service thread
might be too occupied to call DSOUND_timer often enough?)

Also included is a fix for Play when the starting playposition isn't the
beginning of the buffer...

Log:
	Ove Kaaven <ovek@transgaming.com>
	Fix a couple of problems with underruns and stopping/restarting.

Index: wine/dlls/dsound/dsound_main.c
===================================================================
RCS file: /home/wine/wine/dlls/dsound/dsound_main.c,v
retrieving revision 1.23
diff -u -r1.23 dsound_main.c
--- wine/dlls/dsound/dsound_main.c	2000/06/24 12:54:49	1.23
+++ wine/dlls/dsound/dsound_main.c	2000/06/28 20:51:47
@@ -111,7 +111,7 @@
     LPBYTE                    buffer;
     IDirectSound3DBufferImpl* ds3db;
     DWORD                     playflags,state,leadin;
-    DWORD                     playpos,mixpos,writelead,buflen;
+    DWORD                     playpos,mixpos,startpos,writelead,buflen;
     DWORD                     nAvgBytesPerSec;
     DWORD                     freq;
     ULONG                     freqAdjust;
@@ -1150,8 +1150,9 @@
 	);
 	This->playflags = flags;
 	if (This->state == STATE_STOPPED) {
-		This->state = STATE_STARTING;
 		This->leadin = TRUE;
+		This->startpos = This->mixpos;
+		This->state = STATE_STARTING;
 	} else if (This->state == STATE_STOPPING)
 		This->state = STATE_PLAYING;
 	if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
@@ -1274,10 +1275,10 @@
 	} else {
 		if (playpos && (This->state != STATE_PLAYING)) {
 			/* we haven't been merged into the primary buffer (yet) */
-			*playpos = 0;
+			*playpos = This->mixpos;
 		}
 		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 */
 			EnterCriticalSection(&(primarybuf->lock));
 			if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) {
@@ -1290,14 +1291,23 @@
 			/* get last mixed primary play position */
 			lplay = primarybuf->mixpos;
 			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 */
 			splay = This->mixpos;
 			LeaveCriticalSection(&(primarybuf->lock));
 			TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, lplay);
 			TRACE("this mixpos=%ld\n", splay);
 
-			/* the actual primary play position (pplay) is always behind last mixed (lplay)
-			 * (unless the computer is too slow, which we can't fix anyway) */
+			/* the actual primary play position (pplay) is always behind last mixed (lplay),
+			 * unless the computer is too slow or something */
 			/* we need to know how far away we are from there */
 			if (lplay == pplay) {
 				if ((pstate == STATE_PLAYING) || (pstate == STATE_STOPPING)) {
@@ -1309,6 +1319,12 @@
 			}
 			if (lplay < pplay) lplay += primarybuf->buflen; /* wraparound */
 			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 */
 			lplay /= primarybuf->wfx.nChannels * (primarybuf->wfx.wBitsPerSample / 8);
 			TRACE("primary back-samples=%ld\n",lplay);
@@ -1318,24 +1334,26 @@
 			lplay *= This->wfx.nChannels * (This->wfx.wBitsPerSample / 8);
 			TRACE("this back-offset=%ld\n", lplay);
 			/* 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 */
-				splay = 0;
-			} else {
-				while (splay < lplay) splay += This->buflen; /* wraparound */
-				splay -= lplay;
+				TRACE("this still in lead-in phase\n");
+				tplay = This->startpos;
 			}
 			/* return the result */
-			*playpos = splay;
+			*playpos = tplay;
 		}
 		if (writepos) *writepos = This->mixpos;
 	}
-	/* apply the documented 10ms lead to 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;
 	}
-	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;
 }
 
@@ -2321,7 +2339,7 @@
 	ibp = dsb->buffer + dsb->mixpos;
 	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 */
 	if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
 	    (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
@@ -2492,6 +2510,7 @@
 		dsb->state = STATE_STOPPED;
 		dsb->playpos = 0;
 		dsb->mixpos = 0;
+		dsb->leadin = FALSE;
 		/* Check for DSBPN_OFFSETSTOP */
 		DSOUND_CheckEvent(dsb, 0);
 		return 0;
@@ -2538,27 +2557,31 @@
 	if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
 		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->writepos = dsb->playpos + ilen; */
 	
 	if (dsb->mixpos >= dsb->buflen) {
 		if (!(dsb->playflags & DSBPLAY_LOOPING)) {
 			dsb->state = STATE_STOPPED;
 			dsb->playpos = 0;
 			dsb->mixpos = 0;
+			dsb->leadin = FALSE;
 			DSOUND_CheckEvent(dsb, 0);		/* For DSBPN_OFFSETSTOP */
 		} else {
-			dsb->mixpos %= dsb->buflen;		/* wrap */
-			/* HACK... leadin should be reset when the PLAY position reaches the wrap,
-			 * 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;
+			/* wrap */
+			while (dsb->mixpos >= dsb->buflen)
+				dsb->mixpos -= dsb->buflen;
+			if (dsb->leadin && (dsb->startpos <= dsb->mixpos))
+				dsb->leadin = FALSE; /* HACK: see above */
 		}
 	}
-	
-	/* if (dsb->writepos >= dsb->buflen)
-		dsb->writepos %= dsb->buflen; */
 
 	return len;
 }
@@ -2675,6 +2698,8 @@
 			frag = DS_HAL_QUEUE * dsound->fraglen;
 			if (maxq > frag) maxq = frag;
 
+			EnterCriticalSection(&(primarybuf->lock));
+
 			/* check for consistency */
 			if (inq > maxq) {
 				/* the playback position must have passed our last
@@ -2706,8 +2731,6 @@
 				paused = TRUE;
 			}
 
-			EnterCriticalSection(&(primarybuf->lock));
-
 			/* 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 */
 			if (!paused) {
@@ -2809,6 +2832,7 @@
 		}
 		primarybuf->playpos = dsound->pwplay * dsound->fraglen;
 		TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->mixpos);
+		EnterCriticalSection(&(primarybuf->lock));
 		if (!dsound->pwqueue) {
 			/* this is either an underrun or we have nothing more to play...
 			 * since playback has already stopped now, we can enter pause mode,
@@ -2827,7 +2851,6 @@
 		writepos = primarybuf->playpos + DS_HEL_MARGIN * dsound->fraglen;
 		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;
 		 * this should minimize latency even when we have a large prebuffer */
 		if (dsound->priolevel != DSSCL_WRITEPRIMARY) {


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic