From 9fd3538b45ac21e9ab579e689c3c2e6ec3c5c7c7 Mon Sep 17 00:00:00 2001 From: Ove Kaaven Date: Mon, 26 Aug 2002 21:49:42 +0000 Subject: [PATCH] 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. --- dlls/winmm/wineoss/audio.c | 90 +++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/dlls/winmm/wineoss/audio.c b/dlls/winmm/wineoss/audio.c index 733575549a7..10e95cbfc33 100644 --- a/dlls/winmm/wineoss/audio.c +++ b/dlls/winmm/wineoss/audio.c @@ -29,6 +29,13 @@ /*#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 @@ -45,6 +52,10 @@ #ifdef HAVE_SYS_MMAN_H # include #endif +#ifdef HAVE_SYS_POLL_H +# include +#endif + #include "windef.h" #include "wingdi.h" #include "winerror.h" @@ -93,6 +104,21 @@ enum win_wm_message { 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 +131,16 @@ typedef struct { */ 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 +163,7 @@ typedef struct { 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; @@ -592,7 +624,15 @@ static int OSS_InitRingMessage(OSS_MSG_RING* omr) { 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 +644,12 @@ static int OSS_InitRingMessage(OSS_MSG_RING* omr) */ 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 +698,7 @@ static int OSS_AddRingMessage(OSS_MSG_RING* omr, enum win_wm_message msg, DWORD } 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 +729,7 @@ static int OSS_RetrieveRingMessage(OSS_MSG_RING* omr, *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; } @@ -952,7 +998,7 @@ static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset) 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 +1107,11 @@ static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo) 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 +1131,14 @@ static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo) 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 +1165,25 @@ static DWORD CALLBACK wodPlayer(LPVOID pmt) */ 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 +1311,7 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) wwo->dwBufferSize = info.fragstotal * info.fragsize; wwo->dwPlayedTotal = 0; wwo->dwWrittenTotal = 0; + wwo->bNeedPost = TRUE; OSS_InitRingMessage(&wwo->msgRing); @@ -1462,7 +1528,9 @@ static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize) 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", @@ -2246,7 +2314,7 @@ static DWORD CALLBACK widRecorder(LPVOID pmt) } } - WaitForSingleObject(wwi->msgRing.msg_event, dwSleepTime); + WAIT_OMR(&wwi->msgRing, dwSleepTime); while (OSS_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev)) {