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