Enhanced synchronization between playback thread and stop/close commands
Now using notification for detecting when a WAVEHDR has been played

diff --git a/dlls/winmm/mciwave/mciwave.c b/dlls/winmm/mciwave/mciwave.c
index fbb715b..2c08019 100644
--- a/dlls/winmm/mciwave/mciwave.c
+++ b/dlls/winmm/mciwave/mciwave.c
@@ -8,7 +8,6 @@
 /*
  * FIXME:
  *	- record/play should and must be done asynchronous
- *	- segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
  */
 
 #include "winuser.h"
@@ -25,7 +24,6 @@
     int				nUseCount;	/* Incremented for each shared open */
     BOOL			fShareable;	/* TRUE if first open was shareable */
     WORD			wNotifyDeviceID;/* MCI device ID with a pending notification */
-    HANDLE			hCallback;	/* Callback handle for pending notification */
     HMMIO			hFile;		/* mmio file handle open as Element */
     MCI_WAVE_OPEN_PARMSA 	openParms;
     LPWAVEFORMATEX		lpWaveFormat;
@@ -35,6 +33,8 @@
     DWORD			dwFileOffset;   /* Offset of chunk in mmio file */
     DWORD			dwLength;	/* number of bytes in chunk for playing */
     DWORD			dwPosition;	/* position in bytes in chunk for playing */
+    HANDLE			hEvent;		/* for synchronization */
+    DWORD			dwEventCount;	/* for synchronization */
 } WINE_MCIWAVE;
 
 /* ===================================================================
@@ -414,30 +414,40 @@
  */
 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
-    DWORD 		dwRet;
+    DWORD 		dwRet = 0;
     WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
     
     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
     
-    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
     if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
     
-    wmw->dwStatus = MCI_MODE_STOP;
+    /* wait for playback thread (if any) to exit before processing further */
+    switch (wmw->dwStatus) {
+    case MCI_MODE_PAUSE:
+    case MCI_MODE_PLAY:
+    case MCI_MODE_RECORD:
+	{
+	    int oldStat = wmw->dwStatus;
+	    wmw->dwStatus = MCI_MODE_NOT_READY;
+	    if (oldStat == MCI_MODE_PAUSE)
+		dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
+	}
+	while (((volatile WINE_MCIWAVE*)wmw)->dwStatus != MCI_MODE_STOP)
+	    Sleep(10);
+	break;
+    }
+
     wmw->dwPosition = 0;
-    TRACE("wmw->dwStatus=%d\n", wmw->dwStatus);
+
+    /* sanity resets */
+    wmw->dwStatus = MCI_MODE_STOP;
     
-    if (wmw->fInput)
-	dwRet = waveInReset(wmw->hWave);
-    else
-	dwRet = waveOutReset(wmw->hWave);
-    
-    if (dwFlags & MCI_NOTIFY) {
-	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
+    if ((dwFlags & MCI_NOTIFY) && lpParms) {
 	mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
 			wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
     }
     
-    return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
+    return dwRet;
 }
 
 /**************************************************************************
@@ -459,37 +469,69 @@
     wmw->nUseCount--;
     
     if (wmw->nUseCount == 0) {
-	DWORD	mmRet;
 	if (wmw->hFile != 0) {
 	    mmioClose(wmw->hFile, 0);
 	    wmw->hFile = 0;
 	}
-	mmRet = (wmw->fInput) ? waveInClose(wmw->hWave) : waveOutClose(wmw->hWave);
-	
-	if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
     }
     
     HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
     wmw->lpWaveFormat = NULL;
 
     if ((dwFlags & MCI_NOTIFY) && lpParms) {
-	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
 	mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
 			wmw->wNotifyDeviceID,
 			(dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
     }
+
     return 0;
 }
 
 /**************************************************************************
+ * 				WAVE_mciPlayCallback		[internal]
+ */
+static	void	CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg, 
+					      DWORD dwInstance,  
+					      DWORD dwParam1, DWORD dwParam2)
+{
+    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)dwInstance;
+
+    switch (uMsg) {
+    case WOM_OPEN:
+    case WOM_CLOSE:
+	break;
+    case WOM_DONE:
+	InterlockedIncrement(&wmw->dwEventCount);
+	TRACE("Returning waveHdr=%lx\n", dwParam1);
+	SetEvent(wmw->hEvent);
+	break;
+    default:
+	ERR("Unknown uMsg=%d\n", uMsg);
+    }
+}
+
+static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
+{
+    for (;;) {
+	ResetEvent(wmw->hEvent);
+	if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
+	    break;
+	}
+	InterlockedIncrement(&wmw->dwEventCount);
+	
+	WaitForSingleObject(wmw->hEvent, INFINITE);
+    }
+}
+
+/**************************************************************************
  * 				WAVE_mciPlay		[internal]
  */
 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
 {
     DWORD		end;
     LONG		bufsize, count, left;
-    DWORD		dwRet;
-    LPWAVEHDR		waveHdr;
+    DWORD		dwRet = 0;
+    LPWAVEHDR		waveHdr = NULL;
     WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
     int			whidx;
 
@@ -546,15 +588,19 @@
      * and to start sending to hWave when dwPosition is reached)
      */
     mmioSeek(wmw->hFile, wmw->dwFileOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
-    
+
     /* By default the device will be opened for output, the MCI_CUE function is there to
      * change from output to input and back
      */
     /* FIXME: how to choose between several output channels ? here mapper is forced */
-    dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, 0L, 0L, CALLBACK_NULL);
+    dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, 
+			(DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
+
     if (dwRet != 0) {
 	TRACE("Can't open low level audio device %ld\n", dwRet);
-	return MCIERR_DEVICE_OPEN;
+	dwRet = MCIERR_DEVICE_OPEN;
+	wmw->hWave = 0;
+	goto cleanUp;
     }
 
     /* make it so that 3 buffers per second are needed */
@@ -563,62 +609,75 @@
     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
-    waveHdr[0].dwUser  = waveHdr[1].dwUser = 0L;
-    waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
+    waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
+    waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
+    waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
+    waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
+    if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 
+	waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
+	dwRet = MCIERR_INTERNAL;
+	goto cleanUp;
+    }
 
-    wmw->dwStatus = MCI_MODE_PLAY;
-    
     whidx = 0;
     left = MIN(wmw->dwLength, end - wmw->dwPosition);
+    wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+    wmw->dwEventCount = 1L; /* for first buffer */
 
     TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
-
+    wmw->dwStatus = MCI_MODE_PLAY;
+    
     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
-    while (left > 0 && wmw->dwStatus != MCI_MODE_STOP) {
-	waveHdr[whidx].dwFlags = 0L;
+    while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
 	count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, MIN(bufsize, left));
 	TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
 	if (count < 1) 
 	    break;
-	left -= count;
+	/* count is always <= bufsize, so this is correct regarding the 
+	 * waveOutPrepareHeader function 
+	 */
 	waveHdr[whidx].dwBufferLength = count;
-	dwRet = waveOutPrepareHeader(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
-/* EPP 	waveHdr[whidx].dwBytesRecorded = 0; */
+	waveHdr[whidx].dwFlags &= ~WHDR_DONE;
 	TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
 	      &waveHdr[whidx], waveHdr[whidx].dwBufferLength, 
 	      waveHdr[whidx].dwBytesRecorded);
 	dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
+	left -= count;
 	wmw->dwPosition += count;
 	TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
 
+	WAVE_mciPlayWaitDone(wmw);
 	whidx ^= 1;
-	if (waveHdr[whidx].dwFlags & WHDR_PREPARED) {
-	    /* FIXME: should use callback mechanisms from audio driver */
-	    while (!(waveHdr[whidx].dwFlags & WHDR_DONE))
-		Sleep(100);
-	    dwRet = waveOutUnprepareHeader(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
-	}
     }
-    whidx ^= 1;
-    if (waveHdr[whidx].dwFlags & WHDR_PREPARED) {
-	/* test if waveHdr[whidx] has been prepared, if so it has been queued for playing */
-	while (!(waveHdr[whidx].dwFlags & WHDR_DONE))
-	    Sleep(100);
-	waveOutUnprepareHeader(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
-    }
-    
+
+    WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
+
+    /* just to get rid of some race conditions between play, stop and pause */
+    waveOutReset(wmw->hWave);
+
+    waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
+    waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
+
+    dwRet = 0;
+
+cleanUp:    
     HeapFree(GetProcessHeap(), 0, waveHdr);
 
-    waveOutReset(wmw->hWave);
-    waveOutClose(wmw->hWave);
+    if (wmw->hWave) {
+	waveOutClose(wmw->hWave);
+	wmw->hWave = 0;
+    }
+    CloseHandle(wmw->hEvent);
+
+    if (lpParms && (dwFlags & MCI_NOTIFY)) {
+	mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
+			wmw->wNotifyDeviceID, 
+			dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
+    }
 
     wmw->dwStatus = MCI_MODE_STOP;
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-	mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
-			wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-    }
-    return 0;
+
+    return dwRet;
 }
 
 /**************************************************************************
@@ -675,7 +734,6 @@
     HeapFree(GetProcessHeap(), 0, waveHdr.lpData);
 
     if (dwFlags & MCI_NOTIFY) {
-	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
 	mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
 			wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
     }
@@ -715,14 +773,12 @@
     
     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
     
-    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
     if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
     
     if (wmw->dwStatus == MCI_MODE_PAUSE) {
 	wmw->dwStatus = MCI_MODE_PLAY;
     } 
     
-    /* FIXME: I doubt WIDM_START is correct */
     if (wmw->fInput)	dwRet = waveInStart(wmw->hWave);
     else		dwRet = waveOutRestart(wmw->hWave);
     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
@@ -759,7 +815,6 @@
 	TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
 	
 	if (dwFlags & MCI_NOTIFY) {
-	    TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
 	    mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
 			    wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
 	}
@@ -990,7 +1045,6 @@
 	}
     }
     if (dwFlags & MCI_NOTIFY) {
-	TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
 	mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
 			wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
     }