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