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