1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
6 * 1999,2000 Eric Pouech
7 * 2000 Francois Jacques
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include "wine/debug.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_PARMSA 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 DWORD dwEventCount; /* for synchronization */
53 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
54 MMCKINFO ckMainRIFF; /* main RIFF chunk */
55 MMCKINFO ckWaveData; /* data chunk */
58 /* ===================================================================
59 * ===================================================================
60 * FIXME: should be using the new mmThreadXXXX functions from WINMM
62 * it would require to add a wine internal flag to mmThreadCreate
63 * in order to pass a 32 bit function instead of a 16 bit one
64 * ===================================================================
65 * =================================================================== */
74 /**************************************************************************
75 * MCI_SCAStarter [internal]
77 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
79 struct SCA* sca = (struct SCA*)arg;
82 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
83 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
84 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
85 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
86 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
87 HeapFree(GetProcessHeap(), 0, sca);
89 WARN("Should not happen ? what's wrong \n");
90 /* should not go after this point */
94 /**************************************************************************
95 * MCI_SendCommandAsync [internal]
97 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
98 DWORD dwParam2, UINT size)
101 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
104 return MCIERR_OUT_OF_MEMORY;
106 sca->wDevID = wDevID;
108 sca->dwParam1 = dwParam1;
110 if (size && dwParam2) {
111 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
112 /* copy structure passed by program in dwParam2 to be sure
113 * we can still use it whatever the program does
115 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
117 sca->dwParam2 = dwParam2;
120 if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
121 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
122 return MCI_SCAStarter(&sca);
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 DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA 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 DWORD WAVE_drvClose(DWORD 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(UINT 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_mciCreateRIFFSkeleton [internal]
288 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
290 MMCKINFO ckWaveFormat;
291 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
292 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
294 lpckRIFF->ckid = FOURCC_RIFF;
295 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
296 lpckRIFF->cksize = 0;
298 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
301 ckWaveFormat.fccType = 0;
302 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
303 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
305 if (!wmw->lpWaveFormat)
307 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
308 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
309 memcpy(wmw->lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
312 /* we can only record PCM files... there is no way in the MCI API to specify
313 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
316 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
319 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
322 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
325 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
328 lpckWaveData->cksize = 0;
329 lpckWaveData->fccType = 0;
330 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
332 /* create data chunk */
333 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
339 if (wmw->lpWaveFormat)
340 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
341 wmw->lpWaveFormat = NULL;
342 return MCIERR_INVALID_FILE;
345 /**************************************************************************
346 * WAVE_mciOpen [internal]
348 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
351 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
352 CHAR* pszTmpFileName = 0;
354 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
355 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
356 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
358 if (dwFlags & MCI_OPEN_SHAREABLE)
359 return MCIERR_HARDWARE;
361 if (wmw->nUseCount > 0) {
362 /* The driver is already opened on this channel
363 * Wave driver cannot be shared
365 return MCIERR_DEVICE_OPEN;
372 wmw->dwStatus = MCI_MODE_NOT_READY;
374 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
376 if (dwFlags & MCI_OPEN_ELEMENT) {
377 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
378 /* could it be that (DWORD)lpOpenParms->lpstrElementName
379 * contains the hFile value ?
381 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
383 if (strlen(lpOpenParms->lpstrElementName) > 0) {
384 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
386 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
387 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
389 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
390 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
391 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
393 if (wmw->hFile == 0) {
394 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
395 dwRet = MCIERR_FILE_NOT_FOUND;
399 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
401 /* make sure we're are the beginning of the file */
402 mmioSeek(wmw->hFile, 0, SEEK_SET);
404 /* first reading of this file. read the waveformat chunk */
405 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
406 dwRet = MCIERR_INVALID_FILE;
408 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
409 (LPSTR)&(lpckMainRIFF->ckid),
410 (LPSTR) &(lpckMainRIFF->fccType),
411 (lpckMainRIFF->cksize));
413 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
414 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
415 dwRet = MCIERR_INVALID_FILE;
417 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
427 CHAR szTmpPath[MAX_PATH];
428 CHAR szPrefix[4] = "TMP\0";
430 pszTmpFileName = HeapAlloc(GetProcessHeap(),
432 MAX_PATH * sizeof(*pszTmpFileName));
434 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
435 WARN("can't retrieve temp path!\n");
436 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
437 return MCIERR_FILE_NOT_FOUND;
440 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
441 WARN("can't retrieve temp file name!\n");
442 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
443 return MCIERR_FILE_NOT_FOUND;
446 wmw->bTemporaryFile = TRUE;
448 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
450 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
452 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
453 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
455 if (wmw->hFile == 0) {
456 /* temporary file could not be created. clean filename. */
457 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
458 WARN("can't create file='%s' !\n", pszTmpFileName);
459 dwRet = MCIERR_FILE_NOT_FOUND;
466 TRACE("hFile=%p\n", wmw->hFile);
468 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
470 if (wmw->bTemporaryFile == TRUE)
472 /* Additional openParms is temporary file's name */
473 wmw->openParms.lpstrElementName = pszTmpFileName;
477 if (wmw->lpWaveFormat) {
478 switch (wmw->lpWaveFormat->wFormatTag) {
479 case WAVE_FORMAT_PCM:
480 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
481 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
482 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
483 wmw->lpWaveFormat->nAvgBytesPerSec,
484 wmw->lpWaveFormat->nSamplesPerSec *
485 wmw->lpWaveFormat->nBlockAlign);
486 wmw->lpWaveFormat->nAvgBytesPerSec =
487 wmw->lpWaveFormat->nSamplesPerSec *
488 wmw->lpWaveFormat->nBlockAlign;
495 wmw->dwStatus = MCI_MODE_STOP;
499 mmioClose(wmw->hFile, 0);
505 /**************************************************************************
506 * WAVE_mciCue [internal]
508 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
513 This routine is far from complete. At the moment only a check is done on the
514 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
517 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
522 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
524 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
526 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
528 /* always close elements ? */
529 if (wmw->hFile != 0) {
530 mmioClose(wmw->hFile, 0);
534 dwRet = MMSYSERR_NOERROR; /* assume success */
536 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
537 dwRet = waveOutClose(wmw->hWave);
538 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
540 } else if (wmw->fInput) {
541 dwRet = waveInClose(wmw->hWave);
542 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
546 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
549 /**************************************************************************
550 * WAVE_mciStop [internal]
552 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
555 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
557 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
559 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
561 /* wait for playback thread (if any) to exit before processing further */
562 switch (wmw->dwStatus) {
565 case MCI_MODE_RECORD:
567 int oldStat = wmw->dwStatus;
568 wmw->dwStatus = MCI_MODE_NOT_READY;
569 if (oldStat == MCI_MODE_PAUSE)
570 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
572 while (wmw->dwStatus != MCI_MODE_STOP)
580 wmw->dwStatus = MCI_MODE_STOP;
582 if ((dwFlags & MCI_NOTIFY) && lpParms) {
583 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
584 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
590 /**************************************************************************
591 * WAVE_mciClose [internal]
593 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
596 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
598 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
600 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
602 if (wmw->dwStatus != MCI_MODE_STOP) {
603 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
608 if (wmw->nUseCount == 0) {
609 if (wmw->hFile != 0) {
610 mmioClose(wmw->hFile, 0);
615 /* That string got allocated in mciOpen because no filename was specified
616 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
617 * allocated by mciOpen, *NOT* the application.
619 if (wmw->bTemporaryFile)
621 HeapFree(GetProcessHeap(), 0, (char*)wmw->openParms.lpstrElementName);
622 wmw->openParms.lpstrElementName = NULL;
625 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
626 wmw->lpWaveFormat = NULL;
628 if ((dwFlags & MCI_NOTIFY) && lpParms) {
629 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
630 wmw->openParms.wDeviceID,
631 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
637 /**************************************************************************
638 * WAVE_mciPlayCallback [internal]
640 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
642 DWORD dwParam1, DWORD dwParam2)
644 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
651 InterlockedIncrement(&wmw->dwEventCount);
652 TRACE("Returning waveHdr=%lx\n", dwParam1);
653 SetEvent(wmw->hEvent);
656 ERR("Unknown uMsg=%d\n", uMsg);
660 /******************************************************************
661 * WAVE_mciPlayWaitDone
665 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
668 ResetEvent(wmw->hEvent);
669 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
672 InterlockedIncrement(&wmw->dwEventCount);
674 WaitForSingleObject(wmw->hEvent, INFINITE);
678 /**************************************************************************
679 * WAVE_mciPlay [internal]
681 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
684 LONG bufsize, count, left;
686 LPWAVEHDR waveHdr = NULL;
687 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
690 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
692 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
693 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
695 /* FIXME : since there is no way to determine in which mode the device is
696 * open (recording/playback) automatically switch from a mode to another
701 WARN("cannot play on input device\n");
702 return MCIERR_NONAPPLICABLE_FUNCTION;
705 if (wmw->hFile == 0) {
706 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
707 return MCIERR_FILE_NOT_FOUND;
710 if (wmw->dwStatus == MCI_MODE_PAUSE) {
711 /* FIXME: parameters (start/end) in lpParams may not be used */
712 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
715 /** This function will be called again by a thread when async is used.
716 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
717 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
719 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
720 return MCIERR_INTERNAL;
723 wmw->dwStatus = MCI_MODE_PLAY;
725 if (!(dwFlags & MCI_WAIT)) {
726 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
727 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
731 if (lpParms && (dwFlags & MCI_FROM)) {
732 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
734 if (lpParms && (dwFlags & MCI_TO)) {
735 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
738 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
740 if (end <= wmw->dwPosition)
744 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
745 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
747 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
748 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
751 if (wmw->lpWaveFormat) {
752 switch (wmw->lpWaveFormat->wFormatTag) {
753 case WAVE_FORMAT_PCM:
754 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
755 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
756 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
757 wmw->lpWaveFormat->nAvgBytesPerSec,
758 wmw->lpWaveFormat->nSamplesPerSec *
759 wmw->lpWaveFormat->nBlockAlign);
760 wmw->lpWaveFormat->nAvgBytesPerSec =
761 wmw->lpWaveFormat->nSamplesPerSec *
762 wmw->lpWaveFormat->nBlockAlign;
768 TRACE("can't retrieve wave format %ld\n", dwRet);
773 /* go back to beginning of chunk plus the requested position */
774 /* FIXME: I'm not sure this is correct, notably because some data linked to
775 * the decompression state machine will not be correcly initialized.
776 * try it this way (other way would be to decompress from 0 up to dwPosition
777 * and to start sending to hWave when dwPosition is reached)
779 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
781 /* By default the device will be opened for output, the MCI_CUE function is there to
782 * change from output to input and back
784 /* FIXME: how to choose between several output channels ? here mapper is forced */
785 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
786 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
789 TRACE("Can't open low level audio device %ld\n", dwRet);
790 dwRet = MCIERR_DEVICE_OPEN;
795 /* make it so that 3 buffers per second are needed */
796 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
798 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
799 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
800 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
801 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
802 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
803 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
804 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
805 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
806 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
807 dwRet = MCIERR_INTERNAL;
812 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
813 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
814 wmw->dwEventCount = 1L; /* for first buffer */
816 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
818 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
819 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
820 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
821 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
824 /* count is always <= bufsize, so this is correct regarding the
825 * waveOutPrepareHeader function
827 waveHdr[whidx].dwBufferLength = count;
828 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
829 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
830 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
831 waveHdr[whidx].dwBytesRecorded);
832 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
834 wmw->dwPosition += count;
835 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
837 WAVE_mciPlayWaitDone(wmw);
841 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
843 /* just to get rid of some race conditions between play, stop and pause */
844 waveOutReset(wmw->hWave);
846 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
847 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
852 HeapFree(GetProcessHeap(), 0, waveHdr);
855 waveOutClose(wmw->hWave);
858 CloseHandle(wmw->hEvent);
860 if (lpParms && (dwFlags & MCI_NOTIFY)) {
861 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
862 wmw->openParms.wDeviceID,
863 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
866 wmw->dwStatus = MCI_MODE_STOP;
871 /**************************************************************************
872 * WAVE_mciPlayCallback [internal]
874 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
876 DWORD dwParam1, DWORD dwParam2)
878 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
887 lpWaveHdr = (LPWAVEHDR) dwParam1;
889 InterlockedIncrement(&wmw->dwEventCount);
891 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
893 lpWaveHdr->dwFlags &= ~WHDR_DONE;
895 wmw->dwPosition += count;
896 /* else error reporting ?? */
897 if (wmw->dwStatus == MCI_MODE_RECORD)
899 /* Only queue up another buffer if we are recording. We could receive this
900 message also when waveInReset() is called, since it notifies on all wave
901 buffers that are outstanding. Queueing up more sometimes causes waveInClose
903 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
904 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
907 SetEvent(wmw->hEvent);
910 ERR("Unknown uMsg=%d\n", uMsg);
914 /******************************************************************
915 * bWAVE_mciRecordWaitDone
918 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
921 ResetEvent(wmw->hEvent);
922 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
925 InterlockedIncrement(&wmw->dwEventCount);
927 WaitForSingleObject(wmw->hEvent, INFINITE);
931 /**************************************************************************
932 * WAVE_mciRecord [internal]
934 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
937 DWORD dwRet = MMSYSERR_NOERROR;
939 LPWAVEHDR waveHdr = NULL;
940 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
943 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
945 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
946 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
948 /* FIXME : since there is no way to determine in which mode the device is
949 * open (recording/playback) automatically switch from a mode to another
954 WARN("cannot record on output device\n");
955 return MCIERR_NONAPPLICABLE_FUNCTION;
958 if (wmw->dwStatus == MCI_MODE_PAUSE) {
959 /* FIXME: parameters (start/end) in lpParams may not be used */
960 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
963 /** This function will be called again by a thread when async is used.
964 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
965 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
967 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
968 return MCIERR_INTERNAL;
971 wmw->dwStatus = MCI_MODE_RECORD;
973 if (!(dwFlags & MCI_WAIT)) {
974 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
975 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
978 if (!wmw->lpWaveFormat) {
980 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
982 FIXME("Should descend into data chunk. Please report.\n");
986 if (lpParms && (dwFlags & MCI_FROM)) {
987 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
990 if (lpParms && (dwFlags & MCI_TO)) {
991 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
994 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
996 if (end <= wmw->dwPosition)
1001 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1002 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1004 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1005 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1007 /* go back to beginning of chunk plus the requested position */
1008 /* FIXME: I'm not sure this is correct, notably because some data linked to
1009 * the decompression state machine will not be correcly initialized.
1010 * try it this way (other way would be to decompress from 0 up to dwPosition
1011 * and to start sending to hWave when dwPosition is reached)
1013 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1015 /* By default the device will be opened for output, the MCI_CUE function is there to
1016 * change from output to input and back
1018 /* FIXME: how to choose between several output channels ? here mapper is forced */
1019 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1020 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1022 if (dwRet != MMSYSERR_NOERROR) {
1023 TRACE("Can't open low level audio device %ld\n", dwRet);
1024 dwRet = MCIERR_DEVICE_OPEN;
1029 /* make it so that 3 buffers per second are needed */
1030 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1032 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1033 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1034 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1035 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1036 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1037 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1038 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1040 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1041 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1042 dwRet = MCIERR_INTERNAL;
1046 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1047 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1048 dwRet = MCIERR_INTERNAL;
1052 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1053 wmw->dwEventCount = 1L; /* for first buffer */
1055 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1057 dwRet = waveInStart(wmw->hWave);
1059 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1060 WAVE_mciRecordWaitDone(wmw);
1063 /* needed so that the callback above won't add again the buffers returned by the reset */
1064 wmw->dwStatus = MCI_MODE_STOP;
1066 waveInReset(wmw->hWave);
1068 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1069 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1074 HeapFree(GetProcessHeap(), 0, waveHdr);
1077 waveInClose(wmw->hWave);
1080 CloseHandle(wmw->hEvent);
1082 /* need to update the size of the data chunk */
1083 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1084 TRACE("failed on ascend\n");
1087 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1088 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1089 wmw->openParms.wDeviceID,
1090 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1093 wmw->dwStatus = MCI_MODE_STOP;
1099 /**************************************************************************
1100 * WAVE_mciPause [internal]
1102 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1105 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1107 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1109 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1110 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1112 if (wmw->dwStatus == MCI_MODE_PLAY) {
1113 wmw->dwStatus = MCI_MODE_PAUSE;
1116 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1117 else dwRet = waveOutPause(wmw->hWave);
1119 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1122 /**************************************************************************
1123 * WAVE_mciResume [internal]
1125 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1127 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1130 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1132 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1134 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1135 wmw->dwStatus = MCI_MODE_PLAY;
1138 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1139 else dwRet = waveOutRestart(wmw->hWave);
1140 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1143 /**************************************************************************
1144 * WAVE_mciSeek [internal]
1146 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1149 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1151 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1153 if (lpParms == NULL) {
1154 ret = MCIERR_NULL_PARAMETER_BLOCK;
1155 } else if (wmw == NULL) {
1156 ret = MCIERR_INVALID_DEVICE_ID;
1158 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1160 if (dwFlags & MCI_SEEK_TO_START) {
1161 wmw->dwPosition = 0;
1162 } else if (dwFlags & MCI_SEEK_TO_END) {
1163 wmw->dwPosition = wmw->ckWaveData.cksize;
1164 } else if (dwFlags & MCI_TO) {
1165 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1167 WARN("dwFlag doesn't tell where to seek to...\n");
1168 return MCIERR_MISSING_PARAMETER;
1171 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1173 if (dwFlags & MCI_NOTIFY) {
1174 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1175 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1181 /**************************************************************************
1182 * WAVE_mciSet [internal]
1184 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1186 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1188 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1190 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1191 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1193 if (dwFlags & MCI_SET_TIME_FORMAT) {
1194 switch (lpParms->dwTimeFormat) {
1195 case MCI_FORMAT_MILLISECONDS:
1196 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1197 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1199 case MCI_FORMAT_BYTES:
1200 TRACE("MCI_FORMAT_BYTES !\n");
1201 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1203 case MCI_FORMAT_SAMPLES:
1204 TRACE("MCI_FORMAT_SAMPLES !\n");
1205 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1208 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1209 return MCIERR_BAD_TIME_FORMAT;
1212 if (dwFlags & MCI_SET_VIDEO) {
1213 TRACE("No support for video !\n");
1214 return MCIERR_UNSUPPORTED_FUNCTION;
1216 if (dwFlags & MCI_SET_DOOR_OPEN) {
1217 TRACE("No support for door open !\n");
1218 return MCIERR_UNSUPPORTED_FUNCTION;
1220 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1221 TRACE("No support for door close !\n");
1222 return MCIERR_UNSUPPORTED_FUNCTION;
1224 if (dwFlags & MCI_SET_AUDIO) {
1225 if (dwFlags & MCI_SET_ON) {
1226 TRACE("MCI_SET_ON audio !\n");
1227 } else if (dwFlags & MCI_SET_OFF) {
1228 TRACE("MCI_SET_OFF audio !\n");
1230 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1231 return MCIERR_BAD_INTEGER;
1234 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1235 TRACE("MCI_SET_AUDIO_ALL !\n");
1236 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1237 TRACE("MCI_SET_AUDIO_LEFT !\n");
1238 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1239 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1241 if (dwFlags & MCI_WAVE_INPUT)
1242 TRACE("MCI_WAVE_INPUT !\n");
1243 if (dwFlags & MCI_WAVE_OUTPUT)
1244 TRACE("MCI_WAVE_OUTPUT !\n");
1245 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1246 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1247 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1248 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1249 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1250 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1251 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1253 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1254 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1255 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1257 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1258 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1259 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1261 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1262 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1263 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1265 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1266 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1267 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1269 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1270 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1271 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1276 /**************************************************************************
1277 * WAVE_mciSave [internal]
1279 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1281 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1282 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1283 WPARAM wparam = MCI_NOTIFY_FAILURE;
1285 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1286 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1287 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1289 if (dwFlags & MCI_WAIT)
1291 FIXME("MCI_WAIT not implemented\n");
1294 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1295 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1298 ret = mmioClose(wmw->hFile, 0);
1301 If the destination file already exists, it has to be overwritten. (Behaviour
1302 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1303 my applications. We are making use of mmioRename, which WILL NOT overwrite
1304 the destination file (which is what Windows does, also verified in Win2K)
1305 So, lets delete the destination file before calling mmioRename. If the
1306 destination file DOESN'T exist, the delete will fail silently. Let's also be
1307 careful not to lose our previous error code.
1309 tmpRet = GetLastError();
1310 DeleteFileA (lpParms->lpfilename);
1311 SetLastError(tmpRet);
1313 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1314 ret = ERROR_SUCCESS;
1317 if (dwFlags & MCI_NOTIFY) {
1318 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1320 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1321 wmw->openParms.wDeviceID, wparam);
1327 /**************************************************************************
1328 * WAVE_mciStatus [internal]
1330 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1332 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1335 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1336 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1337 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1339 if (dwFlags & MCI_STATUS_ITEM) {
1340 switch (lpParms->dwItem) {
1341 case MCI_STATUS_CURRENT_TRACK:
1342 lpParms->dwReturn = 1;
1343 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1345 case MCI_STATUS_LENGTH:
1347 lpParms->dwReturn = 0;
1348 return MCIERR_UNSUPPORTED_FUNCTION;
1350 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1351 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1352 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1354 case MCI_STATUS_MODE:
1355 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1356 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1357 ret = MCI_RESOURCE_RETURNED;
1359 case MCI_STATUS_MEDIA_PRESENT:
1360 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1361 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1362 ret = MCI_RESOURCE_RETURNED;
1364 case MCI_STATUS_NUMBER_OF_TRACKS:
1365 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1366 lpParms->dwReturn = 1;
1367 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1369 case MCI_STATUS_POSITION:
1371 lpParms->dwReturn = 0;
1372 return MCIERR_UNSUPPORTED_FUNCTION;
1374 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1375 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1376 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1378 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1379 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1381 case MCI_STATUS_READY:
1382 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1383 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1384 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1385 ret = MCI_RESOURCE_RETURNED;
1387 case MCI_STATUS_TIME_FORMAT:
1388 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1389 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1390 ret = MCI_RESOURCE_RETURNED;
1392 case MCI_WAVE_INPUT:
1393 TRACE("MCI_WAVE_INPUT !\n");
1394 lpParms->dwReturn = 0;
1395 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1397 case MCI_WAVE_OUTPUT:
1398 TRACE("MCI_WAVE_OUTPUT !\n");
1401 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1402 lpParms->dwReturn = id;
1404 lpParms->dwReturn = 0;
1405 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1409 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1411 lpParms->dwReturn = 0;
1412 return MCIERR_UNSUPPORTED_FUNCTION;
1414 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1415 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1417 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1419 lpParms->dwReturn = 0;
1420 return MCIERR_UNSUPPORTED_FUNCTION;
1422 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1423 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1425 case MCI_WAVE_STATUS_BLOCKALIGN:
1427 lpParms->dwReturn = 0;
1428 return MCIERR_UNSUPPORTED_FUNCTION;
1430 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1431 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1433 case MCI_WAVE_STATUS_CHANNELS:
1435 lpParms->dwReturn = 0;
1436 return MCIERR_UNSUPPORTED_FUNCTION;
1438 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1439 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1441 case MCI_WAVE_STATUS_FORMATTAG:
1443 lpParms->dwReturn = 0;
1444 return MCIERR_UNSUPPORTED_FUNCTION;
1446 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1447 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1449 case MCI_WAVE_STATUS_LEVEL:
1450 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1451 lpParms->dwReturn = 0xAAAA5555;
1453 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1455 lpParms->dwReturn = 0;
1456 return MCIERR_UNSUPPORTED_FUNCTION;
1458 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1459 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1462 WARN("unknown command %08lX !\n", lpParms->dwItem);
1463 return MCIERR_UNRECOGNIZED_COMMAND;
1466 if (dwFlags & MCI_NOTIFY) {
1467 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1468 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1473 /**************************************************************************
1474 * WAVE_mciGetDevCaps [internal]
1476 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1477 LPMCI_GETDEVCAPS_PARMS lpParms)
1479 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1482 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1484 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1485 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1487 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1488 switch(lpParms->dwItem) {
1489 case MCI_GETDEVCAPS_DEVICE_TYPE:
1490 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1491 ret = MCI_RESOURCE_RETURNED;
1493 case MCI_GETDEVCAPS_HAS_AUDIO:
1494 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1495 ret = MCI_RESOURCE_RETURNED;
1497 case MCI_GETDEVCAPS_HAS_VIDEO:
1498 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1499 ret = MCI_RESOURCE_RETURNED;
1501 case MCI_GETDEVCAPS_USES_FILES:
1502 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1503 ret = MCI_RESOURCE_RETURNED;
1505 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1506 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1507 ret = MCI_RESOURCE_RETURNED;
1509 case MCI_GETDEVCAPS_CAN_RECORD:
1510 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1511 ret = MCI_RESOURCE_RETURNED;
1513 case MCI_GETDEVCAPS_CAN_EJECT:
1514 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1515 ret = MCI_RESOURCE_RETURNED;
1517 case MCI_GETDEVCAPS_CAN_PLAY:
1518 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1519 ret = MCI_RESOURCE_RETURNED;
1521 case MCI_GETDEVCAPS_CAN_SAVE:
1522 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1523 ret = MCI_RESOURCE_RETURNED;
1525 case MCI_WAVE_GETDEVCAPS_INPUTS:
1526 lpParms->dwReturn = 1;
1528 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1529 lpParms->dwReturn = 1;
1532 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1533 return MCIERR_UNRECOGNIZED_COMMAND;
1536 WARN("No GetDevCaps-Item !\n");
1537 return MCIERR_UNRECOGNIZED_COMMAND;
1542 /**************************************************************************
1543 * WAVE_mciInfo [internal]
1545 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1549 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1551 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1553 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1554 ret = MCIERR_NULL_PARAMETER_BLOCK;
1555 } else if (wmw == NULL) {
1556 ret = MCIERR_INVALID_DEVICE_ID;
1558 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1560 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1561 case MCI_INFO_PRODUCT:
1562 str = "Wine's audio player";
1565 str = wmw->openParms.lpstrElementName;
1567 case MCI_WAVE_INPUT:
1568 str = "Wine Wave In";
1570 case MCI_WAVE_OUTPUT:
1571 str = "Wine Wave Out";
1574 WARN("Don't know this info command (%lu)\n", dwFlags);
1575 ret = MCIERR_UNRECOGNIZED_COMMAND;
1579 if (strlen(str) + 1 > lpParms->dwRetSize) {
1580 ret = MCIERR_PARAM_OVERFLOW;
1582 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1585 lpParms->lpstrReturn[0] = 0;
1591 /**************************************************************************
1592 * DriverProc (MCIWAVE.@)
1594 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1595 DWORD dwParam1, DWORD dwParam2)
1597 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1598 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1601 case DRV_LOAD: return 1;
1602 case DRV_FREE: return 1;
1603 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1604 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1605 case DRV_ENABLE: return 1;
1606 case DRV_DISABLE: return 1;
1607 case DRV_QUERYCONFIGURE: return 1;
1608 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1609 case DRV_INSTALL: return DRVCNF_RESTART;
1610 case DRV_REMOVE: return DRVCNF_RESTART;
1613 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1616 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1617 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1618 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1619 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1620 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1621 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1622 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1623 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1624 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1625 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1626 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1627 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1628 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1629 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1630 /* commands that should be supported */
1645 FIXME("Unsupported yet command [%lu]\n", wMsg);
1648 TRACE("Unsupported command [%lu]\n", wMsg);
1650 /* option which can be silenced */
1655 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1658 FIXME("is probably wrong msg [%lu]\n", wMsg);
1659 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1661 return MCIERR_UNRECOGNIZED_COMMAND;