localspl: Exclude unused headers.
[wine.git] / dlls / mciwave / mciwave.c
blob18c77fe44267550d67b3641ff3fe7170a2046f60
1 /*
2 * Sample 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
23 #include <stdarg.h>
25 #include "winerror.h"
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "mmddk.h"
31 #include "wownt32.h"
32 #include "digitalv.h"
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
38 typedef struct {
39 UINT wDevID;
40 HANDLE hWave;
41 int nUseCount; /* Incremented for each shared open */
42 BOOL fShareable; /* TRUE if first open was shareable */
43 HMMIO hFile; /* mmio file handle open as Element */
44 MCI_WAVE_OPEN_PARMSW openParms;
45 WAVEFORMATEX wfxRef;
46 LPWAVEFORMATEX lpWaveFormat;
47 BOOL fInput; /* FALSE = Output, TRUE = Input */
48 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
49 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
50 DWORD dwPosition; /* position in bytes in chunk */
51 HANDLE hEvent; /* for synchronization */
52 LONG dwEventCount; /* for synchronization */
53 MMCKINFO ckMainRIFF; /* main RIFF chunk */
54 MMCKINFO ckWaveData; /* data chunk */
55 } WINE_MCIWAVE;
57 /* ===================================================================
58 * ===================================================================
59 * FIXME: should be using the new mmThreadXXXX functions from WINMM
60 * instead of those
61 * it would require to add a wine internal flag to mmThreadCreate
62 * in order to pass a 32 bit function instead of a 16 bit one
63 * ===================================================================
64 * =================================================================== */
66 struct SCA {
67 UINT wDevID;
68 UINT wMsg;
69 DWORD dwParam1;
70 DWORD dwParam2;
73 /**************************************************************************
74 * MCI_SCAStarter [internal]
76 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
78 struct SCA* sca = (struct SCA*)arg;
79 DWORD ret;
81 TRACE("In thread before async command (%08x,%u,%08x,%08x)\n",
82 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
83 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
84 TRACE("In thread after async command (%08x,%u,%08x,%08x)\n",
85 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
86 HeapFree(GetProcessHeap(), 0, sca);
87 ExitThread(ret);
88 WARN("Should not happen ? what's wrong\n");
89 /* should not go after this point */
90 return ret;
93 /**************************************************************************
94 * MCI_SendCommandAsync [internal]
96 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
97 DWORD dwParam2, UINT size)
99 HANDLE handle;
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 ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
120 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
121 return MCI_SCAStarter(&sca);
123 SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
124 CloseHandle(handle);
125 return 0;
128 /*======================================================================*
129 * MCI WAVE implementation *
130 *======================================================================*/
132 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
134 /**************************************************************************
135 * MCIWAVE_drvOpen [internal]
137 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
139 WINE_MCIWAVE* wmw;
141 if (modp == NULL) return 0xFFFFFFFF;
143 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
145 if (!wmw)
146 return 0;
148 wmw->wDevID = modp->wDeviceID;
149 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
150 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
151 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
153 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
154 wmw->wfxRef.nChannels = 1; /* MONO */
155 wmw->wfxRef.nSamplesPerSec = 11025;
156 wmw->wfxRef.nAvgBytesPerSec = 11025;
157 wmw->wfxRef.nBlockAlign = 1;
158 wmw->wfxRef.wBitsPerSample = 8;
159 wmw->wfxRef.cbSize = 0; /* don't care */
161 return modp->wDeviceID;
164 /**************************************************************************
165 * MCIWAVE_drvClose [internal]
167 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
169 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
171 if (wmw) {
172 HeapFree(GetProcessHeap(), 0, wmw);
173 mciSetDriverData(dwDevID, 0);
174 return 1;
176 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
179 /**************************************************************************
180 * WAVE_mciGetOpenDev [internal]
182 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
184 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
186 if (wmw == NULL || wmw->nUseCount == 0) {
187 WARN("Invalid wDevID=%u\n", wDevID);
188 return 0;
190 return wmw;
193 /**************************************************************************
194 * WAVE_ConvertByteToTimeFormat [internal]
196 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
198 DWORD ret = 0;
200 switch (wmw->dwMciTimeFormat) {
201 case MCI_FORMAT_MILLISECONDS:
202 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
203 break;
204 case MCI_FORMAT_BYTES:
205 ret = val;
206 break;
207 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
208 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
209 break;
210 default:
211 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
213 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
214 *lpRet = 0;
215 return ret;
218 /**************************************************************************
219 * WAVE_ConvertTimeFormatToByte [internal]
221 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
223 DWORD ret = 0;
225 switch (wmw->dwMciTimeFormat) {
226 case MCI_FORMAT_MILLISECONDS:
227 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
228 break;
229 case MCI_FORMAT_BYTES:
230 ret = val;
231 break;
232 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
233 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
234 break;
235 default:
236 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
238 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
239 return ret;
242 /**************************************************************************
243 * WAVE_mciReadFmt [internal]
245 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
247 MMCKINFO mmckInfo;
248 long r;
250 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
251 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
252 return MCIERR_INVALID_FILE;
253 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
254 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
256 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
257 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
258 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
259 if (r < sizeof(WAVEFORMAT))
260 return MCIERR_INVALID_FILE;
262 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
263 TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
264 TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec);
265 TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
266 TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
267 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
268 if (r >= (long)sizeof(WAVEFORMATEX))
269 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
271 mmioAscend(wmw->hFile, &mmckInfo, 0);
272 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
273 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
274 TRACE("can't find data chunk\n");
275 return MCIERR_INVALID_FILE;
277 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
278 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
279 TRACE("nChannels=%d nSamplesPerSec=%d\n",
280 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
282 return 0;
285 /**************************************************************************
286 * WAVE_mciDefaultFmt [internal]
288 static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
290 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
291 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
293 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
294 wmw->lpWaveFormat->nChannels = 1;
295 wmw->lpWaveFormat->nSamplesPerSec = 44000;
296 wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
297 wmw->lpWaveFormat->nBlockAlign = 1;
298 wmw->lpWaveFormat->wBitsPerSample = 8;
300 return 0;
303 /**************************************************************************
304 * WAVE_mciCreateRIFFSkeleton [internal]
306 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
308 MMCKINFO ckWaveFormat;
309 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
310 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
312 lpckRIFF->ckid = FOURCC_RIFF;
313 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
314 lpckRIFF->cksize = 0;
316 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
317 goto err;
319 ckWaveFormat.fccType = 0;
320 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
321 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
323 if (!wmw->lpWaveFormat)
325 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
326 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
327 memcpy(wmw->lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
330 /* we can only record PCM files... there is no way in the MCI API to specify
331 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
332 * structure
334 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
335 goto err;
337 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
338 goto err;
340 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
341 goto err;
343 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
344 goto err;
346 lpckWaveData->cksize = 0;
347 lpckWaveData->fccType = 0;
348 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
350 /* create data chunk */
351 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
352 goto err;
354 return 0;
356 err:
357 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
358 wmw->lpWaveFormat = NULL;
359 return MCIERR_INVALID_FILE;
362 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
364 WCHAR szTmpPath[MAX_PATH];
365 WCHAR szPrefix[4];
366 DWORD dwRet = MMSYSERR_NOERROR;
368 szPrefix[0] = 'M';
369 szPrefix[1] = 'C';
370 szPrefix[2] = 'I';
371 szPrefix[3] = '\0';
373 if (!GetTempPathW(sizeof(szTmpPath), szTmpPath)) {
374 WARN("can't retrieve temp path!\n");
375 return MCIERR_FILE_NOT_FOUND;
378 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
379 HEAP_ZERO_MEMORY,
380 MAX_PATH * sizeof(WCHAR));
381 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
382 WARN("can't retrieve temp file name!\n");
383 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
384 return MCIERR_FILE_NOT_FOUND;
387 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
389 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
391 *hFile = mmioOpenW(*pszTmpFileName, NULL,
392 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
394 if (*hFile == 0) {
395 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
396 /* temporary file could not be created. clean filename. */
397 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
398 dwRet = MCIERR_FILE_NOT_FOUND;
401 return dwRet;
404 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
406 LRESULT dwRet = MMSYSERR_NOERROR;
407 WCHAR* fn;
409 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
410 if (!fn) return MCIERR_OUT_OF_MEMORY;
411 strcpyW(fn, filename);
412 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
413 wmw->openParms.lpstrElementName = fn;
415 if (strlenW(filename) > 0) {
416 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
417 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
419 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
420 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
422 if (wmw->hFile == 0) {
423 WARN("can't find file=%s!\n", debugstr_w(filename));
424 dwRet = MCIERR_FILE_NOT_FOUND;
426 else
428 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
430 /* make sure we're are the beginning of the file */
431 mmioSeek(wmw->hFile, 0, SEEK_SET);
433 /* first reading of this file. read the waveformat chunk */
434 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
435 dwRet = MCIERR_INVALID_FILE;
436 } else {
437 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
438 (LPSTR)&(lpckMainRIFF->ckid),
439 (LPSTR) &(lpckMainRIFF->fccType),
440 (lpckMainRIFF->cksize));
442 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
443 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
444 dwRet = MCIERR_INVALID_FILE;
445 } else {
446 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
451 return dwRet;
454 /**************************************************************************
455 * WAVE_mciOpen [internal]
457 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
459 DWORD dwRet = 0;
460 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
462 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
463 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
464 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
466 if (dwFlags & MCI_OPEN_SHAREABLE)
467 return MCIERR_HARDWARE;
469 if (wmw->nUseCount > 0) {
470 /* The driver is already opened on this channel
471 * Wave driver cannot be shared
473 return MCIERR_DEVICE_OPEN;
476 wmw->nUseCount++;
478 wmw->fInput = FALSE;
479 wmw->hWave = 0;
480 wmw->dwStatus = MCI_MODE_NOT_READY;
481 wmw->hFile = 0;
482 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
483 /* will be set by WAVE_mciOpenFile */
484 wmw->openParms.lpstrElementName = NULL;
486 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
488 if (dwFlags & MCI_OPEN_ELEMENT) {
489 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
490 /* could it be that (DWORD)lpOpenParms->lpstrElementName
491 * contains the hFile value ?
493 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
494 } else {
495 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
499 TRACE("hFile=%p\n", wmw->hFile);
501 if (dwRet == 0 && !wmw->lpWaveFormat)
502 dwRet = WAVE_mciDefaultFmt(wmw);
504 if (dwRet == 0) {
505 if (wmw->lpWaveFormat) {
506 switch (wmw->lpWaveFormat->wFormatTag) {
507 case WAVE_FORMAT_PCM:
508 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
509 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
510 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
511 wmw->lpWaveFormat->nAvgBytesPerSec,
512 wmw->lpWaveFormat->nSamplesPerSec *
513 wmw->lpWaveFormat->nBlockAlign);
514 wmw->lpWaveFormat->nAvgBytesPerSec =
515 wmw->lpWaveFormat->nSamplesPerSec *
516 wmw->lpWaveFormat->nBlockAlign;
518 break;
521 wmw->dwPosition = 0;
523 wmw->dwStatus = MCI_MODE_STOP;
524 } else {
525 wmw->nUseCount--;
526 if (wmw->hFile != 0)
527 mmioClose(wmw->hFile, 0);
528 wmw->hFile = 0;
530 return dwRet;
533 /**************************************************************************
534 * WAVE_mciCue [internal]
536 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
539 FIXME
541 This routine is far from complete. At the moment only a check is done on the
542 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
543 is the default.
545 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
546 are ignored
549 DWORD dwRet;
550 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
552 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
554 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
556 /* FIXME */
557 /* always close elements ? */
558 if (wmw->hFile != 0) {
559 mmioClose(wmw->hFile, 0);
560 wmw->hFile = 0;
563 dwRet = MMSYSERR_NOERROR; /* assume success */
565 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
566 dwRet = waveOutClose(wmw->hWave);
567 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
568 wmw->fInput = TRUE;
569 } else if (wmw->fInput) {
570 dwRet = waveInClose(wmw->hWave);
571 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
572 wmw->fInput = FALSE;
574 wmw->hWave = 0;
575 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
578 /**************************************************************************
579 * WAVE_mciStop [internal]
581 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
583 DWORD dwRet = 0;
584 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
586 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
588 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
590 /* wait for playback thread (if any) to exit before processing further */
591 switch (wmw->dwStatus) {
592 case MCI_MODE_PAUSE:
593 case MCI_MODE_PLAY:
594 case MCI_MODE_RECORD:
596 int oldStat = wmw->dwStatus;
597 wmw->dwStatus = MCI_MODE_NOT_READY;
598 if (oldStat == MCI_MODE_PAUSE)
599 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
601 while (wmw->dwStatus != MCI_MODE_STOP)
602 Sleep(10);
603 break;
606 wmw->dwPosition = 0;
608 /* sanity resets */
609 wmw->dwStatus = MCI_MODE_STOP;
611 if ((dwFlags & MCI_NOTIFY) && lpParms) {
612 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
613 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
616 return dwRet;
619 /**************************************************************************
620 * WAVE_mciClose [internal]
622 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
624 DWORD dwRet = 0;
625 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
627 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
629 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
631 if (wmw->dwStatus != MCI_MODE_STOP) {
632 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
635 wmw->nUseCount--;
637 if (wmw->nUseCount == 0) {
638 if (wmw->hFile != 0) {
639 mmioClose(wmw->hFile, 0);
640 wmw->hFile = 0;
644 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
645 wmw->lpWaveFormat = NULL;
646 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
647 wmw->openParms.lpstrElementName = NULL;
649 if ((dwFlags & MCI_NOTIFY) && lpParms) {
650 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
651 wmw->openParms.wDeviceID,
652 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
655 return 0;
658 /**************************************************************************
659 * WAVE_mciPlayCallback [internal]
661 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
662 DWORD_PTR dwInstance,
663 LPARAM dwParam1, LPARAM dwParam2)
665 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
667 switch (uMsg) {
668 case WOM_OPEN:
669 case WOM_CLOSE:
670 break;
671 case WOM_DONE:
672 InterlockedIncrement(&wmw->dwEventCount);
673 TRACE("Returning waveHdr=%lx\n", dwParam1);
674 SetEvent(wmw->hEvent);
675 break;
676 default:
677 ERR("Unknown uMsg=%d\n", uMsg);
681 /******************************************************************
682 * WAVE_mciPlayWaitDone
686 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
688 for (;;) {
689 ResetEvent(wmw->hEvent);
690 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
691 break;
693 InterlockedIncrement(&wmw->dwEventCount);
695 WaitForSingleObject(wmw->hEvent, INFINITE);
699 /**************************************************************************
700 * WAVE_mciPlay [internal]
702 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
704 DWORD end;
705 LONG bufsize, count, left;
706 DWORD dwRet = 0;
707 LPWAVEHDR waveHdr = NULL;
708 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
709 int whidx;
711 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
713 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
714 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
716 wmw->fInput = FALSE;
718 if (wmw->hFile == 0) {
719 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
720 return MCIERR_FILE_NOT_FOUND;
723 if (wmw->dwStatus == MCI_MODE_PAUSE) {
724 /* FIXME: parameters (start/end) in lpParams may not be used */
725 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
728 /** This function will be called again by a thread when async is used.
729 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
730 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
732 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
733 return MCIERR_INTERNAL;
736 wmw->dwStatus = MCI_MODE_PLAY;
738 if (!(dwFlags & MCI_WAIT)) {
739 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
740 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
743 end = 0xFFFFFFFF;
744 if (lpParms && (dwFlags & MCI_FROM)) {
745 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
747 if (lpParms && (dwFlags & MCI_TO)) {
748 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
751 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
753 if (end <= wmw->dwPosition)
754 return TRUE;
757 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
758 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
760 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
761 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
763 if (dwRet == 0) {
764 if (wmw->lpWaveFormat) {
765 switch (wmw->lpWaveFormat->wFormatTag) {
766 case WAVE_FORMAT_PCM:
767 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
768 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
769 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
770 wmw->lpWaveFormat->nAvgBytesPerSec,
771 wmw->lpWaveFormat->nSamplesPerSec *
772 wmw->lpWaveFormat->nBlockAlign);
773 wmw->lpWaveFormat->nAvgBytesPerSec =
774 wmw->lpWaveFormat->nSamplesPerSec *
775 wmw->lpWaveFormat->nBlockAlign;
777 break;
780 } else {
781 TRACE("can't retrieve wave format %d\n", dwRet);
782 goto cleanUp;
786 /* go back to beginning of chunk plus the requested position */
787 /* FIXME: I'm not sure this is correct, notably because some data linked to
788 * the decompression state machine will not be correcly initialized.
789 * try it this way (other way would be to decompress from 0 up to dwPosition
790 * and to start sending to hWave when dwPosition is reached)
792 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
794 /* By default the device will be opened for output, the MCI_CUE function is there to
795 * change from output to input and back
797 /* FIXME: how to choose between several output channels ? here mapper is forced */
798 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
799 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
801 if (dwRet != 0) {
802 TRACE("Can't open low level audio device %d\n", dwRet);
803 dwRet = MCIERR_DEVICE_OPEN;
804 wmw->hWave = 0;
805 goto cleanUp;
808 /* make it so that 3 buffers per second are needed */
809 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
811 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
812 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
813 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
814 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
815 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
816 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
817 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
818 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
819 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
820 dwRet = MCIERR_INTERNAL;
821 goto cleanUp;
824 whidx = 0;
825 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
826 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
827 wmw->dwEventCount = 1L; /* for first buffer */
829 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
831 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
832 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
833 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
834 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
835 if (count < 1)
836 break;
837 /* count is always <= bufsize, so this is correct regarding the
838 * waveOutPrepareHeader function
840 waveHdr[whidx].dwBufferLength = count;
841 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
842 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
843 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
844 waveHdr[whidx].dwBytesRecorded);
845 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
846 left -= count;
847 wmw->dwPosition += count;
848 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
850 WAVE_mciPlayWaitDone(wmw);
851 whidx ^= 1;
854 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
856 /* just to get rid of some race conditions between play, stop and pause */
857 waveOutReset(wmw->hWave);
859 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
860 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
862 dwRet = 0;
864 cleanUp:
865 HeapFree(GetProcessHeap(), 0, waveHdr);
867 if (wmw->hWave) {
868 waveOutClose(wmw->hWave);
869 wmw->hWave = 0;
871 CloseHandle(wmw->hEvent);
873 if (lpParms && (dwFlags & MCI_NOTIFY)) {
874 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
875 wmw->openParms.wDeviceID,
876 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
879 wmw->dwStatus = MCI_MODE_STOP;
881 return dwRet;
884 /**************************************************************************
885 * WAVE_mciPlayCallback [internal]
887 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
888 DWORD_PTR dwInstance,
889 LPARAM dwParam1, LPARAM dwParam2)
891 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
892 LPWAVEHDR lpWaveHdr;
893 LONG count = 0;
895 switch (uMsg) {
896 case WIM_OPEN:
897 case WIM_CLOSE:
898 break;
899 case WIM_DATA:
900 lpWaveHdr = (LPWAVEHDR) dwParam1;
902 InterlockedIncrement(&wmw->dwEventCount);
904 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
906 lpWaveHdr->dwFlags &= ~WHDR_DONE;
907 if (count > 0)
908 wmw->dwPosition += count;
909 /* else error reporting ?? */
910 if (wmw->dwStatus == MCI_MODE_RECORD)
912 /* Only queue up another buffer if we are recording. We could receive this
913 message also when waveInReset() is called, since it notifies on all wave
914 buffers that are outstanding. Queueing up more sometimes causes waveInClose
915 to fail. */
916 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
917 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
920 SetEvent(wmw->hEvent);
921 break;
922 default:
923 ERR("Unknown uMsg=%d\n", uMsg);
927 /******************************************************************
928 * bWAVE_mciRecordWaitDone
931 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
933 for (;;) {
934 ResetEvent(wmw->hEvent);
935 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
936 break;
938 InterlockedIncrement(&wmw->dwEventCount);
940 WaitForSingleObject(wmw->hEvent, INFINITE);
944 /**************************************************************************
945 * WAVE_mciRecord [internal]
947 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
949 DWORD end;
950 DWORD dwRet = MMSYSERR_NOERROR;
951 LONG bufsize;
952 LPWAVEHDR waveHdr = NULL;
953 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
955 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
957 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
958 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
960 /* FIXME : since there is no way to determine in which mode the device is
961 * open (recording/playback) automatically switch from a mode to another
963 wmw->fInput = TRUE;
965 if (wmw->dwStatus == MCI_MODE_PAUSE) {
966 /* FIXME: parameters (start/end) in lpParams may not be used */
967 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
970 /** This function will be called again by a thread when async is used.
971 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
972 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
974 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
975 return MCIERR_INTERNAL;
978 wmw->dwStatus = MCI_MODE_RECORD;
980 if (!(dwFlags & MCI_WAIT)) {
981 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
982 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
985 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
986 * we don't modify the wave part of an existing file (ie. we always erase an
987 * existing content, we don't overwrite)
989 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
990 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
991 if (dwRet != 0) return dwRet;
993 /* new RIFF file */
994 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
995 if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
997 end = 0xFFFFFFFF;
998 if (lpParms && (dwFlags & MCI_FROM)) {
999 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1002 if (lpParms && (dwFlags & MCI_TO)) {
1003 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1006 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1008 if (end <= wmw->dwPosition)
1010 return TRUE;
1013 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1014 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1016 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1017 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1019 /* go back to beginning of chunk plus the requested position */
1020 /* FIXME: I'm not sure this is correct, notably because some data linked to
1021 * the decompression state machine will not be correcly initialized.
1022 * try it this way (other way would be to decompress from 0 up to dwPosition
1023 * and to start sending to hWave when dwPosition is reached)
1025 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1027 /* By default the device will be opened for output, the MCI_CUE function is there to
1028 * change from output to input and back
1030 /* FIXME: how to choose between several output channels ? here mapper is forced */
1031 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1032 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1034 if (dwRet != MMSYSERR_NOERROR) {
1035 TRACE("Can't open low level audio device %d\n", dwRet);
1036 dwRet = MCIERR_DEVICE_OPEN;
1037 wmw->hWave = 0;
1038 goto cleanUp;
1041 /* make it so that 3 buffers per second are needed */
1042 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1044 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1045 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1046 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1047 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1048 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1049 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1050 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1052 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1053 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1054 dwRet = MCIERR_INTERNAL;
1055 goto cleanUp;
1058 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1059 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1060 dwRet = MCIERR_INTERNAL;
1061 goto cleanUp;
1064 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1065 wmw->dwEventCount = 1L; /* for first buffer */
1067 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1069 dwRet = waveInStart(wmw->hWave);
1071 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1072 WAVE_mciRecordWaitDone(wmw);
1075 /* needed so that the callback above won't add again the buffers returned by the reset */
1076 wmw->dwStatus = MCI_MODE_STOP;
1078 waveInReset(wmw->hWave);
1080 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1081 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1083 dwRet = 0;
1085 cleanUp:
1086 HeapFree(GetProcessHeap(), 0, waveHdr);
1088 if (wmw->hWave) {
1089 waveInClose(wmw->hWave);
1090 wmw->hWave = 0;
1092 CloseHandle(wmw->hEvent);
1094 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1095 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1096 wmw->openParms.wDeviceID,
1097 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1100 wmw->dwStatus = MCI_MODE_STOP;
1102 return dwRet;
1106 /**************************************************************************
1107 * WAVE_mciPause [internal]
1109 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1111 DWORD dwRet;
1112 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1114 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1116 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1117 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1119 if (wmw->dwStatus == MCI_MODE_PLAY) {
1120 wmw->dwStatus = MCI_MODE_PAUSE;
1123 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1124 else dwRet = waveOutPause(wmw->hWave);
1126 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1129 /**************************************************************************
1130 * WAVE_mciResume [internal]
1132 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1134 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1135 DWORD dwRet = 0;
1137 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1139 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1141 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1142 wmw->dwStatus = MCI_MODE_PLAY;
1145 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1146 else dwRet = waveOutRestart(wmw->hWave);
1147 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1150 /**************************************************************************
1151 * WAVE_mciSeek [internal]
1153 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1155 DWORD ret = 0;
1156 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1158 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1160 if (lpParms == NULL) {
1161 ret = MCIERR_NULL_PARAMETER_BLOCK;
1162 } else if (wmw == NULL) {
1163 ret = MCIERR_INVALID_DEVICE_ID;
1164 } else {
1165 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1167 if (dwFlags & MCI_SEEK_TO_START) {
1168 wmw->dwPosition = 0;
1169 } else if (dwFlags & MCI_SEEK_TO_END) {
1170 wmw->dwPosition = wmw->ckWaveData.cksize;
1171 } else if (dwFlags & MCI_TO) {
1172 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1173 } else {
1174 WARN("dwFlag doesn't tell where to seek to...\n");
1175 return MCIERR_MISSING_PARAMETER;
1178 TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1180 if (dwFlags & MCI_NOTIFY) {
1181 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1182 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1185 return ret;
1188 /**************************************************************************
1189 * WAVE_mciSet [internal]
1191 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1193 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1195 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1197 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1198 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1200 if (dwFlags & MCI_SET_TIME_FORMAT) {
1201 switch (lpParms->dwTimeFormat) {
1202 case MCI_FORMAT_MILLISECONDS:
1203 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1204 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1205 break;
1206 case MCI_FORMAT_BYTES:
1207 TRACE("MCI_FORMAT_BYTES !\n");
1208 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1209 break;
1210 case MCI_FORMAT_SAMPLES:
1211 TRACE("MCI_FORMAT_SAMPLES !\n");
1212 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1213 break;
1214 default:
1215 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1216 return MCIERR_BAD_TIME_FORMAT;
1219 if (dwFlags & MCI_SET_VIDEO) {
1220 TRACE("No support for video !\n");
1221 return MCIERR_UNSUPPORTED_FUNCTION;
1223 if (dwFlags & MCI_SET_DOOR_OPEN) {
1224 TRACE("No support for door open !\n");
1225 return MCIERR_UNSUPPORTED_FUNCTION;
1227 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1228 TRACE("No support for door close !\n");
1229 return MCIERR_UNSUPPORTED_FUNCTION;
1231 if (dwFlags & MCI_SET_AUDIO) {
1232 if (dwFlags & MCI_SET_ON) {
1233 TRACE("MCI_SET_ON audio !\n");
1234 } else if (dwFlags & MCI_SET_OFF) {
1235 TRACE("MCI_SET_OFF audio !\n");
1236 } else {
1237 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1238 return MCIERR_BAD_INTEGER;
1241 switch (lpParms->dwAudio)
1243 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1244 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1245 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1246 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1249 if (dwFlags & MCI_WAVE_INPUT)
1250 TRACE("MCI_WAVE_INPUT !\n");
1251 if (dwFlags & MCI_WAVE_OUTPUT)
1252 TRACE("MCI_WAVE_OUTPUT !\n");
1253 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1254 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1255 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1256 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1257 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1258 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1259 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1261 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1262 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1263 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1265 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1266 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1267 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1269 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1270 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1271 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1273 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1274 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1275 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1277 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1278 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1279 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1281 return 0;
1284 /**************************************************************************
1285 * WAVE_mciSave [internal]
1287 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1289 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1290 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1291 WPARAM wparam = MCI_NOTIFY_FAILURE;
1293 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1294 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1295 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1297 if (dwFlags & MCI_WAIT)
1299 FIXME("MCI_WAIT not implemented\n");
1301 WAVE_mciStop(wDevID, 0, NULL);
1303 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1304 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1306 ret = mmioClose(wmw->hFile, 0);
1307 wmw->hFile = 0;
1310 If the destination file already exists, it has to be overwritten. (Behaviour
1311 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1312 my applications. We are making use of mmioRename, which WILL NOT overwrite
1313 the destination file (which is what Windows does, also verified in Win2K)
1314 So, lets delete the destination file before calling mmioRename. If the
1315 destination file DOESN'T exist, the delete will fail silently. Let's also be
1316 careful not to lose our previous error code.
1318 tmpRet = GetLastError();
1319 DeleteFileW (lpParms->lpfilename);
1320 SetLastError(tmpRet);
1322 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1323 ret = MMSYSERR_NOERROR;
1326 if (dwFlags & MCI_NOTIFY) {
1327 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1329 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1330 wmw->openParms.wDeviceID, wparam);
1333 if (ret == MMSYSERR_NOERROR)
1334 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1336 return ret;
1339 /**************************************************************************
1340 * WAVE_mciStatus [internal]
1342 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1344 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1345 DWORD ret = 0;
1347 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1348 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1349 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1351 if (dwFlags & MCI_STATUS_ITEM) {
1352 switch (lpParms->dwItem) {
1353 case MCI_STATUS_CURRENT_TRACK:
1354 lpParms->dwReturn = 1;
1355 TRACE("MCI_STATUS_CURRENT_TRACK => %u\n", lpParms->dwReturn);
1356 break;
1357 case MCI_STATUS_LENGTH:
1358 if (!wmw->hFile) {
1359 lpParms->dwReturn = 0;
1360 return MCIERR_UNSUPPORTED_FUNCTION;
1362 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1363 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1364 TRACE("MCI_STATUS_LENGTH => %u\n", lpParms->dwReturn);
1365 break;
1366 case MCI_STATUS_MODE:
1367 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1368 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1369 ret = MCI_RESOURCE_RETURNED;
1370 break;
1371 case MCI_STATUS_MEDIA_PRESENT:
1372 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1373 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1374 ret = MCI_RESOURCE_RETURNED;
1375 break;
1376 case MCI_STATUS_NUMBER_OF_TRACKS:
1377 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1378 lpParms->dwReturn = 1;
1379 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %u!\n", lpParms->dwReturn);
1380 break;
1381 case MCI_STATUS_POSITION:
1382 if (!wmw->hFile) {
1383 lpParms->dwReturn = 0;
1384 return MCIERR_UNSUPPORTED_FUNCTION;
1386 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1387 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1388 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1389 &ret);
1390 TRACE("MCI_STATUS_POSITION %s => %u\n",
1391 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1392 break;
1393 case MCI_STATUS_READY:
1394 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1395 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1396 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1397 ret = MCI_RESOURCE_RETURNED;
1398 break;
1399 case MCI_STATUS_TIME_FORMAT:
1400 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1401 TRACE("MCI_STATUS_TIME_FORMAT => %u\n", lpParms->dwReturn);
1402 ret = MCI_RESOURCE_RETURNED;
1403 break;
1404 case MCI_WAVE_INPUT:
1405 TRACE("MCI_WAVE_INPUT !\n");
1406 lpParms->dwReturn = 0;
1407 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1408 break;
1409 case MCI_WAVE_OUTPUT:
1410 TRACE("MCI_WAVE_OUTPUT !\n");
1412 UINT id;
1413 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1414 lpParms->dwReturn = id;
1415 } else {
1416 lpParms->dwReturn = 0;
1417 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1420 break;
1421 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1422 if (!wmw->hFile) {
1423 lpParms->dwReturn = 0;
1424 return MCIERR_UNSUPPORTED_FUNCTION;
1426 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1427 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %u!\n", lpParms->dwReturn);
1428 break;
1429 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1430 if (!wmw->hFile) {
1431 lpParms->dwReturn = 0;
1432 return MCIERR_UNSUPPORTED_FUNCTION;
1434 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1435 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %u!\n", lpParms->dwReturn);
1436 break;
1437 case MCI_WAVE_STATUS_BLOCKALIGN:
1438 if (!wmw->hFile) {
1439 lpParms->dwReturn = 0;
1440 return MCIERR_UNSUPPORTED_FUNCTION;
1442 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1443 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %u!\n", lpParms->dwReturn);
1444 break;
1445 case MCI_WAVE_STATUS_CHANNELS:
1446 if (!wmw->hFile) {
1447 lpParms->dwReturn = 0;
1448 return MCIERR_UNSUPPORTED_FUNCTION;
1450 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1451 TRACE("MCI_WAVE_STATUS_CHANNELS => %u!\n", lpParms->dwReturn);
1452 break;
1453 case MCI_WAVE_STATUS_FORMATTAG:
1454 if (!wmw->hFile) {
1455 lpParms->dwReturn = 0;
1456 return MCIERR_UNSUPPORTED_FUNCTION;
1458 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1459 TRACE("MCI_WAVE_FORMATTAG => %u!\n", lpParms->dwReturn);
1460 break;
1461 case MCI_WAVE_STATUS_LEVEL:
1462 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1463 lpParms->dwReturn = 0xAAAA5555;
1464 break;
1465 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1466 if (!wmw->hFile) {
1467 lpParms->dwReturn = 0;
1468 return MCIERR_UNSUPPORTED_FUNCTION;
1470 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1471 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %u!\n", lpParms->dwReturn);
1472 break;
1473 default:
1474 WARN("unknown command %08X !\n", lpParms->dwItem);
1475 return MCIERR_UNRECOGNIZED_COMMAND;
1478 if (dwFlags & MCI_NOTIFY) {
1479 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1480 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1482 return ret;
1485 /**************************************************************************
1486 * WAVE_mciGetDevCaps [internal]
1488 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1489 LPMCI_GETDEVCAPS_PARMS lpParms)
1491 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1492 DWORD ret = 0;
1494 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1496 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1497 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1499 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1500 switch(lpParms->dwItem) {
1501 case MCI_GETDEVCAPS_DEVICE_TYPE:
1502 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1503 ret = MCI_RESOURCE_RETURNED;
1504 break;
1505 case MCI_GETDEVCAPS_HAS_AUDIO:
1506 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1507 ret = MCI_RESOURCE_RETURNED;
1508 break;
1509 case MCI_GETDEVCAPS_HAS_VIDEO:
1510 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1511 ret = MCI_RESOURCE_RETURNED;
1512 break;
1513 case MCI_GETDEVCAPS_USES_FILES:
1514 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1515 ret = MCI_RESOURCE_RETURNED;
1516 break;
1517 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1518 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1519 ret = MCI_RESOURCE_RETURNED;
1520 break;
1521 case MCI_GETDEVCAPS_CAN_RECORD:
1522 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1523 ret = MCI_RESOURCE_RETURNED;
1524 break;
1525 case MCI_GETDEVCAPS_CAN_EJECT:
1526 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1527 ret = MCI_RESOURCE_RETURNED;
1528 break;
1529 case MCI_GETDEVCAPS_CAN_PLAY:
1530 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1531 ret = MCI_RESOURCE_RETURNED;
1532 break;
1533 case MCI_GETDEVCAPS_CAN_SAVE:
1534 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1535 ret = MCI_RESOURCE_RETURNED;
1536 break;
1537 case MCI_WAVE_GETDEVCAPS_INPUTS:
1538 lpParms->dwReturn = 1;
1539 break;
1540 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1541 lpParms->dwReturn = 1;
1542 break;
1543 default:
1544 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1545 return MCIERR_UNRECOGNIZED_COMMAND;
1547 } else {
1548 WARN("No GetDevCaps-Item !\n");
1549 return MCIERR_UNRECOGNIZED_COMMAND;
1551 return ret;
1554 /**************************************************************************
1555 * WAVE_mciInfo [internal]
1557 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1559 DWORD ret = 0;
1560 LPCWSTR str = 0;
1561 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1563 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1565 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1566 ret = MCIERR_NULL_PARAMETER_BLOCK;
1567 } else if (wmw == NULL) {
1568 ret = MCIERR_INVALID_DEVICE_ID;
1569 } else {
1570 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1571 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1572 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1574 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1576 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1577 case MCI_INFO_PRODUCT: str = wszAudio; break;
1578 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1579 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1580 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1581 default:
1582 WARN("Don't know this info command (%u)\n", dwFlags);
1583 ret = MCIERR_UNRECOGNIZED_COMMAND;
1586 if (str) {
1587 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1588 ret = MCIERR_PARAM_OVERFLOW;
1589 } else {
1590 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1592 } else {
1593 lpParms->lpstrReturn[0] = 0;
1596 return ret;
1599 /**************************************************************************
1600 * DriverProc (MCIWAVE.@)
1602 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1603 LPARAM dwParam1, LPARAM dwParam2)
1605 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1606 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1608 switch (wMsg) {
1609 case DRV_LOAD: return 1;
1610 case DRV_FREE: return 1;
1611 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1612 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1613 case DRV_ENABLE: return 1;
1614 case DRV_DISABLE: return 1;
1615 case DRV_QUERYCONFIGURE: return 1;
1616 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1617 case DRV_INSTALL: return DRVCNF_RESTART;
1618 case DRV_REMOVE: return DRVCNF_RESTART;
1621 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1623 switch (wMsg) {
1624 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1625 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1626 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1627 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1628 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1629 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1630 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1631 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1632 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1633 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1634 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1635 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1636 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1637 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1638 /* commands that should be supported */
1639 case MCI_LOAD:
1640 case MCI_FREEZE:
1641 case MCI_PUT:
1642 case MCI_REALIZE:
1643 case MCI_UNFREEZE:
1644 case MCI_UPDATE:
1645 case MCI_WHERE:
1646 case MCI_STEP:
1647 case MCI_SPIN:
1648 case MCI_ESCAPE:
1649 case MCI_COPY:
1650 case MCI_CUT:
1651 case MCI_DELETE:
1652 case MCI_PASTE:
1653 FIXME("Unsupported yet command [%u]\n", wMsg);
1654 break;
1655 case MCI_WINDOW:
1656 TRACE("Unsupported command [%u]\n", wMsg);
1657 break;
1658 /* option which can be silenced */
1659 case MCI_CONFIGURE:
1660 return 0;
1661 case MCI_OPEN:
1662 case MCI_CLOSE:
1663 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1664 break;
1665 default:
1666 FIXME("is probably wrong msg [%u]\n", wMsg);
1667 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1669 return MCIERR_UNRECOGNIZED_COMMAND;