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