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