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 MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
215 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
216 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
217 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
220 /**************************************************************************
221 * WAVE_ConvertByteToTimeFormat [internal]
223 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
227 switch (wmw->dwMciTimeFormat) {
228 case MCI_FORMAT_MILLISECONDS:
229 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
231 case MCI_FORMAT_BYTES:
234 case MCI_FORMAT_SAMPLES:
235 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
238 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
240 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
245 /**************************************************************************
246 * WAVE_ConvertTimeFormatToByte [internal]
248 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
252 switch (wmw->dwMciTimeFormat) {
253 case MCI_FORMAT_MILLISECONDS:
254 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
256 case MCI_FORMAT_BYTES:
259 case MCI_FORMAT_SAMPLES:
260 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
263 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
265 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
269 /**************************************************************************
270 * WAVE_mciReadFmt [internal]
272 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
278 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
279 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
280 return MCIERR_INVALID_FILE;
281 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
282 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
284 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
285 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
287 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
288 if (r < sizeof(PCMWAVEFORMAT)) {
289 HeapFree(GetProcessHeap(), 0, pwfx);
290 return MCIERR_INVALID_FILE;
292 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag);
293 TRACE("nChannels=%d\n", pwfx->nChannels);
294 TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec);
295 TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec);
296 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign);
297 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
298 if (r >= (long)sizeof(WAVEFORMATEX))
299 TRACE("cbSize=%u !\n", pwfx->cbSize);
300 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
301 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
302 HeapFree(GetProcessHeap(), 0, pwfx);
303 return MCIERR_INVALID_FILE;
305 wmw->lpWaveFormat = pwfx;
307 mmioAscend(wmw->hFile, &mmckInfo, 0);
308 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
309 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
310 TRACE("can't find data chunk\n");
311 return MCIERR_INVALID_FILE;
313 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
314 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
318 /**************************************************************************
319 * WAVE_mciDefaultFmt [internal]
321 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
322 * until either Open File or Record. It becomes immutable afterwards,
323 * i.e. Set wave format or channels etc. is subsequently refused.
325 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
327 wmw->lpWaveFormat = &wmw->wfxRef;
328 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
329 wmw->lpWaveFormat->nChannels = 1;
330 wmw->lpWaveFormat->nSamplesPerSec = 11025;
331 wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
332 wmw->lpWaveFormat->nBlockAlign = 1;
333 wmw->lpWaveFormat->wBitsPerSample = 8;
334 wmw->lpWaveFormat->cbSize = 0;
337 /**************************************************************************
338 * WAVE_mciCreateRIFFSkeleton [internal]
340 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
342 MMCKINFO ckWaveFormat;
343 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
344 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
346 lpckRIFF->ckid = FOURCC_RIFF;
347 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
348 lpckRIFF->cksize = 0;
350 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
353 ckWaveFormat.fccType = 0;
354 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
355 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
357 /* Set wave format accepts PCM only, however open an
358 * existing ADPCM file, record into it and the MCI will
359 * happily save back in that format. */
360 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
361 if (wmw->lpWaveFormat->nBlockAlign !=
362 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
363 WORD size = wmw->lpWaveFormat->nChannels *
364 wmw->lpWaveFormat->wBitsPerSample/8;
365 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
366 wmw->lpWaveFormat->nBlockAlign, size);
367 wmw->lpWaveFormat->nBlockAlign = size;
369 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
370 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
371 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
372 wmw->lpWaveFormat->nBlockAlign;
373 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
374 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
375 wmw->lpWaveFormat->nAvgBytesPerSec = speed;
378 if (wmw->lpWaveFormat == &wmw->wfxRef) {
379 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
380 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
381 /* Set wave format accepts PCM only so the size is known. */
382 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
384 wmw->lpWaveFormat = pwfx;
387 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
390 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
391 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
394 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
397 lpckWaveData->cksize = 0;
398 lpckWaveData->fccType = 0;
399 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
401 /* create data chunk */
402 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
408 /* mciClose takes care of wmw->lpWaveFormat. */
409 return MCIERR_INVALID_FILE;
412 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
414 WCHAR szTmpPath[MAX_PATH];
416 DWORD dwRet = MMSYSERR_NOERROR;
423 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
424 WARN("can't retrieve temp path!\n");
425 *pszTmpFileName = NULL;
426 return MCIERR_FILE_NOT_FOUND;
429 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
431 MAX_PATH * sizeof(WCHAR));
432 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
433 WARN("can't retrieve temp file name!\n");
434 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
435 return MCIERR_FILE_NOT_FOUND;
438 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
440 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
442 *hFile = mmioOpenW(*pszTmpFileName, NULL,
443 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
446 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
447 /* temporary file could not be created. clean filename. */
448 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
449 dwRet = MCIERR_FILE_NOT_FOUND;
455 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
457 LRESULT dwRet = MMSYSERR_NOERROR;
460 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
461 if (!fn) return MCIERR_OUT_OF_MEMORY;
462 strcpyW(fn, filename);
463 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
464 wmw->lpFileName = fn;
466 if (strlenW(filename) > 0) {
467 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
468 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
470 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
471 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
473 if (wmw->hFile == 0) {
474 WARN("can't find file=%s!\n", debugstr_w(filename));
475 dwRet = MCIERR_FILE_NOT_FOUND;
479 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
481 /* make sure we're are the beginning of the file */
482 mmioSeek(wmw->hFile, 0, SEEK_SET);
484 /* first reading of this file. read the waveformat chunk */
485 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
486 dwRet = MCIERR_INVALID_FILE;
488 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
489 (LPSTR)&(lpckMainRIFF->ckid),
490 (LPSTR) &(lpckMainRIFF->fccType),
491 (lpckMainRIFF->cksize));
493 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
494 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
495 dwRet = MCIERR_INVALID_FILE;
497 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
505 /**************************************************************************
506 * WAVE_mciOpen [internal]
508 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
511 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
513 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
514 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
515 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
517 if (dwFlags & MCI_OPEN_SHAREABLE)
518 return MCIERR_UNSUPPORTED_FUNCTION;
520 if (wmw->nUseCount > 0) {
521 /* The driver is already opened on this channel
522 * Wave driver cannot be shared
524 return MCIERR_DEVICE_OPEN;
531 wmw->dwStatus = MCI_MODE_NOT_READY;
533 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
534 wmw->hCallback = NULL;
535 WAVE_mciDefaultFmt(wmw);
537 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
538 wmw->wNotifyDeviceID = lpOpenParms->wDeviceID;
540 if (dwFlags & MCI_OPEN_ELEMENT) {
541 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
542 /* could it be that (DWORD)lpOpenParms->lpstrElementName
543 * contains the hFile value ?
545 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
547 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
550 TRACE("hFile=%p\n", wmw->hFile);
555 wmw->dwStatus = MCI_MODE_STOP;
557 if (dwFlags & MCI_NOTIFY)
558 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
562 mmioClose(wmw->hFile, 0);
568 /**************************************************************************
569 * WAVE_mciCue [internal]
571 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
573 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
575 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
577 /* Tests on systems without sound drivers show that Cue, like
578 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
579 * The first Cue Notify does not immediately return the
580 * notification, as if a player or recorder thread is started.
581 * PAUSE mode is reported when successful, but this mode is
582 * different from the normal Pause, because a) Pause then returns
583 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
584 * still accepted, returning the original notification as ABORTED.
585 * I.e. Cue allows subsequent format changes, unlike Record or
586 * Open file, closes winmm if the format changes and stops this
588 * Wine creates one player or recorder thread per async. Play or
589 * Record command. Notification behaviour suggests that MS-W*
590 * reuses a single thread to improve response times. Having Cue
591 * start this thread early helps to improve Play/Record's initial
592 * response time. In effect, Cue is a performance hint, which
593 * justifies our almost no-op implementation.
596 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
597 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
599 if ((dwFlags & MCI_NOTIFY) && lpParms)
600 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
602 return MMSYSERR_NOERROR;
605 /**************************************************************************
606 * WAVE_mciStop [internal]
608 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
611 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
613 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
615 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
617 if (wmw->dwStatus != MCI_MODE_STOP) {
618 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
619 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
622 /* wait for playback thread (if any) to exit before processing further */
623 switch (wmw->dwStatus) {
626 case MCI_MODE_RECORD:
628 int oldStat = wmw->dwStatus;
629 wmw->dwStatus = MCI_MODE_NOT_READY;
630 if (oldStat == MCI_MODE_PAUSE)
631 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
633 while (wmw->dwStatus != MCI_MODE_STOP)
639 wmw->dwStatus = MCI_MODE_STOP;
641 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
642 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
647 /**************************************************************************
648 * WAVE_mciClose [internal]
650 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
653 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
655 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
657 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
659 if (wmw->dwStatus != MCI_MODE_STOP) {
660 /* mciStop handles MCI_NOTIFY_ABORTED */
661 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
666 if (wmw->nUseCount == 0) {
667 if (wmw->hFile != 0) {
668 mmioClose(wmw->hFile, 0);
673 if (wmw->lpWaveFormat != &wmw->wfxRef)
674 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
675 wmw->lpWaveFormat = &wmw->wfxRef;
676 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
677 wmw->lpFileName = NULL;
679 if ((dwFlags & MCI_NOTIFY) && lpParms) {
680 WAVE_mciNotify(lpParms->dwCallback, wmw,
681 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
687 /**************************************************************************
688 * WAVE_mciPlayCallback [internal]
690 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
691 DWORD_PTR dwInstance,
692 LPARAM dwParam1, LPARAM dwParam2)
694 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
701 InterlockedIncrement(&wmw->dwEventCount);
702 TRACE("Returning waveHdr=%lx\n", dwParam1);
703 SetEvent(wmw->hEvent);
706 ERR("Unknown uMsg=%d\n", uMsg);
710 /******************************************************************
711 * WAVE_mciPlayWaitDone [internal]
713 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
716 ResetEvent(wmw->hEvent);
717 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
720 InterlockedIncrement(&wmw->dwEventCount);
722 WaitForSingleObject(wmw->hEvent, INFINITE);
726 /**************************************************************************
727 * WAVE_mciPlay [internal]
729 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
731 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
733 LONG bufsize, count, left;
735 LPWAVEHDR waveHdr = NULL;
736 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
740 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
742 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
743 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
745 if (wmw->hFile == 0) {
746 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
747 return MCIERR_FILE_NOT_FOUND;
750 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
751 /* FIXME: parameters (start/end) in lpParams may not be used */
752 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
755 /** This function will be called again by a thread when async is used.
756 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
757 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
759 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
760 return MCIERR_INTERNAL;
763 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
764 if (wmw->lpWaveFormat->nBlockAlign !=
765 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
766 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
767 wmw->lpWaveFormat->nBlockAlign,
768 wmw->lpWaveFormat->nChannels *
769 wmw->lpWaveFormat->wBitsPerSample/8);
770 wmw->lpWaveFormat->nBlockAlign =
771 wmw->lpWaveFormat->nChannels *
772 wmw->lpWaveFormat->wBitsPerSample/8;
774 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
775 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
776 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
777 wmw->lpWaveFormat->nAvgBytesPerSec,
778 wmw->lpWaveFormat->nSamplesPerSec *
779 wmw->lpWaveFormat->nBlockAlign);
780 wmw->lpWaveFormat->nAvgBytesPerSec =
781 wmw->lpWaveFormat->nSamplesPerSec *
782 wmw->lpWaveFormat->nBlockAlign;
786 end = wmw->ckWaveData.cksize;
787 if (lpParms && (dwFlags & MCI_TO)) {
788 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
789 if (position > end) return MCIERR_OUTOFRANGE;
792 if (lpParms && (dwFlags & MCI_FROM)) {
793 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
794 if (position > end) return MCIERR_OUTOFRANGE;
795 /* Seek rounds down, so do we. */
796 position /= wmw->lpWaveFormat->nBlockAlign;
797 position *= wmw->lpWaveFormat->nBlockAlign;
798 wmw->dwPosition = position;
800 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
801 left = end - wmw->dwPosition;
802 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
804 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
805 wmw->dwStatus = MCI_MODE_PLAY;
807 if (!(dwFlags & MCI_WAIT)) {
808 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, WAVE_mciPlay, dwFlags,
809 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
812 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
814 oldcb = InterlockedExchangePointer(&wmw->hCallback,
815 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
816 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
819 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
820 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
822 /* go back to beginning of chunk plus the requested position */
823 /* FIXME: I'm not sure this is correct, notably because some data linked to
824 * the decompression state machine will not be correctly initialized.
825 * try it this way (other way would be to decompress from 0 up to dwPosition
826 * and to start sending to hWave when dwPosition is reached)
828 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
830 /* FIXME: how to choose between several output channels ? here mapper is forced */
831 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
832 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
835 TRACE("Can't open low level audio device %d\n", dwRet);
836 dwRet = MCIERR_DEVICE_OPEN;
841 /* make it so that 3 buffers per second are needed */
842 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
844 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
845 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
846 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
847 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
848 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
849 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
850 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
851 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
852 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
853 dwRet = MCIERR_INTERNAL;
858 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
859 wmw->dwEventCount = 1L; /* for first buffer */
861 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
862 if (hEvent) SetEvent(hEvent);
864 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
865 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
866 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
867 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
870 /* count is always <= bufsize, so this is correct regarding the
871 * waveOutPrepareHeader function
873 waveHdr[whidx].dwBufferLength = count;
874 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
875 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
876 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
877 waveHdr[whidx].dwBytesRecorded);
878 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
880 wmw->dwPosition += count;
881 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
883 WAVE_mciPlayWaitDone(wmw);
887 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
889 /* just to get rid of some race conditions between play, stop and pause */
890 waveOutReset(wmw->hWave);
892 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
893 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
898 if (dwFlags & MCI_NOTIFY)
899 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
901 HeapFree(GetProcessHeap(), 0, waveHdr);
904 waveOutClose(wmw->hWave);
907 CloseHandle(wmw->hEvent);
909 wmw->dwStatus = MCI_MODE_STOP;
911 /* Let the potentically asynchronous commands support FAILURE notification. */
912 if (oldcb) mciDriverNotify(oldcb, wDevID,
913 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
918 /**************************************************************************
919 * WAVE_mciRecordCallback [internal]
921 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
922 DWORD_PTR dwInstance,
923 LPARAM dwParam1, LPARAM dwParam2)
925 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
934 lpWaveHdr = (LPWAVEHDR) dwParam1;
936 InterlockedIncrement(&wmw->dwEventCount);
938 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
940 lpWaveHdr->dwFlags &= ~WHDR_DONE;
942 wmw->dwPosition += count;
943 /* else error reporting ?? */
944 if (wmw->dwStatus == MCI_MODE_RECORD)
946 /* Only queue up another buffer if we are recording. We could receive this
947 message also when waveInReset() is called, since it notifies on all wave
948 buffers that are outstanding. Queueing up more sometimes causes waveInClose
950 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
951 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
954 SetEvent(wmw->hEvent);
957 ERR("Unknown uMsg=%d\n", uMsg);
961 /******************************************************************
962 * WAVE_mciRecordWaitDone [internal]
964 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
967 ResetEvent(wmw->hEvent);
968 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
971 InterlockedIncrement(&wmw->dwEventCount);
973 WaitForSingleObject(wmw->hEvent, INFINITE);
977 /**************************************************************************
978 * WAVE_mciRecord [internal]
980 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
982 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
984 DWORD dwRet = MMSYSERR_NOERROR;
986 LPWAVEHDR waveHdr = NULL;
987 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
990 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
992 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
993 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
995 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
996 /* FIXME: parameters (start/end) in lpParams may not be used */
997 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1000 /** This function will be called again by a thread when async is used.
1001 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1002 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1004 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
1005 return MCIERR_INTERNAL;
1008 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1009 wmw->dwStatus = MCI_MODE_RECORD;
1011 if (!(dwFlags & MCI_WAIT)) {
1012 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, WAVE_mciRecord, dwFlags,
1013 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1016 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1017 * we don't modify the wave part of an existing file (ie. we always erase an
1018 * existing content, we don't overwrite)
1020 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
1021 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1022 if (dwRet != 0) return dwRet;
1024 /* new RIFF file, lpWaveFormat now valid */
1025 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1026 if (dwRet != 0) return dwRet;
1028 if (lpParms && (dwFlags & MCI_TO)) {
1029 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1030 } else end = 0xFFFFFFFF;
1031 if (lpParms && (dwFlags & MCI_FROM)) {
1032 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1033 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1034 /* Seek rounds down, so do we. */
1035 position /= wmw->lpWaveFormat->nBlockAlign;
1036 position *= wmw->lpWaveFormat->nBlockAlign;
1037 wmw->dwPosition = position;
1039 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1041 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1043 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1044 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1045 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1048 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1049 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1051 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1053 /* Go back to the beginning of the chunk plus the requested position */
1054 /* FIXME: I'm not sure this is correct, notably because some data linked to
1055 * the decompression state machine will not be correctly initialized.
1056 * Try it this way (other way would be to decompress from 0 up to dwPosition
1057 * and to start sending to hWave when dwPosition is reached).
1059 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1061 /* By default the device will be opened for output, the MCI_CUE function is there to
1062 * change from output to input and back
1064 /* FIXME: how to choose between several output channels ? here mapper is forced */
1065 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1066 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1068 if (dwRet != MMSYSERR_NOERROR) {
1069 TRACE("Can't open low level audio device %d\n", dwRet);
1070 dwRet = MCIERR_DEVICE_OPEN;
1075 /* make it so that 3 buffers per second are needed */
1076 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1078 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1079 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1080 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1081 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1082 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1083 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1084 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1086 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1087 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1088 dwRet = MCIERR_INTERNAL;
1092 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1093 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1094 dwRet = MCIERR_INTERNAL;
1098 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1099 wmw->dwEventCount = 1L; /* for first buffer */
1101 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1103 dwRet = waveInStart(wmw->hWave);
1105 if (hEvent) SetEvent(hEvent);
1107 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1108 WAVE_mciRecordWaitDone(wmw);
1110 /* Grab callback before another thread kicks in after we change dwStatus. */
1111 if (dwFlags & MCI_NOTIFY) {
1112 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1113 dwFlags &= ~MCI_NOTIFY;
1115 /* needed so that the callback above won't add again the buffers returned by the reset */
1116 wmw->dwStatus = MCI_MODE_STOP;
1118 waveInReset(wmw->hWave);
1120 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1121 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1126 if (dwFlags & MCI_NOTIFY)
1127 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1129 HeapFree(GetProcessHeap(), 0, waveHdr);
1132 waveInClose(wmw->hWave);
1135 CloseHandle(wmw->hEvent);
1137 wmw->dwStatus = MCI_MODE_STOP;
1139 if (oldcb) mciDriverNotify(oldcb, wDevID,
1140 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1146 /**************************************************************************
1147 * WAVE_mciPause [internal]
1149 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1152 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1154 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1156 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1158 switch (wmw->dwStatus) {
1160 dwRet = waveOutPause(wmw->hWave);
1161 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1162 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1163 ERR("waveOutPause error %d\n",dwRet);
1164 dwRet = MCIERR_INTERNAL;
1167 case MCI_MODE_RECORD:
1168 dwRet = waveInStop(wmw->hWave);
1169 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1171 ERR("waveInStop error %d\n",dwRet);
1172 dwRet = MCIERR_INTERNAL;
1175 case MCI_MODE_PAUSE:
1176 dwRet = MMSYSERR_NOERROR;
1179 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1181 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1182 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1186 /**************************************************************************
1187 * WAVE_mciResume [internal]
1189 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1191 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1194 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1196 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1198 switch (wmw->dwStatus) {
1199 case MCI_MODE_PAUSE:
1200 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1202 dwRet = waveInStart(wmw->hWave);
1203 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1205 ERR("waveInStart error %d\n",dwRet);
1206 dwRet = MCIERR_INTERNAL;
1209 dwRet = waveOutRestart(wmw->hWave);
1210 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1212 ERR("waveOutRestart error %d\n",dwRet);
1213 dwRet = MCIERR_INTERNAL;
1218 case MCI_MODE_RECORD:
1219 dwRet = MMSYSERR_NOERROR;
1222 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1224 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1225 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1229 /**************************************************************************
1230 * WAVE_mciSeek [internal]
1232 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1234 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1235 DWORD position, dwRet;
1237 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1239 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1240 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1242 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1243 if (!position) return MCIERR_MISSING_PARAMETER;
1244 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
1246 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1247 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1248 if (dwRet != MMSYSERR_NOERROR) return dwRet;
1250 if (dwFlags & MCI_TO) {
1251 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1252 if (position > wmw->ckWaveData.cksize)
1253 return MCIERR_OUTOFRANGE;
1254 } else if (dwFlags & MCI_SEEK_TO_START) {
1257 position = wmw->ckWaveData.cksize;
1259 /* Seek rounds down, unless at end */
1260 if (position != wmw->ckWaveData.cksize) {
1261 position /= wmw->lpWaveFormat->nBlockAlign;
1262 position *= wmw->lpWaveFormat->nBlockAlign;
1264 wmw->dwPosition = position;
1265 TRACE("Seeking to position=%u bytes\n", position);
1267 if (dwFlags & MCI_NOTIFY)
1268 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1270 return MMSYSERR_NOERROR;
1273 /**************************************************************************
1274 * WAVE_mciSet [internal]
1276 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1278 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1280 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1282 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1283 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1285 if (dwFlags & MCI_SET_TIME_FORMAT) {
1286 switch (lpParms->dwTimeFormat) {
1287 case MCI_FORMAT_MILLISECONDS:
1288 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1289 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1291 case MCI_FORMAT_BYTES:
1292 TRACE("MCI_FORMAT_BYTES !\n");
1293 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1295 case MCI_FORMAT_SAMPLES:
1296 TRACE("MCI_FORMAT_SAMPLES !\n");
1297 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1300 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1301 return MCIERR_BAD_TIME_FORMAT;
1304 if (dwFlags & MCI_SET_VIDEO) {
1305 TRACE("No support for video !\n");
1306 return MCIERR_UNSUPPORTED_FUNCTION;
1308 if (dwFlags & MCI_SET_DOOR_OPEN) {
1309 TRACE("No support for door open !\n");
1310 return MCIERR_UNSUPPORTED_FUNCTION;
1312 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1313 TRACE("No support for door close !\n");
1314 return MCIERR_UNSUPPORTED_FUNCTION;
1316 if (dwFlags & MCI_SET_AUDIO) {
1317 if (dwFlags & MCI_SET_ON) {
1318 TRACE("MCI_SET_ON audio !\n");
1319 } else if (dwFlags & MCI_SET_OFF) {
1320 TRACE("MCI_SET_OFF audio !\n");
1322 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1323 return MCIERR_BAD_INTEGER;
1326 switch (lpParms->dwAudio)
1328 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1329 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1330 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1331 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1334 if (dwFlags & MCI_WAVE_INPUT)
1335 TRACE("MCI_WAVE_INPUT !\n");
1336 if (dwFlags & MCI_WAVE_OUTPUT)
1337 TRACE("MCI_WAVE_OUTPUT !\n");
1338 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1339 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1340 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1341 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1342 /* Set wave format parameters is refused after Open or Record.*/
1343 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1344 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag);
1345 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1346 if (((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag != WAVE_FORMAT_PCM)
1347 return MCIERR_OUTOFRANGE;
1349 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1350 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1351 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1352 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1354 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1355 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1356 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1357 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1359 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1360 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1361 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1362 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1364 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1365 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1366 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1367 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1369 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1370 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1371 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1372 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1374 if (dwFlags & MCI_NOTIFY)
1375 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1379 /**************************************************************************
1380 * WAVE_mciSave [internal]
1382 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1384 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1385 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1387 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1388 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1389 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1391 if (dwFlags & MCI_WAIT)
1393 FIXME("MCI_WAIT not implemented\n");
1395 WAVE_mciStop(wDevID, 0, NULL);
1397 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1398 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1400 ret = mmioClose(wmw->hFile, 0);
1404 If the destination file already exists, it has to be overwritten. (Behaviour
1405 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1406 my applications. We are making use of mmioRename, which WILL NOT overwrite
1407 the destination file (which is what Windows does, also verified in Win2K)
1408 So, lets delete the destination file before calling mmioRename. If the
1409 destination file DOESN'T exist, the delete will fail silently. Let's also be
1410 careful not to lose our previous error code.
1412 tmpRet = GetLastError();
1413 DeleteFileW (lpParms->lpfilename);
1414 SetLastError(tmpRet);
1416 /* FIXME: Open file.wav; Save; must not rename the original file.
1417 * Nor must Save a.wav; Save b.wav rename a. */
1418 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1419 ret = MMSYSERR_NOERROR;
1422 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1423 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1425 if (ret == MMSYSERR_NOERROR)
1426 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1431 /**************************************************************************
1432 * WAVE_mciStatus [internal]
1434 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1436 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1439 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1440 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1441 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1443 if (dwFlags & MCI_STATUS_ITEM) {
1444 switch (lpParms->dwItem) {
1445 case MCI_STATUS_CURRENT_TRACK:
1446 lpParms->dwReturn = 1;
1447 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1449 case MCI_STATUS_LENGTH:
1451 lpParms->dwReturn = 0;
1452 return MCIERR_UNSUPPORTED_FUNCTION;
1454 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1455 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1456 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1458 case MCI_STATUS_MODE:
1459 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1460 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1461 ret = MCI_RESOURCE_RETURNED;
1463 case MCI_STATUS_MEDIA_PRESENT:
1464 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1465 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1466 ret = MCI_RESOURCE_RETURNED;
1468 case MCI_STATUS_NUMBER_OF_TRACKS:
1469 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1470 lpParms->dwReturn = 1;
1471 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1473 case MCI_STATUS_POSITION:
1475 lpParms->dwReturn = 0;
1476 return MCIERR_UNSUPPORTED_FUNCTION;
1478 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1479 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1480 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1482 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1483 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1485 case MCI_STATUS_READY:
1486 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1487 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1488 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1489 ret = MCI_RESOURCE_RETURNED;
1491 case MCI_STATUS_TIME_FORMAT:
1492 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1493 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1494 ret = MCI_RESOURCE_RETURNED;
1496 case MCI_WAVE_INPUT:
1497 TRACE("MCI_WAVE_INPUT !\n");
1498 lpParms->dwReturn = 0;
1499 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1501 case MCI_WAVE_OUTPUT:
1502 TRACE("MCI_WAVE_OUTPUT !\n");
1505 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1506 lpParms->dwReturn = id;
1508 lpParms->dwReturn = 0;
1509 ret = MCIERR_WAVE_OUTPUTUNSPECIFIED;
1513 /* It is always ok to query wave format parameters,
1514 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1515 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1516 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1517 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1519 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1520 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1521 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1523 case MCI_WAVE_STATUS_BLOCKALIGN:
1524 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1525 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1527 case MCI_WAVE_STATUS_CHANNELS:
1528 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1529 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1531 case MCI_WAVE_STATUS_FORMATTAG:
1532 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1533 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1535 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1536 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1537 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1539 case MCI_WAVE_STATUS_LEVEL:
1540 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1541 lpParms->dwReturn = 0xAAAA5555;
1544 WARN("unknown command %08X !\n", lpParms->dwItem);
1545 return MCIERR_UNRECOGNIZED_COMMAND;
1548 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1549 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1553 /**************************************************************************
1554 * WAVE_mciGetDevCaps [internal]
1556 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1557 LPMCI_GETDEVCAPS_PARMS lpParms)
1559 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1562 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1564 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1565 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1567 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1568 switch(lpParms->dwItem) {
1569 case MCI_GETDEVCAPS_DEVICE_TYPE:
1570 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1571 ret = MCI_RESOURCE_RETURNED;
1573 case MCI_GETDEVCAPS_HAS_AUDIO:
1574 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1575 ret = MCI_RESOURCE_RETURNED;
1577 case MCI_GETDEVCAPS_HAS_VIDEO:
1578 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1579 ret = MCI_RESOURCE_RETURNED;
1581 case MCI_GETDEVCAPS_USES_FILES:
1582 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1583 ret = MCI_RESOURCE_RETURNED;
1585 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1586 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1587 ret = MCI_RESOURCE_RETURNED;
1589 case MCI_GETDEVCAPS_CAN_RECORD:
1590 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1591 ret = MCI_RESOURCE_RETURNED;
1593 case MCI_GETDEVCAPS_CAN_EJECT:
1594 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1595 ret = MCI_RESOURCE_RETURNED;
1597 case MCI_GETDEVCAPS_CAN_PLAY:
1598 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1599 ret = MCI_RESOURCE_RETURNED;
1601 case MCI_GETDEVCAPS_CAN_SAVE:
1602 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1603 ret = MCI_RESOURCE_RETURNED;
1605 case MCI_WAVE_GETDEVCAPS_INPUTS:
1606 lpParms->dwReturn = 1;
1608 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1609 lpParms->dwReturn = 1;
1612 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1613 return MCIERR_UNRECOGNIZED_COMMAND;
1616 WARN("No GetDevCaps-Item !\n");
1617 return MCIERR_UNRECOGNIZED_COMMAND;
1619 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1620 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1624 /**************************************************************************
1625 * WAVE_mciInfo [internal]
1627 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1631 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1633 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1635 if (!lpParms || !lpParms->lpstrReturn)
1636 return MCIERR_NULL_PARAMETER_BLOCK;
1639 ret = MCIERR_INVALID_DEVICE_ID;
1641 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1642 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1643 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1645 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1647 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1648 case MCI_INFO_PRODUCT: str = wszAudio; break;
1649 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1650 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1651 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1653 WARN("Don't know this info command (%u)\n", dwFlags);
1654 ret = MCIERR_UNRECOGNIZED_COMMAND;
1658 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1659 ret = MCIERR_PARAM_OVERFLOW;
1661 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1664 lpParms->lpstrReturn[0] = 0;
1666 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1667 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1671 /**************************************************************************
1672 * DriverProc (MCIWAVE.@)
1674 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1675 LPARAM dwParam1, LPARAM dwParam2)
1677 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1678 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1681 case DRV_LOAD: return 1;
1682 case DRV_FREE: return 1;
1683 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1684 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1685 case DRV_ENABLE: return 1;
1686 case DRV_DISABLE: return 1;
1687 case DRV_QUERYCONFIGURE: return 1;
1688 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1689 case DRV_INSTALL: return DRVCNF_RESTART;
1690 case DRV_REMOVE: return DRVCNF_RESTART;
1693 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1696 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1697 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1698 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1699 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1700 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1701 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1702 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1703 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1704 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1705 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1706 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1707 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1708 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1709 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1710 /* commands that should be supported */
1725 FIXME("Unsupported yet command [%u]\n", wMsg);
1728 TRACE("Unsupported command [%u]\n", wMsg);
1730 /* option which can be silenced */
1735 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1738 FIXME("is probably wrong msg [%u]\n", wMsg);
1739 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1741 return MCIERR_UNRECOGNIZED_COMMAND;