2 * Sample Wine Driver for MCI wave forms
4 * Copyright 1994 Martin Ayotte
5 * 1999,2000,2005 Eric Pouech
6 * 2000 Francois Jacques
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
40 int nUseCount; /* Incremented for each shared open */
41 BOOL fShareable; /* TRUE if first open was shareable */
42 HMMIO hFile; /* mmio file handle open as Element */
43 MCI_WAVE_OPEN_PARMSW openParms;
45 LPWAVEFORMATEX lpWaveFormat;
46 BOOL fInput; /* FALSE = Output, TRUE = Input */
47 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
48 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
49 DWORD dwPosition; /* position in bytes in chunk */
50 HANDLE hEvent; /* for synchronization */
51 LONG dwEventCount; /* for synchronization */
52 MMCKINFO ckMainRIFF; /* main RIFF chunk */
53 MMCKINFO ckWaveData; /* data chunk */
56 /* ===================================================================
57 * ===================================================================
58 * FIXME: should be using the new mmThreadXXXX functions from WINMM
60 * it would require to add a wine internal flag to mmThreadCreate
61 * in order to pass a 32 bit function instead of a 16 bit one
62 * ===================================================================
63 * =================================================================== */
65 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
75 /**************************************************************************
76 * MCI_SCAStarter [internal]
78 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
80 struct SCA* sca = (struct SCA*)arg;
83 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
84 sca->wDevID, sca->dwParam1, sca->dwParam2);
85 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
86 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
87 sca->wDevID, sca->dwParam1, sca->dwParam2);
88 HeapFree(GetProcessHeap(), 0, sca);
90 WARN("Should not happen ? what's wrong\n");
91 /* should not go after this point */
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_ConvertByteToTimeFormat [internal]
208 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
212 switch (wmw->dwMciTimeFormat) {
213 case MCI_FORMAT_MILLISECONDS:
214 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
216 case MCI_FORMAT_BYTES:
219 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
220 ret = (val * 8) / (wmw->lpWaveFormat->wBitsPerSample ? wmw->lpWaveFormat->wBitsPerSample : 1);
223 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
225 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
230 /**************************************************************************
231 * WAVE_ConvertTimeFormatToByte [internal]
233 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
237 switch (wmw->dwMciTimeFormat) {
238 case MCI_FORMAT_MILLISECONDS:
239 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
241 case MCI_FORMAT_BYTES:
244 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
245 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
248 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
250 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
254 /**************************************************************************
255 * WAVE_mciReadFmt [internal]
257 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
262 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
263 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
264 return MCIERR_INVALID_FILE;
265 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
266 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
268 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
269 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
270 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
271 if (r < sizeof(PCMWAVEFORMAT))
272 return MCIERR_INVALID_FILE;
274 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
275 TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
276 TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec);
277 TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
278 TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
279 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
280 if (r >= (long)sizeof(WAVEFORMATEX))
281 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
283 mmioAscend(wmw->hFile, &mmckInfo, 0);
284 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
285 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
286 TRACE("can't find data chunk\n");
287 return MCIERR_INVALID_FILE;
289 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
290 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
291 TRACE("nChannels=%d nSamplesPerSec=%d\n",
292 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
297 /**************************************************************************
298 * WAVE_mciDefaultFmt [internal]
300 static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
302 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
303 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
305 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
306 wmw->lpWaveFormat->nChannels = 1;
307 wmw->lpWaveFormat->nSamplesPerSec = 44000;
308 wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
309 wmw->lpWaveFormat->nBlockAlign = 1;
310 wmw->lpWaveFormat->wBitsPerSample = 8;
315 /**************************************************************************
316 * WAVE_mciCreateRIFFSkeleton [internal]
318 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
320 MMCKINFO ckWaveFormat;
321 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
322 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
324 lpckRIFF->ckid = FOURCC_RIFF;
325 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
326 lpckRIFF->cksize = 0;
328 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
331 ckWaveFormat.fccType = 0;
332 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
333 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
335 if (!wmw->lpWaveFormat)
337 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
338 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
339 *wmw->lpWaveFormat = wmw->wfxRef;
342 /* we can only record PCM files... there is no way in the MCI API to specify
343 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
346 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
349 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
352 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
355 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
358 lpckWaveData->cksize = 0;
359 lpckWaveData->fccType = 0;
360 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
362 /* create data chunk */
363 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
369 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
370 wmw->lpWaveFormat = NULL;
371 return MCIERR_INVALID_FILE;
374 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
376 WCHAR szTmpPath[MAX_PATH];
378 DWORD dwRet = MMSYSERR_NOERROR;
385 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
386 WARN("can't retrieve temp path!\n");
387 return MCIERR_FILE_NOT_FOUND;
390 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
392 MAX_PATH * sizeof(WCHAR));
393 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
394 WARN("can't retrieve temp file name!\n");
395 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
396 return MCIERR_FILE_NOT_FOUND;
399 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
401 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
403 *hFile = mmioOpenW(*pszTmpFileName, NULL,
404 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
407 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
408 /* temporary file could not be created. clean filename. */
409 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
410 dwRet = MCIERR_FILE_NOT_FOUND;
416 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
418 LRESULT dwRet = MMSYSERR_NOERROR;
421 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
422 if (!fn) return MCIERR_OUT_OF_MEMORY;
423 strcpyW(fn, filename);
424 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
425 wmw->openParms.lpstrElementName = fn;
427 if (strlenW(filename) > 0) {
428 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
429 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
431 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
432 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
434 if (wmw->hFile == 0) {
435 WARN("can't find file=%s!\n", debugstr_w(filename));
436 dwRet = MCIERR_FILE_NOT_FOUND;
440 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
442 /* make sure we're are the beginning of the file */
443 mmioSeek(wmw->hFile, 0, SEEK_SET);
445 /* first reading of this file. read the waveformat chunk */
446 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
447 dwRet = MCIERR_INVALID_FILE;
449 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
450 (LPSTR)&(lpckMainRIFF->ckid),
451 (LPSTR) &(lpckMainRIFF->fccType),
452 (lpckMainRIFF->cksize));
454 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
455 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
456 dwRet = MCIERR_INVALID_FILE;
458 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
466 /**************************************************************************
467 * WAVE_mciOpen [internal]
469 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
472 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
474 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
475 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
476 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
478 if (dwFlags & MCI_OPEN_SHAREABLE)
479 return MCIERR_HARDWARE;
481 if (wmw->nUseCount > 0) {
482 /* The driver is already opened on this channel
483 * Wave driver cannot be shared
485 return MCIERR_DEVICE_OPEN;
492 wmw->dwStatus = MCI_MODE_NOT_READY;
494 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
495 /* will be set by WAVE_mciOpenFile */
496 wmw->openParms.lpstrElementName = NULL;
498 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
500 if (dwFlags & MCI_OPEN_ELEMENT) {
501 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
502 /* could it be that (DWORD)lpOpenParms->lpstrElementName
503 * contains the hFile value ?
505 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
507 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
511 TRACE("hFile=%p\n", wmw->hFile);
513 if (dwRet == 0 && !wmw->lpWaveFormat)
514 dwRet = WAVE_mciDefaultFmt(wmw);
517 if (wmw->lpWaveFormat) {
518 switch (wmw->lpWaveFormat->wFormatTag) {
519 case WAVE_FORMAT_PCM:
520 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
521 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
522 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
523 wmw->lpWaveFormat->nAvgBytesPerSec,
524 wmw->lpWaveFormat->nSamplesPerSec *
525 wmw->lpWaveFormat->nBlockAlign);
526 wmw->lpWaveFormat->nAvgBytesPerSec =
527 wmw->lpWaveFormat->nSamplesPerSec *
528 wmw->lpWaveFormat->nBlockAlign;
535 wmw->dwStatus = MCI_MODE_STOP;
539 mmioClose(wmw->hFile, 0);
545 /**************************************************************************
546 * WAVE_mciCue [internal]
548 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
553 This routine is far from complete. At the moment only a check is done on the
554 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
557 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
562 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
564 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
566 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
569 /* always close elements ? */
570 if (wmw->hFile != 0) {
571 mmioClose(wmw->hFile, 0);
575 dwRet = MMSYSERR_NOERROR; /* assume success */
577 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
578 dwRet = waveOutClose(wmw->hWave);
579 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
581 } else if (wmw->fInput) {
582 dwRet = waveInClose(wmw->hWave);
583 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
587 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
590 /**************************************************************************
591 * WAVE_mciStop [internal]
593 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
596 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
598 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
600 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
602 /* wait for playback thread (if any) to exit before processing further */
603 switch (wmw->dwStatus) {
606 case MCI_MODE_RECORD:
608 int oldStat = wmw->dwStatus;
609 wmw->dwStatus = MCI_MODE_NOT_READY;
610 if (oldStat == MCI_MODE_PAUSE)
611 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
613 while (wmw->dwStatus != MCI_MODE_STOP)
621 wmw->dwStatus = MCI_MODE_STOP;
623 if ((dwFlags & MCI_NOTIFY) && lpParms) {
624 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
625 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
631 /**************************************************************************
632 * WAVE_mciClose [internal]
634 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
637 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
639 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
641 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
643 if (wmw->dwStatus != MCI_MODE_STOP) {
644 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
649 if (wmw->nUseCount == 0) {
650 if (wmw->hFile != 0) {
651 mmioClose(wmw->hFile, 0);
656 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
657 wmw->lpWaveFormat = NULL;
658 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
659 wmw->openParms.lpstrElementName = NULL;
661 if ((dwFlags & MCI_NOTIFY) && lpParms) {
662 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
663 wmw->openParms.wDeviceID,
664 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
670 /**************************************************************************
671 * WAVE_mciPlayCallback [internal]
673 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
674 DWORD_PTR dwInstance,
675 LPARAM dwParam1, LPARAM dwParam2)
677 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
684 InterlockedIncrement(&wmw->dwEventCount);
685 TRACE("Returning waveHdr=%lx\n", dwParam1);
686 SetEvent(wmw->hEvent);
689 ERR("Unknown uMsg=%d\n", uMsg);
693 /******************************************************************
694 * WAVE_mciPlayWaitDone
698 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
701 ResetEvent(wmw->hEvent);
702 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
705 InterlockedIncrement(&wmw->dwEventCount);
707 WaitForSingleObject(wmw->hEvent, INFINITE);
711 /**************************************************************************
712 * WAVE_mciPlay [internal]
714 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
716 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
718 LONG bufsize, count, left;
720 LPWAVEHDR waveHdr = NULL;
721 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
724 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
726 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
727 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
729 if (wmw->hFile == 0) {
730 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
731 return MCIERR_FILE_NOT_FOUND;
734 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
735 /* FIXME: parameters (start/end) in lpParams may not be used */
736 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
741 /** This function will be called again by a thread when async is used.
742 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
743 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
745 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
746 return MCIERR_INTERNAL;
749 wmw->dwStatus = MCI_MODE_PLAY;
751 if (!(dwFlags & MCI_WAIT)) {
752 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciPlay, dwFlags,
753 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
757 if (lpParms && (dwFlags & MCI_FROM)) {
758 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
760 if (lpParms && (dwFlags & MCI_TO)) {
761 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
764 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
766 if (end <= wmw->dwPosition)
770 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
771 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
773 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
774 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
777 if (wmw->lpWaveFormat) {
778 switch (wmw->lpWaveFormat->wFormatTag) {
779 case WAVE_FORMAT_PCM:
780 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
781 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
782 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
783 wmw->lpWaveFormat->nAvgBytesPerSec,
784 wmw->lpWaveFormat->nSamplesPerSec *
785 wmw->lpWaveFormat->nBlockAlign);
786 wmw->lpWaveFormat->nAvgBytesPerSec =
787 wmw->lpWaveFormat->nSamplesPerSec *
788 wmw->lpWaveFormat->nBlockAlign;
794 TRACE("can't retrieve wave format %d\n", dwRet);
799 /* go back to beginning of chunk plus the requested position */
800 /* FIXME: I'm not sure this is correct, notably because some data linked to
801 * the decompression state machine will not be correctly initialized.
802 * try it this way (other way would be to decompress from 0 up to dwPosition
803 * and to start sending to hWave when dwPosition is reached)
805 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
807 /* By default the device will be opened for output, the MCI_CUE function is there to
808 * change from output to input and back
810 /* FIXME: how to choose between several output channels ? here mapper is forced */
811 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
812 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
815 TRACE("Can't open low level audio device %d\n", dwRet);
816 dwRet = MCIERR_DEVICE_OPEN;
821 /* make it so that 3 buffers per second are needed */
822 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
824 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
825 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
826 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
827 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
828 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
829 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
830 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
831 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
832 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
833 dwRet = MCIERR_INTERNAL;
838 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
839 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
840 wmw->dwEventCount = 1L; /* for first buffer */
842 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
843 if (hEvent) SetEvent(hEvent);
845 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
846 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
847 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
848 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
851 /* count is always <= bufsize, so this is correct regarding the
852 * waveOutPrepareHeader function
854 waveHdr[whidx].dwBufferLength = count;
855 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
856 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
857 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
858 waveHdr[whidx].dwBytesRecorded);
859 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
861 wmw->dwPosition += count;
862 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
864 WAVE_mciPlayWaitDone(wmw);
868 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
870 /* just to get rid of some race conditions between play, stop and pause */
871 waveOutReset(wmw->hWave);
873 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
874 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
879 HeapFree(GetProcessHeap(), 0, waveHdr);
882 waveOutClose(wmw->hWave);
885 CloseHandle(wmw->hEvent);
887 wmw->dwStatus = MCI_MODE_STOP;
889 if (lpParms && (dwFlags & MCI_NOTIFY)) {
890 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
891 wmw->openParms.wDeviceID,
892 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
898 /**************************************************************************
899 * WAVE_mciPlayCallback [internal]
901 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
902 DWORD_PTR dwInstance,
903 LPARAM dwParam1, LPARAM dwParam2)
905 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
914 lpWaveHdr = (LPWAVEHDR) dwParam1;
916 InterlockedIncrement(&wmw->dwEventCount);
918 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
920 lpWaveHdr->dwFlags &= ~WHDR_DONE;
922 wmw->dwPosition += count;
923 /* else error reporting ?? */
924 if (wmw->dwStatus == MCI_MODE_RECORD)
926 /* Only queue up another buffer if we are recording. We could receive this
927 message also when waveInReset() is called, since it notifies on all wave
928 buffers that are outstanding. Queueing up more sometimes causes waveInClose
930 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
931 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
934 SetEvent(wmw->hEvent);
937 ERR("Unknown uMsg=%d\n", uMsg);
941 /******************************************************************
942 * bWAVE_mciRecordWaitDone
945 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
948 ResetEvent(wmw->hEvent);
949 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
952 InterlockedIncrement(&wmw->dwEventCount);
954 WaitForSingleObject(wmw->hEvent, INFINITE);
958 /**************************************************************************
959 * WAVE_mciRecord [internal]
961 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
963 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
965 DWORD dwRet = MMSYSERR_NOERROR;
967 LPWAVEHDR waveHdr = NULL;
968 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
970 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
972 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
973 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
975 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
976 /* FIXME: parameters (start/end) in lpParams may not be used */
977 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
980 /* FIXME : since there is no way to determine in which mode the device is
981 * open (recording/playback) automatically switch from a mode to another
985 /** This function will be called again by a thread when async is used.
986 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
987 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
989 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
990 return MCIERR_INTERNAL;
993 wmw->dwStatus = MCI_MODE_RECORD;
995 if (!(dwFlags & MCI_WAIT)) {
996 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciRecord, dwFlags,
997 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1000 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1001 * we don't modify the wave part of an existing file (ie. we always erase an
1002 * existing content, we don't overwrite)
1004 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
1005 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
1006 if (dwRet != 0) return dwRet;
1009 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1010 if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
1013 if (lpParms && (dwFlags & MCI_FROM)) {
1014 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1017 if (lpParms && (dwFlags & MCI_TO)) {
1018 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1021 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1023 if (end <= wmw->dwPosition)
1028 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1029 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1031 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1032 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1034 /* Go back to the beginning of the chunk plus the requested position */
1035 /* FIXME: I'm not sure this is correct, notably because some data linked to
1036 * the decompression state machine will not be correctly initialized.
1037 * Try it this way (other way would be to decompress from 0 up to dwPosition
1038 * and to start sending to hWave when dwPosition is reached).
1040 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1042 /* By default the device will be opened for output, the MCI_CUE function is there to
1043 * change from output to input and back
1045 /* FIXME: how to choose between several output channels ? here mapper is forced */
1046 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1047 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1049 if (dwRet != MMSYSERR_NOERROR) {
1050 TRACE("Can't open low level audio device %d\n", dwRet);
1051 dwRet = MCIERR_DEVICE_OPEN;
1056 /* make it so that 3 buffers per second are needed */
1057 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1059 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1060 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1061 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1062 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1063 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1064 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1065 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1067 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1068 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1069 dwRet = MCIERR_INTERNAL;
1073 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1074 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1075 dwRet = MCIERR_INTERNAL;
1079 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1080 wmw->dwEventCount = 1L; /* for first buffer */
1082 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1084 dwRet = waveInStart(wmw->hWave);
1086 if (hEvent) SetEvent(hEvent);
1088 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1089 WAVE_mciRecordWaitDone(wmw);
1092 /* needed so that the callback above won't add again the buffers returned by the reset */
1093 wmw->dwStatus = MCI_MODE_STOP;
1095 waveInReset(wmw->hWave);
1097 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1098 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1103 HeapFree(GetProcessHeap(), 0, waveHdr);
1106 waveInClose(wmw->hWave);
1109 CloseHandle(wmw->hEvent);
1111 wmw->dwStatus = MCI_MODE_STOP;
1113 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1114 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1115 wmw->openParms.wDeviceID,
1116 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1123 /**************************************************************************
1124 * WAVE_mciPause [internal]
1126 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1129 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1131 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1133 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1135 switch (wmw->dwStatus) {
1137 dwRet = waveOutPause(wmw->hWave);
1138 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1139 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1140 ERR("waveOutPause error %d\n",dwRet);
1141 dwRet = MCIERR_INTERNAL;
1144 case MCI_MODE_RECORD:
1145 dwRet = waveInStop(wmw->hWave);
1146 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1148 ERR("waveInStop error %d\n",dwRet);
1149 dwRet = MCIERR_INTERNAL;
1152 case MCI_MODE_PAUSE:
1153 dwRet = MMSYSERR_NOERROR;
1156 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1161 /**************************************************************************
1162 * WAVE_mciResume [internal]
1164 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1166 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1169 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1171 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1173 switch (wmw->dwStatus) {
1174 case MCI_MODE_PAUSE:
1175 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1177 dwRet = waveInStart(wmw->hWave);
1178 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1180 ERR("waveInStart error %d\n",dwRet);
1181 dwRet = MCIERR_INTERNAL;
1184 dwRet = waveOutRestart(wmw->hWave);
1185 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1187 ERR("waveOutRestart error %d\n",dwRet);
1188 dwRet = MCIERR_INTERNAL;
1193 case MCI_MODE_RECORD:
1194 dwRet = MMSYSERR_NOERROR;
1197 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1202 /**************************************************************************
1203 * WAVE_mciSeek [internal]
1205 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1208 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1210 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1212 if (lpParms == NULL) {
1213 ret = MCIERR_NULL_PARAMETER_BLOCK;
1214 } else if (wmw == NULL) {
1215 ret = MCIERR_INVALID_DEVICE_ID;
1217 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1219 if (dwFlags & MCI_SEEK_TO_START) {
1220 wmw->dwPosition = 0;
1221 } else if (dwFlags & MCI_SEEK_TO_END) {
1222 wmw->dwPosition = wmw->ckWaveData.cksize;
1223 } else if (dwFlags & MCI_TO) {
1224 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1226 WARN("dwFlag doesn't tell where to seek to...\n");
1227 return MCIERR_MISSING_PARAMETER;
1230 TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1232 if (dwFlags & MCI_NOTIFY) {
1233 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1234 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1240 /**************************************************************************
1241 * WAVE_mciSet [internal]
1243 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1245 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1247 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1249 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1250 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1252 if (dwFlags & MCI_SET_TIME_FORMAT) {
1253 switch (lpParms->dwTimeFormat) {
1254 case MCI_FORMAT_MILLISECONDS:
1255 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1256 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1258 case MCI_FORMAT_BYTES:
1259 TRACE("MCI_FORMAT_BYTES !\n");
1260 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1262 case MCI_FORMAT_SAMPLES:
1263 TRACE("MCI_FORMAT_SAMPLES !\n");
1264 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1267 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1268 return MCIERR_BAD_TIME_FORMAT;
1271 if (dwFlags & MCI_SET_VIDEO) {
1272 TRACE("No support for video !\n");
1273 return MCIERR_UNSUPPORTED_FUNCTION;
1275 if (dwFlags & MCI_SET_DOOR_OPEN) {
1276 TRACE("No support for door open !\n");
1277 return MCIERR_UNSUPPORTED_FUNCTION;
1279 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1280 TRACE("No support for door close !\n");
1281 return MCIERR_UNSUPPORTED_FUNCTION;
1283 if (dwFlags & MCI_SET_AUDIO) {
1284 if (dwFlags & MCI_SET_ON) {
1285 TRACE("MCI_SET_ON audio !\n");
1286 } else if (dwFlags & MCI_SET_OFF) {
1287 TRACE("MCI_SET_OFF audio !\n");
1289 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1290 return MCIERR_BAD_INTEGER;
1293 switch (lpParms->dwAudio)
1295 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1296 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1297 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1298 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1301 if (dwFlags & MCI_WAVE_INPUT)
1302 TRACE("MCI_WAVE_INPUT !\n");
1303 if (dwFlags & MCI_WAVE_OUTPUT)
1304 TRACE("MCI_WAVE_OUTPUT !\n");
1305 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1306 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1307 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1308 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1309 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1310 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1311 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1313 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1314 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1315 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1317 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1318 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1319 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1321 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1322 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1323 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1325 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1326 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1327 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1329 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1330 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1331 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1336 /**************************************************************************
1337 * WAVE_mciSave [internal]
1339 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1341 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1342 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1343 WPARAM wparam = MCI_NOTIFY_FAILURE;
1345 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1346 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1347 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1349 if (dwFlags & MCI_WAIT)
1351 FIXME("MCI_WAIT not implemented\n");
1353 WAVE_mciStop(wDevID, 0, NULL);
1355 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1356 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1358 ret = mmioClose(wmw->hFile, 0);
1362 If the destination file already exists, it has to be overwritten. (Behaviour
1363 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1364 my applications. We are making use of mmioRename, which WILL NOT overwrite
1365 the destination file (which is what Windows does, also verified in Win2K)
1366 So, lets delete the destination file before calling mmioRename. If the
1367 destination file DOESN'T exist, the delete will fail silently. Let's also be
1368 careful not to lose our previous error code.
1370 tmpRet = GetLastError();
1371 DeleteFileW (lpParms->lpfilename);
1372 SetLastError(tmpRet);
1374 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1375 ret = MMSYSERR_NOERROR;
1378 if (dwFlags & MCI_NOTIFY) {
1379 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1381 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1382 wmw->openParms.wDeviceID, wparam);
1385 if (ret == MMSYSERR_NOERROR)
1386 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1391 /**************************************************************************
1392 * WAVE_mciStatus [internal]
1394 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1396 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1399 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1400 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1401 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1403 if (dwFlags & MCI_STATUS_ITEM) {
1404 switch (lpParms->dwItem) {
1405 case MCI_STATUS_CURRENT_TRACK:
1406 lpParms->dwReturn = 1;
1407 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1409 case MCI_STATUS_LENGTH:
1411 lpParms->dwReturn = 0;
1412 return MCIERR_UNSUPPORTED_FUNCTION;
1414 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1415 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1416 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1418 case MCI_STATUS_MODE:
1419 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1420 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1421 ret = MCI_RESOURCE_RETURNED;
1423 case MCI_STATUS_MEDIA_PRESENT:
1424 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1425 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1426 ret = MCI_RESOURCE_RETURNED;
1428 case MCI_STATUS_NUMBER_OF_TRACKS:
1429 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1430 lpParms->dwReturn = 1;
1431 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1433 case MCI_STATUS_POSITION:
1435 lpParms->dwReturn = 0;
1436 return MCIERR_UNSUPPORTED_FUNCTION;
1438 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1439 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1440 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1442 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1443 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1445 case MCI_STATUS_READY:
1446 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1447 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1448 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1449 ret = MCI_RESOURCE_RETURNED;
1451 case MCI_STATUS_TIME_FORMAT:
1452 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1453 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1454 ret = MCI_RESOURCE_RETURNED;
1456 case MCI_WAVE_INPUT:
1457 TRACE("MCI_WAVE_INPUT !\n");
1458 lpParms->dwReturn = 0;
1459 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1461 case MCI_WAVE_OUTPUT:
1462 TRACE("MCI_WAVE_OUTPUT !\n");
1465 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1466 lpParms->dwReturn = id;
1468 lpParms->dwReturn = 0;
1469 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1473 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1475 lpParms->dwReturn = 0;
1476 return MCIERR_UNSUPPORTED_FUNCTION;
1478 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1479 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1481 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1483 lpParms->dwReturn = 0;
1484 return MCIERR_UNSUPPORTED_FUNCTION;
1486 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1487 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1489 case MCI_WAVE_STATUS_BLOCKALIGN:
1491 lpParms->dwReturn = 0;
1492 return MCIERR_UNSUPPORTED_FUNCTION;
1494 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1495 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1497 case MCI_WAVE_STATUS_CHANNELS:
1499 lpParms->dwReturn = 0;
1500 return MCIERR_UNSUPPORTED_FUNCTION;
1502 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1503 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1505 case MCI_WAVE_STATUS_FORMATTAG:
1507 lpParms->dwReturn = 0;
1508 return MCIERR_UNSUPPORTED_FUNCTION;
1510 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1511 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1513 case MCI_WAVE_STATUS_LEVEL:
1514 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1515 lpParms->dwReturn = 0xAAAA5555;
1517 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1519 lpParms->dwReturn = 0;
1520 return MCIERR_UNSUPPORTED_FUNCTION;
1522 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1523 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1526 WARN("unknown command %08X !\n", lpParms->dwItem);
1527 return MCIERR_UNRECOGNIZED_COMMAND;
1530 if (dwFlags & MCI_NOTIFY) {
1531 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1532 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1537 /**************************************************************************
1538 * WAVE_mciGetDevCaps [internal]
1540 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1541 LPMCI_GETDEVCAPS_PARMS lpParms)
1543 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1546 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1548 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1549 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1551 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1552 switch(lpParms->dwItem) {
1553 case MCI_GETDEVCAPS_DEVICE_TYPE:
1554 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1555 ret = MCI_RESOURCE_RETURNED;
1557 case MCI_GETDEVCAPS_HAS_AUDIO:
1558 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1559 ret = MCI_RESOURCE_RETURNED;
1561 case MCI_GETDEVCAPS_HAS_VIDEO:
1562 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1563 ret = MCI_RESOURCE_RETURNED;
1565 case MCI_GETDEVCAPS_USES_FILES:
1566 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1567 ret = MCI_RESOURCE_RETURNED;
1569 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1570 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1571 ret = MCI_RESOURCE_RETURNED;
1573 case MCI_GETDEVCAPS_CAN_RECORD:
1574 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1575 ret = MCI_RESOURCE_RETURNED;
1577 case MCI_GETDEVCAPS_CAN_EJECT:
1578 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1579 ret = MCI_RESOURCE_RETURNED;
1581 case MCI_GETDEVCAPS_CAN_PLAY:
1582 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1583 ret = MCI_RESOURCE_RETURNED;
1585 case MCI_GETDEVCAPS_CAN_SAVE:
1586 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1587 ret = MCI_RESOURCE_RETURNED;
1589 case MCI_WAVE_GETDEVCAPS_INPUTS:
1590 lpParms->dwReturn = 1;
1592 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1593 lpParms->dwReturn = 1;
1596 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1597 return MCIERR_UNRECOGNIZED_COMMAND;
1600 WARN("No GetDevCaps-Item !\n");
1601 return MCIERR_UNRECOGNIZED_COMMAND;
1606 /**************************************************************************
1607 * WAVE_mciInfo [internal]
1609 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1613 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1615 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1617 if (!lpParms || !lpParms->lpstrReturn)
1618 return MCIERR_NULL_PARAMETER_BLOCK;
1621 ret = MCIERR_INVALID_DEVICE_ID;
1623 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1624 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1625 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1627 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1629 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1630 case MCI_INFO_PRODUCT: str = wszAudio; break;
1631 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1632 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1633 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1635 WARN("Don't know this info command (%u)\n", dwFlags);
1636 ret = MCIERR_UNRECOGNIZED_COMMAND;
1640 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1641 ret = MCIERR_PARAM_OVERFLOW;
1643 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1646 lpParms->lpstrReturn[0] = 0;
1652 /**************************************************************************
1653 * DriverProc (MCIWAVE.@)
1655 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1656 LPARAM dwParam1, LPARAM dwParam2)
1658 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1659 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1662 case DRV_LOAD: return 1;
1663 case DRV_FREE: return 1;
1664 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1665 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1666 case DRV_ENABLE: return 1;
1667 case DRV_DISABLE: return 1;
1668 case DRV_QUERYCONFIGURE: return 1;
1669 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1670 case DRV_INSTALL: return DRVCNF_RESTART;
1671 case DRV_REMOVE: return DRVCNF_RESTART;
1674 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1677 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1678 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1679 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1680 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1681 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1682 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1683 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1684 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1685 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1686 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1687 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1688 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1689 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1690 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1691 /* commands that should be supported */
1706 FIXME("Unsupported yet command [%u]\n", wMsg);
1709 TRACE("Unsupported command [%u]\n", wMsg);
1711 /* option which can be silenced */
1716 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1719 FIXME("is probably wrong msg [%u]\n", wMsg);
1720 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1722 return MCIERR_UNRECOGNIZED_COMMAND;