Implement VerSetConditionMask by forwarding to ntdll.
[wine/hacks.git] / dlls / winmm / mciwave / mciwave.c
blob3c19c8b42646697a81d7a6c9705b3fdea0e8213f
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 "winerror.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "mmddk.h"
30 #include "wownt32.h"
31 #include "digitalv.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
36 typedef struct {
37 UINT wDevID;
38 HANDLE hWave;
39 int nUseCount; /* Incremented for each shared open */
40 BOOL fShareable; /* TRUE if first open was shareable */
41 HMMIO hFile; /* mmio file handle open as Element */
42 MCI_WAVE_OPEN_PARMSA openParms;
43 WAVEFORMATEX wfxRef;
44 LPWAVEFORMATEX lpWaveFormat;
45 BOOL fInput; /* FALSE = Output, TRUE = Input */
46 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
47 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
48 DWORD dwRemaining; /* remaining bytes to play or record */
49 DWORD dwPosition; /* position in bytes in chunk */
50 HANDLE hEvent; /* for synchronization */
51 DWORD dwEventCount; /* for synchronization */
52 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
53 MMCKINFO ckMainRIFF; /* main RIFF chunk */
54 MMCKINFO ckWaveData; /* data chunk */
55 } WINE_MCIWAVE;
57 /* ===================================================================
58 * ===================================================================
59 * FIXME: should be using the new mmThreadXXXX functions from WINMM
60 * instead of those
61 * it would require to add a wine internal flag to mmThreadCreate
62 * in order to pass a 32 bit function instead of a 16 bit one
63 * ===================================================================
64 * =================================================================== */
66 struct SCA {
67 UINT wDevID;
68 UINT wMsg;
69 DWORD dwParam1;
70 DWORD dwParam2;
73 /**************************************************************************
74 * MCI_SCAStarter [internal]
76 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
78 struct SCA* sca = (struct SCA*)arg;
79 DWORD ret;
81 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
82 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
83 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
84 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
85 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
86 HeapFree(GetProcessHeap(), 0, sca);
87 ExitThread(ret);
88 WARN("Should not happen ? what's wrong \n");
89 /* should not go after this point */
90 return ret;
93 /**************************************************************************
94 * MCI_SendCommandAsync [internal]
96 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
97 DWORD dwParam2, UINT size)
99 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
101 if (sca == 0)
102 return MCIERR_OUT_OF_MEMORY;
104 sca->wDevID = wDevID;
105 sca->wMsg = wMsg;
106 sca->dwParam1 = dwParam1;
108 if (size && dwParam2) {
109 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
110 /* copy structure passed by program in dwParam2 to be sure
111 * we can still use it whatever the program does
113 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
114 } else {
115 sca->dwParam2 = dwParam2;
118 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
119 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
120 return MCI_SCAStarter(&sca);
122 return 0;
125 /*======================================================================*
126 * MCI WAVE implemantation *
127 *======================================================================*/
129 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
131 /**************************************************************************
132 * MCIWAVE_drvOpen [internal]
134 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
136 WINE_MCIWAVE* wmw;
138 if (modp == NULL) return 0xFFFFFFFF;
140 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
142 if (!wmw)
143 return 0;
145 wmw->wDevID = modp->wDeviceID;
146 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
147 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
148 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
150 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
151 wmw->wfxRef.nChannels = 1; /* MONO */
152 wmw->wfxRef.nSamplesPerSec = 11025;
153 wmw->wfxRef.nAvgBytesPerSec = 11025;
154 wmw->wfxRef.nBlockAlign = 1;
155 wmw->wfxRef.wBitsPerSample = 8;
156 wmw->wfxRef.cbSize = 0; /* don't care */
158 return modp->wDeviceID;
161 /**************************************************************************
162 * MCIWAVE_drvClose [internal]
164 static DWORD WAVE_drvClose(DWORD dwDevID)
166 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
168 if (wmw) {
169 HeapFree(GetProcessHeap(), 0, wmw);
170 mciSetDriverData(dwDevID, 0);
171 return 1;
173 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
176 /**************************************************************************
177 * WAVE_mciGetOpenDev [internal]
179 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
181 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
183 if (wmw == NULL || wmw->nUseCount == 0) {
184 WARN("Invalid wDevID=%u\n", wDevID);
185 return 0;
187 return wmw;
190 /**************************************************************************
191 * WAVE_ConvertByteToTimeFormat [internal]
193 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
195 DWORD ret = 0;
197 switch (wmw->dwMciTimeFormat) {
198 case MCI_FORMAT_MILLISECONDS:
199 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
200 break;
201 case MCI_FORMAT_BYTES:
202 ret = val;
203 break;
204 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
205 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
206 break;
207 default:
208 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
210 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
211 *lpRet = 0;
212 return ret;
215 /**************************************************************************
216 * WAVE_ConvertTimeFormatToByte [internal]
218 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
220 DWORD ret = 0;
222 switch (wmw->dwMciTimeFormat) {
223 case MCI_FORMAT_MILLISECONDS:
224 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
225 break;
226 case MCI_FORMAT_BYTES:
227 ret = val;
228 break;
229 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
230 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
231 break;
232 default:
233 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
235 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
236 return ret;
239 /**************************************************************************
240 * WAVE_mciReadFmt [internal]
242 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
244 MMCKINFO mmckInfo;
245 long r;
247 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
248 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
249 return MCIERR_INVALID_FILE;
250 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
251 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
253 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
254 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
255 if (r < sizeof(WAVEFORMAT))
256 return MCIERR_INVALID_FILE;
258 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
259 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
260 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
261 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
262 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
263 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
264 if (r >= (long)sizeof(WAVEFORMATEX))
265 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
267 mmioAscend(wmw->hFile, &mmckInfo, 0);
268 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
269 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
270 TRACE("can't find data chunk\n");
271 return MCIERR_INVALID_FILE;
273 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
274 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
275 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
276 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
278 return 0;
281 /**************************************************************************
282 * WAVE_mciCreateRIFFSkeleton [internal]
284 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
286 MMCKINFO ckWaveFormat;
288 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
289 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
290 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
293 HMMIO hmmio = wmw->hFile;
295 lpckRIFF->ckid = FOURCC_RIFF;
296 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
297 lpckRIFF->cksize = 0;
299 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
300 goto err;
302 ckWaveFormat.fccType = 0;
303 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
304 ckWaveFormat.cksize = 16;
306 if (!lpWaveFormat)
308 /* FIXME: for non PCM formats, the size of the waveFormat has to be
309 * gotten
311 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
313 memcpy(lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
316 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
317 goto err;
319 /* only the first 16 bytes are serialized */
320 /* wrong... for non PCM, the whole waveFormat is stored
322 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
323 goto err;
325 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
326 goto err;
328 lpckWaveData->cksize = 0;
329 lpckWaveData->fccType = 0;
330 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
332 /* create data chunk */
333 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
334 goto err;
336 return 0;
338 err:
339 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
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 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
660 for (;;) {
661 ResetEvent(wmw->hEvent);
662 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
663 break;
665 InterlockedIncrement(&wmw->dwEventCount);
667 WaitForSingleObject(wmw->hEvent, INFINITE);
671 /**************************************************************************
672 * WAVE_mciPlay [internal]
674 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
676 DWORD end;
677 LONG bufsize, count, left;
678 DWORD dwRet = 0;
679 LPWAVEHDR waveHdr = NULL;
680 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
681 int whidx;
683 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
685 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
686 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
688 /* FIXME : since there is no way to determine in which mode the device is
689 * open (recording/playback) automatically switch from a mode to another
691 wmw->fInput = FALSE;
693 if (wmw->fInput) {
694 WARN("cannot play on input device\n");
695 return MCIERR_NONAPPLICABLE_FUNCTION;
698 if (wmw->hFile == 0) {
699 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
700 return MCIERR_FILE_NOT_FOUND;
703 if (wmw->dwStatus == MCI_MODE_PAUSE) {
704 /* FIXME: parameters (start/end) in lpParams may not be used */
705 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
708 /** This function will be called again by a thread when async is used.
709 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
710 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
712 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
713 return MCIERR_INTERNAL;
716 wmw->dwStatus = MCI_MODE_PLAY;
718 if (!(dwFlags & MCI_WAIT)) {
719 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
720 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
723 end = 0xFFFFFFFF;
724 if (lpParms && (dwFlags & MCI_FROM)) {
725 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
727 if (lpParms && (dwFlags & MCI_TO)) {
728 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
731 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
733 if (end <= wmw->dwPosition)
734 return TRUE;
737 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
738 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
740 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
741 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
743 if (dwRet == 0) {
744 if (wmw->lpWaveFormat) {
745 switch (wmw->lpWaveFormat->wFormatTag) {
746 case WAVE_FORMAT_PCM:
747 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
748 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
749 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
750 wmw->lpWaveFormat->nAvgBytesPerSec,
751 wmw->lpWaveFormat->nSamplesPerSec *
752 wmw->lpWaveFormat->nBlockAlign);
753 wmw->lpWaveFormat->nAvgBytesPerSec =
754 wmw->lpWaveFormat->nSamplesPerSec *
755 wmw->lpWaveFormat->nBlockAlign;
757 break;
760 } else {
761 TRACE("can't retrieve wave format %ld\n", dwRet);
762 goto cleanUp;
766 /* go back to beginning of chunk plus the requested position */
767 /* FIXME: I'm not sure this is correct, notably because some data linked to
768 * the decompression state machine will not be correcly initialized.
769 * try it this way (other way would be to decompress from 0 up to dwPosition
770 * and to start sending to hWave when dwPosition is reached)
772 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
774 /* By default the device will be opened for output, the MCI_CUE function is there to
775 * change from output to input and back
777 /* FIXME: how to choose between several output channels ? here mapper is forced */
778 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
779 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
781 if (dwRet != 0) {
782 TRACE("Can't open low level audio device %ld\n", dwRet);
783 dwRet = MCIERR_DEVICE_OPEN;
784 wmw->hWave = 0;
785 goto cleanUp;
788 /* make it so that 3 buffers per second are needed */
789 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
791 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
792 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
793 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
794 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
795 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
796 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
797 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
798 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
799 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
800 dwRet = MCIERR_INTERNAL;
801 goto cleanUp;
804 whidx = 0;
805 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
806 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
807 wmw->dwEventCount = 1L; /* for first buffer */
809 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
811 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
812 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
813 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
814 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
815 if (count < 1)
816 break;
817 /* count is always <= bufsize, so this is correct regarding the
818 * waveOutPrepareHeader function
820 waveHdr[whidx].dwBufferLength = count;
821 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
822 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
823 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
824 waveHdr[whidx].dwBytesRecorded);
825 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
826 left -= count;
827 wmw->dwPosition += count;
828 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
830 WAVE_mciPlayWaitDone(wmw);
831 whidx ^= 1;
834 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
836 /* just to get rid of some race conditions between play, stop and pause */
837 waveOutReset(wmw->hWave);
839 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
840 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
842 dwRet = 0;
844 cleanUp:
845 HeapFree(GetProcessHeap(), 0, waveHdr);
847 if (wmw->hWave) {
848 waveOutClose(wmw->hWave);
849 wmw->hWave = 0;
851 CloseHandle(wmw->hEvent);
853 if (lpParms && (dwFlags & MCI_NOTIFY)) {
854 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
855 wmw->openParms.wDeviceID,
856 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
859 wmw->dwStatus = MCI_MODE_STOP;
861 return dwRet;
864 /**************************************************************************
865 * WAVE_mciPlayCallback [internal]
867 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
868 DWORD dwInstance,
869 DWORD dwParam1, DWORD dwParam2)
871 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
872 LPWAVEHDR lpWaveHdr = NULL;
873 LONG count = 0;
874 switch (uMsg) {
875 case WIM_OPEN:
876 case WIM_CLOSE:
877 break;
878 case WIM_DATA:
879 lpWaveHdr = (LPWAVEHDR) dwParam1;
881 InterlockedIncrement(&wmw->dwEventCount);
883 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
885 lpWaveHdr->dwFlags &= ~WHDR_DONE;
886 wmw->dwPosition += count;
887 wmw->dwRemaining -= count;
889 if (wmw->dwStatus == MCI_MODE_RECORD)
891 /* Only queue up another buffer if we are recording. We could receive this
892 message also when waveInReset() is called, since it notifies on all wave
893 buffers that are outstanding. Queueing up more sometimes causes waveInClose
894 to fail. */
895 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
896 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
899 SetEvent(wmw->hEvent);
900 break;
901 default:
902 ERR("Unknown uMsg=%d\n", uMsg);
906 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
908 for (;;) {
909 ResetEvent(wmw->hEvent);
910 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
911 break;
913 InterlockedIncrement(&wmw->dwEventCount);
915 WaitForSingleObject(wmw->hEvent, INFINITE);
919 /**************************************************************************
920 * WAVE_mciRecord [internal]
922 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
924 DWORD end;
925 DWORD dwRet = 0;
926 LONG bufsize;
927 LPWAVEHDR waveHdr = NULL;
928 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
931 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
933 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
934 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
936 /* FIXME : since there is no way to determine in which mode the device is
937 * open (recording/playback) automatically switch from a mode to another
939 wmw->fInput = TRUE;
941 if (!wmw->fInput) {
942 WARN("cannot record on output device\n");
943 return MCIERR_NONAPPLICABLE_FUNCTION;
946 if (wmw->dwStatus == MCI_MODE_PAUSE) {
947 /* FIXME: parameters (start/end) in lpParams may not be used */
948 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
951 /** This function will be called again by a thread when async is used.
952 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
953 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
955 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
956 return MCIERR_INTERNAL;
959 wmw->dwStatus = MCI_MODE_RECORD;
961 if (!(dwFlags & MCI_WAIT)) {
962 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
963 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
966 if (!wmw->lpWaveFormat)
968 /* new RIFF file */
969 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
970 } else
972 FIXME("Should descend into data chunk. Please report.\n");
975 end = 0xFFFFFFFF;
976 if (lpParms && (dwFlags & MCI_FROM)) {
977 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
980 if (lpParms && (dwFlags & MCI_TO)) {
981 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
984 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
986 if (end <= wmw->dwPosition)
988 return TRUE;
991 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
992 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
994 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
995 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
997 /* go back to beginning of chunk plus the requested position */
998 /* FIXME: I'm not sure this is correct, notably because some data linked to
999 * the decompression state machine will not be correcly initialized.
1000 * try it this way (other way would be to decompress from 0 up to dwPosition
1001 * and to start sending to hWave when dwPosition is reached)
1003 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1005 /* By default the device will be opened for output, the MCI_CUE function is there to
1006 * change from output to input and back
1008 /* FIXME: how to choose between several output channels ? here mapper is forced */
1009 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1010 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1012 if (dwRet != 0) {
1013 TRACE("Can't open low level audio device %ld\n", dwRet);
1014 dwRet = MCIERR_DEVICE_OPEN;
1015 wmw->hWave = 0;
1016 goto cleanUp;
1019 /* make it so that 3 buffers per second are needed */
1020 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1022 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1023 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1024 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1025 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1026 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1027 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1028 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1030 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1031 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1032 dwRet = MCIERR_INTERNAL;
1033 goto cleanUp;
1036 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1037 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1038 dwRet = MCIERR_INTERNAL;
1039 goto cleanUp;
1042 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1043 wmw->dwEventCount = 1L; /* for first buffer */
1045 wmw->dwRemaining = end - wmw->dwPosition;
1047 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1049 dwRet = waveInStart(wmw->hWave);
1051 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1052 WAVE_mciRecordWaitDone(wmw);
1055 /* needed so that the callback above won't add again the buffers returned by the reset */
1056 wmw->dwStatus = MCI_MODE_STOP;
1058 waveInReset(wmw->hWave);
1060 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1061 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1063 dwRet = 0;
1065 cleanUp:
1066 HeapFree(GetProcessHeap(), 0, waveHdr);
1068 if (wmw->hWave) {
1069 waveInClose(wmw->hWave);
1070 wmw->hWave = 0;
1072 CloseHandle(wmw->hEvent);
1074 /* need to update the size of the data chunk */
1075 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1076 TRACE("failed on ascend\n");
1079 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1080 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1081 wmw->openParms.wDeviceID,
1082 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1085 wmw->dwStatus = MCI_MODE_STOP;
1087 return dwRet;
1091 /**************************************************************************
1092 * WAVE_mciPause [internal]
1094 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1096 DWORD dwRet;
1097 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1099 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1101 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1102 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1104 if (wmw->dwStatus == MCI_MODE_PLAY) {
1105 wmw->dwStatus = MCI_MODE_PAUSE;
1108 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1109 else dwRet = waveOutPause(wmw->hWave);
1111 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1114 /**************************************************************************
1115 * WAVE_mciResume [internal]
1117 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1119 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1120 DWORD dwRet = 0;
1122 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1124 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1126 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1127 wmw->dwStatus = MCI_MODE_PLAY;
1130 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1131 else dwRet = waveOutRestart(wmw->hWave);
1132 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1135 /**************************************************************************
1136 * WAVE_mciSeek [internal]
1138 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1140 DWORD ret = 0;
1141 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1143 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1145 if (lpParms == NULL) {
1146 ret = MCIERR_NULL_PARAMETER_BLOCK;
1147 } else if (wmw == NULL) {
1148 ret = MCIERR_INVALID_DEVICE_ID;
1149 } else {
1150 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1152 if (dwFlags & MCI_SEEK_TO_START) {
1153 wmw->dwPosition = 0;
1154 } else if (dwFlags & MCI_SEEK_TO_END) {
1155 wmw->dwPosition = wmw->ckWaveData.cksize;
1156 } else if (dwFlags & MCI_TO) {
1157 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1158 } else {
1159 WARN("dwFlag doesn't tell where to seek to...\n");
1160 return MCIERR_MISSING_PARAMETER;
1163 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1165 if (dwFlags & MCI_NOTIFY) {
1166 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1167 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1170 return ret;
1173 /**************************************************************************
1174 * WAVE_mciSet [internal]
1176 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1178 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1180 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1182 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1183 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1185 if (dwFlags & MCI_SET_TIME_FORMAT) {
1186 switch (lpParms->dwTimeFormat) {
1187 case MCI_FORMAT_MILLISECONDS:
1188 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1189 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1190 break;
1191 case MCI_FORMAT_BYTES:
1192 TRACE("MCI_FORMAT_BYTES !\n");
1193 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1194 break;
1195 case MCI_FORMAT_SAMPLES:
1196 TRACE("MCI_FORMAT_SAMPLES !\n");
1197 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1198 break;
1199 default:
1200 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1201 return MCIERR_BAD_TIME_FORMAT;
1204 if (dwFlags & MCI_SET_VIDEO) {
1205 TRACE("No support for video !\n");
1206 return MCIERR_UNSUPPORTED_FUNCTION;
1208 if (dwFlags & MCI_SET_DOOR_OPEN) {
1209 TRACE("No support for door open !\n");
1210 return MCIERR_UNSUPPORTED_FUNCTION;
1212 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1213 TRACE("No support for door close !\n");
1214 return MCIERR_UNSUPPORTED_FUNCTION;
1216 if (dwFlags & MCI_SET_AUDIO) {
1217 if (dwFlags & MCI_SET_ON) {
1218 TRACE("MCI_SET_ON audio !\n");
1219 } else if (dwFlags & MCI_SET_OFF) {
1220 TRACE("MCI_SET_OFF audio !\n");
1221 } else {
1222 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1223 return MCIERR_BAD_INTEGER;
1226 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1227 TRACE("MCI_SET_AUDIO_ALL !\n");
1228 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1229 TRACE("MCI_SET_AUDIO_LEFT !\n");
1230 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1231 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1233 if (dwFlags & MCI_WAVE_INPUT)
1234 TRACE("MCI_WAVE_INPUT !\n");
1235 if (dwFlags & MCI_WAVE_OUTPUT)
1236 TRACE("MCI_WAVE_OUTPUT !\n");
1237 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1238 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1239 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1240 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1241 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1242 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1243 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1245 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1246 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1247 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1249 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1250 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1251 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1253 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1254 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1255 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1257 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1258 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1259 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1261 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1262 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1263 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1265 return 0;
1268 /**************************************************************************
1269 * WAVE_mciSave [internal]
1271 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1273 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1274 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1275 WPARAM wparam = MCI_NOTIFY_FAILURE;
1277 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1278 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1279 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1281 if (dwFlags & MCI_WAIT)
1283 FIXME("MCI_WAIT not implemented\n");
1286 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1287 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1290 ret = mmioClose(wmw->hFile, 0);
1293 If the destination file already exists, it has to be overwritten. (Behaviour
1294 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1295 my applications. We are making use of mmioRename, which WILL NOT overwrite
1296 the destination file (which is what Windows does, also verified in Win2K)
1297 So, lets delete the destination file before calling mmioRename. If the
1298 destination file DOESN'T exist, the delete will fail silently. Let's also be
1299 careful not to lose our previous error code.
1301 tmpRet = GetLastError();
1302 DeleteFileA (lpParms->lpfilename);
1303 SetLastError(tmpRet);
1305 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1306 ret = ERROR_SUCCESS;
1309 if (dwFlags & MCI_NOTIFY) {
1310 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1312 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1313 wmw->openParms.wDeviceID, wparam);
1316 return ret;
1319 /**************************************************************************
1320 * WAVE_mciStatus [internal]
1322 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1324 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1325 DWORD ret = 0;
1327 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1328 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1329 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1331 if (dwFlags & MCI_STATUS_ITEM) {
1332 switch (lpParms->dwItem) {
1333 case MCI_STATUS_CURRENT_TRACK:
1334 lpParms->dwReturn = 1;
1335 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1336 break;
1337 case MCI_STATUS_LENGTH:
1338 if (!wmw->hFile) {
1339 lpParms->dwReturn = 0;
1340 return MCIERR_UNSUPPORTED_FUNCTION;
1342 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1343 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1344 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1345 break;
1346 case MCI_STATUS_MODE:
1347 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1348 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1349 ret = MCI_RESOURCE_RETURNED;
1350 break;
1351 case MCI_STATUS_MEDIA_PRESENT:
1352 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1353 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1354 ret = MCI_RESOURCE_RETURNED;
1355 break;
1356 case MCI_STATUS_NUMBER_OF_TRACKS:
1357 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1358 lpParms->dwReturn = 1;
1359 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1360 break;
1361 case MCI_STATUS_POSITION:
1362 if (!wmw->hFile) {
1363 lpParms->dwReturn = 0;
1364 return MCIERR_UNSUPPORTED_FUNCTION;
1366 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1367 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1368 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1369 &ret);
1370 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1371 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1372 break;
1373 case MCI_STATUS_READY:
1374 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1375 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1376 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1377 ret = MCI_RESOURCE_RETURNED;
1378 break;
1379 case MCI_STATUS_TIME_FORMAT:
1380 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1381 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1382 ret = MCI_RESOURCE_RETURNED;
1383 break;
1384 case MCI_WAVE_INPUT:
1385 TRACE("MCI_WAVE_INPUT !\n");
1386 lpParms->dwReturn = 0;
1387 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1388 break;
1389 case MCI_WAVE_OUTPUT:
1390 TRACE("MCI_WAVE_OUTPUT !\n");
1392 UINT id;
1393 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1394 lpParms->dwReturn = id;
1395 } else {
1396 lpParms->dwReturn = 0;
1397 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1400 break;
1401 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1402 if (!wmw->hFile) {
1403 lpParms->dwReturn = 0;
1404 return MCIERR_UNSUPPORTED_FUNCTION;
1406 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1407 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1408 break;
1409 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1410 if (!wmw->hFile) {
1411 lpParms->dwReturn = 0;
1412 return MCIERR_UNSUPPORTED_FUNCTION;
1414 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1415 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1416 break;
1417 case MCI_WAVE_STATUS_BLOCKALIGN:
1418 if (!wmw->hFile) {
1419 lpParms->dwReturn = 0;
1420 return MCIERR_UNSUPPORTED_FUNCTION;
1422 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1423 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1424 break;
1425 case MCI_WAVE_STATUS_CHANNELS:
1426 if (!wmw->hFile) {
1427 lpParms->dwReturn = 0;
1428 return MCIERR_UNSUPPORTED_FUNCTION;
1430 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1431 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1432 break;
1433 case MCI_WAVE_STATUS_FORMATTAG:
1434 if (!wmw->hFile) {
1435 lpParms->dwReturn = 0;
1436 return MCIERR_UNSUPPORTED_FUNCTION;
1438 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1439 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1440 break;
1441 case MCI_WAVE_STATUS_LEVEL:
1442 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1443 lpParms->dwReturn = 0xAAAA5555;
1444 break;
1445 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1446 if (!wmw->hFile) {
1447 lpParms->dwReturn = 0;
1448 return MCIERR_UNSUPPORTED_FUNCTION;
1450 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1451 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1452 break;
1453 default:
1454 WARN("unknown command %08lX !\n", lpParms->dwItem);
1455 return MCIERR_UNRECOGNIZED_COMMAND;
1458 if (dwFlags & MCI_NOTIFY) {
1459 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1460 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1462 return ret;
1465 /**************************************************************************
1466 * WAVE_mciGetDevCaps [internal]
1468 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1469 LPMCI_GETDEVCAPS_PARMS lpParms)
1471 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1472 DWORD ret = 0;
1474 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1476 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1477 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1479 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1480 switch(lpParms->dwItem) {
1481 case MCI_GETDEVCAPS_DEVICE_TYPE:
1482 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1483 ret = MCI_RESOURCE_RETURNED;
1484 break;
1485 case MCI_GETDEVCAPS_HAS_AUDIO:
1486 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1487 ret = MCI_RESOURCE_RETURNED;
1488 break;
1489 case MCI_GETDEVCAPS_HAS_VIDEO:
1490 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1491 ret = MCI_RESOURCE_RETURNED;
1492 break;
1493 case MCI_GETDEVCAPS_USES_FILES:
1494 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1495 ret = MCI_RESOURCE_RETURNED;
1496 break;
1497 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1498 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1499 ret = MCI_RESOURCE_RETURNED;
1500 break;
1501 case MCI_GETDEVCAPS_CAN_RECORD:
1502 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1503 ret = MCI_RESOURCE_RETURNED;
1504 break;
1505 case MCI_GETDEVCAPS_CAN_EJECT:
1506 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1507 ret = MCI_RESOURCE_RETURNED;
1508 break;
1509 case MCI_GETDEVCAPS_CAN_PLAY:
1510 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1511 ret = MCI_RESOURCE_RETURNED;
1512 break;
1513 case MCI_GETDEVCAPS_CAN_SAVE:
1514 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1515 ret = MCI_RESOURCE_RETURNED;
1516 break;
1517 case MCI_WAVE_GETDEVCAPS_INPUTS:
1518 lpParms->dwReturn = 1;
1519 break;
1520 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1521 lpParms->dwReturn = 1;
1522 break;
1523 default:
1524 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1525 return MCIERR_UNRECOGNIZED_COMMAND;
1527 } else {
1528 WARN("No GetDevCaps-Item !\n");
1529 return MCIERR_UNRECOGNIZED_COMMAND;
1531 return ret;
1534 /**************************************************************************
1535 * WAVE_mciInfo [internal]
1537 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1539 DWORD ret = 0;
1540 LPCSTR str = 0;
1541 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1543 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1545 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1546 ret = MCIERR_NULL_PARAMETER_BLOCK;
1547 } else if (wmw == NULL) {
1548 ret = MCIERR_INVALID_DEVICE_ID;
1549 } else {
1550 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1552 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1553 case MCI_INFO_PRODUCT:
1554 str = "Wine's audio player";
1555 break;
1556 case MCI_INFO_FILE:
1557 str = wmw->openParms.lpstrElementName;
1558 break;
1559 case MCI_WAVE_INPUT:
1560 str = "Wine Wave In";
1561 break;
1562 case MCI_WAVE_OUTPUT:
1563 str = "Wine Wave Out";
1564 break;
1565 default:
1566 WARN("Don't know this info command (%lu)\n", dwFlags);
1567 ret = MCIERR_UNRECOGNIZED_COMMAND;
1570 if (str) {
1571 if (strlen(str) + 1 > lpParms->dwRetSize) {
1572 ret = MCIERR_PARAM_OVERFLOW;
1573 } else {
1574 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1576 } else {
1577 lpParms->lpstrReturn[0] = 0;
1580 return ret;
1583 /**************************************************************************
1584 * DriverProc (MCIWAVE.@)
1586 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1587 DWORD dwParam1, DWORD dwParam2)
1589 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1590 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1592 switch (wMsg) {
1593 case DRV_LOAD: return 1;
1594 case DRV_FREE: return 1;
1595 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1596 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1597 case DRV_ENABLE: return 1;
1598 case DRV_DISABLE: return 1;
1599 case DRV_QUERYCONFIGURE: return 1;
1600 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1601 case DRV_INSTALL: return DRVCNF_RESTART;
1602 case DRV_REMOVE: return DRVCNF_RESTART;
1605 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1607 switch (wMsg) {
1608 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1609 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1610 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1611 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1612 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1613 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1614 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1615 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1616 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1617 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1618 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1619 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1620 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1621 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1622 /* commands that should be supported */
1623 case MCI_LOAD:
1624 case MCI_FREEZE:
1625 case MCI_PUT:
1626 case MCI_REALIZE:
1627 case MCI_UNFREEZE:
1628 case MCI_UPDATE:
1629 case MCI_WHERE:
1630 case MCI_STEP:
1631 case MCI_SPIN:
1632 case MCI_ESCAPE:
1633 case MCI_COPY:
1634 case MCI_CUT:
1635 case MCI_DELETE:
1636 case MCI_PASTE:
1637 FIXME("Unsupported yet command [%lu]\n", wMsg);
1638 break;
1639 case MCI_WINDOW:
1640 TRACE("Unsupported command [%lu]\n", wMsg);
1641 break;
1642 /* option which can be silenced */
1643 case MCI_CONFIGURE:
1644 return 0;
1645 case MCI_OPEN:
1646 case MCI_CLOSE:
1647 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1648 break;
1649 default:
1650 FIXME("is probably wrong msg [%lu]\n", wMsg);
1651 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1653 return MCIERR_UNRECOGNIZED_COMMAND;