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