- Applied to all MCI drivers Dmitry's fix for MCI_STATUS_TIME_FORMAT
[wine.git] / dlls / winmm / mciwave / mciwave.c
blob9ef3430a2982132d4d647ca66449a57122180eec
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"
36 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
38 typedef struct {
39 UINT wDevID;
40 HANDLE hWave;
41 int nUseCount; /* Incremented for each shared open */
42 BOOL fShareable; /* TRUE if first open was shareable */
43 HMMIO hFile; /* mmio file handle open as Element */
44 MCI_WAVE_OPEN_PARMSA openParms;
45 WAVEFORMATEX wfxRef;
46 LPWAVEFORMATEX lpWaveFormat;
47 BOOL fInput; /* FALSE = Output, TRUE = Input */
48 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
49 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
50 DWORD dwPosition; /* position in bytes in chunk */
51 HANDLE hEvent; /* for synchronization */
52 DWORD dwEventCount; /* for synchronization */
53 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
54 MMCKINFO ckMainRIFF; /* main RIFF chunk */
55 MMCKINFO ckWaveData; /* data chunk */
56 } WINE_MCIWAVE;
58 /* ===================================================================
59 * ===================================================================
60 * FIXME: should be using the new mmThreadXXXX functions from WINMM
61 * instead of those
62 * it would require to add a wine internal flag to mmThreadCreate
63 * in order to pass a 32 bit function instead of a 16 bit one
64 * ===================================================================
65 * =================================================================== */
67 struct SCA {
68 UINT wDevID;
69 UINT wMsg;
70 DWORD dwParam1;
71 DWORD dwParam2;
74 /**************************************************************************
75 * MCI_SCAStarter [internal]
77 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
79 struct SCA* sca = (struct SCA*)arg;
80 DWORD ret;
82 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
83 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
84 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
85 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
86 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
87 HeapFree(GetProcessHeap(), 0, sca);
88 ExitThread(ret);
89 WARN("Should not happen ? what's wrong \n");
90 /* should not go after this point */
91 return ret;
94 /**************************************************************************
95 * MCI_SendCommandAsync [internal]
97 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
98 DWORD dwParam2, UINT size)
100 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
102 if (sca == 0)
103 return MCIERR_OUT_OF_MEMORY;
105 sca->wDevID = wDevID;
106 sca->wMsg = wMsg;
107 sca->dwParam1 = dwParam1;
109 if (size && dwParam2) {
110 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
111 /* copy structure passed by program in dwParam2 to be sure
112 * we can still use it whatever the program does
114 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
115 } else {
116 sca->dwParam2 = dwParam2;
119 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
120 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
121 return MCI_SCAStarter(&sca);
123 return 0;
126 /*======================================================================*
127 * MCI WAVE implemantation *
128 *======================================================================*/
130 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
132 /**************************************************************************
133 * MCIWAVE_drvOpen [internal]
135 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
137 WINE_MCIWAVE* wmw;
139 if (modp == NULL) return 0xFFFFFFFF;
141 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
143 if (!wmw)
144 return 0;
146 wmw->wDevID = modp->wDeviceID;
147 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
148 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
149 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
151 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
152 wmw->wfxRef.nChannels = 1; /* MONO */
153 wmw->wfxRef.nSamplesPerSec = 11025;
154 wmw->wfxRef.nAvgBytesPerSec = 11025;
155 wmw->wfxRef.nBlockAlign = 1;
156 wmw->wfxRef.wBitsPerSample = 8;
157 wmw->wfxRef.cbSize = 0; /* don't care */
159 return modp->wDeviceID;
162 /**************************************************************************
163 * MCIWAVE_drvClose [internal]
165 static DWORD WAVE_drvClose(DWORD dwDevID)
167 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
169 if (wmw) {
170 HeapFree(GetProcessHeap(), 0, wmw);
171 mciSetDriverData(dwDevID, 0);
172 return 1;
174 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
177 /**************************************************************************
178 * WAVE_mciGetOpenDev [internal]
180 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
182 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
184 if (wmw == NULL || wmw->nUseCount == 0) {
185 WARN("Invalid wDevID=%u\n", wDevID);
186 return 0;
188 return wmw;
191 /**************************************************************************
192 * WAVE_ConvertByteToTimeFormat [internal]
194 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
196 DWORD ret = 0;
198 switch (wmw->dwMciTimeFormat) {
199 case MCI_FORMAT_MILLISECONDS:
200 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
201 break;
202 case MCI_FORMAT_BYTES:
203 ret = val;
204 break;
205 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
206 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
207 break;
208 default:
209 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
211 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
212 *lpRet = 0;
213 return ret;
216 /**************************************************************************
217 * WAVE_ConvertTimeFormatToByte [internal]
219 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
221 DWORD ret = 0;
223 switch (wmw->dwMciTimeFormat) {
224 case MCI_FORMAT_MILLISECONDS:
225 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
226 break;
227 case MCI_FORMAT_BYTES:
228 ret = val;
229 break;
230 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
231 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
232 break;
233 default:
234 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
236 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
237 return ret;
240 /**************************************************************************
241 * WAVE_mciReadFmt [internal]
243 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
245 MMCKINFO mmckInfo;
246 long r;
248 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
249 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
250 return MCIERR_INVALID_FILE;
251 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
252 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
254 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
255 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
256 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
257 if (r < sizeof(WAVEFORMAT))
258 return MCIERR_INVALID_FILE;
260 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
261 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
262 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
263 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
264 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
265 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
266 if (r >= (long)sizeof(WAVEFORMATEX))
267 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
269 mmioAscend(wmw->hFile, &mmckInfo, 0);
270 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
271 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
272 TRACE("can't find data chunk\n");
273 return MCIERR_INVALID_FILE;
275 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
276 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
277 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
278 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
280 return 0;
283 /**************************************************************************
284 * WAVE_mciCreateRIFFSkeleton [internal]
286 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
288 MMCKINFO ckWaveFormat;
289 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
290 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
292 lpckRIFF->ckid = FOURCC_RIFF;
293 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
294 lpckRIFF->cksize = 0;
296 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
297 goto err;
299 ckWaveFormat.fccType = 0;
300 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
301 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
303 if (!wmw->lpWaveFormat)
305 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
306 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
307 memcpy(wmw->lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
310 /* we can only record PCM files... there is no way in the MCI API to specify
311 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
312 * structure
314 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
315 goto err;
317 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
318 goto err;
320 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
321 goto err;
323 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
324 goto err;
326 lpckWaveData->cksize = 0;
327 lpckWaveData->fccType = 0;
328 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
330 /* create data chunk */
331 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
332 goto err;
334 return 0;
336 err:
337 if (wmw->lpWaveFormat)
338 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
339 wmw->lpWaveFormat = NULL;
340 return MCIERR_INVALID_FILE;
343 /**************************************************************************
344 * WAVE_mciOpen [internal]
346 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
348 DWORD dwRet = 0;
349 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
350 CHAR* pszTmpFileName = 0;
352 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
353 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
354 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
356 if (dwFlags & MCI_OPEN_SHAREABLE)
357 return MCIERR_HARDWARE;
359 if (wmw->nUseCount > 0) {
360 /* The driver is already opened on this channel
361 * Wave driver cannot be shared
363 return MCIERR_DEVICE_OPEN;
366 wmw->nUseCount++;
368 wmw->fInput = FALSE;
369 wmw->hWave = 0;
370 wmw->dwStatus = MCI_MODE_NOT_READY;
372 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
374 if (dwFlags & MCI_OPEN_ELEMENT) {
375 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
376 /* could it be that (DWORD)lpOpenParms->lpstrElementName
377 * contains the hFile value ?
379 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
380 } else {
381 if (strlen(lpOpenParms->lpstrElementName) > 0) {
382 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
384 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
385 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
387 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
388 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
389 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
391 if (wmw->hFile == 0) {
392 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
393 dwRet = MCIERR_FILE_NOT_FOUND;
395 else
397 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
399 /* make sure we're are the beginning of the file */
400 mmioSeek(wmw->hFile, 0, SEEK_SET);
402 /* first reading of this file. read the waveformat chunk */
403 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
404 dwRet = MCIERR_INVALID_FILE;
405 } else {
406 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
407 (LPSTR)&(lpckMainRIFF->ckid),
408 (LPSTR) &(lpckMainRIFF->fccType),
409 (lpckMainRIFF->cksize));
411 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
412 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
413 dwRet = MCIERR_INVALID_FILE;
414 } else {
415 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
420 else {
421 wmw->hFile = 0;
424 else {
425 CHAR szTmpPath[MAX_PATH];
426 CHAR szPrefix[4] = "TMP\0";
428 pszTmpFileName = HeapAlloc(GetProcessHeap(),
429 HEAP_ZERO_MEMORY,
430 MAX_PATH * sizeof(*pszTmpFileName));
432 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
433 WARN("can't retrieve temp path!\n");
434 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
435 return MCIERR_FILE_NOT_FOUND;
438 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
439 WARN("can't retrieve temp file name!\n");
440 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
441 return MCIERR_FILE_NOT_FOUND;
444 wmw->bTemporaryFile = TRUE;
446 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
448 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
450 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
451 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
453 if (wmw->hFile == 0) {
454 /* temporary file could not be created. clean filename. */
455 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
456 WARN("can't create file='%s' !\n", pszTmpFileName);
457 dwRet = MCIERR_FILE_NOT_FOUND;
464 TRACE("hFile=%p\n", wmw->hFile);
466 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
468 if (wmw->bTemporaryFile == TRUE)
470 /* Additional openParms is temporary file's name */
471 wmw->openParms.lpstrElementName = pszTmpFileName;
474 if (dwRet == 0) {
475 if (wmw->lpWaveFormat) {
476 switch (wmw->lpWaveFormat->wFormatTag) {
477 case WAVE_FORMAT_PCM:
478 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
479 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
480 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
481 wmw->lpWaveFormat->nAvgBytesPerSec,
482 wmw->lpWaveFormat->nSamplesPerSec *
483 wmw->lpWaveFormat->nBlockAlign);
484 wmw->lpWaveFormat->nAvgBytesPerSec =
485 wmw->lpWaveFormat->nSamplesPerSec *
486 wmw->lpWaveFormat->nBlockAlign;
488 break;
491 wmw->dwPosition = 0;
493 wmw->dwStatus = MCI_MODE_STOP;
494 } else {
495 wmw->nUseCount--;
496 if (wmw->hFile != 0)
497 mmioClose(wmw->hFile, 0);
498 wmw->hFile = 0;
500 return dwRet;
503 /**************************************************************************
504 * WAVE_mciCue [internal]
506 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
509 FIXME
511 This routine is far from complete. At the moment only a check is done on the
512 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
513 is the default.
515 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
516 are ignored
519 DWORD dwRet;
520 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
522 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
524 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
526 /* always close elements ? */
527 if (wmw->hFile != 0) {
528 mmioClose(wmw->hFile, 0);
529 wmw->hFile = 0;
532 dwRet = MMSYSERR_NOERROR; /* assume success */
534 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
535 dwRet = waveOutClose(wmw->hWave);
536 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
537 wmw->fInput = TRUE;
538 } else if (wmw->fInput) {
539 dwRet = waveInClose(wmw->hWave);
540 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
541 wmw->fInput = FALSE;
543 wmw->hWave = 0;
544 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
547 /**************************************************************************
548 * WAVE_mciStop [internal]
550 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
552 DWORD dwRet = 0;
553 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
555 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
557 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
559 /* wait for playback thread (if any) to exit before processing further */
560 switch (wmw->dwStatus) {
561 case MCI_MODE_PAUSE:
562 case MCI_MODE_PLAY:
563 case MCI_MODE_RECORD:
565 int oldStat = wmw->dwStatus;
566 wmw->dwStatus = MCI_MODE_NOT_READY;
567 if (oldStat == MCI_MODE_PAUSE)
568 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
570 while (wmw->dwStatus != MCI_MODE_STOP)
571 Sleep(10);
572 break;
575 wmw->dwPosition = 0;
577 /* sanity resets */
578 wmw->dwStatus = MCI_MODE_STOP;
580 if ((dwFlags & MCI_NOTIFY) && lpParms) {
581 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
582 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
585 return dwRet;
588 /**************************************************************************
589 * WAVE_mciClose [internal]
591 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
593 DWORD dwRet = 0;
594 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
596 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
598 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
600 if (wmw->dwStatus != MCI_MODE_STOP) {
601 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
604 wmw->nUseCount--;
606 if (wmw->nUseCount == 0) {
607 if (wmw->hFile != 0) {
608 mmioClose(wmw->hFile, 0);
609 wmw->hFile = 0;
613 /* That string got allocated in mciOpen because no filename was specified
614 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
615 * allocated by mciOpen, *NOT* the application.
617 if (wmw->bTemporaryFile)
619 HeapFree(GetProcessHeap(), 0, (char*)wmw->openParms.lpstrElementName);
620 wmw->openParms.lpstrElementName = NULL;
623 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
624 wmw->lpWaveFormat = NULL;
626 if ((dwFlags & MCI_NOTIFY) && lpParms) {
627 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
628 wmw->openParms.wDeviceID,
629 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
632 return 0;
635 /**************************************************************************
636 * WAVE_mciPlayCallback [internal]
638 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
639 DWORD dwInstance,
640 DWORD dwParam1, DWORD dwParam2)
642 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
644 switch (uMsg) {
645 case WOM_OPEN:
646 case WOM_CLOSE:
647 break;
648 case WOM_DONE:
649 InterlockedIncrement(&wmw->dwEventCount);
650 TRACE("Returning waveHdr=%lx\n", dwParam1);
651 SetEvent(wmw->hEvent);
652 break;
653 default:
654 ERR("Unknown uMsg=%d\n", uMsg);
658 /******************************************************************
659 * WAVE_mciPlayWaitDone
663 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
665 for (;;) {
666 ResetEvent(wmw->hEvent);
667 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
668 break;
670 InterlockedIncrement(&wmw->dwEventCount);
672 WaitForSingleObject(wmw->hEvent, INFINITE);
676 /**************************************************************************
677 * WAVE_mciPlay [internal]
679 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
681 DWORD end;
682 LONG bufsize, count, left;
683 DWORD dwRet = 0;
684 LPWAVEHDR waveHdr = NULL;
685 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
686 int whidx;
688 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
690 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
691 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
693 /* FIXME : since there is no way to determine in which mode the device is
694 * open (recording/playback) automatically switch from a mode to another
696 wmw->fInput = FALSE;
698 if (wmw->fInput) {
699 WARN("cannot play on input device\n");
700 return MCIERR_NONAPPLICABLE_FUNCTION;
703 if (wmw->hFile == 0) {
704 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
705 return MCIERR_FILE_NOT_FOUND;
708 if (wmw->dwStatus == MCI_MODE_PAUSE) {
709 /* FIXME: parameters (start/end) in lpParams may not be used */
710 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
713 /** This function will be called again by a thread when async is used.
714 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
715 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
717 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
718 return MCIERR_INTERNAL;
721 wmw->dwStatus = MCI_MODE_PLAY;
723 if (!(dwFlags & MCI_WAIT)) {
724 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
725 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
728 end = 0xFFFFFFFF;
729 if (lpParms && (dwFlags & MCI_FROM)) {
730 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
732 if (lpParms && (dwFlags & MCI_TO)) {
733 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
736 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
738 if (end <= wmw->dwPosition)
739 return TRUE;
742 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
743 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
745 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
746 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
748 if (dwRet == 0) {
749 if (wmw->lpWaveFormat) {
750 switch (wmw->lpWaveFormat->wFormatTag) {
751 case WAVE_FORMAT_PCM:
752 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
753 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
754 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
755 wmw->lpWaveFormat->nAvgBytesPerSec,
756 wmw->lpWaveFormat->nSamplesPerSec *
757 wmw->lpWaveFormat->nBlockAlign);
758 wmw->lpWaveFormat->nAvgBytesPerSec =
759 wmw->lpWaveFormat->nSamplesPerSec *
760 wmw->lpWaveFormat->nBlockAlign;
762 break;
765 } else {
766 TRACE("can't retrieve wave format %ld\n", dwRet);
767 goto cleanUp;
771 /* go back to beginning of chunk plus the requested position */
772 /* FIXME: I'm not sure this is correct, notably because some data linked to
773 * the decompression state machine will not be correcly initialized.
774 * try it this way (other way would be to decompress from 0 up to dwPosition
775 * and to start sending to hWave when dwPosition is reached)
777 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
779 /* By default the device will be opened for output, the MCI_CUE function is there to
780 * change from output to input and back
782 /* FIXME: how to choose between several output channels ? here mapper is forced */
783 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
784 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
786 if (dwRet != 0) {
787 TRACE("Can't open low level audio device %ld\n", dwRet);
788 dwRet = MCIERR_DEVICE_OPEN;
789 wmw->hWave = 0;
790 goto cleanUp;
793 /* make it so that 3 buffers per second are needed */
794 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
796 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
797 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
798 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
799 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
800 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
801 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
802 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
803 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
804 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
805 dwRet = MCIERR_INTERNAL;
806 goto cleanUp;
809 whidx = 0;
810 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
811 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
812 wmw->dwEventCount = 1L; /* for first buffer */
814 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
816 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
817 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
818 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
819 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
820 if (count < 1)
821 break;
822 /* count is always <= bufsize, so this is correct regarding the
823 * waveOutPrepareHeader function
825 waveHdr[whidx].dwBufferLength = count;
826 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
827 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
828 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
829 waveHdr[whidx].dwBytesRecorded);
830 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
831 left -= count;
832 wmw->dwPosition += count;
833 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
835 WAVE_mciPlayWaitDone(wmw);
836 whidx ^= 1;
839 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
841 /* just to get rid of some race conditions between play, stop and pause */
842 waveOutReset(wmw->hWave);
844 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
845 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
847 dwRet = 0;
849 cleanUp:
850 HeapFree(GetProcessHeap(), 0, waveHdr);
852 if (wmw->hWave) {
853 waveOutClose(wmw->hWave);
854 wmw->hWave = 0;
856 CloseHandle(wmw->hEvent);
858 if (lpParms && (dwFlags & MCI_NOTIFY)) {
859 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
860 wmw->openParms.wDeviceID,
861 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
864 wmw->dwStatus = MCI_MODE_STOP;
866 return dwRet;
869 /**************************************************************************
870 * WAVE_mciPlayCallback [internal]
872 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
873 DWORD dwInstance,
874 DWORD dwParam1, DWORD dwParam2)
876 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
877 LPWAVEHDR lpWaveHdr;
878 LONG count = 0;
880 switch (uMsg) {
881 case WIM_OPEN:
882 case WIM_CLOSE:
883 break;
884 case WIM_DATA:
885 lpWaveHdr = (LPWAVEHDR) dwParam1;
887 InterlockedIncrement(&wmw->dwEventCount);
889 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
891 lpWaveHdr->dwFlags &= ~WHDR_DONE;
892 if (count > 0)
893 wmw->dwPosition += count;
894 /* else error reporting ?? */
895 if (wmw->dwStatus == MCI_MODE_RECORD)
897 /* Only queue up another buffer if we are recording. We could receive this
898 message also when waveInReset() is called, since it notifies on all wave
899 buffers that are outstanding. Queueing up more sometimes causes waveInClose
900 to fail. */
901 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
902 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
905 SetEvent(wmw->hEvent);
906 break;
907 default:
908 ERR("Unknown uMsg=%d\n", uMsg);
912 /******************************************************************
913 * bWAVE_mciRecordWaitDone
916 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
918 for (;;) {
919 ResetEvent(wmw->hEvent);
920 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
921 break;
923 InterlockedIncrement(&wmw->dwEventCount);
925 WaitForSingleObject(wmw->hEvent, INFINITE);
929 /**************************************************************************
930 * WAVE_mciRecord [internal]
932 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
934 DWORD end;
935 DWORD dwRet = MMSYSERR_NOERROR;
936 LONG bufsize;
937 LPWAVEHDR waveHdr = NULL;
938 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
941 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
943 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
944 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
946 /* FIXME : since there is no way to determine in which mode the device is
947 * open (recording/playback) automatically switch from a mode to another
949 wmw->fInput = TRUE;
951 if (!wmw->fInput) {
952 WARN("cannot record on output device\n");
953 return MCIERR_NONAPPLICABLE_FUNCTION;
956 if (wmw->dwStatus == MCI_MODE_PAUSE) {
957 /* FIXME: parameters (start/end) in lpParams may not be used */
958 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
961 /** This function will be called again by a thread when async is used.
962 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
963 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
965 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
966 return MCIERR_INTERNAL;
969 wmw->dwStatus = MCI_MODE_RECORD;
971 if (!(dwFlags & MCI_WAIT)) {
972 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
973 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
976 if (!wmw->lpWaveFormat) {
977 /* new RIFF file */
978 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
979 } else {
980 FIXME("Should descend into data chunk. Please report.\n");
983 end = 0xFFFFFFFF;
984 if (lpParms && (dwFlags & MCI_FROM)) {
985 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
988 if (lpParms && (dwFlags & MCI_TO)) {
989 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
992 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
994 if (end <= wmw->dwPosition)
996 return TRUE;
999 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1000 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1002 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1003 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1005 /* go back to beginning of chunk plus the requested position */
1006 /* FIXME: I'm not sure this is correct, notably because some data linked to
1007 * the decompression state machine will not be correcly initialized.
1008 * try it this way (other way would be to decompress from 0 up to dwPosition
1009 * and to start sending to hWave when dwPosition is reached)
1011 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1013 /* By default the device will be opened for output, the MCI_CUE function is there to
1014 * change from output to input and back
1016 /* FIXME: how to choose between several output channels ? here mapper is forced */
1017 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1018 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1020 if (dwRet != MMSYSERR_NOERROR) {
1021 TRACE("Can't open low level audio device %ld\n", dwRet);
1022 dwRet = MCIERR_DEVICE_OPEN;
1023 wmw->hWave = 0;
1024 goto cleanUp;
1027 /* make it so that 3 buffers per second are needed */
1028 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1030 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1031 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1032 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1033 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1034 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1035 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1036 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1038 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1039 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1040 dwRet = MCIERR_INTERNAL;
1041 goto cleanUp;
1044 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1045 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1046 dwRet = MCIERR_INTERNAL;
1047 goto cleanUp;
1050 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1051 wmw->dwEventCount = 1L; /* for first buffer */
1053 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1055 dwRet = waveInStart(wmw->hWave);
1057 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1058 WAVE_mciRecordWaitDone(wmw);
1061 /* needed so that the callback above won't add again the buffers returned by the reset */
1062 wmw->dwStatus = MCI_MODE_STOP;
1064 waveInReset(wmw->hWave);
1066 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1067 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1069 dwRet = 0;
1071 cleanUp:
1072 HeapFree(GetProcessHeap(), 0, waveHdr);
1074 if (wmw->hWave) {
1075 waveInClose(wmw->hWave);
1076 wmw->hWave = 0;
1078 CloseHandle(wmw->hEvent);
1080 /* need to update the size of the data chunk */
1081 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1082 TRACE("failed on ascend\n");
1085 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1086 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1087 wmw->openParms.wDeviceID,
1088 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1091 wmw->dwStatus = MCI_MODE_STOP;
1093 return dwRet;
1097 /**************************************************************************
1098 * WAVE_mciPause [internal]
1100 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1102 DWORD dwRet;
1103 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1105 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1107 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1108 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1110 if (wmw->dwStatus == MCI_MODE_PLAY) {
1111 wmw->dwStatus = MCI_MODE_PAUSE;
1114 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1115 else dwRet = waveOutPause(wmw->hWave);
1117 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1120 /**************************************************************************
1121 * WAVE_mciResume [internal]
1123 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1125 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1126 DWORD dwRet = 0;
1128 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1130 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1132 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1133 wmw->dwStatus = MCI_MODE_PLAY;
1136 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1137 else dwRet = waveOutRestart(wmw->hWave);
1138 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1141 /**************************************************************************
1142 * WAVE_mciSeek [internal]
1144 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1146 DWORD ret = 0;
1147 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1149 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1151 if (lpParms == NULL) {
1152 ret = MCIERR_NULL_PARAMETER_BLOCK;
1153 } else if (wmw == NULL) {
1154 ret = MCIERR_INVALID_DEVICE_ID;
1155 } else {
1156 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1158 if (dwFlags & MCI_SEEK_TO_START) {
1159 wmw->dwPosition = 0;
1160 } else if (dwFlags & MCI_SEEK_TO_END) {
1161 wmw->dwPosition = wmw->ckWaveData.cksize;
1162 } else if (dwFlags & MCI_TO) {
1163 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1164 } else {
1165 WARN("dwFlag doesn't tell where to seek to...\n");
1166 return MCIERR_MISSING_PARAMETER;
1169 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1171 if (dwFlags & MCI_NOTIFY) {
1172 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1173 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1176 return ret;
1179 /**************************************************************************
1180 * WAVE_mciSet [internal]
1182 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1184 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1186 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1188 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1189 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1191 if (dwFlags & MCI_SET_TIME_FORMAT) {
1192 switch (lpParms->dwTimeFormat) {
1193 case MCI_FORMAT_MILLISECONDS:
1194 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1195 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1196 break;
1197 case MCI_FORMAT_BYTES:
1198 TRACE("MCI_FORMAT_BYTES !\n");
1199 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1200 break;
1201 case MCI_FORMAT_SAMPLES:
1202 TRACE("MCI_FORMAT_SAMPLES !\n");
1203 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1204 break;
1205 default:
1206 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1207 return MCIERR_BAD_TIME_FORMAT;
1210 if (dwFlags & MCI_SET_VIDEO) {
1211 TRACE("No support for video !\n");
1212 return MCIERR_UNSUPPORTED_FUNCTION;
1214 if (dwFlags & MCI_SET_DOOR_OPEN) {
1215 TRACE("No support for door open !\n");
1216 return MCIERR_UNSUPPORTED_FUNCTION;
1218 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1219 TRACE("No support for door close !\n");
1220 return MCIERR_UNSUPPORTED_FUNCTION;
1222 if (dwFlags & MCI_SET_AUDIO) {
1223 if (dwFlags & MCI_SET_ON) {
1224 TRACE("MCI_SET_ON audio !\n");
1225 } else if (dwFlags & MCI_SET_OFF) {
1226 TRACE("MCI_SET_OFF audio !\n");
1227 } else {
1228 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1229 return MCIERR_BAD_INTEGER;
1232 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1233 TRACE("MCI_SET_AUDIO_ALL !\n");
1234 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1235 TRACE("MCI_SET_AUDIO_LEFT !\n");
1236 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1237 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1239 if (dwFlags & MCI_WAVE_INPUT)
1240 TRACE("MCI_WAVE_INPUT !\n");
1241 if (dwFlags & MCI_WAVE_OUTPUT)
1242 TRACE("MCI_WAVE_OUTPUT !\n");
1243 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1244 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1245 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1246 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1247 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1248 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1249 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1251 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1252 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1253 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1255 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1256 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1257 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1259 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1260 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1261 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1263 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1264 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1265 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1267 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1268 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1269 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1271 return 0;
1274 /**************************************************************************
1275 * WAVE_mciSave [internal]
1277 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1279 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1280 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1281 WPARAM wparam = MCI_NOTIFY_FAILURE;
1283 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1284 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1285 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1287 if (dwFlags & MCI_WAIT)
1289 FIXME("MCI_WAIT not implemented\n");
1292 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1293 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1296 ret = mmioClose(wmw->hFile, 0);
1299 If the destination file already exists, it has to be overwritten. (Behaviour
1300 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1301 my applications. We are making use of mmioRename, which WILL NOT overwrite
1302 the destination file (which is what Windows does, also verified in Win2K)
1303 So, lets delete the destination file before calling mmioRename. If the
1304 destination file DOESN'T exist, the delete will fail silently. Let's also be
1305 careful not to lose our previous error code.
1307 tmpRet = GetLastError();
1308 DeleteFileA (lpParms->lpfilename);
1309 SetLastError(tmpRet);
1311 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1312 ret = ERROR_SUCCESS;
1315 if (dwFlags & MCI_NOTIFY) {
1316 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1318 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1319 wmw->openParms.wDeviceID, wparam);
1322 return ret;
1325 /**************************************************************************
1326 * WAVE_mciStatus [internal]
1328 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1330 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1331 DWORD ret = 0;
1333 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1334 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1335 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1337 if (dwFlags & MCI_STATUS_ITEM) {
1338 switch (lpParms->dwItem) {
1339 case MCI_STATUS_CURRENT_TRACK:
1340 lpParms->dwReturn = 1;
1341 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1342 break;
1343 case MCI_STATUS_LENGTH:
1344 if (!wmw->hFile) {
1345 lpParms->dwReturn = 0;
1346 return MCIERR_UNSUPPORTED_FUNCTION;
1348 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1349 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1350 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1351 break;
1352 case MCI_STATUS_MODE:
1353 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1354 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1355 ret = MCI_RESOURCE_RETURNED;
1356 break;
1357 case MCI_STATUS_MEDIA_PRESENT:
1358 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1359 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1360 ret = MCI_RESOURCE_RETURNED;
1361 break;
1362 case MCI_STATUS_NUMBER_OF_TRACKS:
1363 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1364 lpParms->dwReturn = 1;
1365 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1366 break;
1367 case MCI_STATUS_POSITION:
1368 if (!wmw->hFile) {
1369 lpParms->dwReturn = 0;
1370 return MCIERR_UNSUPPORTED_FUNCTION;
1372 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1373 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1374 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1375 &ret);
1376 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1377 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1378 break;
1379 case MCI_STATUS_READY:
1380 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1381 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1382 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1383 ret = MCI_RESOURCE_RETURNED;
1384 break;
1385 case MCI_STATUS_TIME_FORMAT:
1386 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1387 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1388 ret = MCI_RESOURCE_RETURNED;
1389 break;
1390 case MCI_WAVE_INPUT:
1391 TRACE("MCI_WAVE_INPUT !\n");
1392 lpParms->dwReturn = 0;
1393 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1394 break;
1395 case MCI_WAVE_OUTPUT:
1396 TRACE("MCI_WAVE_OUTPUT !\n");
1398 UINT id;
1399 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1400 lpParms->dwReturn = id;
1401 } else {
1402 lpParms->dwReturn = 0;
1403 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1406 break;
1407 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1408 if (!wmw->hFile) {
1409 lpParms->dwReturn = 0;
1410 return MCIERR_UNSUPPORTED_FUNCTION;
1412 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1413 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1414 break;
1415 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1416 if (!wmw->hFile) {
1417 lpParms->dwReturn = 0;
1418 return MCIERR_UNSUPPORTED_FUNCTION;
1420 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1421 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1422 break;
1423 case MCI_WAVE_STATUS_BLOCKALIGN:
1424 if (!wmw->hFile) {
1425 lpParms->dwReturn = 0;
1426 return MCIERR_UNSUPPORTED_FUNCTION;
1428 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1429 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1430 break;
1431 case MCI_WAVE_STATUS_CHANNELS:
1432 if (!wmw->hFile) {
1433 lpParms->dwReturn = 0;
1434 return MCIERR_UNSUPPORTED_FUNCTION;
1436 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1437 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1438 break;
1439 case MCI_WAVE_STATUS_FORMATTAG:
1440 if (!wmw->hFile) {
1441 lpParms->dwReturn = 0;
1442 return MCIERR_UNSUPPORTED_FUNCTION;
1444 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1445 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1446 break;
1447 case MCI_WAVE_STATUS_LEVEL:
1448 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1449 lpParms->dwReturn = 0xAAAA5555;
1450 break;
1451 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1452 if (!wmw->hFile) {
1453 lpParms->dwReturn = 0;
1454 return MCIERR_UNSUPPORTED_FUNCTION;
1456 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1457 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1458 break;
1459 default:
1460 WARN("unknown command %08lX !\n", lpParms->dwItem);
1461 return MCIERR_UNRECOGNIZED_COMMAND;
1464 if (dwFlags & MCI_NOTIFY) {
1465 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1466 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1468 return ret;
1471 /**************************************************************************
1472 * WAVE_mciGetDevCaps [internal]
1474 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1475 LPMCI_GETDEVCAPS_PARMS lpParms)
1477 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1478 DWORD ret = 0;
1480 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1482 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1483 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1485 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1486 switch(lpParms->dwItem) {
1487 case MCI_GETDEVCAPS_DEVICE_TYPE:
1488 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1489 ret = MCI_RESOURCE_RETURNED;
1490 break;
1491 case MCI_GETDEVCAPS_HAS_AUDIO:
1492 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1493 ret = MCI_RESOURCE_RETURNED;
1494 break;
1495 case MCI_GETDEVCAPS_HAS_VIDEO:
1496 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1497 ret = MCI_RESOURCE_RETURNED;
1498 break;
1499 case MCI_GETDEVCAPS_USES_FILES:
1500 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1501 ret = MCI_RESOURCE_RETURNED;
1502 break;
1503 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1504 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1505 ret = MCI_RESOURCE_RETURNED;
1506 break;
1507 case MCI_GETDEVCAPS_CAN_RECORD:
1508 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1509 ret = MCI_RESOURCE_RETURNED;
1510 break;
1511 case MCI_GETDEVCAPS_CAN_EJECT:
1512 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1513 ret = MCI_RESOURCE_RETURNED;
1514 break;
1515 case MCI_GETDEVCAPS_CAN_PLAY:
1516 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1517 ret = MCI_RESOURCE_RETURNED;
1518 break;
1519 case MCI_GETDEVCAPS_CAN_SAVE:
1520 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1521 ret = MCI_RESOURCE_RETURNED;
1522 break;
1523 case MCI_WAVE_GETDEVCAPS_INPUTS:
1524 lpParms->dwReturn = 1;
1525 break;
1526 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1527 lpParms->dwReturn = 1;
1528 break;
1529 default:
1530 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1531 return MCIERR_UNRECOGNIZED_COMMAND;
1533 } else {
1534 WARN("No GetDevCaps-Item !\n");
1535 return MCIERR_UNRECOGNIZED_COMMAND;
1537 return ret;
1540 /**************************************************************************
1541 * WAVE_mciInfo [internal]
1543 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1545 DWORD ret = 0;
1546 LPCSTR str = 0;
1547 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1549 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1551 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1552 ret = MCIERR_NULL_PARAMETER_BLOCK;
1553 } else if (wmw == NULL) {
1554 ret = MCIERR_INVALID_DEVICE_ID;
1555 } else {
1556 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1558 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1559 case MCI_INFO_PRODUCT:
1560 str = "Wine's audio player";
1561 break;
1562 case MCI_INFO_FILE:
1563 str = wmw->openParms.lpstrElementName;
1564 break;
1565 case MCI_WAVE_INPUT:
1566 str = "Wine Wave In";
1567 break;
1568 case MCI_WAVE_OUTPUT:
1569 str = "Wine Wave Out";
1570 break;
1571 default:
1572 WARN("Don't know this info command (%lu)\n", dwFlags);
1573 ret = MCIERR_UNRECOGNIZED_COMMAND;
1576 if (str) {
1577 if (strlen(str) + 1 > lpParms->dwRetSize) {
1578 ret = MCIERR_PARAM_OVERFLOW;
1579 } else {
1580 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1582 } else {
1583 lpParms->lpstrReturn[0] = 0;
1586 return ret;
1589 /**************************************************************************
1590 * DriverProc (MCIWAVE.@)
1592 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1593 DWORD dwParam1, DWORD dwParam2)
1595 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1596 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1598 switch (wMsg) {
1599 case DRV_LOAD: return 1;
1600 case DRV_FREE: return 1;
1601 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1602 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1603 case DRV_ENABLE: return 1;
1604 case DRV_DISABLE: return 1;
1605 case DRV_QUERYCONFIGURE: return 1;
1606 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1607 case DRV_INSTALL: return DRVCNF_RESTART;
1608 case DRV_REMOVE: return DRVCNF_RESTART;
1611 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1613 switch (wMsg) {
1614 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1615 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1616 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1617 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1618 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1619 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1620 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1621 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1622 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1623 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1624 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1625 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1626 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1627 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1628 /* commands that should be supported */
1629 case MCI_LOAD:
1630 case MCI_FREEZE:
1631 case MCI_PUT:
1632 case MCI_REALIZE:
1633 case MCI_UNFREEZE:
1634 case MCI_UPDATE:
1635 case MCI_WHERE:
1636 case MCI_STEP:
1637 case MCI_SPIN:
1638 case MCI_ESCAPE:
1639 case MCI_COPY:
1640 case MCI_CUT:
1641 case MCI_DELETE:
1642 case MCI_PASTE:
1643 FIXME("Unsupported yet command [%lu]\n", wMsg);
1644 break;
1645 case MCI_WINDOW:
1646 TRACE("Unsupported command [%lu]\n", wMsg);
1647 break;
1648 /* option which can be silenced */
1649 case MCI_CONFIGURE:
1650 return 0;
1651 case MCI_OPEN:
1652 case MCI_CLOSE:
1653 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1654 break;
1655 default:
1656 FIXME("is probably wrong msg [%lu]\n", wMsg);
1657 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1659 return MCIERR_UNRECOGNIZED_COMMAND;