this patch should fix a lot of last OSS bugs thanks to all the people which reported issues and took time for the testing... A+ -- --------------- Eric Pouech (http://perso.wanadoo.fr/eric.pouech/) "The future will be better tomorrow", Vice President Dan Quayle
Name: oss_audio ChangeLog: thawed some freezes cleaned up time and notification handling typos fixes (Gérard) added breakloop support GenDate: 2001/12/12 20:14:08 UTC ModifiedFiles: dlls/winmm/wineoss/audio.c AddedFiles: =================================================================== RCS file: /usr/share/cvs/cvsroot/wine/wine/dlls/winmm/wineoss/audio.c,v retrieving revision 1.47 diff -u -u -r1.47 audio.c --- dlls/winmm/wineoss/audio.c 2001/11/19 02:10:50 1.47 +++ dlls/winmm/wineoss/audio.c 2001/12/12 20:07:34 @@ -77,21 +77,20 @@ #define WINE_WS_CLOSED 3 /* events to be send to device */ -#define WINE_WM_PAUSING (WM_USER + 1) -#define WINE_WM_RESTARTING (WM_USER + 2) -#define WINE_WM_RESETTING (WM_USER + 3) -#define WINE_WM_CLOSING (WM_USER + 4) -#define WINE_WM_HEADER (WM_USER + 5) +enum win_wm_message { + WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER, + WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING +}; typedef struct { - int msg; /* message identifier */ - DWORD param; /* parameter for this message */ - HANDLE hEvent; /* if message is synchronous, handle of event for synchro */ + enum win_wm_message msg; /* message identifier */ + DWORD param; /* parameter for this message */ + HANDLE hEvent; /* if message is synchronous, handle of event for synchro */ } OSS_MSG; -/* implement a in process message ring for better performance +/* implement an in-process message ring for better performance * (compared to passing thru the server) - * this ring will be used by both the input & output plauback + * this ring will be used by the input (resp output) record (resp playback) routine */ typedef struct { #define OSS_RING_BUFFER_SIZE 30 @@ -105,39 +104,32 @@ typedef struct { int unixdev; volatile int state; /* one of the WINE_WS_ manifest constants */ + WAVEOPENDESC waveDesc; + WORD wFlags; + PCMWAVEFORMAT format; + WAVEOUTCAPSA caps; + + /* OSS information */ DWORD dwFragmentSize; /* size of OSS buffer fragment */ - DWORD dwBufferSize; /* size of whole OSS buffer in bytes - * used to compute dwPlayedTotal from dwWrittenTotal and - * ioctl GETOSPACE info - */ + 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 */ - DWORD dwMillisPerFragment; /* The number of milliseconds of sound in each OSS buffer - * fragment - */ - WAVEOPENDESC waveDesc; - WORD wFlags; - PCMWAVEFORMAT format; LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */ LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */ - - /* info on current lpPlayPtr->lpWaveHdr */ - LPSTR lpPartialData; /* Data still to write on current wavehdr */ - DWORD dwPartialBytes; /* number of bytes to write to end current wavehdr */ + 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; /* number of bytes played since opening */ - DWORD dwWrittenTotal; /* number of bytes written since opening */ + DWORD dwPlayedTotal; /* number of bytes actually played since opening */ + DWORD dwWrittenTotal; /* number of bytes written to OSS buffer since opening */ /* synchronization stuff */ HANDLE hStartUpEvent; HANDLE hThread; DWORD dwThreadID; OSS_MSG_RING msgRing; - WAVEOUTCAPSA caps; /* DirectSound stuff */ LPBYTE mapping; @@ -173,13 +165,21 @@ "WINE_WM_PAUSING", "WINE_WM_RESTARTING", "WINE_WM_RESETTING", + "WINE_WM_HEADER", + "WINE_WM_UPDATE", "WINE_WM_CLOSING", - "WINE_WM_HEADER" }; + "WINE_WM_BREAKLOOP", +}; /*======================================================================* * Low level WAVE implementation * *======================================================================*/ +/****************************************************************** + * OSS_WaveInit + * + * Initialize internal structures from OSS information + */ LONG OSS_WaveInit(void) { int audio; @@ -378,56 +378,12 @@ return 0; } -/************************************************************************** - * OSS_NotifyClient [internal] +/****************************************************************** + * OSS_InitRingMessage + * + * Initialize the ring of messages for passing between driver's caller and playback/record + * thread */ -static DWORD OSS_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwParam2) -{ - TRACE("wDevID = %04X wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2); - - switch (wMsg) { - case WOM_OPEN: - case WOM_CLOSE: - case WOM_DONE: - if (wDevID >= MAX_WAVEOUTDRV) return MCIERR_INTERNAL; - - if (WOutDev[wDevID].wFlags != DCB_NULL && - !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback, - WOutDev[wDevID].wFlags, - WOutDev[wDevID].waveDesc.hWave, - wMsg, - WOutDev[wDevID].waveDesc.dwInstance, - dwParam1, - dwParam2)) { - WARN("can't notify client !\n"); - return MMSYSERR_NOERROR; - } - break; - - case WIM_OPEN: - case WIM_CLOSE: - case WIM_DATA: - if (wDevID >= MAX_WAVEINDRV) return MCIERR_INTERNAL; - - if (WInDev[wDevID].wFlags != DCB_NULL && - !DriverCallback(WInDev[wDevID].waveDesc.dwCallback, - WInDev[wDevID].wFlags, - WInDev[wDevID].waveDesc.hWave, - wMsg, - WInDev[wDevID].waveDesc.dwInstance, - dwParam1, - dwParam2)) { - WARN("can't notify client !\n"); - return MMSYSERR_NOERROR; - } - break; - default: - FIXME("Unknown CB message %u\n", wMsg); - break; - } - return 0; -} - static int OSS_InitRingMessage(OSS_MSG_RING* omr) { omr->msg_toget = 0; @@ -438,7 +394,12 @@ return 0; } -static int OSS_AddRingMessage(OSS_MSG_RING* omr, int msg, DWORD param, BOOL wait) +/****************************************************************** + * OSS_AddRingMessage + * + * Inserts a new message into the ring (should be called from DriverProc derivated routines) + */ +static int OSS_AddRingMessage(OSS_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait) { HANDLE hEvent; @@ -471,7 +432,13 @@ return 1; } -static int OSS_RetrieveRingMessage(OSS_MSG_RING* omr, int *msg, DWORD *param, HANDLE *hEvent) +/****************************************************************** + * OSS_RetrieveRingMessage + * + * Get a message from the ring. Should be called by the playback/record thread. + */ +static int OSS_RetrieveRingMessage(OSS_MSG_RING* omr, + enum win_wm_message *msg, DWORD *param, HANDLE *hEvent) { EnterCriticalSection(&omr->msg_crst); @@ -497,24 +464,38 @@ *======================================================================*/ /************************************************************************** - * updatePlayedTotal [internal] - * - * Calculates wwo->dwPlayed total from wwo->dwWrittenTotal and the amount - * still remaining in the OSS buffer. + * wodNotifyClient [internal] */ -static DWORD updatePlayedTotal( WINE_WAVEOUT* wwo ) +static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2) { - audio_buf_info info; - - if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) { - ERR("ioctl failed (%s)\n", strerror(errno)); - return wwo->dwPlayedTotal; + TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2); + + switch (wMsg) { + case WOM_OPEN: + case WOM_CLOSE: + case WOM_DONE: + if (wwo->wFlags != DCB_NULL && + !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, wwo->waveDesc.hWave, + wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) { + WARN("can't notify client !\n"); + return MMSYSERR_ERROR; + } + break; + default: + FIXME("Unknown CB message %u\n", wMsg); + return MMSYSERR_INVALPARAM; } - - wwo->dwPlayedTotal=wwo->dwWrittenTotal-( wwo->dwBufferSize - info.bytes ); - return wwo->dwPlayedTotal; + return MMSYSERR_NOERROR; } +/************************************************************************** + * wodUpdatePlayedTotal [internal] + * + */ +static void wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, DWORD availInQ) +{ + wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwBufferSize - availInQ); +} /************************************************************************** * wodPlayer_BeginWaveHdr [internal] @@ -523,12 +504,11 @@ * If the specified wave header is a begin loop and we're not already in * a loop, setup the loop. */ -static void wodPlayer_BeginWaveHdr( WINE_WAVEOUT* wwo, - LPWAVEHDR lpWaveHdr ) +static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr) { - wwo->lpPlayPtr=lpWaveHdr; + wwo->lpPlayPtr = lpWaveHdr; - if( !lpWaveHdr ) return; + if (!lpWaveHdr) return; if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) { if (wwo->lpLoopPtr) { @@ -541,6 +521,7 @@ wwo->dwLoops = lpWaveHdr->dwLoops; } } + wwo->dwPartialOffset = 0; } /************************************************************************** @@ -550,8 +531,9 @@ */ static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo) { - LPWAVEHDR lpWaveHdr=wwo->lpPlayPtr; + 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) { @@ -564,17 +546,17 @@ * the opening one or for both ??? * code assumes for closing loop only */ - wwo->lpLoopPtr = NULL; - wodPlayer_BeginWaveHdr( wwo, lpWaveHdr ); } else { - wwo->lpLoopPtr = NULL; - wodPlayer_BeginWaveHdr( wwo, lpWaveHdr=lpWaveHdr->lpNext ); - } + lpWaveHdr = lpWaveHdr->lpNext; + } + wwo->lpLoopPtr = NULL; + wodPlayer_BeginWaveHdr(wwo, lpWaveHdr); } } else { /* We're not in a loop. Advance to the next wave header */ - wodPlayer_BeginWaveHdr( wwo, lpWaveHdr=lpWaveHdr->lpNext ); + wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext); } + return lpWaveHdr; } @@ -584,11 +566,13 @@ * This is based on the number of fragments we want to be clear before * writing and the number of free fragments we already have. */ -static DWORD wodPlayer_DSPWait( WINE_WAVEOUT *wwo, WORD uFreeFragments ) +static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo, DWORD availInQ) { - return (uFreeFragments>wwo->uWaitForFragments)?1: - wwo->dwMillisPerFragment* - (wwo->uWaitForFragments-uFreeFragments); + DWORD uFreeFragments = availInQ / wwo->dwFragmentSize; + + return (uFreeFragments >= wwo->uWaitForFragments) + ? 1 : (wwo->dwFragmentSize * 1000 / wwo->format.wf.nAvgBytesPerSec) * + (wwo->uWaitForFragments - uFreeFragments); } /************************************************************************** @@ -598,20 +582,15 @@ * This is based on the number of bytes remaining to be written in the * wave. */ -static DWORD wodPlayer_NotifyWait( WINE_WAVEOUT *wwo, LPWAVEHDR lpWaveHdr ) +static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr) { DWORD dwMillis; - if( lpWaveHdr->reserved<wwo->dwPlayedTotal ) { - dwMillis=1; + if (lpWaveHdr->reserved < wwo->dwPlayedTotal) { + dwMillis = 1; } else { - dwMillis=(lpWaveHdr->reserved-wwo->dwPlayedTotal) * - wwo->dwMillisPerFragment / wwo->dwFragmentSize; - TRACE( "wait for %lu bytes = %lu\n", - (lpWaveHdr->reserved-wwo->dwPlayedTotal), - dwMillis ); - if( dwMillis < 1 ) - dwMillis=1; + dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec; + if (!dwMillis) dwMillis = 1; } return dwMillis; @@ -621,26 +600,29 @@ /************************************************************************** * wodPlayer_WriteMaxFrags [internal] * Writes the maximum number of bytes possible to the DSP and returns - * the number of bytes written. Also updates fragments in dspspace. + * the number of bytes written. */ -static DWORD wodPlayer_WriteMaxFrags( WINE_WAVEOUT *wwo, LPSTR lpData, - DWORD dwLength, - audio_buf_info* dspspace ) -{ - /* Only attempt to write to free fragments */ - int maxWrite=dspspace->fragments*dspspace->fragsize; - int toWrite=min(dwLength,maxWrite); - - int written=write(wwo->unixdev, lpData, toWrite); - - TRACE("wrote %d of %lu bytes\n", - written, dwLength ); - if( written > 0 ) { - /* Keep a count of the total bytes written to the DSP */ - wwo->dwWrittenTotal+=written; - /* reduce the number of free fragments */ - dspspace->fragments -= (written/dspspace->fragsize)+(written%dspspace->fragsize>0); +static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes) +{ + /* Only attempt to write to free bytes */ + DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset; + int toWrite = min(dwLength, *bytes); + int written; + + TRACE("Writing wavehdr %p.%lu[%lu]\n", + wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength); + written = write(wwo->unixdev, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite); + if (written <= 0) return written; + + if (written >= dwLength) { + /* If we wrote all current wavehdr, skip to the next one */ + wodPlayer_PlayPtrNext(wwo); + } else { + /* Remove the amount written */ + wwo->dwPartialOffset += written; } + *bytes -= written; + wwo->dwWrittenTotal += written; return written; } @@ -654,37 +636,30 @@ * we notify all wavehdrs and remove them all from the queue even if they * are unplayed or part of a loop. */ -static DWORD wodPlayer_NotifyCompletions( WINE_WAVEOUT* wwo, WORD uDevID, BOOL force) +static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force) { LPWAVEHDR lpWaveHdr; - updatePlayedTotal(wwo); /* Start from lpQueuePtr and keep notifying until: * - we hit an unwritten wavehdr * - we hit the beginning of a running loop * - we hit a wavehdr which hasn't finished playing */ - while( (lpWaveHdr=wwo->lpQueuePtr) && - (force || - (lpWaveHdr != wwo->lpPlayPtr && - lpWaveHdr != wwo->lpLoopPtr && - lpWaveHdr->reserved <= wwo->dwPlayedTotal ))) { + while ((lpWaveHdr = wwo->lpQueuePtr) && + (force || + (lpWaveHdr != wwo->lpPlayPtr && + lpWaveHdr != wwo->lpLoopPtr && + lpWaveHdr->reserved <= wwo->dwPlayedTotal))) { wwo->lpQueuePtr = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; - TRACE("Notifying client with %p\n", lpWaveHdr); - if (OSS_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) { - WARN("can't notify client !\n"); - } + wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0); } - return ( lpWaveHdr && - lpWaveHdr != wwo->lpPlayPtr && - lpWaveHdr != wwo->lpLoopPtr ) ? - wodPlayer_NotifyWait( wwo, lpWaveHdr ) : - 0 ; + return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ? + wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE; } /************************************************************************** @@ -692,10 +667,10 @@ * * wodPlayer helper. Resets current output stream. */ -static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset) +static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset) { /* updates current notify list */ - wodPlayer_NotifyCompletions(wwo, uDevID, FALSE); + wodPlayer_NotifyCompletions(wwo, FALSE); /* flush all possible output */ if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) { @@ -706,72 +681,38 @@ } /* Clear partial wavehdr */ - wwo->dwPartialBytes=0; - wwo->lpPartialData=NULL; + wwo->dwPartialOffset = 0; if (reset) { /* empty notify list */ - wodPlayer_NotifyCompletions(wwo, uDevID, TRUE); + wodPlayer_NotifyCompletions(wwo, TRUE); wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; wwo->state = WINE_WS_STOPPED; wwo->dwPlayedTotal = 0; - wwo->dwWrittenTotal = 0; + wwo->dwWrittenTotal = 0; } else { - /* FIXME: this is not accurate when looping, but can be do better ? */ + /* FIXME: this is not accurate when looping, but can we do better ? */ wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr; wwo->state = WINE_WS_PAUSED; } } /************************************************************************** - * wodPlayer_AwaitEvent [internal] - * Wait for a command to be sent to the wodPlayer or for time to pass - * until wodPlayer needs to do something again. - */ -static void wodPlayer_AwaitEvent( WINE_WAVEOUT* wwo, - DWORD dwNextFeedTime, - DWORD dwNextNotifyTime ) -{ - DWORD dwSleepTime; - - /** Wait for the shortest time before an action is required. If there - * are no pending actions, wait forever for a command. - */ - if( dwNextFeedTime == 0 ) { - if( dwNextNotifyTime == 0 ) - dwSleepTime=INFINITE; - else - dwSleepTime=dwNextNotifyTime; - } else { - if( dwNextNotifyTime == 0 ) - dwSleepTime=dwNextFeedTime; - else - dwSleepTime=min( dwNextFeedTime, dwNextNotifyTime ); - } - TRACE( "waiting %lu millis (%lu,%lu)\n", dwSleepTime, - dwNextFeedTime,dwNextNotifyTime ); - WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime); - TRACE( "wait returned\n"); - -} - -/************************************************************************** * wodPlayer_ProcessMessages [internal] */ -static void wodPlayer_ProcessMessages( WINE_WAVEOUT* wwo, WORD uDevID ) +static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo) { LPWAVEHDR lpWaveHdr; - int msg; + enum win_wm_message msg; DWORD param; HANDLE ev; while (OSS_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) { - TRACE( "Received %s %lx\n", - wodPlayerCmdString[msg-WM_USER-1], param ); + TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param); switch (msg) { case WINE_WM_PAUSING: - wodPlayer_Reset(wwo, uDevID, FALSE); + wodPlayer_Reset(wwo, FALSE); wwo->state = WINE_WS_PAUSED; SetEvent(ev); break; @@ -788,14 +729,34 @@ for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); *wh = lpWaveHdr; } - wodPlayer_BeginWaveHdr(wwo,lpWaveHdr); + if (!wwo->lpPlayPtr) + wodPlayer_BeginWaveHdr(wwo,lpWaveHdr); if (wwo->state == WINE_WS_STOPPED) wwo->state = WINE_WS_PLAYING; break; case WINE_WM_RESETTING: - wodPlayer_Reset(wwo, uDevID, TRUE); + wodPlayer_Reset(wwo, TRUE); SetEvent(ev); break; + case WINE_WM_UPDATE: + { + audio_buf_info info; + + if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) { + ERR("ioctl failed (%s)\n", strerror(errno)); + } else { + wodUpdatePlayedTotal(wwo, info.bytes); + } + } + SetEvent(ev); + break; + case WINE_WM_BREAKLOOP: + if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) { + /* ensure exit at end of current loop */ + wwo->dwLoops = 1; + } + SetEvent(ev); + break; case WINE_WM_CLOSING: /* sanity check: this should not happen since the device must have been reset before */ if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n"); @@ -816,77 +777,45 @@ * Feed as much sound data as we can into the DSP and return the number of * milliseconds before it will be necessary to feed the DSP again. */ -static DWORD wodPlayer_FeedDSP( WINE_WAVEOUT* wwo ) +static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo) { - LPWAVEHDR lpWaveHdr=wwo->lpPlayPtr; - DWORD written=0; - DWORD bytesToWrite; audio_buf_info dspspace; + DWORD availInQ; - /* Read output space info so we know how much writing to do */ if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &dspspace) < 0) { - ERR("IOCTL can't 'SNDCTL_DSP_GETOSPACE' !\n"); + ERR("IOCTL can't 'SNDCTL_DSP_GETOSPACE' !\n"); + return INFINITE; } + TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n", + dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes); - TRACE( "fragments=%d, fragsize=%d, fragstotal=%d, bytes=%d\n", - dspspace.fragments, dspspace.fragsize, - dspspace.fragstotal, dspspace.bytes ); + availInQ = dspspace.bytes; + wodUpdatePlayedTotal(wwo, availInQ); - /* Do nothing if the DSP isn't hungry */ - if( dspspace.fragments==0 ) { - return wodPlayer_DSPWait(wwo,dspspace.fragments); + /* 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\n"); + ioctl(wwo->unixdev, SNDCTL_DSP_SYNC, 0); + wodUpdatePlayedTotal(wwo, wwo->dwBufferSize); + return INFINITE; } - /* If there's a partially written wavehdr, feed more of it - * ------------------------------------------------------- - */ - bytesToWrite=wwo->dwPartialBytes; - if( bytesToWrite > 0 ) { - TRACE("partial write %lu bytes at %p\n", - wwo->dwPartialBytes, - wwo->lpPartialData ); - written=wodPlayer_WriteMaxFrags( wwo, wwo->lpPartialData, - bytesToWrite, - &dspspace ); - if( written >= bytesToWrite ) { - /* If we wrote it all, skip to the next header */ - wwo->dwPartialBytes=0; - wwo->lpPartialData=NULL; - lpWaveHdr=wodPlayer_PlayPtrNext( wwo ); - } else { - /* Remove the amount written */ - wwo->lpPartialData+=written; - wwo->dwPartialBytes-=written; - } - } - - /* Feed wavehdrs until we run out of wavehdrs or DSP space - * ------------------------------------------------------- - */ - while( lpWaveHdr && dspspace.fragments > 0 ) { - TRACE( "Writing wavehdr %p %lu bytes\n", lpWaveHdr, - lpWaveHdr->dwBufferLength ); + /* no more room... no need to try to feed */ + if (dspspace.fragments == 0) return wodPlayer_DSPWait(wwo, 0); - /* note the value that dwPlayedTotal will be when this - * wave finishes playing - */ - lpWaveHdr->reserved=wwo->dwWrittenTotal+lpWaveHdr->dwBufferLength; + /* Feed from partial wavehdr */ + if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) { + wodPlayer_WriteMaxFrags(wwo, &availInQ); + } - written=wodPlayer_WriteMaxFrags( wwo, lpWaveHdr->lpData, - lpWaveHdr->dwBufferLength, - &dspspace ); - /* If it's all written, on to the next one, else remember the - * partial write for next FeedDSP call. - */ - if( written >= lpWaveHdr->dwBufferLength ) { - lpWaveHdr=wodPlayer_PlayPtrNext( wwo ); - } else { - wwo->dwPartialBytes=lpWaveHdr->dwBufferLength-written; - wwo->lpPartialData=lpWaveHdr->lpData+written; - } + /* 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 lpWaveHdr ? wodPlayer_DSPWait(wwo,dspspace.fragments) : 0 ; + return wodPlayer_DSPWait(wwo, availInQ); } @@ -897,20 +826,26 @@ { WORD uDevID = (DWORD)pmt; WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID]; - DWORD dwNextFeedTime=0; /* Time before DSP needs feeding */ - DWORD dwNextNotifyTime=0; /* Time before next wave completion */ + DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */ + DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */ + DWORD dwSleepTime; - wwo->state=WINE_WS_STOPPED; - SetEvent( wwo->hStartUpEvent ); + wwo->state = WINE_WS_STOPPED; + SetEvent(wwo->hStartUpEvent); - for(;;) { - wodPlayer_AwaitEvent(wwo,dwNextFeedTime,dwNextNotifyTime); - wodPlayer_ProcessMessages(wwo,uDevID); - if( wwo->state== WINE_WS_PLAYING ) { - dwNextFeedTime=wodPlayer_FeedDSP(wwo); - dwNextNotifyTime=wodPlayer_NotifyCompletions(wwo,uDevID,FALSE); + for (;;) { + /** Wait for the shortest time before an action is required. If there + * are no pending actions, wait forever for a command. + */ + dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime); + TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime); + WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime); + wodPlayer_ProcessMessages(wwo); + if (wwo->state == WINE_WS_PLAYING) { + dwNextFeedTime = wodPlayer_FeedDSP(wwo); + dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE); } else { - dwNextFeedTime=dwNextNotifyTime=0; + dwNextFeedTime = dwNextNotifyTime = INFINITE; } } } @@ -1039,7 +974,7 @@ if (dsp_stereo != (wwo->format.wf.nChannels > 1) ? 1 : 0) ERR("Can't set stereo to %u (%d)\n", (wwo->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo); - if (!NEAR_MATCH(sample_rate,wwo->format.wf.nSamplesPerSec)) + if (!NEAR_MATCH(sample_rate, wwo->format.wf.nSamplesPerSec)) ERR("Can't set sample_rate to %lu (%d)\n", wwo->format.wf.nSamplesPerSec, sample_rate); @@ -1062,11 +997,10 @@ /* Remember fragsize and total buffer size for future use */ wwo->dwFragmentSize = info.fragsize; wwo->dwBufferSize = info.fragstotal * info.fragsize; - wwo->dwMillisPerFragment = info.fragsize * 1000 / wwo->format.wf.nAvgBytesPerSec; wwo->uWaitForFragments = info.fragstotal * REFILL_BUFFER_WHEN; - TRACE( "wait for %d fragments at %lu millis/fragment\n", - wwo->uWaitForFragments, - wwo->dwMillisPerFragment ); + wwo->dwPlayedTotal = 0; + wwo->dwWrittenTotal = 0; + TRACE("wait for %d fragments\n", wwo->uWaitForFragments); OSS_InitRingMessage(&wwo->msgRing); @@ -1091,11 +1025,7 @@ wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels, wwo->format.wf.nBlockAlign); - if (OSS_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { - WARN("can't notify client !\n"); - return MMSYSERR_INVALPARAM; - } - return MMSYSERR_NOERROR; + return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L); } /************************************************************************** @@ -1130,10 +1060,7 @@ close(wwo->unixdev); wwo->unixdev = -1; wwo->dwFragmentSize = 0; - if (OSS_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { - WARN("can't notify client !\n"); - ret = MMSYSERR_INVALPARAM; - } + ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L); } return ret; } @@ -1246,10 +1173,7 @@ /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */ /* FIXME: Myst crashes with this ... hmm -MM - if (OSS_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) { - WARN("can't notify client !\n"); - return MMSYSERR_INVALPARAM; - } + return wodNotifyClient(wwo, WOM_DONE, 0L, 0L); */ return MMSYSERR_NOERROR; @@ -1273,7 +1197,6 @@ return MMSYSERR_NOERROR; } - /************************************************************************** * wodGetPosition [internal] */ @@ -1293,7 +1216,8 @@ if (lpTime == NULL) return MMSYSERR_INVALPARAM; wwo = &WOutDev[wDevID]; - val = updatePlayedTotal(wwo); + OSS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE); + val = wwo->dwPlayedTotal; TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n", lpTime->wType, wwo->format.wBitsPerSample, @@ -1307,7 +1231,7 @@ TRACE("TIME_BYTES=%lu\n", lpTime->u.cb); break; case TIME_SAMPLES: - lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample; + lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels; TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample); break; case TIME_SMPTE: @@ -1336,6 +1260,21 @@ } /************************************************************************** + * wodBreakLoop [internal] + */ +static DWORD wodBreakLoop(WORD wDevID) +{ + TRACE("(%u);\n", wDevID); + + if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) { + WARN("bad device ID !\n"); + return MMSYSERR_BADDEVICEID; + } + OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE); + return MMSYSERR_NOERROR; +} + +/************************************************************************** * wodGetVolume [internal] */ static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol) @@ -1353,7 +1292,7 @@ return MMSYSERR_NOTENABLED; } if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) { - WARN("unable read mixer !\n"); + WARN("unable to read mixer !\n"); return MMSYSERR_NOTENABLED; } close(mixer); @@ -1364,7 +1303,6 @@ return MMSYSERR_NOERROR; } - /************************************************************************** * wodSetVolume [internal] */ @@ -1385,7 +1323,7 @@ return MMSYSERR_NOTENABLED; } if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) { - WARN("unable set mixer !\n"); + WARN("unable to set mixer !\n"); return MMSYSERR_NOTENABLED; } else { TRACE("volume=%04x\n", (unsigned)volume); @@ -1434,7 +1372,7 @@ case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2); case WODM_PAUSE: return wodPause (wDevID); case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2); - case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED; + case WODM_BREAKLOOP: return wodBreakLoop (wDevID); case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2); @@ -1497,14 +1435,14 @@ /* for some reason, es1371 and sblive! sometimes have junk in here. * clear it, or we get junk noise */ /* some libc implementations are buggy: their memset reads from the buffer... - * to work around it, we have the 0 the block by hand and do not call: - * memset(wwo->mapping,0,wwo->maplen); + * to work around it, we have to zero the block by hand. We don't do the expected: + * memset(wwo->mapping,0, wwo->maplen); */ { char* p1 = wwo->mapping; unsigned len = wwo->maplen; - if (len >= 16) /* so we can have at least a 4 longs to store... */ + if (len >= 16) /* so we can have at least a 4 long area to store... */ { /* the mmap:ed value is (at least) dword aligned * so, start filling the complete unsigned long:s @@ -1902,6 +1840,31 @@ *======================================================================*/ /************************************************************************** + * widNotifyClient [internal] + */ +static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2) +{ + TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2); + + switch (wMsg) { + case WIM_OPEN: + case WIM_CLOSE: + case WIM_DATA: + if (wwi->wFlags != DCB_NULL && + !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, wwi->waveDesc.hWave, + wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) { + WARN("can't notify client !\n"); + return MMSYSERR_ERROR; + } + break; + default: + FIXME("Unknown CB message %u\n", wMsg); + return MMSYSERR_INVALPARAM; + } + return MMSYSERR_NOERROR; +} + +/************************************************************************** * widGetDevCaps [internal] */ static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize) @@ -1929,14 +1892,11 @@ WAVEHDR* lpWaveHdr; DWORD dwSleepTime; DWORD bytesRead; - LPVOID buffer = HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - wwi->dwFragmentSize); - + LPVOID buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwFragmentSize); LPVOID pOffset = buffer; audio_buf_info info; int xs; - int msg; + enum win_wm_message msg; DWORD param; HANDLE ev; @@ -1997,11 +1957,7 @@ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; - if (OSS_NotifyClient(uDevID, WIM_DATA, - (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) - { - WARN("can't notify client !\n"); - } + widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0); lpWaveHdr = wwi->lpQueuePtr = lpNext; } } @@ -2041,11 +1997,7 @@ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; - if (OSS_NotifyClient(uDevID, WIM_DATA, - (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) - { - WARN("can't notify client !\n"); - } + widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0); wwi->lpQueuePtr = lpWaveHdr = lpNext; if (!lpNext && bytesRead) { @@ -2116,10 +2068,7 @@ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; - if (OSS_NotifyClient(uDevID, WIM_DATA, - (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) { - WARN("can't notify client !\n"); - } + widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0); } wwi->lpQueuePtr = NULL; SetEvent(ev); @@ -2258,11 +2207,7 @@ CloseHandle(wwi->hStartUpEvent); wwi->hStartUpEvent = INVALID_HANDLE_VALUE; - if (OSS_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { - WARN("can't notify client !\n"); - return MMSYSERR_INVALPARAM; - } - return MMSYSERR_NOERROR; + return widNotifyClient(wwi, WIM_OPEN, 0L, 0L); } /************************************************************************** @@ -2289,11 +2234,7 @@ close(wwi->unixdev); wwi->unixdev = -1; wwi->dwFragmentSize = 0; - if (OSS_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { - WARN("can't notify client !\n"); - return MMSYSERR_INVALPARAM; - } - return MMSYSERR_NOERROR; + return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L); } /**************************************************************************