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