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