wineoss patch

Ove Kaaven ovehk at ping.uio.no
Fri Aug 23 20:02:13 CDT 2002


Getting this patch merged into Wine should make it easier to merge my
fixes to Eric Pouech's code (in WineX) with his own work on it (in Wine).

Log:
Ove Kaaven <ovek at transgaming.com>
Tweaks to improve playback performance and reduce sound glitches:

- wodGetPosition does not send an update message to the player thread;
this reduces the accuracy of the readout from byte-accuracy to near
fragment-accuracy, but we save 2-4 context switches and kernel
scheduling penalties.

- if FeedDSP runs out of data, do not flush output buffers before
notifications are sent and given the chance to provide more sound data.
Do not flush before we're down to the last fragment.

- messages to the player thread are signaled using Unix pipes instead
of Win32 synchronization primitives, to avoid having the player thread
wait for the wineserver (and context switches from/to it) before the
it can feed more data to the sound card.

- ring buffer size is increased from 30 to 192 to support some games
that fires 128 messages at once to determine DMA buffer size.

--- wine.clean/dlls/winmm/wineoss/audio.c	2002-08-24 00:16:23.000000000 +0200
+++ rewind/dlls/winmm/wineoss/audio.c	2002-08-24 02:53:28.000000000 +0200
@@ -29,22 +15,26 @@
 
 /*#define EMULATE_SB16*/
 
+/* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
+#define USE_PIPE_SYNC
+
+/* an exact wodGetPosition is usually not worth the extra context switches,
+ * as we're going to have near fragment accuracy anyway */
+/* #define EXACT_WODPOSITION */
+
 #include "config.h"
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <unistd.h>
 #include <errno.h>
 #include <fcntl.h>
-#ifdef HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
+#include <sys/ioctl.h>
 #ifdef HAVE_SYS_MMAN_H
 # include <sys/mman.h>
 #endif
+#include <sys/poll.h>
 #include "windef.h"
 #include "wingdi.h"
 #include "winerror.h"
@@ -93,6 +83,21 @@
     WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
 };
 
+#ifdef USE_PIPE_SYNC
+#define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
+#define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
+#define RESET_OMR(omr) do { } while (0)
+#define WAIT_OMR(omr, sleep) \
+  do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
+       pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
+#else
+#define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
+#define CLEAR_OMR(omr) do { } while (0)
+#define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
+#define WAIT_OMR(omr, sleep) \
+  do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
+#endif
+
 typedef struct {
     enum win_wm_message 	msg;	/* message identifier */
     DWORD	                param;  /* parameter for this message */
@@ -105,11 +110,16 @@
  */
 typedef struct {
     /* FIXME: this could be made a dynamically growing array (if needed) */
-#define OSS_RING_BUFFER_SIZE	30
+    /* maybe it's needed, a Humongous game manages to transmit 128 messages at once at startup */
+#define OSS_RING_BUFFER_SIZE	192
     OSS_MSG			messages[OSS_RING_BUFFER_SIZE];
     int				msg_tosave;
     int				msg_toget;
+#ifdef USE_PIPE_SYNC
+    int				msg_pipe[2];
+#else
     HANDLE			msg_event;
+#endif
     CRITICAL_SECTION		msg_crst;
 } OSS_MSG_RING;
 
@@ -132,6 +142,7 @@
 
     DWORD			dwPlayedTotal;		/* number of bytes actually played since opening */
     DWORD                       dwWrittenTotal;         /* number of bytes written to OSS buffer since opening */
+    BOOL                        bNeedPost;              /* whether audio still needs to be physically started */
 
     /* synchronization stuff */
     HANDLE			hStartUpEvent;
@@ -434,7 +445,7 @@
 	    !(caps & DSP_CAP_BATCH))
 	    ossdev->out_caps.dwSupport |= WAVECAPS_DIRECTSOUND;
     }
-    OSS_CloseDevice(devID, audio);
+    OSS_CloseDevice(0, audio);
     TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
 	  ossdev->out_caps.dwFormats, ossdev->out_caps.dwSupport);
 
@@ -592,7 +603,15 @@
 {
     omr->msg_toget = 0;
     omr->msg_tosave = 0;
+#ifdef USE_PIPE_SYNC
+    if (pipe(omr->msg_pipe) < 0) {
+	omr->msg_pipe[0] = -1;
+	omr->msg_pipe[1] = -1;
+	ERR("could not create pipe, error=%s\n", strerror(errno));
+    }
+#else
     omr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
+#endif
     memset(omr->messages, 0, sizeof(OSS_MSG) * OSS_RING_BUFFER_SIZE);
     InitializeCriticalSection(&omr->msg_crst);
     return 0;
@@ -604,7 +623,12 @@
  */
 static int OSS_DestroyRingMessage(OSS_MSG_RING* omr)
 {
+#ifdef USE_PIPE_SYNC
+    close(omr->msg_pipe[0]);
+    close(omr->msg_pipe[1]);
+#else
     CloseHandle(omr->msg_event);
+#endif
     DeleteCriticalSection(&omr->msg_crst);
     return 0;
 }
@@ -653,7 +677,7 @@
     }
     LeaveCriticalSection(&omr->msg_crst);
     /* signal a new message */
-    SetEvent(omr->msg_event);
+    SIGNAL_OMR(omr);
     if (wait)
     {
         /* wait for playback/record thread to have processed the message */
@@ -684,6 +708,7 @@
     *param = omr->messages[omr->msg_toget].param;
     *hEvent = omr->messages[omr->msg_toget].hEvent;
     omr->msg_toget = (omr->msg_toget + 1) % OSS_RING_BUFFER_SIZE;
+    CLEAR_OMR(omr);
     LeaveCriticalSection(&omr->msg_crst);
     return 1;
 }
@@ -704,9 +729,8 @@
     case WOM_CLOSE:
     case WOM_DONE:
 	if (wwo->wFlags != DCB_NULL &&
-	    !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
-			    (HDRVR)wwo->waveDesc.hWave, wMsg,
-			    wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
+	    !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, wwo->waveDesc.hWave,
+			    wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
 	    WARN("can't notify client !\n");
 	    return MMSYSERR_ERROR;
 	}
@@ -952,7 +976,7 @@
 
             wodNotifyClient(wwo, WOM_DONE, param, 0);
         }
-        ResetEvent(wwo->msgRing.msg_event);
+        RESET_OMR(&wwo->msgRing);
         LeaveCriticalSection(&wwo->msgRing.msg_crst);
     } else {
         if (wwo->lpLoopPtr) {
@@ -1061,14 +1085,11 @@
     wodUpdatePlayedTotal(wwo, &dspspace);
     availInQ = dspspace.bytes;
     TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
-          dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);
+	  dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);
 
     /* input queue empty and output buffer with less than one fragment to play */
-    if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize) {
-        TRACE("Run out of wavehdr:s... flushing (%lu => %lu)\n",
-              wwo->dwPlayedTotal, wwo->dwWrittenTotal);
-        ioctl(wwo->unixdev, SNDCTL_DSP_SYNC, 0);
-        wwo->dwPlayedTotal = wwo->dwWrittenTotal;
+    if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + wwo->dwFragmentSize) {
+	TRACE("Run out of wavehdr:s...\n");
         return INFINITE;
     }
 
@@ -1088,6 +1109,14 @@
                 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
             } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
         }
+
+        if (wwo->bNeedPost) {
+            /* OSS doesn't start before it gets either 2 fragments or a SNDCTL_DSP_POST;
+             * if it didn't get one, we give it the other */
+            if (wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize)
+                ioctl(wwo->unixdev, SNDCTL_DSP_POST, 0);
+            wwo->bNeedPost = FALSE;
+        }
     }
 
     return wodPlayer_DSPWait(wwo);
@@ -1114,11 +1143,25 @@
          */
         dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
         TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
-        WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);
+	WAIT_OMR(&wwo->msgRing, dwSleepTime);
 	wodPlayer_ProcessMessages(wwo);
 	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 flushing, */
+		/* check that a notification didn't give us more */
+		wodPlayer_ProcessMessages(wwo);
+		if (!wwo->lpPlayPtr) {
+		    TRACE("flushing\n");
+		    ioctl(wwo->unixdev, SNDCTL_DSP_SYNC, 0);
+		    wwo->dwPlayedTotal = wwo->dwWrittenTotal;
+		}
+		else {
+		    TRACE("recovering\n");
+		    dwNextFeedTime = wodPlayer_FeedDSP(wwo);
+		}
+	    }
 	} else {
 	    dwNextFeedTime = dwNextNotifyTime = INFINITE;
 	}
@@ -1246,6 +1289,7 @@
     wwo->dwBufferSize = info.fragstotal * info.fragsize;
     wwo->dwPlayedTotal = 0;
     wwo->dwWrittenTotal = 0;
+    wwo->bNeedPost = TRUE;
 
     OSS_InitRingMessage(&wwo->msgRing);
 
@@ -1462,7 +1506,9 @@
     if (lpTime == NULL)	return MMSYSERR_INVALPARAM;
 
     wwo = &WOutDev[wDevID];
+#ifdef EXACT_WODPOSITION
     OSS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
+#endif
     val = wwo->dwPlayedTotal;
 
     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
@@ -2082,9 +2128,8 @@
     case WIM_CLOSE:
     case WIM_DATA:
 	if (wwi->wFlags != DCB_NULL &&
-	    !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
-			    (HDRVR)wwi->waveDesc.hWave, wMsg,
-			    wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
+	    !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, wwi->waveDesc.hWave,
+			    wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
 	    WARN("can't notify client !\n");
 	    return MMSYSERR_ERROR;
 	}
@@ -2246,7 +2291,7 @@
             }
 	}
 
-	WaitForSingleObject(wwi->msgRing.msg_event, dwSleepTime);
+	WAIT_OMR(&wwi->msgRing, dwSleepTime);
 
 	while (OSS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
 	{




More information about the wine-patches mailing list