2 * Wine Driver for MCI wave forms
4 * Copyright 1994 Martin Ayotte
5 * 1999,2000,2005 Eric Pouech
6 * 2000 Francois Jacques
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
40 int nUseCount; /* Incremented for each shared open */
41 HMMIO hFile; /* mmio file handle open as Element */
42 MCI_WAVE_OPEN_PARMSW openParms;
44 LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */
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 LONG dwEventCount; /* for synchronization */
51 MMCKINFO ckMainRIFF; /* main RIFF chunk */
52 MMCKINFO ckWaveData; /* data chunk */
55 /* ===================================================================
56 * ===================================================================
57 * FIXME: should be using the new mmThreadXXXX functions from WINMM
59 * it would require to add a wine internal flag to mmThreadCreate
60 * in order to pass a 32 bit function instead of a 16 bit one
61 * ===================================================================
62 * =================================================================== */
64 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
74 /**************************************************************************
75 * MCI_SCAStarter [internal]
77 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
79 struct SCA* sca = (struct SCA*)arg;
82 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
83 sca->wDevID, sca->dwParam1, sca->dwParam2);
84 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
85 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
86 sca->wDevID, sca->dwParam1, sca->dwParam2);
87 HeapFree(GetProcessHeap(), 0, sca);
89 WARN("Should not happen ? what's wrong\n");
90 /* should not go after this point */
94 /**************************************************************************
95 * MCI_SendCommandAsync [internal]
97 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
98 DWORD_PTR dwParam2, UINT size)
101 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
104 return MCIERR_OUT_OF_MEMORY;
106 sca->wDevID = wDevID;
108 sca->dwParam1 = dwParam1;
110 if (size && dwParam2) {
111 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
112 /* copy structure passed by program in dwParam2 to be sure
113 * we can still use it whatever the program does
115 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
117 sca->dwParam2 = dwParam2;
120 if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
121 (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
122 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
123 if (handles[1]) CloseHandle(handles[1]);
125 return MCI_SCAStarter(&sca);
128 SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
129 /* wait until either:
130 * - the thread has finished (handles[0], likely an error)
131 * - init phase of async command is done (handles[1])
133 WaitForMultipleObjects(2, handles, FALSE, INFINITE);
134 CloseHandle(handles[0]);
135 CloseHandle(handles[1]);
139 /*======================================================================*
140 * MCI WAVE implementation *
141 *======================================================================*/
143 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
145 /**************************************************************************
146 * MCIWAVE_drvOpen [internal]
148 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
152 if (modp == NULL) return 0xFFFFFFFF;
154 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
159 wmw->wDevID = modp->wDeviceID;
160 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
161 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
162 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
164 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
165 wmw->wfxRef.nChannels = 1; /* MONO */
166 wmw->wfxRef.nSamplesPerSec = 11025;
167 wmw->wfxRef.nAvgBytesPerSec = 11025;
168 wmw->wfxRef.nBlockAlign = 1;
169 wmw->wfxRef.wBitsPerSample = 8;
170 wmw->wfxRef.cbSize = 0; /* don't care */
172 return modp->wDeviceID;
175 /**************************************************************************
176 * MCIWAVE_drvClose [internal]
178 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
180 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
183 HeapFree(GetProcessHeap(), 0, wmw);
184 mciSetDriverData(dwDevID, 0);
187 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
190 /**************************************************************************
191 * WAVE_mciGetOpenDev [internal]
193 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
195 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
197 if (wmw == NULL || wmw->nUseCount == 0) {
198 WARN("Invalid wDevID=%u\n", wDevID);
204 /**************************************************************************
205 * WAVE_ConvertByteToTimeFormat [internal]
207 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
211 switch (wmw->dwMciTimeFormat) {
212 case MCI_FORMAT_MILLISECONDS:
213 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
215 case MCI_FORMAT_BYTES:
218 case MCI_FORMAT_SAMPLES:
219 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
222 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
224 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
229 /**************************************************************************
230 * WAVE_ConvertTimeFormatToByte [internal]
232 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
236 switch (wmw->dwMciTimeFormat) {
237 case MCI_FORMAT_MILLISECONDS:
238 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
240 case MCI_FORMAT_BYTES:
243 case MCI_FORMAT_SAMPLES:
244 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
247 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
249 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
253 /**************************************************************************
254 * WAVE_mciReadFmt [internal]
256 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
261 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
262 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
263 return MCIERR_INVALID_FILE;
264 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
265 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
268 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
269 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
270 wmw->lpWaveFormat = pwfx;
272 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
273 if (r < sizeof(PCMWAVEFORMAT))
274 return MCIERR_INVALID_FILE;
276 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
277 TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
278 TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec);
279 TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
280 TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
281 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
282 if (r >= (long)sizeof(WAVEFORMATEX))
283 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
285 mmioAscend(wmw->hFile, &mmckInfo, 0);
286 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
287 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
288 TRACE("can't find data chunk\n");
289 return MCIERR_INVALID_FILE;
291 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
292 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
293 TRACE("nChannels=%d nSamplesPerSec=%d\n",
294 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
299 /**************************************************************************
300 * WAVE_mciDefaultFmt [internal]
302 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
303 * until either Open File or Record. It becomes immutable afterwards,
304 * i.e. Set wave format or channels etc. is subsequently refused.
306 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
308 wmw->lpWaveFormat = &wmw->wfxRef;
309 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
310 wmw->lpWaveFormat->nChannels = 1;
311 wmw->lpWaveFormat->nSamplesPerSec = 11025;
312 wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
313 wmw->lpWaveFormat->nBlockAlign = 1;
314 wmw->lpWaveFormat->wBitsPerSample = 8;
315 wmw->lpWaveFormat->cbSize = 0;
318 /**************************************************************************
319 * WAVE_mciCreateRIFFSkeleton [internal]
321 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
323 MMCKINFO ckWaveFormat;
324 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
325 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
327 lpckRIFF->ckid = FOURCC_RIFF;
328 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
329 lpckRIFF->cksize = 0;
331 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
334 ckWaveFormat.fccType = 0;
335 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
336 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
338 /* FIXME: Set wave format accepts PCM only, however open an
339 * existing ADPCM file, record into it and the MCI will
340 * happily save back in that format. */
341 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
344 if (wmw->lpWaveFormat->nBlockAlign !=
345 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
346 WORD size = wmw->lpWaveFormat->nChannels *
347 wmw->lpWaveFormat->wBitsPerSample/8;
348 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
349 wmw->lpWaveFormat->nBlockAlign, size);
350 wmw->lpWaveFormat->nBlockAlign = size;
352 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
353 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
354 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
355 wmw->lpWaveFormat->nBlockAlign;
356 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
357 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
358 wmw->lpWaveFormat->nAvgBytesPerSec = speed;
361 if (wmw->lpWaveFormat == &wmw->wfxRef) {
362 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
363 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
365 wmw->lpWaveFormat = pwfx;
368 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
371 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
374 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
377 lpckWaveData->cksize = 0;
378 lpckWaveData->fccType = 0;
379 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
381 /* create data chunk */
382 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
388 /* mciClose takes care of wmw->lpWaveFormat. */
389 return MCIERR_INVALID_FILE;
392 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
394 WCHAR szTmpPath[MAX_PATH];
396 DWORD dwRet = MMSYSERR_NOERROR;
403 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
404 WARN("can't retrieve temp path!\n");
405 return MCIERR_FILE_NOT_FOUND;
408 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
410 MAX_PATH * sizeof(WCHAR));
411 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
412 WARN("can't retrieve temp file name!\n");
413 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
414 return MCIERR_FILE_NOT_FOUND;
417 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
419 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
421 *hFile = mmioOpenW(*pszTmpFileName, NULL,
422 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
425 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
426 /* temporary file could not be created. clean filename. */
427 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
428 dwRet = MCIERR_FILE_NOT_FOUND;
434 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
436 LRESULT dwRet = MMSYSERR_NOERROR;
439 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
440 if (!fn) return MCIERR_OUT_OF_MEMORY;
441 strcpyW(fn, filename);
442 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
443 wmw->openParms.lpstrElementName = fn;
445 if (strlenW(filename) > 0) {
446 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
447 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
449 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
450 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
452 if (wmw->hFile == 0) {
453 WARN("can't find file=%s!\n", debugstr_w(filename));
454 dwRet = MCIERR_FILE_NOT_FOUND;
458 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
460 /* make sure we're are the beginning of the file */
461 mmioSeek(wmw->hFile, 0, SEEK_SET);
463 /* first reading of this file. read the waveformat chunk */
464 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
465 dwRet = MCIERR_INVALID_FILE;
467 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
468 (LPSTR)&(lpckMainRIFF->ckid),
469 (LPSTR) &(lpckMainRIFF->fccType),
470 (lpckMainRIFF->cksize));
472 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
473 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
474 dwRet = MCIERR_INVALID_FILE;
476 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
484 /**************************************************************************
485 * WAVE_mciOpen [internal]
487 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
490 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
492 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
493 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
494 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
496 if (dwFlags & MCI_OPEN_SHAREABLE)
497 return MCIERR_UNSUPPORTED_FUNCTION;
499 if (wmw->nUseCount > 0) {
500 /* The driver is already opened on this channel
501 * Wave driver cannot be shared
503 return MCIERR_DEVICE_OPEN;
510 wmw->dwStatus = MCI_MODE_NOT_READY;
512 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
513 /* will be set by WAVE_mciOpenFile */
514 wmw->openParms.lpstrElementName = NULL;
515 WAVE_mciDefaultFmt(wmw);
517 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
519 if (dwFlags & MCI_OPEN_ELEMENT) {
520 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
521 /* could it be that (DWORD)lpOpenParms->lpstrElementName
522 * contains the hFile value ?
524 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
526 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
529 TRACE("hFile=%p\n", wmw->hFile);
534 wmw->dwStatus = MCI_MODE_STOP;
538 mmioClose(wmw->hFile, 0);
544 /**************************************************************************
545 * WAVE_mciCue [internal]
547 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
552 This routine is far from complete. At the moment only a check is done on the
553 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
556 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
561 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
563 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
565 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
568 /* always close elements ? */
569 if (wmw->hFile != 0) {
570 mmioClose(wmw->hFile, 0);
574 dwRet = MMSYSERR_NOERROR; /* assume success */
576 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
577 dwRet = waveOutClose(wmw->hWave);
578 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
580 } else if (wmw->fInput) {
581 dwRet = waveInClose(wmw->hWave);
582 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
586 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
589 /**************************************************************************
590 * WAVE_mciStop [internal]
592 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
595 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
597 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
599 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
601 /* wait for playback thread (if any) to exit before processing further */
602 switch (wmw->dwStatus) {
605 case MCI_MODE_RECORD:
607 int oldStat = wmw->dwStatus;
608 wmw->dwStatus = MCI_MODE_NOT_READY;
609 if (oldStat == MCI_MODE_PAUSE)
610 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
612 while (wmw->dwStatus != MCI_MODE_STOP)
620 wmw->dwStatus = MCI_MODE_STOP;
622 if ((dwFlags & MCI_NOTIFY) && lpParms) {
623 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
624 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
630 /**************************************************************************
631 * WAVE_mciClose [internal]
633 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
636 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
638 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
640 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
642 if (wmw->dwStatus != MCI_MODE_STOP) {
643 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
648 if (wmw->nUseCount == 0) {
649 if (wmw->hFile != 0) {
650 mmioClose(wmw->hFile, 0);
655 if (wmw->lpWaveFormat != &wmw->wfxRef)
656 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
657 wmw->lpWaveFormat = &wmw->wfxRef;
658 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
659 wmw->openParms.lpstrElementName = NULL;
661 if ((dwFlags & MCI_NOTIFY) && lpParms) {
662 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
663 wmw->openParms.wDeviceID,
664 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
670 /**************************************************************************
671 * WAVE_mciPlayCallback [internal]
673 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
674 DWORD_PTR dwInstance,
675 LPARAM dwParam1, LPARAM dwParam2)
677 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
684 InterlockedIncrement(&wmw->dwEventCount);
685 TRACE("Returning waveHdr=%lx\n", dwParam1);
686 SetEvent(wmw->hEvent);
689 ERR("Unknown uMsg=%d\n", uMsg);
693 /******************************************************************
694 * WAVE_mciPlayWaitDone [internal]
696 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
699 ResetEvent(wmw->hEvent);
700 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
703 InterlockedIncrement(&wmw->dwEventCount);
705 WaitForSingleObject(wmw->hEvent, INFINITE);
709 /**************************************************************************
710 * WAVE_mciPlay [internal]
712 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
714 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
716 LONG bufsize, count, left;
718 LPWAVEHDR waveHdr = NULL;
719 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
722 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
724 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
725 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
727 if (wmw->hFile == 0) {
728 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
729 return MCIERR_FILE_NOT_FOUND;
732 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
733 /* FIXME: parameters (start/end) in lpParams may not be used */
734 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
739 /** This function will be called again by a thread when async is used.
740 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
741 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
743 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
744 return MCIERR_INTERNAL;
747 wmw->dwStatus = MCI_MODE_PLAY;
749 if (!(dwFlags & MCI_WAIT)) {
750 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciPlay, dwFlags,
751 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
754 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
755 if (wmw->lpWaveFormat->nBlockAlign !=
756 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
757 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
758 wmw->lpWaveFormat->nBlockAlign,
759 wmw->lpWaveFormat->nChannels *
760 wmw->lpWaveFormat->wBitsPerSample/8);
761 wmw->lpWaveFormat->nBlockAlign =
762 wmw->lpWaveFormat->nChannels *
763 wmw->lpWaveFormat->wBitsPerSample/8;
765 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
766 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
767 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
768 wmw->lpWaveFormat->nAvgBytesPerSec,
769 wmw->lpWaveFormat->nSamplesPerSec *
770 wmw->lpWaveFormat->nBlockAlign);
771 wmw->lpWaveFormat->nAvgBytesPerSec =
772 wmw->lpWaveFormat->nSamplesPerSec *
773 wmw->lpWaveFormat->nBlockAlign;
778 if (lpParms && (dwFlags & MCI_FROM)) {
779 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
781 if (lpParms && (dwFlags & MCI_TO)) {
782 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
785 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
787 if (end <= wmw->dwPosition)
788 return MMSYSERR_NOERROR;
791 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
792 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
794 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
795 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
798 /* go back to beginning of chunk plus the requested position */
799 /* FIXME: I'm not sure this is correct, notably because some data linked to
800 * the decompression state machine will not be correctly initialized.
801 * try it this way (other way would be to decompress from 0 up to dwPosition
802 * and to start sending to hWave when dwPosition is reached)
804 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
806 /* By default the device will be opened for output, the MCI_CUE function is there to
807 * change from output to input and back
809 /* FIXME: how to choose between several output channels ? here mapper is forced */
810 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
811 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
814 TRACE("Can't open low level audio device %d\n", dwRet);
815 dwRet = MCIERR_DEVICE_OPEN;
820 /* make it so that 3 buffers per second are needed */
821 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
823 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
824 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
825 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
826 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
827 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
828 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
829 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
830 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
831 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
832 dwRet = MCIERR_INTERNAL;
837 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
838 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
839 wmw->dwEventCount = 1L; /* for first buffer */
841 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
842 if (hEvent) SetEvent(hEvent);
844 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
845 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
846 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
847 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
850 /* count is always <= bufsize, so this is correct regarding the
851 * waveOutPrepareHeader function
853 waveHdr[whidx].dwBufferLength = count;
854 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
855 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
856 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
857 waveHdr[whidx].dwBytesRecorded);
858 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
860 wmw->dwPosition += count;
861 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
863 WAVE_mciPlayWaitDone(wmw);
867 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
869 /* just to get rid of some race conditions between play, stop and pause */
870 waveOutReset(wmw->hWave);
872 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
873 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
878 HeapFree(GetProcessHeap(), 0, waveHdr);
881 waveOutClose(wmw->hWave);
884 CloseHandle(wmw->hEvent);
886 wmw->dwStatus = MCI_MODE_STOP;
888 if (lpParms && (dwFlags & MCI_NOTIFY)) {
889 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
890 wmw->openParms.wDeviceID,
891 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
897 /**************************************************************************
898 * WAVE_mciRecordCallback [internal]
900 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
901 DWORD_PTR dwInstance,
902 LPARAM dwParam1, LPARAM dwParam2)
904 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
913 lpWaveHdr = (LPWAVEHDR) dwParam1;
915 InterlockedIncrement(&wmw->dwEventCount);
917 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
919 lpWaveHdr->dwFlags &= ~WHDR_DONE;
921 wmw->dwPosition += count;
922 /* else error reporting ?? */
923 if (wmw->dwStatus == MCI_MODE_RECORD)
925 /* Only queue up another buffer if we are recording. We could receive this
926 message also when waveInReset() is called, since it notifies on all wave
927 buffers that are outstanding. Queueing up more sometimes causes waveInClose
929 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
930 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
933 SetEvent(wmw->hEvent);
936 ERR("Unknown uMsg=%d\n", uMsg);
940 /******************************************************************
941 * WAVE_mciRecordWaitDone [internal]
943 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
946 ResetEvent(wmw->hEvent);
947 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
950 InterlockedIncrement(&wmw->dwEventCount);
952 WaitForSingleObject(wmw->hEvent, INFINITE);
956 /**************************************************************************
957 * WAVE_mciRecord [internal]
959 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
961 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
963 DWORD dwRet = MMSYSERR_NOERROR;
965 LPWAVEHDR waveHdr = NULL;
966 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
968 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
970 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
971 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
973 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
974 /* FIXME: parameters (start/end) in lpParams may not be used */
975 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
978 /* FIXME : since there is no way to determine in which mode the device is
979 * open (recording/playback) automatically switch from a mode to another
983 /** This function will be called again by a thread when async is used.
984 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
985 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
987 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
988 return MCIERR_INTERNAL;
991 wmw->dwStatus = MCI_MODE_RECORD;
993 if (!(dwFlags & MCI_WAIT)) {
994 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciRecord, dwFlags,
995 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
998 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
999 * we don't modify the wave part of an existing file (ie. we always erase an
1000 * existing content, we don't overwrite)
1002 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
1003 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
1004 if (dwRet != 0) return dwRet;
1007 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1008 if (dwRet != 0) return dwRet;
1011 if (lpParms && (dwFlags & MCI_FROM)) {
1012 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1015 if (lpParms && (dwFlags & MCI_TO)) {
1016 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1019 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1021 if (end <= wmw->dwPosition)
1023 return MMSYSERR_NOERROR;
1026 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1027 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1029 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1030 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1032 /* Go back to the beginning of the chunk plus the requested position */
1033 /* FIXME: I'm not sure this is correct, notably because some data linked to
1034 * the decompression state machine will not be correctly initialized.
1035 * Try it this way (other way would be to decompress from 0 up to dwPosition
1036 * and to start sending to hWave when dwPosition is reached).
1038 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1040 /* By default the device will be opened for output, the MCI_CUE function is there to
1041 * change from output to input and back
1043 /* FIXME: how to choose between several output channels ? here mapper is forced */
1044 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1045 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1047 if (dwRet != MMSYSERR_NOERROR) {
1048 TRACE("Can't open low level audio device %d\n", dwRet);
1049 dwRet = MCIERR_DEVICE_OPEN;
1054 /* make it so that 3 buffers per second are needed */
1055 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1057 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1058 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1059 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1060 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1061 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1062 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1063 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1065 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1066 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1067 dwRet = MCIERR_INTERNAL;
1071 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1072 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1073 dwRet = MCIERR_INTERNAL;
1077 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1078 wmw->dwEventCount = 1L; /* for first buffer */
1080 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1082 dwRet = waveInStart(wmw->hWave);
1084 if (hEvent) SetEvent(hEvent);
1086 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1087 WAVE_mciRecordWaitDone(wmw);
1090 /* needed so that the callback above won't add again the buffers returned by the reset */
1091 wmw->dwStatus = MCI_MODE_STOP;
1093 waveInReset(wmw->hWave);
1095 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1096 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1101 HeapFree(GetProcessHeap(), 0, waveHdr);
1104 waveInClose(wmw->hWave);
1107 CloseHandle(wmw->hEvent);
1109 wmw->dwStatus = MCI_MODE_STOP;
1111 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1112 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1113 wmw->openParms.wDeviceID,
1114 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1121 /**************************************************************************
1122 * WAVE_mciPause [internal]
1124 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1127 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1129 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1131 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1133 switch (wmw->dwStatus) {
1135 dwRet = waveOutPause(wmw->hWave);
1136 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1137 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1138 ERR("waveOutPause error %d\n",dwRet);
1139 dwRet = MCIERR_INTERNAL;
1142 case MCI_MODE_RECORD:
1143 dwRet = waveInStop(wmw->hWave);
1144 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1146 ERR("waveInStop error %d\n",dwRet);
1147 dwRet = MCIERR_INTERNAL;
1150 case MCI_MODE_PAUSE:
1151 dwRet = MMSYSERR_NOERROR;
1154 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1159 /**************************************************************************
1160 * WAVE_mciResume [internal]
1162 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1164 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1167 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1169 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1171 switch (wmw->dwStatus) {
1172 case MCI_MODE_PAUSE:
1173 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1175 dwRet = waveInStart(wmw->hWave);
1176 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1178 ERR("waveInStart error %d\n",dwRet);
1179 dwRet = MCIERR_INTERNAL;
1182 dwRet = waveOutRestart(wmw->hWave);
1183 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1185 ERR("waveOutRestart error %d\n",dwRet);
1186 dwRet = MCIERR_INTERNAL;
1191 case MCI_MODE_RECORD:
1192 dwRet = MMSYSERR_NOERROR;
1195 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1200 /**************************************************************************
1201 * WAVE_mciSeek [internal]
1203 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1206 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1208 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1210 if (lpParms == NULL) {
1211 ret = MCIERR_NULL_PARAMETER_BLOCK;
1212 } else if (wmw == NULL) {
1213 ret = MCIERR_INVALID_DEVICE_ID;
1215 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1217 if (dwFlags & MCI_SEEK_TO_START) {
1218 wmw->dwPosition = 0;
1219 } else if (dwFlags & MCI_SEEK_TO_END) {
1220 wmw->dwPosition = wmw->ckWaveData.cksize;
1221 } else if (dwFlags & MCI_TO) {
1222 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1224 WARN("dwFlag doesn't tell where to seek to...\n");
1225 return MCIERR_MISSING_PARAMETER;
1228 TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1230 if (dwFlags & MCI_NOTIFY) {
1231 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1232 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1238 /**************************************************************************
1239 * WAVE_mciSet [internal]
1241 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1243 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1245 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1247 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1248 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1250 if (dwFlags & MCI_SET_TIME_FORMAT) {
1251 switch (lpParms->dwTimeFormat) {
1252 case MCI_FORMAT_MILLISECONDS:
1253 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1254 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1256 case MCI_FORMAT_BYTES:
1257 TRACE("MCI_FORMAT_BYTES !\n");
1258 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1260 case MCI_FORMAT_SAMPLES:
1261 TRACE("MCI_FORMAT_SAMPLES !\n");
1262 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1265 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1266 return MCIERR_BAD_TIME_FORMAT;
1269 if (dwFlags & MCI_SET_VIDEO) {
1270 TRACE("No support for video !\n");
1271 return MCIERR_UNSUPPORTED_FUNCTION;
1273 if (dwFlags & MCI_SET_DOOR_OPEN) {
1274 TRACE("No support for door open !\n");
1275 return MCIERR_UNSUPPORTED_FUNCTION;
1277 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1278 TRACE("No support for door close !\n");
1279 return MCIERR_UNSUPPORTED_FUNCTION;
1281 if (dwFlags & MCI_SET_AUDIO) {
1282 if (dwFlags & MCI_SET_ON) {
1283 TRACE("MCI_SET_ON audio !\n");
1284 } else if (dwFlags & MCI_SET_OFF) {
1285 TRACE("MCI_SET_OFF audio !\n");
1287 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1288 return MCIERR_BAD_INTEGER;
1291 switch (lpParms->dwAudio)
1293 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1294 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1295 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1296 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1299 if (dwFlags & MCI_WAVE_INPUT)
1300 TRACE("MCI_WAVE_INPUT !\n");
1301 if (dwFlags & MCI_WAVE_OUTPUT)
1302 TRACE("MCI_WAVE_OUTPUT !\n");
1303 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1304 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1305 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1306 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1307 /* Set wave format parameters is refused after Open or Record.*/
1308 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1309 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag);
1310 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1311 if (((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag != WAVE_FORMAT_PCM)
1312 return MCIERR_OUTOFRANGE;
1314 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1315 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1316 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1317 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1319 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1320 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1321 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1322 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1324 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1325 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1326 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1327 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1329 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1330 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1331 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1332 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1334 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1335 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1336 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1337 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1342 /**************************************************************************
1343 * WAVE_mciSave [internal]
1345 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1347 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1348 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1349 WPARAM wparam = MCI_NOTIFY_FAILURE;
1351 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1352 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1353 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1355 if (dwFlags & MCI_WAIT)
1357 FIXME("MCI_WAIT not implemented\n");
1359 WAVE_mciStop(wDevID, 0, NULL);
1361 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1362 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1364 ret = mmioClose(wmw->hFile, 0);
1368 If the destination file already exists, it has to be overwritten. (Behaviour
1369 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1370 my applications. We are making use of mmioRename, which WILL NOT overwrite
1371 the destination file (which is what Windows does, also verified in Win2K)
1372 So, lets delete the destination file before calling mmioRename. If the
1373 destination file DOESN'T exist, the delete will fail silently. Let's also be
1374 careful not to lose our previous error code.
1376 tmpRet = GetLastError();
1377 DeleteFileW (lpParms->lpfilename);
1378 SetLastError(tmpRet);
1380 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1381 ret = MMSYSERR_NOERROR;
1384 if (dwFlags & MCI_NOTIFY) {
1385 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1387 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1388 wmw->openParms.wDeviceID, wparam);
1391 if (ret == MMSYSERR_NOERROR)
1392 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1397 /**************************************************************************
1398 * WAVE_mciStatus [internal]
1400 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1402 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1405 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1406 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1407 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1409 if (dwFlags & MCI_STATUS_ITEM) {
1410 switch (lpParms->dwItem) {
1411 case MCI_STATUS_CURRENT_TRACK:
1412 lpParms->dwReturn = 1;
1413 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1415 case MCI_STATUS_LENGTH:
1417 lpParms->dwReturn = 0;
1418 return MCIERR_UNSUPPORTED_FUNCTION;
1420 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1421 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1422 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1424 case MCI_STATUS_MODE:
1425 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1426 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1427 ret = MCI_RESOURCE_RETURNED;
1429 case MCI_STATUS_MEDIA_PRESENT:
1430 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1431 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1432 ret = MCI_RESOURCE_RETURNED;
1434 case MCI_STATUS_NUMBER_OF_TRACKS:
1435 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1436 lpParms->dwReturn = 1;
1437 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1439 case MCI_STATUS_POSITION:
1441 lpParms->dwReturn = 0;
1442 return MCIERR_UNSUPPORTED_FUNCTION;
1444 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1445 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1446 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1448 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1449 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1451 case MCI_STATUS_READY:
1452 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1453 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1454 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1455 ret = MCI_RESOURCE_RETURNED;
1457 case MCI_STATUS_TIME_FORMAT:
1458 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1459 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1460 ret = MCI_RESOURCE_RETURNED;
1462 case MCI_WAVE_INPUT:
1463 TRACE("MCI_WAVE_INPUT !\n");
1464 lpParms->dwReturn = 0;
1465 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1467 case MCI_WAVE_OUTPUT:
1468 TRACE("MCI_WAVE_OUTPUT !\n");
1471 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1472 lpParms->dwReturn = id;
1474 lpParms->dwReturn = 0;
1475 ret = MCIERR_WAVE_OUTPUTUNSPECIFIED;
1479 /* It is always ok to query wave format parameters,
1480 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1481 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1482 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1483 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1485 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1486 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1487 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1489 case MCI_WAVE_STATUS_BLOCKALIGN:
1490 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1491 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1493 case MCI_WAVE_STATUS_CHANNELS:
1494 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1495 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1497 case MCI_WAVE_STATUS_FORMATTAG:
1498 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1499 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1501 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1502 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1503 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1505 case MCI_WAVE_STATUS_LEVEL:
1506 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1507 lpParms->dwReturn = 0xAAAA5555;
1510 WARN("unknown command %08X !\n", lpParms->dwItem);
1511 return MCIERR_UNRECOGNIZED_COMMAND;
1514 if (dwFlags & MCI_NOTIFY) {
1515 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1516 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1521 /**************************************************************************
1522 * WAVE_mciGetDevCaps [internal]
1524 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1525 LPMCI_GETDEVCAPS_PARMS lpParms)
1527 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1530 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1532 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1533 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1535 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1536 switch(lpParms->dwItem) {
1537 case MCI_GETDEVCAPS_DEVICE_TYPE:
1538 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1539 ret = MCI_RESOURCE_RETURNED;
1541 case MCI_GETDEVCAPS_HAS_AUDIO:
1542 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1543 ret = MCI_RESOURCE_RETURNED;
1545 case MCI_GETDEVCAPS_HAS_VIDEO:
1546 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1547 ret = MCI_RESOURCE_RETURNED;
1549 case MCI_GETDEVCAPS_USES_FILES:
1550 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1551 ret = MCI_RESOURCE_RETURNED;
1553 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1554 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1555 ret = MCI_RESOURCE_RETURNED;
1557 case MCI_GETDEVCAPS_CAN_RECORD:
1558 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1559 ret = MCI_RESOURCE_RETURNED;
1561 case MCI_GETDEVCAPS_CAN_EJECT:
1562 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1563 ret = MCI_RESOURCE_RETURNED;
1565 case MCI_GETDEVCAPS_CAN_PLAY:
1566 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1567 ret = MCI_RESOURCE_RETURNED;
1569 case MCI_GETDEVCAPS_CAN_SAVE:
1570 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1571 ret = MCI_RESOURCE_RETURNED;
1573 case MCI_WAVE_GETDEVCAPS_INPUTS:
1574 lpParms->dwReturn = 1;
1576 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1577 lpParms->dwReturn = 1;
1580 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1581 return MCIERR_UNRECOGNIZED_COMMAND;
1584 WARN("No GetDevCaps-Item !\n");
1585 return MCIERR_UNRECOGNIZED_COMMAND;
1590 /**************************************************************************
1591 * WAVE_mciInfo [internal]
1593 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1597 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1599 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1601 if (!lpParms || !lpParms->lpstrReturn)
1602 return MCIERR_NULL_PARAMETER_BLOCK;
1605 ret = MCIERR_INVALID_DEVICE_ID;
1607 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1608 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1609 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1611 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1613 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1614 case MCI_INFO_PRODUCT: str = wszAudio; break;
1615 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1616 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1617 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1619 WARN("Don't know this info command (%u)\n", dwFlags);
1620 ret = MCIERR_UNRECOGNIZED_COMMAND;
1624 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1625 ret = MCIERR_PARAM_OVERFLOW;
1627 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1630 lpParms->lpstrReturn[0] = 0;
1636 /**************************************************************************
1637 * DriverProc (MCIWAVE.@)
1639 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1640 LPARAM dwParam1, LPARAM dwParam2)
1642 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1643 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1646 case DRV_LOAD: return 1;
1647 case DRV_FREE: return 1;
1648 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1649 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1650 case DRV_ENABLE: return 1;
1651 case DRV_DISABLE: return 1;
1652 case DRV_QUERYCONFIGURE: return 1;
1653 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1654 case DRV_INSTALL: return DRVCNF_RESTART;
1655 case DRV_REMOVE: return DRVCNF_RESTART;
1658 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1661 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1662 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1663 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1664 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1665 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1666 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1667 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1668 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1669 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1670 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1671 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1672 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1673 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1674 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1675 /* commands that should be supported */
1690 FIXME("Unsupported yet command [%u]\n", wMsg);
1693 TRACE("Unsupported command [%u]\n", wMsg);
1695 /* option which can be silenced */
1700 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1703 FIXME("is probably wrong msg [%u]\n", wMsg);
1704 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1706 return MCIERR_UNRECOGNIZED_COMMAND;