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