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