mciwave: Rework MCI notification system.
diff --git a/dlls/mciwave/mciwave.c b/dlls/mciwave/mciwave.c
index 05d16ef..deb7b40 100644
--- a/dlls/mciwave/mciwave.c
+++ b/dlls/mciwave/mciwave.c
@@ -4,6 +4,7 @@
* Copyright 1994 Martin Ayotte
* 1999,2000,2005 Eric Pouech
* 2000 Francois Jacques
+ * 2009 Jörg Höhle
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -40,6 +41,7 @@
int nUseCount; /* Incremented for each shared open */
HMMIO hFile; /* mmio file handle open as Element */
MCI_WAVE_OPEN_PARMSW openParms;
+ HANDLE hCallback; /* Callback handle for pending notification */
WAVEFORMATEX wfxRef;
LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */
BOOL fInput; /* FALSE = Output, TRUE = Input */
@@ -199,6 +201,21 @@
}
/**************************************************************************
+ * WAVE_mciNotify [internal]
+ *
+ * Notifications in MCI work like a 1-element queue.
+ * Each new notification request supersedes the previous one.
+ * This affects Play and Record; other commands are immediate.
+ */
+static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
+{
+ MCIDEVICEID wDevID = wmw->openParms.wDeviceID;
+ HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
+ if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
+ mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
+}
+
+/**************************************************************************
* WAVE_ConvertByteToTimeFormat [internal]
*/
static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
@@ -509,6 +526,7 @@
memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
/* will be set by WAVE_mciOpenFile */
wmw->openParms.lpstrElementName = NULL;
+ wmw->hCallback = NULL;
WAVE_mciDefaultFmt(wmw);
TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
@@ -529,6 +547,9 @@
wmw->dwPosition = 0;
wmw->dwStatus = MCI_MODE_STOP;
+
+ if (dwFlags & MCI_NOTIFY)
+ WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
} else {
wmw->nUseCount--;
if (wmw->hFile != 0)
@@ -595,6 +616,11 @@
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
+ if (wmw->dwStatus != MCI_MODE_STOP) {
+ HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
+ if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
+ }
+
/* wait for playback thread (if any) to exit before processing further */
switch (wmw->dwStatus) {
case MCI_MODE_PAUSE:
@@ -614,10 +640,8 @@
/* sanity resets */
wmw->dwStatus = MCI_MODE_STOP;
- if ((dwFlags & MCI_NOTIFY) && lpParms) {
- mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
- wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
- }
+ if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
return dwRet;
}
@@ -635,6 +659,7 @@
if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
if (wmw->dwStatus != MCI_MODE_STOP) {
+ /* mciStop handles MCI_NOTIFY_ABORTED */
dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
}
@@ -654,9 +679,8 @@
wmw->openParms.lpstrElementName = NULL;
if ((dwFlags & MCI_NOTIFY) && lpParms) {
- mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
- wmw->openParms.wDeviceID,
- (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
+ WAVE_mciNotify(lpParms->dwCallback, wmw,
+ (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
}
return 0;
@@ -712,6 +736,7 @@
DWORD dwRet;
LPWAVEHDR waveHdr = NULL;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+ HANDLE oldcb;
int whidx;
TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
@@ -779,6 +804,11 @@
TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
+ oldcb = InterlockedExchangePointer(&wmw->hCallback,
+ (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
+ if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
+ oldcb = NULL;
+
if (end <= wmw->dwPosition)
return MMSYSERR_NOERROR;
@@ -870,6 +900,9 @@
dwRet = 0;
cleanUp:
+ if (dwFlags & MCI_NOTIFY)
+ oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
+
HeapFree(GetProcessHeap(), 0, waveHdr);
if (wmw->hWave) {
@@ -880,11 +913,9 @@
wmw->dwStatus = MCI_MODE_STOP;
- if (lpParms && (dwFlags & MCI_NOTIFY)) {
- mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
- wmw->openParms.wDeviceID,
- dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
- }
+ /* Let the potentically asynchronous commands support FAILURE notification. */
+ if (oldcb) mciDriverNotify(oldcb, wDevID,
+ dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
return dwRet;
}
@@ -959,6 +990,7 @@
LONG bufsize;
LPWAVEHDR waveHdr = NULL;
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
+ HANDLE oldcb;
TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
@@ -1013,6 +1045,11 @@
TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
+ oldcb = InterlockedExchangePointer(&wmw->hCallback,
+ (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
+ if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
+ oldcb = NULL;
+
if (end <= wmw->dwPosition)
{
return MMSYSERR_NOERROR;
@@ -1081,7 +1118,11 @@
while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
WAVE_mciRecordWaitDone(wmw);
}
-
+ /* Grab callback before another thread kicks in after we change dwStatus. */
+ if (dwFlags & MCI_NOTIFY) {
+ oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
+ dwFlags &= ~MCI_NOTIFY;
+ }
/* needed so that the callback above won't add again the buffers returned by the reset */
wmw->dwStatus = MCI_MODE_STOP;
@@ -1093,6 +1134,9 @@
dwRet = 0;
cleanUp:
+ if (dwFlags & MCI_NOTIFY)
+ oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
+
HeapFree(GetProcessHeap(), 0, waveHdr);
if (wmw->hWave) {
@@ -1103,11 +1147,8 @@
wmw->dwStatus = MCI_MODE_STOP;
- if (lpParms && (dwFlags & MCI_NOTIFY)) {
- mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
- wmw->openParms.wDeviceID,
- dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
- }
+ if (oldcb) mciDriverNotify(oldcb, wDevID,
+ dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
return dwRet;
@@ -1148,6 +1189,8 @@
default:
dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
}
+ if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
return dwRet;
}
@@ -1189,6 +1232,8 @@
default:
dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
}
+ if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
return dwRet;
}
@@ -1219,10 +1264,9 @@
TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
- if (dwFlags & MCI_NOTIFY) {
- mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
- wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
- }
+ if (dwFlags & MCI_NOTIFY)
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
+
return MMSYSERR_NOERROR;
}
@@ -1327,6 +1371,8 @@
wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
}
+ if (dwFlags & MCI_NOTIFY)
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
return 0;
}
@@ -1337,7 +1383,6 @@
{
WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
- WPARAM wparam = MCI_NOTIFY_FAILURE;
TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
@@ -1372,12 +1417,8 @@
ret = MMSYSERR_NOERROR;
}
- if (dwFlags & MCI_NOTIFY) {
- if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
-
- mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
- wmw->openParms.wDeviceID, wparam);
- }
+ if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
if (ret == MMSYSERR_NOERROR)
ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
@@ -1502,10 +1543,8 @@
return MCIERR_UNRECOGNIZED_COMMAND;
}
}
- if (dwFlags & MCI_NOTIFY) {
- mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
- wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
- }
+ if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
return ret;
}
@@ -1575,6 +1614,8 @@
WARN("No GetDevCaps-Item !\n");
return MCIERR_UNRECOGNIZED_COMMAND;
}
+ if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
return ret;
}
@@ -1620,7 +1661,8 @@
} else {
lpParms->lpstrReturn[0] = 0;
}
-
+ if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
+ WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
return ret;
}
diff --git a/dlls/winmm/tests/mci.c b/dlls/winmm/tests/mci.c
index 5ce8cd4..e6d83e2 100644
--- a/dlls/winmm/tests/mci.c
+++ b/dlls/winmm/tests/mci.c
@@ -416,8 +416,8 @@
err = mciSendString("status mysound mode notify", buf, sizeof(buf), hwnd);
ok(!err,"mci status mode returned error: %d\n", err);
if(!err) ok(!strcmp(buf,"paused"), "mci status mode: %s\n", buf);
- todo_wine test_notification1(hwnd,"play",MCI_NOTIFY_SUPERSEDED);
- todo_wine test_notification1(hwnd,"status",MCI_NOTIFY_SUCCESSFUL);
+ test_notification(hwnd,"play",MCI_NOTIFY_SUPERSEDED);
+ test_notification(hwnd,"status",MCI_NOTIFY_SUCCESSFUL);
buf[0]=0;
err = mciSendString("status mysound position", buf, sizeof(buf), hwnd);
@@ -430,13 +430,14 @@
ok(!err,"mci stop returned error: %s\n", dbg_mcierr(err));
buf[0]=0;
- err = mciSendString("info mysound file", buf, sizeof(buf), NULL);
+ err = mciSendString("info mysound file notify", buf, sizeof(buf), hwnd);
ok(!err,"mci info file returned error: %d\n", err);
if(!err) { /* fully qualified name */
int len = strlen(buf);
todo_wine ok(len>2 && buf[1]==':',"Expected full pathname from info file: %s\n", buf);
ok(len>=12 && !strcmp(&buf[len-12],"tempfile.wav"), "info file returned: %s\n", buf);
}
+ test_notification(hwnd,"info file",MCI_NOTIFY_SUCCESSFUL);
buf[0]=0;
err = mciSendString("status mysound mode", buf, sizeof(buf), hwnd);
@@ -473,8 +474,8 @@
err = mciSendString("pause mysound notify", NULL, 0, NULL); /* notify no callback */
ok(!err,"mci pause notify returned error: %s\n", dbg_mcierr(err));
- /* Supersede even though pause cannot not notify given no callback */
- todo_wine test_notification1(hwnd,"pause aborted play #1 notification",MCI_NOTIFY_SUPERSEDED);
+ /* Supersede even though pause cannot notify given no callback */
+ test_notification(hwnd,"pause aborted play #1 notification",MCI_NOTIFY_SUPERSEDED);
test_notification(hwnd,"impossible pause notification",0);
/* Seek or even Stop used to hang Wine on MacOS. */
@@ -490,7 +491,7 @@
err = mciSendString("pause mysound wait", NULL, 0, NULL);
ok(!err,"mci pause wait returned error: %d\n", err);
/* Unlike sequencer and cdaudio, waveaudio's pause does not abort. */
- todo_wine test_notification1(hwnd,"pause aborted play #2 notification",0);
+ test_notification(hwnd,"pause aborted play #2 notification",0);
err = mciSendString("resume mysound wait", NULL, 0, NULL);
ok(!err,"mci resume wait returned error: %d\n", err);
@@ -518,7 +519,7 @@
err = mciSendString("close mysound wait", NULL, 0, NULL);
ok(!err,"mci close wait returned error: %d\n", err);
- todo_wine test_notification1(hwnd,"play (aborted by close)",MCI_NOTIFY_ABORTED);
+ test_notification(hwnd,"play (aborted by close)",MCI_NOTIFY_ABORTED);
}
static void test_AutoOpenWAVE(HWND hwnd)