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