- got rid of all the internal MM tweaks to load builtin MCI
[wine] / multimedia / mciwave.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*                                 
3  * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
4  *
5  * Copyright 1994 Martin Ayotte
6  */
7 /*
8  * FIXME:
9  *      - record/play should and must be done asynchronous
10  *      - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
11  */
12
13 #define EMULATE_SB16
14
15 #define DEBUG_MCIWAVE
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include "winuser.h"
24 #include "driver.h"
25 #include "multimedia.h"
26 #include "mmsystem.h"
27 #include "heap.h"
28 #include "debug.h"
29
30 DEFAULT_DEBUG_CHANNEL(mciwave)
31
32 #define MAX_MCIWAVEDRV  (1)
33
34 typedef struct {
35     UINT16                      wDevID;
36     UINT16                      wWavID;
37     int                         nUseCount;      /* Incremented for each shared open */
38     BOOL16                      fShareable;     /* TRUE if first open was shareable */
39     WORD                        wNotifyDeviceID;/* MCI device ID with a pending notification */
40     HANDLE16                    hCallback;      /* Callback handle for pending notification */
41     HMMIO                       hFile;          /* mmio file handle open as Element */
42     MCI_WAVE_OPEN_PARMSA        openParms;
43     WAVEOPENDESC                waveDesc;
44     PCMWAVEFORMAT               WaveFormat;
45     WAVEHDR                     WaveHdr;
46     BOOL16                      fInput;         /* FALSE = Output, TRUE = Input */
47     WORD                        dwStatus;       /* one from MCI_MODE_xxxx */
48     DWORD                       dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
49     DWORD                       dwFileOffset;   /* Offset of chunk in mmio file */
50     DWORD                       dwLength;       /* number of bytes in chunk for playing */
51     DWORD                       dwPosition;     /* position in bytes in chunk for playing */
52 } WINE_MCIWAVE;
53
54 static WINE_MCIWAVE     MCIWaveDev[MAX_MCIWAVEDRV];
55
56 /*======================================================================*
57  *                          MCI WAVE implemantation                     *
58  *======================================================================*/
59
60 /**************************************************************************
61  *                              MCIWAVE_drvGetDrv               [internal]      
62  */
63 static WINE_MCIWAVE*  WAVE_drvGetDrv(UINT16 wDevID)
64 {
65     int i;
66
67     for (i = 0; i < MAX_MCIWAVEDRV; i++) {
68         if (MCIWaveDev[i].wDevID == wDevID) {
69             return &MCIWaveDev[i];
70         }
71     }
72     return 0;
73 }
74
75 /**************************************************************************
76  *                              MCIWAVE_drvOpen                 [internal]      
77  */
78 static  DWORD   WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
79 {
80     int i;
81
82     for (i = 0; i < MAX_MCIWAVEDRV; i++) {
83         if (MCIWaveDev[i].wDevID == 0) {
84             MCIWaveDev[i].wDevID = modp->wDeviceID;
85             modp->wCustomCommandTable = -1;
86             modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
87             return modp->wDeviceID;
88         }
89     }
90     return 0;
91 }
92
93 /**************************************************************************
94  *                              MCIWAVE_drvClose                [internal]      
95  */
96 static  DWORD   WAVE_drvClose(DWORD dwDevID)
97 {
98     WINE_MCIWAVE*  wmcda = WAVE_drvGetDrv(dwDevID);
99
100     if (wmcda) {
101         wmcda->wDevID = 0;
102         return 1;
103     }
104     return 0;
105 }
106
107 /**************************************************************************
108  *                              WAVE_mciGetOpenDev              [internal]      
109  */
110 static WINE_MCIWAVE*  WAVE_mciGetOpenDev(UINT16 wDevID)
111 {
112     WINE_MCIWAVE*       wmw = WAVE_drvGetDrv(wDevID);
113     
114     if (wmw == NULL || wmw->nUseCount == 0) {
115         WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
116         return 0;
117     }
118     return wmw;
119 }
120
121 static  DWORD   WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
122 {
123     DWORD       ret = 0;
124     
125     switch (wmw->dwMciTimeFormat) {
126     case MCI_FORMAT_MILLISECONDS:
127         ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
128         break;
129     case MCI_FORMAT_BYTES:
130         ret = val;
131         break;
132     case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
133         ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
134         break;
135     default:
136         WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
137     }
138     TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
139     return ret;
140 }
141
142 static  DWORD   WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
143 {
144     DWORD       ret = 0;
145     
146     switch (wmw->dwMciTimeFormat) {
147     case MCI_FORMAT_MILLISECONDS:
148         ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
149         break;
150     case MCI_FORMAT_BYTES:
151         ret = val;
152         break;
153     case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
154         ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
155         break;
156     default:
157         WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
158     }
159     TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
160     return ret;
161 }
162
163 static  DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
164 {
165     MMCKINFO    mmckInfo;
166     
167     mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
168     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
169         return MCIERR_INVALID_FILE;
170     TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
171           (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
172     if (mmioRead(wmw->hFile, (HPSTR)&wmw->WaveFormat,
173                  (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
174         return MCIERR_INVALID_FILE;
175     
176     TRACE(mciwave, "wFormatTag=%04X !\n",   wmw->WaveFormat.wf.wFormatTag);
177     TRACE(mciwave, "nChannels=%d \n",       wmw->WaveFormat.wf.nChannels);
178     TRACE(mciwave, "nSamplesPerSec=%ld\n",  wmw->WaveFormat.wf.nSamplesPerSec);
179     TRACE(mciwave, "nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
180     TRACE(mciwave, "nBlockAlign=%d \n",     wmw->WaveFormat.wf.nBlockAlign);
181     TRACE(mciwave, "wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
182     mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
183     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
184         return MCIERR_INVALID_FILE;
185     TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
186           (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
187     TRACE(mciwave, "nChannels=%d nSamplesPerSec=%ld\n",
188           wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
189     wmw->dwLength = mmckInfo.cksize;
190     wmw->dwFileOffset = mmioSeek(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
191     return 0;
192 }
193
194 /**************************************************************************
195  *                      WAVE_mciOpen                            [internal]
196  */
197 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
198 {
199     DWORD               dwRet = 0;
200     DWORD               dwDeviceID;
201     WINE_MCIWAVE*       wmw = WAVE_drvGetDrv(wDevID);
202     
203     TRACE(mciwave, "(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
204     if (lpOpenParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
205     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
206
207     if (dwFlags & MCI_OPEN_SHAREABLE)
208         return MCIERR_HARDWARE;
209     
210     if (wmw->nUseCount > 0) {
211         /* The driver is already opened on this channel
212          * Wave driver cannot be shared
213          */
214         return MCIERR_DEVICE_OPEN;
215     }
216     wmw->nUseCount++;
217     
218     dwDeviceID = lpOpenParms->wDeviceID;
219     
220     wmw->fInput = FALSE;
221     wmw->wWavID = 0;
222
223     TRACE(mciwave, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
224     
225     if (dwFlags & MCI_OPEN_ELEMENT) {
226         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
227             /* could it be that (DWORD)lpOpenParms->lpstrElementName 
228              * contains the hFile value ? 
229              */
230             dwRet = MCIERR_UNRECOGNIZED_COMMAND;
231         } else {
232             LPCSTR      lpstrElementName = lpOpenParms->lpstrElementName;
233             
234             /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
235             TRACE(mciwave, "MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
236             if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
237                 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL, 
238                                        MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
239                 if (wmw->hFile == 0) {
240                     WARN(mciwave, "can't find file='%s' !\n", lpstrElementName);
241                     dwRet = MCIERR_FILE_NOT_FOUND;
242                 }
243             } else {
244                 wmw->hFile = 0;
245             }
246         }
247     }
248     TRACE(mciwave, "hFile=%u\n", wmw->hFile);
249     
250     memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
251     wmw->wNotifyDeviceID = dwDeviceID;
252     wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
253     
254     wmw->waveDesc.hWave = 0;
255     
256     if (dwRet == 0 && wmw->hFile != 0) {
257         MMCKINFO        ckMainRIFF;
258         
259         if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
260             dwRet = MCIERR_INVALID_FILE;
261         } else {
262             TRACE(mciwave, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
263                   (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
264             if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
265                 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
266                 dwRet = MCIERR_INVALID_FILE;
267             } else {
268                 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
269             }
270         }
271     } else {
272         wmw->dwLength = 0;
273     }
274     if (dwRet == 0) {
275         wmw->WaveFormat.wf.nAvgBytesPerSec = 
276             wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
277         wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
278         wmw->dwPosition = 0;
279         
280         wmw->dwStatus = MCI_MODE_STOP;
281     } else {
282         wmw->nUseCount--;
283         if (wmw->hFile != 0)
284             mmioClose(wmw->hFile, 0);
285         wmw->hFile = 0;
286     }
287     return dwRet;
288 }
289
290 /**************************************************************************
291  *                               WAVE_mciCue             [internal]
292  */
293 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
294 {
295     /*
296       FIXME
297       
298       This routine is far from complete. At the moment only a check is done on the
299       MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
300       is the default.
301       
302       The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
303       are ignored
304     */
305     
306     DWORD               dwRet;
307     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
308     
309     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
310     
311     if (wmw == NULL)    return MCIERR_INVALID_DEVICE_ID;
312     
313     /* always close elements ? */    
314     if (wmw->hFile != 0) {
315         mmioClose(wmw->hFile, 0);
316         wmw->hFile = 0;
317     }
318     
319     dwRet = MMSYSERR_NOERROR;  /* assume success */
320     
321     if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
322         dwRet = wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
323         if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
324         dwRet = widMessage(wmw->wWavID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
325         wmw->fInput = TRUE;
326     } else if (wmw->fInput) {
327         dwRet = widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L);
328         if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
329         dwRet = wodMessage(wmw->wWavID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
330         wmw->fInput = FALSE;
331     }
332     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
333 }
334
335 /**************************************************************************
336  *                              WAVE_mciStop                    [internal]
337  */
338 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
339 {
340     DWORD               dwRet;
341     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
342     
343     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
344     
345     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
346     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
347     
348     wmw->dwStatus = MCI_MODE_STOP;
349     wmw->dwPosition = 0;
350     TRACE(mciwave, "wmw->dwStatus=%d\n", wmw->dwStatus);
351     
352     if (wmw->fInput)
353         dwRet = widMessage(wmw->wWavID, WIDM_STOP, 0, dwFlags, (DWORD)lpParms);
354     else
355         dwRet = wodMessage(wmw->wWavID, WODM_STOP, 0, dwFlags, (DWORD)lpParms);
356     
357     if (dwFlags & MCI_NOTIFY) {
358         TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
359         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
360                           wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
361     }
362     
363     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
364 }
365
366 /**************************************************************************
367  *                              WAVE_mciClose           [internal]
368  */
369 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
370 {
371     DWORD               dwRet = 0;
372     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
373     
374     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
375     
376     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
377     
378     if (wmw->dwStatus != MCI_MODE_STOP) {
379         dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
380     }
381     
382     wmw->nUseCount--;
383     
384     if (wmw->nUseCount == 0) {
385         DWORD   mmRet;
386         if (wmw->hFile != 0) {
387             mmioClose(wmw->hFile, 0);
388             wmw->hFile = 0;
389         }
390         mmRet = (wmw->fInput) ? widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L) :
391             wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
392         
393         if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
394     }
395     
396     if ((dwFlags & MCI_NOTIFY) && lpParms) {
397         TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
398         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
399                           wmw->wNotifyDeviceID,
400                           (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
401     }
402     return 0;
403 }
404
405 /**************************************************************************
406  *                              WAVE_mciPlay            [internal]
407  */
408 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
409 {
410     DWORD               end;
411     LONG                bufsize, count;
412     HGLOBAL16           hData;
413     DWORD               dwRet;
414     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
415     
416     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
417     
418     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
419     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
420     
421     if (wmw->fInput) {
422         WARN(mciwave, "cannot play on input device\n");
423         return MCIERR_NONAPPLICABLE_FUNCTION;
424     }
425     
426     if (wmw->hFile == 0) {
427         WARN(mciwave, "Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
428         return MCIERR_FILE_NOT_FOUND;
429     }
430     
431     if (!(dwFlags & MCI_WAIT)) {
432         return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags, 
433                                     (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
434     }
435     
436     end = 0xFFFFFFFF;
437     if (lpParms && (dwFlags & MCI_FROM)) {
438         wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); 
439     }
440     if (lpParms && (dwFlags & MCI_TO)) {
441         end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
442     }
443     
444     TRACE(mciwave, "Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
445     
446     /* go back to begining of chunk */
447     mmioSeek(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
448     
449     /* By default the device will be opened for output, the MCI_CUE function is there to
450      * change from output to input and back
451      */
452     /* FIXME: how to choose between several output channels ? here 0 is forced */
453     dwRet = wodMessage(0, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
454     if (dwRet != 0) {
455         TRACE(mciwave, "Can't open low level audio device %ld\n", dwRet);
456         return MCIERR_DEVICE_OPEN;
457     }
458
459     /* at 22050 bytes per sec => 30 ms by block */
460     bufsize = 10240;
461     hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
462     wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
463     
464     wmw->dwStatus = MCI_MODE_PLAY;
465     
466     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
467     /* FIXME: use several WaveHdr for smoother playback */
468     /* FIXME: use only regular MMSYS functions, not calling directly the driver */
469     while (wmw->dwStatus != MCI_MODE_STOP) {
470         wmw->WaveHdr.dwUser = 0L;
471         wmw->WaveHdr.dwFlags = 0L;
472         wmw->WaveHdr.dwLoops = 0L;
473         count = mmioRead(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
474         TRACE(mciwave, "mmioRead bufsize=%ld count=%ld\n", bufsize, count);
475         if (count < 1) 
476             break;
477         dwRet = wodMessage(wmw->wWavID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
478         wmw->WaveHdr.dwBufferLength = count;
479         wmw->WaveHdr.dwBytesRecorded = 0;
480         /* FIXME */
481         wmw->WaveHdr.reserved = (DWORD)&wmw->WaveHdr;
482         TRACE(mciwave, "before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
483               &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
484         dwRet = wodMessage(wmw->wWavID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
485         /* FIXME: should use callback mechanisms from audio driver */
486 #if 1
487         while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
488             Sleep(1);
489 #endif
490         wmw->dwPosition += count;
491         TRACE(mciwave, "after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
492         dwRet = wodMessage(wmw->wWavID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
493     }
494     
495     if (wmw->WaveHdr.lpData != NULL) {
496         GlobalUnlock16(hData);
497         GlobalFree16(hData);
498         wmw->WaveHdr.lpData = NULL;
499     }
500
501     wodMessage(wmw->wWavID, WODM_STOP,  0, 0L, 0L);
502     wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
503
504     wmw->dwStatus = MCI_MODE_STOP;
505     if (lpParms && (dwFlags & MCI_NOTIFY)) {
506         TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
507         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
508                           wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
509     }
510     return 0;
511 }
512
513 /**************************************************************************
514  *                              WAVE_mciRecord                  [internal]
515  */
516 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
517 {
518     int                 start, end;
519     LONG                bufsize;
520     HGLOBAL16           hData;
521     LPWAVEHDR           lpWaveHdr;
522     DWORD               dwRet;
523     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
524     
525     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
526     
527     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
528     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
529     
530     if (!wmw->fInput) {
531         WARN(mciwave, "cannot record on output device\n");
532         return MCIERR_NONAPPLICABLE_FUNCTION;
533     }
534     
535     if (wmw->hFile == 0) {
536         WARN(mciwave, "can't find file='%s' !\n", 
537              wmw->openParms.lpstrElementName);
538         return MCIERR_FILE_NOT_FOUND;
539     }
540     start = 1;  end = 99999;
541     if (dwFlags & MCI_FROM) {
542         start = lpParms->dwFrom; 
543         TRACE(mciwave, "MCI_FROM=%d \n", start);
544     }
545     if (dwFlags & MCI_TO) {
546         end = lpParms->dwTo;
547         TRACE(mciwave, "MCI_TO=%d \n", end);
548     }
549     bufsize = 64000;
550     lpWaveHdr = &wmw->WaveHdr;
551     hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
552     lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
553     lpWaveHdr->dwBufferLength = bufsize;
554     lpWaveHdr->dwUser = 0L;
555     lpWaveHdr->dwFlags = 0L;
556     lpWaveHdr->dwLoops = 0L;
557     dwRet = widMessage(wmw->wWavID,WIDM_PREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
558     TRACE(mciwave, "after WIDM_PREPARE \n");
559     while (TRUE) {
560         lpWaveHdr->dwBytesRecorded = 0;
561         dwRet = widMessage(wmw->wWavID, WIDM_START, 0, 0L, 0L);
562         TRACE(mciwave, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
563               lpWaveHdr, lpWaveHdr->dwBytesRecorded);
564         if (lpWaveHdr->dwBytesRecorded == 0) break;
565     }
566     TRACE(mciwave, "before WIDM_UNPREPARE \n");
567     dwRet = widMessage(wmw->wWavID,WIDM_UNPREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
568     TRACE(mciwave, "after WIDM_UNPREPARE \n");
569     if (lpWaveHdr->lpData != NULL) {
570         GlobalUnlock16(hData);
571         GlobalFree16(hData);
572         lpWaveHdr->lpData = NULL;
573     }
574     if (dwFlags & MCI_NOTIFY) {
575         TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
576         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
577                           wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
578     }
579     return 0;
580 }
581
582 /**************************************************************************
583  *                              WAVE_mciPause                   [internal]
584  */
585 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
586 {
587     DWORD               dwRet;
588     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
589     
590     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
591     
592     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
593     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
594     
595     if (wmw->dwStatus == MCI_MODE_PLAY) {
596         wmw->dwStatus = MCI_MODE_PAUSE;
597     } 
598     
599     if (wmw->fInput)    dwRet = widMessage(wmw->wWavID, WIDM_PAUSE, 0, dwFlags, (DWORD)lpParms);
600     else                dwRet = wodMessage(wmw->wWavID, WODM_PAUSE, 0, dwFlags, (DWORD)lpParms);
601     
602     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
603 }
604
605 /**************************************************************************
606  *                              WAVE_mciResume                  [internal]
607  */
608 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
609 {
610     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
611     DWORD               dwRet = 0;
612     
613     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
614     
615     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
616     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
617     
618     if (wmw->dwStatus == MCI_MODE_PAUSE) {
619         wmw->dwStatus = MCI_MODE_PLAY;
620     } 
621     
622 #if 0
623     if (wmw->fInput)    dwRet = widMessage(wmw->wWavID, WIDM_PLAY, 0, dwFlags, (DWORD)lpParms);
624     else                dwRet = wodMessage(wmw->wWavID, WODM_PLAY, 0, dwFlags, (DWORD)lpParms);
625     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
626 #else
627     return dwRet;
628 #endif
629     
630 }
631
632 /**************************************************************************
633  *                              WAVE_mciSeek                    [internal]
634  */
635 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
636 {
637     DWORD               ret = 0;
638     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
639     
640     TRACE(mciwave, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
641     
642     if (lpParms == NULL) {
643         ret = MCIERR_NULL_PARAMETER_BLOCK;
644     } else if (wmw == NULL) {
645         ret = MCIERR_INVALID_DEVICE_ID;
646     } else {
647         WAVE_mciStop(wDevID, MCI_WAIT, 0);
648         
649         if (dwFlags & MCI_SEEK_TO_START) {
650             wmw->dwPosition = 0;
651         } else if (dwFlags & MCI_SEEK_TO_END) {
652             wmw->dwPosition = 0xFFFFFFFF; /* fixme */
653         } else if (dwFlags & MCI_TO) {
654             wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
655         } else {
656             WARN(mciwave, "dwFlag doesn't tell where to seek to...\n");
657             return MCIERR_MISSING_PARAMETER;
658         }
659         
660         TRACE(mciwave, "Seeking to position=%lu bytes\n", wmw->dwPosition);
661         
662         if (dwFlags & MCI_NOTIFY) {
663             TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
664             mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
665                               wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
666         }
667     }
668     return ret; 
669 }    
670
671 /**************************************************************************
672  *                              WAVE_mciSet                     [internal]
673  */
674 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
675 {
676     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
677     
678     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
679     
680     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
681     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
682     
683     if (dwFlags & MCI_SET_TIME_FORMAT) {
684         switch (lpParms->dwTimeFormat) {
685         case MCI_FORMAT_MILLISECONDS:
686             TRACE(mciwave, "MCI_FORMAT_MILLISECONDS !\n");
687             wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
688             break;
689         case MCI_FORMAT_BYTES:
690             TRACE(mciwave, "MCI_FORMAT_BYTES !\n");
691             wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
692             break;
693         case MCI_FORMAT_SAMPLES:
694             TRACE(mciwave, "MCI_FORMAT_SAMPLES !\n");
695             wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
696             break;
697         default:
698             WARN(mciwave, "Bad time format %lu!\n", lpParms->dwTimeFormat);
699             return MCIERR_BAD_TIME_FORMAT;
700         }
701     }
702     if (dwFlags & MCI_SET_VIDEO) {
703         TRACE(mciwave, "No support for video !\n");
704         return MCIERR_UNSUPPORTED_FUNCTION;
705     }
706     if (dwFlags & MCI_SET_DOOR_OPEN) {
707         TRACE(mciwave, "No support for door open !\n");
708         return MCIERR_UNSUPPORTED_FUNCTION;
709     }
710     if (dwFlags & MCI_SET_DOOR_CLOSED) {
711         TRACE(mciwave, "No support for door close !\n");
712         return MCIERR_UNSUPPORTED_FUNCTION;
713     }
714     if (dwFlags & MCI_SET_AUDIO) {
715         if (dwFlags & MCI_SET_ON) {
716             TRACE(mciwave, "MCI_SET_ON audio !\n");
717         } else if (dwFlags & MCI_SET_OFF) {
718             TRACE(mciwave, "MCI_SET_OFF audio !\n");
719         } else {
720             WARN(mciwave, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
721             return MCIERR_BAD_INTEGER;
722         }
723         
724         if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
725             TRACE(mciwave, "MCI_SET_AUDIO_ALL !\n");
726         if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
727             TRACE(mciwave, "MCI_SET_AUDIO_LEFT !\n");
728         if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
729             TRACE(mciwave, "MCI_SET_AUDIO_RIGHT !\n");
730     }
731     if (dwFlags & MCI_WAVE_INPUT) 
732         TRACE(mciwave, "MCI_WAVE_INPUT !\n");
733     if (dwFlags & MCI_WAVE_OUTPUT) 
734         TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
735     if (dwFlags & MCI_WAVE_SET_ANYINPUT) 
736         TRACE(mciwave, "MCI_WAVE_SET_ANYINPUT !\n");
737     if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) 
738         TRACE(mciwave, "MCI_WAVE_SET_ANYOUTPUT !\n");
739     if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) 
740         TRACE(mciwave, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
741     if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) 
742         TRACE(mciwave, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
743     if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) 
744         TRACE(mciwave, "MCI_WAVE_SET_BLOCKALIGN !\n");
745     if (dwFlags & MCI_WAVE_SET_CHANNELS) 
746         TRACE(mciwave, "MCI_WAVE_SET_CHANNELS !\n");
747     if (dwFlags & MCI_WAVE_SET_FORMATTAG) 
748         TRACE(mciwave, "MCI_WAVE_SET_FORMATTAG !\n");
749     if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) 
750         TRACE(mciwave, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
751     return 0;
752 }
753
754 /**************************************************************************
755  *                              WAVE_mciStatus          [internal]
756  */
757 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
758 {
759     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
760     
761     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
762     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
763     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
764     
765     if (dwFlags & MCI_STATUS_ITEM) {
766         switch(lpParms->dwItem) {
767         case MCI_STATUS_CURRENT_TRACK:
768             lpParms->dwReturn = 1;
769             TRACE(mciwave, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
770             break;
771         case MCI_STATUS_LENGTH:
772             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
773             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
774             TRACE(mciwave, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
775             break;
776         case MCI_STATUS_MODE:
777             lpParms->dwReturn = wmw->dwStatus;
778             TRACE(mciwave, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
779             break;
780         case MCI_STATUS_MEDIA_PRESENT:
781             TRACE(mciwave, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
782             lpParms->dwReturn = TRUE;
783             break;
784         case MCI_STATUS_NUMBER_OF_TRACKS:
785             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
786             lpParms->dwReturn = 1;
787             TRACE(mciwave, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
788             break;
789         case MCI_STATUS_POSITION:
790             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
791             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, 
792                                                              (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
793             TRACE(mciwave, "MCI_STATUS_POSITION %s => %lu\n", 
794                   (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
795             break;
796         case MCI_STATUS_READY:
797             lpParms->dwReturn = (wmw->dwStatus != MCI_MODE_NOT_READY);
798             TRACE(mciwave, "MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
799             break;
800         case MCI_STATUS_TIME_FORMAT:
801             lpParms->dwReturn = wmw->dwMciTimeFormat;
802             TRACE(mciwave, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
803             break;
804         case MCI_WAVE_INPUT:
805             TRACE(mciwave, "MCI_WAVE_INPUT !\n");
806             lpParms->dwReturn = 0;
807             break;
808         case MCI_WAVE_OUTPUT:
809             TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
810             lpParms->dwReturn = 0;
811             break;
812         case MCI_WAVE_STATUS_AVGBYTESPERSEC:
813             lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
814             TRACE(mciwave, "MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
815             break;
816         case MCI_WAVE_STATUS_BITSPERSAMPLE:
817             lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
818             TRACE(mciwave, "MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
819             break;
820         case MCI_WAVE_STATUS_BLOCKALIGN:
821             lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
822             TRACE(mciwave, "MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
823             break;
824         case MCI_WAVE_STATUS_CHANNELS:
825             lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
826             TRACE(mciwave, "MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
827             break;
828         case MCI_WAVE_STATUS_FORMATTAG:
829             lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
830             TRACE(mciwave, "MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
831             break;
832         case MCI_WAVE_STATUS_LEVEL:
833             TRACE(mciwave, "MCI_WAVE_STATUS_LEVEL !\n");
834             lpParms->dwReturn = 0xAAAA5555;
835             break;
836         case MCI_WAVE_STATUS_SAMPLESPERSEC:
837             lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
838             TRACE(mciwave, "MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
839             break;
840         default:
841             WARN(mciwave, "unknown command %08lX !\n", lpParms->dwItem);
842             return MCIERR_UNRECOGNIZED_COMMAND;
843         }
844     }
845     if (dwFlags & MCI_NOTIFY) {
846         TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
847         mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback), 
848                           wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
849     }
850     return 0;
851 }
852
853 /**************************************************************************
854  *                              WAVE_mciGetDevCaps              [internal]
855  */
856 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags, 
857                                 LPMCI_GETDEVCAPS_PARMS lpParms)
858 {
859     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
860     
861     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
862     
863     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
864     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
865     
866     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
867         switch(lpParms->dwItem) {
868         case MCI_GETDEVCAPS_DEVICE_TYPE:
869             lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
870             break;
871         case MCI_GETDEVCAPS_HAS_AUDIO:
872             lpParms->dwReturn = TRUE;
873             break;
874         case MCI_GETDEVCAPS_HAS_VIDEO:
875             lpParms->dwReturn = FALSE;
876             break;
877         case MCI_GETDEVCAPS_USES_FILES:
878             lpParms->dwReturn = TRUE;
879             break;
880         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
881             lpParms->dwReturn = TRUE;
882             break;
883         case MCI_GETDEVCAPS_CAN_RECORD:
884             lpParms->dwReturn = TRUE;
885             break;
886         case MCI_GETDEVCAPS_CAN_EJECT:
887             lpParms->dwReturn = FALSE;
888             break;
889         case MCI_GETDEVCAPS_CAN_PLAY:
890             lpParms->dwReturn = TRUE;
891             break;
892         case MCI_GETDEVCAPS_CAN_SAVE:
893             lpParms->dwReturn = TRUE;
894             break;
895         case MCI_WAVE_GETDEVCAPS_INPUTS:
896             lpParms->dwReturn = 1;
897             break;
898         case MCI_WAVE_GETDEVCAPS_OUTPUTS:
899             lpParms->dwReturn = 1;
900             break;
901         default:
902             TRACE(mciwave, "Unknown capability (%08lx) !\n", lpParms->dwItem);
903             return MCIERR_UNRECOGNIZED_COMMAND;
904         }
905     }
906     return 0;
907 }
908
909 /**************************************************************************
910  *                              WAVE_mciInfo                    [internal]
911  */
912 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
913 {
914     DWORD               ret = 0;
915     LPCSTR              str = 0;
916     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
917     
918     TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
919     
920     if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
921         ret = MCIERR_NULL_PARAMETER_BLOCK;
922     } else if (wmw == NULL) {
923         ret = MCIERR_INVALID_DEVICE_ID;
924     } else {
925         TRACE(mciwave, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
926         
927         switch(dwFlags) {
928         case MCI_INFO_PRODUCT:
929             str = "Wine's audio player";
930             break;
931         case MCI_INFO_FILE:
932             str = wmw->openParms.lpstrElementName;
933             break;
934         case MCI_WAVE_INPUT:
935             str = "Wine Wave In";
936             break;
937         case MCI_WAVE_OUTPUT:
938             str = "Wine Wave Out";
939             break;
940         default:
941             WARN(mciwave, "Don't know this info command (%lu)\n", dwFlags);
942             ret = MCIERR_UNRECOGNIZED_COMMAND;
943         }
944     }
945     if (str) {
946         if (strlen(str) + 1 > lpParms->dwRetSize) {
947             ret = MCIERR_PARAM_OVERFLOW;
948         } else {
949             lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
950         }
951     } else {
952         lpParms->lpstrReturn[0] = 0;
953     }
954     
955     return ret;
956 }
957
958 /**************************************************************************
959  *                              MCIWAVE_DriverProc              [sample driver]
960  */
961 LONG MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
962                         DWORD dwParam1, DWORD dwParam2)
963 {
964     TRACE(mciwave, "(%08lX, %04X, %08lX, %08lX, %08lX)\n", 
965           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
966     
967     switch(wMsg) {
968     case DRV_LOAD:              return 1;
969     case DRV_FREE:              return 1;
970     case DRV_OPEN:              return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
971     case DRV_CLOSE:             return WAVE_drvClose(dwDevID);
972     case DRV_ENABLE:            return 1;
973     case DRV_DISABLE:           return 1;
974     case DRV_QUERYCONFIGURE:    return 1;
975     case DRV_CONFIGURE:         MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK);      return 1;
976     case DRV_INSTALL:           return DRVCNF_RESTART;
977     case DRV_REMOVE:            return DRVCNF_RESTART;
978     case MCI_OPEN_DRIVER:       return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA)  dwParam2);
979     case MCI_CLOSE_DRIVER:      return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
980     case MCI_CUE:               return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
981     case MCI_PLAY:              return WAVE_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)        dwParam2);
982     case MCI_RECORD:            return WAVE_mciRecord    (dwDevID, dwParam1, (LPMCI_RECORD_PARMS)      dwParam2);
983     case MCI_STOP:              return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
984     case MCI_SET:               return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_SET_PARMS)         dwParam2);
985     case MCI_PAUSE:             return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
986     case MCI_RESUME:            return WAVE_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
987     case MCI_STATUS:            return WAVE_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)      dwParam2);
988     case MCI_GETDEVCAPS:        return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)  dwParam2);
989     case MCI_INFO:              return WAVE_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMS16)      dwParam2);
990     case MCI_SEEK:              return WAVE_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)        dwParam2);               
991     case MCI_LOAD:              
992     case MCI_SAVE:              
993     case MCI_FREEZE:            
994     case MCI_PUT:               
995     case MCI_REALIZE:           
996     case MCI_UNFREEZE:          
997     case MCI_UPDATE:            
998     case MCI_WHERE:             
999     case MCI_WINDOW:            
1000     case MCI_STEP:              
1001     case MCI_SPIN:              
1002     case MCI_ESCAPE:            
1003     case MCI_COPY:              
1004     case MCI_CUT:               
1005     case MCI_DELETE:            
1006     case MCI_PASTE:             
1007         WARN(mciwave, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
1008         break;
1009     case MCI_OPEN:
1010     case MCI_CLOSE:
1011         FIXME(mciwave, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
1012         break;
1013     default:
1014         FIXME(mciwave, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
1015         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1016     }
1017     return MCIERR_UNRECOGNIZED_COMMAND;
1018 }