Commit 738669a7 authored by Eric Pouech's avatar Eric Pouech Committed by Alexandre Julliard

Fixed the wodReset implementation, internal messages priority, full

duplex code, wait algorithm for feeding the OSS buffer.
parent d0f260eb
......@@ -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 @@ DEFAULT_DEBUG_CHANNEL(wave);
/* 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 @@ typedef struct {
* 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 @@ typedef struct {
/* 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 @@ static const char *wodPlayerCmdString[] = {
"WINE_WM_RESETTING",
"WINE_WM_HEADER",
"WINE_WM_UPDATE",
"WINE_WM_CLOSING",
"WINE_WM_BREAKLOOP",
"WINE_WM_CLOSING",
};
/*======================================================================*
......@@ -178,8 +174,9 @@ static const char *wodPlayerCmdString[] = {
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 BOOL OSS_FullDuplex; /* set to non-zero if the device suppo
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 @@ static int OSS_OpenDevice(unsigned req_access)
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_DestroyRingMessage(OSS_MSG_RING* omr)
*/
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;
omr->messages[omr->msg_tosave].msg = msg;
omr->messages[omr->msg_tosave].param = param;
omr->messages[omr->msg_tosave].hEvent = hEvent;
/* 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->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 @@ static int OSS_RetrieveRingMessage(OSS_MSG_RING* omr,
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 @@ static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
/**************************************************************************
* 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 DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
{
wodUpdatePlayedTotal(wwo, NULL);
/* updates current notify list */
wodPlayer_NotifyCompletions(wwo, FALSE);
......@@ -767,7 +784,11 @@ static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
}
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 @@ static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
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, &param, &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 @@ static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
}
/* 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 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
/* 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 @@ static DWORD wodClose(WORD wDevID)
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 @@ static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
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 @@ static DWORD wodPause(WORD wDevID)
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 @@ static DWORD wodRestart(WORD wDevID)
}
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 @@ static DWORD wodReset(WORD wDevID)
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 @@ static DWORD wodGetNumDevs(void)
{
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 @@ static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
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 @@ static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment