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