* Sync up to trunk r55544.
[reactos.git] / dll / win32 / mciseq / mcimidi.c
index ec9a24a..2783c07 100644 (file)
@@ -21,9 +21,8 @@
 
 /* TODO:
  *      + implement it correctly
- *      + finish asynchronous commands (especially for reading/record)
+ *      + finish asynchronous commands
  *      + better implement non waiting command (without the MCI_WAIT flag).
- *      + implement the recording features
  */
 
 #include <stdlib.h>
@@ -62,13 +61,12 @@ typedef struct tagWINE_MCIMIDI {
     UINT               wDevID;                 /* the MCI one */
     HMIDI              hMidi;
     int                        nUseCount;              /* Incremented for each shared open          */
-    WORD               wPort;                  /* the WINMM device unit */
-    WORD               wNotifyDeviceID;        /* MCI device ID with a pending notification */
     HANDLE             hCallback;              /* Callback handle for pending notification  */
     HMMIO              hFile;                  /* mmio file handle open as Element          */
     LPWSTR             lpstrElementName;       /* Name of file (if any)                     */
     LPWSTR             lpstrCopyright;
     LPWSTR             lpstrName;
+    WORD               wPort;                  /* the WINMM device unit */
     WORD               dwStatus;               /* one from MCI_MODE_xxxx */
     DWORD              dwMciTimeFormat;        /* One of the supported MCI_FORMAT_xxxx */
     WORD               wFormat;                /* Format of MIDI hFile (0, 1 or 2) */
@@ -156,7 +154,7 @@ static      DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
  *                         MCI MIDI implementation                     *
  *======================================================================*/
 
-static DWORD MIDI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
+static DWORD MIDI_mciResume(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
 
 /**************************************************************************
  *                             MIDI_drvOpen                    [internal]
@@ -197,17 +195,35 @@ static    DWORD   MIDI_drvClose(DWORD dwDevID)
 /**************************************************************************
  *                             MIDI_mciGetOpenDev              [internal]
  */
-static WINE_MCIMIDI*  MIDI_mciGetOpenDev(UINT wDevID)
+static WINE_MCIMIDI*  MIDI_mciGetOpenDev(MCIDEVICEID wDevID, UINT wMsg)
 {
     WINE_MCIMIDI*      wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID);
 
-    if (wmm == NULL || wmm->nUseCount == 0) {
+    if (wmm == NULL || ((wmm->nUseCount == 0) ^ (wMsg == MCI_OPEN_DRIVER))) {
        WARN("Invalid wDevID=%u\n", wDevID);
        return 0;
     }
     return wmm;
 }
 
+/**************************************************************************
+ *                             MIDI_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 MIDI_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIMIDI* wmm, UINT wStatus)
+{
+    /* We simply save one parameter by not passing the wDevID local
+     * to the command.  They are the same (via mciGetDriverData).
+     */
+    MCIDEVICEID wDevID = wmm->wDevID;
+    HANDLE old = InterlockedExchangePointer(&wmm->hCallback, NULL);
+    if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
+    mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
+}
+
 /**************************************************************************
  *                             MIDI_mciReadByte                [internal]
  */
@@ -704,16 +720,13 @@ static    DWORD   MIDI_GetMThdLengthMS(WINE_MCIMIDI* wmm)
 /**************************************************************************
  *                             MIDI_mciOpen                    [internal]
  */
-static DWORD MIDI_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
+static DWORD MIDI_mciOpen(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
 {
     DWORD              dwRet = 0;
-    DWORD              dwDeviceID;
-    WINE_MCIMIDI*      wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID);
 
-    TRACE("(%04x, %08X, %p)\n", wDevID, dwFlags, lpParms);
+    TRACE("(%d, %08X, %p)\n", wmm->wDevID, dwFlags, lpParms);
 
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
-    if (wmm == NULL)           return MCIERR_INVALID_DEVICE_ID;
     if (dwFlags & MCI_OPEN_SHAREABLE)
        return MCIERR_HARDWARE;
 
@@ -729,9 +742,8 @@ static DWORD MIDI_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
     wmm->hMidi = 0;
     wmm->wPort = MIDI_MAPPER;
     wmm->lpstrElementName = NULL;
-    dwDeviceID = lpParms->wDeviceID;
 
-    TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, dwDeviceID);
+    TRACE("wDevID=%d (lpParams->wDeviceID=%d)\n", wmm->wDevID, lpParms->wDeviceID);
     /* lpParms->wDeviceID = wDevID;*/
 
     if (dwFlags & MCI_OPEN_ELEMENT) {
@@ -754,7 +766,6 @@ static DWORD MIDI_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
     wmm->lpstrCopyright = NULL;
     wmm->lpstrName = NULL;
 
-    wmm->wNotifyDeviceID = dwDeviceID;
     wmm->dwStatus = MCI_MODE_NOT_READY;        /* while loading file contents */
     /* spec says it should be the default format from the MIDI file... */
     wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
@@ -799,11 +810,8 @@ static DWORD MIDI_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
     } else {
        wmm->dwPositionMS = 0;
        wmm->dwStatus = MCI_MODE_STOP;
-       if (dwFlags & MCI_NOTIFY) {
-           TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-           mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                           wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-        }
+       if (dwFlags & MCI_NOTIFY)
+           MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     }
     return dwRet;
 }
@@ -811,20 +819,23 @@ static DWORD MIDI_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms)
 /**************************************************************************
  *                             MIDI_mciStop                    [internal]
  */
-static DWORD MIDI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+static DWORD MIDI_mciStop(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
+    DWORD              dwRet = 0;
 
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
-    if (wmm == NULL)   return MCIERR_INVALID_DEVICE_ID;
+    if (wmm->dwStatus != MCI_MODE_STOP) {
+       HANDLE old = InterlockedExchangePointer(&wmm->hCallback, NULL);
+       if (old) mciDriverNotify(old, wmm->wDevID, MCI_NOTIFY_ABORTED);
+    }
 
     if (wmm->dwStatus != MCI_MODE_STOP) {
        int     oldstat = wmm->dwStatus;
 
        wmm->dwStatus = MCI_MODE_NOT_READY;
        if (oldstat == MCI_MODE_PAUSE)
-           midiOutReset((HMIDIOUT)wmm->hMidi);
+           dwRet = midiOutReset((HMIDIOUT)wmm->hMidi);
 
        while (wmm->dwStatus != MCI_MODE_STOP)
            Sleep(10);
@@ -833,29 +844,22 @@ static DWORD MIDI_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParm
     /* sanity reset */
     wmm->dwStatus = MCI_MODE_STOP;
 
-    TRACE("wmm->dwStatus=%d\n", wmm->dwStatus);
-
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-    }
-    return 0;
+    if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
+    return dwRet;
 }
 
 /**************************************************************************
  *                             MIDI_mciClose                   [internal]
  */
-static DWORD MIDI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+static DWORD MIDI_mciClose(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
-
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
-    if (wmm == NULL)   return MCIERR_INVALID_DEVICE_ID;
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (wmm->dwStatus != MCI_MODE_STOP) {
-       MIDI_mciStop(wDevID, MCI_WAIT, lpParms);
+       /* mciStop handles MCI_NOTIFY_ABORTED */
+       MIDI_mciStop(wmm, MCI_WAIT, lpParms);
     }
 
     wmm->nUseCount--;
@@ -874,10 +878,8 @@ static DWORD MIDI_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpPar
        return MCIERR_INTERNAL;
     }
 
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
+    if ((dwFlags & MCI_NOTIFY) && lpParms) {
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     }
     return 0;
 }
@@ -909,18 +911,16 @@ static MCI_MIDITRACK*     MIDI_mciFindNextEvent(WINE_MCIMIDI* wmm, LPDWORD hiPulse)
 /**************************************************************************
  *                             MIDI_mciPlay                    [internal]
  */
-static DWORD MIDI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+static DWORD MIDI_mciPlay(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
 {
     DWORD              dwStartMS, dwEndMS;
     DWORD              dwRet = 0;
     WORD               doPlay, nt;
     MCI_MIDITRACK*     mmt;
     DWORD              hiPulse;
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
-
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    HANDLE             oldcb;
 
-    if (wmm == NULL)   return MCIERR_INVALID_DEVICE_ID;
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (wmm->hFile == 0) {
        WARN("Can't play: no file %s!\n", debugstr_w(wmm->lpstrElementName));
@@ -930,15 +930,14 @@ static DWORD MIDI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
     if (wmm->dwStatus != MCI_MODE_STOP) {
        if (wmm->dwStatus == MCI_MODE_PAUSE) {
            /* FIXME: parameters (start/end) in lpParams may not be used */
-           return MIDI_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
+           return MIDI_mciResume(wmm, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
        }
        WARN("Can't play: device is not stopped !\n");
        return MCIERR_INTERNAL;
     }
 
     if (!(dwFlags & MCI_WAIT)) {
-       /** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */
-       return MCI_SendCommandAsync(wmm->wNotifyDeviceID, MCI_PLAY, dwFlags, (DWORD_PTR)lpParms, sizeof(LPMCI_PLAY_PARMS));
+       return MCI_SendCommandAsync(wmm->wDevID, MCI_PLAY, dwFlags, (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
     }
 
     if (lpParms && (dwFlags & MCI_FROM)) {
@@ -955,6 +954,11 @@ static DWORD MIDI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
 
     TRACE("Playing from %u to %u\n", dwStartMS, dwEndMS);
 
+    oldcb = InterlockedExchangePointer(&wmm->hCallback,
+       (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
+    if (oldcb) mciDriverNotify(oldcb, wmm->wDevID, MCI_NOTIFY_ABORTED);
+    oldcb = NULL;
+
     /* init tracks */
     for (nt = 0; nt < wmm->nTracks; nt++) {
        mmt = &wmm->tracks[nt];
@@ -970,7 +974,6 @@ static DWORD MIDI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
     }
 
     dwRet = midiOutOpen((LPHMIDIOUT)&wmm->hMidi, wmm->wPort, 0L, 0L, CALLBACK_NULL);
-    /* dwRet = midiInOpen(&wmm->hMidi, wmm->wPort, 0L, 0L, CALLBACK_NULL);*/
     if (dwRet != MMSYSERR_NOERROR) {
        return dwRet;
     }
@@ -1173,87 +1176,24 @@ static DWORD MIDI_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
     midiOutReset((HMIDIOUT)wmm->hMidi);
 
     dwRet = midiOutClose((HMIDIOUT)wmm->hMidi);
-    /* to restart playing at beginning when it's over */
-    wmm->dwPositionMS = 0;
 
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-    }
+    if (dwFlags & MCI_NOTIFY)
+       oldcb = InterlockedExchangePointer(&wmm->hCallback, NULL);
 
     wmm->dwStatus = MCI_MODE_STOP;
-    return dwRet;
-}
-
-/**************************************************************************
- *                             MIDI_mciRecord                  [internal]
- */
-static DWORD MIDI_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
-{
-    int                        start, end;
-    MIDIHDR            midiHdr;
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
-
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
-
-    if (wmm == 0)      return MCIERR_INVALID_DEVICE_ID;
 
-    if (wmm->hFile == 0) {
-       WARN("Can't find file=%s!\n", debugstr_w(wmm->lpstrElementName));
-       return MCIERR_FILE_NOT_FOUND;
-    }
-    start = 1;                 end = 99999;
-    if (lpParms && (dwFlags & MCI_FROM)) {
-       start = lpParms->dwFrom;
-       TRACE("MCI_FROM=%d\n", start);
-    }
-    if (lpParms && (dwFlags & MCI_TO)) {
-       end = lpParms->dwTo;
-       TRACE("MCI_TO=%d\n", end);
-    }
-    midiHdr.lpData = HeapAlloc(GetProcessHeap(), 0, 1200);
-    if (!midiHdr.lpData)
-       return MCIERR_OUT_OF_MEMORY;
-    midiHdr.dwBufferLength = 1024;
-    midiHdr.dwUser = 0L;
-    midiHdr.dwFlags = 0L;
-    midiInPrepareHeader((HMIDIIN)wmm->hMidi, &midiHdr, sizeof(MIDIHDR));
-    TRACE("After MIDM_PREPARE\n");
-    wmm->dwStatus = MCI_MODE_RECORD;
-    /* FIXME: there is no buffer added */
-    while (wmm->dwStatus != MCI_MODE_STOP) {
-       TRACE("wmm->dwStatus=%p %d\n",
-             &wmm->dwStatus, wmm->dwStatus);
-       midiHdr.dwBytesRecorded = 0;
-       midiInStart((HMIDIIN)wmm->hMidi);
-       TRACE("midiInStart => dwBytesRecorded=%u\n", midiHdr.dwBytesRecorded);
-       if (midiHdr.dwBytesRecorded == 0) break;
-    }
-    TRACE("Before MIDM_UNPREPARE\n");
-    midiInUnprepareHeader((HMIDIIN)wmm->hMidi, &midiHdr, sizeof(MIDIHDR));
-    TRACE("After MIDM_UNPREPARE\n");
-    HeapFree(GetProcessHeap(), 0, midiHdr.lpData);
-    midiHdr.lpData = NULL;
-    wmm->dwStatus = MCI_MODE_STOP;
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-    }
-    return 0;
+    /* Let the potentially asynchronous commands support FAILURE notification. */
+    if (oldcb) mciDriverNotify(oldcb, wmm->wDevID,
+       dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
+    return dwRet;
 }
 
 /**************************************************************************
  *                             MIDI_mciPause                   [internal]
  */
-static DWORD MIDI_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+static DWORD MIDI_mciPause(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
-
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
-
-    if (wmm == NULL)   return MCIERR_INVALID_DEVICE_ID;
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (wmm->dwStatus == MCI_MODE_PLAY) {
        /* stop all notes */
@@ -1262,49 +1202,35 @@ static DWORD MIDI_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpPar
            midiOutShortMsg((HMIDIOUT)(wmm->hMidi), 0x78B0 | chn);
        wmm->dwStatus = MCI_MODE_PAUSE;
     }
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-    }
-
+    if ((dwFlags & MCI_NOTIFY) && lpParms)
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     return 0;
 }
 
 /**************************************************************************
  *                             MIDI_mciResume                  [internal]
  */
-static DWORD MIDI_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
+static DWORD MIDI_mciResume(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
-
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
-
-    if (wmm == NULL)   return MCIERR_INVALID_DEVICE_ID;
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (wmm->dwStatus == MCI_MODE_PAUSE) {
        wmm->wStartedPlaying = FALSE;
        wmm->dwStatus = MCI_MODE_PLAY;
     }
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-    }
+    if ((dwFlags & MCI_NOTIFY) && lpParms)
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     return 0;
 }
 
 /**************************************************************************
  *                             MIDI_mciSet                     [internal]
  */
-static DWORD MIDI_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SEQ_SET_PARMS lpParms)
+static DWORD MIDI_mciSet(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_SEQ_SET_PARMS lpParms)
 {
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
-
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
-    if (wmm == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
     if (dwFlags & MCI_SET_TIME_FORMAT) {
        switch (lpParms->dwTimeFormat) {
@@ -1377,21 +1303,21 @@ static DWORD MIDI_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SEQ_SET_PARMS lpParms
     }
     if (dwFlags & MCI_SEQ_SET_TEMPO)
        TRACE("MCI_SEQ_SET_TEMPO !\n");
+    if (dwFlags & MCI_NOTIFY)
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     return 0;
 }
 
 /**************************************************************************
  *                             MIDI_mciStatus                  [internal]
  */
-static DWORD MIDI_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
+static DWORD MIDI_mciStatus(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
 {
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
     DWORD              ret = 0;
 
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
-    if (wmm == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
     if (dwFlags & MCI_STATUS_ITEM) {
        switch (lpParms->dwItem) {
@@ -1493,27 +1419,22 @@ static DWORD MIDI_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpPar
        WARN("No Status-Item!\n");
        return MCIERR_UNRECOGNIZED_COMMAND;
     }
-    if (dwFlags & MCI_NOTIFY) {
-       TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-    }
+    if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     return ret;
 }
 
 /**************************************************************************
  *                             MIDI_mciGetDevCaps              [internal]
  */
-static DWORD MIDI_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
+static DWORD MIDI_mciGetDevCaps(WINE_MCIMIDI* wmm, DWORD dwFlags,
                                LPMCI_GETDEVCAPS_PARMS lpParms)
 {
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
     DWORD              ret;
 
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
-    if (wmm == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
        switch (lpParms->dwItem) {
@@ -1554,7 +1475,7 @@ static DWORD MIDI_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
            break;
        case MCI_GETDEVCAPS_CAN_RECORD:
            TRACE("MCI_GETDEVCAPS_CAN_RECORD !\n");
-           lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
+           lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
            ret = MCI_RESOURCE_RETURNED;
            break;
        case MCI_GETDEVCAPS_CAN_SAVE:
@@ -1570,24 +1491,24 @@ static DWORD MIDI_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
        WARN("No GetDevCaps-Item !\n");
        return MCIERR_UNRECOGNIZED_COMMAND;
     }
+    if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     return ret;
 }
 
 /**************************************************************************
  *                             MIDI_mciInfo                    [internal]
  */
-static DWORD MIDI_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
+static DWORD MIDI_mciInfo(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
 {
     LPCWSTR            str = 0;
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
     DWORD              ret = 0;
     static const WCHAR wszMidiSeq[] = {'W','i','n','e','\'','s',' ','M','I','D','I',' ','s','e','q','u','e','n','c','e','r',0};
 
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (lpParms == NULL || lpParms->lpstrReturn == NULL)
        return MCIERR_NULL_PARAMETER_BLOCK;
-    if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID;
 
     TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
 
@@ -1600,35 +1521,32 @@ static DWORD MIDI_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
        WARN("Don't know this info command (%u)\n", dwFlags);
        return MCIERR_UNRECOGNIZED_COMMAND;
     }
-    if (str) {
-       if (lpParms->dwRetSize <= strlenW(str)) {
-           lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
-           ret = MCIERR_PARAM_OVERFLOW;
-       } else {
-           strcpyW(lpParms->lpstrReturn, str);
-       }
-    } else {
-       *lpParms->lpstrReturn = 0;
+    if (!ret) {
+       if (lpParms->dwRetSize) {
+           WCHAR zero = 0;
+           /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
+            *        to the number of characters written, excluding \0. */
+           lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
+       } else ret = MCIERR_PARAM_OVERFLOW;
     }
+    if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
+       MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     return ret;
 }
 
 /**************************************************************************
  *                             MIDI_mciSeek                    [internal]
  */
-static DWORD MIDI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
+static DWORD MIDI_mciSeek(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
 {
     DWORD              ret = 0;
-    WINE_MCIMIDI*      wmm = MIDI_mciGetOpenDev(wDevID);
 
-    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms);
 
     if (lpParms == NULL) {
        ret = MCIERR_NULL_PARAMETER_BLOCK;
-    } else if (wmm == NULL) {
-       ret = MCIERR_INVALID_DEVICE_ID;
     } else {
-       MIDI_mciStop(wDevID, MCI_WAIT, 0);
+       MIDI_mciStop(wmm, MCI_WAIT, 0);
 
        if (dwFlags & MCI_SEEK_TO_START) {
            wmm->dwPositionMS = 0;
@@ -1643,11 +1561,8 @@ static DWORD MIDI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
 
        TRACE("Seeking to position=%u ms\n", wmm->dwPositionMS);
 
-       if (dwFlags & MCI_NOTIFY) {
-           TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
-           mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                           wmm->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
-       }
+       if (dwFlags & MCI_NOTIFY)
+           MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL);
     }
     return ret;
 }
@@ -1662,6 +1577,7 @@ static DWORD MIDI_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
 LRESULT CALLBACK MCIMIDI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
                                     LPARAM dwParam1, LPARAM dwParam2)
 {
+    WINE_MCIMIDI*      wmm;
     switch (wMsg) {
     case DRV_LOAD:             return 1;
     case DRV_FREE:             return 1;
@@ -1674,51 +1590,32 @@ LRESULT CALLBACK MCIMIDI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
     case DRV_OPEN:             return MIDI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
     case DRV_CLOSE:            return MIDI_drvClose(dwDevID);
     }
+    if ((wMsg < DRV_MCI_FIRST) || (wMsg > DRV_MCI_LAST)) {
+       TRACE("Sending msg %04x to default driver proc\n", wMsg);
+       return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
 
-    if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
+    wmm = MIDI_mciGetOpenDev(dwDevID, wMsg);
+    if (wmm == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
     switch (wMsg) {
-    case MCI_OPEN_DRIVER:      return MIDI_mciOpen      (dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)     dwParam2);
-    case MCI_CLOSE_DRIVER:     return MIDI_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
-    case MCI_PLAY:             return MIDI_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)      dwParam2);
-    case MCI_RECORD:           return MIDI_mciRecord    (dwDevID, dwParam1, (LPMCI_RECORD_PARMS)    dwParam2);
-    case MCI_STOP:             return MIDI_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
-    case MCI_SET:              return MIDI_mciSet       (dwDevID, dwParam1, (LPMCI_SEQ_SET_PARMS)       dwParam2);
-    case MCI_PAUSE:            return MIDI_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
-    case MCI_RESUME:           return MIDI_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
-    case MCI_STATUS:           return MIDI_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)    dwParam2);
-    case MCI_GETDEVCAPS:       return MIDI_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
-    case MCI_INFO:             return MIDI_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMSW)     dwParam2);
-    case MCI_SEEK:             return MIDI_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)      dwParam2);
-    /* commands that should be supported */
-    case MCI_LOAD:
-    case MCI_SAVE:
-    case MCI_FREEZE:
-    case MCI_PUT:
-    case MCI_REALIZE:
-    case MCI_UNFREEZE:
-    case MCI_UPDATE:
-    case MCI_WHERE:
-    case MCI_STEP:
-    case MCI_SPIN:
-    case MCI_ESCAPE:
-    case MCI_COPY:
-    case MCI_CUT:
-    case MCI_DELETE:
-    case MCI_PASTE:
-       WARN("Unsupported command [%u]\n", wMsg);
-       break;
-    /* commands that should report an error */
-    case MCI_WINDOW:
-       TRACE("Unsupported command [%u]\n", wMsg);
-       break;
+    case MCI_OPEN_DRIVER:      return MIDI_mciOpen      (wmm, dwParam1, (LPMCI_OPEN_PARMSW)     dwParam2);
+    case MCI_CLOSE_DRIVER:     return MIDI_mciClose     (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
+    case MCI_PLAY:             return MIDI_mciPlay      (wmm, dwParam1, (LPMCI_PLAY_PARMS)      dwParam2);
+    case MCI_STOP:             return MIDI_mciStop      (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
+    case MCI_SET:              return MIDI_mciSet       (wmm, dwParam1, (LPMCI_SEQ_SET_PARMS)   dwParam2);
+    case MCI_PAUSE:            return MIDI_mciPause     (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
+    case MCI_RESUME:           return MIDI_mciResume    (wmm, dwParam1, (LPMCI_GENERIC_PARMS)   dwParam2);
+    case MCI_STATUS:           return MIDI_mciStatus    (wmm, dwParam1, (LPMCI_STATUS_PARMS)    dwParam2);
+    case MCI_GETDEVCAPS:       return MIDI_mciGetDevCaps(wmm, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
+    case MCI_INFO:             return MIDI_mciInfo      (wmm, dwParam1, (LPMCI_INFO_PARMSW)     dwParam2);
+    case MCI_SEEK:             return MIDI_mciSeek      (wmm, dwParam1, (LPMCI_SEEK_PARMS)      dwParam2);
     case MCI_OPEN:
     case MCI_CLOSE:
        FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
-       break;
+       /* fall through */
     default:
-       TRACE("Sending msg [%u] to default driver proc\n", wMsg);
-       return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+       TRACE("Unsupported command [0x%x]\n", wMsg);
+        return MCIERR_UNSUPPORTED_FUNCTION; /* Win9x: MCIERR_UNRECOGNIZED_COMMAND */
     }
-    return MCIERR_UNRECOGNIZED_COMMAND;
 }