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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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));
484 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
486 if (dwFlags & MCI_OPEN_ELEMENT) {
487 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
488 /* could it be that (DWORD)lpOpenParms->lpstrElementName
489 * contains the hFile value ?
491 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
493 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
497 TRACE("hFile=%p\n", wmw->hFile);
499 if (dwRet == 0 && !wmw->lpWaveFormat)
500 dwRet = WAVE_mciDefaultFmt(wmw);
503 if (wmw->lpWaveFormat) {
504 switch (wmw->lpWaveFormat->wFormatTag) {
505 case WAVE_FORMAT_PCM:
506 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
507 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
508 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
509 wmw->lpWaveFormat->nAvgBytesPerSec,
510 wmw->lpWaveFormat->nSamplesPerSec *
511 wmw->lpWaveFormat->nBlockAlign);
512 wmw->lpWaveFormat->nAvgBytesPerSec =
513 wmw->lpWaveFormat->nSamplesPerSec *
514 wmw->lpWaveFormat->nBlockAlign;
521 wmw->dwStatus = MCI_MODE_STOP;
525 mmioClose(wmw->hFile, 0);
531 /**************************************************************************
532 * WAVE_mciCue [internal]
534 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
539 This routine is far from complete. At the moment only a check is done on the
540 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
543 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
548 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
550 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
552 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
555 /* always close elements ? */
556 if (wmw->hFile != 0) {
557 mmioClose(wmw->hFile, 0);
561 dwRet = MMSYSERR_NOERROR; /* assume success */
563 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
564 dwRet = waveOutClose(wmw->hWave);
565 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
567 } else if (wmw->fInput) {
568 dwRet = waveInClose(wmw->hWave);
569 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
573 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
576 /**************************************************************************
577 * WAVE_mciStop [internal]
579 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
582 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
584 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
586 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
588 /* wait for playback thread (if any) to exit before processing further */
589 switch (wmw->dwStatus) {
592 case MCI_MODE_RECORD:
594 int oldStat = wmw->dwStatus;
595 wmw->dwStatus = MCI_MODE_NOT_READY;
596 if (oldStat == MCI_MODE_PAUSE)
597 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
599 while (wmw->dwStatus != MCI_MODE_STOP)
607 wmw->dwStatus = MCI_MODE_STOP;
609 if ((dwFlags & MCI_NOTIFY) && lpParms) {
610 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
611 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
617 /**************************************************************************
618 * WAVE_mciClose [internal]
620 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
623 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
625 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
627 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
629 if (wmw->dwStatus != MCI_MODE_STOP) {
630 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
635 if (wmw->nUseCount == 0) {
636 if (wmw->hFile != 0) {
637 mmioClose(wmw->hFile, 0);
642 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
643 wmw->lpWaveFormat = NULL;
644 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
645 wmw->openParms.lpstrElementName = NULL;
647 if ((dwFlags & MCI_NOTIFY) && lpParms) {
648 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
649 wmw->openParms.wDeviceID,
650 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
656 /**************************************************************************
657 * WAVE_mciPlayCallback [internal]
659 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
660 DWORD_PTR dwInstance,
661 LPARAM dwParam1, LPARAM dwParam2)
663 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
670 InterlockedIncrement(&wmw->dwEventCount);
671 TRACE("Returning waveHdr=%lx\n", dwParam1);
672 SetEvent(wmw->hEvent);
675 ERR("Unknown uMsg=%d\n", uMsg);
679 /******************************************************************
680 * WAVE_mciPlayWaitDone
684 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
687 ResetEvent(wmw->hEvent);
688 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
691 InterlockedIncrement(&wmw->dwEventCount);
693 WaitForSingleObject(wmw->hEvent, INFINITE);
697 /**************************************************************************
698 * WAVE_mciPlay [internal]
700 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
703 LONG bufsize, count, left;
705 LPWAVEHDR waveHdr = NULL;
706 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
709 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
711 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
712 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
716 if (wmw->hFile == 0) {
717 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
718 return MCIERR_FILE_NOT_FOUND;
721 if (wmw->dwStatus == MCI_MODE_PAUSE) {
722 /* FIXME: parameters (start/end) in lpParams may not be used */
723 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
726 /** This function will be called again by a thread when async is used.
727 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
728 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
730 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
731 return MCIERR_INTERNAL;
734 wmw->dwStatus = MCI_MODE_PLAY;
736 if (!(dwFlags & MCI_WAIT)) {
737 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
738 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
742 if (lpParms && (dwFlags & MCI_FROM)) {
743 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
745 if (lpParms && (dwFlags & MCI_TO)) {
746 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
749 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
751 if (end <= wmw->dwPosition)
755 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
756 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
758 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
759 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
762 if (wmw->lpWaveFormat) {
763 switch (wmw->lpWaveFormat->wFormatTag) {
764 case WAVE_FORMAT_PCM:
765 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
766 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
767 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
768 wmw->lpWaveFormat->nAvgBytesPerSec,
769 wmw->lpWaveFormat->nSamplesPerSec *
770 wmw->lpWaveFormat->nBlockAlign);
771 wmw->lpWaveFormat->nAvgBytesPerSec =
772 wmw->lpWaveFormat->nSamplesPerSec *
773 wmw->lpWaveFormat->nBlockAlign;
779 TRACE("can't retrieve wave format %ld\n", dwRet);
784 /* go back to beginning of chunk plus the requested position */
785 /* FIXME: I'm not sure this is correct, notably because some data linked to
786 * the decompression state machine will not be correcly initialized.
787 * try it this way (other way would be to decompress from 0 up to dwPosition
788 * and to start sending to hWave when dwPosition is reached)
790 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
792 /* By default the device will be opened for output, the MCI_CUE function is there to
793 * change from output to input and back
795 /* FIXME: how to choose between several output channels ? here mapper is forced */
796 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
797 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
800 TRACE("Can't open low level audio device %ld\n", dwRet);
801 dwRet = MCIERR_DEVICE_OPEN;
806 /* make it so that 3 buffers per second are needed */
807 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
809 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
810 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
811 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
812 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
813 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
814 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
815 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
816 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
817 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
818 dwRet = MCIERR_INTERNAL;
823 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
824 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
825 wmw->dwEventCount = 1L; /* for first buffer */
827 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
829 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
830 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
831 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
832 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
835 /* count is always <= bufsize, so this is correct regarding the
836 * waveOutPrepareHeader function
838 waveHdr[whidx].dwBufferLength = count;
839 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
840 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
841 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
842 waveHdr[whidx].dwBytesRecorded);
843 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
845 wmw->dwPosition += count;
846 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
848 WAVE_mciPlayWaitDone(wmw);
852 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
854 /* just to get rid of some race conditions between play, stop and pause */
855 waveOutReset(wmw->hWave);
857 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
858 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
863 HeapFree(GetProcessHeap(), 0, waveHdr);
866 waveOutClose(wmw->hWave);
869 CloseHandle(wmw->hEvent);
871 if (lpParms && (dwFlags & MCI_NOTIFY)) {
872 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
873 wmw->openParms.wDeviceID,
874 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
877 wmw->dwStatus = MCI_MODE_STOP;
882 /**************************************************************************
883 * WAVE_mciPlayCallback [internal]
885 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
886 DWORD_PTR dwInstance,
887 LPARAM dwParam1, LPARAM dwParam2)
889 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
898 lpWaveHdr = (LPWAVEHDR) dwParam1;
900 InterlockedIncrement(&wmw->dwEventCount);
902 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
904 lpWaveHdr->dwFlags &= ~WHDR_DONE;
906 wmw->dwPosition += count;
907 /* else error reporting ?? */
908 if (wmw->dwStatus == MCI_MODE_RECORD)
910 /* Only queue up another buffer if we are recording. We could receive this
911 message also when waveInReset() is called, since it notifies on all wave
912 buffers that are outstanding. Queueing up more sometimes causes waveInClose
914 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
915 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
918 SetEvent(wmw->hEvent);
921 ERR("Unknown uMsg=%d\n", uMsg);
925 /******************************************************************
926 * bWAVE_mciRecordWaitDone
929 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
932 ResetEvent(wmw->hEvent);
933 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
936 InterlockedIncrement(&wmw->dwEventCount);
938 WaitForSingleObject(wmw->hEvent, INFINITE);
942 /**************************************************************************
943 * WAVE_mciRecord [internal]
945 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
948 DWORD dwRet = MMSYSERR_NOERROR;
950 LPWAVEHDR waveHdr = NULL;
951 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
953 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
955 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
956 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
958 /* FIXME : since there is no way to determine in which mode the device is
959 * open (recording/playback) automatically switch from a mode to another
963 if (wmw->dwStatus == MCI_MODE_PAUSE) {
964 /* FIXME: parameters (start/end) in lpParams may not be used */
965 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
968 /** This function will be called again by a thread when async is used.
969 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
970 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
972 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
973 return MCIERR_INTERNAL;
976 wmw->dwStatus = MCI_MODE_RECORD;
978 if (!(dwFlags & MCI_WAIT)) {
979 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
980 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
983 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
984 * we don't modify the wave part of an existing file (ie. we always erase an
985 * existing content, we don't overwrite)
987 HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
988 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
989 if (dwRet != 0) return dwRet;
992 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
993 if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
996 if (lpParms && (dwFlags & MCI_FROM)) {
997 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1000 if (lpParms && (dwFlags & MCI_TO)) {
1001 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1004 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
1006 if (end <= wmw->dwPosition)
1011 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1012 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1014 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1015 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1017 /* go back to beginning of chunk plus the requested position */
1018 /* FIXME: I'm not sure this is correct, notably because some data linked to
1019 * the decompression state machine will not be correcly initialized.
1020 * try it this way (other way would be to decompress from 0 up to dwPosition
1021 * and to start sending to hWave when dwPosition is reached)
1023 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1025 /* By default the device will be opened for output, the MCI_CUE function is there to
1026 * change from output to input and back
1028 /* FIXME: how to choose between several output channels ? here mapper is forced */
1029 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1030 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1032 if (dwRet != MMSYSERR_NOERROR) {
1033 TRACE("Can't open low level audio device %ld\n", dwRet);
1034 dwRet = MCIERR_DEVICE_OPEN;
1039 /* make it so that 3 buffers per second are needed */
1040 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1042 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1043 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1044 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1045 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1046 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1047 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1048 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1050 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1051 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1052 dwRet = MCIERR_INTERNAL;
1056 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1057 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1058 dwRet = MCIERR_INTERNAL;
1062 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1063 wmw->dwEventCount = 1L; /* for first buffer */
1065 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1067 dwRet = waveInStart(wmw->hWave);
1069 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1070 WAVE_mciRecordWaitDone(wmw);
1073 /* needed so that the callback above won't add again the buffers returned by the reset */
1074 wmw->dwStatus = MCI_MODE_STOP;
1076 waveInReset(wmw->hWave);
1078 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1079 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1084 HeapFree(GetProcessHeap(), 0, waveHdr);
1087 waveInClose(wmw->hWave);
1090 CloseHandle(wmw->hEvent);
1092 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1093 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1094 wmw->openParms.wDeviceID,
1095 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1098 wmw->dwStatus = MCI_MODE_STOP;
1104 /**************************************************************************
1105 * WAVE_mciPause [internal]
1107 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1110 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1112 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1114 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1115 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1117 if (wmw->dwStatus == MCI_MODE_PLAY) {
1118 wmw->dwStatus = MCI_MODE_PAUSE;
1121 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1122 else dwRet = waveOutPause(wmw->hWave);
1124 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1127 /**************************************************************************
1128 * WAVE_mciResume [internal]
1130 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1132 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1135 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1137 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1139 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1140 wmw->dwStatus = MCI_MODE_PLAY;
1143 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1144 else dwRet = waveOutRestart(wmw->hWave);
1145 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1148 /**************************************************************************
1149 * WAVE_mciSeek [internal]
1151 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1154 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1156 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1158 if (lpParms == NULL) {
1159 ret = MCIERR_NULL_PARAMETER_BLOCK;
1160 } else if (wmw == NULL) {
1161 ret = MCIERR_INVALID_DEVICE_ID;
1163 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1165 if (dwFlags & MCI_SEEK_TO_START) {
1166 wmw->dwPosition = 0;
1167 } else if (dwFlags & MCI_SEEK_TO_END) {
1168 wmw->dwPosition = wmw->ckWaveData.cksize;
1169 } else if (dwFlags & MCI_TO) {
1170 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1172 WARN("dwFlag doesn't tell where to seek to...\n");
1173 return MCIERR_MISSING_PARAMETER;
1176 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1178 if (dwFlags & MCI_NOTIFY) {
1179 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1180 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1186 /**************************************************************************
1187 * WAVE_mciSet [internal]
1189 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1191 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1193 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1195 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1196 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1198 if (dwFlags & MCI_SET_TIME_FORMAT) {
1199 switch (lpParms->dwTimeFormat) {
1200 case MCI_FORMAT_MILLISECONDS:
1201 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1202 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1204 case MCI_FORMAT_BYTES:
1205 TRACE("MCI_FORMAT_BYTES !\n");
1206 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1208 case MCI_FORMAT_SAMPLES:
1209 TRACE("MCI_FORMAT_SAMPLES !\n");
1210 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1213 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1214 return MCIERR_BAD_TIME_FORMAT;
1217 if (dwFlags & MCI_SET_VIDEO) {
1218 TRACE("No support for video !\n");
1219 return MCIERR_UNSUPPORTED_FUNCTION;
1221 if (dwFlags & MCI_SET_DOOR_OPEN) {
1222 TRACE("No support for door open !\n");
1223 return MCIERR_UNSUPPORTED_FUNCTION;
1225 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1226 TRACE("No support for door close !\n");
1227 return MCIERR_UNSUPPORTED_FUNCTION;
1229 if (dwFlags & MCI_SET_AUDIO) {
1230 if (dwFlags & MCI_SET_ON) {
1231 TRACE("MCI_SET_ON audio !\n");
1232 } else if (dwFlags & MCI_SET_OFF) {
1233 TRACE("MCI_SET_OFF audio !\n");
1235 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1236 return MCIERR_BAD_INTEGER;
1239 switch (lpParms->dwAudio)
1241 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1242 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1243 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1244 default: WARN("Unknown audio channel %lu\n", lpParms->dwAudio); break;
1247 if (dwFlags & MCI_WAVE_INPUT)
1248 TRACE("MCI_WAVE_INPUT !\n");
1249 if (dwFlags & MCI_WAVE_OUTPUT)
1250 TRACE("MCI_WAVE_OUTPUT !\n");
1251 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1252 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1253 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1254 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1255 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1256 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1257 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1259 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1260 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1261 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1263 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1264 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1265 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1267 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1268 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1269 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1271 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1272 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1273 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1275 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1276 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1277 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1282 /**************************************************************************
1283 * WAVE_mciSave [internal]
1285 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1287 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1288 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1289 WPARAM wparam = MCI_NOTIFY_FAILURE;
1291 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1292 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1293 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1295 if (dwFlags & MCI_WAIT)
1297 FIXME("MCI_WAIT not implemented\n");
1299 WAVE_mciStop(wDevID, 0, NULL);
1301 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1302 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1304 ret = mmioClose(wmw->hFile, 0);
1308 If the destination file already exists, it has to be overwritten. (Behaviour
1309 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1310 my applications. We are making use of mmioRename, which WILL NOT overwrite
1311 the destination file (which is what Windows does, also verified in Win2K)
1312 So, lets delete the destination file before calling mmioRename. If the
1313 destination file DOESN'T exist, the delete will fail silently. Let's also be
1314 careful not to lose our previous error code.
1316 tmpRet = GetLastError();
1317 DeleteFileW (lpParms->lpfilename);
1318 SetLastError(tmpRet);
1320 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1321 ret = MMSYSERR_NOERROR;
1324 if (dwFlags & MCI_NOTIFY) {
1325 if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1327 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1328 wmw->openParms.wDeviceID, wparam);
1331 if (ret == MMSYSERR_NOERROR)
1332 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1337 /**************************************************************************
1338 * WAVE_mciStatus [internal]
1340 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1342 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1345 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1346 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1347 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1349 if (dwFlags & MCI_STATUS_ITEM) {
1350 switch (lpParms->dwItem) {
1351 case MCI_STATUS_CURRENT_TRACK:
1352 lpParms->dwReturn = 1;
1353 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1355 case MCI_STATUS_LENGTH:
1357 lpParms->dwReturn = 0;
1358 return MCIERR_UNSUPPORTED_FUNCTION;
1360 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1361 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1362 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1364 case MCI_STATUS_MODE:
1365 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1366 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1367 ret = MCI_RESOURCE_RETURNED;
1369 case MCI_STATUS_MEDIA_PRESENT:
1370 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1371 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1372 ret = MCI_RESOURCE_RETURNED;
1374 case MCI_STATUS_NUMBER_OF_TRACKS:
1375 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1376 lpParms->dwReturn = 1;
1377 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1379 case MCI_STATUS_POSITION:
1381 lpParms->dwReturn = 0;
1382 return MCIERR_UNSUPPORTED_FUNCTION;
1384 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1385 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1386 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1388 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1389 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1391 case MCI_STATUS_READY:
1392 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1393 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1394 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1395 ret = MCI_RESOURCE_RETURNED;
1397 case MCI_STATUS_TIME_FORMAT:
1398 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1399 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1400 ret = MCI_RESOURCE_RETURNED;
1402 case MCI_WAVE_INPUT:
1403 TRACE("MCI_WAVE_INPUT !\n");
1404 lpParms->dwReturn = 0;
1405 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1407 case MCI_WAVE_OUTPUT:
1408 TRACE("MCI_WAVE_OUTPUT !\n");
1411 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1412 lpParms->dwReturn = id;
1414 lpParms->dwReturn = 0;
1415 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1419 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1421 lpParms->dwReturn = 0;
1422 return MCIERR_UNSUPPORTED_FUNCTION;
1424 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1425 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1427 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1429 lpParms->dwReturn = 0;
1430 return MCIERR_UNSUPPORTED_FUNCTION;
1432 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1433 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1435 case MCI_WAVE_STATUS_BLOCKALIGN:
1437 lpParms->dwReturn = 0;
1438 return MCIERR_UNSUPPORTED_FUNCTION;
1440 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1441 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1443 case MCI_WAVE_STATUS_CHANNELS:
1445 lpParms->dwReturn = 0;
1446 return MCIERR_UNSUPPORTED_FUNCTION;
1448 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1449 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1451 case MCI_WAVE_STATUS_FORMATTAG:
1453 lpParms->dwReturn = 0;
1454 return MCIERR_UNSUPPORTED_FUNCTION;
1456 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1457 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1459 case MCI_WAVE_STATUS_LEVEL:
1460 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1461 lpParms->dwReturn = 0xAAAA5555;
1463 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1465 lpParms->dwReturn = 0;
1466 return MCIERR_UNSUPPORTED_FUNCTION;
1468 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1469 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1472 WARN("unknown command %08lX !\n", lpParms->dwItem);
1473 return MCIERR_UNRECOGNIZED_COMMAND;
1476 if (dwFlags & MCI_NOTIFY) {
1477 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1478 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1483 /**************************************************************************
1484 * WAVE_mciGetDevCaps [internal]
1486 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1487 LPMCI_GETDEVCAPS_PARMS lpParms)
1489 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1492 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1494 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1495 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1497 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1498 switch(lpParms->dwItem) {
1499 case MCI_GETDEVCAPS_DEVICE_TYPE:
1500 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1501 ret = MCI_RESOURCE_RETURNED;
1503 case MCI_GETDEVCAPS_HAS_AUDIO:
1504 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1505 ret = MCI_RESOURCE_RETURNED;
1507 case MCI_GETDEVCAPS_HAS_VIDEO:
1508 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1509 ret = MCI_RESOURCE_RETURNED;
1511 case MCI_GETDEVCAPS_USES_FILES:
1512 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1513 ret = MCI_RESOURCE_RETURNED;
1515 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1516 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1517 ret = MCI_RESOURCE_RETURNED;
1519 case MCI_GETDEVCAPS_CAN_RECORD:
1520 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1521 ret = MCI_RESOURCE_RETURNED;
1523 case MCI_GETDEVCAPS_CAN_EJECT:
1524 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1525 ret = MCI_RESOURCE_RETURNED;
1527 case MCI_GETDEVCAPS_CAN_PLAY:
1528 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1529 ret = MCI_RESOURCE_RETURNED;
1531 case MCI_GETDEVCAPS_CAN_SAVE:
1532 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1533 ret = MCI_RESOURCE_RETURNED;
1535 case MCI_WAVE_GETDEVCAPS_INPUTS:
1536 lpParms->dwReturn = 1;
1538 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1539 lpParms->dwReturn = 1;
1542 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1543 return MCIERR_UNRECOGNIZED_COMMAND;
1546 WARN("No GetDevCaps-Item !\n");
1547 return MCIERR_UNRECOGNIZED_COMMAND;
1552 /**************************************************************************
1553 * WAVE_mciInfo [internal]
1555 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1559 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1561 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1563 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1564 ret = MCIERR_NULL_PARAMETER_BLOCK;
1565 } else if (wmw == NULL) {
1566 ret = MCIERR_INVALID_DEVICE_ID;
1568 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1569 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1570 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1572 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1574 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1575 case MCI_INFO_PRODUCT: str = wszAudio; break;
1576 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1577 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1578 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1580 WARN("Don't know this info command (%lu)\n", dwFlags);
1581 ret = MCIERR_UNRECOGNIZED_COMMAND;
1585 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1586 ret = MCIERR_PARAM_OVERFLOW;
1588 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1591 lpParms->lpstrReturn[0] = 0;
1597 /**************************************************************************
1598 * DriverProc (MCIWAVE.@)
1600 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1601 LPARAM dwParam1, LPARAM dwParam2)
1603 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1604 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1607 case DRV_LOAD: return 1;
1608 case DRV_FREE: return 1;
1609 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1610 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1611 case DRV_ENABLE: return 1;
1612 case DRV_DISABLE: return 1;
1613 case DRV_QUERYCONFIGURE: return 1;
1614 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1615 case DRV_INSTALL: return DRVCNF_RESTART;
1616 case DRV_REMOVE: return DRVCNF_RESTART;
1619 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1622 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1623 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1624 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1625 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1626 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1627 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1628 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1629 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1630 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1631 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1632 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1633 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1634 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1635 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1636 /* commands that should be supported */
1651 FIXME("Unsupported yet command [%u]\n", wMsg);
1654 TRACE("Unsupported command [%u]\n", wMsg);
1656 /* option which can be silenced */
1661 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1664 FIXME("is probably wrong msg [%u]\n", wMsg);
1665 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1667 return MCIERR_UNRECOGNIZED_COMMAND;