From c8cfbbf206921c94f90f93455aaa0e0032b6aae0 Mon Sep 17 00:00:00 2001 From: =?utf-8?q?J=C3=B6rg=20H=C3=B6hle?= Date: Mon, 19 Oct 2009 18:04:43 +0200 Subject: [PATCH] mciwave: Rework MCI notification system. --- dlls/mciwave/mciwave.c | 153 +++++++++++++++++++++++++++++------------------ dlls/winmm/tests/mci.c | 12 ++-- 2 files changed, 100 insertions(+), 65 deletions(-) diff --git a/dlls/mciwave/mciwave.c b/dlls/mciwave/mciwave.c index d3a5b10..2a89572 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 @@ typedef struct { int nUseCount; /* Incremented for each shared open */ HMMIO hFile; /* mmio file handle open as Element */ MCI_WAVE_OPEN_PARMSW openParms; + volatile 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 */ @@ -201,6 +203,21 @@ static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID) } /************************************************************************** + * 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 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P 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 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P 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 @@ static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS 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: @@ -616,10 +642,8 @@ static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS /* 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; } @@ -637,6 +661,7 @@ static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM 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); } @@ -656,9 +681,8 @@ static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM 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; @@ -714,6 +738,7 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, DWORD dwRet; LPWAVEHDR waveHdr = NULL; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); + HANDLE oldcb; int whidx; TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); @@ -781,6 +806,11 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, 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; @@ -872,6 +902,9 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, dwRet = 0; cleanUp: + if (dwFlags & MCI_NOTIFY) + oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); + HeapFree(GetProcessHeap(), 0, waveHdr); if (wmw->hWave) { @@ -882,11 +915,9 @@ cleanUp: 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; } @@ -961,6 +992,7 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt LONG bufsize; LPWAVEHDR waveHdr = NULL; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); + HANDLE oldcb; TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms); @@ -1015,6 +1047,11 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt 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; @@ -1083,7 +1120,11 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt 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; @@ -1095,6 +1136,9 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt dwRet = 0; cleanUp: + if (dwFlags & MCI_NOTIFY) + oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL); + HeapFree(GetProcessHeap(), 0, waveHdr); if (wmw->hWave) { @@ -1105,11 +1149,8 @@ cleanUp: 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; @@ -1150,6 +1191,8 @@ static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM default: dwRet = MCIERR_NONAPPLICABLE_FUNCTION; } + if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) + WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL); return dwRet; } @@ -1191,6 +1234,8 @@ static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PAR default: dwRet = MCIERR_NONAPPLICABLE_FUNCTION; } + if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms) + WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL); return dwRet; } @@ -1199,37 +1244,32 @@ static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PAR */ static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) { - DWORD ret = 0; WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID); TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); - if (lpParms == NULL) { - ret = MCIERR_NULL_PARAMETER_BLOCK; - } else if (wmw == NULL) { - ret = MCIERR_INVALID_DEVICE_ID; - } else { - WAVE_mciStop(wDevID, MCI_WAIT, 0); - - if (dwFlags & MCI_SEEK_TO_START) { - wmw->dwPosition = 0; - } else if (dwFlags & MCI_SEEK_TO_END) { - wmw->dwPosition = wmw->ckWaveData.cksize; - } else if (dwFlags & MCI_TO) { - wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); - } else { - WARN("dwFlag doesn't tell where to seek to...\n"); - return MCIERR_MISSING_PARAMETER; - } + if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; + if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; - TRACE("Seeking to position=%u bytes\n", wmw->dwPosition); + WAVE_mciStop(wDevID, MCI_WAIT, 0); - if (dwFlags & MCI_NOTIFY) { - mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)), - wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL); - } + if (dwFlags & MCI_SEEK_TO_START) { + wmw->dwPosition = 0; + } else if (dwFlags & MCI_SEEK_TO_END) { + wmw->dwPosition = wmw->ckWaveData.cksize; + } else if (dwFlags & MCI_TO) { + wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo); + } else { + WARN("dwFlag doesn't tell where to seek to...\n"); + return MCIERR_MISSING_PARAMETER; } - return ret; + + TRACE("Seeking to position=%u bytes\n", wmw->dwPosition); + + if (dwFlags & MCI_NOTIFY) + WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL); + + return MMSYSERR_NOERROR; } /************************************************************************** @@ -1333,6 +1373,8 @@ static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpPa 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; } @@ -1343,17 +1385,13 @@ static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW l { 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; if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID; + if (wmw->hFile == 0) return MCIERR_INTERNAL; /* FIXME: Save must work even after Open new. */ - if (dwFlags & MCI_WAIT) - { - FIXME("MCI_WAIT not implemented\n"); - } - WAVE_mciStop(wDevID, 0, NULL); + WAVE_mciStop(wDevID, MCI_WAIT, NULL); ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0); ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0); @@ -1378,12 +1416,8 @@ static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW l 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); @@ -1508,10 +1542,8 @@ static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARM 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; } @@ -1581,6 +1613,8 @@ static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags, 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; } @@ -1626,7 +1660,8 @@ static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW l } 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 6edb528..3340f33 100644 --- a/dlls/winmm/tests/mci.c +++ b/dlls/winmm/tests/mci.c @@ -458,8 +458,8 @@ static void test_asyncWAVE(HWND hwnd) 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); @@ -515,8 +515,8 @@ static void test_asyncWAVE(HWND hwnd) 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. */ @@ -532,7 +532,7 @@ static void test_asyncWAVE(HWND hwnd) 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); @@ -560,7 +560,7 @@ static void test_asyncWAVE(HWND hwnd) 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) -- 1.5.6.3