Added mappings for a few messages.
[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,2000 Eric Pouech
7  *              2000 Francois Jacques
8  */
9
10 #include "winerror.h"
11 #include "windef.h"
12 #include "winbase.h"
13 #include "wingdi.h"
14 #include "winuser.h"
15 #include "mmddk.h"
16 #include "digitalv.h"
17 #include "debugtools.h"
18
19 DEFAULT_DEBUG_CHANNEL(mciwave);
20
21 typedef struct {
22     UINT                        wDevID;
23     HANDLE                      hWave;
24     int                         nUseCount;      /* Incremented for each shared open */
25     BOOL                        fShareable;     /* TRUE if first open was shareable */
26     HMMIO                       hFile;          /* mmio file handle open as Element */
27     MCI_WAVE_OPEN_PARMSA        openParms;
28     WAVEFORMATEX                wfxRef;
29     LPWAVEFORMATEX              lpWaveFormat;
30     BOOL                        fInput;         /* FALSE = Output, TRUE = Input */
31     volatile WORD               dwStatus;       /* one from MCI_MODE_xxxx */
32     DWORD                       dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
33     DWORD                       dwRemaining;    /* remaining bytes to play or record */
34     DWORD                       dwPosition;     /* position in bytes in chunk */
35     HANDLE                      hEvent;         /* for synchronization */
36     DWORD                       dwEventCount;   /* for synchronization */
37     BOOL                        bTemporaryFile; /* temporary file (MCI_RECORD) */
38     MMCKINFO                    ckMainRIFF;     /* main RIFF chunk */
39     MMCKINFO                    ckWaveData;     /* data chunk */
40 } WINE_MCIWAVE;
41
42 /* ===================================================================
43  * ===================================================================
44  * FIXME: should be using the new mmThreadXXXX functions from WINMM
45  * instead of those
46  * it would require to add a wine internal flag to mmThreadCreate
47  * in order to pass a 32 bit function instead of a 16 bit one
48  * ===================================================================
49  * =================================================================== */
50
51 struct SCA {
52     UINT        wDevID;
53     UINT        wMsg;
54     DWORD       dwParam1;
55     DWORD       dwParam2;
56 };
57
58 /**************************************************************************
59  *                              MCI_SCAStarter                  [internal]
60  */
61 static DWORD CALLBACK   MCI_SCAStarter(LPVOID arg)
62 {
63     struct SCA* sca = (struct SCA*)arg;
64     DWORD               ret;
65
66     TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
67           sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
68     ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
69     TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
70           sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
71     HeapFree(GetProcessHeap(), 0, sca);
72     ExitThread(ret);
73     WARN("Should not happen ? what's wrong \n");
74     /* should not go after this point */
75     return ret;
76 }
77
78 /**************************************************************************
79  *                              MCI_SendCommandAsync            [internal]
80  */
81 static  DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1, 
82                                    DWORD dwParam2, UINT size)
83 {
84     struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
85
86     if (sca == 0)
87         return MCIERR_OUT_OF_MEMORY;
88
89     sca->wDevID   = wDevID;
90     sca->wMsg     = wMsg;
91     sca->dwParam1 = dwParam1;
92     
93     if (size && dwParam2) {
94         sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
95         /* copy structure passed by program in dwParam2 to be sure 
96          * we can still use it whatever the program does 
97          */
98         memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
99     } else {
100         sca->dwParam2 = dwParam2;
101     }
102
103     if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
104         WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
105         return MCI_SCAStarter(&sca);
106     }
107     return 0;
108 }
109
110 /*======================================================================*
111  *                          MCI WAVE implemantation                     *
112  *======================================================================*/
113
114 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
115
116 /**************************************************************************
117  *                              MCIWAVE_drvOpen                 [internal]      
118  */
119 static  DWORD   WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
120 {
121     WINE_MCIWAVE*       wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
122
123     if (!wmw)
124         return 0;
125
126     wmw->wDevID = modp->wDeviceID;
127     mciSetDriverData(wmw->wDevID, (DWORD)wmw);
128     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
129     modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
130
131     wmw->wfxRef.wFormatTag      = WAVE_FORMAT_PCM; 
132     wmw->wfxRef.nChannels       = 1;      /* MONO */
133     wmw->wfxRef.nSamplesPerSec  = 11025;
134     wmw->wfxRef.nAvgBytesPerSec = 11025;
135     wmw->wfxRef.nBlockAlign     = 1; 
136     wmw->wfxRef.wBitsPerSample  = 8;
137     wmw->wfxRef.cbSize          = 0;      /* don't care */
138
139     return modp->wDeviceID;
140 }
141
142 /**************************************************************************
143  *                              MCIWAVE_drvClose                [internal]      
144  */
145 static  DWORD   WAVE_drvClose(DWORD dwDevID)
146 {
147     WINE_MCIWAVE*  wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
148
149     if (wmw) {
150         HeapFree(GetProcessHeap(), 0, wmw);     
151         mciSetDriverData(dwDevID, 0);
152         return 1;
153     }
154     return 0;
155 }
156
157 /**************************************************************************
158  *                              WAVE_mciGetOpenDev              [internal]      
159  */
160 static WINE_MCIWAVE*  WAVE_mciGetOpenDev(UINT wDevID)
161 {
162     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
163     
164     if (wmw == NULL || wmw->nUseCount == 0) {
165         WARN("Invalid wDevID=%u\n", wDevID);
166         return 0;
167     }
168     return wmw;
169 }
170
171 /**************************************************************************
172  *                              WAVE_ConvertByteToTimeFormat    [internal]      
173  */
174 static  DWORD   WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
175 {
176     DWORD          ret = 0;
177      
178     switch (wmw->dwMciTimeFormat) {
179     case MCI_FORMAT_MILLISECONDS:
180         ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
181         break;
182     case MCI_FORMAT_BYTES:
183         ret = val;
184         break;
185     case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
186         ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
187         break;
188     default:
189         WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
190     }
191     TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
192     *lpRet = 0;
193     return ret;
194 }
195
196 /**************************************************************************
197  *                              WAVE_ConvertTimeFormatToByte    [internal]      
198  */
199 static  DWORD   WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
200 {
201     DWORD       ret = 0;
202     
203     switch (wmw->dwMciTimeFormat) {
204     case MCI_FORMAT_MILLISECONDS:
205         ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
206         break;
207     case MCI_FORMAT_BYTES:
208         ret = val;
209         break;
210     case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
211         ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
212         break;
213     default:
214         WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
215     }
216     TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
217     return ret;
218 }
219
220 /**************************************************************************
221  *                      WAVE_mciReadFmt                         [internal]
222  */
223 static  DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
224 {
225     MMCKINFO    mmckInfo;
226     long        r;
227
228     mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
229     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
230         return MCIERR_INVALID_FILE;
231     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
232           (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
233
234     wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
235     r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
236     if (r < sizeof(WAVEFORMAT))
237         return MCIERR_INVALID_FILE;
238     
239     TRACE("wFormatTag=%04X !\n",   wmw->lpWaveFormat->wFormatTag);
240     TRACE("nChannels=%d \n",       wmw->lpWaveFormat->nChannels);
241     TRACE("nSamplesPerSec=%ld\n",  wmw->lpWaveFormat->nSamplesPerSec);
242     TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
243     TRACE("nBlockAlign=%d \n",     wmw->lpWaveFormat->nBlockAlign);
244     TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
245     if (r >= (long)sizeof(WAVEFORMATEX))
246         TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
247         
248     mmioAscend(wmw->hFile, &mmckInfo, 0);
249     wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
250     if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
251         TRACE("can't find data chunk\n");
252         return MCIERR_INVALID_FILE;
253     }
254     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
255           (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
256     TRACE("nChannels=%d nSamplesPerSec=%ld\n",
257           wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
258     
259     return 0;
260 }
261
262 /**************************************************************************
263  *                      WAVE_mciCreateRIFFSkeleton              [internal]
264  */
265 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
266 {
267    MMCKINFO     ckWaveFormat;
268
269    LPMMCKINFO     lpckRIFF     = &(wmw->ckMainRIFF);
270    LPMMCKINFO     lpckWaveData = &(wmw->ckWaveData);
271    LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
272
273
274    HMMIO     hmmio      = wmw->hFile;
275
276    lpckRIFF->ckid    = FOURCC_RIFF;
277    lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
278    lpckRIFF->cksize  = 0;
279
280    if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
281         goto err;
282
283    ckWaveFormat.fccType = 0;
284    ckWaveFormat.ckid    = mmioFOURCC('f', 'm', 't', ' ');
285    ckWaveFormat.cksize  = 16;
286
287    if (!lpWaveFormat)
288    {
289        /* FIXME: for non PCM formats, the size of the waveFormat has to be
290         * gotten
291         */
292         lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
293
294         memcpy(lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
295    }
296
297    if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
298         goto err;
299
300    /* only the first 16 bytes are serialized */
301    /* wrong... for non PCM, the whole waveFormat is stored
302     */
303    if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))       
304         goto err;               
305
306    if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
307         goto err;
308
309    lpckWaveData->cksize  = 0;
310    lpckWaveData->fccType = 0;
311    lpckWaveData->ckid    = mmioFOURCC('d', 'a', 't', 'a');
312    
313    /* create data chunk */
314    if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
315         goto err;
316
317    return 0;
318
319 err:    
320    HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
321    return MCIERR_INVALID_FILE;
322 }
323
324 /**************************************************************************
325  *                      WAVE_mciOpen                            [internal]
326  */
327 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
328 {
329     DWORD               dwRet = 0;
330     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
331     CHAR*               pszTmpFileName = 0;
332     
333     TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
334     if (lpOpenParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
335     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
336
337     if (dwFlags & MCI_OPEN_SHAREABLE)
338         return MCIERR_HARDWARE;
339     
340     if (wmw->nUseCount > 0) {
341         /* The driver is already opened on this channel
342          * Wave driver cannot be shared
343          */
344         return MCIERR_DEVICE_OPEN;
345     }
346
347     wmw->nUseCount++;
348     
349     wmw->fInput = FALSE;
350     wmw->hWave = 0;
351     wmw->dwStatus = MCI_MODE_NOT_READY; 
352
353     TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
354     
355     if (dwFlags & MCI_OPEN_ELEMENT) {
356         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
357             /* could it be that (DWORD)lpOpenParms->lpstrElementName 
358              * contains the hFile value ? 
359              */
360             dwRet = MCIERR_UNRECOGNIZED_COMMAND;
361         } else {
362             if (strlen(lpOpenParms->lpstrElementName) > 0) {
363                 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
364
365                 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
366                 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
367
368                 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
369                     wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL, 
370                                     MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
371
372                     if (wmw->hFile == 0) {
373                         WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
374                         dwRet = MCIERR_FILE_NOT_FOUND;
375                     }
376                     else
377                     {
378                         LPMMCKINFO          lpckMainRIFF = &wmw->ckMainRIFF;
379
380                         /* make sure we're are the beginning of the file */
381                         mmioSeek(wmw->hFile, 0, SEEK_SET);
382
383                         /* first reading of this file. read the waveformat chunk */
384                         if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
385                             dwRet = MCIERR_INVALID_FILE;
386                         } else {
387                             TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
388                                 (LPSTR)&(lpckMainRIFF->ckid), 
389                                 (LPSTR) &(lpckMainRIFF->fccType), 
390                                 (lpckMainRIFF->cksize));
391
392                             if ((lpckMainRIFF->ckid    != FOURCC_RIFF) || 
393                                  lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
394                                 dwRet = MCIERR_INVALID_FILE;
395                             } else {
396                                 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
397                             }
398                         }   
399                     }
400                 } 
401                 else {
402                     wmw->hFile = 0;
403                 }
404             } 
405             else {
406                 CHAR  szTmpPath[MAX_PATH];
407                 CHAR  szPrefix[4]    = "TMP\0";
408                 
409                 pszTmpFileName = HeapAlloc(GetProcessHeap(),
410                                            HEAP_ZERO_MEMORY, 
411                                            MAX_PATH * sizeof(*pszTmpFileName));
412                 
413                 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
414                     WARN("can't retrieve temp path!\n");
415                     HeapFree(GetProcessHeap(), 0, pszTmpFileName);
416                     return MCIERR_FILE_NOT_FOUND;
417                 }
418
419                 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
420                         WARN("can't retrieve temp file name!\n");
421                         HeapFree(GetProcessHeap(), 0, pszTmpFileName);
422                         return MCIERR_FILE_NOT_FOUND;
423                 }
424
425                 wmw->bTemporaryFile = TRUE;
426     
427                 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
428             
429                 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
430
431                     wmw->hFile = mmioOpenA(pszTmpFileName, NULL, 
432                                            MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
433                 
434                     if (wmw->hFile == 0) {
435                         /* temporary file could not be created. clean filename. */
436                         HeapFree(GetProcessHeap(), 0, pszTmpFileName);
437                         WARN("can't create file='%s' !\n", pszTmpFileName);
438                         dwRet = MCIERR_FILE_NOT_FOUND;
439                     }
440                 }
441             } 
442         }
443     }
444
445     TRACE("hFile=%u\n", wmw->hFile);
446     
447     memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
448
449     if (wmw->bTemporaryFile == TRUE)
450     {
451             /* Additional openParms is temporary file's name */
452             wmw->openParms.lpstrElementName = pszTmpFileName;
453     }
454     
455     if (dwRet == 0) {
456         if (wmw->lpWaveFormat) {
457             switch (wmw->lpWaveFormat->wFormatTag) {
458             case WAVE_FORMAT_PCM:
459                 if (wmw->lpWaveFormat->nAvgBytesPerSec != 
460                     wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
461                     WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n", 
462                         wmw->lpWaveFormat->nAvgBytesPerSec, 
463                         wmw->lpWaveFormat->nSamplesPerSec * 
464                          wmw->lpWaveFormat->nBlockAlign);
465                     wmw->lpWaveFormat->nAvgBytesPerSec = 
466                         wmw->lpWaveFormat->nSamplesPerSec * 
467                         wmw->lpWaveFormat->nBlockAlign;
468                 }
469                 break;
470             }
471         }
472         wmw->dwPosition = 0;
473         
474         wmw->dwStatus = MCI_MODE_STOP;
475     } else {
476         wmw->nUseCount--;
477         if (wmw->hFile != 0)
478             mmioClose(wmw->hFile, 0);
479         wmw->hFile = 0;
480     }
481     return dwRet;
482 }
483
484 /**************************************************************************
485  *                               WAVE_mciCue             [internal]
486  */
487 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
488 {
489     /*
490       FIXME
491       
492       This routine is far from complete. At the moment only a check is done on the
493       MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
494       is the default.
495       
496       The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
497       are ignored
498     */
499     
500     DWORD               dwRet;
501     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
502     
503     FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
504     
505     if (wmw == NULL)    return MCIERR_INVALID_DEVICE_ID;
506     
507     /* always close elements ? */    
508     if (wmw->hFile != 0) {
509         mmioClose(wmw->hFile, 0);
510         wmw->hFile = 0;
511     }
512     
513     dwRet = MMSYSERR_NOERROR;  /* assume success */
514     
515     if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
516         dwRet = waveOutClose(wmw->hWave);
517         if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
518         wmw->fInput = TRUE;
519     } else if (wmw->fInput) {
520         dwRet = waveInClose(wmw->hWave);
521         if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
522         wmw->fInput = FALSE;
523     }
524     wmw->hWave = 0;
525     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
526 }
527
528 /**************************************************************************
529  *                              WAVE_mciStop                    [internal]
530  */
531 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
532 {
533     DWORD               dwRet = 0;
534     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
535     
536     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
537     
538     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
539     
540     /* wait for playback thread (if any) to exit before processing further */
541     switch (wmw->dwStatus) {
542     case MCI_MODE_PAUSE:
543     case MCI_MODE_PLAY:
544     case MCI_MODE_RECORD:
545         {
546             int oldStat = wmw->dwStatus;
547             wmw->dwStatus = MCI_MODE_NOT_READY;
548             if (oldStat == MCI_MODE_PAUSE)
549                 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
550         }
551         while (wmw->dwStatus != MCI_MODE_STOP)
552             Sleep(10);
553         break;
554     }
555
556     wmw->dwPosition = 0;
557
558     /* sanity resets */
559     wmw->dwStatus = MCI_MODE_STOP;
560     
561     if ((dwFlags & MCI_NOTIFY) && lpParms) {
562         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
563                         wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
564     }
565     
566     return dwRet;
567 }
568
569 /**************************************************************************
570  *                              WAVE_mciClose           [internal]
571  */
572 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
573 {
574     DWORD               dwRet = 0;
575     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
576     
577     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
578     
579     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
580     
581     if (wmw->dwStatus != MCI_MODE_STOP) {
582         dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
583     }
584     
585     wmw->nUseCount--;
586     
587     if (wmw->nUseCount == 0) {
588         if (wmw->hFile != 0) {
589             mmioClose(wmw->hFile, 0);
590             wmw->hFile = 0;
591         }
592     }
593
594     /* That string got allocated in mciOpen because no filename was specified
595      * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
596      * allocated by mciOpen, *NOT* the application.
597      */
598     if (wmw->bTemporaryFile)
599     {
600         HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
601         wmw->openParms.lpstrElementName = NULL;
602     }
603     
604     HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
605     wmw->lpWaveFormat = NULL;
606
607     if ((dwFlags & MCI_NOTIFY) && lpParms) {
608         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
609                         wmw->openParms.wDeviceID,
610                         (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
611     }
612
613     return 0;
614 }
615
616 /**************************************************************************
617  *                              WAVE_mciPlayCallback            [internal]
618  */
619 static  void    CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg, 
620                                               DWORD dwInstance,  
621                                               DWORD dwParam1, DWORD dwParam2)
622 {
623     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)dwInstance;
624
625     switch (uMsg) {
626     case WOM_OPEN:
627     case WOM_CLOSE:
628         break;
629     case WOM_DONE:
630         InterlockedIncrement(&wmw->dwEventCount);
631         TRACE("Returning waveHdr=%lx\n", dwParam1);
632         SetEvent(wmw->hEvent);
633         break;
634     default:
635         ERR("Unknown uMsg=%d\n", uMsg);
636     }
637 }
638
639 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
640 {
641     for (;;) {
642         ResetEvent(wmw->hEvent);
643         if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
644             break;
645         }
646         InterlockedIncrement(&wmw->dwEventCount);
647         
648         WaitForSingleObject(wmw->hEvent, INFINITE);
649     }
650 }
651
652 /**************************************************************************
653  *                              WAVE_mciPlay            [internal]
654  */
655 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
656 {
657     DWORD               end;
658     LONG                bufsize, count, left;
659     DWORD               dwRet = 0;
660     LPWAVEHDR           waveHdr = NULL;
661     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
662     int                 whidx;
663     
664     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
665     
666     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
667     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
668
669     /* FIXME : since there is no way to determine in which mode the device is 
670      * open (recording/playback) automatically switch from a mode to another
671      */
672     wmw->fInput = FALSE;
673
674     if (wmw->fInput) {
675         WARN("cannot play on input device\n");
676         return MCIERR_NONAPPLICABLE_FUNCTION;
677     }
678
679     if (wmw->hFile == 0) {
680         WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
681         return MCIERR_FILE_NOT_FOUND;
682     }
683     
684     if (wmw->dwStatus == MCI_MODE_PAUSE) {
685         /* FIXME: parameters (start/end) in lpParams may not be used */
686         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
687     }
688     
689     /** This function will be called again by a thread when async is used.
690      * We have to set MCI_MODE_PLAY before we do this so that the app can spin
691      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
692      */
693     if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
694         return MCIERR_INTERNAL;
695     }
696
697     wmw->dwStatus = MCI_MODE_PLAY;
698     
699     if (!(dwFlags & MCI_WAIT)) {
700         return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags, 
701                                     (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
702     }
703
704     end = 0xFFFFFFFF;
705     if (lpParms && (dwFlags & MCI_FROM)) {
706         wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); 
707     }
708     if (lpParms && (dwFlags & MCI_TO)) {
709         end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
710     }
711     
712     TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
713     
714     if (end <= wmw->dwPosition)
715         return TRUE;
716
717
718 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
719 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
720
721     wmw->dwPosition        = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
722     wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
723
724     if (dwRet == 0) {
725         if (wmw->lpWaveFormat) {
726             switch (wmw->lpWaveFormat->wFormatTag) {
727             case WAVE_FORMAT_PCM:
728                 if (wmw->lpWaveFormat->nAvgBytesPerSec != 
729                     wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
730                     WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n", 
731                         wmw->lpWaveFormat->nAvgBytesPerSec, 
732                         wmw->lpWaveFormat->nSamplesPerSec * 
733                          wmw->lpWaveFormat->nBlockAlign);
734                     wmw->lpWaveFormat->nAvgBytesPerSec = 
735                         wmw->lpWaveFormat->nSamplesPerSec * 
736                         wmw->lpWaveFormat->nBlockAlign;
737                 }
738                 break;
739             }
740         }
741     } else {
742         TRACE("can't retrieve wave format %ld\n", dwRet);
743         goto cleanUp;
744     }
745     
746         
747     /* go back to begining of chunk plus the requested position */
748     /* FIXME: I'm not sure this is correct, notably because some data linked to 
749      * the decompression state machine will not be correcly initialized.
750      * try it this way (other way would be to decompress from 0 up to dwPosition
751      * and to start sending to hWave when dwPosition is reached)
752      */
753     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
754
755     /* By default the device will be opened for output, the MCI_CUE function is there to
756      * change from output to input and back
757      */
758     /* FIXME: how to choose between several output channels ? here mapper is forced */
759     dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, 
760                         (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
761
762     if (dwRet != 0) {
763         TRACE("Can't open low level audio device %ld\n", dwRet);
764         dwRet = MCIERR_DEVICE_OPEN;
765         wmw->hWave = 0;
766         goto cleanUp;
767     }
768
769     /* make it so that 3 buffers per second are needed */
770     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
771
772     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
773     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
774     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
775     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
776     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
777     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
778     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
779     if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 
780         waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
781         dwRet = MCIERR_INTERNAL;
782         goto cleanUp;
783     }
784
785     whidx = 0;
786     left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
787     wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
788     wmw->dwEventCount = 1L; /* for first buffer */
789
790     TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
791     
792     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
793     while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
794         count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
795         TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
796         if (count < 1) 
797             break;
798         /* count is always <= bufsize, so this is correct regarding the 
799          * waveOutPrepareHeader function 
800          */
801         waveHdr[whidx].dwBufferLength = count;
802         waveHdr[whidx].dwFlags &= ~WHDR_DONE;
803         TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
804               &waveHdr[whidx], waveHdr[whidx].dwBufferLength, 
805               waveHdr[whidx].dwBytesRecorded);
806         dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
807         left -= count;
808         wmw->dwPosition += count;
809         TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
810
811         WAVE_mciPlayWaitDone(wmw);
812         whidx ^= 1;
813     }
814
815     WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
816
817     /* just to get rid of some race conditions between play, stop and pause */
818     waveOutReset(wmw->hWave);
819
820     waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
821     waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
822
823     dwRet = 0;
824
825 cleanUp:    
826     HeapFree(GetProcessHeap(), 0, waveHdr);
827
828     if (wmw->hWave) {
829         waveOutClose(wmw->hWave);
830         wmw->hWave = 0;
831     }
832     CloseHandle(wmw->hEvent);
833
834     if (lpParms && (dwFlags & MCI_NOTIFY)) {
835         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
836                         wmw->openParms.wDeviceID, 
837                         dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
838     }
839
840     wmw->dwStatus = MCI_MODE_STOP;
841
842     return dwRet;
843 }
844
845 /**************************************************************************
846  *                              WAVE_mciPlayCallback            [internal]
847  */
848 static  void    CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg, 
849                                               DWORD dwInstance,  
850                                               DWORD dwParam1, DWORD dwParam2)
851 {
852     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)dwInstance;
853     LPWAVEHDR           lpWaveHdr = NULL; 
854     LONG                count = 0; 
855     switch (uMsg) {
856     case WIM_OPEN:
857     case WIM_CLOSE:
858         break;
859     case WIM_DATA:
860         lpWaveHdr = (LPWAVEHDR) dwParam1;
861
862         InterlockedIncrement(&wmw->dwEventCount);
863
864         count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
865
866         lpWaveHdr->dwFlags &= ~WHDR_DONE;
867         wmw->dwPosition  += count;
868         wmw->dwRemaining -= count;      
869
870         if (wmw->dwStatus == MCI_MODE_RECORD)
871         {
872            /* Only queue up another buffer if we are recording.  We could receive this
873               message also when waveInReset() is called, since it notifies on all wave
874               buffers that are outstanding.  Queueing up more sometimes causes waveInClose
875               to fail. */ 
876            waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
877            TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
878         }
879         
880         SetEvent(wmw->hEvent);
881         break;
882     default:
883         ERR("Unknown uMsg=%d\n", uMsg);
884     }
885 }
886
887 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
888 {
889     for (;;) {
890         ResetEvent(wmw->hEvent);
891         if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
892             break;
893         }
894         InterlockedIncrement(&wmw->dwEventCount);
895         
896         WaitForSingleObject(wmw->hEvent, INFINITE);
897     }
898 }
899
900 /**************************************************************************
901  *                              WAVE_mciRecord                  [internal]
902  */
903 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
904 {
905     DWORD               end;
906     DWORD               dwRet = 0;
907     LONG                bufsize; 
908     LPWAVEHDR           waveHdr = NULL;
909     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
910
911
912     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
913     
914     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
915     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
916  
917     /* FIXME : since there is no way to determine in which mode the device is 
918      * open (recording/playback) automatically switch from a mode to another
919      */
920     wmw->fInput = TRUE;
921
922     if (!wmw->fInput) {
923         WARN("cannot record on output device\n");
924         return MCIERR_NONAPPLICABLE_FUNCTION;
925     }
926
927     if (wmw->dwStatus == MCI_MODE_PAUSE) {
928         /* FIXME: parameters (start/end) in lpParams may not be used */
929         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
930     }
931     
932     /** This function will be called again by a thread when async is used.
933      * We have to set MCI_MODE_PLAY before we do this so that the app can spin
934      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
935      */
936     if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
937         return MCIERR_INTERNAL;
938     }
939
940     wmw->dwStatus = MCI_MODE_RECORD;
941     
942     if (!(dwFlags & MCI_WAIT)) { 
943         return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags, 
944                                     (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
945     }
946
947     if (!wmw->lpWaveFormat)
948     {
949             /* new RIFF file */
950             dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
951     } else 
952     {
953         FIXME("Should descend into data chunk. Please report.\n");
954     }
955   
956     end = 0xFFFFFFFF;
957     if (lpParms && (dwFlags & MCI_FROM)) {
958         wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom); 
959     }
960
961     if (lpParms && (dwFlags & MCI_TO)) {
962         end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
963     }
964
965     TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
966
967     if (end <= wmw->dwPosition)
968     {
969         return TRUE;
970     }
971
972 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
973 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
974
975     wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
976     wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
977
978     /* go back to begining of chunk plus the requested position */
979     /* FIXME: I'm not sure this is correct, notably because some data linked to 
980      * the decompression state machine will not be correcly initialized.
981      * try it this way (other way would be to decompress from 0 up to dwPosition
982      * and to start sending to hWave when dwPosition is reached)
983      */
984     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
985
986     /* By default the device will be opened for output, the MCI_CUE function is there to
987      * change from output to input and back
988      */
989     /* FIXME: how to choose between several output channels ? here mapper is forced */
990     dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, 
991                         (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
992
993     if (dwRet != 0) {
994         TRACE("Can't open low level audio device %ld\n", dwRet);
995         dwRet = MCIERR_DEVICE_OPEN;
996         wmw->hWave = 0;
997         goto cleanUp;
998     }
999
1000     /* make it so that 3 buffers per second are needed */
1001     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1002
1003     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1004     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1005     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1006     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
1007     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
1008     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
1009     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1010
1011     if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) || 
1012         waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1013         dwRet = MCIERR_INTERNAL;
1014         goto cleanUp;
1015     }
1016
1017     if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1018         waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1019         dwRet = MCIERR_INTERNAL;
1020         goto cleanUp;
1021     }
1022         
1023     wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1024     wmw->dwEventCount = 1L; /* for first buffer */
1025
1026     wmw->dwRemaining = end - wmw->dwPosition;
1027     
1028     TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1029
1030     dwRet = waveInStart(wmw->hWave); 
1031    
1032     while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1033         WAVE_mciRecordWaitDone(wmw);  
1034     }
1035
1036     /* needed so that the callback above won't add again the buffers returned by the reset */
1037     wmw->dwStatus = MCI_MODE_STOP;
1038    
1039     waveInReset(wmw->hWave);
1040
1041     waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1042     waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1043
1044     dwRet = 0;
1045
1046 cleanUp:    
1047     HeapFree(GetProcessHeap(), 0, waveHdr);
1048
1049     if (wmw->hWave) {
1050         waveInClose(wmw->hWave);
1051         wmw->hWave = 0;
1052     }
1053     CloseHandle(wmw->hEvent);
1054
1055     /* need to update the size of the data chunk */
1056     if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1057         TRACE("failed on ascend\n");
1058     }
1059
1060     if (lpParms && (dwFlags & MCI_NOTIFY)) {
1061         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
1062                         wmw->openParms.wDeviceID, 
1063                         dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1064     }
1065
1066     wmw->dwStatus = MCI_MODE_STOP;
1067
1068     return dwRet;
1069
1070 }
1071
1072 /**************************************************************************
1073  *                              WAVE_mciPause                   [internal]
1074  */
1075 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1076 {
1077     DWORD               dwRet;
1078     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1079     
1080     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1081     
1082     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1083     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1084     
1085     if (wmw->dwStatus == MCI_MODE_PLAY) {
1086         wmw->dwStatus = MCI_MODE_PAUSE;
1087     } 
1088     
1089     if (wmw->fInput)    dwRet = waveInStop(wmw->hWave);
1090     else                dwRet = waveOutPause(wmw->hWave);
1091     
1092     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1093 }
1094
1095 /**************************************************************************
1096  *                              WAVE_mciResume                  [internal]
1097  */
1098 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1099 {
1100     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1101     DWORD               dwRet = 0;
1102     
1103     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1104     
1105     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1106     
1107     if (wmw->dwStatus == MCI_MODE_PAUSE) {
1108         wmw->dwStatus = MCI_MODE_PLAY;
1109     } 
1110     
1111     if (wmw->fInput)    dwRet = waveInStart(wmw->hWave);
1112     else                dwRet = waveOutRestart(wmw->hWave);
1113     return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1114 }
1115
1116 /**************************************************************************
1117  *                              WAVE_mciSeek                    [internal]
1118  */
1119 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1120 {
1121     DWORD               ret = 0;
1122     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1123     
1124     TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1125     
1126     if (lpParms == NULL) {
1127         ret = MCIERR_NULL_PARAMETER_BLOCK;
1128     } else if (wmw == NULL) {
1129         ret = MCIERR_INVALID_DEVICE_ID;
1130     } else {
1131         WAVE_mciStop(wDevID, MCI_WAIT, 0);
1132         
1133         if (dwFlags & MCI_SEEK_TO_START) {
1134             wmw->dwPosition = 0;
1135         } else if (dwFlags & MCI_SEEK_TO_END) {
1136             wmw->dwPosition = wmw->ckWaveData.cksize;
1137         } else if (dwFlags & MCI_TO) {
1138             wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1139         } else {
1140             WARN("dwFlag doesn't tell where to seek to...\n");
1141             return MCIERR_MISSING_PARAMETER;
1142         }
1143         
1144         TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1145         
1146         if (dwFlags & MCI_NOTIFY) {
1147             mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
1148                             wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1149         }
1150     }
1151     return ret; 
1152 }    
1153
1154 /**************************************************************************
1155  *                              WAVE_mciSet                     [internal]
1156  */
1157 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1158 {
1159     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1160     
1161     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1162     
1163     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1164     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1165     
1166     if (dwFlags & MCI_SET_TIME_FORMAT) {
1167         switch (lpParms->dwTimeFormat) {
1168         case MCI_FORMAT_MILLISECONDS:
1169             TRACE("MCI_FORMAT_MILLISECONDS !\n");
1170             wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1171             break;
1172         case MCI_FORMAT_BYTES:
1173             TRACE("MCI_FORMAT_BYTES !\n");
1174             wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1175             break;
1176         case MCI_FORMAT_SAMPLES:
1177             TRACE("MCI_FORMAT_SAMPLES !\n");
1178             wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1179             break;
1180         default:
1181             WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1182             return MCIERR_BAD_TIME_FORMAT;
1183         }
1184     }
1185     if (dwFlags & MCI_SET_VIDEO) {
1186         TRACE("No support for video !\n");
1187         return MCIERR_UNSUPPORTED_FUNCTION;
1188     }
1189     if (dwFlags & MCI_SET_DOOR_OPEN) {
1190         TRACE("No support for door open !\n");
1191         return MCIERR_UNSUPPORTED_FUNCTION;
1192     }
1193     if (dwFlags & MCI_SET_DOOR_CLOSED) {
1194         TRACE("No support for door close !\n");
1195         return MCIERR_UNSUPPORTED_FUNCTION;
1196     }
1197     if (dwFlags & MCI_SET_AUDIO) {
1198         if (dwFlags & MCI_SET_ON) {
1199             TRACE("MCI_SET_ON audio !\n");
1200         } else if (dwFlags & MCI_SET_OFF) {
1201             TRACE("MCI_SET_OFF audio !\n");
1202         } else {
1203             WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1204             return MCIERR_BAD_INTEGER;
1205         }
1206         
1207         if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1208             TRACE("MCI_SET_AUDIO_ALL !\n");
1209         if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1210             TRACE("MCI_SET_AUDIO_LEFT !\n");
1211         if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1212             TRACE("MCI_SET_AUDIO_RIGHT !\n");
1213     }
1214     if (dwFlags & MCI_WAVE_INPUT) 
1215         TRACE("MCI_WAVE_INPUT !\n");
1216     if (dwFlags & MCI_WAVE_OUTPUT) 
1217         TRACE("MCI_WAVE_OUTPUT !\n");
1218     if (dwFlags & MCI_WAVE_SET_ANYINPUT) 
1219         TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1220     if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) 
1221         TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1222     if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1223         wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1224         TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1225     }
1226     if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1227         wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1228         TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1229     }
1230     if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1231         wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1232         TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1233     }
1234     if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1235         wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1236         TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1237     }
1238     if (dwFlags & MCI_WAVE_SET_FORMATTAG) { 
1239         wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1240         TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1241     }
1242     if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1243         wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1244         TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1245     }
1246     return 0;
1247 }
1248
1249 /**************************************************************************
1250  *                              WAVE_mciSave            [internal]
1251  */
1252 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1253 {
1254     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1255     DWORD               ret = MCIERR_FILE_NOT_SAVED, tmpRet; 
1256     WPARAM              wparam = MCI_NOTIFY_FAILURE;
1257
1258     TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1259     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1260     if (wmw     == NULL)        return MCIERR_INVALID_DEVICE_ID;
1261
1262     if (dwFlags & MCI_WAIT)
1263     {
1264         FIXME("MCI_WAIT not implemented\n");
1265     }
1266
1267     ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1268     ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1269
1270
1271     ret = mmioClose(wmw->hFile, 0);
1272     
1273     /*
1274       If the destination file already exists, it has to be overwritten.  (Behaviour
1275       verified in Windows (2000)).  If it doesn't overwrite, it is breaking one of
1276       my applications.  We are making use of mmioRename, which WILL NOT overwrite
1277       the destination file (which is what Windows does, also verified in Win2K)
1278       So, lets delete the destination file before calling mmioRename.  If the
1279       destination file DOESN'T exist, the delete will fail silently.  Let's also be
1280       careful not to lose our previous error code.
1281     */
1282     tmpRet = GetLastError();
1283     DeleteFileA (lpParms->lpfilename);
1284     SetLastError(tmpRet);
1285     
1286     if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1287         ret = ERROR_SUCCESS;
1288     }
1289     
1290     if (dwFlags & MCI_NOTIFY) {
1291         if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1292
1293         mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback), 
1294                          wmw->openParms.wDeviceID, wparam);
1295     }
1296
1297     return ret;
1298 }
1299
1300 /**************************************************************************
1301  *                              WAVE_mciStatus          [internal]
1302  */
1303 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1304 {
1305     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1306     DWORD               ret = 0;
1307
1308     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1309     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1310     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1311     
1312     if (dwFlags & MCI_STATUS_ITEM) {
1313         switch (lpParms->dwItem) {
1314         case MCI_STATUS_CURRENT_TRACK:
1315             lpParms->dwReturn = 1;
1316             TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1317             break;
1318         case MCI_STATUS_LENGTH:
1319             if (!wmw->hFile) {
1320                 lpParms->dwReturn = 0;
1321                 return MCIERR_UNSUPPORTED_FUNCTION;
1322             }
1323             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1324             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1325             TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1326             break;
1327         case MCI_STATUS_MODE:
1328             TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1329             lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1330             ret = MCI_RESOURCE_RETURNED;
1331             break;
1332         case MCI_STATUS_MEDIA_PRESENT:
1333             TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1334             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1335             ret = MCI_RESOURCE_RETURNED;
1336             break;
1337         case MCI_STATUS_NUMBER_OF_TRACKS:
1338             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1339             lpParms->dwReturn = 1;
1340             TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1341             break;
1342         case MCI_STATUS_POSITION:
1343             if (!wmw->hFile) {
1344                 lpParms->dwReturn = 0;
1345                 return MCIERR_UNSUPPORTED_FUNCTION;
1346             }
1347             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1348             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, 
1349                                                              (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1350                                                              &ret);
1351             TRACE("MCI_STATUS_POSITION %s => %lu\n", 
1352                   (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1353             break;
1354         case MCI_STATUS_READY:
1355             lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1356                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1357             TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1358             ret = MCI_RESOURCE_RETURNED;
1359             break;
1360         case MCI_STATUS_TIME_FORMAT:
1361             lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1362             TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1363             ret = MCI_RESOURCE_RETURNED;
1364             break;
1365         case MCI_WAVE_INPUT:
1366             TRACE("MCI_WAVE_INPUT !\n");
1367             lpParms->dwReturn = 0;
1368             ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1369             break;
1370         case MCI_WAVE_OUTPUT:
1371             TRACE("MCI_WAVE_OUTPUT !\n");
1372             {
1373                 UINT    id;
1374                 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1375                     lpParms->dwReturn = id;
1376                 } else {
1377                     lpParms->dwReturn = 0;
1378                     ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1379                 }
1380             }
1381             break;
1382         case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1383             if (!wmw->hFile) {
1384                 lpParms->dwReturn = 0;
1385                 return MCIERR_UNSUPPORTED_FUNCTION;
1386             }
1387             lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1388             TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1389             break;
1390         case MCI_WAVE_STATUS_BITSPERSAMPLE:
1391             if (!wmw->hFile) {
1392                 lpParms->dwReturn = 0;
1393                 return MCIERR_UNSUPPORTED_FUNCTION;
1394             }
1395             lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1396             TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1397             break;
1398         case MCI_WAVE_STATUS_BLOCKALIGN:
1399             if (!wmw->hFile) {
1400                 lpParms->dwReturn = 0;
1401                 return MCIERR_UNSUPPORTED_FUNCTION;
1402             }
1403             lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1404             TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1405             break;
1406         case MCI_WAVE_STATUS_CHANNELS:
1407             if (!wmw->hFile) {
1408                 lpParms->dwReturn = 0;
1409                 return MCIERR_UNSUPPORTED_FUNCTION;
1410             }
1411             lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1412             TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1413             break;
1414         case MCI_WAVE_STATUS_FORMATTAG:
1415             if (!wmw->hFile) {
1416                 lpParms->dwReturn = 0;
1417                 return MCIERR_UNSUPPORTED_FUNCTION;
1418             }
1419             lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1420             TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1421             break;
1422         case MCI_WAVE_STATUS_LEVEL:
1423             TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1424             lpParms->dwReturn = 0xAAAA5555;
1425             break;
1426         case MCI_WAVE_STATUS_SAMPLESPERSEC:
1427             if (!wmw->hFile) {
1428                 lpParms->dwReturn = 0;
1429                 return MCIERR_UNSUPPORTED_FUNCTION;
1430             }
1431             lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1432             TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1433             break;
1434         default:
1435             WARN("unknown command %08lX !\n", lpParms->dwItem);
1436             return MCIERR_UNRECOGNIZED_COMMAND;
1437         }
1438     }
1439     if (dwFlags & MCI_NOTIFY) {
1440         mciDriverNotify((HWND)LOWORD(lpParms->dwCallback), 
1441                         wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1442     }
1443     return ret;
1444 }
1445
1446 /**************************************************************************
1447  *                              WAVE_mciGetDevCaps              [internal]
1448  */
1449 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags, 
1450                                 LPMCI_GETDEVCAPS_PARMS lpParms)
1451 {
1452     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1453     DWORD               ret = 0;
1454
1455     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1456     
1457     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1458     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1459     
1460     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1461         switch(lpParms->dwItem) {
1462         case MCI_GETDEVCAPS_DEVICE_TYPE:
1463             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1464             ret = MCI_RESOURCE_RETURNED;
1465             break;
1466         case MCI_GETDEVCAPS_HAS_AUDIO:
1467             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1468             ret = MCI_RESOURCE_RETURNED;
1469             break;
1470         case MCI_GETDEVCAPS_HAS_VIDEO:
1471             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1472             ret = MCI_RESOURCE_RETURNED;
1473             break;
1474         case MCI_GETDEVCAPS_USES_FILES:
1475             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1476             ret = MCI_RESOURCE_RETURNED;
1477             break;
1478         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1479             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1480             ret = MCI_RESOURCE_RETURNED;
1481             break;
1482         case MCI_GETDEVCAPS_CAN_RECORD:
1483             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1484             ret = MCI_RESOURCE_RETURNED;
1485             break;
1486         case MCI_GETDEVCAPS_CAN_EJECT:
1487             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1488             ret = MCI_RESOURCE_RETURNED;
1489             break;
1490         case MCI_GETDEVCAPS_CAN_PLAY:
1491             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1492             ret = MCI_RESOURCE_RETURNED;
1493             break;
1494         case MCI_GETDEVCAPS_CAN_SAVE:
1495             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1496             ret = MCI_RESOURCE_RETURNED;
1497             break;
1498         case MCI_WAVE_GETDEVCAPS_INPUTS:
1499             lpParms->dwReturn = 1;
1500             break;
1501         case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1502             lpParms->dwReturn = 1;
1503             break;
1504         default:
1505             FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1506             return MCIERR_UNRECOGNIZED_COMMAND;
1507         }
1508     } else {
1509         WARN("No GetDevCaps-Item !\n");
1510         return MCIERR_UNRECOGNIZED_COMMAND;
1511     }
1512     return ret;
1513 }
1514
1515 /**************************************************************************
1516  *                              WAVE_mciInfo                    [internal]
1517  */
1518 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1519 {
1520     DWORD               ret = 0;
1521     LPCSTR              str = 0;
1522     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1523     
1524     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1525     
1526     if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1527         ret = MCIERR_NULL_PARAMETER_BLOCK;
1528     } else if (wmw == NULL) {
1529         ret = MCIERR_INVALID_DEVICE_ID;
1530     } else {
1531         TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1532         
1533         switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1534         case MCI_INFO_PRODUCT:
1535             str = "Wine's audio player";
1536             break;
1537         case MCI_INFO_FILE:
1538             str = wmw->openParms.lpstrElementName;
1539             break;
1540         case MCI_WAVE_INPUT:
1541             str = "Wine Wave In";
1542             break;
1543         case MCI_WAVE_OUTPUT:
1544             str = "Wine Wave Out";
1545             break;
1546         default:
1547             WARN("Don't know this info command (%lu)\n", dwFlags);
1548             ret = MCIERR_UNRECOGNIZED_COMMAND;
1549         }
1550     }
1551     if (str) {
1552         if (strlen(str) + 1 > lpParms->dwRetSize) {
1553             ret = MCIERR_PARAM_OVERFLOW;
1554         } else {
1555             lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1556         }
1557     } else {
1558         lpParms->lpstrReturn[0] = 0;
1559     }
1560     
1561     return ret;
1562 }
1563
1564 /**************************************************************************
1565  *                              DriverProc (MCIWAVE.@)
1566  */
1567 LONG CALLBACK   MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg, 
1568                                    DWORD dwParam1, DWORD dwParam2)
1569 {
1570     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", 
1571           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1572     
1573     switch(wMsg) {
1574     case DRV_LOAD:              return 1;
1575     case DRV_FREE:              return 1;
1576     case DRV_OPEN:              return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1577     case DRV_CLOSE:             return WAVE_drvClose(dwDevID);
1578     case DRV_ENABLE:            return 1;
1579     case DRV_DISABLE:           return 1;
1580     case DRV_QUERYCONFIGURE:    return 1;
1581     case DRV_CONFIGURE:         MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK);      return 1;
1582     case DRV_INSTALL:           return DRVCNF_RESTART;
1583     case DRV_REMOVE:            return DRVCNF_RESTART;
1584     case MCI_OPEN_DRIVER:       return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA)  dwParam2);
1585     case MCI_CLOSE_DRIVER:      return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1586     case MCI_CUE:               return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1587     case MCI_PLAY:              return WAVE_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)        dwParam2);
1588     case MCI_RECORD:            return WAVE_mciRecord    (dwDevID, dwParam1, (LPMCI_RECORD_PARMS)      dwParam2);
1589     case MCI_STOP:              return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1590     case MCI_SET:               return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_SET_PARMS)         dwParam2);
1591     case MCI_PAUSE:             return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1592     case MCI_RESUME:            return WAVE_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1593     case MCI_STATUS:            return WAVE_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)      dwParam2);
1594     case MCI_GETDEVCAPS:        return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)  dwParam2);
1595     case MCI_INFO:              return WAVE_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMSA)       dwParam2);
1596     case MCI_SEEK:              return WAVE_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)        dwParam2);               
1597     case MCI_SAVE:              return WAVE_mciSave      (dwDevID, dwParam1, (LPMCI_SAVE_PARMS)        dwParam2);
1598         /* commands that should be supported */
1599     case MCI_LOAD:              
1600     case MCI_FREEZE:            
1601     case MCI_PUT:               
1602     case MCI_REALIZE:           
1603     case MCI_UNFREEZE:          
1604     case MCI_UPDATE:            
1605     case MCI_WHERE:             
1606     case MCI_STEP:              
1607     case MCI_SPIN:              
1608     case MCI_ESCAPE:            
1609     case MCI_COPY:              
1610     case MCI_CUT:               
1611     case MCI_DELETE:            
1612     case MCI_PASTE:             
1613         FIXME("Unsupported yet command [%lu]\n", wMsg);
1614         break;
1615     case MCI_WINDOW:
1616         TRACE("Unsupported command [%lu]\n", wMsg);
1617         break;
1618         /* option which can be silenced */
1619     case MCI_CONFIGURE:
1620         return 0;
1621     case MCI_OPEN:
1622     case MCI_CLOSE:
1623         ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1624         break;
1625     default:
1626         FIXME("is probably wrong msg [%lu]\n", wMsg);
1627         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1628     }
1629     return MCIERR_UNRECOGNIZED_COMMAND;
1630 }