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;
731 if (wmw->hFile == 0) {
732 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
733 return MCIERR_FILE_NOT_FOUND;
736 if (wmw->dwStatus == MCI_MODE_PAUSE) {
737 /* FIXME: parameters (start/end) in lpParams may not be used */
738 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
741 /** This function will be called again by a thread when async is used.
742 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
743 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
745 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
746 return MCIERR_INTERNAL;
749 wmw->dwStatus = MCI_MODE_PLAY;
751 if (!(dwFlags & MCI_WAIT)) {
752 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciPlay, dwFlags,
753 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
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 if (lpParms && (dwFlags & MCI_NOTIFY)) {
888 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
889 wmw->openParms.wDeviceID,
890 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
893 wmw->dwStatus = MCI_MODE_STOP;
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 /* FIXME : since there is no way to determine in which mode the device is
976 * open (recording/playback) automatically switch from a mode to another
980 if (wmw->dwStatus == MCI_MODE_PAUSE) {
981 /* FIXME: parameters (start/end) in lpParams may not be used */
982 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
985 /** This function will be called again by a thread when async is used.
986 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
987 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
989 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
990 return MCIERR_INTERNAL;
993 wmw->dwStatus = MCI_MODE_RECORD;
995 if (!(dwFlags & MCI_WAIT)) {
996 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, WAVE_mciRecord, dwFlags,
997 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1000 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1001 * we don't modify the wave part of an existing file (ie. we always erase an
1002 * existing content, we don't overwrite)
1004 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
1005 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
1006 if (dwRet != 0) return dwRet;
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 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1112 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1113 wmw->openParms.wDeviceID,
1114 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1117 wmw->dwStatus = MCI_MODE_STOP;
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 (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1134 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1136 if (wmw->dwStatus == MCI_MODE_PLAY) {
1137 wmw->dwStatus = MCI_MODE_PAUSE;
1140 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1141 else dwRet = waveOutPause(wmw->hWave);
1143 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1146 /**************************************************************************
1147 * WAVE_mciResume [internal]
1149 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1151 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 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1159 wmw->dwStatus = MCI_MODE_PLAY;
1162 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1163 else dwRet = waveOutRestart(wmw->hWave);
1164 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1167 /**************************************************************************
1168 * WAVE_mciSeek [internal]
1170 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1173 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1175 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1177 if (lpParms == NULL) {
1178 ret = MCIERR_NULL_PARAMETER_BLOCK;
1179 } else if (wmw == NULL) {
1180 ret = MCIERR_INVALID_DEVICE_ID;
1182 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1184 if (dwFlags & MCI_SEEK_TO_START) {
1185 wmw->dwPosition = 0;
1186 } else if (dwFlags & MCI_SEEK_TO_END) {
1187 wmw->dwPosition = wmw->ckWaveData.cksize;
1188 } else if (dwFlags & MCI_TO) {
1189 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1191 WARN("dwFlag doesn't tell where to seek to...\n");
1192 return MCIERR_MISSING_PARAMETER;
1195 TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1197 if (dwFlags & MCI_NOTIFY) {
1198 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1199 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1205 /**************************************************************************
1206 * WAVE_mciSet [internal]
1208 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1210 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1212 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1214 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1215 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1217 if (dwFlags & MCI_SET_TIME_FORMAT) {
1218 switch (lpParms->dwTimeFormat) {
1219 case MCI_FORMAT_MILLISECONDS:
1220 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1221 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1223 case MCI_FORMAT_BYTES:
1224 TRACE("MCI_FORMAT_BYTES !\n");
1225 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1227 case MCI_FORMAT_SAMPLES:
1228 TRACE("MCI_FORMAT_SAMPLES !\n");
1229 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1232 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1233 return MCIERR_BAD_TIME_FORMAT;
1236 if (dwFlags & MCI_SET_VIDEO) {
1237 TRACE("No support for video !\n");
1238 return MCIERR_UNSUPPORTED_FUNCTION;
1240 if (dwFlags & MCI_SET_DOOR_OPEN) {
1241 TRACE("No support for door open !\n");
1242 return MCIERR_UNSUPPORTED_FUNCTION;
1244 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1245 TRACE("No support for door close !\n");
1246 return MCIERR_UNSUPPORTED_FUNCTION;
1248 if (dwFlags & MCI_SET_AUDIO) {
1249 if (dwFlags & MCI_SET_ON) {
1250 TRACE("MCI_SET_ON audio !\n");
1251 } else if (dwFlags & MCI_SET_OFF) {
1252 TRACE("MCI_SET_OFF audio !\n");
1254 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1255 return MCIERR_BAD_INTEGER;
1258 switch (lpParms->dwAudio)
1260 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1261 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1262 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1263 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1266 if (dwFlags & MCI_WAVE_INPUT)
1267 TRACE("MCI_WAVE_INPUT !\n");
1268 if (dwFlags & MCI_WAVE_OUTPUT)
1269 TRACE("MCI_WAVE_OUTPUT !\n");
1270 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1271 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1272 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1273 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1274 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1275 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1276 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1278 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1279 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1280 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1282 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1283 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1284 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1286 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1287 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1288 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1290 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1291 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1292 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1294 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1295 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1296 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1301 /**************************************************************************
1302 * WAVE_mciSave [internal]
1304 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1306 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1307 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1308 WPARAM wparam = MCI_NOTIFY_FAILURE;
1310 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1311 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1312 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1314 if (dwFlags & MCI_WAIT)
1316 FIXME("MCI_WAIT not implemented\n");
1318 WAVE_mciStop(wDevID, 0, NULL);
1320 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1321 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1323 ret = mmioClose(wmw->hFile, 0);
1327 If the destination file already exists, it has to be overwritten. (Behaviour
1328 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1329 my applications. We are making use of mmioRename, which WILL NOT overwrite
1330 the destination file (which is what Windows does, also verified in Win2K)
1331 So, lets delete the destination file before calling mmioRename. If the
1332 destination file DOESN'T exist, the delete will fail silently. Let's also be
1333 careful not to lose our previous error code.
1335 tmpRet = GetLastError();
1336 DeleteFileW (lpParms->lpfilename);
1337 SetLastError(tmpRet);
1339 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1340 ret = MMSYSERR_NOERROR;
1343 if (dwFlags & MCI_NOTIFY) {
1344 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1346 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1347 wmw->openParms.wDeviceID, wparam);
1350 if (ret == MMSYSERR_NOERROR)
1351 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1356 /**************************************************************************
1357 * WAVE_mciStatus [internal]
1359 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1361 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1364 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1365 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1366 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1368 if (dwFlags & MCI_STATUS_ITEM) {
1369 switch (lpParms->dwItem) {
1370 case MCI_STATUS_CURRENT_TRACK:
1371 lpParms->dwReturn = 1;
1372 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1374 case MCI_STATUS_LENGTH:
1376 lpParms->dwReturn = 0;
1377 return MCIERR_UNSUPPORTED_FUNCTION;
1379 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1380 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1381 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1383 case MCI_STATUS_MODE:
1384 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1385 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1386 ret = MCI_RESOURCE_RETURNED;
1388 case MCI_STATUS_MEDIA_PRESENT:
1389 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1390 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1391 ret = MCI_RESOURCE_RETURNED;
1393 case MCI_STATUS_NUMBER_OF_TRACKS:
1394 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1395 lpParms->dwReturn = 1;
1396 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1398 case MCI_STATUS_POSITION:
1400 lpParms->dwReturn = 0;
1401 return MCIERR_UNSUPPORTED_FUNCTION;
1403 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1404 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1405 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1407 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1408 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1410 case MCI_STATUS_READY:
1411 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1412 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1413 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1414 ret = MCI_RESOURCE_RETURNED;
1416 case MCI_STATUS_TIME_FORMAT:
1417 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1418 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1419 ret = MCI_RESOURCE_RETURNED;
1421 case MCI_WAVE_INPUT:
1422 TRACE("MCI_WAVE_INPUT !\n");
1423 lpParms->dwReturn = 0;
1424 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1426 case MCI_WAVE_OUTPUT:
1427 TRACE("MCI_WAVE_OUTPUT !\n");
1430 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1431 lpParms->dwReturn = id;
1433 lpParms->dwReturn = 0;
1434 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1438 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1440 lpParms->dwReturn = 0;
1441 return MCIERR_UNSUPPORTED_FUNCTION;
1443 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1444 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1446 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1448 lpParms->dwReturn = 0;
1449 return MCIERR_UNSUPPORTED_FUNCTION;
1451 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1452 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1454 case MCI_WAVE_STATUS_BLOCKALIGN:
1456 lpParms->dwReturn = 0;
1457 return MCIERR_UNSUPPORTED_FUNCTION;
1459 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1460 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1462 case MCI_WAVE_STATUS_CHANNELS:
1464 lpParms->dwReturn = 0;
1465 return MCIERR_UNSUPPORTED_FUNCTION;
1467 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1468 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1470 case MCI_WAVE_STATUS_FORMATTAG:
1472 lpParms->dwReturn = 0;
1473 return MCIERR_UNSUPPORTED_FUNCTION;
1475 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1476 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1478 case MCI_WAVE_STATUS_LEVEL:
1479 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1480 lpParms->dwReturn = 0xAAAA5555;
1482 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1484 lpParms->dwReturn = 0;
1485 return MCIERR_UNSUPPORTED_FUNCTION;
1487 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1488 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1491 WARN("unknown command %08X !\n", lpParms->dwItem);
1492 return MCIERR_UNRECOGNIZED_COMMAND;
1495 if (dwFlags & MCI_NOTIFY) {
1496 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1497 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1502 /**************************************************************************
1503 * WAVE_mciGetDevCaps [internal]
1505 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1506 LPMCI_GETDEVCAPS_PARMS lpParms)
1508 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1511 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1513 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1514 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1516 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1517 switch(lpParms->dwItem) {
1518 case MCI_GETDEVCAPS_DEVICE_TYPE:
1519 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1520 ret = MCI_RESOURCE_RETURNED;
1522 case MCI_GETDEVCAPS_HAS_AUDIO:
1523 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1524 ret = MCI_RESOURCE_RETURNED;
1526 case MCI_GETDEVCAPS_HAS_VIDEO:
1527 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1528 ret = MCI_RESOURCE_RETURNED;
1530 case MCI_GETDEVCAPS_USES_FILES:
1531 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1532 ret = MCI_RESOURCE_RETURNED;
1534 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1535 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1536 ret = MCI_RESOURCE_RETURNED;
1538 case MCI_GETDEVCAPS_CAN_RECORD:
1539 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1540 ret = MCI_RESOURCE_RETURNED;
1542 case MCI_GETDEVCAPS_CAN_EJECT:
1543 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1544 ret = MCI_RESOURCE_RETURNED;
1546 case MCI_GETDEVCAPS_CAN_PLAY:
1547 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1548 ret = MCI_RESOURCE_RETURNED;
1550 case MCI_GETDEVCAPS_CAN_SAVE:
1551 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1552 ret = MCI_RESOURCE_RETURNED;
1554 case MCI_WAVE_GETDEVCAPS_INPUTS:
1555 lpParms->dwReturn = 1;
1557 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1558 lpParms->dwReturn = 1;
1561 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1562 return MCIERR_UNRECOGNIZED_COMMAND;
1565 WARN("No GetDevCaps-Item !\n");
1566 return MCIERR_UNRECOGNIZED_COMMAND;
1571 /**************************************************************************
1572 * WAVE_mciInfo [internal]
1574 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1578 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1580 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1582 if (!lpParms || !lpParms->lpstrReturn)
1583 return MCIERR_NULL_PARAMETER_BLOCK;
1586 ret = MCIERR_INVALID_DEVICE_ID;
1588 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1589 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1590 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1592 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1594 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1595 case MCI_INFO_PRODUCT: str = wszAudio; break;
1596 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1597 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1598 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1600 WARN("Don't know this info command (%u)\n", dwFlags);
1601 ret = MCIERR_UNRECOGNIZED_COMMAND;
1605 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1606 ret = MCIERR_PARAM_OVERFLOW;
1608 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1611 lpParms->lpstrReturn[0] = 0;
1617 /**************************************************************************
1618 * DriverProc (MCIWAVE.@)
1620 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1621 LPARAM dwParam1, LPARAM dwParam2)
1623 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1624 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1627 case DRV_LOAD: return 1;
1628 case DRV_FREE: return 1;
1629 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1630 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1631 case DRV_ENABLE: return 1;
1632 case DRV_DISABLE: return 1;
1633 case DRV_QUERYCONFIGURE: return 1;
1634 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1635 case DRV_INSTALL: return DRVCNF_RESTART;
1636 case DRV_REMOVE: return DRVCNF_RESTART;
1639 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1642 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1643 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1644 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1645 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1646 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1647 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1648 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1649 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1650 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1651 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1652 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1653 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1654 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1655 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1656 /* commands that should be supported */
1671 FIXME("Unsupported yet command [%u]\n", wMsg);
1674 TRACE("Unsupported command [%u]\n", wMsg);
1676 /* option which can be silenced */
1681 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1684 FIXME("is probably wrong msg [%u]\n", wMsg);
1685 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1687 return MCIERR_UNRECOGNIZED_COMMAND;