Release 20030408.
[wine/gsoc-2012-control.git] / dlls / winmm / mciwave / mciwave.c
blob470cc3d39287a2d79414b21c46896baa9da97097
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 dwPosition; /* position in bytes in chunk */
49 HANDLE hEvent; /* for synchronization */
50 DWORD dwEventCount; /* for synchronization */
51 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
52 MMCKINFO ckMainRIFF; /* main RIFF chunk */
53 MMCKINFO ckWaveData; /* data chunk */
54 } WINE_MCIWAVE;
56 /* ===================================================================
57 * ===================================================================
58 * FIXME: should be using the new mmThreadXXXX functions from WINMM
59 * instead of those
60 * it would require to add a wine internal flag to mmThreadCreate
61 * in order to pass a 32 bit function instead of a 16 bit one
62 * ===================================================================
63 * =================================================================== */
65 struct SCA {
66 UINT wDevID;
67 UINT wMsg;
68 DWORD dwParam1;
69 DWORD dwParam2;
72 /**************************************************************************
73 * MCI_SCAStarter [internal]
75 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
77 struct SCA* sca = (struct SCA*)arg;
78 DWORD ret;
80 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
81 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
82 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
83 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
84 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
85 HeapFree(GetProcessHeap(), 0, sca);
86 ExitThread(ret);
87 WARN("Should not happen ? what's wrong \n");
88 /* should not go after this point */
89 return ret;
92 /**************************************************************************
93 * MCI_SendCommandAsync [internal]
95 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
96 DWORD dwParam2, UINT size)
98 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
100 if (sca == 0)
101 return MCIERR_OUT_OF_MEMORY;
103 sca->wDevID = wDevID;
104 sca->wMsg = wMsg;
105 sca->dwParam1 = dwParam1;
107 if (size && dwParam2) {
108 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
109 /* copy structure passed by program in dwParam2 to be sure
110 * we can still use it whatever the program does
112 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
113 } else {
114 sca->dwParam2 = dwParam2;
117 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
118 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
119 return MCI_SCAStarter(&sca);
121 return 0;
124 /*======================================================================*
125 * MCI WAVE implemantation *
126 *======================================================================*/
128 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
130 /**************************************************************************
131 * MCIWAVE_drvOpen [internal]
133 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
135 WINE_MCIWAVE* wmw;
137 if (modp == NULL) return 0xFFFFFFFF;
139 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
141 if (!wmw)
142 return 0;
144 wmw->wDevID = modp->wDeviceID;
145 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
146 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
147 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
149 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
150 wmw->wfxRef.nChannels = 1; /* MONO */
151 wmw->wfxRef.nSamplesPerSec = 11025;
152 wmw->wfxRef.nAvgBytesPerSec = 11025;
153 wmw->wfxRef.nBlockAlign = 1;
154 wmw->wfxRef.wBitsPerSample = 8;
155 wmw->wfxRef.cbSize = 0; /* don't care */
157 return modp->wDeviceID;
160 /**************************************************************************
161 * MCIWAVE_drvClose [internal]
163 static DWORD WAVE_drvClose(DWORD dwDevID)
165 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
167 if (wmw) {
168 HeapFree(GetProcessHeap(), 0, wmw);
169 mciSetDriverData(dwDevID, 0);
170 return 1;
172 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
175 /**************************************************************************
176 * WAVE_mciGetOpenDev [internal]
178 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
180 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
182 if (wmw == NULL || wmw->nUseCount == 0) {
183 WARN("Invalid wDevID=%u\n", wDevID);
184 return 0;
186 return wmw;
189 /**************************************************************************
190 * WAVE_ConvertByteToTimeFormat [internal]
192 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
194 DWORD ret = 0;
196 switch (wmw->dwMciTimeFormat) {
197 case MCI_FORMAT_MILLISECONDS:
198 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
199 break;
200 case MCI_FORMAT_BYTES:
201 ret = val;
202 break;
203 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
204 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
205 break;
206 default:
207 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
209 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
210 *lpRet = 0;
211 return ret;
214 /**************************************************************************
215 * WAVE_ConvertTimeFormatToByte [internal]
217 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
219 DWORD ret = 0;
221 switch (wmw->dwMciTimeFormat) {
222 case MCI_FORMAT_MILLISECONDS:
223 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
224 break;
225 case MCI_FORMAT_BYTES:
226 ret = val;
227 break;
228 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
229 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
230 break;
231 default:
232 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
234 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
235 return ret;
238 /**************************************************************************
239 * WAVE_mciReadFmt [internal]
241 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
243 MMCKINFO mmckInfo;
244 long r;
246 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
247 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
248 return MCIERR_INVALID_FILE;
249 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
250 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
252 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
253 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
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;
287 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
288 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
290 lpckRIFF->ckid = FOURCC_RIFF;
291 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
292 lpckRIFF->cksize = 0;
294 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
295 goto err;
297 ckWaveFormat.fccType = 0;
298 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
299 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
301 if (!wmw->lpWaveFormat)
303 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
304 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
305 memcpy(wmw->lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
308 /* we can only record PCM files... there is no way in the MCI API to specify
309 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
310 * structure
312 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
313 goto err;
315 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
316 goto err;
318 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
319 goto err;
321 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
322 goto err;
324 lpckWaveData->cksize = 0;
325 lpckWaveData->fccType = 0;
326 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
328 /* create data chunk */
329 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
330 goto err;
332 return 0;
334 err:
335 if (wmw->lpWaveFormat)
336 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
337 wmw->lpWaveFormat = NULL;
338 return MCIERR_INVALID_FILE;
341 /**************************************************************************
342 * WAVE_mciOpen [internal]
344 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
346 DWORD dwRet = 0;
347 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
348 CHAR* pszTmpFileName = 0;
350 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
351 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
352 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
354 if (dwFlags & MCI_OPEN_SHAREABLE)
355 return MCIERR_HARDWARE;
357 if (wmw->nUseCount > 0) {
358 /* The driver is already opened on this channel
359 * Wave driver cannot be shared
361 return MCIERR_DEVICE_OPEN;
364 wmw->nUseCount++;
366 wmw->fInput = FALSE;
367 wmw->hWave = 0;
368 wmw->dwStatus = MCI_MODE_NOT_READY;
370 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
372 if (dwFlags & MCI_OPEN_ELEMENT) {
373 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
374 /* could it be that (DWORD)lpOpenParms->lpstrElementName
375 * contains the hFile value ?
377 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
378 } else {
379 if (strlen(lpOpenParms->lpstrElementName) > 0) {
380 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
382 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
383 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
385 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
386 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
387 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
389 if (wmw->hFile == 0) {
390 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
391 dwRet = MCIERR_FILE_NOT_FOUND;
393 else
395 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
397 /* make sure we're are the beginning of the file */
398 mmioSeek(wmw->hFile, 0, SEEK_SET);
400 /* first reading of this file. read the waveformat chunk */
401 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
402 dwRet = MCIERR_INVALID_FILE;
403 } else {
404 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
405 (LPSTR)&(lpckMainRIFF->ckid),
406 (LPSTR) &(lpckMainRIFF->fccType),
407 (lpckMainRIFF->cksize));
409 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
410 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
411 dwRet = MCIERR_INVALID_FILE;
412 } else {
413 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
418 else {
419 wmw->hFile = 0;
422 else {
423 CHAR szTmpPath[MAX_PATH];
424 CHAR szPrefix[4] = "TMP\0";
426 pszTmpFileName = HeapAlloc(GetProcessHeap(),
427 HEAP_ZERO_MEMORY,
428 MAX_PATH * sizeof(*pszTmpFileName));
430 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
431 WARN("can't retrieve temp path!\n");
432 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
433 return MCIERR_FILE_NOT_FOUND;
436 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
437 WARN("can't retrieve temp file name!\n");
438 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
439 return MCIERR_FILE_NOT_FOUND;
442 wmw->bTemporaryFile = TRUE;
444 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
446 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
448 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
449 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
451 if (wmw->hFile == 0) {
452 /* temporary file could not be created. clean filename. */
453 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
454 WARN("can't create file='%s' !\n", pszTmpFileName);
455 dwRet = MCIERR_FILE_NOT_FOUND;
462 TRACE("hFile=%p\n", wmw->hFile);
464 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
466 if (wmw->bTemporaryFile == TRUE)
468 /* Additional openParms is temporary file's name */
469 wmw->openParms.lpstrElementName = pszTmpFileName;
472 if (dwRet == 0) {
473 if (wmw->lpWaveFormat) {
474 switch (wmw->lpWaveFormat->wFormatTag) {
475 case WAVE_FORMAT_PCM:
476 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
477 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
478 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
479 wmw->lpWaveFormat->nAvgBytesPerSec,
480 wmw->lpWaveFormat->nSamplesPerSec *
481 wmw->lpWaveFormat->nBlockAlign);
482 wmw->lpWaveFormat->nAvgBytesPerSec =
483 wmw->lpWaveFormat->nSamplesPerSec *
484 wmw->lpWaveFormat->nBlockAlign;
486 break;
489 wmw->dwPosition = 0;
491 wmw->dwStatus = MCI_MODE_STOP;
492 } else {
493 wmw->nUseCount--;
494 if (wmw->hFile != 0)
495 mmioClose(wmw->hFile, 0);
496 wmw->hFile = 0;
498 return dwRet;
501 /**************************************************************************
502 * WAVE_mciCue [internal]
504 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
507 FIXME
509 This routine is far from complete. At the moment only a check is done on the
510 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
511 is the default.
513 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
514 are ignored
517 DWORD dwRet;
518 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
520 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
522 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
524 /* always close elements ? */
525 if (wmw->hFile != 0) {
526 mmioClose(wmw->hFile, 0);
527 wmw->hFile = 0;
530 dwRet = MMSYSERR_NOERROR; /* assume success */
532 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
533 dwRet = waveOutClose(wmw->hWave);
534 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
535 wmw->fInput = TRUE;
536 } else if (wmw->fInput) {
537 dwRet = waveInClose(wmw->hWave);
538 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
539 wmw->fInput = FALSE;
541 wmw->hWave = 0;
542 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
545 /**************************************************************************
546 * WAVE_mciStop [internal]
548 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
550 DWORD dwRet = 0;
551 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
553 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
555 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
557 /* wait for playback thread (if any) to exit before processing further */
558 switch (wmw->dwStatus) {
559 case MCI_MODE_PAUSE:
560 case MCI_MODE_PLAY:
561 case MCI_MODE_RECORD:
563 int oldStat = wmw->dwStatus;
564 wmw->dwStatus = MCI_MODE_NOT_READY;
565 if (oldStat == MCI_MODE_PAUSE)
566 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
568 while (wmw->dwStatus != MCI_MODE_STOP)
569 Sleep(10);
570 break;
573 wmw->dwPosition = 0;
575 /* sanity resets */
576 wmw->dwStatus = MCI_MODE_STOP;
578 if ((dwFlags & MCI_NOTIFY) && lpParms) {
579 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
580 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
583 return dwRet;
586 /**************************************************************************
587 * WAVE_mciClose [internal]
589 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
591 DWORD dwRet = 0;
592 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
594 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
596 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
598 if (wmw->dwStatus != MCI_MODE_STOP) {
599 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
602 wmw->nUseCount--;
604 if (wmw->nUseCount == 0) {
605 if (wmw->hFile != 0) {
606 mmioClose(wmw->hFile, 0);
607 wmw->hFile = 0;
611 /* That string got allocated in mciOpen because no filename was specified
612 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
613 * allocated by mciOpen, *NOT* the application.
615 if (wmw->bTemporaryFile)
617 HeapFree(GetProcessHeap(), 0, (char*)wmw->openParms.lpstrElementName);
618 wmw->openParms.lpstrElementName = NULL;
621 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
622 wmw->lpWaveFormat = NULL;
624 if ((dwFlags & MCI_NOTIFY) && lpParms) {
625 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
626 wmw->openParms.wDeviceID,
627 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
630 return 0;
633 /**************************************************************************
634 * WAVE_mciPlayCallback [internal]
636 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
637 DWORD dwInstance,
638 DWORD dwParam1, DWORD dwParam2)
640 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
642 switch (uMsg) {
643 case WOM_OPEN:
644 case WOM_CLOSE:
645 break;
646 case WOM_DONE:
647 InterlockedIncrement(&wmw->dwEventCount);
648 TRACE("Returning waveHdr=%lx\n", dwParam1);
649 SetEvent(wmw->hEvent);
650 break;
651 default:
652 ERR("Unknown uMsg=%d\n", uMsg);
656 /******************************************************************
657 * WAVE_mciPlayWaitDone
661 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
663 for (;;) {
664 ResetEvent(wmw->hEvent);
665 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
666 break;
668 InterlockedIncrement(&wmw->dwEventCount);
670 WaitForSingleObject(wmw->hEvent, INFINITE);
674 /**************************************************************************
675 * WAVE_mciPlay [internal]
677 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
679 DWORD end;
680 LONG bufsize, count, left;
681 DWORD dwRet = 0;
682 LPWAVEHDR waveHdr = NULL;
683 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
684 int whidx;
686 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
688 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
689 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
691 /* FIXME : since there is no way to determine in which mode the device is
692 * open (recording/playback) automatically switch from a mode to another
694 wmw->fInput = FALSE;
696 if (wmw->fInput) {
697 WARN("cannot play on input device\n");
698 return MCIERR_NONAPPLICABLE_FUNCTION;
701 if (wmw->hFile == 0) {
702 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
703 return MCIERR_FILE_NOT_FOUND;
706 if (wmw->dwStatus == MCI_MODE_PAUSE) {
707 /* FIXME: parameters (start/end) in lpParams may not be used */
708 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
711 /** This function will be called again by a thread when async is used.
712 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
713 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
715 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
716 return MCIERR_INTERNAL;
719 wmw->dwStatus = MCI_MODE_PLAY;
721 if (!(dwFlags & MCI_WAIT)) {
722 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
723 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
726 end = 0xFFFFFFFF;
727 if (lpParms && (dwFlags & MCI_FROM)) {
728 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
730 if (lpParms && (dwFlags & MCI_TO)) {
731 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
734 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
736 if (end <= wmw->dwPosition)
737 return TRUE;
740 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
741 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
743 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
744 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
746 if (dwRet == 0) {
747 if (wmw->lpWaveFormat) {
748 switch (wmw->lpWaveFormat->wFormatTag) {
749 case WAVE_FORMAT_PCM:
750 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
751 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
752 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
753 wmw->lpWaveFormat->nAvgBytesPerSec,
754 wmw->lpWaveFormat->nSamplesPerSec *
755 wmw->lpWaveFormat->nBlockAlign);
756 wmw->lpWaveFormat->nAvgBytesPerSec =
757 wmw->lpWaveFormat->nSamplesPerSec *
758 wmw->lpWaveFormat->nBlockAlign;
760 break;
763 } else {
764 TRACE("can't retrieve wave format %ld\n", dwRet);
765 goto cleanUp;
769 /* go back to beginning of chunk plus the requested position */
770 /* FIXME: I'm not sure this is correct, notably because some data linked to
771 * the decompression state machine will not be correcly initialized.
772 * try it this way (other way would be to decompress from 0 up to dwPosition
773 * and to start sending to hWave when dwPosition is reached)
775 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
777 /* By default the device will be opened for output, the MCI_CUE function is there to
778 * change from output to input and back
780 /* FIXME: how to choose between several output channels ? here mapper is forced */
781 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
782 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
784 if (dwRet != 0) {
785 TRACE("Can't open low level audio device %ld\n", dwRet);
786 dwRet = MCIERR_DEVICE_OPEN;
787 wmw->hWave = 0;
788 goto cleanUp;
791 /* make it so that 3 buffers per second are needed */
792 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
794 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
795 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
796 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
797 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
798 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
799 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
800 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
801 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
802 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
803 dwRet = MCIERR_INTERNAL;
804 goto cleanUp;
807 whidx = 0;
808 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
809 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
810 wmw->dwEventCount = 1L; /* for first buffer */
812 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
814 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
815 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
816 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
817 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
818 if (count < 1)
819 break;
820 /* count is always <= bufsize, so this is correct regarding the
821 * waveOutPrepareHeader function
823 waveHdr[whidx].dwBufferLength = count;
824 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
825 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
826 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
827 waveHdr[whidx].dwBytesRecorded);
828 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
829 left -= count;
830 wmw->dwPosition += count;
831 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
833 WAVE_mciPlayWaitDone(wmw);
834 whidx ^= 1;
837 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
839 /* just to get rid of some race conditions between play, stop and pause */
840 waveOutReset(wmw->hWave);
842 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
843 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
845 dwRet = 0;
847 cleanUp:
848 HeapFree(GetProcessHeap(), 0, waveHdr);
850 if (wmw->hWave) {
851 waveOutClose(wmw->hWave);
852 wmw->hWave = 0;
854 CloseHandle(wmw->hEvent);
856 if (lpParms && (dwFlags & MCI_NOTIFY)) {
857 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
858 wmw->openParms.wDeviceID,
859 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
862 wmw->dwStatus = MCI_MODE_STOP;
864 return dwRet;
867 /**************************************************************************
868 * WAVE_mciPlayCallback [internal]
870 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
871 DWORD dwInstance,
872 DWORD dwParam1, DWORD dwParam2)
874 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
875 LPWAVEHDR lpWaveHdr;
876 LONG count = 0;
878 switch (uMsg) {
879 case WIM_OPEN:
880 case WIM_CLOSE:
881 break;
882 case WIM_DATA:
883 lpWaveHdr = (LPWAVEHDR) dwParam1;
885 InterlockedIncrement(&wmw->dwEventCount);
887 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
889 lpWaveHdr->dwFlags &= ~WHDR_DONE;
890 if (count > 0)
891 wmw->dwPosition += count;
892 /* else error reporting ?? */
893 if (wmw->dwStatus == MCI_MODE_RECORD)
895 /* Only queue up another buffer if we are recording. We could receive this
896 message also when waveInReset() is called, since it notifies on all wave
897 buffers that are outstanding. Queueing up more sometimes causes waveInClose
898 to fail. */
899 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
900 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
903 SetEvent(wmw->hEvent);
904 break;
905 default:
906 ERR("Unknown uMsg=%d\n", uMsg);
910 /******************************************************************
911 * bWAVE_mciRecordWaitDone
914 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
916 for (;;) {
917 ResetEvent(wmw->hEvent);
918 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
919 break;
921 InterlockedIncrement(&wmw->dwEventCount);
923 WaitForSingleObject(wmw->hEvent, INFINITE);
927 /**************************************************************************
928 * WAVE_mciRecord [internal]
930 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
932 DWORD end;
933 DWORD dwRet = MMSYSERR_NOERROR;
934 LONG bufsize;
935 LPWAVEHDR waveHdr = NULL;
936 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
939 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
941 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
942 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
944 /* FIXME : since there is no way to determine in which mode the device is
945 * open (recording/playback) automatically switch from a mode to another
947 wmw->fInput = TRUE;
949 if (!wmw->fInput) {
950 WARN("cannot record on output device\n");
951 return MCIERR_NONAPPLICABLE_FUNCTION;
954 if (wmw->dwStatus == MCI_MODE_PAUSE) {
955 /* FIXME: parameters (start/end) in lpParams may not be used */
956 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
959 /** This function will be called again by a thread when async is used.
960 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
961 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
963 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
964 return MCIERR_INTERNAL;
967 wmw->dwStatus = MCI_MODE_RECORD;
969 if (!(dwFlags & MCI_WAIT)) {
970 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
971 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
974 if (!wmw->lpWaveFormat) {
975 /* new RIFF file */
976 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
977 } else {
978 FIXME("Should descend into data chunk. Please report.\n");
981 end = 0xFFFFFFFF;
982 if (lpParms && (dwFlags & MCI_FROM)) {
983 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
986 if (lpParms && (dwFlags & MCI_TO)) {
987 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
990 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
992 if (end <= wmw->dwPosition)
994 return TRUE;
997 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
998 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1000 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1001 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1003 /* go back to beginning of chunk plus the requested position */
1004 /* FIXME: I'm not sure this is correct, notably because some data linked to
1005 * the decompression state machine will not be correcly initialized.
1006 * try it this way (other way would be to decompress from 0 up to dwPosition
1007 * and to start sending to hWave when dwPosition is reached)
1009 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1011 /* By default the device will be opened for output, the MCI_CUE function is there to
1012 * change from output to input and back
1014 /* FIXME: how to choose between several output channels ? here mapper is forced */
1015 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1016 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1018 if (dwRet != MMSYSERR_NOERROR) {
1019 TRACE("Can't open low level audio device %ld\n", dwRet);
1020 dwRet = MCIERR_DEVICE_OPEN;
1021 wmw->hWave = 0;
1022 goto cleanUp;
1025 /* make it so that 3 buffers per second are needed */
1026 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1028 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1029 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1030 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1031 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1032 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1033 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1034 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1036 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1037 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1038 dwRet = MCIERR_INTERNAL;
1039 goto cleanUp;
1042 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1043 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1044 dwRet = MCIERR_INTERNAL;
1045 goto cleanUp;
1048 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1049 wmw->dwEventCount = 1L; /* for first buffer */
1051 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1053 dwRet = waveInStart(wmw->hWave);
1055 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1056 WAVE_mciRecordWaitDone(wmw);
1059 /* needed so that the callback above won't add again the buffers returned by the reset */
1060 wmw->dwStatus = MCI_MODE_STOP;
1062 waveInReset(wmw->hWave);
1064 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1065 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1067 dwRet = 0;
1069 cleanUp:
1070 HeapFree(GetProcessHeap(), 0, waveHdr);
1072 if (wmw->hWave) {
1073 waveInClose(wmw->hWave);
1074 wmw->hWave = 0;
1076 CloseHandle(wmw->hEvent);
1078 /* need to update the size of the data chunk */
1079 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1080 TRACE("failed on ascend\n");
1083 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1084 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1085 wmw->openParms.wDeviceID,
1086 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1089 wmw->dwStatus = MCI_MODE_STOP;
1091 return dwRet;
1095 /**************************************************************************
1096 * WAVE_mciPause [internal]
1098 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1100 DWORD dwRet;
1101 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1103 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1105 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1106 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1108 if (wmw->dwStatus == MCI_MODE_PLAY) {
1109 wmw->dwStatus = MCI_MODE_PAUSE;
1112 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1113 else dwRet = waveOutPause(wmw->hWave);
1115 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1118 /**************************************************************************
1119 * WAVE_mciResume [internal]
1121 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1123 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1124 DWORD dwRet = 0;
1126 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1128 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1130 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1131 wmw->dwStatus = MCI_MODE_PLAY;
1134 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1135 else dwRet = waveOutRestart(wmw->hWave);
1136 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1139 /**************************************************************************
1140 * WAVE_mciSeek [internal]
1142 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1144 DWORD ret = 0;
1145 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1147 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1149 if (lpParms == NULL) {
1150 ret = MCIERR_NULL_PARAMETER_BLOCK;
1151 } else if (wmw == NULL) {
1152 ret = MCIERR_INVALID_DEVICE_ID;
1153 } else {
1154 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1156 if (dwFlags & MCI_SEEK_TO_START) {
1157 wmw->dwPosition = 0;
1158 } else if (dwFlags & MCI_SEEK_TO_END) {
1159 wmw->dwPosition = wmw->ckWaveData.cksize;
1160 } else if (dwFlags & MCI_TO) {
1161 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1162 } else {
1163 WARN("dwFlag doesn't tell where to seek to...\n");
1164 return MCIERR_MISSING_PARAMETER;
1167 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1169 if (dwFlags & MCI_NOTIFY) {
1170 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1171 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1174 return ret;
1177 /**************************************************************************
1178 * WAVE_mciSet [internal]
1180 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1182 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1184 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1186 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1187 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1189 if (dwFlags & MCI_SET_TIME_FORMAT) {
1190 switch (lpParms->dwTimeFormat) {
1191 case MCI_FORMAT_MILLISECONDS:
1192 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1193 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1194 break;
1195 case MCI_FORMAT_BYTES:
1196 TRACE("MCI_FORMAT_BYTES !\n");
1197 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1198 break;
1199 case MCI_FORMAT_SAMPLES:
1200 TRACE("MCI_FORMAT_SAMPLES !\n");
1201 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1202 break;
1203 default:
1204 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1205 return MCIERR_BAD_TIME_FORMAT;
1208 if (dwFlags & MCI_SET_VIDEO) {
1209 TRACE("No support for video !\n");
1210 return MCIERR_UNSUPPORTED_FUNCTION;
1212 if (dwFlags & MCI_SET_DOOR_OPEN) {
1213 TRACE("No support for door open !\n");
1214 return MCIERR_UNSUPPORTED_FUNCTION;
1216 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1217 TRACE("No support for door close !\n");
1218 return MCIERR_UNSUPPORTED_FUNCTION;
1220 if (dwFlags & MCI_SET_AUDIO) {
1221 if (dwFlags & MCI_SET_ON) {
1222 TRACE("MCI_SET_ON audio !\n");
1223 } else if (dwFlags & MCI_SET_OFF) {
1224 TRACE("MCI_SET_OFF audio !\n");
1225 } else {
1226 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1227 return MCIERR_BAD_INTEGER;
1230 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1231 TRACE("MCI_SET_AUDIO_ALL !\n");
1232 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1233 TRACE("MCI_SET_AUDIO_LEFT !\n");
1234 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1235 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1237 if (dwFlags & MCI_WAVE_INPUT)
1238 TRACE("MCI_WAVE_INPUT !\n");
1239 if (dwFlags & MCI_WAVE_OUTPUT)
1240 TRACE("MCI_WAVE_OUTPUT !\n");
1241 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1242 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1243 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1244 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1245 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1246 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1247 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1249 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1250 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1251 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1253 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1254 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1255 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1257 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1258 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1259 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1261 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1262 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1263 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1265 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1266 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1267 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1269 return 0;
1272 /**************************************************************************
1273 * WAVE_mciSave [internal]
1275 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1277 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1278 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1279 WPARAM wparam = MCI_NOTIFY_FAILURE;
1281 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1282 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1283 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1285 if (dwFlags & MCI_WAIT)
1287 FIXME("MCI_WAIT not implemented\n");
1290 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1291 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1294 ret = mmioClose(wmw->hFile, 0);
1297 If the destination file already exists, it has to be overwritten. (Behaviour
1298 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1299 my applications. We are making use of mmioRename, which WILL NOT overwrite
1300 the destination file (which is what Windows does, also verified in Win2K)
1301 So, lets delete the destination file before calling mmioRename. If the
1302 destination file DOESN'T exist, the delete will fail silently. Let's also be
1303 careful not to lose our previous error code.
1305 tmpRet = GetLastError();
1306 DeleteFileA (lpParms->lpfilename);
1307 SetLastError(tmpRet);
1309 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1310 ret = ERROR_SUCCESS;
1313 if (dwFlags & MCI_NOTIFY) {
1314 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1316 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1317 wmw->openParms.wDeviceID, wparam);
1320 return ret;
1323 /**************************************************************************
1324 * WAVE_mciStatus [internal]
1326 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1328 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1329 DWORD ret = 0;
1331 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1332 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1333 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1335 if (dwFlags & MCI_STATUS_ITEM) {
1336 switch (lpParms->dwItem) {
1337 case MCI_STATUS_CURRENT_TRACK:
1338 lpParms->dwReturn = 1;
1339 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1340 break;
1341 case MCI_STATUS_LENGTH:
1342 if (!wmw->hFile) {
1343 lpParms->dwReturn = 0;
1344 return MCIERR_UNSUPPORTED_FUNCTION;
1346 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1347 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1348 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1349 break;
1350 case MCI_STATUS_MODE:
1351 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1352 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1353 ret = MCI_RESOURCE_RETURNED;
1354 break;
1355 case MCI_STATUS_MEDIA_PRESENT:
1356 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1357 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1358 ret = MCI_RESOURCE_RETURNED;
1359 break;
1360 case MCI_STATUS_NUMBER_OF_TRACKS:
1361 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1362 lpParms->dwReturn = 1;
1363 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1364 break;
1365 case MCI_STATUS_POSITION:
1366 if (!wmw->hFile) {
1367 lpParms->dwReturn = 0;
1368 return MCIERR_UNSUPPORTED_FUNCTION;
1370 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1371 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1372 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1373 &ret);
1374 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1375 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1376 break;
1377 case MCI_STATUS_READY:
1378 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1379 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1380 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1381 ret = MCI_RESOURCE_RETURNED;
1382 break;
1383 case MCI_STATUS_TIME_FORMAT:
1384 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1385 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1386 ret = MCI_RESOURCE_RETURNED;
1387 break;
1388 case MCI_WAVE_INPUT:
1389 TRACE("MCI_WAVE_INPUT !\n");
1390 lpParms->dwReturn = 0;
1391 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1392 break;
1393 case MCI_WAVE_OUTPUT:
1394 TRACE("MCI_WAVE_OUTPUT !\n");
1396 UINT id;
1397 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1398 lpParms->dwReturn = id;
1399 } else {
1400 lpParms->dwReturn = 0;
1401 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1404 break;
1405 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1406 if (!wmw->hFile) {
1407 lpParms->dwReturn = 0;
1408 return MCIERR_UNSUPPORTED_FUNCTION;
1410 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1411 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1412 break;
1413 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1414 if (!wmw->hFile) {
1415 lpParms->dwReturn = 0;
1416 return MCIERR_UNSUPPORTED_FUNCTION;
1418 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1419 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1420 break;
1421 case MCI_WAVE_STATUS_BLOCKALIGN:
1422 if (!wmw->hFile) {
1423 lpParms->dwReturn = 0;
1424 return MCIERR_UNSUPPORTED_FUNCTION;
1426 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1427 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1428 break;
1429 case MCI_WAVE_STATUS_CHANNELS:
1430 if (!wmw->hFile) {
1431 lpParms->dwReturn = 0;
1432 return MCIERR_UNSUPPORTED_FUNCTION;
1434 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1435 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1436 break;
1437 case MCI_WAVE_STATUS_FORMATTAG:
1438 if (!wmw->hFile) {
1439 lpParms->dwReturn = 0;
1440 return MCIERR_UNSUPPORTED_FUNCTION;
1442 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1443 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1444 break;
1445 case MCI_WAVE_STATUS_LEVEL:
1446 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1447 lpParms->dwReturn = 0xAAAA5555;
1448 break;
1449 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1450 if (!wmw->hFile) {
1451 lpParms->dwReturn = 0;
1452 return MCIERR_UNSUPPORTED_FUNCTION;
1454 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1455 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1456 break;
1457 default:
1458 WARN("unknown command %08lX !\n", lpParms->dwItem);
1459 return MCIERR_UNRECOGNIZED_COMMAND;
1462 if (dwFlags & MCI_NOTIFY) {
1463 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1464 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1466 return ret;
1469 /**************************************************************************
1470 * WAVE_mciGetDevCaps [internal]
1472 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1473 LPMCI_GETDEVCAPS_PARMS lpParms)
1475 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1476 DWORD ret = 0;
1478 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1480 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1481 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1483 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1484 switch(lpParms->dwItem) {
1485 case MCI_GETDEVCAPS_DEVICE_TYPE:
1486 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1487 ret = MCI_RESOURCE_RETURNED;
1488 break;
1489 case MCI_GETDEVCAPS_HAS_AUDIO:
1490 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1491 ret = MCI_RESOURCE_RETURNED;
1492 break;
1493 case MCI_GETDEVCAPS_HAS_VIDEO:
1494 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1495 ret = MCI_RESOURCE_RETURNED;
1496 break;
1497 case MCI_GETDEVCAPS_USES_FILES:
1498 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1499 ret = MCI_RESOURCE_RETURNED;
1500 break;
1501 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1502 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1503 ret = MCI_RESOURCE_RETURNED;
1504 break;
1505 case MCI_GETDEVCAPS_CAN_RECORD:
1506 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1507 ret = MCI_RESOURCE_RETURNED;
1508 break;
1509 case MCI_GETDEVCAPS_CAN_EJECT:
1510 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1511 ret = MCI_RESOURCE_RETURNED;
1512 break;
1513 case MCI_GETDEVCAPS_CAN_PLAY:
1514 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1515 ret = MCI_RESOURCE_RETURNED;
1516 break;
1517 case MCI_GETDEVCAPS_CAN_SAVE:
1518 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1519 ret = MCI_RESOURCE_RETURNED;
1520 break;
1521 case MCI_WAVE_GETDEVCAPS_INPUTS:
1522 lpParms->dwReturn = 1;
1523 break;
1524 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1525 lpParms->dwReturn = 1;
1526 break;
1527 default:
1528 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1529 return MCIERR_UNRECOGNIZED_COMMAND;
1531 } else {
1532 WARN("No GetDevCaps-Item !\n");
1533 return MCIERR_UNRECOGNIZED_COMMAND;
1535 return ret;
1538 /**************************************************************************
1539 * WAVE_mciInfo [internal]
1541 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1543 DWORD ret = 0;
1544 LPCSTR str = 0;
1545 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1547 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1549 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1550 ret = MCIERR_NULL_PARAMETER_BLOCK;
1551 } else if (wmw == NULL) {
1552 ret = MCIERR_INVALID_DEVICE_ID;
1553 } else {
1554 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1556 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1557 case MCI_INFO_PRODUCT:
1558 str = "Wine's audio player";
1559 break;
1560 case MCI_INFO_FILE:
1561 str = wmw->openParms.lpstrElementName;
1562 break;
1563 case MCI_WAVE_INPUT:
1564 str = "Wine Wave In";
1565 break;
1566 case MCI_WAVE_OUTPUT:
1567 str = "Wine Wave Out";
1568 break;
1569 default:
1570 WARN("Don't know this info command (%lu)\n", dwFlags);
1571 ret = MCIERR_UNRECOGNIZED_COMMAND;
1574 if (str) {
1575 if (strlen(str) + 1 > lpParms->dwRetSize) {
1576 ret = MCIERR_PARAM_OVERFLOW;
1577 } else {
1578 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1580 } else {
1581 lpParms->lpstrReturn[0] = 0;
1584 return ret;
1587 /**************************************************************************
1588 * DriverProc (MCIWAVE.@)
1590 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1591 DWORD dwParam1, DWORD dwParam2)
1593 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1594 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1596 switch (wMsg) {
1597 case DRV_LOAD: return 1;
1598 case DRV_FREE: return 1;
1599 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1600 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1601 case DRV_ENABLE: return 1;
1602 case DRV_DISABLE: return 1;
1603 case DRV_QUERYCONFIGURE: return 1;
1604 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1605 case DRV_INSTALL: return DRVCNF_RESTART;
1606 case DRV_REMOVE: return DRVCNF_RESTART;
1609 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1611 switch (wMsg) {
1612 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1613 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1614 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1615 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1616 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1617 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1618 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1619 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1620 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1621 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1622 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1623 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1624 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1625 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1626 /* commands that should be supported */
1627 case MCI_LOAD:
1628 case MCI_FREEZE:
1629 case MCI_PUT:
1630 case MCI_REALIZE:
1631 case MCI_UNFREEZE:
1632 case MCI_UPDATE:
1633 case MCI_WHERE:
1634 case MCI_STEP:
1635 case MCI_SPIN:
1636 case MCI_ESCAPE:
1637 case MCI_COPY:
1638 case MCI_CUT:
1639 case MCI_DELETE:
1640 case MCI_PASTE:
1641 FIXME("Unsupported yet command [%lu]\n", wMsg);
1642 break;
1643 case MCI_WINDOW:
1644 TRACE("Unsupported command [%lu]\n", wMsg);
1645 break;
1646 /* option which can be silenced */
1647 case MCI_CONFIGURE:
1648 return 0;
1649 case MCI_OPEN:
1650 case MCI_CLOSE:
1651 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1652 break;
1653 default:
1654 FIXME("is probably wrong msg [%lu]\n", wMsg);
1655 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1657 return MCIERR_UNRECOGNIZED_COMMAND;