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