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