mciwave: Pause/resume is not accepted from every state.
[wine/multimedia.git] / dlls / mciwave / mciwave.c
bloba0415bda1298f1aa4ab275df2b5957940eff99cf
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 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
67 struct SCA {
68 async_cmd cmd;
69 HANDLE evt;
70 UINT wDevID;
71 DWORD_PTR dwParam1;
72 DWORD_PTR dwParam2;
75 /**************************************************************************
76 * MCI_SCAStarter [internal]
78 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
80 struct SCA* sca = (struct SCA*)arg;
81 DWORD ret;
83 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
84 sca->wDevID, sca->dwParam1, sca->dwParam2);
85 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
86 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
87 sca->wDevID, sca->dwParam1, sca->dwParam2);
88 HeapFree(GetProcessHeap(), 0, sca);
89 ExitThread(ret);
90 WARN("Should not happen ? what's wrong\n");
91 /* should not go after this point */
92 return ret;
95 /**************************************************************************
96 * MCI_SendCommandAsync [internal]
98 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
99 DWORD_PTR dwParam2, UINT size)
101 HANDLE handles[2];
102 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
104 if (sca == 0)
105 return MCIERR_OUT_OF_MEMORY;
107 sca->wDevID = wDevID;
108 sca->cmd = cmd;
109 sca->dwParam1 = dwParam1;
111 if (size && dwParam2) {
112 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
113 /* copy structure passed by program in dwParam2 to be sure
114 * we can still use it whatever the program does
116 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
117 } else {
118 sca->dwParam2 = dwParam2;
121 if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
122 (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
123 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
124 if (handles[1]) CloseHandle(handles[1]);
125 sca->evt = NULL;
126 return MCI_SCAStarter(&sca);
129 SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
130 /* wait until either:
131 * - the thread has finished (handles[0], likely an error)
132 * - init phase of async command is done (handles[1])
134 WaitForMultipleObjects(2, handles, FALSE, INFINITE);
135 CloseHandle(handles[0]);
136 CloseHandle(handles[1]);
137 return 0;
140 /*======================================================================*
141 * MCI WAVE implementation *
142 *======================================================================*/
144 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
146 /**************************************************************************
147 * MCIWAVE_drvOpen [internal]
149 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
151 WINE_MCIWAVE* wmw;
153 if (modp == NULL) return 0xFFFFFFFF;
155 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
157 if (!wmw)
158 return 0;
160 wmw->wDevID = modp->wDeviceID;
161 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
162 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
163 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
165 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
166 wmw->wfxRef.nChannels = 1; /* MONO */
167 wmw->wfxRef.nSamplesPerSec = 11025;
168 wmw->wfxRef.nAvgBytesPerSec = 11025;
169 wmw->wfxRef.nBlockAlign = 1;
170 wmw->wfxRef.wBitsPerSample = 8;
171 wmw->wfxRef.cbSize = 0; /* don't care */
173 return modp->wDeviceID;
176 /**************************************************************************
177 * MCIWAVE_drvClose [internal]
179 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
181 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
183 if (wmw) {
184 HeapFree(GetProcessHeap(), 0, wmw);
185 mciSetDriverData(dwDevID, 0);
186 return 1;
188 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
191 /**************************************************************************
192 * WAVE_mciGetOpenDev [internal]
194 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
196 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
198 if (wmw == NULL || wmw->nUseCount == 0) {
199 WARN("Invalid wDevID=%u\n", wDevID);
200 return 0;
202 return wmw;
205 /**************************************************************************
206 * WAVE_ConvertByteToTimeFormat [internal]
208 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
210 DWORD ret = 0;
212 switch (wmw->dwMciTimeFormat) {
213 case MCI_FORMAT_MILLISECONDS:
214 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
215 break;
216 case MCI_FORMAT_BYTES:
217 ret = val;
218 break;
219 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
220 ret = (val * 8) / (wmw->lpWaveFormat->wBitsPerSample ? wmw->lpWaveFormat->wBitsPerSample : 1);
221 break;
222 default:
223 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
225 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
226 *lpRet = 0;
227 return ret;
230 /**************************************************************************
231 * WAVE_ConvertTimeFormatToByte [internal]
233 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
235 DWORD ret = 0;
237 switch (wmw->dwMciTimeFormat) {
238 case MCI_FORMAT_MILLISECONDS:
239 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
240 break;
241 case MCI_FORMAT_BYTES:
242 ret = val;
243 break;
244 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
245 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
246 break;
247 default:
248 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
250 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
251 return ret;
254 /**************************************************************************
255 * WAVE_mciReadFmt [internal]
257 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
259 MMCKINFO mmckInfo;
260 long r;
262 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
263 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
264 return MCIERR_INVALID_FILE;
265 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
266 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
268 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
269 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
270 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
271 if (r < sizeof(PCMWAVEFORMAT))
272 return MCIERR_INVALID_FILE;
274 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
275 TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
276 TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec);
277 TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
278 TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
279 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
280 if (r >= (long)sizeof(WAVEFORMATEX))
281 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
283 mmioAscend(wmw->hFile, &mmckInfo, 0);
284 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
285 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
286 TRACE("can't find data chunk\n");
287 return MCIERR_INVALID_FILE;
289 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
290 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
291 TRACE("nChannels=%d nSamplesPerSec=%d\n",
292 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
294 return 0;
297 /**************************************************************************
298 * WAVE_mciDefaultFmt [internal]
300 static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
302 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
303 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
305 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
306 wmw->lpWaveFormat->nChannels = 1;
307 wmw->lpWaveFormat->nSamplesPerSec = 44000;
308 wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
309 wmw->lpWaveFormat->nBlockAlign = 1;
310 wmw->lpWaveFormat->wBitsPerSample = 8;
312 return 0;
315 /**************************************************************************
316 * WAVE_mciCreateRIFFSkeleton [internal]
318 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
320 MMCKINFO ckWaveFormat;
321 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
322 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
324 lpckRIFF->ckid = FOURCC_RIFF;
325 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
326 lpckRIFF->cksize = 0;
328 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
329 goto err;
331 ckWaveFormat.fccType = 0;
332 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
333 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
335 if (!wmw->lpWaveFormat)
337 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
338 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
339 *wmw->lpWaveFormat = wmw->wfxRef;
342 /* we can only record PCM files... there is no way in the MCI API to specify
343 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
344 * structure
346 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
347 goto err;
349 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
350 goto err;
352 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
353 goto err;
355 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
356 goto err;
358 lpckWaveData->cksize = 0;
359 lpckWaveData->fccType = 0;
360 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
362 /* create data chunk */
363 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
364 goto err;
366 return 0;
368 err:
369 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
370 wmw->lpWaveFormat = NULL;
371 return MCIERR_INVALID_FILE;
374 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
376 WCHAR szTmpPath[MAX_PATH];
377 WCHAR szPrefix[4];
378 DWORD dwRet = MMSYSERR_NOERROR;
380 szPrefix[0] = 'M';
381 szPrefix[1] = 'C';
382 szPrefix[2] = 'I';
383 szPrefix[3] = '\0';
385 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
386 WARN("can't retrieve temp path!\n");
387 return MCIERR_FILE_NOT_FOUND;
390 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
391 HEAP_ZERO_MEMORY,
392 MAX_PATH * sizeof(WCHAR));
393 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
394 WARN("can't retrieve temp file name!\n");
395 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
396 return MCIERR_FILE_NOT_FOUND;
399 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
401 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
403 *hFile = mmioOpenW(*pszTmpFileName, NULL,
404 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
406 if (*hFile == 0) {
407 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
408 /* temporary file could not be created. clean filename. */
409 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
410 dwRet = MCIERR_FILE_NOT_FOUND;
413 return dwRet;
416 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
418 LRESULT dwRet = MMSYSERR_NOERROR;
419 WCHAR* fn;
421 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
422 if (!fn) return MCIERR_OUT_OF_MEMORY;
423 strcpyW(fn, filename);
424 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
425 wmw->openParms.lpstrElementName = fn;
427 if (strlenW(filename) > 0) {
428 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
429 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
431 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
432 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
434 if (wmw->hFile == 0) {
435 WARN("can't find file=%s!\n", debugstr_w(filename));
436 dwRet = MCIERR_FILE_NOT_FOUND;
438 else
440 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
442 /* make sure we're are the beginning of the file */
443 mmioSeek(wmw->hFile, 0, SEEK_SET);
445 /* first reading of this file. read the waveformat chunk */
446 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
447 dwRet = MCIERR_INVALID_FILE;
448 } else {
449 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
450 (LPSTR)&(lpckMainRIFF->ckid),
451 (LPSTR) &(lpckMainRIFF->fccType),
452 (lpckMainRIFF->cksize));
454 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
455 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
456 dwRet = MCIERR_INVALID_FILE;
457 } else {
458 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
463 return dwRet;
466 /**************************************************************************
467 * WAVE_mciOpen [internal]
469 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
471 DWORD dwRet = 0;
472 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
474 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
475 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
476 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
478 if (dwFlags & MCI_OPEN_SHAREABLE)
479 return MCIERR_HARDWARE;
481 if (wmw->nUseCount > 0) {
482 /* The driver is already opened on this channel
483 * Wave driver cannot be shared
485 return MCIERR_DEVICE_OPEN;
488 wmw->nUseCount++;
490 wmw->fInput = FALSE;
491 wmw->hWave = 0;
492 wmw->dwStatus = MCI_MODE_NOT_READY;
493 wmw->hFile = 0;
494 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
495 /* will be set by WAVE_mciOpenFile */
496 wmw->openParms.lpstrElementName = NULL;
498 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
500 if (dwFlags & MCI_OPEN_ELEMENT) {
501 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
502 /* could it be that (DWORD)lpOpenParms->lpstrElementName
503 * contains the hFile value ?
505 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
506 } else {
507 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
511 TRACE("hFile=%p\n", wmw->hFile);
513 if (dwRet == 0 && !wmw->lpWaveFormat)
514 dwRet = WAVE_mciDefaultFmt(wmw);
516 if (dwRet == 0) {
517 if (wmw->lpWaveFormat) {
518 switch (wmw->lpWaveFormat->wFormatTag) {
519 case WAVE_FORMAT_PCM:
520 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
521 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
522 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
523 wmw->lpWaveFormat->nAvgBytesPerSec,
524 wmw->lpWaveFormat->nSamplesPerSec *
525 wmw->lpWaveFormat->nBlockAlign);
526 wmw->lpWaveFormat->nAvgBytesPerSec =
527 wmw->lpWaveFormat->nSamplesPerSec *
528 wmw->lpWaveFormat->nBlockAlign;
530 break;
533 wmw->dwPosition = 0;
535 wmw->dwStatus = MCI_MODE_STOP;
536 } else {
537 wmw->nUseCount--;
538 if (wmw->hFile != 0)
539 mmioClose(wmw->hFile, 0);
540 wmw->hFile = 0;
542 return dwRet;
545 /**************************************************************************
546 * WAVE_mciCue [internal]
548 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
551 FIXME
553 This routine is far from complete. At the moment only a check is done on the
554 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
555 is the default.
557 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
558 are ignored
561 DWORD dwRet;
562 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
564 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
566 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
568 /* FIXME */
569 /* always close elements ? */
570 if (wmw->hFile != 0) {
571 mmioClose(wmw->hFile, 0);
572 wmw->hFile = 0;
575 dwRet = MMSYSERR_NOERROR; /* assume success */
577 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
578 dwRet = waveOutClose(wmw->hWave);
579 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
580 wmw->fInput = TRUE;
581 } else if (wmw->fInput) {
582 dwRet = waveInClose(wmw->hWave);
583 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
584 wmw->fInput = FALSE;
586 wmw->hWave = 0;
587 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
590 /**************************************************************************
591 * WAVE_mciStop [internal]
593 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
595 DWORD dwRet = 0;
596 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
598 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
600 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
602 /* wait for playback thread (if any) to exit before processing further */
603 switch (wmw->dwStatus) {
604 case MCI_MODE_PAUSE:
605 case MCI_MODE_PLAY:
606 case MCI_MODE_RECORD:
608 int oldStat = wmw->dwStatus;
609 wmw->dwStatus = MCI_MODE_NOT_READY;
610 if (oldStat == MCI_MODE_PAUSE)
611 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
613 while (wmw->dwStatus != MCI_MODE_STOP)
614 Sleep(10);
615 break;
618 wmw->dwPosition = 0;
620 /* sanity resets */
621 wmw->dwStatus = MCI_MODE_STOP;
623 if ((dwFlags & MCI_NOTIFY) && lpParms) {
624 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
625 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
628 return dwRet;
631 /**************************************************************************
632 * WAVE_mciClose [internal]
634 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
636 DWORD dwRet = 0;
637 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
639 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
641 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
643 if (wmw->dwStatus != MCI_MODE_STOP) {
644 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
647 wmw->nUseCount--;
649 if (wmw->nUseCount == 0) {
650 if (wmw->hFile != 0) {
651 mmioClose(wmw->hFile, 0);
652 wmw->hFile = 0;
656 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
657 wmw->lpWaveFormat = NULL;
658 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
659 wmw->openParms.lpstrElementName = NULL;
661 if ((dwFlags & MCI_NOTIFY) && lpParms) {
662 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
663 wmw->openParms.wDeviceID,
664 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
667 return 0;
670 /**************************************************************************
671 * WAVE_mciPlayCallback [internal]
673 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
674 DWORD_PTR dwInstance,
675 LPARAM dwParam1, LPARAM dwParam2)
677 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
679 switch (uMsg) {
680 case WOM_OPEN:
681 case WOM_CLOSE:
682 break;
683 case WOM_DONE:
684 InterlockedIncrement(&wmw->dwEventCount);
685 TRACE("Returning waveHdr=%lx\n", dwParam1);
686 SetEvent(wmw->hEvent);
687 break;
688 default:
689 ERR("Unknown uMsg=%d\n", uMsg);
693 /******************************************************************
694 * WAVE_mciPlayWaitDone
698 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
700 for (;;) {
701 ResetEvent(wmw->hEvent);
702 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
703 break;
705 InterlockedIncrement(&wmw->dwEventCount);
707 WaitForSingleObject(wmw->hEvent, INFINITE);
711 /**************************************************************************
712 * WAVE_mciPlay [internal]
714 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
716 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
717 DWORD end;
718 LONG bufsize, count, left;
719 DWORD dwRet = 0;
720 LPWAVEHDR waveHdr = NULL;
721 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
722 int whidx;
724 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
726 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
727 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
729 wmw->fInput = FALSE;
731 if (wmw->hFile == 0) {
732 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
733 return MCIERR_FILE_NOT_FOUND;
736 if (wmw->dwStatus == MCI_MODE_PAUSE) {
737 /* FIXME: parameters (start/end) in lpParams may not be used */
738 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
741 /** This function will be called again by a thread when async is used.
742 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
743 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
745 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
746 return MCIERR_INTERNAL;
749 wmw->dwStatus = MCI_MODE_PLAY;
751 if (!(dwFlags & MCI_WAIT)) {
752 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciPlay, dwFlags,
753 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
756 end = 0xFFFFFFFF;
757 if (lpParms && (dwFlags & MCI_FROM)) {
758 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
760 if (lpParms && (dwFlags & MCI_TO)) {
761 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
764 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
766 if (end <= wmw->dwPosition)
767 return TRUE;
770 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
771 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
773 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
774 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
776 if (dwRet == 0) {
777 if (wmw->lpWaveFormat) {
778 switch (wmw->lpWaveFormat->wFormatTag) {
779 case WAVE_FORMAT_PCM:
780 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
781 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
782 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
783 wmw->lpWaveFormat->nAvgBytesPerSec,
784 wmw->lpWaveFormat->nSamplesPerSec *
785 wmw->lpWaveFormat->nBlockAlign);
786 wmw->lpWaveFormat->nAvgBytesPerSec =
787 wmw->lpWaveFormat->nSamplesPerSec *
788 wmw->lpWaveFormat->nBlockAlign;
790 break;
793 } else {
794 TRACE("can't retrieve wave format %d\n", dwRet);
795 goto cleanUp;
799 /* go back to beginning of chunk plus the requested position */
800 /* FIXME: I'm not sure this is correct, notably because some data linked to
801 * the decompression state machine will not be correctly initialized.
802 * try it this way (other way would be to decompress from 0 up to dwPosition
803 * and to start sending to hWave when dwPosition is reached)
805 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
807 /* By default the device will be opened for output, the MCI_CUE function is there to
808 * change from output to input and back
810 /* FIXME: how to choose between several output channels ? here mapper is forced */
811 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
812 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
814 if (dwRet != 0) {
815 TRACE("Can't open low level audio device %d\n", dwRet);
816 dwRet = MCIERR_DEVICE_OPEN;
817 wmw->hWave = 0;
818 goto cleanUp;
821 /* make it so that 3 buffers per second are needed */
822 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
824 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
825 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
826 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
827 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
828 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
829 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
830 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
831 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
832 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
833 dwRet = MCIERR_INTERNAL;
834 goto cleanUp;
837 whidx = 0;
838 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
839 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
840 wmw->dwEventCount = 1L; /* for first buffer */
842 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
843 if (hEvent) SetEvent(hEvent);
845 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
846 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
847 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
848 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
849 if (count < 1)
850 break;
851 /* count is always <= bufsize, so this is correct regarding the
852 * waveOutPrepareHeader function
854 waveHdr[whidx].dwBufferLength = count;
855 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
856 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
857 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
858 waveHdr[whidx].dwBytesRecorded);
859 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
860 left -= count;
861 wmw->dwPosition += count;
862 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
864 WAVE_mciPlayWaitDone(wmw);
865 whidx ^= 1;
868 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
870 /* just to get rid of some race conditions between play, stop and pause */
871 waveOutReset(wmw->hWave);
873 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
874 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
876 dwRet = 0;
878 cleanUp:
879 HeapFree(GetProcessHeap(), 0, waveHdr);
881 if (wmw->hWave) {
882 waveOutClose(wmw->hWave);
883 wmw->hWave = 0;
885 CloseHandle(wmw->hEvent);
887 if (lpParms && (dwFlags & MCI_NOTIFY)) {
888 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
889 wmw->openParms.wDeviceID,
890 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
893 wmw->dwStatus = MCI_MODE_STOP;
895 return dwRet;
898 /**************************************************************************
899 * WAVE_mciPlayCallback [internal]
901 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
902 DWORD_PTR dwInstance,
903 LPARAM dwParam1, LPARAM dwParam2)
905 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
906 LPWAVEHDR lpWaveHdr;
907 LONG count = 0;
909 switch (uMsg) {
910 case WIM_OPEN:
911 case WIM_CLOSE:
912 break;
913 case WIM_DATA:
914 lpWaveHdr = (LPWAVEHDR) dwParam1;
916 InterlockedIncrement(&wmw->dwEventCount);
918 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
920 lpWaveHdr->dwFlags &= ~WHDR_DONE;
921 if (count > 0)
922 wmw->dwPosition += count;
923 /* else error reporting ?? */
924 if (wmw->dwStatus == MCI_MODE_RECORD)
926 /* Only queue up another buffer if we are recording. We could receive this
927 message also when waveInReset() is called, since it notifies on all wave
928 buffers that are outstanding. Queueing up more sometimes causes waveInClose
929 to fail. */
930 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
931 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
934 SetEvent(wmw->hEvent);
935 break;
936 default:
937 ERR("Unknown uMsg=%d\n", uMsg);
941 /******************************************************************
942 * bWAVE_mciRecordWaitDone
945 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
947 for (;;) {
948 ResetEvent(wmw->hEvent);
949 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
950 break;
952 InterlockedIncrement(&wmw->dwEventCount);
954 WaitForSingleObject(wmw->hEvent, INFINITE);
958 /**************************************************************************
959 * WAVE_mciRecord [internal]
961 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
963 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
964 DWORD end;
965 DWORD dwRet = MMSYSERR_NOERROR;
966 LONG bufsize;
967 LPWAVEHDR waveHdr = NULL;
968 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
970 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
972 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
973 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
975 /* FIXME : since there is no way to determine in which mode the device is
976 * open (recording/playback) automatically switch from a mode to another
978 wmw->fInput = TRUE;
980 if (wmw->dwStatus == MCI_MODE_PAUSE) {
981 /* FIXME: parameters (start/end) in lpParams may not be used */
982 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
985 /** This function will be called again by a thread when async is used.
986 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
987 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
989 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
990 return MCIERR_INTERNAL;
993 wmw->dwStatus = MCI_MODE_RECORD;
995 if (!(dwFlags & MCI_WAIT)) {
996 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciRecord, dwFlags,
997 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1000 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1001 * we don't modify the wave part of an existing file (ie. we always erase an
1002 * existing content, we don't overwrite)
1004 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
1005 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
1006 if (dwRet != 0) return dwRet;
1008 /* new RIFF file */
1009 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1010 if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
1012 end = 0xFFFFFFFF;
1013 if (lpParms && (dwFlags & MCI_FROM)) {
1014 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1017 if (lpParms && (dwFlags & MCI_TO)) {
1018 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1021 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1023 if (end <= wmw->dwPosition)
1025 return TRUE;
1028 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1029 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1031 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1032 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1034 /* Go back to the beginning of the chunk plus the requested position */
1035 /* FIXME: I'm not sure this is correct, notably because some data linked to
1036 * the decompression state machine will not be correctly initialized.
1037 * Try it this way (other way would be to decompress from 0 up to dwPosition
1038 * and to start sending to hWave when dwPosition is reached).
1040 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1042 /* By default the device will be opened for output, the MCI_CUE function is there to
1043 * change from output to input and back
1045 /* FIXME: how to choose between several output channels ? here mapper is forced */
1046 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1047 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1049 if (dwRet != MMSYSERR_NOERROR) {
1050 TRACE("Can't open low level audio device %d\n", dwRet);
1051 dwRet = MCIERR_DEVICE_OPEN;
1052 wmw->hWave = 0;
1053 goto cleanUp;
1056 /* make it so that 3 buffers per second are needed */
1057 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1059 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1060 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1061 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1062 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1063 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1064 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1065 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1067 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1068 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1069 dwRet = MCIERR_INTERNAL;
1070 goto cleanUp;
1073 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1074 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1075 dwRet = MCIERR_INTERNAL;
1076 goto cleanUp;
1079 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1080 wmw->dwEventCount = 1L; /* for first buffer */
1082 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1084 dwRet = waveInStart(wmw->hWave);
1086 if (hEvent) SetEvent(hEvent);
1088 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1089 WAVE_mciRecordWaitDone(wmw);
1092 /* needed so that the callback above won't add again the buffers returned by the reset */
1093 wmw->dwStatus = MCI_MODE_STOP;
1095 waveInReset(wmw->hWave);
1097 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1098 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1100 dwRet = 0;
1102 cleanUp:
1103 HeapFree(GetProcessHeap(), 0, waveHdr);
1105 if (wmw->hWave) {
1106 waveInClose(wmw->hWave);
1107 wmw->hWave = 0;
1109 CloseHandle(wmw->hEvent);
1111 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1112 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1113 wmw->openParms.wDeviceID,
1114 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1117 wmw->dwStatus = MCI_MODE_STOP;
1119 return dwRet;
1123 /**************************************************************************
1124 * WAVE_mciPause [internal]
1126 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1128 DWORD dwRet;
1129 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1131 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1133 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1135 switch (wmw->dwStatus) {
1136 case MCI_MODE_PLAY:
1137 wmw->dwStatus = MCI_MODE_PAUSE;
1138 dwRet = waveOutPause(wmw->hWave);
1139 break;
1140 case MCI_MODE_RECORD:
1141 wmw->dwStatus = MCI_MODE_PAUSE;
1142 dwRet = waveInStop(wmw->hWave);
1143 break;
1144 case MCI_MODE_PAUSE:
1145 dwRet = MMSYSERR_NOERROR;
1146 break;
1147 default:
1148 return MCIERR_NONAPPLICABLE_FUNCTION;
1150 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1153 /**************************************************************************
1154 * WAVE_mciResume [internal]
1156 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1158 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1159 DWORD dwRet;
1161 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1163 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1165 switch (wmw->dwStatus) {
1166 case MCI_MODE_PAUSE:
1167 if (wmw->fInput) {
1168 wmw->dwStatus = MCI_MODE_RECORD;
1169 dwRet = waveInStart(wmw->hWave);
1170 } else {
1171 wmw->dwStatus = MCI_MODE_PLAY;
1172 dwRet = waveOutRestart(wmw->hWave);
1174 break;
1175 case MCI_MODE_PLAY:
1176 case MCI_MODE_RECORD:
1177 dwRet = MMSYSERR_NOERROR;
1178 break;
1179 default:
1180 return MCIERR_NONAPPLICABLE_FUNCTION;
1182 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1185 /**************************************************************************
1186 * WAVE_mciSeek [internal]
1188 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1190 DWORD ret = 0;
1191 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1193 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1195 if (lpParms == NULL) {
1196 ret = MCIERR_NULL_PARAMETER_BLOCK;
1197 } else if (wmw == NULL) {
1198 ret = MCIERR_INVALID_DEVICE_ID;
1199 } else {
1200 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1202 if (dwFlags & MCI_SEEK_TO_START) {
1203 wmw->dwPosition = 0;
1204 } else if (dwFlags & MCI_SEEK_TO_END) {
1205 wmw->dwPosition = wmw->ckWaveData.cksize;
1206 } else if (dwFlags & MCI_TO) {
1207 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1208 } else {
1209 WARN("dwFlag doesn't tell where to seek to...\n");
1210 return MCIERR_MISSING_PARAMETER;
1213 TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1215 if (dwFlags & MCI_NOTIFY) {
1216 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1217 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1220 return ret;
1223 /**************************************************************************
1224 * WAVE_mciSet [internal]
1226 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1228 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1230 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1232 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1233 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1235 if (dwFlags & MCI_SET_TIME_FORMAT) {
1236 switch (lpParms->dwTimeFormat) {
1237 case MCI_FORMAT_MILLISECONDS:
1238 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1239 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1240 break;
1241 case MCI_FORMAT_BYTES:
1242 TRACE("MCI_FORMAT_BYTES !\n");
1243 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1244 break;
1245 case MCI_FORMAT_SAMPLES:
1246 TRACE("MCI_FORMAT_SAMPLES !\n");
1247 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1248 break;
1249 default:
1250 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1251 return MCIERR_BAD_TIME_FORMAT;
1254 if (dwFlags & MCI_SET_VIDEO) {
1255 TRACE("No support for video !\n");
1256 return MCIERR_UNSUPPORTED_FUNCTION;
1258 if (dwFlags & MCI_SET_DOOR_OPEN) {
1259 TRACE("No support for door open !\n");
1260 return MCIERR_UNSUPPORTED_FUNCTION;
1262 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1263 TRACE("No support for door close !\n");
1264 return MCIERR_UNSUPPORTED_FUNCTION;
1266 if (dwFlags & MCI_SET_AUDIO) {
1267 if (dwFlags & MCI_SET_ON) {
1268 TRACE("MCI_SET_ON audio !\n");
1269 } else if (dwFlags & MCI_SET_OFF) {
1270 TRACE("MCI_SET_OFF audio !\n");
1271 } else {
1272 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1273 return MCIERR_BAD_INTEGER;
1276 switch (lpParms->dwAudio)
1278 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1279 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1280 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1281 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1284 if (dwFlags & MCI_WAVE_INPUT)
1285 TRACE("MCI_WAVE_INPUT !\n");
1286 if (dwFlags & MCI_WAVE_OUTPUT)
1287 TRACE("MCI_WAVE_OUTPUT !\n");
1288 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1289 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1290 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1291 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1292 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1293 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1294 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1296 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1297 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1298 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1300 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1301 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1302 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1304 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1305 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1306 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1308 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1309 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1310 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1312 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1313 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1314 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1316 return 0;
1319 /**************************************************************************
1320 * WAVE_mciSave [internal]
1322 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1324 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1325 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1326 WPARAM wparam = MCI_NOTIFY_FAILURE;
1328 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1329 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1330 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1332 if (dwFlags & MCI_WAIT)
1334 FIXME("MCI_WAIT not implemented\n");
1336 WAVE_mciStop(wDevID, 0, NULL);
1338 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1339 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1341 ret = mmioClose(wmw->hFile, 0);
1342 wmw->hFile = 0;
1345 If the destination file already exists, it has to be overwritten. (Behaviour
1346 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1347 my applications. We are making use of mmioRename, which WILL NOT overwrite
1348 the destination file (which is what Windows does, also verified in Win2K)
1349 So, lets delete the destination file before calling mmioRename. If the
1350 destination file DOESN'T exist, the delete will fail silently. Let's also be
1351 careful not to lose our previous error code.
1353 tmpRet = GetLastError();
1354 DeleteFileW (lpParms->lpfilename);
1355 SetLastError(tmpRet);
1357 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1358 ret = MMSYSERR_NOERROR;
1361 if (dwFlags & MCI_NOTIFY) {
1362 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1364 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1365 wmw->openParms.wDeviceID, wparam);
1368 if (ret == MMSYSERR_NOERROR)
1369 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1371 return ret;
1374 /**************************************************************************
1375 * WAVE_mciStatus [internal]
1377 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1379 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1380 DWORD ret = 0;
1382 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1383 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1384 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1386 if (dwFlags & MCI_STATUS_ITEM) {
1387 switch (lpParms->dwItem) {
1388 case MCI_STATUS_CURRENT_TRACK:
1389 lpParms->dwReturn = 1;
1390 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1391 break;
1392 case MCI_STATUS_LENGTH:
1393 if (!wmw->hFile) {
1394 lpParms->dwReturn = 0;
1395 return MCIERR_UNSUPPORTED_FUNCTION;
1397 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1398 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1399 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1400 break;
1401 case MCI_STATUS_MODE:
1402 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1403 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1404 ret = MCI_RESOURCE_RETURNED;
1405 break;
1406 case MCI_STATUS_MEDIA_PRESENT:
1407 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1408 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1409 ret = MCI_RESOURCE_RETURNED;
1410 break;
1411 case MCI_STATUS_NUMBER_OF_TRACKS:
1412 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1413 lpParms->dwReturn = 1;
1414 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1415 break;
1416 case MCI_STATUS_POSITION:
1417 if (!wmw->hFile) {
1418 lpParms->dwReturn = 0;
1419 return MCIERR_UNSUPPORTED_FUNCTION;
1421 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1422 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1423 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1424 &ret);
1425 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1426 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1427 break;
1428 case MCI_STATUS_READY:
1429 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1430 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1431 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1432 ret = MCI_RESOURCE_RETURNED;
1433 break;
1434 case MCI_STATUS_TIME_FORMAT:
1435 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1436 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1437 ret = MCI_RESOURCE_RETURNED;
1438 break;
1439 case MCI_WAVE_INPUT:
1440 TRACE("MCI_WAVE_INPUT !\n");
1441 lpParms->dwReturn = 0;
1442 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1443 break;
1444 case MCI_WAVE_OUTPUT:
1445 TRACE("MCI_WAVE_OUTPUT !\n");
1447 UINT id;
1448 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1449 lpParms->dwReturn = id;
1450 } else {
1451 lpParms->dwReturn = 0;
1452 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1455 break;
1456 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1457 if (!wmw->hFile) {
1458 lpParms->dwReturn = 0;
1459 return MCIERR_UNSUPPORTED_FUNCTION;
1461 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1462 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1463 break;
1464 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1465 if (!wmw->hFile) {
1466 lpParms->dwReturn = 0;
1467 return MCIERR_UNSUPPORTED_FUNCTION;
1469 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1470 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1471 break;
1472 case MCI_WAVE_STATUS_BLOCKALIGN:
1473 if (!wmw->hFile) {
1474 lpParms->dwReturn = 0;
1475 return MCIERR_UNSUPPORTED_FUNCTION;
1477 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1478 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1479 break;
1480 case MCI_WAVE_STATUS_CHANNELS:
1481 if (!wmw->hFile) {
1482 lpParms->dwReturn = 0;
1483 return MCIERR_UNSUPPORTED_FUNCTION;
1485 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1486 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1487 break;
1488 case MCI_WAVE_STATUS_FORMATTAG:
1489 if (!wmw->hFile) {
1490 lpParms->dwReturn = 0;
1491 return MCIERR_UNSUPPORTED_FUNCTION;
1493 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1494 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1495 break;
1496 case MCI_WAVE_STATUS_LEVEL:
1497 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1498 lpParms->dwReturn = 0xAAAA5555;
1499 break;
1500 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1501 if (!wmw->hFile) {
1502 lpParms->dwReturn = 0;
1503 return MCIERR_UNSUPPORTED_FUNCTION;
1505 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1506 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1507 break;
1508 default:
1509 WARN("unknown command %08X !\n", lpParms->dwItem);
1510 return MCIERR_UNRECOGNIZED_COMMAND;
1513 if (dwFlags & MCI_NOTIFY) {
1514 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1515 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1517 return ret;
1520 /**************************************************************************
1521 * WAVE_mciGetDevCaps [internal]
1523 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1524 LPMCI_GETDEVCAPS_PARMS lpParms)
1526 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1527 DWORD ret = 0;
1529 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1531 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1532 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1534 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1535 switch(lpParms->dwItem) {
1536 case MCI_GETDEVCAPS_DEVICE_TYPE:
1537 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1538 ret = MCI_RESOURCE_RETURNED;
1539 break;
1540 case MCI_GETDEVCAPS_HAS_AUDIO:
1541 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1542 ret = MCI_RESOURCE_RETURNED;
1543 break;
1544 case MCI_GETDEVCAPS_HAS_VIDEO:
1545 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1546 ret = MCI_RESOURCE_RETURNED;
1547 break;
1548 case MCI_GETDEVCAPS_USES_FILES:
1549 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1550 ret = MCI_RESOURCE_RETURNED;
1551 break;
1552 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1553 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1554 ret = MCI_RESOURCE_RETURNED;
1555 break;
1556 case MCI_GETDEVCAPS_CAN_RECORD:
1557 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1558 ret = MCI_RESOURCE_RETURNED;
1559 break;
1560 case MCI_GETDEVCAPS_CAN_EJECT:
1561 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1562 ret = MCI_RESOURCE_RETURNED;
1563 break;
1564 case MCI_GETDEVCAPS_CAN_PLAY:
1565 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1566 ret = MCI_RESOURCE_RETURNED;
1567 break;
1568 case MCI_GETDEVCAPS_CAN_SAVE:
1569 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1570 ret = MCI_RESOURCE_RETURNED;
1571 break;
1572 case MCI_WAVE_GETDEVCAPS_INPUTS:
1573 lpParms->dwReturn = 1;
1574 break;
1575 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1576 lpParms->dwReturn = 1;
1577 break;
1578 default:
1579 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1580 return MCIERR_UNRECOGNIZED_COMMAND;
1582 } else {
1583 WARN("No GetDevCaps-Item !\n");
1584 return MCIERR_UNRECOGNIZED_COMMAND;
1586 return ret;
1589 /**************************************************************************
1590 * WAVE_mciInfo [internal]
1592 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1594 DWORD ret = 0;
1595 LPCWSTR str = 0;
1596 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1598 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1600 if (!lpParms || !lpParms->lpstrReturn)
1601 return MCIERR_NULL_PARAMETER_BLOCK;
1603 if (wmw == NULL) {
1604 ret = MCIERR_INVALID_DEVICE_ID;
1605 } else {
1606 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1607 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1608 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1610 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1612 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1613 case MCI_INFO_PRODUCT: str = wszAudio; break;
1614 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1615 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1616 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1617 default:
1618 WARN("Don't know this info command (%u)\n", dwFlags);
1619 ret = MCIERR_UNRECOGNIZED_COMMAND;
1622 if (str) {
1623 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1624 ret = MCIERR_PARAM_OVERFLOW;
1625 } else {
1626 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1628 } else {
1629 lpParms->lpstrReturn[0] = 0;
1632 return ret;
1635 /**************************************************************************
1636 * DriverProc (MCIWAVE.@)
1638 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1639 LPARAM dwParam1, LPARAM dwParam2)
1641 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1642 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1644 switch (wMsg) {
1645 case DRV_LOAD: return 1;
1646 case DRV_FREE: return 1;
1647 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1648 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1649 case DRV_ENABLE: return 1;
1650 case DRV_DISABLE: return 1;
1651 case DRV_QUERYCONFIGURE: return 1;
1652 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1653 case DRV_INSTALL: return DRVCNF_RESTART;
1654 case DRV_REMOVE: return DRVCNF_RESTART;
1657 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1659 switch (wMsg) {
1660 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1661 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1662 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1663 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1664 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1665 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1666 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1667 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1668 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1669 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1670 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1671 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1672 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1673 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1674 /* commands that should be supported */
1675 case MCI_LOAD:
1676 case MCI_FREEZE:
1677 case MCI_PUT:
1678 case MCI_REALIZE:
1679 case MCI_UNFREEZE:
1680 case MCI_UPDATE:
1681 case MCI_WHERE:
1682 case MCI_STEP:
1683 case MCI_SPIN:
1684 case MCI_ESCAPE:
1685 case MCI_COPY:
1686 case MCI_CUT:
1687 case MCI_DELETE:
1688 case MCI_PASTE:
1689 FIXME("Unsupported yet command [%u]\n", wMsg);
1690 break;
1691 case MCI_WINDOW:
1692 TRACE("Unsupported command [%u]\n", wMsg);
1693 break;
1694 /* option which can be silenced */
1695 case MCI_CONFIGURE:
1696 return 0;
1697 case MCI_OPEN:
1698 case MCI_CLOSE:
1699 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1700 break;
1701 default:
1702 FIXME("is probably wrong msg [%u]\n", wMsg);
1703 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1705 return MCIERR_UNRECOGNIZED_COMMAND;