Prepare dlls/{comctl32,gdi,msvideo,setupapi,shell32,twain,winmm} for
[wine/multimedia.git] / dlls / winmm / mciwave / mciwave.c
blob55ee125d9353630f6b9a38d3a6deb46b09ad6dad
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 "digitalv.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
35 #define HWND_32(h16) ((HWND)(ULONG_PTR)(h16))
37 typedef struct {
38 UINT wDevID;
39 HANDLE hWave;
40 int nUseCount; /* Incremented for each shared open */
41 BOOL fShareable; /* TRUE if first open was shareable */
42 HMMIO hFile; /* mmio file handle open as Element */
43 MCI_WAVE_OPEN_PARMSA openParms;
44 WAVEFORMATEX wfxRef;
45 LPWAVEFORMATEX lpWaveFormat;
46 BOOL fInput; /* FALSE = Output, TRUE = Input */
47 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
48 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
49 DWORD dwRemaining; /* remaining bytes to play or record */
50 DWORD dwPosition; /* position in bytes in chunk */
51 HANDLE hEvent; /* for synchronization */
52 DWORD dwEventCount; /* for synchronization */
53 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
54 MMCKINFO ckMainRIFF; /* main RIFF chunk */
55 MMCKINFO ckWaveData; /* data chunk */
56 } WINE_MCIWAVE;
58 /* ===================================================================
59 * ===================================================================
60 * FIXME: should be using the new mmThreadXXXX functions from WINMM
61 * instead of those
62 * it would require to add a wine internal flag to mmThreadCreate
63 * in order to pass a 32 bit function instead of a 16 bit one
64 * ===================================================================
65 * =================================================================== */
67 struct SCA {
68 UINT wDevID;
69 UINT wMsg;
70 DWORD dwParam1;
71 DWORD dwParam2;
74 /**************************************************************************
75 * MCI_SCAStarter [internal]
77 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
79 struct SCA* sca = (struct SCA*)arg;
80 DWORD ret;
82 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
83 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
84 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
85 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
86 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
87 HeapFree(GetProcessHeap(), 0, sca);
88 ExitThread(ret);
89 WARN("Should not happen ? what's wrong \n");
90 /* should not go after this point */
91 return ret;
94 /**************************************************************************
95 * MCI_SendCommandAsync [internal]
97 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
98 DWORD dwParam2, UINT size)
100 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
102 if (sca == 0)
103 return MCIERR_OUT_OF_MEMORY;
105 sca->wDevID = wDevID;
106 sca->wMsg = wMsg;
107 sca->dwParam1 = dwParam1;
109 if (size && dwParam2) {
110 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
111 /* copy structure passed by program in dwParam2 to be sure
112 * we can still use it whatever the program does
114 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
115 } else {
116 sca->dwParam2 = dwParam2;
119 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
120 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
121 return MCI_SCAStarter(&sca);
123 return 0;
126 /*======================================================================*
127 * MCI WAVE implemantation *
128 *======================================================================*/
130 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
132 /**************************************************************************
133 * MCIWAVE_drvOpen [internal]
135 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
137 WINE_MCIWAVE* wmw;
139 if (modp == NULL) return 0xFFFFFFFF;
141 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
143 if (!wmw)
144 return 0;
146 wmw->wDevID = modp->wDeviceID;
147 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
148 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
149 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
151 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
152 wmw->wfxRef.nChannels = 1; /* MONO */
153 wmw->wfxRef.nSamplesPerSec = 11025;
154 wmw->wfxRef.nAvgBytesPerSec = 11025;
155 wmw->wfxRef.nBlockAlign = 1;
156 wmw->wfxRef.wBitsPerSample = 8;
157 wmw->wfxRef.cbSize = 0; /* don't care */
159 return modp->wDeviceID;
162 /**************************************************************************
163 * MCIWAVE_drvClose [internal]
165 static DWORD WAVE_drvClose(DWORD dwDevID)
167 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
169 if (wmw) {
170 HeapFree(GetProcessHeap(), 0, wmw);
171 mciSetDriverData(dwDevID, 0);
172 return 1;
174 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
177 /**************************************************************************
178 * WAVE_mciGetOpenDev [internal]
180 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
182 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
184 if (wmw == NULL || wmw->nUseCount == 0) {
185 WARN("Invalid wDevID=%u\n", wDevID);
186 return 0;
188 return wmw;
191 /**************************************************************************
192 * WAVE_ConvertByteToTimeFormat [internal]
194 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
196 DWORD ret = 0;
198 switch (wmw->dwMciTimeFormat) {
199 case MCI_FORMAT_MILLISECONDS:
200 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
201 break;
202 case MCI_FORMAT_BYTES:
203 ret = val;
204 break;
205 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
206 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
207 break;
208 default:
209 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
211 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
212 *lpRet = 0;
213 return ret;
216 /**************************************************************************
217 * WAVE_ConvertTimeFormatToByte [internal]
219 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
221 DWORD ret = 0;
223 switch (wmw->dwMciTimeFormat) {
224 case MCI_FORMAT_MILLISECONDS:
225 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
226 break;
227 case MCI_FORMAT_BYTES:
228 ret = val;
229 break;
230 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
231 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
232 break;
233 default:
234 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
236 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
237 return ret;
240 /**************************************************************************
241 * WAVE_mciReadFmt [internal]
243 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
245 MMCKINFO mmckInfo;
246 long r;
248 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
249 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
250 return MCIERR_INVALID_FILE;
251 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
252 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
254 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
255 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
256 if (r < sizeof(WAVEFORMAT))
257 return MCIERR_INVALID_FILE;
259 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
260 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
261 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
262 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
263 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
264 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
265 if (r >= (long)sizeof(WAVEFORMATEX))
266 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
268 mmioAscend(wmw->hFile, &mmckInfo, 0);
269 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
270 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
271 TRACE("can't find data chunk\n");
272 return MCIERR_INVALID_FILE;
274 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
275 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
276 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
277 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
279 return 0;
282 /**************************************************************************
283 * WAVE_mciCreateRIFFSkeleton [internal]
285 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
287 MMCKINFO ckWaveFormat;
289 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
290 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
291 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
294 HMMIO hmmio = wmw->hFile;
296 lpckRIFF->ckid = FOURCC_RIFF;
297 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
298 lpckRIFF->cksize = 0;
300 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
301 goto err;
303 ckWaveFormat.fccType = 0;
304 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
305 ckWaveFormat.cksize = 16;
307 if (!lpWaveFormat)
309 /* FIXME: for non PCM formats, the size of the waveFormat has to be
310 * gotten
312 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
314 memcpy(lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
317 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
318 goto err;
320 /* only the first 16 bytes are serialized */
321 /* wrong... for non PCM, the whole waveFormat is stored
323 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
324 goto err;
326 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
327 goto err;
329 lpckWaveData->cksize = 0;
330 lpckWaveData->fccType = 0;
331 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
333 /* create data chunk */
334 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
335 goto err;
337 return 0;
339 err:
340 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
341 return MCIERR_INVALID_FILE;
344 /**************************************************************************
345 * WAVE_mciOpen [internal]
347 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
349 DWORD dwRet = 0;
350 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
351 CHAR* pszTmpFileName = 0;
353 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
354 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
355 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
357 if (dwFlags & MCI_OPEN_SHAREABLE)
358 return MCIERR_HARDWARE;
360 if (wmw->nUseCount > 0) {
361 /* The driver is already opened on this channel
362 * Wave driver cannot be shared
364 return MCIERR_DEVICE_OPEN;
367 wmw->nUseCount++;
369 wmw->fInput = FALSE;
370 wmw->hWave = 0;
371 wmw->dwStatus = MCI_MODE_NOT_READY;
373 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
375 if (dwFlags & MCI_OPEN_ELEMENT) {
376 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
377 /* could it be that (DWORD)lpOpenParms->lpstrElementName
378 * contains the hFile value ?
380 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
381 } else {
382 if (strlen(lpOpenParms->lpstrElementName) > 0) {
383 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
385 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
386 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
388 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
389 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
390 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
392 if (wmw->hFile == 0) {
393 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
394 dwRet = MCIERR_FILE_NOT_FOUND;
396 else
398 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
400 /* make sure we're are the beginning of the file */
401 mmioSeek(wmw->hFile, 0, SEEK_SET);
403 /* first reading of this file. read the waveformat chunk */
404 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
405 dwRet = MCIERR_INVALID_FILE;
406 } else {
407 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
408 (LPSTR)&(lpckMainRIFF->ckid),
409 (LPSTR) &(lpckMainRIFF->fccType),
410 (lpckMainRIFF->cksize));
412 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
413 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
414 dwRet = MCIERR_INVALID_FILE;
415 } else {
416 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
421 else {
422 wmw->hFile = 0;
425 else {
426 CHAR szTmpPath[MAX_PATH];
427 CHAR szPrefix[4] = "TMP\0";
429 pszTmpFileName = HeapAlloc(GetProcessHeap(),
430 HEAP_ZERO_MEMORY,
431 MAX_PATH * sizeof(*pszTmpFileName));
433 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
434 WARN("can't retrieve temp path!\n");
435 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
436 return MCIERR_FILE_NOT_FOUND;
439 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
440 WARN("can't retrieve temp file name!\n");
441 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
442 return MCIERR_FILE_NOT_FOUND;
445 wmw->bTemporaryFile = TRUE;
447 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
449 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
451 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
452 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
454 if (wmw->hFile == 0) {
455 /* temporary file could not be created. clean filename. */
456 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
457 WARN("can't create file='%s' !\n", pszTmpFileName);
458 dwRet = MCIERR_FILE_NOT_FOUND;
465 TRACE("hFile=%u\n", wmw->hFile);
467 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
469 if (wmw->bTemporaryFile == TRUE)
471 /* Additional openParms is temporary file's name */
472 wmw->openParms.lpstrElementName = pszTmpFileName;
475 if (dwRet == 0) {
476 if (wmw->lpWaveFormat) {
477 switch (wmw->lpWaveFormat->wFormatTag) {
478 case WAVE_FORMAT_PCM:
479 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
480 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
481 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
482 wmw->lpWaveFormat->nAvgBytesPerSec,
483 wmw->lpWaveFormat->nSamplesPerSec *
484 wmw->lpWaveFormat->nBlockAlign);
485 wmw->lpWaveFormat->nAvgBytesPerSec =
486 wmw->lpWaveFormat->nSamplesPerSec *
487 wmw->lpWaveFormat->nBlockAlign;
489 break;
492 wmw->dwPosition = 0;
494 wmw->dwStatus = MCI_MODE_STOP;
495 } else {
496 wmw->nUseCount--;
497 if (wmw->hFile != 0)
498 mmioClose(wmw->hFile, 0);
499 wmw->hFile = 0;
501 return dwRet;
504 /**************************************************************************
505 * WAVE_mciCue [internal]
507 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
510 FIXME
512 This routine is far from complete. At the moment only a check is done on the
513 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
514 is the default.
516 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
517 are ignored
520 DWORD dwRet;
521 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
523 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
525 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
527 /* always close elements ? */
528 if (wmw->hFile != 0) {
529 mmioClose(wmw->hFile, 0);
530 wmw->hFile = 0;
533 dwRet = MMSYSERR_NOERROR; /* assume success */
535 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
536 dwRet = waveOutClose(wmw->hWave);
537 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
538 wmw->fInput = TRUE;
539 } else if (wmw->fInput) {
540 dwRet = waveInClose(wmw->hWave);
541 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
542 wmw->fInput = FALSE;
544 wmw->hWave = 0;
545 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
548 /**************************************************************************
549 * WAVE_mciStop [internal]
551 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
553 DWORD dwRet = 0;
554 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
556 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
558 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
560 /* wait for playback thread (if any) to exit before processing further */
561 switch (wmw->dwStatus) {
562 case MCI_MODE_PAUSE:
563 case MCI_MODE_PLAY:
564 case MCI_MODE_RECORD:
566 int oldStat = wmw->dwStatus;
567 wmw->dwStatus = MCI_MODE_NOT_READY;
568 if (oldStat == MCI_MODE_PAUSE)
569 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
571 while (wmw->dwStatus != MCI_MODE_STOP)
572 Sleep(10);
573 break;
576 wmw->dwPosition = 0;
578 /* sanity resets */
579 wmw->dwStatus = MCI_MODE_STOP;
581 if ((dwFlags & MCI_NOTIFY) && lpParms) {
582 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
583 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
586 return dwRet;
589 /**************************************************************************
590 * WAVE_mciClose [internal]
592 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
594 DWORD dwRet = 0;
595 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
597 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
599 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
601 if (wmw->dwStatus != MCI_MODE_STOP) {
602 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
605 wmw->nUseCount--;
607 if (wmw->nUseCount == 0) {
608 if (wmw->hFile != 0) {
609 mmioClose(wmw->hFile, 0);
610 wmw->hFile = 0;
614 /* That string got allocated in mciOpen because no filename was specified
615 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
616 * allocated by mciOpen, *NOT* the application.
618 if (wmw->bTemporaryFile)
620 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
621 wmw->openParms.lpstrElementName = NULL;
624 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
625 wmw->lpWaveFormat = NULL;
627 if ((dwFlags & MCI_NOTIFY) && lpParms) {
628 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
629 wmw->openParms.wDeviceID,
630 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
633 return 0;
636 /**************************************************************************
637 * WAVE_mciPlayCallback [internal]
639 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
640 DWORD dwInstance,
641 DWORD dwParam1, DWORD dwParam2)
643 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
645 switch (uMsg) {
646 case WOM_OPEN:
647 case WOM_CLOSE:
648 break;
649 case WOM_DONE:
650 InterlockedIncrement(&wmw->dwEventCount);
651 TRACE("Returning waveHdr=%lx\n", dwParam1);
652 SetEvent(wmw->hEvent);
653 break;
654 default:
655 ERR("Unknown uMsg=%d\n", uMsg);
659 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
661 for (;;) {
662 ResetEvent(wmw->hEvent);
663 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
664 break;
666 InterlockedIncrement(&wmw->dwEventCount);
668 WaitForSingleObject(wmw->hEvent, INFINITE);
672 /**************************************************************************
673 * WAVE_mciPlay [internal]
675 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
677 DWORD end;
678 LONG bufsize, count, left;
679 DWORD dwRet = 0;
680 LPWAVEHDR waveHdr = NULL;
681 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
682 int whidx;
684 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
686 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
687 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
689 /* FIXME : since there is no way to determine in which mode the device is
690 * open (recording/playback) automatically switch from a mode to another
692 wmw->fInput = FALSE;
694 if (wmw->fInput) {
695 WARN("cannot play on input device\n");
696 return MCIERR_NONAPPLICABLE_FUNCTION;
699 if (wmw->hFile == 0) {
700 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
701 return MCIERR_FILE_NOT_FOUND;
704 if (wmw->dwStatus == MCI_MODE_PAUSE) {
705 /* FIXME: parameters (start/end) in lpParams may not be used */
706 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
709 /** This function will be called again by a thread when async is used.
710 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
711 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
713 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
714 return MCIERR_INTERNAL;
717 wmw->dwStatus = MCI_MODE_PLAY;
719 if (!(dwFlags & MCI_WAIT)) {
720 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
721 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
724 end = 0xFFFFFFFF;
725 if (lpParms && (dwFlags & MCI_FROM)) {
726 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
728 if (lpParms && (dwFlags & MCI_TO)) {
729 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
732 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
734 if (end <= wmw->dwPosition)
735 return TRUE;
738 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
739 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
741 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
742 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
744 if (dwRet == 0) {
745 if (wmw->lpWaveFormat) {
746 switch (wmw->lpWaveFormat->wFormatTag) {
747 case WAVE_FORMAT_PCM:
748 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
749 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
750 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
751 wmw->lpWaveFormat->nAvgBytesPerSec,
752 wmw->lpWaveFormat->nSamplesPerSec *
753 wmw->lpWaveFormat->nBlockAlign);
754 wmw->lpWaveFormat->nAvgBytesPerSec =
755 wmw->lpWaveFormat->nSamplesPerSec *
756 wmw->lpWaveFormat->nBlockAlign;
758 break;
761 } else {
762 TRACE("can't retrieve wave format %ld\n", dwRet);
763 goto cleanUp;
767 /* go back to beginning of chunk plus the requested position */
768 /* FIXME: I'm not sure this is correct, notably because some data linked to
769 * the decompression state machine will not be correcly initialized.
770 * try it this way (other way would be to decompress from 0 up to dwPosition
771 * and to start sending to hWave when dwPosition is reached)
773 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
775 /* By default the device will be opened for output, the MCI_CUE function is there to
776 * change from output to input and back
778 /* FIXME: how to choose between several output channels ? here mapper is forced */
779 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
780 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
782 if (dwRet != 0) {
783 TRACE("Can't open low level audio device %ld\n", dwRet);
784 dwRet = MCIERR_DEVICE_OPEN;
785 wmw->hWave = 0;
786 goto cleanUp;
789 /* make it so that 3 buffers per second are needed */
790 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
792 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
793 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
794 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
795 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
796 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
797 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
798 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
799 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
800 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
801 dwRet = MCIERR_INTERNAL;
802 goto cleanUp;
805 whidx = 0;
806 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
807 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
808 wmw->dwEventCount = 1L; /* for first buffer */
810 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
812 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
813 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
814 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
815 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
816 if (count < 1)
817 break;
818 /* count is always <= bufsize, so this is correct regarding the
819 * waveOutPrepareHeader function
821 waveHdr[whidx].dwBufferLength = count;
822 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
823 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
824 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
825 waveHdr[whidx].dwBytesRecorded);
826 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
827 left -= count;
828 wmw->dwPosition += count;
829 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
831 WAVE_mciPlayWaitDone(wmw);
832 whidx ^= 1;
835 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
837 /* just to get rid of some race conditions between play, stop and pause */
838 waveOutReset(wmw->hWave);
840 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
841 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
843 dwRet = 0;
845 cleanUp:
846 HeapFree(GetProcessHeap(), 0, waveHdr);
848 if (wmw->hWave) {
849 waveOutClose(wmw->hWave);
850 wmw->hWave = 0;
852 CloseHandle(wmw->hEvent);
854 if (lpParms && (dwFlags & MCI_NOTIFY)) {
855 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
856 wmw->openParms.wDeviceID,
857 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
860 wmw->dwStatus = MCI_MODE_STOP;
862 return dwRet;
865 /**************************************************************************
866 * WAVE_mciPlayCallback [internal]
868 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
869 DWORD dwInstance,
870 DWORD dwParam1, DWORD dwParam2)
872 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
873 LPWAVEHDR lpWaveHdr = NULL;
874 LONG count = 0;
875 switch (uMsg) {
876 case WIM_OPEN:
877 case WIM_CLOSE:
878 break;
879 case WIM_DATA:
880 lpWaveHdr = (LPWAVEHDR) dwParam1;
882 InterlockedIncrement(&wmw->dwEventCount);
884 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
886 lpWaveHdr->dwFlags &= ~WHDR_DONE;
887 wmw->dwPosition += count;
888 wmw->dwRemaining -= count;
890 if (wmw->dwStatus == MCI_MODE_RECORD)
892 /* Only queue up another buffer if we are recording. We could receive this
893 message also when waveInReset() is called, since it notifies on all wave
894 buffers that are outstanding. Queueing up more sometimes causes waveInClose
895 to fail. */
896 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
897 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
900 SetEvent(wmw->hEvent);
901 break;
902 default:
903 ERR("Unknown uMsg=%d\n", uMsg);
907 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
909 for (;;) {
910 ResetEvent(wmw->hEvent);
911 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
912 break;
914 InterlockedIncrement(&wmw->dwEventCount);
916 WaitForSingleObject(wmw->hEvent, INFINITE);
920 /**************************************************************************
921 * WAVE_mciRecord [internal]
923 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
925 DWORD end;
926 DWORD dwRet = 0;
927 LONG bufsize;
928 LPWAVEHDR waveHdr = NULL;
929 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
932 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
934 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
935 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
937 /* FIXME : since there is no way to determine in which mode the device is
938 * open (recording/playback) automatically switch from a mode to another
940 wmw->fInput = TRUE;
942 if (!wmw->fInput) {
943 WARN("cannot record on output device\n");
944 return MCIERR_NONAPPLICABLE_FUNCTION;
947 if (wmw->dwStatus == MCI_MODE_PAUSE) {
948 /* FIXME: parameters (start/end) in lpParams may not be used */
949 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
952 /** This function will be called again by a thread when async is used.
953 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
954 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
956 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
957 return MCIERR_INTERNAL;
960 wmw->dwStatus = MCI_MODE_RECORD;
962 if (!(dwFlags & MCI_WAIT)) {
963 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
964 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
967 if (!wmw->lpWaveFormat)
969 /* new RIFF file */
970 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
971 } else
973 FIXME("Should descend into data chunk. Please report.\n");
976 end = 0xFFFFFFFF;
977 if (lpParms && (dwFlags & MCI_FROM)) {
978 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
981 if (lpParms && (dwFlags & MCI_TO)) {
982 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
985 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
987 if (end <= wmw->dwPosition)
989 return TRUE;
992 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
993 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
995 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
996 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
998 /* go back to beginning of chunk plus the requested position */
999 /* FIXME: I'm not sure this is correct, notably because some data linked to
1000 * the decompression state machine will not be correcly initialized.
1001 * try it this way (other way would be to decompress from 0 up to dwPosition
1002 * and to start sending to hWave when dwPosition is reached)
1004 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1006 /* By default the device will be opened for output, the MCI_CUE function is there to
1007 * change from output to input and back
1009 /* FIXME: how to choose between several output channels ? here mapper is forced */
1010 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1011 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1013 if (dwRet != 0) {
1014 TRACE("Can't open low level audio device %ld\n", dwRet);
1015 dwRet = MCIERR_DEVICE_OPEN;
1016 wmw->hWave = 0;
1017 goto cleanUp;
1020 /* make it so that 3 buffers per second are needed */
1021 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1023 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1024 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1025 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1026 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1027 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1028 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1029 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1031 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1032 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1033 dwRet = MCIERR_INTERNAL;
1034 goto cleanUp;
1037 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1038 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1039 dwRet = MCIERR_INTERNAL;
1040 goto cleanUp;
1043 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1044 wmw->dwEventCount = 1L; /* for first buffer */
1046 wmw->dwRemaining = end - wmw->dwPosition;
1048 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1050 dwRet = waveInStart(wmw->hWave);
1052 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1053 WAVE_mciRecordWaitDone(wmw);
1056 /* needed so that the callback above won't add again the buffers returned by the reset */
1057 wmw->dwStatus = MCI_MODE_STOP;
1059 waveInReset(wmw->hWave);
1061 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1062 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1064 dwRet = 0;
1066 cleanUp:
1067 HeapFree(GetProcessHeap(), 0, waveHdr);
1069 if (wmw->hWave) {
1070 waveInClose(wmw->hWave);
1071 wmw->hWave = 0;
1073 CloseHandle(wmw->hEvent);
1075 /* need to update the size of the data chunk */
1076 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1077 TRACE("failed on ascend\n");
1080 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1081 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1082 wmw->openParms.wDeviceID,
1083 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1086 wmw->dwStatus = MCI_MODE_STOP;
1088 return dwRet;
1092 /**************************************************************************
1093 * WAVE_mciPause [internal]
1095 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1097 DWORD dwRet;
1098 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1100 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1102 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1103 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1105 if (wmw->dwStatus == MCI_MODE_PLAY) {
1106 wmw->dwStatus = MCI_MODE_PAUSE;
1109 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1110 else dwRet = waveOutPause(wmw->hWave);
1112 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1115 /**************************************************************************
1116 * WAVE_mciResume [internal]
1118 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1120 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1121 DWORD dwRet = 0;
1123 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1125 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1127 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1128 wmw->dwStatus = MCI_MODE_PLAY;
1131 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1132 else dwRet = waveOutRestart(wmw->hWave);
1133 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1136 /**************************************************************************
1137 * WAVE_mciSeek [internal]
1139 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1141 DWORD ret = 0;
1142 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1144 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1146 if (lpParms == NULL) {
1147 ret = MCIERR_NULL_PARAMETER_BLOCK;
1148 } else if (wmw == NULL) {
1149 ret = MCIERR_INVALID_DEVICE_ID;
1150 } else {
1151 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1153 if (dwFlags & MCI_SEEK_TO_START) {
1154 wmw->dwPosition = 0;
1155 } else if (dwFlags & MCI_SEEK_TO_END) {
1156 wmw->dwPosition = wmw->ckWaveData.cksize;
1157 } else if (dwFlags & MCI_TO) {
1158 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1159 } else {
1160 WARN("dwFlag doesn't tell where to seek to...\n");
1161 return MCIERR_MISSING_PARAMETER;
1164 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1166 if (dwFlags & MCI_NOTIFY) {
1167 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1168 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1171 return ret;
1174 /**************************************************************************
1175 * WAVE_mciSet [internal]
1177 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1179 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1181 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1183 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1184 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1186 if (dwFlags & MCI_SET_TIME_FORMAT) {
1187 switch (lpParms->dwTimeFormat) {
1188 case MCI_FORMAT_MILLISECONDS:
1189 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1190 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1191 break;
1192 case MCI_FORMAT_BYTES:
1193 TRACE("MCI_FORMAT_BYTES !\n");
1194 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1195 break;
1196 case MCI_FORMAT_SAMPLES:
1197 TRACE("MCI_FORMAT_SAMPLES !\n");
1198 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1199 break;
1200 default:
1201 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1202 return MCIERR_BAD_TIME_FORMAT;
1205 if (dwFlags & MCI_SET_VIDEO) {
1206 TRACE("No support for video !\n");
1207 return MCIERR_UNSUPPORTED_FUNCTION;
1209 if (dwFlags & MCI_SET_DOOR_OPEN) {
1210 TRACE("No support for door open !\n");
1211 return MCIERR_UNSUPPORTED_FUNCTION;
1213 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1214 TRACE("No support for door close !\n");
1215 return MCIERR_UNSUPPORTED_FUNCTION;
1217 if (dwFlags & MCI_SET_AUDIO) {
1218 if (dwFlags & MCI_SET_ON) {
1219 TRACE("MCI_SET_ON audio !\n");
1220 } else if (dwFlags & MCI_SET_OFF) {
1221 TRACE("MCI_SET_OFF audio !\n");
1222 } else {
1223 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1224 return MCIERR_BAD_INTEGER;
1227 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1228 TRACE("MCI_SET_AUDIO_ALL !\n");
1229 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1230 TRACE("MCI_SET_AUDIO_LEFT !\n");
1231 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1232 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1234 if (dwFlags & MCI_WAVE_INPUT)
1235 TRACE("MCI_WAVE_INPUT !\n");
1236 if (dwFlags & MCI_WAVE_OUTPUT)
1237 TRACE("MCI_WAVE_OUTPUT !\n");
1238 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1239 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1240 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1241 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1242 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1243 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1244 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1246 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1247 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1248 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1250 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1251 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1252 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1254 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1255 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1256 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1258 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1259 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1260 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1262 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1263 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1264 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1266 return 0;
1269 /**************************************************************************
1270 * WAVE_mciSave [internal]
1272 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1274 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1275 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1276 WPARAM wparam = MCI_NOTIFY_FAILURE;
1278 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1279 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1280 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1282 if (dwFlags & MCI_WAIT)
1284 FIXME("MCI_WAIT not implemented\n");
1287 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1288 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1291 ret = mmioClose(wmw->hFile, 0);
1294 If the destination file already exists, it has to be overwritten. (Behaviour
1295 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1296 my applications. We are making use of mmioRename, which WILL NOT overwrite
1297 the destination file (which is what Windows does, also verified in Win2K)
1298 So, lets delete the destination file before calling mmioRename. If the
1299 destination file DOESN'T exist, the delete will fail silently. Let's also be
1300 careful not to lose our previous error code.
1302 tmpRet = GetLastError();
1303 DeleteFileA (lpParms->lpfilename);
1304 SetLastError(tmpRet);
1306 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1307 ret = ERROR_SUCCESS;
1310 if (dwFlags & MCI_NOTIFY) {
1311 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1313 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1314 wmw->openParms.wDeviceID, wparam);
1317 return ret;
1320 /**************************************************************************
1321 * WAVE_mciStatus [internal]
1323 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1325 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1326 DWORD ret = 0;
1328 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1329 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1330 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1332 if (dwFlags & MCI_STATUS_ITEM) {
1333 switch (lpParms->dwItem) {
1334 case MCI_STATUS_CURRENT_TRACK:
1335 lpParms->dwReturn = 1;
1336 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1337 break;
1338 case MCI_STATUS_LENGTH:
1339 if (!wmw->hFile) {
1340 lpParms->dwReturn = 0;
1341 return MCIERR_UNSUPPORTED_FUNCTION;
1343 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1344 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1345 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1346 break;
1347 case MCI_STATUS_MODE:
1348 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1349 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1350 ret = MCI_RESOURCE_RETURNED;
1351 break;
1352 case MCI_STATUS_MEDIA_PRESENT:
1353 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1354 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1355 ret = MCI_RESOURCE_RETURNED;
1356 break;
1357 case MCI_STATUS_NUMBER_OF_TRACKS:
1358 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1359 lpParms->dwReturn = 1;
1360 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1361 break;
1362 case MCI_STATUS_POSITION:
1363 if (!wmw->hFile) {
1364 lpParms->dwReturn = 0;
1365 return MCIERR_UNSUPPORTED_FUNCTION;
1367 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1368 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1369 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1370 &ret);
1371 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1372 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1373 break;
1374 case MCI_STATUS_READY:
1375 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1376 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1377 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1378 ret = MCI_RESOURCE_RETURNED;
1379 break;
1380 case MCI_STATUS_TIME_FORMAT:
1381 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1382 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1383 ret = MCI_RESOURCE_RETURNED;
1384 break;
1385 case MCI_WAVE_INPUT:
1386 TRACE("MCI_WAVE_INPUT !\n");
1387 lpParms->dwReturn = 0;
1388 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1389 break;
1390 case MCI_WAVE_OUTPUT:
1391 TRACE("MCI_WAVE_OUTPUT !\n");
1393 UINT id;
1394 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1395 lpParms->dwReturn = id;
1396 } else {
1397 lpParms->dwReturn = 0;
1398 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1401 break;
1402 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1403 if (!wmw->hFile) {
1404 lpParms->dwReturn = 0;
1405 return MCIERR_UNSUPPORTED_FUNCTION;
1407 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1408 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1409 break;
1410 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1411 if (!wmw->hFile) {
1412 lpParms->dwReturn = 0;
1413 return MCIERR_UNSUPPORTED_FUNCTION;
1415 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1416 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1417 break;
1418 case MCI_WAVE_STATUS_BLOCKALIGN:
1419 if (!wmw->hFile) {
1420 lpParms->dwReturn = 0;
1421 return MCIERR_UNSUPPORTED_FUNCTION;
1423 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1424 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1425 break;
1426 case MCI_WAVE_STATUS_CHANNELS:
1427 if (!wmw->hFile) {
1428 lpParms->dwReturn = 0;
1429 return MCIERR_UNSUPPORTED_FUNCTION;
1431 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1432 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1433 break;
1434 case MCI_WAVE_STATUS_FORMATTAG:
1435 if (!wmw->hFile) {
1436 lpParms->dwReturn = 0;
1437 return MCIERR_UNSUPPORTED_FUNCTION;
1439 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1440 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1441 break;
1442 case MCI_WAVE_STATUS_LEVEL:
1443 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1444 lpParms->dwReturn = 0xAAAA5555;
1445 break;
1446 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1447 if (!wmw->hFile) {
1448 lpParms->dwReturn = 0;
1449 return MCIERR_UNSUPPORTED_FUNCTION;
1451 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1452 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1453 break;
1454 default:
1455 WARN("unknown command %08lX !\n", lpParms->dwItem);
1456 return MCIERR_UNRECOGNIZED_COMMAND;
1459 if (dwFlags & MCI_NOTIFY) {
1460 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1461 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1463 return ret;
1466 /**************************************************************************
1467 * WAVE_mciGetDevCaps [internal]
1469 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1470 LPMCI_GETDEVCAPS_PARMS lpParms)
1472 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1473 DWORD ret = 0;
1475 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1477 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1478 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1480 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1481 switch(lpParms->dwItem) {
1482 case MCI_GETDEVCAPS_DEVICE_TYPE:
1483 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1484 ret = MCI_RESOURCE_RETURNED;
1485 break;
1486 case MCI_GETDEVCAPS_HAS_AUDIO:
1487 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1488 ret = MCI_RESOURCE_RETURNED;
1489 break;
1490 case MCI_GETDEVCAPS_HAS_VIDEO:
1491 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1492 ret = MCI_RESOURCE_RETURNED;
1493 break;
1494 case MCI_GETDEVCAPS_USES_FILES:
1495 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1496 ret = MCI_RESOURCE_RETURNED;
1497 break;
1498 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1499 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1500 ret = MCI_RESOURCE_RETURNED;
1501 break;
1502 case MCI_GETDEVCAPS_CAN_RECORD:
1503 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1504 ret = MCI_RESOURCE_RETURNED;
1505 break;
1506 case MCI_GETDEVCAPS_CAN_EJECT:
1507 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1508 ret = MCI_RESOURCE_RETURNED;
1509 break;
1510 case MCI_GETDEVCAPS_CAN_PLAY:
1511 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1512 ret = MCI_RESOURCE_RETURNED;
1513 break;
1514 case MCI_GETDEVCAPS_CAN_SAVE:
1515 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1516 ret = MCI_RESOURCE_RETURNED;
1517 break;
1518 case MCI_WAVE_GETDEVCAPS_INPUTS:
1519 lpParms->dwReturn = 1;
1520 break;
1521 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1522 lpParms->dwReturn = 1;
1523 break;
1524 default:
1525 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1526 return MCIERR_UNRECOGNIZED_COMMAND;
1528 } else {
1529 WARN("No GetDevCaps-Item !\n");
1530 return MCIERR_UNRECOGNIZED_COMMAND;
1532 return ret;
1535 /**************************************************************************
1536 * WAVE_mciInfo [internal]
1538 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1540 DWORD ret = 0;
1541 LPCSTR str = 0;
1542 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1544 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1546 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1547 ret = MCIERR_NULL_PARAMETER_BLOCK;
1548 } else if (wmw == NULL) {
1549 ret = MCIERR_INVALID_DEVICE_ID;
1550 } else {
1551 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1553 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1554 case MCI_INFO_PRODUCT:
1555 str = "Wine's audio player";
1556 break;
1557 case MCI_INFO_FILE:
1558 str = wmw->openParms.lpstrElementName;
1559 break;
1560 case MCI_WAVE_INPUT:
1561 str = "Wine Wave In";
1562 break;
1563 case MCI_WAVE_OUTPUT:
1564 str = "Wine Wave Out";
1565 break;
1566 default:
1567 WARN("Don't know this info command (%lu)\n", dwFlags);
1568 ret = MCIERR_UNRECOGNIZED_COMMAND;
1571 if (str) {
1572 if (strlen(str) + 1 > lpParms->dwRetSize) {
1573 ret = MCIERR_PARAM_OVERFLOW;
1574 } else {
1575 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1577 } else {
1578 lpParms->lpstrReturn[0] = 0;
1581 return ret;
1584 /**************************************************************************
1585 * DriverProc (MCIWAVE.@)
1587 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1588 DWORD dwParam1, DWORD dwParam2)
1590 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1591 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1593 switch (wMsg) {
1594 case DRV_LOAD: return 1;
1595 case DRV_FREE: return 1;
1596 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1597 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1598 case DRV_ENABLE: return 1;
1599 case DRV_DISABLE: return 1;
1600 case DRV_QUERYCONFIGURE: return 1;
1601 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1602 case DRV_INSTALL: return DRVCNF_RESTART;
1603 case DRV_REMOVE: return DRVCNF_RESTART;
1606 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1608 switch (wMsg) {
1609 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1610 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1611 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1612 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1613 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1614 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1615 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1616 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1617 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1618 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1619 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1620 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1621 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1622 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1623 /* commands that should be supported */
1624 case MCI_LOAD:
1625 case MCI_FREEZE:
1626 case MCI_PUT:
1627 case MCI_REALIZE:
1628 case MCI_UNFREEZE:
1629 case MCI_UPDATE:
1630 case MCI_WHERE:
1631 case MCI_STEP:
1632 case MCI_SPIN:
1633 case MCI_ESCAPE:
1634 case MCI_COPY:
1635 case MCI_CUT:
1636 case MCI_DELETE:
1637 case MCI_PASTE:
1638 FIXME("Unsupported yet command [%lu]\n", wMsg);
1639 break;
1640 case MCI_WINDOW:
1641 TRACE("Unsupported command [%lu]\n", wMsg);
1642 break;
1643 /* option which can be silenced */
1644 case MCI_CONFIGURE:
1645 return 0;
1646 case MCI_OPEN:
1647 case MCI_CLOSE:
1648 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1649 break;
1650 default:
1651 FIXME("is probably wrong msg [%lu]\n", wMsg);
1652 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1654 return MCIERR_UNRECOGNIZED_COMMAND;