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