As Ori Pessach noticed, there were still bugs in OSS driver Pyjama Sam now works correctly A+ -- --------------- Eric Pouech (http://perso.wanadoo.fr/eric.pouech/) "The future will be better tomorrow", Vice President Dan Quayle
Name: oss_audio ChangeLog: fixed the wodReset implementation, internal messages priority, full duplex code, wait algorithm for feeding the OSS buffer GenDate: 2002/01/12 14:32:23 UTC ModifiedFiles: dlls/winmm/wineoss/audio.c AddedFiles: =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/dlls/winmm/wineoss/audio.c,v retrieving revision 1.49 diff -u -u -r1.49 audio.c --- dlls/winmm/wineoss/audio.c 21 Dec 2001 20:28:43 -0000 1.49 +++ dlls/winmm/wineoss/audio.c 10 Jan 2002 19:57:21 -0000 @@ -9,7 +9,7 @@ /* * FIXME: * pause in waveOut does not work correctly in loop mode - * experimental full duples mode + * experimental full duplex mode * only one sound card is currently supported */ @@ -42,8 +42,6 @@ /* Allow 1% deviation for sample rates (some ES137x cards) */ #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0) -#define REFILL_BUFFER_WHEN 3/4 /* fill the buffer when it's 3/4 empty */ - #ifdef HAVE_OSS #define SOUND_DEV "/dev/dsp" @@ -92,6 +90,7 @@ * this ring will be used by the input (resp output) record (resp playback) routine */ typedef struct { + /* FIXME: this could be made a dynamically growing array (if needed) */ #define OSS_RING_BUFFER_SIZE 30 OSS_MSG messages[OSS_RING_BUFFER_SIZE]; int msg_tosave; @@ -111,9 +110,6 @@ /* OSS information */ DWORD dwFragmentSize; /* size of OSS buffer fragment */ DWORD dwBufferSize; /* size of whole OSS buffer in bytes */ - WORD uWaitForFragments; /* The number of OSS buffer fragments we would like to be free - * before trying to write to the DSP - */ 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 */ @@ -166,8 +162,8 @@ "WINE_WM_RESETTING", "WINE_WM_HEADER", "WINE_WM_UPDATE", - "WINE_WM_CLOSING", "WINE_WM_BREAKLOOP", + "WINE_WM_CLOSING", }; /*======================================================================* @@ -178,8 +174,9 @@ static unsigned OSS_OpenCount /* = 0 */; /* number of times fd has been opened */ static unsigned OSS_OpenAccess /* = 0 */; /* access used for opening... used to handle compat */ static int OSS_OpenFD; -static BOOL OSS_FullDuplex; /* set to non-zero if the device supports full duplex */ +static DWORD OSS_OwnerThreadID /* = 0 */; #endif +static BOOL OSS_FullDuplex; /* set to non-zero if the device supports full duplex */ /****************************************************************** * OSS_OpenDevice @@ -191,6 +188,7 @@ static int OSS_OpenDevice(unsigned req_access) { #ifdef USE_FULLDUPLEX + /* FIXME: race */ if (OSS_OpenCount == 0) { if (access(SOUND_DEV, 0) != 0 || @@ -203,12 +201,22 @@ if (req_access == O_RDWR && OSS_FullDuplex) ioctl(OSS_OpenFD, SNDCTL_DSP_SETDUPLEX, 0); OSS_OpenAccess = req_access; + OSS_OwnerThreadID = GetCurrentThreadId(); } - else if (OSS_OpenAccess != req_access) + else { - WARN("Mismatch in access...\n"); - return -1; + if (OSS_OpenAccess != req_access) + { + WARN("Mismatch in access...\n"); + return -1; + } + if (GetCurrentThreadId() == OSS_OwnerThreadID) + { + WARN("Another thread is trying to access audio...\n"); + return -1; + } } + OSS_OpenCount++; return OSS_OpenFD; #else @@ -477,31 +485,47 @@ */ static int OSS_AddRingMessage(OSS_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait) { - HANDLE hEvent; + HANDLE hEvent = INVALID_HANDLE_VALUE; EnterCriticalSection(&omr->msg_crst); - if ((omr->msg_tosave == omr->msg_toget) /* buffer overflow ? */ - && (omr->messages[omr->msg_toget].msg)) + if ((omr->msg_toget == ((omr->msg_tosave + 1) % OSS_RING_BUFFER_SIZE))) /* buffer overflow ? */ { ERR("buffer overflow !?\n"); LeaveCriticalSection(&omr->msg_crst); return 0; } + if (wait) + { + hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (hEvent == INVALID_HANDLE_VALUE) + { + ERR("can't create event !?\n"); + LeaveCriticalSection(&omr->msg_crst); + return 0; + } + if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER) + FIXME("two fast messages in the queue!!!!\n"); - hEvent = wait ? CreateEventA(NULL, FALSE, FALSE, NULL) : INVALID_HANDLE_VALUE; + /* fast messages have to be added at the start of the queue */ + omr->msg_toget = (omr->msg_toget + OSS_RING_BUFFER_SIZE - 1) % OSS_RING_BUFFER_SIZE; - omr->messages[omr->msg_tosave].msg = msg; - omr->messages[omr->msg_tosave].param = param; - omr->messages[omr->msg_tosave].hEvent = hEvent; - - omr->msg_tosave++; - if (omr->msg_tosave > OSS_RING_BUFFER_SIZE-1) - omr->msg_tosave = 0; + omr->messages[omr->msg_toget].msg = msg; + omr->messages[omr->msg_toget].param = param; + omr->messages[omr->msg_toget].hEvent = hEvent; + } + else + { + omr->messages[omr->msg_tosave].msg = msg; + omr->messages[omr->msg_tosave].param = param; + omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE; + omr->msg_tosave = (omr->msg_tosave + 1) % OSS_RING_BUFFER_SIZE; + } LeaveCriticalSection(&omr->msg_crst); /* signal a new message */ SetEvent(omr->msg_event); if (wait) { + /* wait for playback/record thread to have processed the message */ WaitForSingleObject(hEvent, INFINITE); CloseHandle(hEvent); } @@ -528,9 +552,7 @@ omr->messages[omr->msg_toget].msg = 0; *param = omr->messages[omr->msg_toget].param; *hEvent = omr->messages[omr->msg_toget].hEvent; - omr->msg_toget++; - if (omr->msg_toget > OSS_RING_BUFFER_SIZE-1) - omr->msg_toget = 0; + omr->msg_toget = (omr->msg_toget + 1) % OSS_RING_BUFFER_SIZE; LeaveCriticalSection(&omr->msg_crst); return 1; } @@ -646,17 +668,13 @@ /************************************************************************** * wodPlayer_DSPWait [internal] - * Returns the number of milliseconds to wait for the DSP buffer to clear. - * This is based on the number of fragments we want to be clear before - * writing and the number of free fragments we already have. + * Returns the number of milliseconds to wait for the DSP buffer to write + * one fragment. */ -static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo, DWORD availInQ) +static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo) { - DWORD uFreeFragments = availInQ / wwo->dwFragmentSize; - - return (uFreeFragments >= wwo->uWaitForFragments) - ? 1 : (wwo->dwFragmentSize * 1000 / wwo->format.wf.nAvgBytesPerSec) * - (wwo->uWaitForFragments - uFreeFragments); + /* time for one fragment to be played */ + return wwo->dwFragmentSize * 1000 / wwo->format.wf.nAvgBytesPerSec; } /************************************************************************** @@ -754,7 +772,6 @@ static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset) { wodUpdatePlayedTotal(wwo, NULL); - /* updates current notify list */ wodPlayer_NotifyCompletions(wwo, FALSE); @@ -767,7 +784,11 @@ } if (reset) { - /* empty notify list */ + enum win_wm_message msg; + DWORD param; + HANDLE ev; + + /* remove any buffer */ wodPlayer_NotifyCompletions(wwo, TRUE); wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; @@ -775,6 +796,25 @@ wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0; /* Clear partial wavehdr */ wwo->dwPartialOffset = 0; + + /* remove any existing message in the ring */ + EnterCriticalSection(&wwo->msgRing.msg_crst); + /* return all pending headers in queue */ + while (OSS_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) + { + if (msg != WINE_WM_HEADER) + { + FIXME("shouldn't have headers left\n"); + SetEvent(ev); + continue; + } + ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE; + ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE; + + wodNotifyClient(wwo, WOM_DONE, param, 0); + } + ResetEvent(wwo->msgRing.msg_event); + LeaveCriticalSection(&wwo->msgRing.msg_crst); } else { if (wwo->lpLoopPtr) { /* complicated case, not handled yet (could imply modifying the loop counter */ @@ -890,21 +930,23 @@ } /* no more room... no need to try to feed */ - if (dspspace.fragments == 0) return wodPlayer_DSPWait(wwo, 0); - - /* Feed from partial wavehdr */ - if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) { - wodPlayer_WriteMaxFrags(wwo, &availInQ); - } + if (dspspace.fragments != 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) while (wwo->lpPlayPtr && availInQ > 0) { - /* note the value that dwPlayedTotal will return when this wave finishes playing */ - wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength; - wodPlayer_WriteMaxFrags(wwo, &availInQ); + /* Feed wavehdrs until we run out of wavehdrs or DSP space */ + if (wwo->dwPartialOffset == 0) { + while (wwo->lpPlayPtr && availInQ > 0) { + /* note the value that dwPlayedTotal will return when this wave finishes playing */ + wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength; + wodPlayer_WriteMaxFrags(wwo, &availInQ); + } + } } - return wodPlayer_DSPWait(wwo, availInQ); + return wodPlayer_DSPWait(wwo); } @@ -1080,10 +1122,8 @@ /* Remember fragsize and total buffer size for future use */ wwo->dwFragmentSize = info.fragsize; wwo->dwBufferSize = info.fragstotal * info.fragsize; - wwo->uWaitForFragments = info.fragstotal * REFILL_BUFFER_WHEN; wwo->dwPlayedTotal = 0; wwo->dwWrittenTotal = 0; - TRACE("wait for %d fragments\n", wwo->uWaitForFragments); OSS_InitRingMessage(&wwo->msgRing); @@ -1131,7 +1171,6 @@ WARN("buffers still playing !\n"); ret = WAVERR_STILLPLAYING; } else { - TRACE("imhere[3-close]\n"); if (wwo->hThread != INVALID_HANDLE_VALUE) { OSS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE); } @@ -1174,7 +1213,6 @@ lpWaveHdr->dwFlags |= WHDR_INQUEUE; lpWaveHdr->lpNext = 0; - TRACE("imhere[3-HEADER]\n"); OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE); return MMSYSERR_NOERROR; @@ -1233,7 +1271,6 @@ return MMSYSERR_BADDEVICEID; } - TRACE("imhere[3-PAUSING]\n"); OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE); return MMSYSERR_NOERROR; @@ -1252,7 +1289,6 @@ } if (WOutDev[wDevID].state == WINE_WS_PAUSED) { - TRACE("imhere[3-RESTARTING]\n"); OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE); } @@ -1276,7 +1312,6 @@ return MMSYSERR_BADDEVICEID; } - TRACE("imhere[3-RESET]\n"); OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE); return MMSYSERR_NOERROR; @@ -1424,7 +1459,7 @@ { DWORD ret = 1; /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */ - int audio = OSS_OpenDevice(O_WRONLY); + int audio = OSS_OpenDevice(OSS_FullDuplex ? O_RDWR : O_WRONLY); if (audio == -1) { if (errno != EBUSY) @@ -2357,7 +2392,7 @@ return WAVERR_STILLPLAYING; lpWaveHdr->dwFlags |= WHDR_PREPARED; - lpWaveHdr->dwFlags &= ~(WHDR_INQUEUE|WHDR_DONE); + lpWaveHdr->dwFlags &= ~WHDR_DONE; lpWaveHdr->dwBytesRecorded = 0; TRACE("header prepared !\n"); return MMSYSERR_NOERROR; @@ -2374,7 +2409,7 @@ if (lpWaveHdr->dwFlags & WHDR_INQUEUE) return WAVERR_STILLPLAYING; - lpWaveHdr->dwFlags &= ~(WHDR_PREPARED|WHDR_INQUEUE); + lpWaveHdr->dwFlags &= ~WHDR_PREPARED; lpWaveHdr->dwFlags |= WHDR_DONE; return MMSYSERR_NOERROR;