2 * Wine Driver for MCI wave forms
4 * Copyright 1994 Martin Ayotte
5 * 1999,2000,2005 Eric Pouech
6 * 2000 Francois Jacques
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
42 int nUseCount; /* Incremented for each shared open */
43 HMMIO hFile; /* mmio file handle open as Element */
44 MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */
45 HANDLE hCallback; /* Callback handle for pending notification */
46 LPWSTR lpFileName; /* Name of file (if any) */
48 LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */
49 BOOL fInput; /* FALSE = Output, TRUE = Input */
50 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
51 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
52 DWORD dwPosition; /* position in bytes in chunk */
53 HANDLE hEvent; /* for synchronization */
54 LONG dwEventCount; /* for synchronization */
55 MMCKINFO ckMainRIFF; /* main RIFF chunk */
56 MMCKINFO ckWaveData; /* data chunk */
59 /* ===================================================================
60 * ===================================================================
61 * FIXME: should be using the new mmThreadXXXX functions from WINMM
63 * it would require to add a wine internal flag to mmThreadCreate
64 * in order to pass a 32 bit function instead of a 16 bit one
65 * ===================================================================
66 * =================================================================== */
68 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
78 /**************************************************************************
79 * MCI_SCAStarter [internal]
81 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
83 struct SCA* sca = (struct SCA*)arg;
86 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
87 sca->wDevID, sca->dwParam1, sca->dwParam2);
88 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
89 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
90 sca->wDevID, sca->dwParam1, sca->dwParam2);
91 HeapFree(GetProcessHeap(), 0, sca);
95 /**************************************************************************
96 * MCI_SendCommandAsync [internal]
98 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
99 DWORD_PTR dwParam2, UINT size)
102 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
105 return MCIERR_OUT_OF_MEMORY;
107 sca->wDevID = wDevID;
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);
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]);
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]);
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)
153 if (modp == NULL) return 0xFFFFFFFF;
155 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
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);
184 HeapFree(GetProcessHeap(), 0, wmw);
185 mciSetDriverData(dwDevID, 0);
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);
205 /**************************************************************************
206 * WAVE_mciNotify [internal]
208 * Notifications in MCI work like a 1-element queue.
209 * Each new notification request supersedes the previous one.
210 * This affects Play and Record; other commands are immediate.
212 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
214 /* We simply save one parameter by not passing the wDevID local
215 * to the command. They are the same (via mciGetDriverData).
217 MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
218 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
219 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
220 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
223 /**************************************************************************
224 * WAVE_ConvertByteToTimeFormat [internal]
226 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
230 switch (wmw->dwMciTimeFormat) {
231 case MCI_FORMAT_MILLISECONDS:
232 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
234 case MCI_FORMAT_BYTES:
237 case MCI_FORMAT_SAMPLES:
238 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
241 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
243 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
248 /**************************************************************************
249 * WAVE_ConvertTimeFormatToByte [internal]
251 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
255 switch (wmw->dwMciTimeFormat) {
256 case MCI_FORMAT_MILLISECONDS:
257 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
259 case MCI_FORMAT_BYTES:
262 case MCI_FORMAT_SAMPLES:
263 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
266 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
268 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
272 /**************************************************************************
273 * WAVE_mciReadFmt [internal]
275 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
281 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
282 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
283 return MCIERR_INVALID_FILE;
284 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
285 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
287 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
288 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
290 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
291 if (r < sizeof(PCMWAVEFORMAT)) {
292 HeapFree(GetProcessHeap(), 0, pwfx);
293 return MCIERR_INVALID_FILE;
295 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag);
296 TRACE("nChannels=%d\n", pwfx->nChannels);
297 TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec);
298 TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec);
299 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign);
300 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
301 if (r >= (long)sizeof(WAVEFORMATEX))
302 TRACE("cbSize=%u !\n", pwfx->cbSize);
303 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
304 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
305 HeapFree(GetProcessHeap(), 0, pwfx);
306 return MCIERR_INVALID_FILE;
308 wmw->lpWaveFormat = pwfx;
310 mmioAscend(wmw->hFile, &mmckInfo, 0);
311 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
312 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
313 TRACE("can't find data chunk\n");
314 return MCIERR_INVALID_FILE;
316 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
317 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
321 /**************************************************************************
322 * WAVE_mciDefaultFmt [internal]
324 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
325 * until either Open File or Record. It becomes immutable afterwards,
326 * i.e. Set wave format or channels etc. is subsequently refused.
328 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
330 wmw->lpWaveFormat = &wmw->wfxRef;
331 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
332 wmw->lpWaveFormat->nChannels = 1;
333 wmw->lpWaveFormat->nSamplesPerSec = 11025;
334 wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
335 wmw->lpWaveFormat->nBlockAlign = 1;
336 wmw->lpWaveFormat->wBitsPerSample = 8;
337 wmw->lpWaveFormat->cbSize = 0;
340 /**************************************************************************
341 * WAVE_mciCreateRIFFSkeleton [internal]
343 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
345 MMCKINFO ckWaveFormat;
346 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
347 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
349 lpckRIFF->ckid = FOURCC_RIFF;
350 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
351 lpckRIFF->cksize = 0;
353 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
356 ckWaveFormat.fccType = 0;
357 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
358 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
360 /* Set wave format accepts PCM only, however open an
361 * existing ADPCM file, record into it and the MCI will
362 * happily save back in that format. */
363 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
364 if (wmw->lpWaveFormat->nBlockAlign !=
365 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
366 WORD size = wmw->lpWaveFormat->nChannels *
367 wmw->lpWaveFormat->wBitsPerSample/8;
368 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
369 wmw->lpWaveFormat->nBlockAlign, size);
370 wmw->lpWaveFormat->nBlockAlign = size;
372 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
373 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
374 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
375 wmw->lpWaveFormat->nBlockAlign;
376 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
377 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
378 wmw->lpWaveFormat->nAvgBytesPerSec = speed;
381 if (wmw->lpWaveFormat == &wmw->wfxRef) {
382 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
383 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
384 /* Set wave format accepts PCM only so the size is known. */
385 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
387 wmw->lpWaveFormat = pwfx;
390 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
393 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
394 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
397 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
400 lpckWaveData->cksize = 0;
401 lpckWaveData->fccType = 0;
402 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
404 /* create data chunk */
405 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
411 /* mciClose takes care of wmw->lpWaveFormat. */
412 return MCIERR_INVALID_FILE;
415 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
417 WCHAR szTmpPath[MAX_PATH];
419 DWORD dwRet = MMSYSERR_NOERROR;
426 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
427 WARN("can't retrieve temp path!\n");
428 *pszTmpFileName = NULL;
429 return MCIERR_FILE_NOT_FOUND;
432 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
434 MAX_PATH * sizeof(WCHAR));
435 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
436 WARN("can't retrieve temp file name!\n");
437 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
438 return MCIERR_FILE_NOT_FOUND;
441 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
443 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
445 *hFile = mmioOpenW(*pszTmpFileName, NULL,
446 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
449 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
450 /* temporary file could not be created. clean filename. */
451 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
452 dwRet = MCIERR_FILE_NOT_FOUND;
458 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
460 LRESULT dwRet = MMSYSERR_NOERROR;
463 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
464 if (!fn) return MCIERR_OUT_OF_MEMORY;
465 strcpyW(fn, filename);
466 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
467 wmw->lpFileName = fn;
469 if (strlenW(filename) > 0) {
470 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
471 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
473 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
474 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
476 if (wmw->hFile == 0) {
477 WARN("can't find file=%s!\n", debugstr_w(filename));
478 dwRet = MCIERR_FILE_NOT_FOUND;
482 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
484 /* make sure we're are the beginning of the file */
485 mmioSeek(wmw->hFile, 0, SEEK_SET);
487 /* first reading of this file. read the waveformat chunk */
488 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
489 dwRet = MCIERR_INVALID_FILE;
491 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
492 (LPSTR)&(lpckMainRIFF->ckid),
493 (LPSTR) &(lpckMainRIFF->fccType),
494 (lpckMainRIFF->cksize));
496 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
497 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
498 dwRet = MCIERR_INVALID_FILE;
500 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
508 /**************************************************************************
509 * WAVE_mciOpen [internal]
511 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
514 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
516 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
517 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
518 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
520 if (dwFlags & MCI_OPEN_SHAREABLE)
521 return MCIERR_UNSUPPORTED_FUNCTION;
523 if (wmw->nUseCount > 0) {
524 /* The driver is already opened on this channel
525 * Wave driver cannot be shared
527 return MCIERR_DEVICE_OPEN;
534 wmw->dwStatus = MCI_MODE_NOT_READY;
536 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
537 wmw->hCallback = NULL;
538 WAVE_mciDefaultFmt(wmw);
540 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
541 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
542 wmw->wNotifyDeviceID = wDevID;
544 if (dwFlags & MCI_OPEN_ELEMENT) {
545 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
546 /* could it be that (DWORD)lpOpenParms->lpstrElementName
547 * contains the hFile value ?
549 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
551 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
554 TRACE("hFile=%p\n", wmw->hFile);
559 wmw->dwStatus = MCI_MODE_STOP;
561 if (dwFlags & MCI_NOTIFY)
562 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
566 mmioClose(wmw->hFile, 0);
572 /**************************************************************************
573 * WAVE_mciCue [internal]
575 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
577 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
579 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
581 /* Tests on systems without sound drivers show that Cue, like
582 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
583 * The first Cue Notify does not immediately return the
584 * notification, as if a player or recorder thread is started.
585 * PAUSE mode is reported when successful, but this mode is
586 * different from the normal Pause, because a) Pause then returns
587 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
588 * still accepted, returning the original notification as ABORTED.
589 * I.e. Cue allows subsequent format changes, unlike Record or
590 * Open file, closes winmm if the format changes and stops this
592 * Wine creates one player or recorder thread per async. Play or
593 * Record command. Notification behaviour suggests that MS-W*
594 * reuses a single thread to improve response times. Having Cue
595 * start this thread early helps to improve Play/Record's initial
596 * response time. In effect, Cue is a performance hint, which
597 * justifies our almost no-op implementation.
600 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
601 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
603 if ((dwFlags & MCI_NOTIFY) && lpParms)
604 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
606 return MMSYSERR_NOERROR;
609 /**************************************************************************
610 * WAVE_mciStop [internal]
612 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
615 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
617 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
619 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
621 if (wmw->dwStatus != MCI_MODE_STOP) {
622 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
623 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
626 /* wait for playback thread (if any) to exit before processing further */
627 switch (wmw->dwStatus) {
630 case MCI_MODE_RECORD:
632 int oldStat = wmw->dwStatus;
633 wmw->dwStatus = MCI_MODE_NOT_READY;
634 if (oldStat == MCI_MODE_PAUSE)
635 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
637 while (wmw->dwStatus != MCI_MODE_STOP)
643 wmw->dwStatus = MCI_MODE_STOP;
645 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
646 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
651 /**************************************************************************
652 * WAVE_mciClose [internal]
654 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
657 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
659 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
661 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
663 if (wmw->dwStatus != MCI_MODE_STOP) {
664 /* mciStop handles MCI_NOTIFY_ABORTED */
665 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
670 if (wmw->nUseCount == 0) {
671 if (wmw->hFile != 0) {
672 mmioClose(wmw->hFile, 0);
677 if (wmw->lpWaveFormat != &wmw->wfxRef)
678 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
679 wmw->lpWaveFormat = &wmw->wfxRef;
680 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
681 wmw->lpFileName = NULL;
683 if ((dwFlags & MCI_NOTIFY) && lpParms) {
684 WAVE_mciNotify(lpParms->dwCallback, wmw,
685 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
691 /**************************************************************************
692 * WAVE_mciPlayCallback [internal]
694 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
695 DWORD_PTR dwInstance,
696 LPARAM dwParam1, LPARAM dwParam2)
698 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
705 InterlockedIncrement(&wmw->dwEventCount);
706 TRACE("Returning waveHdr=%lx\n", dwParam1);
707 SetEvent(wmw->hEvent);
710 ERR("Unknown uMsg=%d\n", uMsg);
714 /******************************************************************
715 * WAVE_mciPlayWaitDone [internal]
717 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
720 ResetEvent(wmw->hEvent);
721 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
724 InterlockedIncrement(&wmw->dwEventCount);
726 WaitForSingleObject(wmw->hEvent, INFINITE);
730 /**************************************************************************
731 * WAVE_mciPlay [internal]
733 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
735 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
737 LONG bufsize, count, left;
739 LPWAVEHDR waveHdr = NULL;
740 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
744 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
746 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
747 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
749 if (wmw->hFile == 0) {
750 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
751 return MCIERR_FILE_NOT_FOUND;
754 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
755 /* FIXME: parameters (start/end) in lpParams may not be used */
756 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
759 /** This function will be called again by a thread when async is used.
760 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
761 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
763 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
764 return MCIERR_INTERNAL;
767 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
768 if (wmw->lpWaveFormat->nBlockAlign !=
769 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
770 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
771 wmw->lpWaveFormat->nBlockAlign,
772 wmw->lpWaveFormat->nChannels *
773 wmw->lpWaveFormat->wBitsPerSample/8);
774 wmw->lpWaveFormat->nBlockAlign =
775 wmw->lpWaveFormat->nChannels *
776 wmw->lpWaveFormat->wBitsPerSample/8;
778 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
779 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
780 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
781 wmw->lpWaveFormat->nAvgBytesPerSec,
782 wmw->lpWaveFormat->nSamplesPerSec *
783 wmw->lpWaveFormat->nBlockAlign);
784 wmw->lpWaveFormat->nAvgBytesPerSec =
785 wmw->lpWaveFormat->nSamplesPerSec *
786 wmw->lpWaveFormat->nBlockAlign;
790 end = wmw->ckWaveData.cksize;
791 if (lpParms && (dwFlags & MCI_TO)) {
792 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
793 if (position > end) return MCIERR_OUTOFRANGE;
796 if (lpParms && (dwFlags & MCI_FROM)) {
797 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
798 if (position > end) return MCIERR_OUTOFRANGE;
799 /* Seek rounds down, so do we. */
800 position /= wmw->lpWaveFormat->nBlockAlign;
801 position *= wmw->lpWaveFormat->nBlockAlign;
802 wmw->dwPosition = position;
804 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
805 left = end - wmw->dwPosition;
806 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
808 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
809 wmw->dwStatus = MCI_MODE_PLAY;
811 if (!(dwFlags & MCI_WAIT)) {
812 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
813 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
816 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
818 oldcb = InterlockedExchangePointer(&wmw->hCallback,
819 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
820 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
823 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
824 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
826 /* go back to beginning of chunk plus the requested position */
827 /* FIXME: I'm not sure this is correct, notably because some data linked to
828 * the decompression state machine will not be correctly initialized.
829 * try it this way (other way would be to decompress from 0 up to dwPosition
830 * and to start sending to hWave when dwPosition is reached)
832 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
834 /* FIXME: how to choose between several output channels ? here mapper is forced */
835 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
836 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
839 TRACE("Can't open low level audio device %d\n", dwRet);
840 dwRet = MCIERR_DEVICE_OPEN;
845 /* make it so that 3 buffers per second are needed */
846 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
848 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
849 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
850 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
851 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
852 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
853 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
854 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
855 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
856 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
857 dwRet = MCIERR_INTERNAL;
862 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
863 wmw->dwEventCount = 1L; /* for first buffer */
865 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
866 if (hEvent) SetEvent(hEvent);
868 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
869 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
870 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
871 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
874 /* count is always <= bufsize, so this is correct regarding the
875 * waveOutPrepareHeader function
877 waveHdr[whidx].dwBufferLength = count;
878 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
879 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
880 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
881 waveHdr[whidx].dwBytesRecorded);
882 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
884 wmw->dwPosition += count;
885 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
887 WAVE_mciPlayWaitDone(wmw);
891 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
893 /* just to get rid of some race conditions between play, stop and pause */
894 waveOutReset(wmw->hWave);
896 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
897 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
902 if (dwFlags & MCI_NOTIFY)
903 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
905 HeapFree(GetProcessHeap(), 0, waveHdr);
908 waveOutClose(wmw->hWave);
911 CloseHandle(wmw->hEvent);
913 wmw->dwStatus = MCI_MODE_STOP;
915 /* Let the potentically asynchronous commands support FAILURE notification. */
916 if (oldcb) mciDriverNotify(oldcb, wDevID,
917 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
922 /**************************************************************************
923 * WAVE_mciRecordCallback [internal]
925 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
926 DWORD_PTR dwInstance,
927 LPARAM dwParam1, LPARAM dwParam2)
929 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
938 lpWaveHdr = (LPWAVEHDR) dwParam1;
940 InterlockedIncrement(&wmw->dwEventCount);
942 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
944 lpWaveHdr->dwFlags &= ~WHDR_DONE;
946 wmw->dwPosition += count;
947 /* else error reporting ?? */
948 if (wmw->dwStatus == MCI_MODE_RECORD)
950 /* Only queue up another buffer if we are recording. We could receive this
951 message also when waveInReset() is called, since it notifies on all wave
952 buffers that are outstanding. Queueing up more sometimes causes waveInClose
954 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
955 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
958 SetEvent(wmw->hEvent);
961 ERR("Unknown uMsg=%d\n", uMsg);
965 /******************************************************************
966 * WAVE_mciRecordWaitDone [internal]
968 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
971 ResetEvent(wmw->hEvent);
972 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
975 InterlockedIncrement(&wmw->dwEventCount);
977 WaitForSingleObject(wmw->hEvent, INFINITE);
981 /**************************************************************************
982 * WAVE_mciRecord [internal]
984 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
986 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
988 DWORD dwRet = MMSYSERR_NOERROR;
990 LPWAVEHDR waveHdr = NULL;
991 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
994 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
996 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
997 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
999 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1000 /* FIXME: parameters (start/end) in lpParams may not be used */
1001 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1004 /** This function will be called again by a thread when async is used.
1005 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1006 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1008 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
1009 return MCIERR_INTERNAL;
1012 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1013 wmw->dwStatus = MCI_MODE_RECORD;
1015 if (!(dwFlags & MCI_WAIT)) {
1016 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1017 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1020 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1021 * we don't modify the wave part of an existing file (ie. we always erase an
1022 * existing content, we don't overwrite)
1024 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
1025 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1026 if (dwRet != 0) return dwRet;
1028 /* new RIFF file, lpWaveFormat now valid */
1029 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1030 if (dwRet != 0) return dwRet;
1032 if (lpParms && (dwFlags & MCI_TO)) {
1033 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1034 } else end = 0xFFFFFFFF;
1035 if (lpParms && (dwFlags & MCI_FROM)) {
1036 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1037 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1038 /* Seek rounds down, so do we. */
1039 position /= wmw->lpWaveFormat->nBlockAlign;
1040 position *= wmw->lpWaveFormat->nBlockAlign;
1041 wmw->dwPosition = position;
1043 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1045 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1047 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1048 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1049 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1052 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1053 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1055 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1057 /* Go back to the beginning of the chunk plus the requested position */
1058 /* FIXME: I'm not sure this is correct, notably because some data linked to
1059 * the decompression state machine will not be correctly initialized.
1060 * Try it this way (other way would be to decompress from 0 up to dwPosition
1061 * and to start sending to hWave when dwPosition is reached).
1063 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1065 /* By default the device will be opened for output, the MCI_CUE function is there to
1066 * change from output to input and back
1068 /* FIXME: how to choose between several output channels ? here mapper is forced */
1069 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1070 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1072 if (dwRet != MMSYSERR_NOERROR) {
1073 TRACE("Can't open low level audio device %d\n", dwRet);
1074 dwRet = MCIERR_DEVICE_OPEN;
1079 /* make it so that 3 buffers per second are needed */
1080 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1082 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1083 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1084 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1085 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1086 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1087 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1088 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1090 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1091 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1092 dwRet = MCIERR_INTERNAL;
1096 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1097 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1098 dwRet = MCIERR_INTERNAL;
1102 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1103 wmw->dwEventCount = 1L; /* for first buffer */
1105 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1107 dwRet = waveInStart(wmw->hWave);
1109 if (hEvent) SetEvent(hEvent);
1111 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1112 WAVE_mciRecordWaitDone(wmw);
1114 /* Grab callback before another thread kicks in after we change dwStatus. */
1115 if (dwFlags & MCI_NOTIFY) {
1116 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1117 dwFlags &= ~MCI_NOTIFY;
1119 /* needed so that the callback above won't add again the buffers returned by the reset */
1120 wmw->dwStatus = MCI_MODE_STOP;
1122 waveInReset(wmw->hWave);
1124 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1125 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1130 if (dwFlags & MCI_NOTIFY)
1131 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1133 HeapFree(GetProcessHeap(), 0, waveHdr);
1136 waveInClose(wmw->hWave);
1139 CloseHandle(wmw->hEvent);
1141 wmw->dwStatus = MCI_MODE_STOP;
1143 if (oldcb) mciDriverNotify(oldcb, wDevID,
1144 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1150 /**************************************************************************
1151 * WAVE_mciPause [internal]
1153 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1156 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1158 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1160 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1162 switch (wmw->dwStatus) {
1164 dwRet = waveOutPause(wmw->hWave);
1165 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1166 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1167 ERR("waveOutPause error %d\n",dwRet);
1168 dwRet = MCIERR_INTERNAL;
1171 case MCI_MODE_RECORD:
1172 dwRet = waveInStop(wmw->hWave);
1173 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1175 ERR("waveInStop error %d\n",dwRet);
1176 dwRet = MCIERR_INTERNAL;
1179 case MCI_MODE_PAUSE:
1180 dwRet = MMSYSERR_NOERROR;
1183 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1185 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1186 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1190 /**************************************************************************
1191 * WAVE_mciResume [internal]
1193 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1195 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1198 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1200 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1202 switch (wmw->dwStatus) {
1203 case MCI_MODE_PAUSE:
1204 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1206 dwRet = waveInStart(wmw->hWave);
1207 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1209 ERR("waveInStart error %d\n",dwRet);
1210 dwRet = MCIERR_INTERNAL;
1213 dwRet = waveOutRestart(wmw->hWave);
1214 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1216 ERR("waveOutRestart error %d\n",dwRet);
1217 dwRet = MCIERR_INTERNAL;
1222 case MCI_MODE_RECORD:
1223 dwRet = MMSYSERR_NOERROR;
1226 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1228 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1229 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1233 /**************************************************************************
1234 * WAVE_mciSeek [internal]
1236 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1238 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1239 DWORD position, dwRet;
1241 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1243 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1244 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1246 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1247 if (!position) return MCIERR_MISSING_PARAMETER;
1248 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
1250 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1251 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1252 if (dwRet != MMSYSERR_NOERROR) return dwRet;
1254 if (dwFlags & MCI_TO) {
1255 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1256 if (position > wmw->ckWaveData.cksize)
1257 return MCIERR_OUTOFRANGE;
1258 } else if (dwFlags & MCI_SEEK_TO_START) {
1261 position = wmw->ckWaveData.cksize;
1263 /* Seek rounds down, unless at end */
1264 if (position != wmw->ckWaveData.cksize) {
1265 position /= wmw->lpWaveFormat->nBlockAlign;
1266 position *= wmw->lpWaveFormat->nBlockAlign;
1268 wmw->dwPosition = position;
1269 TRACE("Seeking to position=%u bytes\n", position);
1271 if (dwFlags & MCI_NOTIFY)
1272 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1274 return MMSYSERR_NOERROR;
1277 /**************************************************************************
1278 * WAVE_mciSet [internal]
1280 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1282 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1284 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1286 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1287 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1289 if (dwFlags & MCI_SET_TIME_FORMAT) {
1290 switch (lpParms->dwTimeFormat) {
1291 case MCI_FORMAT_MILLISECONDS:
1292 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1293 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1295 case MCI_FORMAT_BYTES:
1296 TRACE("MCI_FORMAT_BYTES !\n");
1297 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1299 case MCI_FORMAT_SAMPLES:
1300 TRACE("MCI_FORMAT_SAMPLES !\n");
1301 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1304 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1305 return MCIERR_BAD_TIME_FORMAT;
1308 if (dwFlags & MCI_SET_VIDEO) {
1309 TRACE("No support for video !\n");
1310 return MCIERR_UNSUPPORTED_FUNCTION;
1312 if (dwFlags & MCI_SET_DOOR_OPEN) {
1313 TRACE("No support for door open !\n");
1314 return MCIERR_UNSUPPORTED_FUNCTION;
1316 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1317 TRACE("No support for door close !\n");
1318 return MCIERR_UNSUPPORTED_FUNCTION;
1320 if (dwFlags & MCI_SET_AUDIO) {
1321 if (dwFlags & MCI_SET_ON) {
1322 TRACE("MCI_SET_ON audio !\n");
1323 } else if (dwFlags & MCI_SET_OFF) {
1324 TRACE("MCI_SET_OFF audio !\n");
1326 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1327 return MCIERR_BAD_INTEGER;
1330 switch (lpParms->dwAudio)
1332 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1333 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1334 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1335 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1338 if (dwFlags & MCI_WAVE_INPUT)
1339 TRACE("MCI_WAVE_INPUT !\n");
1340 if (dwFlags & MCI_WAVE_OUTPUT)
1341 TRACE("MCI_WAVE_OUTPUT !\n");
1342 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1343 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1344 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1345 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1346 /* Set wave format parameters is refused after Open or Record.*/
1347 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1348 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag);
1349 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1350 if (((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag != WAVE_FORMAT_PCM)
1351 return MCIERR_OUTOFRANGE;
1353 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1354 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1355 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1356 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1358 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1359 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1360 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1361 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1363 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1364 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1365 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1366 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1368 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1369 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1370 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1371 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1373 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1374 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1375 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1376 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1378 if (dwFlags & MCI_NOTIFY)
1379 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1383 /**************************************************************************
1384 * WAVE_mciSave [internal]
1386 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1388 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1389 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1391 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1392 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1393 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1395 if (dwFlags & MCI_WAIT)
1397 FIXME("MCI_WAIT not implemented\n");
1399 WAVE_mciStop(wDevID, 0, NULL);
1401 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1402 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1404 ret = mmioClose(wmw->hFile, 0);
1408 If the destination file already exists, it has to be overwritten. (Behaviour
1409 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1410 my applications. We are making use of mmioRename, which WILL NOT overwrite
1411 the destination file (which is what Windows does, also verified in Win2K)
1412 So, lets delete the destination file before calling mmioRename. If the
1413 destination file DOESN'T exist, the delete will fail silently. Let's also be
1414 careful not to lose our previous error code.
1416 tmpRet = GetLastError();
1417 DeleteFileW (lpParms->lpfilename);
1418 SetLastError(tmpRet);
1420 /* FIXME: Open file.wav; Save; must not rename the original file.
1421 * Nor must Save a.wav; Save b.wav rename a. */
1422 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1423 ret = MMSYSERR_NOERROR;
1426 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1427 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1429 if (ret == MMSYSERR_NOERROR)
1430 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1435 /**************************************************************************
1436 * WAVE_mciStatus [internal]
1438 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1440 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1443 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1444 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1445 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1447 if (dwFlags & MCI_STATUS_ITEM) {
1448 switch (lpParms->dwItem) {
1449 case MCI_STATUS_CURRENT_TRACK:
1450 lpParms->dwReturn = 1;
1451 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1453 case MCI_STATUS_LENGTH:
1455 lpParms->dwReturn = 0;
1456 return MCIERR_UNSUPPORTED_FUNCTION;
1458 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1459 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1460 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1462 case MCI_STATUS_MODE:
1463 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1464 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1465 ret = MCI_RESOURCE_RETURNED;
1467 case MCI_STATUS_MEDIA_PRESENT:
1468 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1469 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1470 ret = MCI_RESOURCE_RETURNED;
1472 case MCI_STATUS_NUMBER_OF_TRACKS:
1473 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1474 lpParms->dwReturn = 1;
1475 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1477 case MCI_STATUS_POSITION:
1479 lpParms->dwReturn = 0;
1480 return MCIERR_UNSUPPORTED_FUNCTION;
1482 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1483 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1484 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1486 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1487 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1489 case MCI_STATUS_READY:
1490 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1491 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1492 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1493 ret = MCI_RESOURCE_RETURNED;
1495 case MCI_STATUS_TIME_FORMAT:
1496 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1497 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1498 ret = MCI_RESOURCE_RETURNED;
1500 case MCI_WAVE_INPUT:
1501 TRACE("MCI_WAVE_INPUT !\n");
1502 lpParms->dwReturn = 0;
1503 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1505 case MCI_WAVE_OUTPUT:
1506 TRACE("MCI_WAVE_OUTPUT !\n");
1509 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1510 lpParms->dwReturn = id;
1512 lpParms->dwReturn = 0;
1513 ret = MCIERR_WAVE_OUTPUTUNSPECIFIED;
1517 /* It is always ok to query wave format parameters,
1518 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1519 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1520 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1521 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1523 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1524 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1525 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1527 case MCI_WAVE_STATUS_BLOCKALIGN:
1528 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1529 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1531 case MCI_WAVE_STATUS_CHANNELS:
1532 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1533 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1535 case MCI_WAVE_STATUS_FORMATTAG:
1536 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1537 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1539 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1540 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1541 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1543 case MCI_WAVE_STATUS_LEVEL:
1544 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1545 lpParms->dwReturn = 0xAAAA5555;
1548 WARN("unknown command %08X !\n", lpParms->dwItem);
1549 return MCIERR_UNRECOGNIZED_COMMAND;
1552 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1553 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1557 /**************************************************************************
1558 * WAVE_mciGetDevCaps [internal]
1560 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1561 LPMCI_GETDEVCAPS_PARMS lpParms)
1563 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1566 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1568 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1569 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1571 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1572 switch(lpParms->dwItem) {
1573 case MCI_GETDEVCAPS_DEVICE_TYPE:
1574 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1575 ret = MCI_RESOURCE_RETURNED;
1577 case MCI_GETDEVCAPS_HAS_AUDIO:
1578 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1579 ret = MCI_RESOURCE_RETURNED;
1581 case MCI_GETDEVCAPS_HAS_VIDEO:
1582 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1583 ret = MCI_RESOURCE_RETURNED;
1585 case MCI_GETDEVCAPS_USES_FILES:
1586 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1587 ret = MCI_RESOURCE_RETURNED;
1589 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1590 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1591 ret = MCI_RESOURCE_RETURNED;
1593 case MCI_GETDEVCAPS_CAN_RECORD:
1594 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1595 ret = MCI_RESOURCE_RETURNED;
1597 case MCI_GETDEVCAPS_CAN_EJECT:
1598 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1599 ret = MCI_RESOURCE_RETURNED;
1601 case MCI_GETDEVCAPS_CAN_PLAY:
1602 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1603 ret = MCI_RESOURCE_RETURNED;
1605 case MCI_GETDEVCAPS_CAN_SAVE:
1606 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1607 ret = MCI_RESOURCE_RETURNED;
1609 case MCI_WAVE_GETDEVCAPS_INPUTS:
1610 lpParms->dwReturn = 1;
1612 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1613 lpParms->dwReturn = 1;
1616 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1617 return MCIERR_UNRECOGNIZED_COMMAND;
1620 WARN("No GetDevCaps-Item !\n");
1621 return MCIERR_UNRECOGNIZED_COMMAND;
1623 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1624 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1628 /**************************************************************************
1629 * WAVE_mciInfo [internal]
1631 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1635 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1637 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1639 if (!lpParms || !lpParms->lpstrReturn)
1640 return MCIERR_NULL_PARAMETER_BLOCK;
1643 ret = MCIERR_INVALID_DEVICE_ID;
1645 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1646 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1647 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1649 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1651 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1652 case MCI_INFO_PRODUCT: str = wszAudio; break;
1653 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1654 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1655 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1657 WARN("Don't know this info command (%u)\n", dwFlags);
1658 ret = MCIERR_UNRECOGNIZED_COMMAND;
1662 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1663 ret = MCIERR_PARAM_OVERFLOW;
1665 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1668 lpParms->lpstrReturn[0] = 0;
1670 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1671 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1675 /**************************************************************************
1676 * DriverProc (MCIWAVE.@)
1678 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1679 LPARAM dwParam1, LPARAM dwParam2)
1681 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1682 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1685 case DRV_LOAD: return 1;
1686 case DRV_FREE: return 1;
1687 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1688 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1689 case DRV_ENABLE: return 1;
1690 case DRV_DISABLE: return 1;
1691 case DRV_QUERYCONFIGURE: return 1;
1692 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1693 case DRV_INSTALL: return DRVCNF_RESTART;
1694 case DRV_REMOVE: return DRVCNF_RESTART;
1697 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1700 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1701 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1702 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1703 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1704 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1705 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1706 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1707 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1708 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1709 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1710 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1711 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1712 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1713 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1714 /* commands that should be supported */
1729 FIXME("Unsupported yet command [%u]\n", wMsg);
1732 TRACE("Unsupported command [%u]\n", wMsg);
1734 /* option which can be silenced */
1739 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1742 FIXME("is probably wrong msg [%u]\n", wMsg);
1743 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1745 return MCIERR_UNRECOGNIZED_COMMAND;