Fixed mmioOpenW flags for playback.
[wine/multimedia.git] / dlls / winmm / mciwave / mciwave.c
blob2caa0b68e14a02fefa871428f0edbed3a3a1fe0d
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
6 * 1999,2000 Eric Pouech
7 * 2000 Francois Jacques
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.
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.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <stdarg.h>
26 #include "winerror.h"
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"
37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
39 typedef struct {
40 UINT wDevID;
41 HANDLE hWave;
42 int nUseCount; /* Incremented for each shared open */
43 BOOL fShareable; /* TRUE if first open was shareable */
44 HMMIO hFile; /* mmio file handle open as Element */
45 MCI_WAVE_OPEN_PARMSW openParms;
46 WAVEFORMATEX wfxRef;
47 LPWAVEFORMATEX lpWaveFormat;
48 BOOL fInput; /* FALSE = Output, TRUE = Input */
49 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
50 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
51 DWORD dwPosition; /* position in bytes in chunk */
52 HANDLE hEvent; /* for synchronization */
53 LONG dwEventCount; /* for synchronization */
54 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
55 MMCKINFO ckMainRIFF; /* main RIFF chunk */
56 MMCKINFO ckWaveData; /* data chunk */
57 } WINE_MCIWAVE;
59 /* ===================================================================
60 * ===================================================================
61 * FIXME: should be using the new mmThreadXXXX functions from WINMM
62 * instead of those
63 * it would require to add a wine internal flag to mmThreadCreate
64 * in order to pass a 32 bit function instead of a 16 bit one
65 * ===================================================================
66 * =================================================================== */
68 struct SCA {
69 UINT wDevID;
70 UINT wMsg;
71 DWORD dwParam1;
72 DWORD dwParam2;
75 /**************************************************************************
76 * MCI_SCAStarter [internal]
78 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
80 struct SCA* sca = (struct SCA*)arg;
81 DWORD ret;
83 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
84 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
85 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
86 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
87 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
88 HeapFree(GetProcessHeap(), 0, sca);
89 ExitThread(ret);
90 WARN("Should not happen ? what's wrong \n");
91 /* should not go after this point */
92 return ret;
95 /**************************************************************************
96 * MCI_SendCommandAsync [internal]
98 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
99 DWORD dwParam2, UINT size)
101 HANDLE handle;
102 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
104 if (sca == 0)
105 return MCIERR_OUT_OF_MEMORY;
107 sca->wDevID = wDevID;
108 sca->wMsg = wMsg;
109 sca->dwParam1 = dwParam1;
111 if (size && dwParam2) {
112 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
113 /* copy structure passed by program in dwParam2 to be sure
114 * we can still use it whatever the program does
116 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
117 } else {
118 sca->dwParam2 = dwParam2;
121 if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
122 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
123 return MCI_SCAStarter(&sca);
125 SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
126 CloseHandle(handle);
127 return 0;
130 /*======================================================================*
131 * MCI WAVE implemantation *
132 *======================================================================*/
134 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
136 /**************************************************************************
137 * MCIWAVE_drvOpen [internal]
139 static DWORD WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
141 WINE_MCIWAVE* wmw;
143 if (modp == NULL) return 0xFFFFFFFF;
145 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
147 if (!wmw)
148 return 0;
150 wmw->wDevID = modp->wDeviceID;
151 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
152 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
153 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
155 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
156 wmw->wfxRef.nChannels = 1; /* MONO */
157 wmw->wfxRef.nSamplesPerSec = 11025;
158 wmw->wfxRef.nAvgBytesPerSec = 11025;
159 wmw->wfxRef.nBlockAlign = 1;
160 wmw->wfxRef.wBitsPerSample = 8;
161 wmw->wfxRef.cbSize = 0; /* don't care */
163 return modp->wDeviceID;
166 /**************************************************************************
167 * MCIWAVE_drvClose [internal]
169 static DWORD WAVE_drvClose(DWORD dwDevID)
171 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
173 if (wmw) {
174 HeapFree(GetProcessHeap(), 0, wmw);
175 mciSetDriverData(dwDevID, 0);
176 return 1;
178 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
181 /**************************************************************************
182 * WAVE_mciGetOpenDev [internal]
184 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
186 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
188 if (wmw == NULL || wmw->nUseCount == 0) {
189 WARN("Invalid wDevID=%u\n", wDevID);
190 return 0;
192 return wmw;
195 /**************************************************************************
196 * WAVE_ConvertByteToTimeFormat [internal]
198 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
200 DWORD ret = 0;
202 switch (wmw->dwMciTimeFormat) {
203 case MCI_FORMAT_MILLISECONDS:
204 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
205 break;
206 case MCI_FORMAT_BYTES:
207 ret = val;
208 break;
209 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
210 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
211 break;
212 default:
213 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
215 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
216 *lpRet = 0;
217 return ret;
220 /**************************************************************************
221 * WAVE_ConvertTimeFormatToByte [internal]
223 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
225 DWORD ret = 0;
227 switch (wmw->dwMciTimeFormat) {
228 case MCI_FORMAT_MILLISECONDS:
229 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
230 break;
231 case MCI_FORMAT_BYTES:
232 ret = val;
233 break;
234 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
235 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
236 break;
237 default:
238 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
240 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
241 return ret;
244 /**************************************************************************
245 * WAVE_mciReadFmt [internal]
247 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
249 MMCKINFO mmckInfo;
250 long r;
252 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
253 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
254 return MCIERR_INVALID_FILE;
255 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
256 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
258 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
259 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
260 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
261 if (r < sizeof(WAVEFORMAT))
262 return MCIERR_INVALID_FILE;
264 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
265 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
266 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
267 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
268 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
269 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
270 if (r >= (long)sizeof(WAVEFORMATEX))
271 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
273 mmioAscend(wmw->hFile, &mmckInfo, 0);
274 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
275 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
276 TRACE("can't find data chunk\n");
277 return MCIERR_INVALID_FILE;
279 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
280 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
281 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
282 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
284 return 0;
287 /**************************************************************************
288 * WAVE_mciCreateRIFFSkeleton [internal]
290 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
292 MMCKINFO ckWaveFormat;
293 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
294 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
296 lpckRIFF->ckid = FOURCC_RIFF;
297 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
298 lpckRIFF->cksize = 0;
300 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
301 goto err;
303 ckWaveFormat.fccType = 0;
304 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
305 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
307 if (!wmw->lpWaveFormat)
309 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
310 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
311 memcpy(wmw->lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
314 /* we can only record PCM files... there is no way in the MCI API to specify
315 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
316 * structure
318 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
319 goto err;
321 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
322 goto err;
324 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
325 goto err;
327 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
328 goto err;
330 lpckWaveData->cksize = 0;
331 lpckWaveData->fccType = 0;
332 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
334 /* create data chunk */
335 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
336 goto err;
338 return 0;
340 err:
341 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
342 wmw->lpWaveFormat = NULL;
343 return MCIERR_INVALID_FILE;
346 /**************************************************************************
347 * WAVE_mciOpen [internal]
349 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
351 DWORD dwRet = 0;
352 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
353 WCHAR* pszTmpFileName = 0;
355 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
356 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
357 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
359 if (dwFlags & MCI_OPEN_SHAREABLE)
360 return MCIERR_HARDWARE;
362 if (wmw->nUseCount > 0) {
363 /* The driver is already opened on this channel
364 * Wave driver cannot be shared
366 return MCIERR_DEVICE_OPEN;
369 wmw->nUseCount++;
371 wmw->fInput = FALSE;
372 wmw->hWave = 0;
373 wmw->dwStatus = MCI_MODE_NOT_READY;
375 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
377 if (dwFlags & MCI_OPEN_ELEMENT) {
378 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
379 /* could it be that (DWORD)lpOpenParms->lpstrElementName
380 * contains the hFile value ?
382 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
383 } else {
384 if (strlenW(lpOpenParms->lpstrElementName) > 0) {
385 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
387 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
388 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(lpOpenParms->lpstrElementName));
390 if (lpOpenParms->lpstrElementName && (strlenW(lpOpenParms->lpstrElementName) > 0)) {
391 wmw->hFile = mmioOpenW((LPWSTR)lpOpenParms->lpstrElementName, NULL,
392 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
394 if (wmw->hFile == 0) {
395 WARN("can't find file=%s!\n", debugstr_w(lpOpenParms->lpstrElementName));
396 dwRet = MCIERR_FILE_NOT_FOUND;
398 else
400 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
402 /* make sure we're are the beginning of the file */
403 mmioSeek(wmw->hFile, 0, SEEK_SET);
405 /* first reading of this file. read the waveformat chunk */
406 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
407 dwRet = MCIERR_INVALID_FILE;
408 } else {
409 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
410 (LPSTR)&(lpckMainRIFF->ckid),
411 (LPSTR) &(lpckMainRIFF->fccType),
412 (lpckMainRIFF->cksize));
414 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
415 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
416 dwRet = MCIERR_INVALID_FILE;
417 } else {
418 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
423 else {
424 wmw->hFile = 0;
427 else {
428 WCHAR szTmpPath[MAX_PATH];
429 WCHAR szPrefix[4];
431 szPrefix[0] = 'T';
432 szPrefix[1] = 'M';
433 szPrefix[2] = 'P';
434 szPrefix[3] = '\0';
435 pszTmpFileName = HeapAlloc(GetProcessHeap(),
436 HEAP_ZERO_MEMORY,
437 MAX_PATH * sizeof(*pszTmpFileName));
439 if (!GetTempPathW(sizeof(szTmpPath), szTmpPath)) {
440 WARN("can't retrieve temp path!\n");
441 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
442 return MCIERR_FILE_NOT_FOUND;
445 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, pszTmpFileName)) {
446 WARN("can't retrieve temp file name!\n");
447 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
448 return MCIERR_FILE_NOT_FOUND;
451 wmw->bTemporaryFile = TRUE;
453 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(pszTmpFileName));
455 if (pszTmpFileName && (strlenW(pszTmpFileName) > 0)) {
457 wmw->hFile = mmioOpenW(pszTmpFileName, NULL,
458 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
460 if (wmw->hFile == 0) {
461 WARN("can't create file=%s!\n", debugstr_w(pszTmpFileName));
462 /* temporary file could not be created. clean filename. */
463 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
464 dwRet = MCIERR_FILE_NOT_FOUND;
471 TRACE("hFile=%p\n", wmw->hFile);
473 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
475 if (wmw->bTemporaryFile)
477 /* Additional openParms is temporary file's name */
478 wmw->openParms.lpstrElementName = pszTmpFileName;
481 if (dwRet == 0) {
482 if (wmw->lpWaveFormat) {
483 switch (wmw->lpWaveFormat->wFormatTag) {
484 case WAVE_FORMAT_PCM:
485 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
486 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
487 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
488 wmw->lpWaveFormat->nAvgBytesPerSec,
489 wmw->lpWaveFormat->nSamplesPerSec *
490 wmw->lpWaveFormat->nBlockAlign);
491 wmw->lpWaveFormat->nAvgBytesPerSec =
492 wmw->lpWaveFormat->nSamplesPerSec *
493 wmw->lpWaveFormat->nBlockAlign;
495 break;
498 wmw->dwPosition = 0;
500 wmw->dwStatus = MCI_MODE_STOP;
501 } else {
502 wmw->nUseCount--;
503 if (wmw->hFile != 0)
504 mmioClose(wmw->hFile, 0);
505 wmw->hFile = 0;
507 return dwRet;
510 /**************************************************************************
511 * WAVE_mciCue [internal]
513 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
516 FIXME
518 This routine is far from complete. At the moment only a check is done on the
519 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
520 is the default.
522 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
523 are ignored
526 DWORD dwRet;
527 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
529 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
531 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
533 /* always close elements ? */
534 if (wmw->hFile != 0) {
535 mmioClose(wmw->hFile, 0);
536 wmw->hFile = 0;
539 dwRet = MMSYSERR_NOERROR; /* assume success */
541 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
542 dwRet = waveOutClose(wmw->hWave);
543 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
544 wmw->fInput = TRUE;
545 } else if (wmw->fInput) {
546 dwRet = waveInClose(wmw->hWave);
547 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
548 wmw->fInput = FALSE;
550 wmw->hWave = 0;
551 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
554 /**************************************************************************
555 * WAVE_mciStop [internal]
557 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
559 DWORD dwRet = 0;
560 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
562 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
564 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
566 /* wait for playback thread (if any) to exit before processing further */
567 switch (wmw->dwStatus) {
568 case MCI_MODE_PAUSE:
569 case MCI_MODE_PLAY:
570 case MCI_MODE_RECORD:
572 int oldStat = wmw->dwStatus;
573 wmw->dwStatus = MCI_MODE_NOT_READY;
574 if (oldStat == MCI_MODE_PAUSE)
575 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
577 while (wmw->dwStatus != MCI_MODE_STOP)
578 Sleep(10);
579 break;
582 wmw->dwPosition = 0;
584 /* sanity resets */
585 wmw->dwStatus = MCI_MODE_STOP;
587 if ((dwFlags & MCI_NOTIFY) && lpParms) {
588 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
589 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
592 return dwRet;
595 /**************************************************************************
596 * WAVE_mciClose [internal]
598 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
600 DWORD dwRet = 0;
601 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
603 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
605 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
607 if (wmw->dwStatus != MCI_MODE_STOP) {
608 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
611 wmw->nUseCount--;
613 if (wmw->nUseCount == 0) {
614 if (wmw->hFile != 0) {
615 mmioClose(wmw->hFile, 0);
616 wmw->hFile = 0;
620 /* That string got allocated in mciOpen because no filename was specified
621 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
622 * allocated by mciOpen, *NOT* the application.
624 if (wmw->bTemporaryFile)
626 HeapFree(GetProcessHeap(), 0, (WCHAR*)wmw->openParms.lpstrElementName);
627 wmw->openParms.lpstrElementName = NULL;
630 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
631 wmw->lpWaveFormat = NULL;
633 if ((dwFlags & MCI_NOTIFY) && lpParms) {
634 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
635 wmw->openParms.wDeviceID,
636 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
639 return 0;
642 /**************************************************************************
643 * WAVE_mciPlayCallback [internal]
645 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
646 DWORD dwInstance,
647 DWORD dwParam1, DWORD dwParam2)
649 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
651 switch (uMsg) {
652 case WOM_OPEN:
653 case WOM_CLOSE:
654 break;
655 case WOM_DONE:
656 InterlockedIncrement(&wmw->dwEventCount);
657 TRACE("Returning waveHdr=%lx\n", dwParam1);
658 SetEvent(wmw->hEvent);
659 break;
660 default:
661 ERR("Unknown uMsg=%d\n", uMsg);
665 /******************************************************************
666 * WAVE_mciPlayWaitDone
670 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
672 for (;;) {
673 ResetEvent(wmw->hEvent);
674 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
675 break;
677 InterlockedIncrement(&wmw->dwEventCount);
679 WaitForSingleObject(wmw->hEvent, INFINITE);
683 /**************************************************************************
684 * WAVE_mciPlay [internal]
686 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
688 DWORD end;
689 LONG bufsize, count, left;
690 DWORD dwRet = 0;
691 LPWAVEHDR waveHdr = NULL;
692 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
693 int whidx;
695 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
697 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
698 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
700 /* FIXME : since there is no way to determine in which mode the device is
701 * open (recording/playback) automatically switch from a mode to another
703 wmw->fInput = FALSE;
705 if (wmw->fInput) {
706 WARN("cannot play on input device\n");
707 return MCIERR_NONAPPLICABLE_FUNCTION;
710 if (wmw->hFile == 0) {
711 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
712 return MCIERR_FILE_NOT_FOUND;
715 if (wmw->dwStatus == MCI_MODE_PAUSE) {
716 /* FIXME: parameters (start/end) in lpParams may not be used */
717 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
720 /** This function will be called again by a thread when async is used.
721 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
722 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
724 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
725 return MCIERR_INTERNAL;
728 wmw->dwStatus = MCI_MODE_PLAY;
730 if (!(dwFlags & MCI_WAIT)) {
731 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
732 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
735 end = 0xFFFFFFFF;
736 if (lpParms && (dwFlags & MCI_FROM)) {
737 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
739 if (lpParms && (dwFlags & MCI_TO)) {
740 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
743 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
745 if (end <= wmw->dwPosition)
746 return TRUE;
749 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
750 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
752 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
753 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
755 if (dwRet == 0) {
756 if (wmw->lpWaveFormat) {
757 switch (wmw->lpWaveFormat->wFormatTag) {
758 case WAVE_FORMAT_PCM:
759 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
760 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
761 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
762 wmw->lpWaveFormat->nAvgBytesPerSec,
763 wmw->lpWaveFormat->nSamplesPerSec *
764 wmw->lpWaveFormat->nBlockAlign);
765 wmw->lpWaveFormat->nAvgBytesPerSec =
766 wmw->lpWaveFormat->nSamplesPerSec *
767 wmw->lpWaveFormat->nBlockAlign;
769 break;
772 } else {
773 TRACE("can't retrieve wave format %ld\n", dwRet);
774 goto cleanUp;
778 /* go back to beginning of chunk plus the requested position */
779 /* FIXME: I'm not sure this is correct, notably because some data linked to
780 * the decompression state machine will not be correcly initialized.
781 * try it this way (other way would be to decompress from 0 up to dwPosition
782 * and to start sending to hWave when dwPosition is reached)
784 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
786 /* By default the device will be opened for output, the MCI_CUE function is there to
787 * change from output to input and back
789 /* FIXME: how to choose between several output channels ? here mapper is forced */
790 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
791 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
793 if (dwRet != 0) {
794 TRACE("Can't open low level audio device %ld\n", dwRet);
795 dwRet = MCIERR_DEVICE_OPEN;
796 wmw->hWave = 0;
797 goto cleanUp;
800 /* make it so that 3 buffers per second are needed */
801 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
803 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
804 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
805 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
806 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
807 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
808 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
809 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
810 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
811 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
812 dwRet = MCIERR_INTERNAL;
813 goto cleanUp;
816 whidx = 0;
817 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
818 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
819 wmw->dwEventCount = 1L; /* for first buffer */
821 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
823 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
824 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
825 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
826 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
827 if (count < 1)
828 break;
829 /* count is always <= bufsize, so this is correct regarding the
830 * waveOutPrepareHeader function
832 waveHdr[whidx].dwBufferLength = count;
833 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
834 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
835 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
836 waveHdr[whidx].dwBytesRecorded);
837 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
838 left -= count;
839 wmw->dwPosition += count;
840 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
842 WAVE_mciPlayWaitDone(wmw);
843 whidx ^= 1;
846 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
848 /* just to get rid of some race conditions between play, stop and pause */
849 waveOutReset(wmw->hWave);
851 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
852 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
854 dwRet = 0;
856 cleanUp:
857 HeapFree(GetProcessHeap(), 0, waveHdr);
859 if (wmw->hWave) {
860 waveOutClose(wmw->hWave);
861 wmw->hWave = 0;
863 CloseHandle(wmw->hEvent);
865 if (lpParms && (dwFlags & MCI_NOTIFY)) {
866 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
867 wmw->openParms.wDeviceID,
868 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
871 wmw->dwStatus = MCI_MODE_STOP;
873 return dwRet;
876 /**************************************************************************
877 * WAVE_mciPlayCallback [internal]
879 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
880 DWORD dwInstance,
881 DWORD dwParam1, DWORD dwParam2)
883 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
884 LPWAVEHDR lpWaveHdr;
885 LONG count = 0;
887 switch (uMsg) {
888 case WIM_OPEN:
889 case WIM_CLOSE:
890 break;
891 case WIM_DATA:
892 lpWaveHdr = (LPWAVEHDR) dwParam1;
894 InterlockedIncrement(&wmw->dwEventCount);
896 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
898 lpWaveHdr->dwFlags &= ~WHDR_DONE;
899 if (count > 0)
900 wmw->dwPosition += count;
901 /* else error reporting ?? */
902 if (wmw->dwStatus == MCI_MODE_RECORD)
904 /* Only queue up another buffer if we are recording. We could receive this
905 message also when waveInReset() is called, since it notifies on all wave
906 buffers that are outstanding. Queueing up more sometimes causes waveInClose
907 to fail. */
908 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
909 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
912 SetEvent(wmw->hEvent);
913 break;
914 default:
915 ERR("Unknown uMsg=%d\n", uMsg);
919 /******************************************************************
920 * bWAVE_mciRecordWaitDone
923 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
925 for (;;) {
926 ResetEvent(wmw->hEvent);
927 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
928 break;
930 InterlockedIncrement(&wmw->dwEventCount);
932 WaitForSingleObject(wmw->hEvent, INFINITE);
936 /**************************************************************************
937 * WAVE_mciRecord [internal]
939 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
941 DWORD end;
942 DWORD dwRet = MMSYSERR_NOERROR;
943 LONG bufsize;
944 LPWAVEHDR waveHdr = NULL;
945 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
948 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
950 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
951 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
953 /* FIXME : since there is no way to determine in which mode the device is
954 * open (recording/playback) automatically switch from a mode to another
956 wmw->fInput = TRUE;
958 if (!wmw->fInput) {
959 WARN("cannot record on output device\n");
960 return MCIERR_NONAPPLICABLE_FUNCTION;
963 if (wmw->dwStatus == MCI_MODE_PAUSE) {
964 /* FIXME: parameters (start/end) in lpParams may not be used */
965 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
968 /** This function will be called again by a thread when async is used.
969 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
970 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
972 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
973 return MCIERR_INTERNAL;
976 wmw->dwStatus = MCI_MODE_RECORD;
978 if (!(dwFlags & MCI_WAIT)) {
979 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
980 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
983 if (!wmw->lpWaveFormat) {
984 /* new RIFF file */
985 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
986 } else {
987 FIXME("Should descend into data chunk. Please report.\n");
990 end = 0xFFFFFFFF;
991 if (lpParms && (dwFlags & MCI_FROM)) {
992 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
995 if (lpParms && (dwFlags & MCI_TO)) {
996 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
999 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
1001 if (end <= wmw->dwPosition)
1003 return TRUE;
1006 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1007 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1009 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1010 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1012 /* go back to beginning of chunk plus the requested position */
1013 /* FIXME: I'm not sure this is correct, notably because some data linked to
1014 * the decompression state machine will not be correcly initialized.
1015 * try it this way (other way would be to decompress from 0 up to dwPosition
1016 * and to start sending to hWave when dwPosition is reached)
1018 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1020 /* By default the device will be opened for output, the MCI_CUE function is there to
1021 * change from output to input and back
1023 /* FIXME: how to choose between several output channels ? here mapper is forced */
1024 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1025 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1027 if (dwRet != MMSYSERR_NOERROR) {
1028 TRACE("Can't open low level audio device %ld\n", dwRet);
1029 dwRet = MCIERR_DEVICE_OPEN;
1030 wmw->hWave = 0;
1031 goto cleanUp;
1034 /* make it so that 3 buffers per second are needed */
1035 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1037 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1038 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1039 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1040 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1041 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1042 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1043 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1045 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1046 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1047 dwRet = MCIERR_INTERNAL;
1048 goto cleanUp;
1051 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1052 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1053 dwRet = MCIERR_INTERNAL;
1054 goto cleanUp;
1057 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1058 wmw->dwEventCount = 1L; /* for first buffer */
1060 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1062 dwRet = waveInStart(wmw->hWave);
1064 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1065 WAVE_mciRecordWaitDone(wmw);
1068 /* needed so that the callback above won't add again the buffers returned by the reset */
1069 wmw->dwStatus = MCI_MODE_STOP;
1071 waveInReset(wmw->hWave);
1073 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1074 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1076 dwRet = 0;
1078 cleanUp:
1079 HeapFree(GetProcessHeap(), 0, waveHdr);
1081 if (wmw->hWave) {
1082 waveInClose(wmw->hWave);
1083 wmw->hWave = 0;
1085 CloseHandle(wmw->hEvent);
1087 /* need to update the size of the data chunk */
1088 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1089 TRACE("failed on ascend\n");
1092 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1093 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1094 wmw->openParms.wDeviceID,
1095 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1098 wmw->dwStatus = MCI_MODE_STOP;
1100 return dwRet;
1104 /**************************************************************************
1105 * WAVE_mciPause [internal]
1107 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1109 DWORD dwRet;
1110 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1112 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1114 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1115 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1117 if (wmw->dwStatus == MCI_MODE_PLAY) {
1118 wmw->dwStatus = MCI_MODE_PAUSE;
1121 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1122 else dwRet = waveOutPause(wmw->hWave);
1124 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1127 /**************************************************************************
1128 * WAVE_mciResume [internal]
1130 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1132 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1133 DWORD dwRet = 0;
1135 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1137 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1139 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1140 wmw->dwStatus = MCI_MODE_PLAY;
1143 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1144 else dwRet = waveOutRestart(wmw->hWave);
1145 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1148 /**************************************************************************
1149 * WAVE_mciSeek [internal]
1151 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1153 DWORD ret = 0;
1154 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1156 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1158 if (lpParms == NULL) {
1159 ret = MCIERR_NULL_PARAMETER_BLOCK;
1160 } else if (wmw == NULL) {
1161 ret = MCIERR_INVALID_DEVICE_ID;
1162 } else {
1163 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1165 if (dwFlags & MCI_SEEK_TO_START) {
1166 wmw->dwPosition = 0;
1167 } else if (dwFlags & MCI_SEEK_TO_END) {
1168 wmw->dwPosition = wmw->ckWaveData.cksize;
1169 } else if (dwFlags & MCI_TO) {
1170 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1171 } else {
1172 WARN("dwFlag doesn't tell where to seek to...\n");
1173 return MCIERR_MISSING_PARAMETER;
1176 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1178 if (dwFlags & MCI_NOTIFY) {
1179 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1180 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1183 return ret;
1186 /**************************************************************************
1187 * WAVE_mciSet [internal]
1189 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1191 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1193 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1195 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1196 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1198 if (dwFlags & MCI_SET_TIME_FORMAT) {
1199 switch (lpParms->dwTimeFormat) {
1200 case MCI_FORMAT_MILLISECONDS:
1201 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1202 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1203 break;
1204 case MCI_FORMAT_BYTES:
1205 TRACE("MCI_FORMAT_BYTES !\n");
1206 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1207 break;
1208 case MCI_FORMAT_SAMPLES:
1209 TRACE("MCI_FORMAT_SAMPLES !\n");
1210 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1211 break;
1212 default:
1213 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1214 return MCIERR_BAD_TIME_FORMAT;
1217 if (dwFlags & MCI_SET_VIDEO) {
1218 TRACE("No support for video !\n");
1219 return MCIERR_UNSUPPORTED_FUNCTION;
1221 if (dwFlags & MCI_SET_DOOR_OPEN) {
1222 TRACE("No support for door open !\n");
1223 return MCIERR_UNSUPPORTED_FUNCTION;
1225 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1226 TRACE("No support for door close !\n");
1227 return MCIERR_UNSUPPORTED_FUNCTION;
1229 if (dwFlags & MCI_SET_AUDIO) {
1230 if (dwFlags & MCI_SET_ON) {
1231 TRACE("MCI_SET_ON audio !\n");
1232 } else if (dwFlags & MCI_SET_OFF) {
1233 TRACE("MCI_SET_OFF audio !\n");
1234 } else {
1235 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1236 return MCIERR_BAD_INTEGER;
1239 switch (lpParms->dwAudio)
1241 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1242 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1243 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1244 default: WARN("Unknown audio channel %lu\n", lpParms->dwAudio); break;
1247 if (dwFlags & MCI_WAVE_INPUT)
1248 TRACE("MCI_WAVE_INPUT !\n");
1249 if (dwFlags & MCI_WAVE_OUTPUT)
1250 TRACE("MCI_WAVE_OUTPUT !\n");
1251 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1252 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1253 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1254 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1255 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1256 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1257 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1259 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1260 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1261 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1263 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1264 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1265 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1267 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1268 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1269 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1271 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1272 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1273 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1275 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1276 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1277 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1279 return 0;
1282 /**************************************************************************
1283 * WAVE_mciSave [internal]
1285 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1287 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1288 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1289 WPARAM wparam = MCI_NOTIFY_FAILURE;
1291 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1292 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1293 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1295 if (dwFlags & MCI_WAIT)
1297 FIXME("MCI_WAIT not implemented\n");
1300 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1301 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1304 ret = mmioClose(wmw->hFile, 0);
1307 If the destination file already exists, it has to be overwritten. (Behaviour
1308 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1309 my applications. We are making use of mmioRename, which WILL NOT overwrite
1310 the destination file (which is what Windows does, also verified in Win2K)
1311 So, lets delete the destination file before calling mmioRename. If the
1312 destination file DOESN'T exist, the delete will fail silently. Let's also be
1313 careful not to lose our previous error code.
1315 tmpRet = GetLastError();
1316 DeleteFileW (lpParms->lpfilename);
1317 SetLastError(tmpRet);
1319 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1320 ret = ERROR_SUCCESS;
1323 if (dwFlags & MCI_NOTIFY) {
1324 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1326 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1327 wmw->openParms.wDeviceID, wparam);
1330 return ret;
1333 /**************************************************************************
1334 * WAVE_mciStatus [internal]
1336 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1338 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1339 DWORD ret = 0;
1341 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1342 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1343 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1345 if (dwFlags & MCI_STATUS_ITEM) {
1346 switch (lpParms->dwItem) {
1347 case MCI_STATUS_CURRENT_TRACK:
1348 lpParms->dwReturn = 1;
1349 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1350 break;
1351 case MCI_STATUS_LENGTH:
1352 if (!wmw->hFile) {
1353 lpParms->dwReturn = 0;
1354 return MCIERR_UNSUPPORTED_FUNCTION;
1356 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1357 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1358 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1359 break;
1360 case MCI_STATUS_MODE:
1361 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1362 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1363 ret = MCI_RESOURCE_RETURNED;
1364 break;
1365 case MCI_STATUS_MEDIA_PRESENT:
1366 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1367 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1368 ret = MCI_RESOURCE_RETURNED;
1369 break;
1370 case MCI_STATUS_NUMBER_OF_TRACKS:
1371 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1372 lpParms->dwReturn = 1;
1373 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1374 break;
1375 case MCI_STATUS_POSITION:
1376 if (!wmw->hFile) {
1377 lpParms->dwReturn = 0;
1378 return MCIERR_UNSUPPORTED_FUNCTION;
1380 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1381 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1382 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1383 &ret);
1384 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1385 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1386 break;
1387 case MCI_STATUS_READY:
1388 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1389 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1390 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1391 ret = MCI_RESOURCE_RETURNED;
1392 break;
1393 case MCI_STATUS_TIME_FORMAT:
1394 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1395 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1396 ret = MCI_RESOURCE_RETURNED;
1397 break;
1398 case MCI_WAVE_INPUT:
1399 TRACE("MCI_WAVE_INPUT !\n");
1400 lpParms->dwReturn = 0;
1401 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1402 break;
1403 case MCI_WAVE_OUTPUT:
1404 TRACE("MCI_WAVE_OUTPUT !\n");
1406 UINT id;
1407 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1408 lpParms->dwReturn = id;
1409 } else {
1410 lpParms->dwReturn = 0;
1411 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1414 break;
1415 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1416 if (!wmw->hFile) {
1417 lpParms->dwReturn = 0;
1418 return MCIERR_UNSUPPORTED_FUNCTION;
1420 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1421 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1422 break;
1423 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1424 if (!wmw->hFile) {
1425 lpParms->dwReturn = 0;
1426 return MCIERR_UNSUPPORTED_FUNCTION;
1428 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1429 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1430 break;
1431 case MCI_WAVE_STATUS_BLOCKALIGN:
1432 if (!wmw->hFile) {
1433 lpParms->dwReturn = 0;
1434 return MCIERR_UNSUPPORTED_FUNCTION;
1436 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1437 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1438 break;
1439 case MCI_WAVE_STATUS_CHANNELS:
1440 if (!wmw->hFile) {
1441 lpParms->dwReturn = 0;
1442 return MCIERR_UNSUPPORTED_FUNCTION;
1444 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1445 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1446 break;
1447 case MCI_WAVE_STATUS_FORMATTAG:
1448 if (!wmw->hFile) {
1449 lpParms->dwReturn = 0;
1450 return MCIERR_UNSUPPORTED_FUNCTION;
1452 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1453 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1454 break;
1455 case MCI_WAVE_STATUS_LEVEL:
1456 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1457 lpParms->dwReturn = 0xAAAA5555;
1458 break;
1459 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1460 if (!wmw->hFile) {
1461 lpParms->dwReturn = 0;
1462 return MCIERR_UNSUPPORTED_FUNCTION;
1464 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1465 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1466 break;
1467 default:
1468 WARN("unknown command %08lX !\n", lpParms->dwItem);
1469 return MCIERR_UNRECOGNIZED_COMMAND;
1472 if (dwFlags & MCI_NOTIFY) {
1473 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1474 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1476 return ret;
1479 /**************************************************************************
1480 * WAVE_mciGetDevCaps [internal]
1482 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1483 LPMCI_GETDEVCAPS_PARMS lpParms)
1485 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1486 DWORD ret = 0;
1488 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1490 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1491 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1493 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1494 switch(lpParms->dwItem) {
1495 case MCI_GETDEVCAPS_DEVICE_TYPE:
1496 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1497 ret = MCI_RESOURCE_RETURNED;
1498 break;
1499 case MCI_GETDEVCAPS_HAS_AUDIO:
1500 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1501 ret = MCI_RESOURCE_RETURNED;
1502 break;
1503 case MCI_GETDEVCAPS_HAS_VIDEO:
1504 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1505 ret = MCI_RESOURCE_RETURNED;
1506 break;
1507 case MCI_GETDEVCAPS_USES_FILES:
1508 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1509 ret = MCI_RESOURCE_RETURNED;
1510 break;
1511 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1512 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1513 ret = MCI_RESOURCE_RETURNED;
1514 break;
1515 case MCI_GETDEVCAPS_CAN_RECORD:
1516 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1517 ret = MCI_RESOURCE_RETURNED;
1518 break;
1519 case MCI_GETDEVCAPS_CAN_EJECT:
1520 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1521 ret = MCI_RESOURCE_RETURNED;
1522 break;
1523 case MCI_GETDEVCAPS_CAN_PLAY:
1524 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1525 ret = MCI_RESOURCE_RETURNED;
1526 break;
1527 case MCI_GETDEVCAPS_CAN_SAVE:
1528 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1529 ret = MCI_RESOURCE_RETURNED;
1530 break;
1531 case MCI_WAVE_GETDEVCAPS_INPUTS:
1532 lpParms->dwReturn = 1;
1533 break;
1534 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1535 lpParms->dwReturn = 1;
1536 break;
1537 default:
1538 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1539 return MCIERR_UNRECOGNIZED_COMMAND;
1541 } else {
1542 WARN("No GetDevCaps-Item !\n");
1543 return MCIERR_UNRECOGNIZED_COMMAND;
1545 return ret;
1548 /**************************************************************************
1549 * WAVE_mciInfo [internal]
1551 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1553 DWORD ret = 0;
1554 LPCWSTR str = 0;
1555 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1557 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1559 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1560 ret = MCIERR_NULL_PARAMETER_BLOCK;
1561 } else if (wmw == NULL) {
1562 ret = MCIERR_INVALID_DEVICE_ID;
1563 } else {
1564 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1565 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1566 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1568 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1570 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1571 case MCI_INFO_PRODUCT: str = wszAudio; break;
1572 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1573 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1574 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1575 default:
1576 WARN("Don't know this info command (%lu)\n", dwFlags);
1577 ret = MCIERR_UNRECOGNIZED_COMMAND;
1580 if (str) {
1581 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1582 ret = MCIERR_PARAM_OVERFLOW;
1583 } else {
1584 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1586 } else {
1587 lpParms->lpstrReturn[0] = 0;
1590 return ret;
1593 /**************************************************************************
1594 * DriverProc (MCIWAVE.@)
1596 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1597 DWORD dwParam1, DWORD dwParam2)
1599 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1600 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1602 switch (wMsg) {
1603 case DRV_LOAD: return 1;
1604 case DRV_FREE: return 1;
1605 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1606 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1607 case DRV_ENABLE: return 1;
1608 case DRV_DISABLE: return 1;
1609 case DRV_QUERYCONFIGURE: return 1;
1610 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1611 case DRV_INSTALL: return DRVCNF_RESTART;
1612 case DRV_REMOVE: return DRVCNF_RESTART;
1615 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1617 switch (wMsg) {
1618 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1619 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1620 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1621 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1622 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1623 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1624 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1625 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1626 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1627 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1628 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1629 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1630 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1631 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1632 /* commands that should be supported */
1633 case MCI_LOAD:
1634 case MCI_FREEZE:
1635 case MCI_PUT:
1636 case MCI_REALIZE:
1637 case MCI_UNFREEZE:
1638 case MCI_UPDATE:
1639 case MCI_WHERE:
1640 case MCI_STEP:
1641 case MCI_SPIN:
1642 case MCI_ESCAPE:
1643 case MCI_COPY:
1644 case MCI_CUT:
1645 case MCI_DELETE:
1646 case MCI_PASTE:
1647 FIXME("Unsupported yet command [%lu]\n", wMsg);
1648 break;
1649 case MCI_WINDOW:
1650 TRACE("Unsupported command [%lu]\n", wMsg);
1651 break;
1652 /* option which can be silenced */
1653 case MCI_CONFIGURE:
1654 return 0;
1655 case MCI_OPEN:
1656 case MCI_CLOSE:
1657 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1658 break;
1659 default:
1660 FIXME("is probably wrong msg [%lu]\n", wMsg);
1661 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1663 return MCIERR_UNRECOGNIZED_COMMAND;