[MCIWAVE]
authorChristoph von Wittich <christoph_vw@reactos.org>
Sat, 6 Mar 2010 12:31:57 +0000 (12:31 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Sat, 6 Mar 2010 12:31:57 +0000 (12:31 +0000)
sync mciwave to wine 1.1.40

svn path=/trunk/; revision=45928

reactos/dll/win32/mciwave/mciwave.c

index c533e85..24e75ee 100644 (file)
@@ -1,9 +1,10 @@
 /*
 /*
- * Sample Wine Driver for MCI wave forms
+ * Wine Driver for MCI wave forms
  *
  * Copyright   1994 Martin Ayotte
  *             1999,2000,2005 Eric Pouech
  *              2000 Francois Jacques
  *
  * 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
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,6 +21,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <assert.h>
 #include <stdarg.h>
 
 #include "windef.h"
 #include <stdarg.h>
 
 #include "windef.h"
@@ -38,11 +40,12 @@ typedef struct {
     UINT                       wDevID;
     HANDLE                     hWave;
     int                                nUseCount;      /* Incremented for each shared open */
     UINT                       wDevID;
     HANDLE                     hWave;
     int                                nUseCount;      /* Incremented for each shared open */
-    BOOL                       fShareable;     /* TRUE if first open was shareable */
     HMMIO                      hFile;          /* mmio file handle open as Element */
     HMMIO                      hFile;          /* mmio file handle open as Element */
-    MCI_WAVE_OPEN_PARMSW       openParms;
+    MCIDEVICEID                        wNotifyDeviceID;        /* MCI device ID with a pending notification */
+    HANDLE                     hCallback;      /* Callback handle for pending notification */
+    LPWSTR                     lpFileName;     /* Name of file (if any)                     */
     WAVEFORMATEX               wfxRef;
     WAVEFORMATEX               wfxRef;
-    LPWAVEFORMATEX             lpWaveFormat;
+    LPWAVEFORMATEX             lpWaveFormat;   /* Points to wfxRef until set by OPEN or RECORD */
     BOOL                       fInput;         /* FALSE = Output, TRUE = Input */
     volatile WORD              dwStatus;       /* one from MCI_MODE_xxxx */
     DWORD                      dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
     BOOL                       fInput;         /* FALSE = Output, TRUE = Input */
     volatile WORD              dwStatus;       /* one from MCI_MODE_xxxx */
     DWORD                      dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
@@ -62,9 +65,12 @@ typedef struct {
  * ===================================================================
  * =================================================================== */
 
  * ===================================================================
  * =================================================================== */
 
+typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
+
 struct SCA {
 struct SCA {
+    async_cmd   cmd;
+    HANDLE      evt;
     UINT       wDevID;
     UINT       wDevID;
-    UINT       wMsg;
     DWORD_PTR   dwParam1;
     DWORD_PTR   dwParam2;
 };
     DWORD_PTR   dwParam1;
     DWORD_PTR   dwParam2;
 };
@@ -77,32 +83,29 @@ static DWORD CALLBACK       MCI_SCAStarter(LPVOID arg)
     struct SCA*        sca = (struct SCA*)arg;
     DWORD              ret;
 
     struct SCA*        sca = (struct SCA*)arg;
     DWORD              ret;
 
-    TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
-         sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
-    ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
-    TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
-         sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
+    TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
+         sca->wDevID, sca->dwParam1, sca->dwParam2);
+    ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
+    TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
+         sca->wDevID, sca->dwParam1, sca->dwParam2);
     HeapFree(GetProcessHeap(), 0, sca);
     HeapFree(GetProcessHeap(), 0, sca);
-    ExitThread(ret);
-    WARN("Should not happen ? what's wrong\n");
-    /* should not go after this point */
     return ret;
 }
 
 /**************************************************************************
  *                             MCI_SendCommandAsync            [internal]
  */
     return ret;
 }
 
 /**************************************************************************
  *                             MCI_SendCommandAsync            [internal]
  */
-static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
+static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
                                   DWORD_PTR dwParam2, UINT size)
 {
                                   DWORD_PTR dwParam2, UINT size)
 {
-    HANDLE handle;
+    HANDLE handles[2];
     struct SCA*        sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
 
     if (sca == 0)
        return MCIERR_OUT_OF_MEMORY;
 
     sca->wDevID   = wDevID;
     struct SCA*        sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
 
     if (sca == 0)
        return MCIERR_OUT_OF_MEMORY;
 
     sca->wDevID   = wDevID;
-    sca->wMsg     = wMsg;
+    sca->cmd      = cmd;
     sca->dwParam1 = dwParam1;
 
     if (size && dwParam2) {
     sca->dwParam1 = dwParam1;
 
     if (size && dwParam2) {
@@ -115,12 +118,22 @@ static    DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
        sca->dwParam2 = dwParam2;
     }
 
        sca->dwParam2 = dwParam2;
     }
 
-    if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
+    if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
+        (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
        WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
        WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
+        if (handles[1]) CloseHandle(handles[1]);
+        sca->evt = NULL;
        return MCI_SCAStarter(&sca);
     }
        return MCI_SCAStarter(&sca);
     }
-    SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
-    CloseHandle(handle);
+
+    SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
+    /* wait until either:
+     * - the thread has finished (handles[0], likely an error)
+     * - init phase of async command is done (handles[1])
+     */
+    WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+    CloseHandle(handles[0]);
+    CloseHandle(handles[1]);
     return 0;
 }
 
     return 0;
 }
 
@@ -189,6 +202,24 @@ static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
     return wmw;
 }
 
     return wmw;
 }
 
+/**************************************************************************
+ *                             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)
+{
+    /* We simply save one parameter by not passing the wDevID local
+     * to the command.  They are the same (via mciGetDriverData).
+     */
+    MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
+    HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
+    if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
+    mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
+}
+
 /**************************************************************************
  *                             WAVE_ConvertByteToTimeFormat    [internal]
  */
 /**************************************************************************
  *                             WAVE_ConvertByteToTimeFormat    [internal]
  */
@@ -203,8 +234,8 @@ static      DWORD   WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD
     case MCI_FORMAT_BYTES:
        ret = val;
        break;
     case MCI_FORMAT_BYTES:
        ret = val;
        break;
-    case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
-       ret = (val * 8) / (wmw->lpWaveFormat->wBitsPerSample ? wmw->lpWaveFormat->wBitsPerSample : 1);
+    case MCI_FORMAT_SAMPLES:
+       ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
        break;
     default:
        WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
        break;
     default:
        WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
@@ -223,13 +254,13 @@ static    DWORD   WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
 
     switch (wmw->dwMciTimeFormat) {
     case MCI_FORMAT_MILLISECONDS:
 
     switch (wmw->dwMciTimeFormat) {
     case MCI_FORMAT_MILLISECONDS:
-       ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
+       ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
        break;
     case MCI_FORMAT_BYTES:
        ret = val;
        break;
        break;
     case MCI_FORMAT_BYTES:
        ret = val;
        break;
-    case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
-       ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
+    case MCI_FORMAT_SAMPLES:
+       ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
        break;
     default:
        WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
        break;
     default:
        WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
@@ -245,6 +276,7 @@ static      DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
 {
     MMCKINFO   mmckInfo;
     long       r;
 {
     MMCKINFO   mmckInfo;
     long       r;
+    LPWAVEFORMATEX pwfx;
 
     mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
 
     mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
@@ -252,20 +284,28 @@ static    DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
          (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
 
     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
          (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
 
-    wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
-    if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
-    r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
-    if (r < sizeof(WAVEFORMAT))
-       return MCIERR_INVALID_FILE;
+    pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
+    if (!pwfx) return MCIERR_OUT_OF_MEMORY;
 
 
-    TRACE("wFormatTag=%04X !\n",   wmw->lpWaveFormat->wFormatTag);
-    TRACE("nChannels=%d\n",       wmw->lpWaveFormat->nChannels);
-    TRACE("nSamplesPerSec=%d\n",  wmw->lpWaveFormat->nSamplesPerSec);
-    TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
-    TRACE("nBlockAlign=%d\n",     wmw->lpWaveFormat->nBlockAlign);
-    TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
+    r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
+    if (r < sizeof(PCMWAVEFORMAT)) {
+       HeapFree(GetProcessHeap(), 0, pwfx);
+       return MCIERR_INVALID_FILE;
+    }
+    TRACE("wFormatTag=%04X !\n",   pwfx->wFormatTag);
+    TRACE("nChannels=%d\n",        pwfx->nChannels);
+    TRACE("nSamplesPerSec=%d\n",   pwfx->nSamplesPerSec);
+    TRACE("nAvgBytesPerSec=%d\n",  pwfx->nAvgBytesPerSec);
+    TRACE("nBlockAlign=%d\n",      pwfx->nBlockAlign);
+    TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
     if (r >= (long)sizeof(WAVEFORMATEX))
     if (r >= (long)sizeof(WAVEFORMATEX))
-       TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
+       TRACE("cbSize=%u !\n",     pwfx->cbSize);
+    if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
+       && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
+       HeapFree(GetProcessHeap(), 0, pwfx);
+       return MCIERR_INVALID_FILE;
+    }
+    wmw->lpWaveFormat = pwfx;
 
     mmioAscend(wmw->hFile, &mmckInfo, 0);
     wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
 
     mmioAscend(wmw->hFile, &mmckInfo, 0);
     wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
@@ -275,28 +315,26 @@ static    DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
     }
     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
          (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
     }
     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
          (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
-    TRACE("nChannels=%d nSamplesPerSec=%d\n",
-         wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
-
     return 0;
 }
 
 /**************************************************************************
     return 0;
 }
 
 /**************************************************************************
- *                     WAVE_mciDefaultFmt                       [internal]
+ *                     WAVE_mciDefaultFmt                      [internal]
+ *
+ * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
+ * until either Open File or Record.  It becomes immutable afterwards,
+ * i.e. Set wave format or channels etc. is subsequently refused.
  */
  */
-static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
+static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
 {
 {
-    wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
-    if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
-
+    wmw->lpWaveFormat = &wmw->wfxRef;
     wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
     wmw->lpWaveFormat->nChannels = 1;
     wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
     wmw->lpWaveFormat->nChannels = 1;
-    wmw->lpWaveFormat->nSamplesPerSec = 44000;
-    wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
+    wmw->lpWaveFormat->nSamplesPerSec = 11025;
+    wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
     wmw->lpWaveFormat->nBlockAlign = 1;
     wmw->lpWaveFormat->wBitsPerSample = 8;
     wmw->lpWaveFormat->nBlockAlign = 1;
     wmw->lpWaveFormat->wBitsPerSample = 8;
-
-    return 0;
+    wmw->lpWaveFormat->cbSize = 0;
 }
 
 /**************************************************************************
 }
 
 /**************************************************************************
@@ -319,24 +357,41 @@ static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
    ckWaveFormat.ckid    = mmioFOURCC('f', 'm', 't', ' ');
    ckWaveFormat.cksize  = sizeof(PCMWAVEFORMAT);
 
    ckWaveFormat.ckid    = mmioFOURCC('f', 'm', 't', ' ');
    ckWaveFormat.cksize  = sizeof(PCMWAVEFORMAT);
 
-   if (!wmw->lpWaveFormat)
-   {
-       wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
-       if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
-       *wmw->lpWaveFormat = wmw->wfxRef;
+   /* Set wave format accepts PCM only, however open an
+    * existing ADPCM file, record into it and the MCI will
+    * happily save back in that format. */
+   if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
+       if (wmw->lpWaveFormat->nBlockAlign !=
+           wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
+           WORD size = wmw->lpWaveFormat->nChannels *
+               wmw->lpWaveFormat->wBitsPerSample/8;
+           WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
+               wmw->lpWaveFormat->nBlockAlign, size);
+           wmw->lpWaveFormat->nBlockAlign = size;
+       }
+       if (wmw->lpWaveFormat->nAvgBytesPerSec !=
+           wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
+           DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
+               wmw->lpWaveFormat->nBlockAlign;
+           WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
+               wmw->lpWaveFormat->nAvgBytesPerSec, speed);
+           wmw->lpWaveFormat->nAvgBytesPerSec = speed;
+       }
+   }
+   if (wmw->lpWaveFormat == &wmw->wfxRef) {
+       LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
+       if (!pwfx) return MCIERR_OUT_OF_MEMORY;
+       /* Set wave format accepts PCM only so the size is known. */
+       assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
+       *pwfx = wmw->wfxRef;
+       wmw->lpWaveFormat = pwfx;
    }
    }
-
-   /* we can only record PCM files... there is no way in the MCI API to specify
-    * the necessary data to initialize the extra bytes of the WAVEFORMATEX
-    * structure
-    */
-   if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
-       goto err;
 
    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
        goto err;
 
 
    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
        goto err;
 
-   if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
+   if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
+       ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
        goto err;
 
    if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
        goto err;
 
    if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
@@ -353,8 +408,7 @@ static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
    return 0;
 
 err:
    return 0;
 
 err:
-   HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
-   wmw->lpWaveFormat = NULL;
+   /* mciClose takes care of wmw->lpWaveFormat. */
    return MCIERR_INVALID_FILE;
 }
 
    return MCIERR_INVALID_FILE;
 }
 
@@ -371,6 +425,7 @@ static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
 
     if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
         WARN("can't retrieve temp path!\n");
 
     if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
         WARN("can't retrieve temp path!\n");
+        *pszTmpFileName = NULL;
         return MCIERR_FILE_NOT_FOUND;
     }
 
         return MCIERR_FILE_NOT_FOUND;
     }
 
@@ -400,16 +455,16 @@ static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
     return dwRet;
 }
 
     return dwRet;
 }
 
-static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
+static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
 {
     LRESULT dwRet = MMSYSERR_NOERROR;
 {
     LRESULT dwRet = MMSYSERR_NOERROR;
-    WCHAR* fn;
+    LPWSTR fn;
 
     fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
     if (!fn) return MCIERR_OUT_OF_MEMORY;
     strcpyW(fn, filename);
 
     fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
     if (!fn) return MCIERR_OUT_OF_MEMORY;
     strcpyW(fn, filename);
-    HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
-    wmw->openParms.lpstrElementName = fn;
+    HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
+    wmw->lpFileName = fn;
 
     if (strlenW(filename) > 0) {
         /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
 
     if (strlenW(filename) > 0) {
         /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
@@ -463,7 +518,7 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
     if (dwFlags & MCI_OPEN_SHAREABLE)
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
     if (dwFlags & MCI_OPEN_SHAREABLE)
-       return MCIERR_HARDWARE;
+       return MCIERR_UNSUPPORTED_FUNCTION;
 
     if (wmw->nUseCount > 0) {
        /* The driver is already opened on this channel
 
     if (wmw->nUseCount > 0) {
        /* The driver is already opened on this channel
@@ -478,11 +533,13 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P
     wmw->hWave = 0;
     wmw->dwStatus = MCI_MODE_NOT_READY;
     wmw->hFile = 0;
     wmw->hWave = 0;
     wmw->dwStatus = MCI_MODE_NOT_READY;
     wmw->hFile = 0;
-    memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
-    /* will be set by WAVE_mciOpenFile */
-    wmw->openParms.lpstrElementName = NULL;
+    wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
+    wmw->hCallback = NULL;
+    WAVE_mciDefaultFmt(wmw);
 
     TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
 
     TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
+    /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
+    wmw->wNotifyDeviceID = wDevID;
 
     if (dwFlags & MCI_OPEN_ELEMENT) {
        if (dwFlags & MCI_OPEN_ELEMENT_ID) {
 
     if (dwFlags & MCI_OPEN_ELEMENT) {
        if (dwFlags & MCI_OPEN_ELEMENT_ID) {
@@ -494,32 +551,15 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P
             dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
        }
     }
             dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
        }
     }
-
     TRACE("hFile=%p\n", wmw->hFile);
 
     TRACE("hFile=%p\n", wmw->hFile);
 
-    if (dwRet == 0 && !wmw->lpWaveFormat)
-        dwRet = WAVE_mciDefaultFmt(wmw);
-
     if (dwRet == 0) {
     if (dwRet == 0) {
-       if (wmw->lpWaveFormat) {
-           switch (wmw->lpWaveFormat->wFormatTag) {
-           case WAVE_FORMAT_PCM:
-               if (wmw->lpWaveFormat->nAvgBytesPerSec !=
-                   wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
-                    WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
-                       wmw->lpWaveFormat->nAvgBytesPerSec,
-                       wmw->lpWaveFormat->nSamplesPerSec *
-                        wmw->lpWaveFormat->nBlockAlign);
-                   wmw->lpWaveFormat->nAvgBytesPerSec =
-                       wmw->lpWaveFormat->nSamplesPerSec *
-                       wmw->lpWaveFormat->nBlockAlign;
-               }
-               break;
-           }
-       }
        wmw->dwPosition = 0;
 
        wmw->dwStatus = MCI_MODE_STOP;
        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)
     } else {
        wmw->nUseCount--;
        if (wmw->hFile != 0)
@@ -532,46 +572,38 @@ static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_P
 /**************************************************************************
  *                               WAVE_mciCue             [internal]
  */
 /**************************************************************************
  *                               WAVE_mciCue             [internal]
  */
-static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
+static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
 {
-    /*
-      FIXME
-
-      This routine is far from complete. At the moment only a check is done on the
-      MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
-      is the default.
-
-      The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
-      are ignored
-    */
-
-    DWORD              dwRet;
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
 
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
 
-    FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
+    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
 
-    if (wmw == NULL)   return MCIERR_INVALID_DEVICE_ID;
+    /* Tests on systems without sound drivers show that Cue, like
+     * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
+     * The first Cue Notify does not immediately return the
+     * notification, as if a player or recorder thread is started.
+     * PAUSE mode is reported when successful, but this mode is
+     * different from the normal Pause, because a) Pause then returns
+     * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
+     * still accepted, returning the original notification as ABORTED.
+     * I.e. Cue allows subsequent format changes, unlike Record or
+     * Open file, closes winmm if the format changes and stops this
+     * thread.
+     * Wine creates one player or recorder thread per async. Play or
+     * Record command.  Notification behaviour suggests that MS-W*
+     * reuses a single thread to improve response times.  Having Cue
+     * start this thread early helps to improve Play/Record's initial
+     * response time.  In effect, Cue is a performance hint, which
+     * justifies our almost no-op implementation.
+     */
 
 
-    /* FIXME */
-    /* always close elements ? */
-    if (wmw->hFile != 0) {
-       mmioClose(wmw->hFile, 0);
-       wmw->hFile = 0;
-    }
+    if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
+    if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
 
 
-    dwRet = MMSYSERR_NOERROR;  /* assume success */
+    if ((dwFlags & MCI_NOTIFY) && lpParms)
+       WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
 
 
-    if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
-       dwRet = waveOutClose(wmw->hWave);
-       if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
-       wmw->fInput = TRUE;
-    } else if (wmw->fInput) {
-       dwRet = waveInClose(wmw->hWave);
-       if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
-       wmw->fInput = FALSE;
-    }
-    wmw->hWave = 0;
-    return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
+    return MMSYSERR_NOERROR;
 }
 
 /**************************************************************************
 }
 
 /**************************************************************************
@@ -586,6 +618,11 @@ static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
 
     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:
     /* wait for playback thread (if any) to exit before processing further */
     switch (wmw->dwStatus) {
     case MCI_MODE_PAUSE:
@@ -602,15 +639,11 @@ static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS
        break;
     }
 
        break;
     }
 
-    wmw->dwPosition = 0;
-
     /* sanity resets */
     wmw->dwStatus = MCI_MODE_STOP;
 
     /* 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;
 }
 
     return dwRet;
 }
@@ -628,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) {
     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);
     }
 
        dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
     }
 
@@ -640,15 +674,15 @@ static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM
        }
     }
 
        }
     }
 
-    HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
-    wmw->lpWaveFormat = NULL;
-    HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
-    wmw->openParms.lpstrElementName = NULL;
+    if (wmw->lpWaveFormat != &wmw->wfxRef)
+       HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
+    wmw->lpWaveFormat = &wmw->wfxRef;
+    HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
+    wmw->lpFileName = NULL;
 
     if ((dwFlags & MCI_NOTIFY) && lpParms) {
 
     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;
     }
 
     return 0;
@@ -678,9 +712,7 @@ static      void    CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
 }
 
 /******************************************************************
 }
 
 /******************************************************************
- *             WAVE_mciPlayWaitDone
- *
- *
+ *                     WAVE_mciPlayWaitDone            [internal]
  */
 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
 {
  */
 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
 {
@@ -698,28 +730,28 @@ static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
 /**************************************************************************
  *                             WAVE_mciPlay            [internal]
  */
 /**************************************************************************
  *                             WAVE_mciPlay            [internal]
  */
-static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
+static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
 {
 {
+    LPMCI_PLAY_PARMS    lpParms = (void*)pmt;
     DWORD              end;
     LONG               bufsize, count, left;
     DWORD              end;
     LONG               bufsize, count, left;
-    DWORD              dwRet = 0;
+    DWORD              dwRet;
     LPWAVEHDR          waveHdr = NULL;
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
     LPWAVEHDR          waveHdr = NULL;
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
+    HANDLE             oldcb;
     int                        whidx;
 
     int                        whidx;
 
-    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
 
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
 
-    wmw->fInput = FALSE;
-
     if (wmw->hFile == 0) {
     if (wmw->hFile == 0) {
-       WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
+       WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
        return MCIERR_FILE_NOT_FOUND;
     }
 
        return MCIERR_FILE_NOT_FOUND;
     }
 
-    if (wmw->dwStatus == MCI_MODE_PAUSE) {
+    if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
        /* FIXME: parameters (start/end) in lpParams may not be used */
        return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
     }
        /* FIXME: parameters (start/end) in lpParams may not be used */
        return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
     }
@@ -732,56 +764,65 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp
        return MCIERR_INTERNAL;
     }
 
        return MCIERR_INTERNAL;
     }
 
+    if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
+        if (wmw->lpWaveFormat->nBlockAlign !=
+            wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
+            WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
+                wmw->lpWaveFormat->nBlockAlign,
+                wmw->lpWaveFormat->nChannels *
+                 wmw->lpWaveFormat->wBitsPerSample/8);
+            wmw->lpWaveFormat->nBlockAlign =
+                wmw->lpWaveFormat->nChannels *
+                wmw->lpWaveFormat->wBitsPerSample/8;
+        }
+        if (wmw->lpWaveFormat->nAvgBytesPerSec !=
+            wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
+            WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
+                wmw->lpWaveFormat->nAvgBytesPerSec,
+                wmw->lpWaveFormat->nSamplesPerSec *
+                 wmw->lpWaveFormat->nBlockAlign);
+            wmw->lpWaveFormat->nAvgBytesPerSec =
+                wmw->lpWaveFormat->nSamplesPerSec *
+                wmw->lpWaveFormat->nBlockAlign;
+        }
+    }
+
+    end = wmw->ckWaveData.cksize;
+    if (lpParms && (dwFlags & MCI_TO)) {
+       DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
+       if (position > end)             return MCIERR_OUTOFRANGE;
+       end = position;
+    }
+    if (lpParms && (dwFlags & MCI_FROM)) {
+       DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
+       if (position > end)             return MCIERR_OUTOFRANGE;
+       /* Seek rounds down, so do we. */
+       position /= wmw->lpWaveFormat->nBlockAlign;
+       position *= wmw->lpWaveFormat->nBlockAlign;
+       wmw->dwPosition = position;
+    }
+    if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
+    left = end - wmw->dwPosition;
+    if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
+
+    wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
     wmw->dwStatus = MCI_MODE_PLAY;
 
     if (!(dwFlags & MCI_WAIT)) {
     wmw->dwStatus = MCI_MODE_PLAY;
 
     if (!(dwFlags & MCI_WAIT)) {
-       return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
+       return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
                                    (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
     }
 
                                    (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
     }
 
-    end = 0xFFFFFFFF;
-    if (lpParms && (dwFlags & MCI_FROM)) {
-       wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
-    }
-    if (lpParms && (dwFlags & MCI_TO)) {
-       end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
-    }
-
     TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
 
     TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
 
-    if (end <= wmw->dwPosition)
-       return TRUE;
-
+    oldcb = InterlockedExchangePointer(&wmw->hCallback,
+       (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
+    if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
+    oldcb = NULL;
 
 #define        WAVE_ALIGN_ON_BLOCK(wmw,v) \
 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
 
 
 #define        WAVE_ALIGN_ON_BLOCK(wmw,v) \
 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
 
-    wmw->dwPosition        = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
-    wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
-
-    if (dwRet == 0) {
-       if (wmw->lpWaveFormat) {
-           switch (wmw->lpWaveFormat->wFormatTag) {
-           case WAVE_FORMAT_PCM:
-               if (wmw->lpWaveFormat->nAvgBytesPerSec !=
-                   wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
-                    WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
-                       wmw->lpWaveFormat->nAvgBytesPerSec,
-                       wmw->lpWaveFormat->nSamplesPerSec *
-                        wmw->lpWaveFormat->nBlockAlign);
-                   wmw->lpWaveFormat->nAvgBytesPerSec =
-                       wmw->lpWaveFormat->nSamplesPerSec *
-                       wmw->lpWaveFormat->nBlockAlign;
-               }
-               break;
-           }
-       }
-    } else {
-       TRACE("can't retrieve wave format %d\n", dwRet);
-       goto cleanUp;
-    }
-
-
     /* go back to beginning of chunk plus the requested position */
     /* FIXME: I'm not sure this is correct, notably because some data linked to
      * the decompression state machine will not be correctly initialized.
     /* go back to beginning of chunk plus the requested position */
     /* FIXME: I'm not sure this is correct, notably because some data linked to
      * the decompression state machine will not be correctly initialized.
@@ -790,9 +831,6 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp
      */
     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
 
      */
     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + 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((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
                        (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
     /* FIXME: how to choose between several output channels ? here mapper is forced */
     dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
                        (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
@@ -821,11 +859,11 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp
     }
 
     whidx = 0;
     }
 
     whidx = 0;
-    left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
     wmw->dwEventCount = 1L; /* for first buffer */
 
     TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
     wmw->dwEventCount = 1L; /* for first buffer */
 
     TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
+    if (hEvent) SetEvent(hEvent);
 
     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
     while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
 
     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
     while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
@@ -838,14 +876,18 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp
         */
        waveHdr[whidx].dwBufferLength = count;
        waveHdr[whidx].dwFlags &= ~WHDR_DONE;
         */
        waveHdr[whidx].dwBufferLength = count;
        waveHdr[whidx].dwFlags &= ~WHDR_DONE;
-       TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
-             &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
-             waveHdr[whidx].dwBytesRecorded);
+       TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
+             &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
        dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
        dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
+       if (dwRet) {
+           ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
+           dwRet = MCIERR_HARDWARE;
+           break;
+       }
        left -= count;
        wmw->dwPosition += count;
        TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
        left -= count;
        wmw->dwPosition += count;
        TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
-
+       /* InterlockedDecrement if and only if waveOutWrite is successful */
        WAVE_mciPlayWaitDone(wmw);
        whidx ^= 1;
     }
        WAVE_mciPlayWaitDone(wmw);
        whidx ^= 1;
     }
@@ -858,9 +900,10 @@ static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lp
     waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
     waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
 
     waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
     waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
 
-    dwRet = 0;
-
 cleanUp:
 cleanUp:
+    if (dwFlags & MCI_NOTIFY)
+       oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
+
     HeapFree(GetProcessHeap(), 0, waveHdr);
 
     if (wmw->hWave) {
     HeapFree(GetProcessHeap(), 0, waveHdr);
 
     if (wmw->hWave) {
@@ -869,19 +912,17 @@ cleanUp:
     }
     CloseHandle(wmw->hEvent);
 
     }
     CloseHandle(wmw->hEvent);
 
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmw->openParms.wDeviceID,
-                       dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
-    }
-
     wmw->dwStatus = MCI_MODE_STOP;
 
     wmw->dwStatus = MCI_MODE_STOP;
 
+    /* Let the potentically asynchronous commands support FAILURE notification. */
+    if (oldcb) mciDriverNotify(oldcb, wDevID,
+       dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
+
     return dwRet;
 }
 
 /**************************************************************************
     return dwRet;
 }
 
 /**************************************************************************
- *                             WAVE_mciPlayCallback            [internal]
+ *                             WAVE_mciRecordCallback          [internal]
  */
 static void    CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
                                                 DWORD_PTR dwInstance,
  */
 static void    CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
                                                 DWORD_PTR dwInstance,
@@ -924,8 +965,7 @@ static      void    CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
 }
 
 /******************************************************************
 }
 
 /******************************************************************
- *             bWAVE_mciRecordWaitDone
- *
+ *                     WAVE_mciRecordWaitDone          [internal]
  */
 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
 {
  */
 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
 {
@@ -943,41 +983,39 @@ static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
 /**************************************************************************
  *                             WAVE_mciRecord                  [internal]
  */
 /**************************************************************************
  *                             WAVE_mciRecord                  [internal]
  */
-static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
+static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
 {
 {
+    LPMCI_RECORD_PARMS  lpParms = (void*)pmt;
     DWORD              end;
     DWORD              dwRet = MMSYSERR_NOERROR;
     LONG               bufsize;
     LPWAVEHDR          waveHdr = NULL;
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
     DWORD              end;
     DWORD              dwRet = MMSYSERR_NOERROR;
     LONG               bufsize;
     LPWAVEHDR          waveHdr = NULL;
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
+    HANDLE             oldcb;
 
 
-    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
+    TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
 
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
 
-    /* FIXME : since there is no way to determine in which mode the device is
-     * open (recording/playback) automatically switch from a mode to another
-     */
-    wmw->fInput = TRUE;
-
-    if (wmw->dwStatus == MCI_MODE_PAUSE) {
+    if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
         /* FIXME: parameters (start/end) in lpParams may not be used */
         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
     }
 
     /** This function will be called again by a thread when async is used.
         /* FIXME: parameters (start/end) in lpParams may not be used */
         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
     }
 
     /** This function will be called again by a thread when async is used.
-     * We have to set MCI_MODE_PLAY before we do this so that the app can spin
+     * We have to set MCI_MODE_RECORD before we do this so that the app can spin
      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
      */
     if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
        return MCIERR_INTERNAL;
     }
 
      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
      */
     if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
        return MCIERR_INTERNAL;
     }
 
+    wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
     wmw->dwStatus = MCI_MODE_RECORD;
 
     if (!(dwFlags & MCI_WAIT)) {
     wmw->dwStatus = MCI_MODE_RECORD;
 
     if (!(dwFlags & MCI_WAIT)) {
-       return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
+       return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
                                    (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
     }
 
                                    (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
     }
 
@@ -985,34 +1023,37 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARM
      * we don't modify the wave part of an existing file (ie. we always erase an
      * existing content, we don't overwrite)
      */
      * we don't modify the wave part of an existing file (ie. we always erase an
      * existing content, we don't overwrite)
      */
-    HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
-    dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
+    HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
+    dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
     if (dwRet != 0) return dwRet;
 
     if (dwRet != 0) return dwRet;
 
-    /* new RIFF file */
+    /* new RIFF file, lpWaveFormat now valid */
     dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
     dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
-    if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
-
-    end = 0xFFFFFFFF;
-    if (lpParms && (dwFlags & MCI_FROM)) {
-       wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
-    }
+    if (dwRet != 0) return dwRet;
 
     if (lpParms && (dwFlags & MCI_TO)) {
        end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
 
     if (lpParms && (dwFlags & MCI_TO)) {
        end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
+    } else end = 0xFFFFFFFF;
+    if (lpParms && (dwFlags & MCI_FROM)) {
+       DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
+       if (wmw->ckWaveData.cksize < position)  return MCIERR_OUTOFRANGE;
+       /* Seek rounds down, so do we. */
+       position /= wmw->lpWaveFormat->nBlockAlign;
+       position *= wmw->lpWaveFormat->nBlockAlign;
+       wmw->dwPosition = position;
     }
     }
+    if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
 
     TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
 
 
     TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
 
-    if (end <= wmw->dwPosition)
-    {
-       return TRUE;
-    }
+    oldcb = InterlockedExchangePointer(&wmw->hCallback,
+       (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
+    if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
+    oldcb = NULL;
 
 #define        WAVE_ALIGN_ON_BLOCK(wmw,v) \
 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
 
 
 #define        WAVE_ALIGN_ON_BLOCK(wmw,v) \
 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
 
-    wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
     wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
 
     /* Go back to the beginning of the chunk plus the requested position */
     wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
 
     /* Go back to the beginning of the chunk plus the requested position */
@@ -1067,10 +1108,16 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARM
 
     dwRet = waveInStart(wmw->hWave);
 
 
     dwRet = waveInStart(wmw->hWave);
 
+    if (hEvent) SetEvent(hEvent);
+
     while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
        WAVE_mciRecordWaitDone(wmw);
     }
     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;
 
     /* needed so that the callback above won't add again the buffers returned by the reset */
     wmw->dwStatus = MCI_MODE_STOP;
 
@@ -1082,6 +1129,9 @@ static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARM
     dwRet = 0;
 
 cleanUp:
     dwRet = 0;
 
 cleanUp:
+    if (dwFlags & MCI_NOTIFY)
+       oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
+
     HeapFree(GetProcessHeap(), 0, waveHdr);
 
     if (wmw->hWave) {
     HeapFree(GetProcessHeap(), 0, waveHdr);
 
     if (wmw->hWave) {
@@ -1090,14 +1140,11 @@ cleanUp:
     }
     CloseHandle(wmw->hEvent);
 
     }
     CloseHandle(wmw->hEvent);
 
-    if (lpParms && (dwFlags & MCI_NOTIFY)) {
-       mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                       wmw->openParms.wDeviceID,
-                       dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
-    }
-
     wmw->dwStatus = MCI_MODE_STOP;
 
     wmw->dwStatus = MCI_MODE_STOP;
 
+    if (oldcb) mciDriverNotify(oldcb, wDevID,
+       dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
+
     return dwRet;
 
 }
     return dwRet;
 
 }
@@ -1112,17 +1159,34 @@ static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM
 
     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
 
     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
-    if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
-    if (wmw->dwStatus == MCI_MODE_PLAY) {
-       wmw->dwStatus = MCI_MODE_PAUSE;
+    switch (wmw->dwStatus) {
+    case MCI_MODE_PLAY:
+       dwRet = waveOutPause(wmw->hWave);
+       if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
+       else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
+           ERR("waveOutPause error %d\n",dwRet);
+           dwRet = MCIERR_INTERNAL;
+       }
+       break;
+    case MCI_MODE_RECORD:
+       dwRet = waveInStop(wmw->hWave);
+       if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
+       else {
+           ERR("waveInStop error %d\n",dwRet);
+           dwRet = MCIERR_INTERNAL;
+       }
+       break;
+    case MCI_MODE_PAUSE:
+       dwRet = MMSYSERR_NOERROR;
+       break;
+    default:
+       dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
     }
     }
-
-    if (wmw->fInput)   dwRet = waveInStop(wmw->hWave);
-    else               dwRet = waveOutPause(wmw->hWave);
-
-    return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
+    if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
+       WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
+    return dwRet;
 }
 
 /**************************************************************************
 }
 
 /**************************************************************************
@@ -1131,19 +1195,41 @@ static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARM
 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
 {
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
-    DWORD              dwRet = 0;
+    DWORD              dwRet;
 
     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
 
     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
     if (wmw == NULL)           return MCIERR_INVALID_DEVICE_ID;
 
-    if (wmw->dwStatus == MCI_MODE_PAUSE) {
-       wmw->dwStatus = MCI_MODE_PLAY;
+    switch (wmw->dwStatus) {
+    case MCI_MODE_PAUSE:
+       /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
+       if (wmw->fInput) {
+           dwRet = waveInStart(wmw->hWave);
+           if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
+           else {
+               ERR("waveInStart error %d\n",dwRet);
+               dwRet = MCIERR_INTERNAL;
+           }
+       } else {
+           dwRet = waveOutRestart(wmw->hWave);
+           if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
+           else {
+               ERR("waveOutRestart error %d\n",dwRet);
+               dwRet = MCIERR_INTERNAL;
+           }
+       }
+       break;
+    case MCI_MODE_PLAY:
+    case MCI_MODE_RECORD:
+       dwRet = MMSYSERR_NOERROR;
+       break;
+    default:
+       dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
     }
     }
-
-    if (wmw->fInput)   dwRet = waveInStart(wmw->hWave);
-    else               dwRet = waveOutRestart(wmw->hWave);
-    return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
+    if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
+       WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
+    return dwRet;
 }
 
 /**************************************************************************
 }
 
 /**************************************************************************
@@ -1151,37 +1237,43 @@ static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PAR
  */
 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
 {
  */
 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
 {
-    DWORD              ret = 0;
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
     WINE_MCIWAVE*      wmw = WAVE_mciGetOpenDev(wDevID);
+    DWORD              position, dwRet;
 
     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
 
     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);
+    position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
+    if (!position)             return MCIERR_MISSING_PARAMETER;
+    if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
 
 
-       if (dwFlags & MCI_NOTIFY) {
-           mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
-                           wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
-       }
+    /* Stop sends MCI_NOTIFY_ABORTED when needed */
+    dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
+    if (dwRet != MMSYSERR_NOERROR) return dwRet;
+
+    if (dwFlags & MCI_TO) {
+       position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
+       if (position > wmw->ckWaveData.cksize)
+           return MCIERR_OUTOFRANGE;
+    } else if (dwFlags & MCI_SEEK_TO_START) {
+       position = 0;
+    } else {
+       position = wmw->ckWaveData.cksize;
     }
     }
-    return ret;
+    /* Seek rounds down, unless at end */
+    if (position != wmw->ckWaveData.cksize) {
+       position /= wmw->lpWaveFormat->nBlockAlign;
+       position *= wmw->lpWaveFormat->nBlockAlign;
+    }
+    wmw->dwPosition = position;
+    TRACE("Seeking to position=%u bytes\n", position);
+
+    if (dwFlags & MCI_NOTIFY)
+       WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
+
+    return MMSYSERR_NOERROR;
 }
 
 /**************************************************************************
 }
 
 /**************************************************************************
@@ -1253,30 +1345,40 @@ static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpPa
        TRACE("MCI_WAVE_SET_ANYINPUT !\n");
     if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
        TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
        TRACE("MCI_WAVE_SET_ANYINPUT !\n");
     if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
        TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
+    /* Set wave format parameters is refused after Open or Record.*/
+    if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
+       TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag);
+       if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
+       if (((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag != WAVE_FORMAT_PCM)
+           return MCIERR_OUTOFRANGE;
+    }
     if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
     if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
+       if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
        wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
        TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
     }
     if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
        wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
        TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
     }
     if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
+       if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
        wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
        TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
     }
     if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
        wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
        TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
     }
     if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
+       if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
        wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
        TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
     }
     if (dwFlags & MCI_WAVE_SET_CHANNELS) {
        wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
        TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
     }
     if (dwFlags & MCI_WAVE_SET_CHANNELS) {
+       if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
        wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
        TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
     }
        wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
        TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
     }
-    if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
-       wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
-       TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
-    }
     if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
     if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
+       if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
        wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
        TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
     }
        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;
 }
 
     return 0;
 }
 
@@ -1287,7 +1389,6 @@ 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;
 {
     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;
 
     TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
     if (lpParms == NULL)       return MCIERR_NULL_PARAMETER_BLOCK;
@@ -1318,16 +1419,14 @@ static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW l
     DeleteFileW (lpParms->lpfilename);
     SetLastError(tmpRet);
 
     DeleteFileW (lpParms->lpfilename);
     SetLastError(tmpRet);
 
-    if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
+    /* FIXME: Open file.wav; Save; must not rename the original file.
+     * Nor must Save a.wav; Save b.wav rename a. */
+    if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
        ret = MMSYSERR_NOERROR;
     }
 
        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);
 
     if (ret == MMSYSERR_NOERROR)
         ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
@@ -1413,71 +1512,47 @@ static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARM
                    lpParms->dwReturn = id;
                } else {
                    lpParms->dwReturn = 0;
                    lpParms->dwReturn = id;
                } else {
                    lpParms->dwReturn = 0;
-                   ret = MCIERR_WAVE_INPUTUNSPECIFIED;
+                   ret = MCIERR_WAVE_OUTPUTUNSPECIFIED;
                }
            }
            break;
                }
            }
            break;
+       /* It is always ok to query wave format parameters,
+        * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
        case MCI_WAVE_STATUS_AVGBYTESPERSEC:
        case MCI_WAVE_STATUS_AVGBYTESPERSEC:
-           if (!wmw->hFile) {
-               lpParms->dwReturn = 0;
-               return MCIERR_UNSUPPORTED_FUNCTION;
-           }
            lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
            lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
-            TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
+           TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
            break;
        case MCI_WAVE_STATUS_BITSPERSAMPLE:
            break;
        case MCI_WAVE_STATUS_BITSPERSAMPLE:
-           if (!wmw->hFile) {
-               lpParms->dwReturn = 0;
-               return MCIERR_UNSUPPORTED_FUNCTION;
-           }
            lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
            lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
-            TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
+           TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
            break;
        case MCI_WAVE_STATUS_BLOCKALIGN:
            break;
        case MCI_WAVE_STATUS_BLOCKALIGN:
-           if (!wmw->hFile) {
-               lpParms->dwReturn = 0;
-               return MCIERR_UNSUPPORTED_FUNCTION;
-           }
            lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
            lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
-            TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
+           TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
            break;
        case MCI_WAVE_STATUS_CHANNELS:
            break;
        case MCI_WAVE_STATUS_CHANNELS:
-           if (!wmw->hFile) {
-               lpParms->dwReturn = 0;
-               return MCIERR_UNSUPPORTED_FUNCTION;
-           }
            lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
            lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
-            TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
+           TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
            break;
        case MCI_WAVE_STATUS_FORMATTAG:
            break;
        case MCI_WAVE_STATUS_FORMATTAG:
-           if (!wmw->hFile) {
-               lpParms->dwReturn = 0;
-               return MCIERR_UNSUPPORTED_FUNCTION;
-           }
            lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
            lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
-            TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
+           TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
+           break;
+       case MCI_WAVE_STATUS_SAMPLESPERSEC:
+           lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
+           TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
            break;
        case MCI_WAVE_STATUS_LEVEL:
            TRACE("MCI_WAVE_STATUS_LEVEL !\n");
            lpParms->dwReturn = 0xAAAA5555;
            break;
            break;
        case MCI_WAVE_STATUS_LEVEL:
            TRACE("MCI_WAVE_STATUS_LEVEL !\n");
            lpParms->dwReturn = 0xAAAA5555;
            break;
-       case MCI_WAVE_STATUS_SAMPLESPERSEC:
-           if (!wmw->hFile) {
-               lpParms->dwReturn = 0;
-               return MCIERR_UNSUPPORTED_FUNCTION;
-           }
-           lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
-            TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
-           break;
        default:
             WARN("unknown command %08X !\n", lpParms->dwItem);
            return MCIERR_UNRECOGNIZED_COMMAND;
        }
     }
        default:
             WARN("unknown command %08X !\n", lpParms->dwItem);
            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;
 }
 
     return ret;
 }
 
@@ -1534,10 +1609,10 @@ static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
            ret = MCI_RESOURCE_RETURNED;
            break;
        case MCI_WAVE_GETDEVCAPS_INPUTS:
            ret = MCI_RESOURCE_RETURNED;
            break;
        case MCI_WAVE_GETDEVCAPS_INPUTS:
-           lpParms->dwReturn = 1;
+           lpParms->dwReturn = waveInGetNumDevs();
            break;
        case MCI_WAVE_GETDEVCAPS_OUTPUTS:
            break;
        case MCI_WAVE_GETDEVCAPS_OUTPUTS:
-           lpParms->dwReturn = 1;
+           lpParms->dwReturn = waveOutGetNumDevs();
            break;
        default:
             FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
            break;
        default:
             FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
@@ -1547,6 +1622,8 @@ static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
        WARN("No GetDevCaps-Item !\n");
        return MCIERR_UNRECOGNIZED_COMMAND;
     }
        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;
 }
 
     return ret;
 }
 
@@ -1561,9 +1638,10 @@ static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW l
 
     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
 
     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
 
-    if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
-       ret = MCIERR_NULL_PARAMETER_BLOCK;
-    } else if (wmw == NULL) {
+    if (!lpParms || !lpParms->lpstrReturn)
+       return MCIERR_NULL_PARAMETER_BLOCK;
+
+    if (wmw == NULL) {
        ret = MCIERR_INVALID_DEVICE_ID;
     } else {
         static const WCHAR wszAudio  [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
        ret = MCIERR_INVALID_DEVICE_ID;
     } else {
         static const WCHAR wszAudio  [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
@@ -1574,7 +1652,7 @@ static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW l
 
        switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
        case MCI_INFO_PRODUCT: str = wszAudio; break;
 
        switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
        case MCI_INFO_PRODUCT: str = wszAudio; break;
-       case MCI_INFO_FILE:    str = wmw->openParms.lpstrElementName; break;
+       case MCI_INFO_FILE:    str = wmw->lpFileName; break;
        case MCI_WAVE_INPUT:   str = wszWaveIn; break;
        case MCI_WAVE_OUTPUT:  str = wszWaveOut; break;
        default:
        case MCI_WAVE_INPUT:   str = wszWaveIn; break;
        case MCI_WAVE_OUTPUT:  str = wszWaveOut; break;
        default:
@@ -1591,7 +1669,8 @@ static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW l
     } else {
        lpParms->lpstrReturn[0] = 0;
     }
     } else {
        lpParms->lpstrReturn[0] = 0;
     }
-
+    if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
+       WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
     return ret;
 }
 
     return ret;
 }
 
@@ -1612,7 +1691,7 @@ LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
     case DRV_ENABLE:           return 1;
     case DRV_DISABLE:          return 1;
     case DRV_QUERYCONFIGURE:   return 1;
     case DRV_ENABLE:           return 1;
     case DRV_DISABLE:          return 1;
     case DRV_QUERYCONFIGURE:   return 1;
-    case DRV_CONFIGURE:                MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK);      return 1;
+    case DRV_CONFIGURE:                MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
     case DRV_INSTALL:          return DRVCNF_RESTART;
     case DRV_REMOVE:           return DRVCNF_RESTART;
     }
     case DRV_INSTALL:          return DRVCNF_RESTART;
     case DRV_REMOVE:           return DRVCNF_RESTART;
     }
@@ -1623,8 +1702,8 @@ LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
     case MCI_OPEN_DRIVER:      return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW)  dwParam2);
     case MCI_CLOSE_DRIVER:     return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
     case MCI_CUE:              return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
     case MCI_OPEN_DRIVER:      return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW)  dwParam2);
     case MCI_CLOSE_DRIVER:     return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
     case MCI_CUE:              return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
-    case MCI_PLAY:             return WAVE_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)        dwParam2);
-    case MCI_RECORD:           return WAVE_mciRecord    (dwDevID, dwParam1, (LPMCI_RECORD_PARMS)      dwParam2);
+    case MCI_PLAY:             return WAVE_mciPlay      (dwDevID, dwParam1, dwParam2, NULL);
+    case MCI_RECORD:           return WAVE_mciRecord    (dwDevID, dwParam1, dwParam2, NULL);
     case MCI_STOP:             return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
     case MCI_SET:              return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_SET_PARMS)         dwParam2);
     case MCI_PAUSE:            return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
     case MCI_STOP:             return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
     case MCI_SET:              return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_SET_PARMS)         dwParam2);
     case MCI_PAUSE:            return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);