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