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
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
41 int nUseCount; /* Incremented for each shared open */
42 BOOL fShareable; /* TRUE if first open was shareable */
43 HMMIO hFile; /* mmio file handle open as Element */
44 MCI_WAVE_OPEN_PARMSW openParms;
46 LPWAVEFORMATEX lpWaveFormat;
47 BOOL fInput; /* FALSE = Output, TRUE = Input */
48 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
49 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
50 DWORD dwPosition; /* position in bytes in chunk */
51 HANDLE hEvent; /* for synchronization */
52 LONG dwEventCount; /* for synchronization */
53 MMCKINFO ckMainRIFF; /* main RIFF chunk */
54 MMCKINFO ckWaveData; /* data chunk */
57 /* ===================================================================
58 * ===================================================================
59 * FIXME: should be using the new mmThreadXXXX functions from WINMM
61 * it would require to add a wine internal flag to mmThreadCreate
62 * in order to pass a 32 bit function instead of a 16 bit one
63 * ===================================================================
64 * =================================================================== */
73 /**************************************************************************
74 * MCI_SCAStarter [internal]
76 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
78 struct SCA* sca = (struct SCA*)arg;
81 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
82 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
83 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
84 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
85 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
86 HeapFree(GetProcessHeap(), 0, sca);
88 WARN("Should not happen ? what's wrong\n");
89 /* should not go after this point */
93 /**************************************************************************
94 * MCI_SendCommandAsync [internal]
96 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
97 DWORD dwParam2, UINT size)
100 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
103 return MCIERR_OUT_OF_MEMORY;
105 sca->wDevID = wDevID;
107 sca->dwParam1 = dwParam1;
109 if (size && dwParam2) {
110 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
111 /* copy structure passed by program in dwParam2 to be sure
112 * we can still use it whatever the program does
114 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
116 sca->dwParam2 = dwParam2;
119 if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
120 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
121 return MCI_SCAStarter(&sca);
123 SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
128 /*======================================================================*
129 * MCI WAVE implemantation *
130 *======================================================================*/
132 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
134 /**************************************************************************
135 * MCIWAVE_drvOpen [internal]
137 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
141 if (modp == NULL) return 0xFFFFFFFF;
143 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
148 wmw->wDevID = modp->wDeviceID;
149 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
150 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
151 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
153 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
154 wmw->wfxRef.nChannels = 1; /* MONO */
155 wmw->wfxRef.nSamplesPerSec = 11025;
156 wmw->wfxRef.nAvgBytesPerSec = 11025;
157 wmw->wfxRef.nBlockAlign = 1;
158 wmw->wfxRef.wBitsPerSample = 8;
159 wmw->wfxRef.cbSize = 0; /* don't care */
161 return modp->wDeviceID;
164 /**************************************************************************
165 * MCIWAVE_drvClose [internal]
167 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
169 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
172 HeapFree(GetProcessHeap(), 0, wmw);
173 mciSetDriverData(dwDevID, 0);
176 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
179 /**************************************************************************
180 * WAVE_mciGetOpenDev [internal]
182 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
184 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
186 if (wmw == NULL || wmw->nUseCount == 0) {
187 WARN("Invalid wDevID=%u\n", wDevID);
193 /**************************************************************************
194 * WAVE_ConvertByteToTimeFormat [internal]
196 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
200 switch (wmw->dwMciTimeFormat) {
201 case MCI_FORMAT_MILLISECONDS:
202 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
204 case MCI_FORMAT_BYTES:
207 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
208 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
211 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
213 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
218 /**************************************************************************
219 * WAVE_ConvertTimeFormatToByte [internal]
221 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
225 switch (wmw->dwMciTimeFormat) {
226 case MCI_FORMAT_MILLISECONDS:
227 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
229 case MCI_FORMAT_BYTES:
232 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
233 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
236 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
238 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
242 /**************************************************************************
243 * WAVE_mciReadFmt [internal]
245 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
250 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
251 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
252 return MCIERR_INVALID_FILE;
253 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX\n",
254 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
256 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
257 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
258 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
259 if (r < sizeof(WAVEFORMAT))
260 return MCIERR_INVALID_FILE;
262 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
263 TRACE("nChannels=%d\n", wmw->lpWaveFormat->nChannels);
264 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
265 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
266 TRACE("nBlockAlign=%d\n", wmw->lpWaveFormat->nBlockAlign);
267 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
268 if (r >= (long)sizeof(WAVEFORMATEX))
269 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
271 mmioAscend(wmw->hFile, &mmckInfo, 0);
272 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
273 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
274 TRACE("can't find data chunk\n");
275 return MCIERR_INVALID_FILE;
277 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX\n",
278 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
279 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
280 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
285 /**************************************************************************
286 * WAVE_mciDefaultFmt [internal]
288 static DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
290 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
291 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
293 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
294 wmw->lpWaveFormat->nChannels = 1;
295 wmw->lpWaveFormat->nSamplesPerSec = 44000;
296 wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
297 wmw->lpWaveFormat->nBlockAlign = 1;
298 wmw->lpWaveFormat->wBitsPerSample = 8;
303 /**************************************************************************
304 * WAVE_mciCreateRIFFSkeleton [internal]
306 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
308 MMCKINFO ckWaveFormat;
309 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
310 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
312 lpckRIFF->ckid = FOURCC_RIFF;
313 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
314 lpckRIFF->cksize = 0;
316 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
319 ckWaveFormat.fccType = 0;
320 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
321 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
323 if (!wmw->lpWaveFormat)
325 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
326 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
327 memcpy(wmw->lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
330 /* we can only record PCM files... there is no way in the MCI API to specify
331 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
334 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
337 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
340 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
343 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
346 lpckWaveData->cksize = 0;
347 lpckWaveData->fccType = 0;
348 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
350 /* create data chunk */
351 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
357 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
358 wmw->lpWaveFormat = NULL;
359 return MCIERR_INVALID_FILE;
362 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
364 WCHAR szTmpPath[MAX_PATH];
366 DWORD dwRet = MMSYSERR_NOERROR;
373 if (!GetTempPathW(sizeof(szTmpPath), szTmpPath)) {
374 WARN("can't retrieve temp path!\n");
375 return MCIERR_FILE_NOT_FOUND;
378 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
380 MAX_PATH * sizeof(WCHAR));
381 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
382 WARN("can't retrieve temp file name!\n");
383 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
384 return MCIERR_FILE_NOT_FOUND;
387 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
389 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
391 *hFile = mmioOpenW(*pszTmpFileName, NULL,
392 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
395 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
396 /* temporary file could not be created. clean filename. */
397 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
398 dwRet = MCIERR_FILE_NOT_FOUND;
404 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
406 LRESULT dwRet = MMSYSERR_NOERROR;
409 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
410 if (!fn) return MCIERR_OUT_OF_MEMORY;
411 strcpyW(fn, filename);
412 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
413 wmw->openParms.lpstrElementName = fn;
415 if (strlenW(filename) > 0) {
416 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
417 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
419 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
420 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
422 if (wmw->hFile == 0) {
423 WARN("can't find file=%s!\n", debugstr_w(filename));
424 dwRet = MCIERR_FILE_NOT_FOUND;
428 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
430 /* make sure we're are the beginning of the file */
431 mmioSeek(wmw->hFile, 0, SEEK_SET);
433 /* first reading of this file. read the waveformat chunk */
434 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
435 dwRet = MCIERR_INVALID_FILE;
437 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX\n",
438 (LPSTR)&(lpckMainRIFF->ckid),
439 (LPSTR) &(lpckMainRIFF->fccType),
440 (lpckMainRIFF->cksize));
442 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
443 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
444 dwRet = MCIERR_INVALID_FILE;
446 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
454 /**************************************************************************
455 * WAVE_mciOpen [internal]
457 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
460 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
462 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
463 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
464 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
466 if (dwFlags & MCI_OPEN_SHAREABLE)
467 return MCIERR_HARDWARE;
469 if (wmw->nUseCount > 0) {
470 /* The driver is already opened on this channel
471 * Wave driver cannot be shared
473 return MCIERR_DEVICE_OPEN;
480 wmw->dwStatus = MCI_MODE_NOT_READY;
482 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
483 /* will be set by WAVE_mciOpenFile */
484 wmw->openParms.lpstrElementName = NULL;
486 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
488 if (dwFlags & MCI_OPEN_ELEMENT) {
489 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
490 /* could it be that (DWORD)lpOpenParms->lpstrElementName
491 * contains the hFile value ?
493 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
495 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
499 TRACE("hFile=%p\n", wmw->hFile);
501 if (dwRet == 0 && !wmw->lpWaveFormat)
502 dwRet = WAVE_mciDefaultFmt(wmw);
505 if (wmw->lpWaveFormat) {
506 switch (wmw->lpWaveFormat->wFormatTag) {
507 case WAVE_FORMAT_PCM:
508 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
509 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
510 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
511 wmw->lpWaveFormat->nAvgBytesPerSec,
512 wmw->lpWaveFormat->nSamplesPerSec *
513 wmw->lpWaveFormat->nBlockAlign);
514 wmw->lpWaveFormat->nAvgBytesPerSec =
515 wmw->lpWaveFormat->nSamplesPerSec *
516 wmw->lpWaveFormat->nBlockAlign;
523 wmw->dwStatus = MCI_MODE_STOP;
527 mmioClose(wmw->hFile, 0);
533 /**************************************************************************
534 * WAVE_mciCue [internal]
536 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
541 This routine is far from complete. At the moment only a check is done on the
542 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
545 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
550 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
552 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
554 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
557 /* always close elements ? */
558 if (wmw->hFile != 0) {
559 mmioClose(wmw->hFile, 0);
563 dwRet = MMSYSERR_NOERROR; /* assume success */
565 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
566 dwRet = waveOutClose(wmw->hWave);
567 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
569 } else if (wmw->fInput) {
570 dwRet = waveInClose(wmw->hWave);
571 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
575 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
578 /**************************************************************************
579 * WAVE_mciStop [internal]
581 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
584 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
586 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
588 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
590 /* wait for playback thread (if any) to exit before processing further */
591 switch (wmw->dwStatus) {
594 case MCI_MODE_RECORD:
596 int oldStat = wmw->dwStatus;
597 wmw->dwStatus = MCI_MODE_NOT_READY;
598 if (oldStat == MCI_MODE_PAUSE)
599 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
601 while (wmw->dwStatus != MCI_MODE_STOP)
609 wmw->dwStatus = MCI_MODE_STOP;
611 if ((dwFlags & MCI_NOTIFY) && lpParms) {
612 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
613 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
619 /**************************************************************************
620 * WAVE_mciClose [internal]
622 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
625 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
627 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
629 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
631 if (wmw->dwStatus != MCI_MODE_STOP) {
632 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
637 if (wmw->nUseCount == 0) {
638 if (wmw->hFile != 0) {
639 mmioClose(wmw->hFile, 0);
644 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
645 wmw->lpWaveFormat = NULL;
646 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
647 wmw->openParms.lpstrElementName = NULL;
649 if ((dwFlags & MCI_NOTIFY) && lpParms) {
650 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
651 wmw->openParms.wDeviceID,
652 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
658 /**************************************************************************
659 * WAVE_mciPlayCallback [internal]
661 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
662 DWORD_PTR dwInstance,
663 LPARAM dwParam1, LPARAM dwParam2)
665 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
672 InterlockedIncrement(&wmw->dwEventCount);
673 TRACE("Returning waveHdr=%lx\n", dwParam1);
674 SetEvent(wmw->hEvent);
677 ERR("Unknown uMsg=%d\n", uMsg);
681 /******************************************************************
682 * WAVE_mciPlayWaitDone
686 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
689 ResetEvent(wmw->hEvent);
690 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
693 InterlockedIncrement(&wmw->dwEventCount);
695 WaitForSingleObject(wmw->hEvent, INFINITE);
699 /**************************************************************************
700 * WAVE_mciPlay [internal]
702 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
705 LONG bufsize, count, left;
707 LPWAVEHDR waveHdr = NULL;
708 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
711 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
713 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
714 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
718 if (wmw->hFile == 0) {
719 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
720 return MCIERR_FILE_NOT_FOUND;
723 if (wmw->dwStatus == MCI_MODE_PAUSE) {
724 /* FIXME: parameters (start/end) in lpParams may not be used */
725 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
728 /** This function will be called again by a thread when async is used.
729 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
730 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
732 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
733 return MCIERR_INTERNAL;
736 wmw->dwStatus = MCI_MODE_PLAY;
738 if (!(dwFlags & MCI_WAIT)) {
739 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
740 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
744 if (lpParms && (dwFlags & MCI_FROM)) {
745 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
747 if (lpParms && (dwFlags & MCI_TO)) {
748 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
751 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
753 if (end <= wmw->dwPosition)
757 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
758 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
760 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
761 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
764 if (wmw->lpWaveFormat) {
765 switch (wmw->lpWaveFormat->wFormatTag) {
766 case WAVE_FORMAT_PCM:
767 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
768 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
769 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
770 wmw->lpWaveFormat->nAvgBytesPerSec,
771 wmw->lpWaveFormat->nSamplesPerSec *
772 wmw->lpWaveFormat->nBlockAlign);
773 wmw->lpWaveFormat->nAvgBytesPerSec =
774 wmw->lpWaveFormat->nSamplesPerSec *
775 wmw->lpWaveFormat->nBlockAlign;
781 TRACE("can't retrieve wave format %ld\n", dwRet);
786 /* go back to beginning of chunk plus the requested position */
787 /* FIXME: I'm not sure this is correct, notably because some data linked to
788 * the decompression state machine will not be correcly initialized.
789 * try it this way (other way would be to decompress from 0 up to dwPosition
790 * and to start sending to hWave when dwPosition is reached)
792 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
794 /* By default the device will be opened for output, the MCI_CUE function is there to
795 * change from output to input and back
797 /* FIXME: how to choose between several output channels ? here mapper is forced */
798 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
799 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
802 TRACE("Can't open low level audio device %ld\n", dwRet);
803 dwRet = MCIERR_DEVICE_OPEN;
808 /* make it so that 3 buffers per second are needed */
809 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
811 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
812 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
813 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
814 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
815 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
816 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
817 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
818 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
819 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
820 dwRet = MCIERR_INTERNAL;
825 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
826 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
827 wmw->dwEventCount = 1L; /* for first buffer */
829 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
831 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
832 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
833 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
834 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
837 /* count is always <= bufsize, so this is correct regarding the
838 * waveOutPrepareHeader function
840 waveHdr[whidx].dwBufferLength = count;
841 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
842 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
843 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
844 waveHdr[whidx].dwBytesRecorded);
845 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
847 wmw->dwPosition += count;
848 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
850 WAVE_mciPlayWaitDone(wmw);
854 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
856 /* just to get rid of some race conditions between play, stop and pause */
857 waveOutReset(wmw->hWave);
859 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
860 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
865 HeapFree(GetProcessHeap(), 0, waveHdr);
868 waveOutClose(wmw->hWave);
871 CloseHandle(wmw->hEvent);
873 if (lpParms && (dwFlags & MCI_NOTIFY)) {
874 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
875 wmw->openParms.wDeviceID,
876 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
879 wmw->dwStatus = MCI_MODE_STOP;
884 /**************************************************************************
885 * WAVE_mciPlayCallback [internal]
887 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
888 DWORD_PTR dwInstance,
889 LPARAM dwParam1, LPARAM dwParam2)
891 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
900 lpWaveHdr = (LPWAVEHDR) dwParam1;
902 InterlockedIncrement(&wmw->dwEventCount);
904 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
906 lpWaveHdr->dwFlags &= ~WHDR_DONE;
908 wmw->dwPosition += count;
909 /* else error reporting ?? */
910 if (wmw->dwStatus == MCI_MODE_RECORD)
912 /* Only queue up another buffer if we are recording. We could receive this
913 message also when waveInReset() is called, since it notifies on all wave
914 buffers that are outstanding. Queueing up more sometimes causes waveInClose
916 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
917 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
920 SetEvent(wmw->hEvent);
923 ERR("Unknown uMsg=%d\n", uMsg);
927 /******************************************************************
928 * bWAVE_mciRecordWaitDone
931 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
934 ResetEvent(wmw->hEvent);
935 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
938 InterlockedIncrement(&wmw->dwEventCount);
940 WaitForSingleObject(wmw->hEvent, INFINITE);
944 /**************************************************************************
945 * WAVE_mciRecord [internal]
947 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
950 DWORD dwRet = MMSYSERR_NOERROR;
952 LPWAVEHDR waveHdr = NULL;
953 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
955 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
957 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
958 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
960 /* FIXME : since there is no way to determine in which mode the device is
961 * open (recording/playback) automatically switch from a mode to another
965 if (wmw->dwStatus == MCI_MODE_PAUSE) {
966 /* FIXME: parameters (start/end) in lpParams may not be used */
967 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
970 /** This function will be called again by a thread when async is used.
971 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
972 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
974 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
975 return MCIERR_INTERNAL;
978 wmw->dwStatus = MCI_MODE_RECORD;
980 if (!(dwFlags & MCI_WAIT)) {
981 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
982 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
985 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
986 * we don't modify the wave part of an existing file (ie. we always erase an
987 * existing content, we don't overwrite)
989 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
990 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
991 if (dwRet != 0) return dwRet;
994 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
995 if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
998 if (lpParms && (dwFlags & MCI_FROM)) {
999 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1002 if (lpParms && (dwFlags & MCI_TO)) {
1003 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1006 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
1008 if (end <= wmw->dwPosition)
1013 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1014 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1016 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1017 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1019 /* go back to beginning of chunk plus the requested position */
1020 /* FIXME: I'm not sure this is correct, notably because some data linked to
1021 * the decompression state machine will not be correcly initialized.
1022 * try it this way (other way would be to decompress from 0 up to dwPosition
1023 * and to start sending to hWave when dwPosition is reached)
1025 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1027 /* By default the device will be opened for output, the MCI_CUE function is there to
1028 * change from output to input and back
1030 /* FIXME: how to choose between several output channels ? here mapper is forced */
1031 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1032 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1034 if (dwRet != MMSYSERR_NOERROR) {
1035 TRACE("Can't open low level audio device %ld\n", dwRet);
1036 dwRet = MCIERR_DEVICE_OPEN;
1041 /* make it so that 3 buffers per second are needed */
1042 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1044 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1045 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1046 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1047 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1048 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1049 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1050 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1052 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1053 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1054 dwRet = MCIERR_INTERNAL;
1058 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1059 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1060 dwRet = MCIERR_INTERNAL;
1064 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1065 wmw->dwEventCount = 1L; /* for first buffer */
1067 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1069 dwRet = waveInStart(wmw->hWave);
1071 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1072 WAVE_mciRecordWaitDone(wmw);
1075 /* needed so that the callback above won't add again the buffers returned by the reset */
1076 wmw->dwStatus = MCI_MODE_STOP;
1078 waveInReset(wmw->hWave);
1080 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1081 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1086 HeapFree(GetProcessHeap(), 0, waveHdr);
1089 waveInClose(wmw->hWave);
1092 CloseHandle(wmw->hEvent);
1094 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1095 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1096 wmw->openParms.wDeviceID,
1097 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1100 wmw->dwStatus = MCI_MODE_STOP;
1106 /**************************************************************************
1107 * WAVE_mciPause [internal]
1109 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1112 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1114 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1116 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1117 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1119 if (wmw->dwStatus == MCI_MODE_PLAY) {
1120 wmw->dwStatus = MCI_MODE_PAUSE;
1123 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1124 else dwRet = waveOutPause(wmw->hWave);
1126 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1129 /**************************************************************************
1130 * WAVE_mciResume [internal]
1132 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1134 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1137 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1139 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1141 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1142 wmw->dwStatus = MCI_MODE_PLAY;
1145 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1146 else dwRet = waveOutRestart(wmw->hWave);
1147 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1150 /**************************************************************************
1151 * WAVE_mciSeek [internal]
1153 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1156 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1158 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1160 if (lpParms == NULL) {
1161 ret = MCIERR_NULL_PARAMETER_BLOCK;
1162 } else if (wmw == NULL) {
1163 ret = MCIERR_INVALID_DEVICE_ID;
1165 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1167 if (dwFlags & MCI_SEEK_TO_START) {
1168 wmw->dwPosition = 0;
1169 } else if (dwFlags & MCI_SEEK_TO_END) {
1170 wmw->dwPosition = wmw->ckWaveData.cksize;
1171 } else if (dwFlags & MCI_TO) {
1172 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1174 WARN("dwFlag doesn't tell where to seek to...\n");
1175 return MCIERR_MISSING_PARAMETER;
1178 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1180 if (dwFlags & MCI_NOTIFY) {
1181 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1182 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1188 /**************************************************************************
1189 * WAVE_mciSet [internal]
1191 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1193 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1195 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1197 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1198 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1200 if (dwFlags & MCI_SET_TIME_FORMAT) {
1201 switch (lpParms->dwTimeFormat) {
1202 case MCI_FORMAT_MILLISECONDS:
1203 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1204 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1206 case MCI_FORMAT_BYTES:
1207 TRACE("MCI_FORMAT_BYTES !\n");
1208 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1210 case MCI_FORMAT_SAMPLES:
1211 TRACE("MCI_FORMAT_SAMPLES !\n");
1212 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1215 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1216 return MCIERR_BAD_TIME_FORMAT;
1219 if (dwFlags & MCI_SET_VIDEO) {
1220 TRACE("No support for video !\n");
1221 return MCIERR_UNSUPPORTED_FUNCTION;
1223 if (dwFlags & MCI_SET_DOOR_OPEN) {
1224 TRACE("No support for door open !\n");
1225 return MCIERR_UNSUPPORTED_FUNCTION;
1227 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1228 TRACE("No support for door close !\n");
1229 return MCIERR_UNSUPPORTED_FUNCTION;
1231 if (dwFlags & MCI_SET_AUDIO) {
1232 if (dwFlags & MCI_SET_ON) {
1233 TRACE("MCI_SET_ON audio !\n");
1234 } else if (dwFlags & MCI_SET_OFF) {
1235 TRACE("MCI_SET_OFF audio !\n");
1237 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1238 return MCIERR_BAD_INTEGER;
1241 switch (lpParms->dwAudio)
1243 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1244 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1245 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1246 default: WARN("Unknown audio channel %lu\n", lpParms->dwAudio); break;
1249 if (dwFlags & MCI_WAVE_INPUT)
1250 TRACE("MCI_WAVE_INPUT !\n");
1251 if (dwFlags & MCI_WAVE_OUTPUT)
1252 TRACE("MCI_WAVE_OUTPUT !\n");
1253 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1254 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1255 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1256 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1257 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1258 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1259 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1261 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1262 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1263 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1265 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1266 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1267 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1269 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1270 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1271 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1273 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1274 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1275 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1277 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1278 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1279 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1284 /**************************************************************************
1285 * WAVE_mciSave [internal]
1287 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1289 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1290 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1291 WPARAM wparam = MCI_NOTIFY_FAILURE;
1293 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1294 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1295 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1297 if (dwFlags & MCI_WAIT)
1299 FIXME("MCI_WAIT not implemented\n");
1301 WAVE_mciStop(wDevID, 0, NULL);
1303 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1304 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1306 ret = mmioClose(wmw->hFile, 0);
1310 If the destination file already exists, it has to be overwritten. (Behaviour
1311 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1312 my applications. We are making use of mmioRename, which WILL NOT overwrite
1313 the destination file (which is what Windows does, also verified in Win2K)
1314 So, lets delete the destination file before calling mmioRename. If the
1315 destination file DOESN'T exist, the delete will fail silently. Let's also be
1316 careful not to lose our previous error code.
1318 tmpRet = GetLastError();
1319 DeleteFileW (lpParms->lpfilename);
1320 SetLastError(tmpRet);
1322 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1323 ret = MMSYSERR_NOERROR;
1326 if (dwFlags & MCI_NOTIFY) {
1327 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1329 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1330 wmw->openParms.wDeviceID, wparam);
1333 if (ret == MMSYSERR_NOERROR)
1334 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1339 /**************************************************************************
1340 * WAVE_mciStatus [internal]
1342 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1344 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1347 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1348 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1349 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1351 if (dwFlags & MCI_STATUS_ITEM) {
1352 switch (lpParms->dwItem) {
1353 case MCI_STATUS_CURRENT_TRACK:
1354 lpParms->dwReturn = 1;
1355 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1357 case MCI_STATUS_LENGTH:
1359 lpParms->dwReturn = 0;
1360 return MCIERR_UNSUPPORTED_FUNCTION;
1362 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1363 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1364 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1366 case MCI_STATUS_MODE:
1367 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1368 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1369 ret = MCI_RESOURCE_RETURNED;
1371 case MCI_STATUS_MEDIA_PRESENT:
1372 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1373 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1374 ret = MCI_RESOURCE_RETURNED;
1376 case MCI_STATUS_NUMBER_OF_TRACKS:
1377 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1378 lpParms->dwReturn = 1;
1379 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1381 case MCI_STATUS_POSITION:
1383 lpParms->dwReturn = 0;
1384 return MCIERR_UNSUPPORTED_FUNCTION;
1386 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1387 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1388 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1390 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1391 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1393 case MCI_STATUS_READY:
1394 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1395 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1396 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1397 ret = MCI_RESOURCE_RETURNED;
1399 case MCI_STATUS_TIME_FORMAT:
1400 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1401 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1402 ret = MCI_RESOURCE_RETURNED;
1404 case MCI_WAVE_INPUT:
1405 TRACE("MCI_WAVE_INPUT !\n");
1406 lpParms->dwReturn = 0;
1407 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1409 case MCI_WAVE_OUTPUT:
1410 TRACE("MCI_WAVE_OUTPUT !\n");
1413 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1414 lpParms->dwReturn = id;
1416 lpParms->dwReturn = 0;
1417 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1421 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1423 lpParms->dwReturn = 0;
1424 return MCIERR_UNSUPPORTED_FUNCTION;
1426 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1427 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1429 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1431 lpParms->dwReturn = 0;
1432 return MCIERR_UNSUPPORTED_FUNCTION;
1434 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1435 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1437 case MCI_WAVE_STATUS_BLOCKALIGN:
1439 lpParms->dwReturn = 0;
1440 return MCIERR_UNSUPPORTED_FUNCTION;
1442 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1443 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1445 case MCI_WAVE_STATUS_CHANNELS:
1447 lpParms->dwReturn = 0;
1448 return MCIERR_UNSUPPORTED_FUNCTION;
1450 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1451 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1453 case MCI_WAVE_STATUS_FORMATTAG:
1455 lpParms->dwReturn = 0;
1456 return MCIERR_UNSUPPORTED_FUNCTION;
1458 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1459 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1461 case MCI_WAVE_STATUS_LEVEL:
1462 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1463 lpParms->dwReturn = 0xAAAA5555;
1465 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1467 lpParms->dwReturn = 0;
1468 return MCIERR_UNSUPPORTED_FUNCTION;
1470 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1471 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1474 WARN("unknown command %08lX !\n", lpParms->dwItem);
1475 return MCIERR_UNRECOGNIZED_COMMAND;
1478 if (dwFlags & MCI_NOTIFY) {
1479 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1480 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1485 /**************************************************************************
1486 * WAVE_mciGetDevCaps [internal]
1488 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1489 LPMCI_GETDEVCAPS_PARMS lpParms)
1491 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1494 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1496 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1497 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1499 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1500 switch(lpParms->dwItem) {
1501 case MCI_GETDEVCAPS_DEVICE_TYPE:
1502 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1503 ret = MCI_RESOURCE_RETURNED;
1505 case MCI_GETDEVCAPS_HAS_AUDIO:
1506 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1507 ret = MCI_RESOURCE_RETURNED;
1509 case MCI_GETDEVCAPS_HAS_VIDEO:
1510 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1511 ret = MCI_RESOURCE_RETURNED;
1513 case MCI_GETDEVCAPS_USES_FILES:
1514 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1515 ret = MCI_RESOURCE_RETURNED;
1517 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1518 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1519 ret = MCI_RESOURCE_RETURNED;
1521 case MCI_GETDEVCAPS_CAN_RECORD:
1522 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1523 ret = MCI_RESOURCE_RETURNED;
1525 case MCI_GETDEVCAPS_CAN_EJECT:
1526 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1527 ret = MCI_RESOURCE_RETURNED;
1529 case MCI_GETDEVCAPS_CAN_PLAY:
1530 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1531 ret = MCI_RESOURCE_RETURNED;
1533 case MCI_GETDEVCAPS_CAN_SAVE:
1534 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1535 ret = MCI_RESOURCE_RETURNED;
1537 case MCI_WAVE_GETDEVCAPS_INPUTS:
1538 lpParms->dwReturn = 1;
1540 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1541 lpParms->dwReturn = 1;
1544 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1545 return MCIERR_UNRECOGNIZED_COMMAND;
1548 WARN("No GetDevCaps-Item !\n");
1549 return MCIERR_UNRECOGNIZED_COMMAND;
1554 /**************************************************************************
1555 * WAVE_mciInfo [internal]
1557 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1561 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1563 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1565 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1566 ret = MCIERR_NULL_PARAMETER_BLOCK;
1567 } else if (wmw == NULL) {
1568 ret = MCIERR_INVALID_DEVICE_ID;
1570 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1571 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1572 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1574 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1576 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1577 case MCI_INFO_PRODUCT: str = wszAudio; break;
1578 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1579 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1580 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1582 WARN("Don't know this info command (%lu)\n", dwFlags);
1583 ret = MCIERR_UNRECOGNIZED_COMMAND;
1587 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1588 ret = MCIERR_PARAM_OVERFLOW;
1590 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1593 lpParms->lpstrReturn[0] = 0;
1599 /**************************************************************************
1600 * DriverProc (MCIWAVE.@)
1602 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1603 LPARAM dwParam1, LPARAM dwParam2)
1605 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1606 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1609 case DRV_LOAD: return 1;
1610 case DRV_FREE: return 1;
1611 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1612 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1613 case DRV_ENABLE: return 1;
1614 case DRV_DISABLE: return 1;
1615 case DRV_QUERYCONFIGURE: return 1;
1616 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1617 case DRV_INSTALL: return DRVCNF_RESTART;
1618 case DRV_REMOVE: return DRVCNF_RESTART;
1621 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1624 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1625 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1626 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1627 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1628 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1629 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1630 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1631 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1632 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1633 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1634 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1635 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1636 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1637 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1638 /* commands that should be supported */
1653 FIXME("Unsupported yet command [%u]\n", wMsg);
1656 TRACE("Unsupported command [%u]\n", wMsg);
1658 /* option which can be silenced */
1663 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1666 FIXME("is probably wrong msg [%u]\n", wMsg);
1667 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1669 return MCIERR_UNRECOGNIZED_COMMAND;