What I've done instead is reversed my poll patch and
added a patch to mmsystem.c which causes application
(and possibly some non-application) callbacks to be
called from a different thread so that
wodPlayer_Notify doesn't ever halt waiting for the
application to return (which may in turn be waiting on
wodReset,wodRestart, wodClose, wodPause etc). I'll
post this to wine-patches in just a moment.
well, I think it's a bit overkill. furthermore, this won't
let synchronize that the notification has been done before
returning from the call. this stops waiting in notify, but
doesn't synchronize at the end (except if you wrap every
entry point in winmm/mmsys which might generate a
notification with a synchro with this extra thread)
I think we should not use a general event for the synchro
in audio.c, but rather create a new event for each time we
need to wait (wodReset...) and pass it to the WWO_MSG struct,
hence removing the race condition. does the attached patch
solves your issue ?
With the audio.c poll patch I posted, wodPlayer_Notify
doesn't update the total bytes played until after
sufficient time has elapsed for the dsp to have
finished playing it. The absence of more data to send
to the dsp may stop the polling loop before all sounds
have been played, meaning that the total bytes for a
the last sample written may not be updated until the
next wave is queued to be written to the dsp.
It appears that HL loops checking wodGetPosition until
the last sound has been played before writing the next
sound. This never happens with my poll patch because
of the issue in the previous para. Hence: no sound.
I verified this by doing another evil hack which just
notified and updated total bytes as soon as they were
written. The sound returned.
ok (I didn't get it was with the poll patch applied)
but OTOH, the poll patch can have nasty effects:
1/ let's assume an app uses only two wavehdr:s which cumuled
size if smaller than the OSS play ring
2/ the app will send the two wavehdr:s and they will be queued
and written to /dev/dsp
3/ if we use the modification to not have the busy wait, then
no notification will be ever made (IIRC some Flash apps are
doing this). They normally wait for notification to occur
in order to resubmit the same wavehdr:s will different
values
so, I think the poll patch (even with some modifications)
introduces some more evil parts than it fixes (and your deadlock
issues don't come from the playback loop)
--
---------------
Eric Pouech (
http://perso.wanadoo.fr/eric.pouech/)
"The future will be better tomorrow", Vice President Dan Quayle
Index: dlls/winmm/wineoss/audio.c
===================================================================
RCS file: /usr/share/cvs/cvsroot/wine/wine/dlls/winmm/wineoss/audio.c,v
retrieving revision 1.44
diff -u -r1.44 audio.c
--- dlls/winmm/wineoss/audio.c 2001/08/18 16:09:41 1.44
+++ dlls/winmm/wineoss/audio.c 2001/11/01 10:19:52
@@ -87,6 +87,7 @@
typedef struct {
int msg;
DWORD param;
+ HANDLE hEvent;
} WWO_MSG;
typedef struct {
@@ -109,9 +110,9 @@
DWORD dwRemain; /* number of bytes to write to end the current fragment */
/* synchronization stuff */
+ HANDLE hStartUpEvent;
HANDLE hThread;
DWORD dwThreadID;
- HANDLE hEvent;
#define WWO_RING_BUFFER_SIZE 30
WWO_MSG messages[WWO_RING_BUFFER_SIZE];
int msg_tosave;
@@ -522,29 +523,40 @@
}
-int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param)
+static int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param, BOOL wait)
{
+ HANDLE hEvent;
+
EnterCriticalSection(&wwo->msg_crst);
if ((wwo->msg_tosave == wwo->msg_toget) /* buffer overflow ? */
- && (wwo->messages[wwo->msg_toget].msg))
+ && (wwo->messages[wwo->msg_toget].msg))
{
ERR("buffer overflow !?\n");
LeaveCriticalSection(&wwo->msg_crst);
return 0;
}
+ hEvent = wait ? CreateEventA(NULL, FALSE, FALSE, NULL) : INVALID_HANDLE_VALUE;
+
wwo->messages[wwo->msg_tosave].msg = msg;
wwo->messages[wwo->msg_tosave].param = param;
+ wwo->messages[wwo->msg_tosave].hEvent = hEvent;
+
wwo->msg_tosave++;
if (wwo->msg_tosave > WWO_RING_BUFFER_SIZE-1)
wwo->msg_tosave = 0;
LeaveCriticalSection(&wwo->msg_crst);
/* signal a new message */
SetEvent(wwo->msg_event);
+ if (wait)
+ {
+ WaitForSingleObject(hEvent, INFINITE);
+ CloseHandle(hEvent);
+ }
return 1;
}
-int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param)
+static int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param, HANDLE *hEvent)
{
EnterCriticalSection(&wwo->msg_crst);
@@ -557,6 +569,7 @@
*msg = wwo->messages[wwo->msg_toget].msg;
wwo->messages[wwo->msg_toget].msg = 0;
*param = wwo->messages[wwo->msg_toget].param;
+ *hEvent = wwo->messages[wwo->msg_toget].hEvent;
wwo->msg_toget++;
if (wwo->msg_toget > WWO_RING_BUFFER_SIZE-1)
wwo->msg_toget = 0;
@@ -642,6 +655,7 @@
DWORD param;
DWORD tc;
BOOL had_msg;
+ HANDLE ev;
wwo->state = WINE_WS_STOPPED;
@@ -652,7 +666,7 @@
wwo->dwPlayedTotal = 0;
TRACE("imhere[0]\n");
- SetEvent(wwo->hEvent);
+ SetEvent(wwo->hStartUpEvent);
for (;;) {
/* wait for dwSleepTime or an event in thread's queue
@@ -684,17 +698,17 @@
TRACE("imhere[2] (q=%p p=%p) tc = %08lx\n", wwo->lpQueuePtr,
wwo->lpPlayPtr, GetTickCount());
had_msg = FALSE;
- while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m)) {
+ while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m, &ev)) {
had_msg = TRUE;
switch (msg) {
case WINE_WM_PAUSING:
wodPlayer_Reset(wwo, uDevID, FALSE);
wwo->state = WINE_WS_PAUSED;
- SetEvent(wwo->hEvent);
+ SetEvent(ev);
break;
case WINE_WM_RESTARTING:
wwo->state = WINE_WS_PLAYING;
- SetEvent(wwo->hEvent);
+ SetEvent(ev);
break;
case WINE_WM_HEADER:
lpWaveHdr = (LPWAVEHDR)param;
@@ -711,14 +725,14 @@
break;
case WINE_WM_RESETTING:
wodPlayer_Reset(wwo, uDevID, TRUE);
- SetEvent(wwo->hEvent);
+ 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");
wwo->hThread = 0;
wwo->state = WINE_WS_CLOSED;
- SetEvent(wwo->hEvent);
+ SetEvent(ev);
ExitThread(0);
/* shouldn't go here */
default:
@@ -895,14 +909,15 @@
InitializeCriticalSection(&wwo->msg_crst);
if (!(dwFlags & WAVE_DIRECTSOUND)) {
- wwo->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+ wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
- WaitForSingleObject(wwo->hEvent, INFINITE);
+ WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
+ CloseHandle(wwo->hStartUpEvent);
} else {
- wwo->hEvent = INVALID_HANDLE_VALUE;
wwo->hThread = INVALID_HANDLE_VALUE;
wwo->dwThreadID = 0;
}
+ wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
TRACE("fd=%d fragmentSize=%ld\n",
wwo->unixdev, wwo->dwFragmentSize);
@@ -942,10 +957,8 @@
ret = WAVERR_STILLPLAYING;
} else {
TRACE("imhere[3-close]\n");
- if (wwo->hEvent != INVALID_HANDLE_VALUE) {
- wodPlayer_Message(wwo, WINE_WM_CLOSING, 0);
- WaitForSingleObject(wwo->hEvent, INFINITE);
- CloseHandle(wwo->hEvent);
+ if (wwo->hThread != INVALID_HANDLE_VALUE) {
+ wodPlayer_Message(wwo, WINE_WM_CLOSING, 0, TRUE);
}
if (wwo->mapping) {
munmap(wwo->mapping, wwo->maplen);
@@ -988,7 +1001,7 @@
lpWaveHdr->lpNext = 0;
TRACE("imhere[3-HEADER]\n");
- wodPlayer_Message(&WOutDev[wDevID], WINE_WM_HEADER, (DWORD)lpWaveHdr);
+ wodPlayer_Message(&WOutDev[wDevID], WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
return MMSYSERR_NOERROR;
}
@@ -1047,8 +1060,7 @@
}
TRACE("imhere[3-PAUSING]\n");
- wodPlayer_Message(&WOutDev[wDevID], WINE_WM_PAUSING, 0);
- WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
+ wodPlayer_Message(&WOutDev[wDevID], WINE_WM_PAUSING, 0, TRUE);
return MMSYSERR_NOERROR;
}
@@ -1067,8 +1079,7 @@
if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
TRACE("imhere[3-RESTARTING]\n");
- wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESTARTING, 0);
- WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
+ wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESTARTING, 0, TRUE);
}
/* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
@@ -1095,8 +1106,7 @@
}
TRACE("imhere[3-RESET]\n");
- wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESETTING, 0);
- WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
+ wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESETTING, 0, TRUE);
return MMSYSERR_NOERROR;
}