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