mciwave: Store only slots from OpenParms that we need.
[wine] / dlls / mciwave / mciwave.c
1 /*
2  * Wine Driver for MCI wave forms
3  *
4  * Copyright    1994 Martin Ayotte
5  *              1999,2000,2005 Eric Pouech
6  *              2000 Francois Jacques
7  *              2009 Jörg Höhle
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include <assert.h>
25 #include <stdarg.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "mmddk.h"
32 #include "wownt32.h"
33 #include "digitalv.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
38
39 typedef struct {
40     UINT                        wDevID;
41     HANDLE                      hWave;
42     int                         nUseCount;      /* Incremented for each shared open */
43     HMMIO                       hFile;          /* mmio file handle open as Element */
44     MCIDEVICEID                 wNotifyDeviceID;        /* MCI device ID with a pending notification */
45     HANDLE                      hCallback;      /* Callback handle for pending notification */
46     LPWSTR                      lpFileName;     /* Name of file (if any)                     */
47     WAVEFORMATEX                wfxRef;
48     LPWAVEFORMATEX              lpWaveFormat;   /* Points to wfxRef until set by OPEN or RECORD */
49     BOOL                        fInput;         /* FALSE = Output, TRUE = Input */
50     volatile WORD               dwStatus;       /* one from MCI_MODE_xxxx */
51     DWORD                       dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
52     DWORD                       dwPosition;     /* position in bytes in chunk */
53     HANDLE                      hEvent;         /* for synchronization */
54     LONG                        dwEventCount;   /* for synchronization */
55     MMCKINFO                    ckMainRIFF;     /* main RIFF chunk */
56     MMCKINFO                    ckWaveData;     /* data chunk */
57 } WINE_MCIWAVE;
58
59 /* ===================================================================
60  * ===================================================================
61  * FIXME: should be using the new mmThreadXXXX functions from WINMM
62  * instead of those
63  * it would require to add a wine internal flag to mmThreadCreate
64  * in order to pass a 32 bit function instead of a 16 bit one
65  * ===================================================================
66  * =================================================================== */
67
68 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
69
70 struct SCA {
71     async_cmd   cmd;
72     HANDLE      evt;
73     UINT        wDevID;
74     DWORD_PTR   dwParam1;
75     DWORD_PTR   dwParam2;
76 };
77
78 /**************************************************************************
79  *                              MCI_SCAStarter                  [internal]
80  */
81 static DWORD CALLBACK   MCI_SCAStarter(LPVOID arg)
82 {
83     struct SCA* sca = (struct SCA*)arg;
84     DWORD               ret;
85
86     TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
87           sca->wDevID, sca->dwParam1, sca->dwParam2);
88     ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
89     TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
90           sca->wDevID, sca->dwParam1, sca->dwParam2);
91     HeapFree(GetProcessHeap(), 0, sca);
92     return ret;
93 }
94
95 /**************************************************************************
96  *                              MCI_SendCommandAsync            [internal]
97  */
98 static  DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
99                                    DWORD_PTR dwParam2, UINT size)
100 {
101     HANDLE handles[2];
102     struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
103
104     if (sca == 0)
105         return MCIERR_OUT_OF_MEMORY;
106
107     sca->wDevID   = wDevID;
108     sca->cmd      = cmd;
109     sca->dwParam1 = dwParam1;
110
111     if (size && dwParam2) {
112         sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
113         /* copy structure passed by program in dwParam2 to be sure
114          * we can still use it whatever the program does
115          */
116         memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
117     } else {
118         sca->dwParam2 = dwParam2;
119     }
120
121     if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
122         (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
123         WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
124         if (handles[1]) CloseHandle(handles[1]);
125         sca->evt = NULL;
126         return MCI_SCAStarter(&sca);
127     }
128
129     SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
130     /* wait until either:
131      * - the thread has finished (handles[0], likely an error)
132      * - init phase of async command is done (handles[1])
133      */
134     WaitForMultipleObjects(2, handles, FALSE, INFINITE);
135     CloseHandle(handles[0]);
136     CloseHandle(handles[1]);
137     return 0;
138 }
139
140 /*======================================================================*
141  *                          MCI WAVE implementation                     *
142  *======================================================================*/
143
144 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
145
146 /**************************************************************************
147  *                              MCIWAVE_drvOpen                 [internal]
148  */
149 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
150 {
151     WINE_MCIWAVE*       wmw;
152
153     if (modp == NULL) return 0xFFFFFFFF;
154
155     wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
156
157     if (!wmw)
158         return 0;
159
160     wmw->wDevID = modp->wDeviceID;
161     mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
162     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
163     modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
164
165     wmw->wfxRef.wFormatTag      = WAVE_FORMAT_PCM;
166     wmw->wfxRef.nChannels       = 1;      /* MONO */
167     wmw->wfxRef.nSamplesPerSec  = 11025;
168     wmw->wfxRef.nAvgBytesPerSec = 11025;
169     wmw->wfxRef.nBlockAlign     = 1;
170     wmw->wfxRef.wBitsPerSample  = 8;
171     wmw->wfxRef.cbSize          = 0;      /* don't care */
172
173     return modp->wDeviceID;
174 }
175
176 /**************************************************************************
177  *                              MCIWAVE_drvClose                [internal]
178  */
179 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
180 {
181     WINE_MCIWAVE*  wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
182
183     if (wmw) {
184         HeapFree(GetProcessHeap(), 0, wmw);
185         mciSetDriverData(dwDevID, 0);
186         return 1;
187     }
188     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
189 }
190
191 /**************************************************************************
192  *                              WAVE_mciGetOpenDev              [internal]
193  */
194 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
195 {
196     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
197
198     if (wmw == NULL || wmw->nUseCount == 0) {
199         WARN("Invalid wDevID=%u\n", wDevID);
200         return 0;
201     }
202     return wmw;
203 }
204
205 /**************************************************************************
206  *                              WAVE_mciNotify                  [internal]
207  *
208  * Notifications in MCI work like a 1-element queue.
209  * Each new notification request supersedes the previous one.
210  * This affects Play and Record; other commands are immediate.
211  */
212 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
213 {
214     MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
215     HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
216     if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
217     mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
218 }
219
220 /**************************************************************************
221  *                              WAVE_ConvertByteToTimeFormat    [internal]
222  */
223 static  DWORD   WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
224 {
225     DWORD          ret = 0;
226
227     switch (wmw->dwMciTimeFormat) {
228     case MCI_FORMAT_MILLISECONDS:
229         ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
230         break;
231     case MCI_FORMAT_BYTES:
232         ret = val;
233         break;
234     case MCI_FORMAT_SAMPLES:
235         ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
236         break;
237     default:
238         WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
239     }
240     TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
241     *lpRet = 0;
242     return ret;
243 }
244
245 /**************************************************************************
246  *                              WAVE_ConvertTimeFormatToByte    [internal]
247  */
248 static  DWORD   WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
249 {
250     DWORD       ret = 0;
251
252     switch (wmw->dwMciTimeFormat) {
253     case MCI_FORMAT_MILLISECONDS:
254         ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
255         break;
256     case MCI_FORMAT_BYTES:
257         ret = val;
258         break;
259     case MCI_FORMAT_SAMPLES:
260         ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
261         break;
262     default:
263         WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
264     }
265     TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
266     return ret;
267 }
268
269 /**************************************************************************
270  *                      WAVE_mciReadFmt                         [internal]
271  */
272 static  DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
273 {
274     MMCKINFO    mmckInfo;
275     long        r;
276     LPWAVEFORMATEX pwfx;
277
278     mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
279     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
280         return MCIERR_INVALID_FILE;
281     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
282           (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
283
284     pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
285     if (!pwfx) return MCIERR_OUT_OF_MEMORY;
286
287     r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
288     if (r < sizeof(PCMWAVEFORMAT)) {
289         HeapFree(GetProcessHeap(), 0, pwfx);
290         return MCIERR_INVALID_FILE;
291     }
292     TRACE("wFormatTag=%04X !\n",   pwfx->wFormatTag);
293     TRACE("nChannels=%d\n",        pwfx->nChannels);
294     TRACE("nSamplesPerSec=%d\n",   pwfx->nSamplesPerSec);
295     TRACE("nAvgBytesPerSec=%d\n",  pwfx->nAvgBytesPerSec);
296     TRACE("nBlockAlign=%d\n",      pwfx->nBlockAlign);
297     TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
298     if (r >= (long)sizeof(WAVEFORMATEX))
299         TRACE("cbSize=%u !\n",     pwfx->cbSize);
300     if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
301         && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
302         HeapFree(GetProcessHeap(), 0, pwfx);
303         return MCIERR_INVALID_FILE;
304     }
305     wmw->lpWaveFormat = pwfx;
306
307     mmioAscend(wmw->hFile, &mmckInfo, 0);
308     wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
309     if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
310         TRACE("can't find data chunk\n");
311         return MCIERR_INVALID_FILE;
312     }
313     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
314           (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
315     return 0;
316 }
317
318 /**************************************************************************
319  *                      WAVE_mciDefaultFmt                      [internal]
320  *
321  * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
322  * until either Open File or Record.  It becomes immutable afterwards,
323  * i.e. Set wave format or channels etc. is subsequently refused.
324  */
325 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
326 {
327     wmw->lpWaveFormat = &wmw->wfxRef;
328     wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
329     wmw->lpWaveFormat->nChannels = 1;
330     wmw->lpWaveFormat->nSamplesPerSec = 11025;
331     wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
332     wmw->lpWaveFormat->nBlockAlign = 1;
333     wmw->lpWaveFormat->wBitsPerSample = 8;
334     wmw->lpWaveFormat->cbSize = 0;
335 }
336
337 /**************************************************************************
338  *                      WAVE_mciCreateRIFFSkeleton              [internal]
339  */
340 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
341 {
342    MMCKINFO     ckWaveFormat;
343    LPMMCKINFO   lpckRIFF     = &(wmw->ckMainRIFF);
344    LPMMCKINFO   lpckWaveData = &(wmw->ckWaveData);
345
346    lpckRIFF->ckid    = FOURCC_RIFF;
347    lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
348    lpckRIFF->cksize  = 0;
349
350    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
351         goto err;
352
353    ckWaveFormat.fccType = 0;
354    ckWaveFormat.ckid    = mmioFOURCC('f', 'm', 't', ' ');
355    ckWaveFormat.cksize  = sizeof(PCMWAVEFORMAT);
356
357    /* Set wave format accepts PCM only, however open an
358     * existing ADPCM file, record into it and the MCI will
359     * happily save back in that format. */
360    if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
361         if (wmw->lpWaveFormat->nBlockAlign !=
362             wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
363             WORD size = wmw->lpWaveFormat->nChannels *
364                 wmw->lpWaveFormat->wBitsPerSample/8;
365             WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
366                 wmw->lpWaveFormat->nBlockAlign, size);
367             wmw->lpWaveFormat->nBlockAlign = size;
368         }
369         if (wmw->lpWaveFormat->nAvgBytesPerSec !=
370             wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
371             DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
372                 wmw->lpWaveFormat->nBlockAlign;
373             WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
374                 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
375             wmw->lpWaveFormat->nAvgBytesPerSec = speed;
376         }
377    }
378    if (wmw->lpWaveFormat == &wmw->wfxRef) {
379         LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
380         if (!pwfx) return MCIERR_OUT_OF_MEMORY;
381         /* Set wave format accepts PCM only so the size is known. */
382         assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
383         *pwfx = wmw->wfxRef;
384         wmw->lpWaveFormat = pwfx;
385    }
386
387    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
388         goto err;
389
390    if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
391         ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
392         goto err;
393
394    if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
395         goto err;
396
397    lpckWaveData->cksize  = 0;
398    lpckWaveData->fccType = 0;
399    lpckWaveData->ckid    = mmioFOURCC('d', 'a', 't', 'a');
400
401    /* create data chunk */
402    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
403         goto err;
404
405    return 0;
406
407 err:
408    /* mciClose takes care of wmw->lpWaveFormat. */
409    return MCIERR_INVALID_FILE;
410 }
411
412 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
413 {
414     WCHAR       szTmpPath[MAX_PATH];
415     WCHAR       szPrefix[4];
416     DWORD       dwRet = MMSYSERR_NOERROR;
417
418     szPrefix[0] = 'M';
419     szPrefix[1] = 'C';
420     szPrefix[2] = 'I';
421     szPrefix[3] = '\0';
422
423     if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
424         WARN("can't retrieve temp path!\n");
425         *pszTmpFileName = NULL;
426         return MCIERR_FILE_NOT_FOUND;
427     }
428
429     *pszTmpFileName = HeapAlloc(GetProcessHeap(),
430                                 HEAP_ZERO_MEMORY,
431                                 MAX_PATH * sizeof(WCHAR));
432     if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
433         WARN("can't retrieve temp file name!\n");
434         HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
435         return MCIERR_FILE_NOT_FOUND;
436     }
437
438     TRACE("%s!\n", debugstr_w(*pszTmpFileName));
439
440     if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
441
442         *hFile = mmioOpenW(*pszTmpFileName, NULL,
443                            MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
444
445         if (*hFile == 0) {
446             WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
447             /* temporary file could not be created. clean filename. */
448             HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
449             dwRet = MCIERR_FILE_NOT_FOUND;
450         }
451     }
452     return dwRet;
453 }
454
455 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
456 {
457     LRESULT dwRet = MMSYSERR_NOERROR;
458     LPWSTR fn;
459
460     fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
461     if (!fn) return MCIERR_OUT_OF_MEMORY;
462     strcpyW(fn, filename);
463     HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
464     wmw->lpFileName = fn;
465
466     if (strlenW(filename) > 0) {
467         /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
468         TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
469
470         wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
471                                MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
472
473         if (wmw->hFile == 0) {
474             WARN("can't find file=%s!\n", debugstr_w(filename));
475             dwRet = MCIERR_FILE_NOT_FOUND;
476         }
477         else
478         {
479             LPMMCKINFO          lpckMainRIFF = &wmw->ckMainRIFF;
480
481             /* make sure we're are the beginning of the file */
482             mmioSeek(wmw->hFile, 0, SEEK_SET);
483
484             /* first reading of this file. read the waveformat chunk */
485             if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
486                 dwRet = MCIERR_INVALID_FILE;
487             } else {
488                 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
489                       (LPSTR)&(lpckMainRIFF->ckid),
490                       (LPSTR) &(lpckMainRIFF->fccType),
491                       (lpckMainRIFF->cksize));
492
493                 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
494                     lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
495                     dwRet = MCIERR_INVALID_FILE;
496                 } else {
497                     dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
498                 }
499             }
500         }
501     }
502     return dwRet;
503 }
504
505 /**************************************************************************
506  *                      WAVE_mciOpen                            [internal]
507  */
508 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
509 {
510     DWORD               dwRet = 0;
511     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
512
513     TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
514     if (lpOpenParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
515     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
516
517     if (dwFlags & MCI_OPEN_SHAREABLE)
518         return MCIERR_UNSUPPORTED_FUNCTION;
519
520     if (wmw->nUseCount > 0) {
521         /* The driver is already opened on this channel
522          * Wave driver cannot be shared
523          */
524         return MCIERR_DEVICE_OPEN;
525     }
526
527     wmw->nUseCount++;
528
529     wmw->fInput = FALSE;
530     wmw->hWave = 0;
531     wmw->dwStatus = MCI_MODE_NOT_READY;
532     wmw->hFile = 0;
533     wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
534     wmw->hCallback = NULL;
535     WAVE_mciDefaultFmt(wmw);
536
537     TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
538     wmw->wNotifyDeviceID = lpOpenParms->wDeviceID;
539
540     if (dwFlags & MCI_OPEN_ELEMENT) {
541         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
542             /* could it be that (DWORD)lpOpenParms->lpstrElementName
543              * contains the hFile value ?
544              */
545             dwRet = MCIERR_UNRECOGNIZED_COMMAND;
546         } else {
547             dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
548         }
549     }
550     TRACE("hFile=%p\n", wmw->hFile);
551
552     if (dwRet == 0) {
553         wmw->dwPosition = 0;
554
555         wmw->dwStatus = MCI_MODE_STOP;
556
557         if (dwFlags & MCI_NOTIFY)
558             WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
559     } else {
560         wmw->nUseCount--;
561         if (wmw->hFile != 0)
562             mmioClose(wmw->hFile, 0);
563         wmw->hFile = 0;
564     }
565     return dwRet;
566 }
567
568 /**************************************************************************
569  *                               WAVE_mciCue             [internal]
570  */
571 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
572 {
573     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
574
575     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
576
577     /* Tests on systems without sound drivers show that Cue, like
578      * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
579      * The first Cue Notify does not immediately return the
580      * notification, as if a player or recorder thread is started.
581      * PAUSE mode is reported when successful, but this mode is
582      * different from the normal Pause, because a) Pause then returns
583      * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
584      * still accepted, returning the original notification as ABORTED.
585      * I.e. Cue allows subsequent format changes, unlike Record or
586      * Open file, closes winmm if the format changes and stops this
587      * thread.
588      * Wine creates one player or recorder thread per async. Play or
589      * Record command.  Notification behaviour suggests that MS-W*
590      * reuses a single thread to improve response times.  Having Cue
591      * start this thread early helps to improve Play/Record's initial
592      * response time.  In effect, Cue is a performance hint, which
593      * justifies our almost no-op implementation.
594      */
595
596     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
597     if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
598
599     if ((dwFlags & MCI_NOTIFY) && lpParms)
600         WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
601
602     return MMSYSERR_NOERROR;
603 }
604
605 /**************************************************************************
606  *                              WAVE_mciStop                    [internal]
607  */
608 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
609 {
610     DWORD               dwRet = 0;
611     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
612
613     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
614
615     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
616
617     if (wmw->dwStatus != MCI_MODE_STOP) {
618         HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
619         if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
620     }
621
622     /* wait for playback thread (if any) to exit before processing further */
623     switch (wmw->dwStatus) {
624     case MCI_MODE_PAUSE:
625     case MCI_MODE_PLAY:
626     case MCI_MODE_RECORD:
627         {
628             int oldStat = wmw->dwStatus;
629             wmw->dwStatus = MCI_MODE_NOT_READY;
630             if (oldStat == MCI_MODE_PAUSE)
631                 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
632         }
633         while (wmw->dwStatus != MCI_MODE_STOP)
634             Sleep(10);
635         break;
636     }
637
638     /* sanity resets */
639     wmw->dwStatus = MCI_MODE_STOP;
640
641     if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
642         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
643
644     return dwRet;
645 }
646
647 /**************************************************************************
648  *                              WAVE_mciClose           [internal]
649  */
650 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
651 {
652     DWORD               dwRet = 0;
653     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
654
655     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
656
657     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
658
659     if (wmw->dwStatus != MCI_MODE_STOP) {
660         /* mciStop handles MCI_NOTIFY_ABORTED */
661         dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
662     }
663
664     wmw->nUseCount--;
665
666     if (wmw->nUseCount == 0) {
667         if (wmw->hFile != 0) {
668             mmioClose(wmw->hFile, 0);
669             wmw->hFile = 0;
670         }
671     }
672
673     if (wmw->lpWaveFormat != &wmw->wfxRef)
674         HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
675     wmw->lpWaveFormat = &wmw->wfxRef;
676     HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
677     wmw->lpFileName = NULL;
678
679     if ((dwFlags & MCI_NOTIFY) && lpParms) {
680         WAVE_mciNotify(lpParms->dwCallback, wmw,
681             (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
682     }
683
684     return 0;
685 }
686
687 /**************************************************************************
688  *                              WAVE_mciPlayCallback            [internal]
689  */
690 static  void    CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
691                                               DWORD_PTR dwInstance,
692                                               LPARAM dwParam1, LPARAM dwParam2)
693 {
694     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)dwInstance;
695
696     switch (uMsg) {
697     case WOM_OPEN:
698     case WOM_CLOSE:
699         break;
700     case WOM_DONE:
701         InterlockedIncrement(&wmw->dwEventCount);
702         TRACE("Returning waveHdr=%lx\n", dwParam1);
703         SetEvent(wmw->hEvent);
704         break;
705     default:
706         ERR("Unknown uMsg=%d\n", uMsg);
707     }
708 }
709
710 /******************************************************************
711  *                      WAVE_mciPlayWaitDone            [internal]
712  */
713 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
714 {
715     for (;;) {
716         ResetEvent(wmw->hEvent);
717         if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
718             break;
719         }
720         InterlockedIncrement(&wmw->dwEventCount);
721
722         WaitForSingleObject(wmw->hEvent, INFINITE);
723     }
724 }
725
726 /**************************************************************************
727  *                              WAVE_mciPlay            [internal]
728  */
729 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
730 {
731     LPMCI_PLAY_PARMS    lpParms = (void*)pmt;
732     DWORD               end;
733     LONG                bufsize, count, left;
734     DWORD               dwRet;
735     LPWAVEHDR           waveHdr = NULL;
736     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
737     HANDLE              oldcb;
738     int                 whidx;
739
740     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
741
742     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
743     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
744
745     if (wmw->hFile == 0) {
746         WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
747         return MCIERR_FILE_NOT_FOUND;
748     }
749
750     if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
751         /* FIXME: parameters (start/end) in lpParams may not be used */
752         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
753     }
754
755     /** This function will be called again by a thread when async is used.
756      * We have to set MCI_MODE_PLAY before we do this so that the app can spin
757      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
758      */
759     if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
760         return MCIERR_INTERNAL;
761     }
762
763     if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
764         if (wmw->lpWaveFormat->nBlockAlign !=
765             wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
766             WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
767                 wmw->lpWaveFormat->nBlockAlign,
768                 wmw->lpWaveFormat->nChannels *
769                  wmw->lpWaveFormat->wBitsPerSample/8);
770             wmw->lpWaveFormat->nBlockAlign =
771                 wmw->lpWaveFormat->nChannels *
772                 wmw->lpWaveFormat->wBitsPerSample/8;
773         }
774         if (wmw->lpWaveFormat->nAvgBytesPerSec !=
775             wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
776             WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
777                 wmw->lpWaveFormat->nAvgBytesPerSec,
778                 wmw->lpWaveFormat->nSamplesPerSec *
779                  wmw->lpWaveFormat->nBlockAlign);
780             wmw->lpWaveFormat->nAvgBytesPerSec =
781                 wmw->lpWaveFormat->nSamplesPerSec *
782                 wmw->lpWaveFormat->nBlockAlign;
783         }
784     }
785
786     end = wmw->ckWaveData.cksize;
787     if (lpParms && (dwFlags & MCI_TO)) {
788         DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
789         if (position > end)             return MCIERR_OUTOFRANGE;
790         end = position;
791     }
792     if (lpParms && (dwFlags & MCI_FROM)) {
793         DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
794         if (position > end)             return MCIERR_OUTOFRANGE;
795         /* Seek rounds down, so do we. */
796         position /= wmw->lpWaveFormat->nBlockAlign;
797         position *= wmw->lpWaveFormat->nBlockAlign;
798         wmw->dwPosition = position;
799     }
800     if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
801     left = end - wmw->dwPosition;
802     if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
803
804     wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
805     wmw->dwStatus = MCI_MODE_PLAY;
806
807     if (!(dwFlags & MCI_WAIT)) {
808         return MCI_SendCommandAsync(wmw->wNotifyDeviceID, WAVE_mciPlay, dwFlags,
809                                     (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
810     }
811
812     TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
813
814     oldcb = InterlockedExchangePointer(&wmw->hCallback,
815         (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
816     if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
817     oldcb = NULL;
818
819 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
820 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
821
822     /* go back to beginning of chunk plus the requested position */
823     /* FIXME: I'm not sure this is correct, notably because some data linked to
824      * the decompression state machine will not be correctly initialized.
825      * try it this way (other way would be to decompress from 0 up to dwPosition
826      * and to start sending to hWave when dwPosition is reached)
827      */
828     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
829
830     /* FIXME: how to choose between several output channels ? here mapper is forced */
831     dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
832                         (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
833
834     if (dwRet != 0) {
835         TRACE("Can't open low level audio device %d\n", dwRet);
836         dwRet = MCIERR_DEVICE_OPEN;
837         wmw->hWave = 0;
838         goto cleanUp;
839     }
840
841     /* make it so that 3 buffers per second are needed */
842     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
843
844     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
845     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
846     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
847     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
848     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
849     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
850     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
851     if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
852         waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
853         dwRet = MCIERR_INTERNAL;
854         goto cleanUp;
855     }
856
857     whidx = 0;
858     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
859     wmw->dwEventCount = 1L; /* for first buffer */
860
861     TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
862     if (hEvent) SetEvent(hEvent);
863
864     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
865     while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
866         count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
867         TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
868         if (count < 1)
869             break;
870         /* count is always <= bufsize, so this is correct regarding the
871          * waveOutPrepareHeader function
872          */
873         waveHdr[whidx].dwBufferLength = count;
874         waveHdr[whidx].dwFlags &= ~WHDR_DONE;
875         TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
876               &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
877               waveHdr[whidx].dwBytesRecorded);
878         dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
879         left -= count;
880         wmw->dwPosition += count;
881         TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
882
883         WAVE_mciPlayWaitDone(wmw);
884         whidx ^= 1;
885     }
886
887     WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
888
889     /* just to get rid of some race conditions between play, stop and pause */
890     waveOutReset(wmw->hWave);
891
892     waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
893     waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
894
895     dwRet = 0;
896
897 cleanUp:
898     if (dwFlags & MCI_NOTIFY)
899         oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
900
901     HeapFree(GetProcessHeap(), 0, waveHdr);
902
903     if (wmw->hWave) {
904         waveOutClose(wmw->hWave);
905         wmw->hWave = 0;
906     }
907     CloseHandle(wmw->hEvent);
908
909     wmw->dwStatus = MCI_MODE_STOP;
910
911     /* Let the potentically asynchronous commands support FAILURE notification. */
912     if (oldcb) mciDriverNotify(oldcb, wDevID,
913         dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
914
915     return dwRet;
916 }
917
918 /**************************************************************************
919  *                              WAVE_mciRecordCallback          [internal]
920  */
921 static  void    CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
922                                                 DWORD_PTR dwInstance,
923                                                 LPARAM dwParam1, LPARAM dwParam2)
924 {
925     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)dwInstance;
926     LPWAVEHDR           lpWaveHdr;
927     LONG                count = 0;
928
929     switch (uMsg) {
930     case WIM_OPEN:
931     case WIM_CLOSE:
932         break;
933     case WIM_DATA:
934         lpWaveHdr = (LPWAVEHDR) dwParam1;
935
936         InterlockedIncrement(&wmw->dwEventCount);
937
938         count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
939
940         lpWaveHdr->dwFlags &= ~WHDR_DONE;
941         if (count > 0)
942             wmw->dwPosition  += count;
943         /* else error reporting ?? */
944         if (wmw->dwStatus == MCI_MODE_RECORD)
945         {
946            /* Only queue up another buffer if we are recording.  We could receive this
947               message also when waveInReset() is called, since it notifies on all wave
948               buffers that are outstanding.  Queueing up more sometimes causes waveInClose
949               to fail. */
950            waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
951            TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
952         }
953
954         SetEvent(wmw->hEvent);
955         break;
956     default:
957         ERR("Unknown uMsg=%d\n", uMsg);
958     }
959 }
960
961 /******************************************************************
962  *                      WAVE_mciRecordWaitDone          [internal]
963  */
964 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
965 {
966     for (;;) {
967         ResetEvent(wmw->hEvent);
968         if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
969             break;
970         }
971         InterlockedIncrement(&wmw->dwEventCount);
972
973         WaitForSingleObject(wmw->hEvent, INFINITE);
974     }
975 }
976
977 /**************************************************************************
978  *                              WAVE_mciRecord                  [internal]
979  */
980 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
981 {
982     LPMCI_RECORD_PARMS  lpParms = (void*)pmt;
983     DWORD               end;
984     DWORD               dwRet = MMSYSERR_NOERROR;
985     LONG                bufsize;
986     LPWAVEHDR           waveHdr = NULL;
987     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
988     HANDLE              oldcb;
989
990     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
991
992     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
993     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
994
995     if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
996         /* FIXME: parameters (start/end) in lpParams may not be used */
997         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
998     }
999
1000     /** This function will be called again by a thread when async is used.
1001      * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1002      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1003      */
1004     if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
1005         return MCIERR_INTERNAL;
1006     }
1007
1008     wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1009     wmw->dwStatus = MCI_MODE_RECORD;
1010
1011     if (!(dwFlags & MCI_WAIT)) {
1012         return MCI_SendCommandAsync(wmw->wNotifyDeviceID, WAVE_mciRecord, dwFlags,
1013                                     (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1014     }
1015
1016     /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1017      * we don't modify the wave part of an existing file (ie. we always erase an
1018      * existing content, we don't overwrite)
1019      */
1020     HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
1021     dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1022     if (dwRet != 0) return dwRet;
1023
1024     /* new RIFF file, lpWaveFormat now valid */
1025     dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1026     if (dwRet != 0) return dwRet;
1027
1028     if (lpParms && (dwFlags & MCI_TO)) {
1029         end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1030     } else end = 0xFFFFFFFF;
1031     if (lpParms && (dwFlags & MCI_FROM)) {
1032         DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1033         if (wmw->ckWaveData.cksize < position)  return MCIERR_OUTOFRANGE;
1034         /* Seek rounds down, so do we. */
1035         position /= wmw->lpWaveFormat->nBlockAlign;
1036         position *= wmw->lpWaveFormat->nBlockAlign;
1037         wmw->dwPosition = position;
1038     }
1039     if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1040
1041     TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1042
1043     oldcb = InterlockedExchangePointer(&wmw->hCallback,
1044         (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1045     if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1046     oldcb = NULL;
1047
1048 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1049 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1050
1051     wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1052
1053     /* Go back to the beginning of the chunk plus the requested position */
1054     /* FIXME: I'm not sure this is correct, notably because some data linked to
1055      * the decompression state machine will not be correctly initialized.
1056      * Try it this way (other way would be to decompress from 0 up to dwPosition
1057      * and to start sending to hWave when dwPosition is reached).
1058      */
1059     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1060
1061     /* By default the device will be opened for output, the MCI_CUE function is there to
1062      * change from output to input and back
1063      */
1064     /* FIXME: how to choose between several output channels ? here mapper is forced */
1065     dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1066                         (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1067
1068     if (dwRet != MMSYSERR_NOERROR) {
1069         TRACE("Can't open low level audio device %d\n", dwRet);
1070         dwRet = MCIERR_DEVICE_OPEN;
1071         wmw->hWave = 0;
1072         goto cleanUp;
1073     }
1074
1075     /* make it so that 3 buffers per second are needed */
1076     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1077
1078     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1079     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1080     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1081     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
1082     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
1083     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
1084     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1085
1086     if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1087         waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1088         dwRet = MCIERR_INTERNAL;
1089         goto cleanUp;
1090     }
1091
1092     if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1093         waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1094         dwRet = MCIERR_INTERNAL;
1095         goto cleanUp;
1096     }
1097
1098     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1099     wmw->dwEventCount = 1L; /* for first buffer */
1100
1101     TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1102
1103     dwRet = waveInStart(wmw->hWave);
1104
1105     if (hEvent) SetEvent(hEvent);
1106
1107     while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1108         WAVE_mciRecordWaitDone(wmw);
1109     }
1110     /* Grab callback before another thread kicks in after we change dwStatus. */
1111     if (dwFlags & MCI_NOTIFY) {
1112         oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1113         dwFlags &= ~MCI_NOTIFY;
1114     }
1115     /* needed so that the callback above won't add again the buffers returned by the reset */
1116     wmw->dwStatus = MCI_MODE_STOP;
1117
1118     waveInReset(wmw->hWave);
1119
1120     waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1121     waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1122
1123     dwRet = 0;
1124
1125 cleanUp:
1126     if (dwFlags & MCI_NOTIFY)
1127         oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1128
1129     HeapFree(GetProcessHeap(), 0, waveHdr);
1130
1131     if (wmw->hWave) {
1132         waveInClose(wmw->hWave);
1133         wmw->hWave = 0;
1134     }
1135     CloseHandle(wmw->hEvent);
1136
1137     wmw->dwStatus = MCI_MODE_STOP;
1138
1139     if (oldcb) mciDriverNotify(oldcb, wDevID,
1140         dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1141
1142     return dwRet;
1143
1144 }
1145
1146 /**************************************************************************
1147  *                              WAVE_mciPause                   [internal]
1148  */
1149 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1150 {
1151     DWORD               dwRet;
1152     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1153
1154     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1155
1156     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1157
1158     switch (wmw->dwStatus) {
1159     case MCI_MODE_PLAY:
1160         dwRet = waveOutPause(wmw->hWave);
1161         if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1162         else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1163             ERR("waveOutPause error %d\n",dwRet);
1164             dwRet = MCIERR_INTERNAL;
1165         }
1166         break;
1167     case MCI_MODE_RECORD:
1168         dwRet = waveInStop(wmw->hWave);
1169         if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1170         else {
1171             ERR("waveInStop error %d\n",dwRet);
1172             dwRet = MCIERR_INTERNAL;
1173         }
1174         break;
1175     case MCI_MODE_PAUSE:
1176         dwRet = MMSYSERR_NOERROR;
1177         break;
1178     default:
1179         dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1180     }
1181     if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1182         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1183     return dwRet;
1184 }
1185
1186 /**************************************************************************
1187  *                              WAVE_mciResume                  [internal]
1188  */
1189 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1190 {
1191     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1192     DWORD               dwRet;
1193
1194     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1195
1196     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1197
1198     switch (wmw->dwStatus) {
1199     case MCI_MODE_PAUSE:
1200         /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1201         if (wmw->fInput) {
1202             dwRet = waveInStart(wmw->hWave);
1203             if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1204             else {
1205                 ERR("waveInStart error %d\n",dwRet);
1206                 dwRet = MCIERR_INTERNAL;
1207             }
1208         } else {
1209             dwRet = waveOutRestart(wmw->hWave);
1210             if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1211             else {
1212                 ERR("waveOutRestart error %d\n",dwRet);
1213                 dwRet = MCIERR_INTERNAL;
1214             }
1215         }
1216         break;
1217     case MCI_MODE_PLAY:
1218     case MCI_MODE_RECORD:
1219         dwRet = MMSYSERR_NOERROR;
1220         break;
1221     default:
1222         dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1223     }
1224     if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1225         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1226     return dwRet;
1227 }
1228
1229 /**************************************************************************
1230  *                              WAVE_mciSeek                    [internal]
1231  */
1232 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1233 {
1234     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1235     DWORD               position, dwRet;
1236
1237     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1238
1239     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1240     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1241
1242     position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1243     if (!position)              return MCIERR_MISSING_PARAMETER;
1244     if (position&(position-1))  return MCIERR_FLAGS_NOT_COMPATIBLE;
1245
1246     /* Stop sends MCI_NOTIFY_ABORTED when needed */
1247     dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1248     if (dwRet != MMSYSERR_NOERROR) return dwRet;
1249
1250     if (dwFlags & MCI_TO) {
1251         position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1252         if (position > wmw->ckWaveData.cksize)
1253             return MCIERR_OUTOFRANGE;
1254     } else if (dwFlags & MCI_SEEK_TO_START) {
1255         position = 0;
1256     } else {
1257         position = wmw->ckWaveData.cksize;
1258     }
1259     /* Seek rounds down, unless at end */
1260     if (position != wmw->ckWaveData.cksize) {
1261         position /= wmw->lpWaveFormat->nBlockAlign;
1262         position *= wmw->lpWaveFormat->nBlockAlign;
1263     }
1264     wmw->dwPosition = position;
1265     TRACE("Seeking to position=%u bytes\n", position);
1266
1267     if (dwFlags & MCI_NOTIFY)
1268         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1269
1270     return MMSYSERR_NOERROR;
1271 }
1272
1273 /**************************************************************************
1274  *                              WAVE_mciSet                     [internal]
1275  */
1276 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1277 {
1278     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1279
1280     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1281
1282     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1283     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1284
1285     if (dwFlags & MCI_SET_TIME_FORMAT) {
1286         switch (lpParms->dwTimeFormat) {
1287         case MCI_FORMAT_MILLISECONDS:
1288             TRACE("MCI_FORMAT_MILLISECONDS !\n");
1289             wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1290             break;
1291         case MCI_FORMAT_BYTES:
1292             TRACE("MCI_FORMAT_BYTES !\n");
1293             wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1294             break;
1295         case MCI_FORMAT_SAMPLES:
1296             TRACE("MCI_FORMAT_SAMPLES !\n");
1297             wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1298             break;
1299         default:
1300             WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1301             return MCIERR_BAD_TIME_FORMAT;
1302         }
1303     }
1304     if (dwFlags & MCI_SET_VIDEO) {
1305         TRACE("No support for video !\n");
1306         return MCIERR_UNSUPPORTED_FUNCTION;
1307     }
1308     if (dwFlags & MCI_SET_DOOR_OPEN) {
1309         TRACE("No support for door open !\n");
1310         return MCIERR_UNSUPPORTED_FUNCTION;
1311     }
1312     if (dwFlags & MCI_SET_DOOR_CLOSED) {
1313         TRACE("No support for door close !\n");
1314         return MCIERR_UNSUPPORTED_FUNCTION;
1315     }
1316     if (dwFlags & MCI_SET_AUDIO) {
1317         if (dwFlags & MCI_SET_ON) {
1318             TRACE("MCI_SET_ON audio !\n");
1319         } else if (dwFlags & MCI_SET_OFF) {
1320             TRACE("MCI_SET_OFF audio !\n");
1321         } else {
1322             WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1323             return MCIERR_BAD_INTEGER;
1324         }
1325
1326         switch (lpParms->dwAudio)
1327         {
1328         case MCI_SET_AUDIO_ALL:         TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1329         case MCI_SET_AUDIO_LEFT:        TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1330         case MCI_SET_AUDIO_RIGHT:       TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1331         default:                        WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1332         }
1333     }
1334     if (dwFlags & MCI_WAVE_INPUT)
1335         TRACE("MCI_WAVE_INPUT !\n");
1336     if (dwFlags & MCI_WAVE_OUTPUT)
1337         TRACE("MCI_WAVE_OUTPUT !\n");
1338     if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1339         TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1340     if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1341         TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1342     /* Set wave format parameters is refused after Open or Record.*/
1343     if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1344         TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag);
1345         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1346         if (((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag != WAVE_FORMAT_PCM)
1347             return MCIERR_OUTOFRANGE;
1348     }
1349     if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1350         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1351         wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1352         TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1353     }
1354     if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1355         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1356         wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1357         TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1358     }
1359     if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1360         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1361         wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1362         TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1363     }
1364     if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1365         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1366         wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1367         TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1368     }
1369     if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1370         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1371         wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1372         TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1373     }
1374     if (dwFlags & MCI_NOTIFY)
1375         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1376     return 0;
1377 }
1378
1379 /**************************************************************************
1380  *                              WAVE_mciSave            [internal]
1381  */
1382 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1383 {
1384     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1385     DWORD               ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1386
1387     TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1388     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1389     if (wmw     == NULL)        return MCIERR_INVALID_DEVICE_ID;
1390
1391     if (dwFlags & MCI_WAIT)
1392     {
1393         FIXME("MCI_WAIT not implemented\n");
1394     }
1395     WAVE_mciStop(wDevID, 0, NULL);
1396
1397     ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1398     ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1399
1400     ret = mmioClose(wmw->hFile, 0);
1401     wmw->hFile = 0;
1402
1403     /*
1404       If the destination file already exists, it has to be overwritten.  (Behaviour
1405       verified in Windows (2000)).  If it doesn't overwrite, it is breaking one of
1406       my applications.  We are making use of mmioRename, which WILL NOT overwrite
1407       the destination file (which is what Windows does, also verified in Win2K)
1408       So, lets delete the destination file before calling mmioRename.  If the
1409       destination file DOESN'T exist, the delete will fail silently.  Let's also be
1410       careful not to lose our previous error code.
1411     */
1412     tmpRet = GetLastError();
1413     DeleteFileW (lpParms->lpfilename);
1414     SetLastError(tmpRet);
1415
1416     /* FIXME: Open file.wav; Save; must not rename the original file.
1417      * Nor must Save a.wav; Save b.wav rename a. */
1418     if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1419         ret = MMSYSERR_NOERROR;
1420     }
1421
1422     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1423         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1424
1425     if (ret == MMSYSERR_NOERROR)
1426         ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1427
1428     return ret;
1429 }
1430
1431 /**************************************************************************
1432  *                              WAVE_mciStatus          [internal]
1433  */
1434 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1435 {
1436     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1437     DWORD               ret = 0;
1438
1439     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1440     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1441     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1442
1443     if (dwFlags & MCI_STATUS_ITEM) {
1444         switch (lpParms->dwItem) {
1445         case MCI_STATUS_CURRENT_TRACK:
1446             lpParms->dwReturn = 1;
1447             TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1448             break;
1449         case MCI_STATUS_LENGTH:
1450             if (!wmw->hFile) {
1451                 lpParms->dwReturn = 0;
1452                 return MCIERR_UNSUPPORTED_FUNCTION;
1453             }
1454             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1455             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1456             TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1457             break;
1458         case MCI_STATUS_MODE:
1459             TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1460             lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1461             ret = MCI_RESOURCE_RETURNED;
1462             break;
1463         case MCI_STATUS_MEDIA_PRESENT:
1464             TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1465             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1466             ret = MCI_RESOURCE_RETURNED;
1467             break;
1468         case MCI_STATUS_NUMBER_OF_TRACKS:
1469             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1470             lpParms->dwReturn = 1;
1471             TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1472             break;
1473         case MCI_STATUS_POSITION:
1474             if (!wmw->hFile) {
1475                 lpParms->dwReturn = 0;
1476                 return MCIERR_UNSUPPORTED_FUNCTION;
1477             }
1478             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1479             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1480                                                              (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1481                                                              &ret);
1482             TRACE("MCI_STATUS_POSITION %s => %lu\n",
1483                   (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1484             break;
1485         case MCI_STATUS_READY:
1486             lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1487                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1488             TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1489             ret = MCI_RESOURCE_RETURNED;
1490             break;
1491         case MCI_STATUS_TIME_FORMAT:
1492             lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1493             TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1494             ret = MCI_RESOURCE_RETURNED;
1495             break;
1496         case MCI_WAVE_INPUT:
1497             TRACE("MCI_WAVE_INPUT !\n");
1498             lpParms->dwReturn = 0;
1499             ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1500             break;
1501         case MCI_WAVE_OUTPUT:
1502             TRACE("MCI_WAVE_OUTPUT !\n");
1503             {
1504                 UINT    id;
1505                 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1506                     lpParms->dwReturn = id;
1507                 } else {
1508                     lpParms->dwReturn = 0;
1509                     ret = MCIERR_WAVE_OUTPUTUNSPECIFIED;
1510                 }
1511             }
1512             break;
1513         /* It is always ok to query wave format parameters,
1514          * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1515         case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1516             lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1517             TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1518             break;
1519         case MCI_WAVE_STATUS_BITSPERSAMPLE:
1520             lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1521             TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1522             break;
1523         case MCI_WAVE_STATUS_BLOCKALIGN:
1524             lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1525             TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1526             break;
1527         case MCI_WAVE_STATUS_CHANNELS:
1528             lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1529             TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1530             break;
1531         case MCI_WAVE_STATUS_FORMATTAG:
1532             lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1533             TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1534             break;
1535         case MCI_WAVE_STATUS_SAMPLESPERSEC:
1536             lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1537             TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1538             break;
1539         case MCI_WAVE_STATUS_LEVEL:
1540             TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1541             lpParms->dwReturn = 0xAAAA5555;
1542             break;
1543         default:
1544             WARN("unknown command %08X !\n", lpParms->dwItem);
1545             return MCIERR_UNRECOGNIZED_COMMAND;
1546         }
1547     }
1548     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1549         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1550     return ret;
1551 }
1552
1553 /**************************************************************************
1554  *                              WAVE_mciGetDevCaps              [internal]
1555  */
1556 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1557                                 LPMCI_GETDEVCAPS_PARMS lpParms)
1558 {
1559     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1560     DWORD               ret = 0;
1561
1562     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1563
1564     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1565     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1566
1567     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1568         switch(lpParms->dwItem) {
1569         case MCI_GETDEVCAPS_DEVICE_TYPE:
1570             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1571             ret = MCI_RESOURCE_RETURNED;
1572             break;
1573         case MCI_GETDEVCAPS_HAS_AUDIO:
1574             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1575             ret = MCI_RESOURCE_RETURNED;
1576             break;
1577         case MCI_GETDEVCAPS_HAS_VIDEO:
1578             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1579             ret = MCI_RESOURCE_RETURNED;
1580             break;
1581         case MCI_GETDEVCAPS_USES_FILES:
1582             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1583             ret = MCI_RESOURCE_RETURNED;
1584             break;
1585         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1586             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1587             ret = MCI_RESOURCE_RETURNED;
1588             break;
1589         case MCI_GETDEVCAPS_CAN_RECORD:
1590             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1591             ret = MCI_RESOURCE_RETURNED;
1592             break;
1593         case MCI_GETDEVCAPS_CAN_EJECT:
1594             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1595             ret = MCI_RESOURCE_RETURNED;
1596             break;
1597         case MCI_GETDEVCAPS_CAN_PLAY:
1598             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1599             ret = MCI_RESOURCE_RETURNED;
1600             break;
1601         case MCI_GETDEVCAPS_CAN_SAVE:
1602             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1603             ret = MCI_RESOURCE_RETURNED;
1604             break;
1605         case MCI_WAVE_GETDEVCAPS_INPUTS:
1606             lpParms->dwReturn = 1;
1607             break;
1608         case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1609             lpParms->dwReturn = 1;
1610             break;
1611         default:
1612             FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1613             return MCIERR_UNRECOGNIZED_COMMAND;
1614         }
1615     } else {
1616         WARN("No GetDevCaps-Item !\n");
1617         return MCIERR_UNRECOGNIZED_COMMAND;
1618     }
1619     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1620         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1621     return ret;
1622 }
1623
1624 /**************************************************************************
1625  *                              WAVE_mciInfo                    [internal]
1626  */
1627 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1628 {
1629     DWORD               ret = 0;
1630     LPCWSTR             str = 0;
1631     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1632
1633     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1634
1635     if (!lpParms || !lpParms->lpstrReturn)
1636         return MCIERR_NULL_PARAMETER_BLOCK;
1637
1638     if (wmw == NULL) {
1639         ret = MCIERR_INVALID_DEVICE_ID;
1640     } else {
1641         static const WCHAR wszAudio  [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1642         static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1643         static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1644
1645         TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1646
1647         switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1648         case MCI_INFO_PRODUCT: str = wszAudio; break;
1649         case MCI_INFO_FILE:    str = wmw->lpFileName; break;
1650         case MCI_WAVE_INPUT:   str = wszWaveIn; break;
1651         case MCI_WAVE_OUTPUT:  str = wszWaveOut; break;
1652         default:
1653             WARN("Don't know this info command (%u)\n", dwFlags);
1654             ret = MCIERR_UNRECOGNIZED_COMMAND;
1655         }
1656     }
1657     if (str) {
1658         if (strlenW(str) + 1 > lpParms->dwRetSize) {
1659             ret = MCIERR_PARAM_OVERFLOW;
1660         } else {
1661             lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1662         }
1663     } else {
1664         lpParms->lpstrReturn[0] = 0;
1665     }
1666     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1667         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1668     return ret;
1669 }
1670
1671 /**************************************************************************
1672  *                              DriverProc (MCIWAVE.@)
1673  */
1674 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1675                                     LPARAM dwParam1, LPARAM dwParam2)
1676 {
1677     TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1678           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1679
1680     switch (wMsg) {
1681     case DRV_LOAD:              return 1;
1682     case DRV_FREE:              return 1;
1683     case DRV_OPEN:              return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1684     case DRV_CLOSE:             return WAVE_drvClose(dwDevID);
1685     case DRV_ENABLE:            return 1;
1686     case DRV_DISABLE:           return 1;
1687     case DRV_QUERYCONFIGURE:    return 1;
1688     case DRV_CONFIGURE:         MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1689     case DRV_INSTALL:           return DRVCNF_RESTART;
1690     case DRV_REMOVE:            return DRVCNF_RESTART;
1691     }
1692
1693     if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1694
1695     switch (wMsg) {
1696     case MCI_OPEN_DRIVER:       return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW)  dwParam2);
1697     case MCI_CLOSE_DRIVER:      return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1698     case MCI_CUE:               return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1699     case MCI_PLAY:              return WAVE_mciPlay      (dwDevID, dwParam1, dwParam2, NULL);
1700     case MCI_RECORD:            return WAVE_mciRecord    (dwDevID, dwParam1, dwParam2, NULL);
1701     case MCI_STOP:              return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1702     case MCI_SET:               return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_SET_PARMS)         dwParam2);
1703     case MCI_PAUSE:             return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1704     case MCI_RESUME:            return WAVE_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1705     case MCI_STATUS:            return WAVE_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)      dwParam2);
1706     case MCI_GETDEVCAPS:        return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)  dwParam2);
1707     case MCI_INFO:              return WAVE_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMSW)       dwParam2);
1708     case MCI_SEEK:              return WAVE_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)        dwParam2);
1709     case MCI_SAVE:              return WAVE_mciSave      (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW)       dwParam2);
1710         /* commands that should be supported */
1711     case MCI_LOAD:
1712     case MCI_FREEZE:
1713     case MCI_PUT:
1714     case MCI_REALIZE:
1715     case MCI_UNFREEZE:
1716     case MCI_UPDATE:
1717     case MCI_WHERE:
1718     case MCI_STEP:
1719     case MCI_SPIN:
1720     case MCI_ESCAPE:
1721     case MCI_COPY:
1722     case MCI_CUT:
1723     case MCI_DELETE:
1724     case MCI_PASTE:
1725         FIXME("Unsupported yet command [%u]\n", wMsg);
1726         break;
1727     case MCI_WINDOW:
1728         TRACE("Unsupported command [%u]\n", wMsg);
1729         break;
1730         /* option which can be silenced */
1731     case MCI_CONFIGURE:
1732         return 0;
1733     case MCI_OPEN:
1734     case MCI_CLOSE:
1735         ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1736         break;
1737     default:
1738         FIXME("is probably wrong msg [%u]\n", wMsg);
1739         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1740     }
1741     return MCIERR_UNRECOGNIZED_COMMAND;
1742 }