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