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