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