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 * =================================================================== */
72 /**************************************************************************
73 * MCI_SCAStarter [internal]
75 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
77 struct SCA* sca = (struct SCA*)arg;
80 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
81 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
82 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
83 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
84 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
85 HeapFree(GetProcessHeap(), 0, sca);
87 WARN("Should not happen ? what's wrong\n");
88 /* should not go after this point */
92 /**************************************************************************
93 * MCI_SendCommandAsync [internal]
95 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
96 DWORD_PTR dwParam2, UINT size)
99 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
102 return MCIERR_OUT_OF_MEMORY;
104 sca->wDevID = wDevID;
106 sca->dwParam1 = dwParam1;
108 if (size && dwParam2) {
109 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
110 /* copy structure passed by program in dwParam2 to be sure
111 * we can still use it whatever the program does
113 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
115 sca->dwParam2 = dwParam2;
118 if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
119 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
120 return MCI_SCAStarter(&sca);
122 SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
127 /*======================================================================*
128 * MCI WAVE implementation *
129 *======================================================================*/
131 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
133 /**************************************************************************
134 * MCIWAVE_drvOpen [internal]
136 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
140 if (modp == NULL) return 0xFFFFFFFF;
142 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
147 wmw->wDevID = modp->wDeviceID;
148 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
149 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
150 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
152 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
153 wmw->wfxRef.nChannels = 1; /* MONO */
154 wmw->wfxRef.nSamplesPerSec = 11025;
155 wmw->wfxRef.nAvgBytesPerSec = 11025;
156 wmw->wfxRef.nBlockAlign = 1;
157 wmw->wfxRef.wBitsPerSample = 8;
158 wmw->wfxRef.cbSize = 0; /* don't care */
160 return modp->wDeviceID;
163 /**************************************************************************
164 * MCIWAVE_drvClose [internal]
166 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
168 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
171 HeapFree(GetProcessHeap(), 0, wmw);
172 mciSetDriverData(dwDevID, 0);
175 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
178 /**************************************************************************
179 * WAVE_mciGetOpenDev [internal]
181 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
183 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
185 if (wmw == NULL || wmw->nUseCount == 0) {
186 WARN("Invalid wDevID=%u\n", wDevID);
192 /**************************************************************************
193 * WAVE_ConvertByteToTimeFormat [internal]
195 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
199 switch (wmw->dwMciTimeFormat) {
200 case MCI_FORMAT_MILLISECONDS:
201 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
203 case MCI_FORMAT_BYTES:
206 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
207 ret = (val * 8) / (wmw->lpWaveFormat->wBitsPerSample ? wmw->lpWaveFormat->wBitsPerSample : 1);
210 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
212 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
217 /**************************************************************************
218 * WAVE_ConvertTimeFormatToByte [internal]
220 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
224 switch (wmw->dwMciTimeFormat) {
225 case MCI_FORMAT_MILLISECONDS:
226 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
228 case MCI_FORMAT_BYTES:
231 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
232 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
235 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
237 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
241 /**************************************************************************
242 * WAVE_mciReadFmt [internal]
244 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
249 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
250 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
251 return MCIERR_INVALID_FILE;
252 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
253 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
255 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
256 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
257 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
258 if (r < sizeof(WAVEFORMAT))
259 return MCIERR_INVALID_FILE;
261 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
262 TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
263 TRACE("nSamplesPerSec=%d\n", wmw->lpWaveFormat->nSamplesPerSec);
264 TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
265 TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
266 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
267 if (r >= (long)sizeof(WAVEFORMATEX))
268 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
270 mmioAscend(wmw->hFile, &mmckInfo, 0);
271 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
272 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
273 TRACE("can't find data chunk\n");
274 return MCIERR_INVALID_FILE;
276 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
277 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
278 TRACE("nChannels=%d nSamplesPerSec=%d\n",
279 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
284 /**************************************************************************
285 * WAVE_mciDefaultFmt [internal]
287 static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
289 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
290 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
292 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
293 wmw->lpWaveFormat->nChannels = 1;
294 wmw->lpWaveFormat->nSamplesPerSec = 44000;
295 wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
296 wmw->lpWaveFormat->nBlockAlign = 1;
297 wmw->lpWaveFormat->wBitsPerSample = 8;
302 /**************************************************************************
303 * WAVE_mciCreateRIFFSkeleton [internal]
305 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
307 MMCKINFO ckWaveFormat;
308 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
309 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
311 lpckRIFF->ckid = FOURCC_RIFF;
312 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
313 lpckRIFF->cksize = 0;
315 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
318 ckWaveFormat.fccType = 0;
319 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
320 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
322 if (!wmw->lpWaveFormat)
324 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
325 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
326 *wmw->lpWaveFormat = wmw->wfxRef;
329 /* we can only record PCM files... there is no way in the MCI API to specify
330 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
333 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
336 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
339 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
342 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
345 lpckWaveData->cksize = 0;
346 lpckWaveData->fccType = 0;
347 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
349 /* create data chunk */
350 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
356 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
357 wmw->lpWaveFormat = NULL;
358 return MCIERR_INVALID_FILE;
361 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
363 WCHAR szTmpPath[MAX_PATH];
365 DWORD dwRet = MMSYSERR_NOERROR;
372 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
373 WARN("can't retrieve temp path!\n");
374 return MCIERR_FILE_NOT_FOUND;
377 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
379 MAX_PATH * sizeof(WCHAR));
380 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
381 WARN("can't retrieve temp file name!\n");
382 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
383 return MCIERR_FILE_NOT_FOUND;
386 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
388 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
390 *hFile = mmioOpenW(*pszTmpFileName, NULL,
391 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
394 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
395 /* temporary file could not be created. clean filename. */
396 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
397 dwRet = MCIERR_FILE_NOT_FOUND;
403 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
405 LRESULT dwRet = MMSYSERR_NOERROR;
408 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
409 if (!fn) return MCIERR_OUT_OF_MEMORY;
410 strcpyW(fn, filename);
411 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
412 wmw->openParms.lpstrElementName = fn;
414 if (strlenW(filename) > 0) {
415 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
416 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
418 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
419 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
421 if (wmw->hFile == 0) {
422 WARN("can't find file=%s!\n", debugstr_w(filename));
423 dwRet = MCIERR_FILE_NOT_FOUND;
427 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
429 /* make sure we're are the beginning of the file */
430 mmioSeek(wmw->hFile, 0, SEEK_SET);
432 /* first reading of this file. read the waveformat chunk */
433 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
434 dwRet = MCIERR_INVALID_FILE;
436 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
437 (LPSTR)&(lpckMainRIFF->ckid),
438 (LPSTR) &(lpckMainRIFF->fccType),
439 (lpckMainRIFF->cksize));
441 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
442 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
443 dwRet = MCIERR_INVALID_FILE;
445 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
453 /**************************************************************************
454 * WAVE_mciOpen [internal]
456 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
459 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
461 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
462 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
463 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
465 if (dwFlags & MCI_OPEN_SHAREABLE)
466 return MCIERR_HARDWARE;
468 if (wmw->nUseCount > 0) {
469 /* The driver is already opened on this channel
470 * Wave driver cannot be shared
472 return MCIERR_DEVICE_OPEN;
479 wmw->dwStatus = MCI_MODE_NOT_READY;
481 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
482 /* will be set by WAVE_mciOpenFile */
483 wmw->openParms.lpstrElementName = NULL;
485 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
487 if (dwFlags & MCI_OPEN_ELEMENT) {
488 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
489 /* could it be that (DWORD)lpOpenParms->lpstrElementName
490 * contains the hFile value ?
492 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
494 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
498 TRACE("hFile=%p\n", wmw->hFile);
500 if (dwRet == 0 && !wmw->lpWaveFormat)
501 dwRet = WAVE_mciDefaultFmt(wmw);
504 if (wmw->lpWaveFormat) {
505 switch (wmw->lpWaveFormat->wFormatTag) {
506 case WAVE_FORMAT_PCM:
507 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
508 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
509 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
510 wmw->lpWaveFormat->nAvgBytesPerSec,
511 wmw->lpWaveFormat->nSamplesPerSec *
512 wmw->lpWaveFormat->nBlockAlign);
513 wmw->lpWaveFormat->nAvgBytesPerSec =
514 wmw->lpWaveFormat->nSamplesPerSec *
515 wmw->lpWaveFormat->nBlockAlign;
522 wmw->dwStatus = MCI_MODE_STOP;
526 mmioClose(wmw->hFile, 0);
532 /**************************************************************************
533 * WAVE_mciCue [internal]
535 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
540 This routine is far from complete. At the moment only a check is done on the
541 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
544 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
549 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
551 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
553 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
556 /* always close elements ? */
557 if (wmw->hFile != 0) {
558 mmioClose(wmw->hFile, 0);
562 dwRet = MMSYSERR_NOERROR; /* assume success */
564 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
565 dwRet = waveOutClose(wmw->hWave);
566 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
568 } else if (wmw->fInput) {
569 dwRet = waveInClose(wmw->hWave);
570 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
574 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
577 /**************************************************************************
578 * WAVE_mciStop [internal]
580 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
583 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
585 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
587 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
589 /* wait for playback thread (if any) to exit before processing further */
590 switch (wmw->dwStatus) {
593 case MCI_MODE_RECORD:
595 int oldStat = wmw->dwStatus;
596 wmw->dwStatus = MCI_MODE_NOT_READY;
597 if (oldStat == MCI_MODE_PAUSE)
598 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
600 while (wmw->dwStatus != MCI_MODE_STOP)
608 wmw->dwStatus = MCI_MODE_STOP;
610 if ((dwFlags & MCI_NOTIFY) && lpParms) {
611 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
612 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
618 /**************************************************************************
619 * WAVE_mciClose [internal]
621 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
624 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
626 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
628 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
630 if (wmw->dwStatus != MCI_MODE_STOP) {
631 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
636 if (wmw->nUseCount == 0) {
637 if (wmw->hFile != 0) {
638 mmioClose(wmw->hFile, 0);
643 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
644 wmw->lpWaveFormat = NULL;
645 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
646 wmw->openParms.lpstrElementName = NULL;
648 if ((dwFlags & MCI_NOTIFY) && lpParms) {
649 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
650 wmw->openParms.wDeviceID,
651 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
657 /**************************************************************************
658 * WAVE_mciPlayCallback [internal]
660 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
661 DWORD_PTR dwInstance,
662 LPARAM dwParam1, LPARAM dwParam2)
664 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
671 InterlockedIncrement(&wmw->dwEventCount);
672 TRACE("Returning waveHdr=%lx\n", dwParam1);
673 SetEvent(wmw->hEvent);
676 ERR("Unknown uMsg=%d\n", uMsg);
680 /******************************************************************
681 * WAVE_mciPlayWaitDone
685 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
688 ResetEvent(wmw->hEvent);
689 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
692 InterlockedIncrement(&wmw->dwEventCount);
694 WaitForSingleObject(wmw->hEvent, INFINITE);
698 /**************************************************************************
699 * WAVE_mciPlay [internal]
701 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
704 LONG bufsize, count, left;
706 LPWAVEHDR waveHdr = NULL;
707 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
710 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
712 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
713 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
717 if (wmw->hFile == 0) {
718 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
719 return MCIERR_FILE_NOT_FOUND;
722 if (wmw->dwStatus == MCI_MODE_PAUSE) {
723 /* FIXME: parameters (start/end) in lpParams may not be used */
724 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
727 /** This function will be called again by a thread when async is used.
728 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
729 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
731 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
732 return MCIERR_INTERNAL;
735 wmw->dwStatus = MCI_MODE_PLAY;
737 if (!(dwFlags & MCI_WAIT)) {
738 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
739 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
743 if (lpParms && (dwFlags & MCI_FROM)) {
744 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
746 if (lpParms && (dwFlags & MCI_TO)) {
747 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
750 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
752 if (end <= wmw->dwPosition)
756 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
757 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
759 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
760 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
763 if (wmw->lpWaveFormat) {
764 switch (wmw->lpWaveFormat->wFormatTag) {
765 case WAVE_FORMAT_PCM:
766 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
767 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
768 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
769 wmw->lpWaveFormat->nAvgBytesPerSec,
770 wmw->lpWaveFormat->nSamplesPerSec *
771 wmw->lpWaveFormat->nBlockAlign);
772 wmw->lpWaveFormat->nAvgBytesPerSec =
773 wmw->lpWaveFormat->nSamplesPerSec *
774 wmw->lpWaveFormat->nBlockAlign;
780 TRACE("can't retrieve wave format %d\n", dwRet);
785 /* go back to beginning of chunk plus the requested position */
786 /* FIXME: I'm not sure this is correct, notably because some data linked to
787 * the decompression state machine will not be correctly initialized.
788 * try it this way (other way would be to decompress from 0 up to dwPosition
789 * and to start sending to hWave when dwPosition is reached)
791 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
793 /* By default the device will be opened for output, the MCI_CUE function is there to
794 * change from output to input and back
796 /* FIXME: how to choose between several output channels ? here mapper is forced */
797 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
798 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
801 TRACE("Can't open low level audio device %d\n", dwRet);
802 dwRet = MCIERR_DEVICE_OPEN;
807 /* make it so that 3 buffers per second are needed */
808 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
810 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
811 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
812 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
813 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
814 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
815 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
816 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
817 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
818 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
819 dwRet = MCIERR_INTERNAL;
824 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
825 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
826 wmw->dwEventCount = 1L; /* for first buffer */
828 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
830 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
831 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
832 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
833 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
836 /* count is always <= bufsize, so this is correct regarding the
837 * waveOutPrepareHeader function
839 waveHdr[whidx].dwBufferLength = count;
840 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
841 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
842 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
843 waveHdr[whidx].dwBytesRecorded);
844 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
846 wmw->dwPosition += count;
847 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
849 WAVE_mciPlayWaitDone(wmw);
853 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
855 /* just to get rid of some race conditions between play, stop and pause */
856 waveOutReset(wmw->hWave);
858 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
859 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
864 HeapFree(GetProcessHeap(), 0, waveHdr);
867 waveOutClose(wmw->hWave);
870 CloseHandle(wmw->hEvent);
872 if (lpParms && (dwFlags & MCI_NOTIFY)) {
873 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
874 wmw->openParms.wDeviceID,
875 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
878 wmw->dwStatus = MCI_MODE_STOP;
883 /**************************************************************************
884 * WAVE_mciPlayCallback [internal]
886 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
887 DWORD_PTR dwInstance,
888 LPARAM dwParam1, LPARAM dwParam2)
890 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
899 lpWaveHdr = (LPWAVEHDR) dwParam1;
901 InterlockedIncrement(&wmw->dwEventCount);
903 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
905 lpWaveHdr->dwFlags &= ~WHDR_DONE;
907 wmw->dwPosition += count;
908 /* else error reporting ?? */
909 if (wmw->dwStatus == MCI_MODE_RECORD)
911 /* Only queue up another buffer if we are recording. We could receive this
912 message also when waveInReset() is called, since it notifies on all wave
913 buffers that are outstanding. Queueing up more sometimes causes waveInClose
915 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
916 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
919 SetEvent(wmw->hEvent);
922 ERR("Unknown uMsg=%d\n", uMsg);
926 /******************************************************************
927 * bWAVE_mciRecordWaitDone
930 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
933 ResetEvent(wmw->hEvent);
934 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
937 InterlockedIncrement(&wmw->dwEventCount);
939 WaitForSingleObject(wmw->hEvent, INFINITE);
943 /**************************************************************************
944 * WAVE_mciRecord [internal]
946 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
949 DWORD dwRet = MMSYSERR_NOERROR;
951 LPWAVEHDR waveHdr = NULL;
952 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
954 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
956 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
957 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
959 /* FIXME : since there is no way to determine in which mode the device is
960 * open (recording/playback) automatically switch from a mode to another
964 if (wmw->dwStatus == MCI_MODE_PAUSE) {
965 /* FIXME: parameters (start/end) in lpParams may not be used */
966 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
969 /** This function will be called again by a thread when async is used.
970 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
971 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
973 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
974 return MCIERR_INTERNAL;
977 wmw->dwStatus = MCI_MODE_RECORD;
979 if (!(dwFlags & MCI_WAIT)) {
980 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
981 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
984 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
985 * we don't modify the wave part of an existing file (ie. we always erase an
986 * existing content, we don't overwrite)
988 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
989 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
990 if (dwRet != 0) return dwRet;
993 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
994 if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
997 if (lpParms && (dwFlags & MCI_FROM)) {
998 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1001 if (lpParms && (dwFlags & MCI_TO)) {
1002 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1005 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1007 if (end <= wmw->dwPosition)
1012 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1013 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1015 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1016 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1018 /* Go back to the beginning of the chunk plus the requested position */
1019 /* FIXME: I'm not sure this is correct, notably because some data linked to
1020 * the decompression state machine will not be correctly initialized.
1021 * Try it this way (other way would be to decompress from 0 up to dwPosition
1022 * and to start sending to hWave when dwPosition is reached).
1024 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1026 /* By default the device will be opened for output, the MCI_CUE function is there to
1027 * change from output to input and back
1029 /* FIXME: how to choose between several output channels ? here mapper is forced */
1030 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1031 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1033 if (dwRet != MMSYSERR_NOERROR) {
1034 TRACE("Can't open low level audio device %d\n", dwRet);
1035 dwRet = MCIERR_DEVICE_OPEN;
1040 /* make it so that 3 buffers per second are needed */
1041 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1043 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1044 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1045 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1046 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1047 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1048 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1049 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1051 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1052 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1053 dwRet = MCIERR_INTERNAL;
1057 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1058 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1059 dwRet = MCIERR_INTERNAL;
1063 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1064 wmw->dwEventCount = 1L; /* for first buffer */
1066 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1068 dwRet = waveInStart(wmw->hWave);
1070 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1071 WAVE_mciRecordWaitDone(wmw);
1074 /* needed so that the callback above won't add again the buffers returned by the reset */
1075 wmw->dwStatus = MCI_MODE_STOP;
1077 waveInReset(wmw->hWave);
1079 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1080 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1085 HeapFree(GetProcessHeap(), 0, waveHdr);
1088 waveInClose(wmw->hWave);
1091 CloseHandle(wmw->hEvent);
1093 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1094 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1095 wmw->openParms.wDeviceID,
1096 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1099 wmw->dwStatus = MCI_MODE_STOP;
1105 /**************************************************************************
1106 * WAVE_mciPause [internal]
1108 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1111 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1113 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1115 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1116 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1118 if (wmw->dwStatus == MCI_MODE_PLAY) {
1119 wmw->dwStatus = MCI_MODE_PAUSE;
1122 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1123 else dwRet = waveOutPause(wmw->hWave);
1125 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1128 /**************************************************************************
1129 * WAVE_mciResume [internal]
1131 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1133 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1136 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1138 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1140 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1141 wmw->dwStatus = MCI_MODE_PLAY;
1144 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1145 else dwRet = waveOutRestart(wmw->hWave);
1146 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1149 /**************************************************************************
1150 * WAVE_mciSeek [internal]
1152 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1155 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1157 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1159 if (lpParms == NULL) {
1160 ret = MCIERR_NULL_PARAMETER_BLOCK;
1161 } else if (wmw == NULL) {
1162 ret = MCIERR_INVALID_DEVICE_ID;
1164 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1166 if (dwFlags & MCI_SEEK_TO_START) {
1167 wmw->dwPosition = 0;
1168 } else if (dwFlags & MCI_SEEK_TO_END) {
1169 wmw->dwPosition = wmw->ckWaveData.cksize;
1170 } else if (dwFlags & MCI_TO) {
1171 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1173 WARN("dwFlag doesn't tell where to seek to...\n");
1174 return MCIERR_MISSING_PARAMETER;
1177 TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1179 if (dwFlags & MCI_NOTIFY) {
1180 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1181 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1187 /**************************************************************************
1188 * WAVE_mciSet [internal]
1190 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1192 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1194 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1196 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1197 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1199 if (dwFlags & MCI_SET_TIME_FORMAT) {
1200 switch (lpParms->dwTimeFormat) {
1201 case MCI_FORMAT_MILLISECONDS:
1202 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1203 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1205 case MCI_FORMAT_BYTES:
1206 TRACE("MCI_FORMAT_BYTES !\n");
1207 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1209 case MCI_FORMAT_SAMPLES:
1210 TRACE("MCI_FORMAT_SAMPLES !\n");
1211 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1214 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1215 return MCIERR_BAD_TIME_FORMAT;
1218 if (dwFlags & MCI_SET_VIDEO) {
1219 TRACE("No support for video !\n");
1220 return MCIERR_UNSUPPORTED_FUNCTION;
1222 if (dwFlags & MCI_SET_DOOR_OPEN) {
1223 TRACE("No support for door open !\n");
1224 return MCIERR_UNSUPPORTED_FUNCTION;
1226 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1227 TRACE("No support for door close !\n");
1228 return MCIERR_UNSUPPORTED_FUNCTION;
1230 if (dwFlags & MCI_SET_AUDIO) {
1231 if (dwFlags & MCI_SET_ON) {
1232 TRACE("MCI_SET_ON audio !\n");
1233 } else if (dwFlags & MCI_SET_OFF) {
1234 TRACE("MCI_SET_OFF audio !\n");
1236 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1237 return MCIERR_BAD_INTEGER;
1240 switch (lpParms->dwAudio)
1242 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1243 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1244 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1245 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1248 if (dwFlags & MCI_WAVE_INPUT)
1249 TRACE("MCI_WAVE_INPUT !\n");
1250 if (dwFlags & MCI_WAVE_OUTPUT)
1251 TRACE("MCI_WAVE_OUTPUT !\n");
1252 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1253 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1254 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1255 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1256 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1257 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1258 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1260 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1261 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1262 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1264 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1265 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1266 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1268 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1269 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1270 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1272 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1273 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1274 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1276 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1277 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1278 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1283 /**************************************************************************
1284 * WAVE_mciSave [internal]
1286 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1288 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1289 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1290 WPARAM wparam = MCI_NOTIFY_FAILURE;
1292 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1293 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1294 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1296 if (dwFlags & MCI_WAIT)
1298 FIXME("MCI_WAIT not implemented\n");
1300 WAVE_mciStop(wDevID, 0, NULL);
1302 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1303 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1305 ret = mmioClose(wmw->hFile, 0);
1309 If the destination file already exists, it has to be overwritten. (Behaviour
1310 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1311 my applications. We are making use of mmioRename, which WILL NOT overwrite
1312 the destination file (which is what Windows does, also verified in Win2K)
1313 So, lets delete the destination file before calling mmioRename. If the
1314 destination file DOESN'T exist, the delete will fail silently. Let's also be
1315 careful not to lose our previous error code.
1317 tmpRet = GetLastError();
1318 DeleteFileW (lpParms->lpfilename);
1319 SetLastError(tmpRet);
1321 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1322 ret = MMSYSERR_NOERROR;
1325 if (dwFlags & MCI_NOTIFY) {
1326 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1328 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1329 wmw->openParms.wDeviceID, wparam);
1332 if (ret == MMSYSERR_NOERROR)
1333 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1338 /**************************************************************************
1339 * WAVE_mciStatus [internal]
1341 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1343 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1346 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1347 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1348 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1350 if (dwFlags & MCI_STATUS_ITEM) {
1351 switch (lpParms->dwItem) {
1352 case MCI_STATUS_CURRENT_TRACK:
1353 lpParms->dwReturn = 1;
1354 TRACE("MCI_STATUS_CURRENT_TRACK => %u\n", lpParms->dwReturn);
1356 case MCI_STATUS_LENGTH:
1358 lpParms->dwReturn = 0;
1359 return MCIERR_UNSUPPORTED_FUNCTION;
1361 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1362 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1363 TRACE("MCI_STATUS_LENGTH => %u\n", lpParms->dwReturn);
1365 case MCI_STATUS_MODE:
1366 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1367 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1368 ret = MCI_RESOURCE_RETURNED;
1370 case MCI_STATUS_MEDIA_PRESENT:
1371 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1372 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1373 ret = MCI_RESOURCE_RETURNED;
1375 case MCI_STATUS_NUMBER_OF_TRACKS:
1376 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1377 lpParms->dwReturn = 1;
1378 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %u!\n", lpParms->dwReturn);
1380 case MCI_STATUS_POSITION:
1382 lpParms->dwReturn = 0;
1383 return MCIERR_UNSUPPORTED_FUNCTION;
1385 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1386 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1387 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1389 TRACE("MCI_STATUS_POSITION %s => %u\n",
1390 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1392 case MCI_STATUS_READY:
1393 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1394 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1395 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1396 ret = MCI_RESOURCE_RETURNED;
1398 case MCI_STATUS_TIME_FORMAT:
1399 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1400 TRACE("MCI_STATUS_TIME_FORMAT => %u\n", lpParms->dwReturn);
1401 ret = MCI_RESOURCE_RETURNED;
1403 case MCI_WAVE_INPUT:
1404 TRACE("MCI_WAVE_INPUT !\n");
1405 lpParms->dwReturn = 0;
1406 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1408 case MCI_WAVE_OUTPUT:
1409 TRACE("MCI_WAVE_OUTPUT !\n");
1412 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1413 lpParms->dwReturn = id;
1415 lpParms->dwReturn = 0;
1416 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1420 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1422 lpParms->dwReturn = 0;
1423 return MCIERR_UNSUPPORTED_FUNCTION;
1425 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1426 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %u!\n", lpParms->dwReturn);
1428 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1430 lpParms->dwReturn = 0;
1431 return MCIERR_UNSUPPORTED_FUNCTION;
1433 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1434 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %u!\n", lpParms->dwReturn);
1436 case MCI_WAVE_STATUS_BLOCKALIGN:
1438 lpParms->dwReturn = 0;
1439 return MCIERR_UNSUPPORTED_FUNCTION;
1441 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1442 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %u!\n", lpParms->dwReturn);
1444 case MCI_WAVE_STATUS_CHANNELS:
1446 lpParms->dwReturn = 0;
1447 return MCIERR_UNSUPPORTED_FUNCTION;
1449 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1450 TRACE("MCI_WAVE_STATUS_CHANNELS => %u!\n", lpParms->dwReturn);
1452 case MCI_WAVE_STATUS_FORMATTAG:
1454 lpParms->dwReturn = 0;
1455 return MCIERR_UNSUPPORTED_FUNCTION;
1457 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1458 TRACE("MCI_WAVE_FORMATTAG => %u!\n", lpParms->dwReturn);
1460 case MCI_WAVE_STATUS_LEVEL:
1461 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1462 lpParms->dwReturn = 0xAAAA5555;
1464 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1466 lpParms->dwReturn = 0;
1467 return MCIERR_UNSUPPORTED_FUNCTION;
1469 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1470 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %u!\n", lpParms->dwReturn);
1473 WARN("unknown command %08X !\n", lpParms->dwItem);
1474 return MCIERR_UNRECOGNIZED_COMMAND;
1477 if (dwFlags & MCI_NOTIFY) {
1478 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1479 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1484 /**************************************************************************
1485 * WAVE_mciGetDevCaps [internal]
1487 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1488 LPMCI_GETDEVCAPS_PARMS lpParms)
1490 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1493 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1495 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1496 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1498 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1499 switch(lpParms->dwItem) {
1500 case MCI_GETDEVCAPS_DEVICE_TYPE:
1501 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1502 ret = MCI_RESOURCE_RETURNED;
1504 case MCI_GETDEVCAPS_HAS_AUDIO:
1505 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1506 ret = MCI_RESOURCE_RETURNED;
1508 case MCI_GETDEVCAPS_HAS_VIDEO:
1509 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1510 ret = MCI_RESOURCE_RETURNED;
1512 case MCI_GETDEVCAPS_USES_FILES:
1513 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1514 ret = MCI_RESOURCE_RETURNED;
1516 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1517 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1518 ret = MCI_RESOURCE_RETURNED;
1520 case MCI_GETDEVCAPS_CAN_RECORD:
1521 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1522 ret = MCI_RESOURCE_RETURNED;
1524 case MCI_GETDEVCAPS_CAN_EJECT:
1525 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1526 ret = MCI_RESOURCE_RETURNED;
1528 case MCI_GETDEVCAPS_CAN_PLAY:
1529 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1530 ret = MCI_RESOURCE_RETURNED;
1532 case MCI_GETDEVCAPS_CAN_SAVE:
1533 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1534 ret = MCI_RESOURCE_RETURNED;
1536 case MCI_WAVE_GETDEVCAPS_INPUTS:
1537 lpParms->dwReturn = 1;
1539 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1540 lpParms->dwReturn = 1;
1543 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1544 return MCIERR_UNRECOGNIZED_COMMAND;
1547 WARN("No GetDevCaps-Item !\n");
1548 return MCIERR_UNRECOGNIZED_COMMAND;
1553 /**************************************************************************
1554 * WAVE_mciInfo [internal]
1556 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1560 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1562 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1564 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1565 ret = MCIERR_NULL_PARAMETER_BLOCK;
1566 } else if (wmw == NULL) {
1567 ret = MCIERR_INVALID_DEVICE_ID;
1569 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1570 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1571 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1573 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1575 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1576 case MCI_INFO_PRODUCT: str = wszAudio; break;
1577 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1578 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1579 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1581 WARN("Don't know this info command (%u)\n", dwFlags);
1582 ret = MCIERR_UNRECOGNIZED_COMMAND;
1586 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1587 ret = MCIERR_PARAM_OVERFLOW;
1589 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1592 lpParms->lpstrReturn[0] = 0;
1598 /**************************************************************************
1599 * DriverProc (MCIWAVE.@)
1601 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1602 LPARAM dwParam1, LPARAM dwParam2)
1604 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1605 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1608 case DRV_LOAD: return 1;
1609 case DRV_FREE: return 1;
1610 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1611 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1612 case DRV_ENABLE: return 1;
1613 case DRV_DISABLE: return 1;
1614 case DRV_QUERYCONFIGURE: return 1;
1615 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1616 case DRV_INSTALL: return DRVCNF_RESTART;
1617 case DRV_REMOVE: return DRVCNF_RESTART;
1620 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1623 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1624 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1625 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1626 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1627 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1628 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1629 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1630 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1631 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1632 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1633 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1634 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1635 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1636 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1637 /* commands that should be supported */
1652 FIXME("Unsupported yet command [%u]\n", wMsg);
1655 TRACE("Unsupported command [%u]\n", wMsg);
1657 /* option which can be silenced */
1662 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1665 FIXME("is probably wrong msg [%u]\n", wMsg);
1666 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1668 return MCIERR_UNRECOGNIZED_COMMAND;