winealsa patch

Ove Kaaven ovek at transgaming.com
Sun Apr 18 06:37:57 CDT 2004


Here's a patch from WineX for the non-dsound part of winealsa which lets
winealsa return sample-accurate data from waveOutGetPosition, like
wineoss does. I haven't tested whether it works on Wine but the code
doesn't seem to have changed much and the WineX patch applied without
rejects, so perhaps it's useful.

If you want, we also have code I probably could submit for the dsound
part of winealsa, so that it provides sample-accurate GetPosition and
zero-copy primary buffer, but unlike the existing code, uses only
documented API features (thanks to advice given to TransGaming by
Jaroslav Kysela, ALSA developer). That would probably be in conflict
with the existing Wine code though, so would mean more work for me to
merge, and perhaps you don't need it anyway.

Log:
Code and concepts merged in from wineoss in order to get the non-dsound
wave output performance in winealsa to an acceptable level. It's still
possible to do better than the current code, but this should do for now.

Index: dlls/winmm/winealsa/audio.c
===================================================================
RCS file: /home/wine/wine/dlls/winmm/winealsa/audio.c,v
retrieving revision 1.34
diff -u -r1.34 audio.c
--- dlls/winmm/winealsa/audio.c	31 Mar 2004 20:05:45 -0000	1.34
+++ dlls/winmm/winealsa/audio.c	18 Apr 2004 11:18:04 -0000
@@ -167,11 +167,13 @@
     DWORD                       dwBufferSize;           /* size of whole ALSA buffer in bytes */
     LPWAVEHDR			lpQueuePtr;		/* start of queued WAVEHDRs (waiting to be notified) */
     LPWAVEHDR			lpPlayPtr;		/* start of not yet fully played buffers */
+    DWORD			dwPartialOffset;	/* Offset of not yet written bytes in lpPlayPtr */
 
     LPWAVEHDR			lpLoopPtr;              /* pointer of first buffer in loop, if any */
     DWORD			dwLoops;		/* private copy of loop counter */
 
-    DWORD			dwPlayedTotal;
+    DWORD			dwPlayedTotal;		/* number of bytes actually played since opening */
+    DWORD			dwWrittenTotal;		/* number of bytes written to ALSA buffer since opening */
 
     /* synchronization stuff */
     HANDLE			hStartUpEvent;
@@ -937,6 +939,9 @@
  */
 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, snd_pcm_status_t* ps)
 {
+   snd_pcm_sframes_t delay = 0;
+   snd_pcm_delay(wwo->p_handle, &delay);
+   wwo->dwPlayedTotal = wwo->dwWrittenTotal - delay;
    return TRUE;
 }
 
@@ -953,8 +958,6 @@
 
     if (!lpWaveHdr) return;
 
-    wwo->lpPlayPtr->reserved = 0;
-
     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
 	if (wwo->lpLoopPtr) {
 	    WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
@@ -966,6 +969,7 @@
 	    wwo->dwLoops = lpWaveHdr->dwLoops;
 	}
     }
+    wwo->dwPartialOffset = 0;
 }
 
 /**************************************************************************
@@ -977,11 +981,11 @@
 {
     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
 
+    wwo->dwPartialOffset = 0;
     if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
 	/* We're at the end of a loop, loop if required */
 	if (--wwo->dwLoops > 0) {
 	    wwo->lpPlayPtr = wwo->lpLoopPtr;
-	    wwo->lpPlayPtr->reserved = 0;
 	} else {
 	    /* Handle overlapping loops correctly */
 	    if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
@@ -1016,7 +1020,7 @@
 }
 
 /**************************************************************************
- * 			     wodPllayer_NotifyWait               [internal]
+ * 			     wodPlayer_NotifyWait               [internal]
  * Returns the number of milliseconds to wait before attempting to notify
  * completion of the specified wavehdr.
  * This is based on the number of bytes remaining to be written in the
@@ -1026,8 +1030,12 @@
 {
     DWORD dwMillis;
 
-    dwMillis = (lpWaveHdr->dwBufferLength - lpWaveHdr->reserved) * 1000 / wwo->format.wf.nAvgBytesPerSec;
-    if (!dwMillis) dwMillis = 1;
+    if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
+        dwMillis = 1;
+    } else {
+        dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
+        if (!dwMillis) dwMillis = 1;
+    }
 
     return dwMillis;
 }
@@ -1042,18 +1050,18 @@
 {
     /* Only attempt to write to free frames */
     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
-    DWORD dwLength = snd_pcm_bytes_to_frames(wwo->p_handle, lpWaveHdr->dwBufferLength - lpWaveHdr->reserved);
+    DWORD dwLength = snd_pcm_bytes_to_frames(wwo->p_handle, lpWaveHdr->dwBufferLength - wwo->dwPartialOffset);
     int toWrite = min(dwLength, *frames);
     int written;
 
-    TRACE("Writing wavehdr %p.%lu[%lu]\n", lpWaveHdr, lpWaveHdr->reserved, lpWaveHdr->dwBufferLength);
+    TRACE("Writing wavehdr %p.%lu[%lu]\n", lpWaveHdr, wwo->dwPartialOffset, lpWaveHdr->dwBufferLength);
 
-    written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + lpWaveHdr->reserved, toWrite);
+    written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
     if ( written < 0)
     {
     	/* XRUN occurred. let's try to recover */
         ALSA_XRUNRecovery(wwo, written);
-	written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + lpWaveHdr->reserved, toWrite);
+	written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
     }
     if (written <= 0)
     {
@@ -1062,15 +1070,15 @@
         return written;
     }
 
-    lpWaveHdr->reserved += snd_pcm_frames_to_bytes(wwo->p_handle, written);
-    if ( lpWaveHdr->reserved >= lpWaveHdr->dwBufferLength) {
+    wwo->dwPartialOffset += snd_pcm_frames_to_bytes(wwo->p_handle, written);
+    if ( wwo->dwPartialOffset >= lpWaveHdr->dwBufferLength) {
 	/* this will be used to check if the given wave header has been fully played or not... */
-        lpWaveHdr->reserved = lpWaveHdr->dwBufferLength;
+        wwo->dwPartialOffset = lpWaveHdr->dwBufferLength;
         /* If we wrote all current wavehdr, skip to the next one */
         wodPlayer_PlayPtrNext(wwo);
     }
     *frames -= written;
-    wwo->dwPlayedTotal += snd_pcm_frames_to_bytes(wwo->p_handle, written);
+    wwo->dwWrittenTotal += snd_pcm_frames_to_bytes(wwo->p_handle, written);
 
     return written;
 }
@@ -1093,11 +1101,12 @@
      * - we hit the beginning of a running loop
      * - we hit a wavehdr which hasn't finished playing
      */
+#if 0
     while ((lpWaveHdr = wwo->lpQueuePtr) &&
            (force ||
             (lpWaveHdr != wwo->lpPlayPtr &&
              lpWaveHdr != wwo->lpLoopPtr &&
-             lpWaveHdr->reserved == lpWaveHdr->dwBufferLength))) {
+             lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
 
 	wwo->lpQueuePtr = lpWaveHdr->lpNext;
 
@@ -1106,6 +1115,25 @@
 
 	wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
     }
+#else
+    for (;;)
+    {
+        lpWaveHdr = wwo->lpQueuePtr;
+        if (!lpWaveHdr) {TRACE("Empty queue\n"); break;}
+        if (!force)
+        {
+            if (lpWaveHdr == wwo->lpPlayPtr) {TRACE("play %p\n", lpWaveHdr); break;}
+            if (lpWaveHdr == wwo->lpLoopPtr) {TRACE("loop %p\n", lpWaveHdr); break;}
+            if (lpWaveHdr->reserved > wwo->dwPlayedTotal){TRACE("still playing %p (%lu/%lu)\n", lpWaveHdr, lpWaveHdr->reserved, wwo->dwPlayedTotal);break;}
+        }
+	wwo->lpQueuePtr = lpWaveHdr->lpNext;
+
+	lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+	lpWaveHdr->dwFlags |= WHDR_DONE;
+
+	wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
+    }
+#endif
     return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
         wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
 }
@@ -1146,6 +1174,7 @@
     /* flush all possible output */
     wait_for_poll(wwo->p_handle, wwo->ufds, wwo->count);
 
+    wodUpdatePlayedTotal(wwo, NULL);
     /* updates current notify list */
     wodPlayer_NotifyCompletions(wwo, FALSE);
 
@@ -1163,6 +1192,9 @@
 
     wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
     wwo->state = WINE_WS_STOPPED;
+    wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
+    /* Clear partial wavehdr */
+    wwo->dwPartialOffset = 0;
 
     /* remove any existing message in the ring */
     EnterCriticalSection(&wwo->msgRing.msg_crst);
@@ -1273,12 +1305,36 @@
  */
 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
 {
-    DWORD               availInQ = snd_pcm_avail_update(wwo->p_handle);
+    DWORD               availInQ;
+
+    wodUpdatePlayedTotal(wwo, NULL);
+    availInQ = snd_pcm_avail_update(wwo->p_handle);
+
+#if 0
+    /* input queue empty and output buffer with less than one fragment to play */
+    if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + wwo->dwFragmentSize) {
+	TRACE("Run out of wavehdr:s...\n");
+        return INFINITE;
+    }
+#endif
 
     /* no more room... no need to try to feed */
-    while (wwo->lpPlayPtr && availInQ > 0)
-        if ( wodPlayer_WriteMaxFrags(wwo, &availInQ) < 0 )
-	    break;
+    if (availInQ > 0) {
+        /* Feed from partial wavehdr */
+        if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
+            wodPlayer_WriteMaxFrags(wwo, &availInQ);
+        }
+
+        /* Feed wavehdrs until we run out of wavehdrs or DSP space */
+        if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
+            do {
+                TRACE("Setting time to elapse for %p to %lu\n",
+                      wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
+                /* note the value that dwPlayedTotal will return when this wave finishes playing */
+                wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
+            } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
+        }
+    }
 
     return wodPlayer_DSPWait(wwo);
 }
@@ -1308,6 +1364,15 @@
 	if (wwo->state == WINE_WS_PLAYING) {
 	    dwNextFeedTime = wodPlayer_FeedDSP(wwo);
 	    dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
+	    if (dwNextFeedTime == INFINITE) {
+		/* FeedDSP ran out of data, but before giving up, */
+		/* check that a notification didn't give us more */
+		wodPlayer_ProcessMessages(wwo);
+		if (wwo->lpPlayPtr) {
+		    TRACE("recovering\n");
+		    dwNextFeedTime = wodPlayer_FeedDSP(wwo);
+		}
+	    }
 	} else {
 	    dwNextFeedTime = dwNextNotifyTime = INFINITE;
 	}





More information about the wine-patches mailing list