t2embed: Add stub dll.
[wine/multimedia.git] / dlls / mciwave / mciwave.c
blobc533e8577978a102e7c21468bfb6a89180bfe729
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 "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "mmddk.h"
30 #include "wownt32.h"
31 #include "digitalv.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
37 typedef struct {
38 UINT wDevID;
39 HANDLE hWave;
40 int nUseCount; /* Incremented for each shared open */
41 BOOL fShareable; /* TRUE if first open was shareable */
42 HMMIO hFile; /* mmio file handle open as Element */
43 MCI_WAVE_OPEN_PARMSW openParms;
44 WAVEFORMATEX wfxRef;
45 LPWAVEFORMATEX lpWaveFormat;
46 BOOL fInput; /* FALSE = Output, TRUE = Input */
47 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
48 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
49 DWORD dwPosition; /* position in bytes in chunk */
50 HANDLE hEvent; /* for synchronization */
51 LONG dwEventCount; /* for synchronization */
52 MMCKINFO ckMainRIFF; /* main RIFF chunk */
53 MMCKINFO ckWaveData; /* data chunk */
54 } WINE_MCIWAVE;
56 /* ===================================================================
57 * ===================================================================
58 * FIXME: should be using the new mmThreadXXXX functions from WINMM
59 * instead of those
60 * it would require to add a wine internal flag to mmThreadCreate
61 * in order to pass a 32 bit function instead of a 16 bit one
62 * ===================================================================
63 * =================================================================== */
65 struct SCA {
66 UINT wDevID;
67 UINT wMsg;
68 DWORD_PTR dwParam1;
69 DWORD_PTR dwParam2;
72 /**************************************************************************
73 * MCI_SCAStarter [internal]
75 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
77 struct SCA* sca = (struct SCA*)arg;
78 DWORD ret;
80 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
81 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
82 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
83 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
84 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
85 HeapFree(GetProcessHeap(), 0, sca);
86 ExitThread(ret);
87 WARN("Should not happen ? what's wrong\n");
88 /* should not go after this point */
89 return ret;
92 /**************************************************************************
93 * MCI_SendCommandAsync [internal]
95 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
96 DWORD_PTR dwParam2, UINT size)
98 HANDLE handle;
99 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
101 if (sca == 0)
102 return MCIERR_OUT_OF_MEMORY;
104 sca->wDevID = wDevID;
105 sca->wMsg = wMsg;
106 sca->dwParam1 = dwParam1;
108 if (size && dwParam2) {
109 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
110 /* copy structure passed by program in dwParam2 to be sure
111 * we can still use it whatever the program does
113 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
114 } else {
115 sca->dwParam2 = dwParam2;
118 if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
119 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
120 return MCI_SCAStarter(&sca);
122 SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
123 CloseHandle(handle);
124 return 0;
127 /*======================================================================*
128 * MCI WAVE implementation *
129 *======================================================================*/
131 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
133 /**************************************************************************
134 * MCIWAVE_drvOpen [internal]
136 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
138 WINE_MCIWAVE* wmw;
140 if (modp == NULL) return 0xFFFFFFFF;
142 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
144 if (!wmw)
145 return 0;
147 wmw->wDevID = modp->wDeviceID;
148 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
149 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
150 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
152 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
153 wmw->wfxRef.nChannels = 1; /* MONO */
154 wmw->wfxRef.nSamplesPerSec = 11025;
155 wmw->wfxRef.nAvgBytesPerSec = 11025;
156 wmw->wfxRef.nBlockAlign = 1;
157 wmw->wfxRef.wBitsPerSample = 8;
158 wmw->wfxRef.cbSize = 0; /* don't care */
160 return modp->wDeviceID;
163 /**************************************************************************
164 * MCIWAVE_drvClose [internal]
166 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
168 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
170 if (wmw) {
171 HeapFree(GetProcessHeap(), 0, wmw);
172 mciSetDriverData(dwDevID, 0);
173 return 1;
175 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
178 /**************************************************************************
179 * WAVE_mciGetOpenDev [internal]
181 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
183 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
185 if (wmw == NULL || wmw->nUseCount == 0) {
186 WARN("Invalid wDevID=%u\n", wDevID);
187 return 0;
189 return wmw;
192 /**************************************************************************
193 * WAVE_ConvertByteToTimeFormat [internal]
195 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
197 DWORD ret = 0;
199 switch (wmw->dwMciTimeFormat) {
200 case MCI_FORMAT_MILLISECONDS:
201 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
202 break;
203 case MCI_FORMAT_BYTES:
204 ret = val;
205 break;
206 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
207 ret = (val * 8) / (wmw->lpWaveFormat->wBitsPerSample ? wmw->lpWaveFormat->wBitsPerSample : 1);
208 break;
209 default:
210 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
212 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
213 *lpRet = 0;
214 return ret;
217 /**************************************************************************
218 * WAVE_ConvertTimeFormatToByte [internal]
220 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
222 DWORD ret = 0;
224 switch (wmw->dwMciTimeFormat) {
225 case MCI_FORMAT_MILLISECONDS:
226 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
227 break;
228 case MCI_FORMAT_BYTES:
229 ret = val;
230 break;
231 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
232 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
233 break;
234 default:
235 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
237 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
238 return ret;
241 /**************************************************************************
242 * WAVE_mciReadFmt [internal]
244 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
246 MMCKINFO mmckInfo;
247 long r;
249 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
250 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
251 return MCIERR_INVALID_FILE;
252 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
253 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
255 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
256 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
257 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
258 if (r < sizeof(WAVEFORMAT))
259 return MCIERR_INVALID_FILE;
261 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
262 TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
263 TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec);
264 TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
265 TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
266 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
267 if (r >= (long)sizeof(WAVEFORMATEX))
268 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
270 mmioAscend(wmw->hFile, &mmckInfo, 0);
271 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
272 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
273 TRACE("can't find data chunk\n");
274 return MCIERR_INVALID_FILE;
276 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
277 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
278 TRACE("nChannels=%d nSamplesPerSec=%d\n",
279 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
281 return 0;
284 /**************************************************************************
285 * WAVE_mciDefaultFmt [internal]
287 static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
289 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
290 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
292 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
293 wmw->lpWaveFormat->nChannels = 1;
294 wmw->lpWaveFormat->nSamplesPerSec = 44000;
295 wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
296 wmw->lpWaveFormat->nBlockAlign = 1;
297 wmw->lpWaveFormat->wBitsPerSample = 8;
299 return 0;
302 /**************************************************************************
303 * WAVE_mciCreateRIFFSkeleton [internal]
305 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
307 MMCKINFO ckWaveFormat;
308 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
309 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
311 lpckRIFF->ckid = FOURCC_RIFF;
312 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
313 lpckRIFF->cksize = 0;
315 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
316 goto err;
318 ckWaveFormat.fccType = 0;
319 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
320 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
322 if (!wmw->lpWaveFormat)
324 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
325 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
326 *wmw->lpWaveFormat = wmw->wfxRef;
329 /* we can only record PCM files... there is no way in the MCI API to specify
330 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
331 * structure
333 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
334 goto err;
336 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
337 goto err;
339 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
340 goto err;
342 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
343 goto err;
345 lpckWaveData->cksize = 0;
346 lpckWaveData->fccType = 0;
347 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
349 /* create data chunk */
350 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
351 goto err;
353 return 0;
355 err:
356 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
357 wmw->lpWaveFormat = NULL;
358 return MCIERR_INVALID_FILE;
361 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
363 WCHAR szTmpPath[MAX_PATH];
364 WCHAR szPrefix[4];
365 DWORD dwRet = MMSYSERR_NOERROR;
367 szPrefix[0] = 'M';
368 szPrefix[1] = 'C';
369 szPrefix[2] = 'I';
370 szPrefix[3] = '\0';
372 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
373 WARN("can't retrieve temp path!\n");
374 return MCIERR_FILE_NOT_FOUND;
377 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
378 HEAP_ZERO_MEMORY,
379 MAX_PATH * sizeof(WCHAR));
380 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
381 WARN("can't retrieve temp file name!\n");
382 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
383 return MCIERR_FILE_NOT_FOUND;
386 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
388 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
390 *hFile = mmioOpenW(*pszTmpFileName, NULL,
391 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
393 if (*hFile == 0) {
394 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
395 /* temporary file could not be created. clean filename. */
396 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
397 dwRet = MCIERR_FILE_NOT_FOUND;
400 return dwRet;
403 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
405 LRESULT dwRet = MMSYSERR_NOERROR;
406 WCHAR* fn;
408 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
409 if (!fn) return MCIERR_OUT_OF_MEMORY;
410 strcpyW(fn, filename);
411 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
412 wmw->openParms.lpstrElementName = fn;
414 if (strlenW(filename) > 0) {
415 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
416 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
418 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
419 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
421 if (wmw->hFile == 0) {
422 WARN("can't find file=%s!\n", debugstr_w(filename));
423 dwRet = MCIERR_FILE_NOT_FOUND;
425 else
427 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
429 /* make sure we're are the beginning of the file */
430 mmioSeek(wmw->hFile, 0, SEEK_SET);
432 /* first reading of this file. read the waveformat chunk */
433 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
434 dwRet = MCIERR_INVALID_FILE;
435 } else {
436 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
437 (LPSTR)&(lpckMainRIFF->ckid),
438 (LPSTR) &(lpckMainRIFF->fccType),
439 (lpckMainRIFF->cksize));
441 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
442 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
443 dwRet = MCIERR_INVALID_FILE;
444 } else {
445 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
450 return dwRet;
453 /**************************************************************************
454 * WAVE_mciOpen [internal]
456 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
458 DWORD dwRet = 0;
459 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
461 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
462 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
463 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
465 if (dwFlags & MCI_OPEN_SHAREABLE)
466 return MCIERR_HARDWARE;
468 if (wmw->nUseCount > 0) {
469 /* The driver is already opened on this channel
470 * Wave driver cannot be shared
472 return MCIERR_DEVICE_OPEN;
475 wmw->nUseCount++;
477 wmw->fInput = FALSE;
478 wmw->hWave = 0;
479 wmw->dwStatus = MCI_MODE_NOT_READY;
480 wmw->hFile = 0;
481 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
482 /* will be set by WAVE_mciOpenFile */
483 wmw->openParms.lpstrElementName = NULL;
485 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
487 if (dwFlags & MCI_OPEN_ELEMENT) {
488 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
489 /* could it be that (DWORD)lpOpenParms->lpstrElementName
490 * contains the hFile value ?
492 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
493 } else {
494 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
498 TRACE("hFile=%p\n", wmw->hFile);
500 if (dwRet == 0 && !wmw->lpWaveFormat)
501 dwRet = WAVE_mciDefaultFmt(wmw);
503 if (dwRet == 0) {
504 if (wmw->lpWaveFormat) {
505 switch (wmw->lpWaveFormat->wFormatTag) {
506 case WAVE_FORMAT_PCM:
507 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
508 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
509 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
510 wmw->lpWaveFormat->nAvgBytesPerSec,
511 wmw->lpWaveFormat->nSamplesPerSec *
512 wmw->lpWaveFormat->nBlockAlign);
513 wmw->lpWaveFormat->nAvgBytesPerSec =
514 wmw->lpWaveFormat->nSamplesPerSec *
515 wmw->lpWaveFormat->nBlockAlign;
517 break;
520 wmw->dwPosition = 0;
522 wmw->dwStatus = MCI_MODE_STOP;
523 } else {
524 wmw->nUseCount--;
525 if (wmw->hFile != 0)
526 mmioClose(wmw->hFile, 0);
527 wmw->hFile = 0;
529 return dwRet;
532 /**************************************************************************
533 * WAVE_mciCue [internal]
535 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
538 FIXME
540 This routine is far from complete. At the moment only a check is done on the
541 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
542 is the default.
544 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
545 are ignored
548 DWORD dwRet;
549 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
551 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
553 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
555 /* FIXME */
556 /* always close elements ? */
557 if (wmw->hFile != 0) {
558 mmioClose(wmw->hFile, 0);
559 wmw->hFile = 0;
562 dwRet = MMSYSERR_NOERROR; /* assume success */
564 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
565 dwRet = waveOutClose(wmw->hWave);
566 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
567 wmw->fInput = TRUE;
568 } else if (wmw->fInput) {
569 dwRet = waveInClose(wmw->hWave);
570 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
571 wmw->fInput = FALSE;
573 wmw->hWave = 0;
574 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
577 /**************************************************************************
578 * WAVE_mciStop [internal]
580 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
582 DWORD dwRet = 0;
583 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
585 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
587 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
589 /* wait for playback thread (if any) to exit before processing further */
590 switch (wmw->dwStatus) {
591 case MCI_MODE_PAUSE:
592 case MCI_MODE_PLAY:
593 case MCI_MODE_RECORD:
595 int oldStat = wmw->dwStatus;
596 wmw->dwStatus = MCI_MODE_NOT_READY;
597 if (oldStat == MCI_MODE_PAUSE)
598 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
600 while (wmw->dwStatus != MCI_MODE_STOP)
601 Sleep(10);
602 break;
605 wmw->dwPosition = 0;
607 /* sanity resets */
608 wmw->dwStatus = MCI_MODE_STOP;
610 if ((dwFlags & MCI_NOTIFY) && lpParms) {
611 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
612 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
615 return dwRet;
618 /**************************************************************************
619 * WAVE_mciClose [internal]
621 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
623 DWORD dwRet = 0;
624 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
626 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
628 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
630 if (wmw->dwStatus != MCI_MODE_STOP) {
631 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
634 wmw->nUseCount--;
636 if (wmw->nUseCount == 0) {
637 if (wmw->hFile != 0) {
638 mmioClose(wmw->hFile, 0);
639 wmw->hFile = 0;
643 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
644 wmw->lpWaveFormat = NULL;
645 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
646 wmw->openParms.lpstrElementName = NULL;
648 if ((dwFlags & MCI_NOTIFY) && lpParms) {
649 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
650 wmw->openParms.wDeviceID,
651 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
654 return 0;
657 /**************************************************************************
658 * WAVE_mciPlayCallback [internal]
660 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
661 DWORD_PTR dwInstance,
662 LPARAM dwParam1, LPARAM dwParam2)
664 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
666 switch (uMsg) {
667 case WOM_OPEN:
668 case WOM_CLOSE:
669 break;
670 case WOM_DONE:
671 InterlockedIncrement(&wmw->dwEventCount);
672 TRACE("Returning waveHdr=%lx\n", dwParam1);
673 SetEvent(wmw->hEvent);
674 break;
675 default:
676 ERR("Unknown uMsg=%d\n", uMsg);
680 /******************************************************************
681 * WAVE_mciPlayWaitDone
685 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
687 for (;;) {
688 ResetEvent(wmw->hEvent);
689 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
690 break;
692 InterlockedIncrement(&wmw->dwEventCount);
694 WaitForSingleObject(wmw->hEvent, INFINITE);
698 /**************************************************************************
699 * WAVE_mciPlay [internal]
701 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
703 DWORD end;
704 LONG bufsize, count, left;
705 DWORD dwRet = 0;
706 LPWAVEHDR waveHdr = NULL;
707 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
708 int whidx;
710 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
712 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
713 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
715 wmw->fInput = FALSE;
717 if (wmw->hFile == 0) {
718 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
719 return MCIERR_FILE_NOT_FOUND;
722 if (wmw->dwStatus == MCI_MODE_PAUSE) {
723 /* FIXME: parameters (start/end) in lpParams may not be used */
724 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
727 /** This function will be called again by a thread when async is used.
728 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
729 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
731 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
732 return MCIERR_INTERNAL;
735 wmw->dwStatus = MCI_MODE_PLAY;
737 if (!(dwFlags & MCI_WAIT)) {
738 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
739 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
742 end = 0xFFFFFFFF;
743 if (lpParms && (dwFlags & MCI_FROM)) {
744 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
746 if (lpParms && (dwFlags & MCI_TO)) {
747 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
750 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
752 if (end <= wmw->dwPosition)
753 return TRUE;
756 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
757 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
759 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
760 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
762 if (dwRet == 0) {
763 if (wmw->lpWaveFormat) {
764 switch (wmw->lpWaveFormat->wFormatTag) {
765 case WAVE_FORMAT_PCM:
766 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
767 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
768 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
769 wmw->lpWaveFormat->nAvgBytesPerSec,
770 wmw->lpWaveFormat->nSamplesPerSec *
771 wmw->lpWaveFormat->nBlockAlign);
772 wmw->lpWaveFormat->nAvgBytesPerSec =
773 wmw->lpWaveFormat->nSamplesPerSec *
774 wmw->lpWaveFormat->nBlockAlign;
776 break;
779 } else {
780 TRACE("can't retrieve wave format %d\n", dwRet);
781 goto cleanUp;
785 /* go back to beginning of chunk plus the requested position */
786 /* FIXME: I'm not sure this is correct, notably because some data linked to
787 * the decompression state machine will not be correctly initialized.
788 * try it this way (other way would be to decompress from 0 up to dwPosition
789 * and to start sending to hWave when dwPosition is reached)
791 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
793 /* By default the device will be opened for output, the MCI_CUE function is there to
794 * change from output to input and back
796 /* FIXME: how to choose between several output channels ? here mapper is forced */
797 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
798 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
800 if (dwRet != 0) {
801 TRACE("Can't open low level audio device %d\n", dwRet);
802 dwRet = MCIERR_DEVICE_OPEN;
803 wmw->hWave = 0;
804 goto cleanUp;
807 /* make it so that 3 buffers per second are needed */
808 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
810 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
811 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
812 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
813 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
814 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
815 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
816 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
817 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
818 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
819 dwRet = MCIERR_INTERNAL;
820 goto cleanUp;
823 whidx = 0;
824 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
825 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
826 wmw->dwEventCount = 1L; /* for first buffer */
828 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
830 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
831 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
832 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
833 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
834 if (count < 1)
835 break;
836 /* count is always <= bufsize, so this is correct regarding the
837 * waveOutPrepareHeader function
839 waveHdr[whidx].dwBufferLength = count;
840 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
841 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
842 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
843 waveHdr[whidx].dwBytesRecorded);
844 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
845 left -= count;
846 wmw->dwPosition += count;
847 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
849 WAVE_mciPlayWaitDone(wmw);
850 whidx ^= 1;
853 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
855 /* just to get rid of some race conditions between play, stop and pause */
856 waveOutReset(wmw->hWave);
858 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
859 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
861 dwRet = 0;
863 cleanUp:
864 HeapFree(GetProcessHeap(), 0, waveHdr);
866 if (wmw->hWave) {
867 waveOutClose(wmw->hWave);
868 wmw->hWave = 0;
870 CloseHandle(wmw->hEvent);
872 if (lpParms && (dwFlags & MCI_NOTIFY)) {
873 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
874 wmw->openParms.wDeviceID,
875 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
878 wmw->dwStatus = MCI_MODE_STOP;
880 return dwRet;
883 /**************************************************************************
884 * WAVE_mciPlayCallback [internal]
886 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
887 DWORD_PTR dwInstance,
888 LPARAM dwParam1, LPARAM dwParam2)
890 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
891 LPWAVEHDR lpWaveHdr;
892 LONG count = 0;
894 switch (uMsg) {
895 case WIM_OPEN:
896 case WIM_CLOSE:
897 break;
898 case WIM_DATA:
899 lpWaveHdr = (LPWAVEHDR) dwParam1;
901 InterlockedIncrement(&wmw->dwEventCount);
903 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
905 lpWaveHdr->dwFlags &= ~WHDR_DONE;
906 if (count > 0)
907 wmw->dwPosition += count;
908 /* else error reporting ?? */
909 if (wmw->dwStatus == MCI_MODE_RECORD)
911 /* Only queue up another buffer if we are recording. We could receive this
912 message also when waveInReset() is called, since it notifies on all wave
913 buffers that are outstanding. Queueing up more sometimes causes waveInClose
914 to fail. */
915 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
916 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
919 SetEvent(wmw->hEvent);
920 break;
921 default:
922 ERR("Unknown uMsg=%d\n", uMsg);
926 /******************************************************************
927 * bWAVE_mciRecordWaitDone
930 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
932 for (;;) {
933 ResetEvent(wmw->hEvent);
934 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
935 break;
937 InterlockedIncrement(&wmw->dwEventCount);
939 WaitForSingleObject(wmw->hEvent, INFINITE);
943 /**************************************************************************
944 * WAVE_mciRecord [internal]
946 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
948 DWORD end;
949 DWORD dwRet = MMSYSERR_NOERROR;
950 LONG bufsize;
951 LPWAVEHDR waveHdr = NULL;
952 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
954 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
956 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
957 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
959 /* FIXME : since there is no way to determine in which mode the device is
960 * open (recording/playback) automatically switch from a mode to another
962 wmw->fInput = TRUE;
964 if (wmw->dwStatus == MCI_MODE_PAUSE) {
965 /* FIXME: parameters (start/end) in lpParams may not be used */
966 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
969 /** This function will be called again by a thread when async is used.
970 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
971 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
973 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
974 return MCIERR_INTERNAL;
977 wmw->dwStatus = MCI_MODE_RECORD;
979 if (!(dwFlags & MCI_WAIT)) {
980 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
981 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
984 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
985 * we don't modify the wave part of an existing file (ie. we always erase an
986 * existing content, we don't overwrite)
988 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
989 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
990 if (dwRet != 0) return dwRet;
992 /* new RIFF file */
993 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
994 if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
996 end = 0xFFFFFFFF;
997 if (lpParms && (dwFlags & MCI_FROM)) {
998 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1001 if (lpParms && (dwFlags & MCI_TO)) {
1002 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1005 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1007 if (end <= wmw->dwPosition)
1009 return TRUE;
1012 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1013 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1015 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1016 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1018 /* Go back to the beginning of the chunk plus the requested position */
1019 /* FIXME: I'm not sure this is correct, notably because some data linked to
1020 * the decompression state machine will not be correctly initialized.
1021 * Try it this way (other way would be to decompress from 0 up to dwPosition
1022 * and to start sending to hWave when dwPosition is reached).
1024 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1026 /* By default the device will be opened for output, the MCI_CUE function is there to
1027 * change from output to input and back
1029 /* FIXME: how to choose between several output channels ? here mapper is forced */
1030 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1031 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1033 if (dwRet != MMSYSERR_NOERROR) {
1034 TRACE("Can't open low level audio device %d\n", dwRet);
1035 dwRet = MCIERR_DEVICE_OPEN;
1036 wmw->hWave = 0;
1037 goto cleanUp;
1040 /* make it so that 3 buffers per second are needed */
1041 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1043 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1044 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1045 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1046 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1047 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1048 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1049 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1051 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1052 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1053 dwRet = MCIERR_INTERNAL;
1054 goto cleanUp;
1057 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1058 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1059 dwRet = MCIERR_INTERNAL;
1060 goto cleanUp;
1063 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1064 wmw->dwEventCount = 1L; /* for first buffer */
1066 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1068 dwRet = waveInStart(wmw->hWave);
1070 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1071 WAVE_mciRecordWaitDone(wmw);
1074 /* needed so that the callback above won't add again the buffers returned by the reset */
1075 wmw->dwStatus = MCI_MODE_STOP;
1077 waveInReset(wmw->hWave);
1079 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1080 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1082 dwRet = 0;
1084 cleanUp:
1085 HeapFree(GetProcessHeap(), 0, waveHdr);
1087 if (wmw->hWave) {
1088 waveInClose(wmw->hWave);
1089 wmw->hWave = 0;
1091 CloseHandle(wmw->hEvent);
1093 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1094 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1095 wmw->openParms.wDeviceID,
1096 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1099 wmw->dwStatus = MCI_MODE_STOP;
1101 return dwRet;
1105 /**************************************************************************
1106 * WAVE_mciPause [internal]
1108 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1110 DWORD dwRet;
1111 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1113 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1115 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1116 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1118 if (wmw->dwStatus == MCI_MODE_PLAY) {
1119 wmw->dwStatus = MCI_MODE_PAUSE;
1122 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1123 else dwRet = waveOutPause(wmw->hWave);
1125 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1128 /**************************************************************************
1129 * WAVE_mciResume [internal]
1131 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1133 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1134 DWORD dwRet = 0;
1136 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1138 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1140 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1141 wmw->dwStatus = MCI_MODE_PLAY;
1144 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1145 else dwRet = waveOutRestart(wmw->hWave);
1146 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1149 /**************************************************************************
1150 * WAVE_mciSeek [internal]
1152 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1154 DWORD ret = 0;
1155 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1157 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1159 if (lpParms == NULL) {
1160 ret = MCIERR_NULL_PARAMETER_BLOCK;
1161 } else if (wmw == NULL) {
1162 ret = MCIERR_INVALID_DEVICE_ID;
1163 } else {
1164 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1166 if (dwFlags & MCI_SEEK_TO_START) {
1167 wmw->dwPosition = 0;
1168 } else if (dwFlags & MCI_SEEK_TO_END) {
1169 wmw->dwPosition = wmw->ckWaveData.cksize;
1170 } else if (dwFlags & MCI_TO) {
1171 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1172 } else {
1173 WARN("dwFlag doesn't tell where to seek to...\n");
1174 return MCIERR_MISSING_PARAMETER;
1177 TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1179 if (dwFlags & MCI_NOTIFY) {
1180 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1181 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1184 return ret;
1187 /**************************************************************************
1188 * WAVE_mciSet [internal]
1190 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1192 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1194 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1196 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1197 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1199 if (dwFlags & MCI_SET_TIME_FORMAT) {
1200 switch (lpParms->dwTimeFormat) {
1201 case MCI_FORMAT_MILLISECONDS:
1202 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1203 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1204 break;
1205 case MCI_FORMAT_BYTES:
1206 TRACE("MCI_FORMAT_BYTES !\n");
1207 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1208 break;
1209 case MCI_FORMAT_SAMPLES:
1210 TRACE("MCI_FORMAT_SAMPLES !\n");
1211 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1212 break;
1213 default:
1214 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1215 return MCIERR_BAD_TIME_FORMAT;
1218 if (dwFlags & MCI_SET_VIDEO) {
1219 TRACE("No support for video !\n");
1220 return MCIERR_UNSUPPORTED_FUNCTION;
1222 if (dwFlags & MCI_SET_DOOR_OPEN) {
1223 TRACE("No support for door open !\n");
1224 return MCIERR_UNSUPPORTED_FUNCTION;
1226 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1227 TRACE("No support for door close !\n");
1228 return MCIERR_UNSUPPORTED_FUNCTION;
1230 if (dwFlags & MCI_SET_AUDIO) {
1231 if (dwFlags & MCI_SET_ON) {
1232 TRACE("MCI_SET_ON audio !\n");
1233 } else if (dwFlags & MCI_SET_OFF) {
1234 TRACE("MCI_SET_OFF audio !\n");
1235 } else {
1236 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1237 return MCIERR_BAD_INTEGER;
1240 switch (lpParms->dwAudio)
1242 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1243 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1244 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1245 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1248 if (dwFlags & MCI_WAVE_INPUT)
1249 TRACE("MCI_WAVE_INPUT !\n");
1250 if (dwFlags & MCI_WAVE_OUTPUT)
1251 TRACE("MCI_WAVE_OUTPUT !\n");
1252 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1253 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1254 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1255 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1256 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1257 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1258 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1260 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1261 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1262 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1264 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1265 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1266 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1268 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1269 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1270 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1272 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1273 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1274 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1276 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1277 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1278 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1280 return 0;
1283 /**************************************************************************
1284 * WAVE_mciSave [internal]
1286 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1288 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1289 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1290 WPARAM wparam = MCI_NOTIFY_FAILURE;
1292 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1293 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1294 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1296 if (dwFlags & MCI_WAIT)
1298 FIXME("MCI_WAIT not implemented\n");
1300 WAVE_mciStop(wDevID, 0, NULL);
1302 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1303 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1305 ret = mmioClose(wmw->hFile, 0);
1306 wmw->hFile = 0;
1309 If the destination file already exists, it has to be overwritten. (Behaviour
1310 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1311 my applications. We are making use of mmioRename, which WILL NOT overwrite
1312 the destination file (which is what Windows does, also verified in Win2K)
1313 So, lets delete the destination file before calling mmioRename. If the
1314 destination file DOESN'T exist, the delete will fail silently. Let's also be
1315 careful not to lose our previous error code.
1317 tmpRet = GetLastError();
1318 DeleteFileW (lpParms->lpfilename);
1319 SetLastError(tmpRet);
1321 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1322 ret = MMSYSERR_NOERROR;
1325 if (dwFlags & MCI_NOTIFY) {
1326 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1328 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1329 wmw->openParms.wDeviceID, wparam);
1332 if (ret == MMSYSERR_NOERROR)
1333 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1335 return ret;
1338 /**************************************************************************
1339 * WAVE_mciStatus [internal]
1341 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1343 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1344 DWORD ret = 0;
1346 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1347 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1348 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1350 if (dwFlags & MCI_STATUS_ITEM) {
1351 switch (lpParms->dwItem) {
1352 case MCI_STATUS_CURRENT_TRACK:
1353 lpParms->dwReturn = 1;
1354 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1355 break;
1356 case MCI_STATUS_LENGTH:
1357 if (!wmw->hFile) {
1358 lpParms->dwReturn = 0;
1359 return MCIERR_UNSUPPORTED_FUNCTION;
1361 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1362 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1363 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1364 break;
1365 case MCI_STATUS_MODE:
1366 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1367 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1368 ret = MCI_RESOURCE_RETURNED;
1369 break;
1370 case MCI_STATUS_MEDIA_PRESENT:
1371 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1372 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1373 ret = MCI_RESOURCE_RETURNED;
1374 break;
1375 case MCI_STATUS_NUMBER_OF_TRACKS:
1376 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1377 lpParms->dwReturn = 1;
1378 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1379 break;
1380 case MCI_STATUS_POSITION:
1381 if (!wmw->hFile) {
1382 lpParms->dwReturn = 0;
1383 return MCIERR_UNSUPPORTED_FUNCTION;
1385 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1386 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1387 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1388 &ret);
1389 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1390 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1391 break;
1392 case MCI_STATUS_READY:
1393 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1394 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1395 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1396 ret = MCI_RESOURCE_RETURNED;
1397 break;
1398 case MCI_STATUS_TIME_FORMAT:
1399 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1400 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1401 ret = MCI_RESOURCE_RETURNED;
1402 break;
1403 case MCI_WAVE_INPUT:
1404 TRACE("MCI_WAVE_INPUT !\n");
1405 lpParms->dwReturn = 0;
1406 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1407 break;
1408 case MCI_WAVE_OUTPUT:
1409 TRACE("MCI_WAVE_OUTPUT !\n");
1411 UINT id;
1412 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1413 lpParms->dwReturn = id;
1414 } else {
1415 lpParms->dwReturn = 0;
1416 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1419 break;
1420 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1421 if (!wmw->hFile) {
1422 lpParms->dwReturn = 0;
1423 return MCIERR_UNSUPPORTED_FUNCTION;
1425 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1426 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1427 break;
1428 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1429 if (!wmw->hFile) {
1430 lpParms->dwReturn = 0;
1431 return MCIERR_UNSUPPORTED_FUNCTION;
1433 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1434 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1435 break;
1436 case MCI_WAVE_STATUS_BLOCKALIGN:
1437 if (!wmw->hFile) {
1438 lpParms->dwReturn = 0;
1439 return MCIERR_UNSUPPORTED_FUNCTION;
1441 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1442 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1443 break;
1444 case MCI_WAVE_STATUS_CHANNELS:
1445 if (!wmw->hFile) {
1446 lpParms->dwReturn = 0;
1447 return MCIERR_UNSUPPORTED_FUNCTION;
1449 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1450 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1451 break;
1452 case MCI_WAVE_STATUS_FORMATTAG:
1453 if (!wmw->hFile) {
1454 lpParms->dwReturn = 0;
1455 return MCIERR_UNSUPPORTED_FUNCTION;
1457 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1458 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1459 break;
1460 case MCI_WAVE_STATUS_LEVEL:
1461 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1462 lpParms->dwReturn = 0xAAAA5555;
1463 break;
1464 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1465 if (!wmw->hFile) {
1466 lpParms->dwReturn = 0;
1467 return MCIERR_UNSUPPORTED_FUNCTION;
1469 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1470 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1471 break;
1472 default:
1473 WARN("unknown command %08X !\n", lpParms->dwItem);
1474 return MCIERR_UNRECOGNIZED_COMMAND;
1477 if (dwFlags & MCI_NOTIFY) {
1478 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1479 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1481 return ret;
1484 /**************************************************************************
1485 * WAVE_mciGetDevCaps [internal]
1487 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1488 LPMCI_GETDEVCAPS_PARMS lpParms)
1490 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1491 DWORD ret = 0;
1493 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1495 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1496 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1498 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1499 switch(lpParms->dwItem) {
1500 case MCI_GETDEVCAPS_DEVICE_TYPE:
1501 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1502 ret = MCI_RESOURCE_RETURNED;
1503 break;
1504 case MCI_GETDEVCAPS_HAS_AUDIO:
1505 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1506 ret = MCI_RESOURCE_RETURNED;
1507 break;
1508 case MCI_GETDEVCAPS_HAS_VIDEO:
1509 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1510 ret = MCI_RESOURCE_RETURNED;
1511 break;
1512 case MCI_GETDEVCAPS_USES_FILES:
1513 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1514 ret = MCI_RESOURCE_RETURNED;
1515 break;
1516 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1517 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1518 ret = MCI_RESOURCE_RETURNED;
1519 break;
1520 case MCI_GETDEVCAPS_CAN_RECORD:
1521 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1522 ret = MCI_RESOURCE_RETURNED;
1523 break;
1524 case MCI_GETDEVCAPS_CAN_EJECT:
1525 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1526 ret = MCI_RESOURCE_RETURNED;
1527 break;
1528 case MCI_GETDEVCAPS_CAN_PLAY:
1529 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1530 ret = MCI_RESOURCE_RETURNED;
1531 break;
1532 case MCI_GETDEVCAPS_CAN_SAVE:
1533 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1534 ret = MCI_RESOURCE_RETURNED;
1535 break;
1536 case MCI_WAVE_GETDEVCAPS_INPUTS:
1537 lpParms->dwReturn = 1;
1538 break;
1539 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1540 lpParms->dwReturn = 1;
1541 break;
1542 default:
1543 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1544 return MCIERR_UNRECOGNIZED_COMMAND;
1546 } else {
1547 WARN("No GetDevCaps-Item !\n");
1548 return MCIERR_UNRECOGNIZED_COMMAND;
1550 return ret;
1553 /**************************************************************************
1554 * WAVE_mciInfo [internal]
1556 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1558 DWORD ret = 0;
1559 LPCWSTR str = 0;
1560 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1562 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1564 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1565 ret = MCIERR_NULL_PARAMETER_BLOCK;
1566 } else if (wmw == NULL) {
1567 ret = MCIERR_INVALID_DEVICE_ID;
1568 } else {
1569 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1570 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1571 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1573 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1575 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1576 case MCI_INFO_PRODUCT: str = wszAudio; break;
1577 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1578 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1579 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1580 default:
1581 WARN("Don't know this info command (%u)\n", dwFlags);
1582 ret = MCIERR_UNRECOGNIZED_COMMAND;
1585 if (str) {
1586 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1587 ret = MCIERR_PARAM_OVERFLOW;
1588 } else {
1589 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1591 } else {
1592 lpParms->lpstrReturn[0] = 0;
1595 return ret;
1598 /**************************************************************************
1599 * DriverProc (MCIWAVE.@)
1601 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1602 LPARAM dwParam1, LPARAM dwParam2)
1604 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1605 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1607 switch (wMsg) {
1608 case DRV_LOAD: return 1;
1609 case DRV_FREE: return 1;
1610 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1611 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1612 case DRV_ENABLE: return 1;
1613 case DRV_DISABLE: return 1;
1614 case DRV_QUERYCONFIGURE: return 1;
1615 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1616 case DRV_INSTALL: return DRVCNF_RESTART;
1617 case DRV_REMOVE: return DRVCNF_RESTART;
1620 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1622 switch (wMsg) {
1623 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1624 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1625 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1626 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1627 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1628 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1629 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1630 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1631 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1632 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1633 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1634 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1635 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1636 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1637 /* commands that should be supported */
1638 case MCI_LOAD:
1639 case MCI_FREEZE:
1640 case MCI_PUT:
1641 case MCI_REALIZE:
1642 case MCI_UNFREEZE:
1643 case MCI_UPDATE:
1644 case MCI_WHERE:
1645 case MCI_STEP:
1646 case MCI_SPIN:
1647 case MCI_ESCAPE:
1648 case MCI_COPY:
1649 case MCI_CUT:
1650 case MCI_DELETE:
1651 case MCI_PASTE:
1652 FIXME("Unsupported yet command [%u]\n", wMsg);
1653 break;
1654 case MCI_WINDOW:
1655 TRACE("Unsupported command [%u]\n", wMsg);
1656 break;
1657 /* option which can be silenced */
1658 case MCI_CONFIGURE:
1659 return 0;
1660 case MCI_OPEN:
1661 case MCI_CLOSE:
1662 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1663 break;
1664 default:
1665 FIXME("is probably wrong msg [%u]\n", wMsg);
1666 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1668 return MCIERR_UNRECOGNIZED_COMMAND;