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