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 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
340 wmw->lpWaveFormat = NULL;
341 return MCIERR_INVALID_FILE;
344 /**************************************************************************
345 * WAVE_mciOpen [internal]
347 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
350 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
351 CHAR* pszTmpFileName = 0;
353 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
354 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
355 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
357 if (dwFlags & MCI_OPEN_SHAREABLE)
358 return MCIERR_HARDWARE;
360 if (wmw->nUseCount > 0) {
361 /* The driver is already opened on this channel
362 * Wave driver cannot be shared
364 return MCIERR_DEVICE_OPEN;
371 wmw->dwStatus = MCI_MODE_NOT_READY;
373 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
375 if (dwFlags & MCI_OPEN_ELEMENT) {
376 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
377 /* could it be that (DWORD)lpOpenParms->lpstrElementName
378 * contains the hFile value ?
380 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
382 if (strlen(lpOpenParms->lpstrElementName) > 0) {
383 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
385 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
386 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
388 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
389 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
390 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
392 if (wmw->hFile == 0) {
393 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
394 dwRet = MCIERR_FILE_NOT_FOUND;
398 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
400 /* make sure we're are the beginning of the file */
401 mmioSeek(wmw->hFile, 0, SEEK_SET);
403 /* first reading of this file. read the waveformat chunk */
404 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
405 dwRet = MCIERR_INVALID_FILE;
407 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
408 (LPSTR)&(lpckMainRIFF->ckid),
409 (LPSTR) &(lpckMainRIFF->fccType),
410 (lpckMainRIFF->cksize));
412 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
413 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
414 dwRet = MCIERR_INVALID_FILE;
416 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
426 CHAR szTmpPath[MAX_PATH];
427 CHAR szPrefix[4] = "TMP\0";
429 pszTmpFileName = HeapAlloc(GetProcessHeap(),
431 MAX_PATH * sizeof(*pszTmpFileName));
433 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
434 WARN("can't retrieve temp path!\n");
435 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
436 return MCIERR_FILE_NOT_FOUND;
439 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
440 WARN("can't retrieve temp file name!\n");
441 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
442 return MCIERR_FILE_NOT_FOUND;
445 wmw->bTemporaryFile = TRUE;
447 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
449 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
451 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
452 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
454 if (wmw->hFile == 0) {
455 /* temporary file could not be created. clean filename. */
456 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
457 WARN("can't create file='%s' !\n", pszTmpFileName);
458 dwRet = MCIERR_FILE_NOT_FOUND;
465 TRACE("hFile=%p\n", wmw->hFile);
467 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
469 if (wmw->bTemporaryFile)
471 /* Additional openParms is temporary file's name */
472 wmw->openParms.lpstrElementName = pszTmpFileName;
476 if (wmw->lpWaveFormat) {
477 switch (wmw->lpWaveFormat->wFormatTag) {
478 case WAVE_FORMAT_PCM:
479 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
480 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
481 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
482 wmw->lpWaveFormat->nAvgBytesPerSec,
483 wmw->lpWaveFormat->nSamplesPerSec *
484 wmw->lpWaveFormat->nBlockAlign);
485 wmw->lpWaveFormat->nAvgBytesPerSec =
486 wmw->lpWaveFormat->nSamplesPerSec *
487 wmw->lpWaveFormat->nBlockAlign;
494 wmw->dwStatus = MCI_MODE_STOP;
498 mmioClose(wmw->hFile, 0);
504 /**************************************************************************
505 * WAVE_mciCue [internal]
507 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
512 This routine is far from complete. At the moment only a check is done on the
513 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
516 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
521 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
523 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
525 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
527 /* always close elements ? */
528 if (wmw->hFile != 0) {
529 mmioClose(wmw->hFile, 0);
533 dwRet = MMSYSERR_NOERROR; /* assume success */
535 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
536 dwRet = waveOutClose(wmw->hWave);
537 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
539 } else if (wmw->fInput) {
540 dwRet = waveInClose(wmw->hWave);
541 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
545 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
548 /**************************************************************************
549 * WAVE_mciStop [internal]
551 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
554 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
556 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
558 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
560 /* wait for playback thread (if any) to exit before processing further */
561 switch (wmw->dwStatus) {
564 case MCI_MODE_RECORD:
566 int oldStat = wmw->dwStatus;
567 wmw->dwStatus = MCI_MODE_NOT_READY;
568 if (oldStat == MCI_MODE_PAUSE)
569 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
571 while (wmw->dwStatus != MCI_MODE_STOP)
579 wmw->dwStatus = MCI_MODE_STOP;
581 if ((dwFlags & MCI_NOTIFY) && lpParms) {
582 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
583 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
589 /**************************************************************************
590 * WAVE_mciClose [internal]
592 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
595 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
597 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
599 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
601 if (wmw->dwStatus != MCI_MODE_STOP) {
602 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
607 if (wmw->nUseCount == 0) {
608 if (wmw->hFile != 0) {
609 mmioClose(wmw->hFile, 0);
614 /* That string got allocated in mciOpen because no filename was specified
615 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
616 * allocated by mciOpen, *NOT* the application.
618 if (wmw->bTemporaryFile)
620 HeapFree(GetProcessHeap(), 0, (char*)wmw->openParms.lpstrElementName);
621 wmw->openParms.lpstrElementName = NULL;
624 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
625 wmw->lpWaveFormat = NULL;
627 if ((dwFlags & MCI_NOTIFY) && lpParms) {
628 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
629 wmw->openParms.wDeviceID,
630 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
636 /**************************************************************************
637 * WAVE_mciPlayCallback [internal]
639 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
641 DWORD dwParam1, DWORD dwParam2)
643 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
650 InterlockedIncrement(&wmw->dwEventCount);
651 TRACE("Returning waveHdr=%lx\n", dwParam1);
652 SetEvent(wmw->hEvent);
655 ERR("Unknown uMsg=%d\n", uMsg);
659 /******************************************************************
660 * WAVE_mciPlayWaitDone
664 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
667 ResetEvent(wmw->hEvent);
668 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
671 InterlockedIncrement(&wmw->dwEventCount);
673 WaitForSingleObject(wmw->hEvent, INFINITE);
677 /**************************************************************************
678 * WAVE_mciPlay [internal]
680 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
683 LONG bufsize, count, left;
685 LPWAVEHDR waveHdr = NULL;
686 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
689 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
691 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
692 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
694 /* FIXME : since there is no way to determine in which mode the device is
695 * open (recording/playback) automatically switch from a mode to another
700 WARN("cannot play on input device\n");
701 return MCIERR_NONAPPLICABLE_FUNCTION;
704 if (wmw->hFile == 0) {
705 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
706 return MCIERR_FILE_NOT_FOUND;
709 if (wmw->dwStatus == MCI_MODE_PAUSE) {
710 /* FIXME: parameters (start/end) in lpParams may not be used */
711 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
714 /** This function will be called again by a thread when async is used.
715 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
716 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
718 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
719 return MCIERR_INTERNAL;
722 wmw->dwStatus = MCI_MODE_PLAY;
724 if (!(dwFlags & MCI_WAIT)) {
725 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
726 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
730 if (lpParms && (dwFlags & MCI_FROM)) {
731 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
733 if (lpParms && (dwFlags & MCI_TO)) {
734 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
737 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
739 if (end <= wmw->dwPosition)
743 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
744 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
746 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
747 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
750 if (wmw->lpWaveFormat) {
751 switch (wmw->lpWaveFormat->wFormatTag) {
752 case WAVE_FORMAT_PCM:
753 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
754 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
755 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
756 wmw->lpWaveFormat->nAvgBytesPerSec,
757 wmw->lpWaveFormat->nSamplesPerSec *
758 wmw->lpWaveFormat->nBlockAlign);
759 wmw->lpWaveFormat->nAvgBytesPerSec =
760 wmw->lpWaveFormat->nSamplesPerSec *
761 wmw->lpWaveFormat->nBlockAlign;
767 TRACE("can't retrieve wave format %ld\n", dwRet);
772 /* go back to beginning of chunk plus the requested position */
773 /* FIXME: I'm not sure this is correct, notably because some data linked to
774 * the decompression state machine will not be correcly initialized.
775 * try it this way (other way would be to decompress from 0 up to dwPosition
776 * and to start sending to hWave when dwPosition is reached)
778 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
780 /* By default the device will be opened for output, the MCI_CUE function is there to
781 * change from output to input and back
783 /* FIXME: how to choose between several output channels ? here mapper is forced */
784 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
785 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
788 TRACE("Can't open low level audio device %ld\n", dwRet);
789 dwRet = MCIERR_DEVICE_OPEN;
794 /* make it so that 3 buffers per second are needed */
795 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
797 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
798 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
799 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
800 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
801 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
802 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
803 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
804 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
805 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
806 dwRet = MCIERR_INTERNAL;
811 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
812 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
813 wmw->dwEventCount = 1L; /* for first buffer */
815 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
817 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
818 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
819 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
820 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
823 /* count is always <= bufsize, so this is correct regarding the
824 * waveOutPrepareHeader function
826 waveHdr[whidx].dwBufferLength = count;
827 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
828 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
829 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
830 waveHdr[whidx].dwBytesRecorded);
831 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
833 wmw->dwPosition += count;
834 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
836 WAVE_mciPlayWaitDone(wmw);
840 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
842 /* just to get rid of some race conditions between play, stop and pause */
843 waveOutReset(wmw->hWave);
845 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
846 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
851 HeapFree(GetProcessHeap(), 0, waveHdr);
854 waveOutClose(wmw->hWave);
857 CloseHandle(wmw->hEvent);
859 if (lpParms && (dwFlags & MCI_NOTIFY)) {
860 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
861 wmw->openParms.wDeviceID,
862 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
865 wmw->dwStatus = MCI_MODE_STOP;
870 /**************************************************************************
871 * WAVE_mciPlayCallback [internal]
873 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
875 DWORD dwParam1, DWORD dwParam2)
877 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
886 lpWaveHdr = (LPWAVEHDR) dwParam1;
888 InterlockedIncrement(&wmw->dwEventCount);
890 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
892 lpWaveHdr->dwFlags &= ~WHDR_DONE;
894 wmw->dwPosition += count;
895 /* else error reporting ?? */
896 if (wmw->dwStatus == MCI_MODE_RECORD)
898 /* Only queue up another buffer if we are recording. We could receive this
899 message also when waveInReset() is called, since it notifies on all wave
900 buffers that are outstanding. Queueing up more sometimes causes waveInClose
902 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
903 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
906 SetEvent(wmw->hEvent);
909 ERR("Unknown uMsg=%d\n", uMsg);
913 /******************************************************************
914 * bWAVE_mciRecordWaitDone
917 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
920 ResetEvent(wmw->hEvent);
921 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
924 InterlockedIncrement(&wmw->dwEventCount);
926 WaitForSingleObject(wmw->hEvent, INFINITE);
930 /**************************************************************************
931 * WAVE_mciRecord [internal]
933 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
936 DWORD dwRet = MMSYSERR_NOERROR;
938 LPWAVEHDR waveHdr = NULL;
939 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
942 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
944 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
945 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
947 /* FIXME : since there is no way to determine in which mode the device is
948 * open (recording/playback) automatically switch from a mode to another
953 WARN("cannot record on output device\n");
954 return MCIERR_NONAPPLICABLE_FUNCTION;
957 if (wmw->dwStatus == MCI_MODE_PAUSE) {
958 /* FIXME: parameters (start/end) in lpParams may not be used */
959 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
962 /** This function will be called again by a thread when async is used.
963 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
964 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
966 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
967 return MCIERR_INTERNAL;
970 wmw->dwStatus = MCI_MODE_RECORD;
972 if (!(dwFlags & MCI_WAIT)) {
973 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
974 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
977 if (!wmw->lpWaveFormat) {
979 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
981 FIXME("Should descend into data chunk. Please report.\n");
985 if (lpParms && (dwFlags & MCI_FROM)) {
986 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
989 if (lpParms && (dwFlags & MCI_TO)) {
990 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
993 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
995 if (end <= wmw->dwPosition)
1000 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1001 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1003 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1004 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1006 /* go back to beginning of chunk plus the requested position */
1007 /* FIXME: I'm not sure this is correct, notably because some data linked to
1008 * the decompression state machine will not be correcly initialized.
1009 * try it this way (other way would be to decompress from 0 up to dwPosition
1010 * and to start sending to hWave when dwPosition is reached)
1012 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1014 /* By default the device will be opened for output, the MCI_CUE function is there to
1015 * change from output to input and back
1017 /* FIXME: how to choose between several output channels ? here mapper is forced */
1018 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1019 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1021 if (dwRet != MMSYSERR_NOERROR) {
1022 TRACE("Can't open low level audio device %ld\n", dwRet);
1023 dwRet = MCIERR_DEVICE_OPEN;
1028 /* make it so that 3 buffers per second are needed */
1029 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1031 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1032 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1033 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1034 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1035 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1036 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1037 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1039 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1040 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1041 dwRet = MCIERR_INTERNAL;
1045 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1046 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1047 dwRet = MCIERR_INTERNAL;
1051 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1052 wmw->dwEventCount = 1L; /* for first buffer */
1054 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1056 dwRet = waveInStart(wmw->hWave);
1058 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1059 WAVE_mciRecordWaitDone(wmw);
1062 /* needed so that the callback above won't add again the buffers returned by the reset */
1063 wmw->dwStatus = MCI_MODE_STOP;
1065 waveInReset(wmw->hWave);
1067 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1068 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1073 HeapFree(GetProcessHeap(), 0, waveHdr);
1076 waveInClose(wmw->hWave);
1079 CloseHandle(wmw->hEvent);
1081 /* need to update the size of the data chunk */
1082 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1083 TRACE("failed on ascend\n");
1086 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1087 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1088 wmw->openParms.wDeviceID,
1089 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1092 wmw->dwStatus = MCI_MODE_STOP;
1098 /**************************************************************************
1099 * WAVE_mciPause [internal]
1101 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1104 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1106 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1108 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1109 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1111 if (wmw->dwStatus == MCI_MODE_PLAY) {
1112 wmw->dwStatus = MCI_MODE_PAUSE;
1115 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1116 else dwRet = waveOutPause(wmw->hWave);
1118 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1121 /**************************************************************************
1122 * WAVE_mciResume [internal]
1124 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1126 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1129 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1131 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1133 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1134 wmw->dwStatus = MCI_MODE_PLAY;
1137 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1138 else dwRet = waveOutRestart(wmw->hWave);
1139 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1142 /**************************************************************************
1143 * WAVE_mciSeek [internal]
1145 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1148 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1150 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1152 if (lpParms == NULL) {
1153 ret = MCIERR_NULL_PARAMETER_BLOCK;
1154 } else if (wmw == NULL) {
1155 ret = MCIERR_INVALID_DEVICE_ID;
1157 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1159 if (dwFlags & MCI_SEEK_TO_START) {
1160 wmw->dwPosition = 0;
1161 } else if (dwFlags & MCI_SEEK_TO_END) {
1162 wmw->dwPosition = wmw->ckWaveData.cksize;
1163 } else if (dwFlags & MCI_TO) {
1164 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1166 WARN("dwFlag doesn't tell where to seek to...\n");
1167 return MCIERR_MISSING_PARAMETER;
1170 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1172 if (dwFlags & MCI_NOTIFY) {
1173 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1174 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1180 /**************************************************************************
1181 * WAVE_mciSet [internal]
1183 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1185 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1187 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1189 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1190 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1192 if (dwFlags & MCI_SET_TIME_FORMAT) {
1193 switch (lpParms->dwTimeFormat) {
1194 case MCI_FORMAT_MILLISECONDS:
1195 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1196 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1198 case MCI_FORMAT_BYTES:
1199 TRACE("MCI_FORMAT_BYTES !\n");
1200 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1202 case MCI_FORMAT_SAMPLES:
1203 TRACE("MCI_FORMAT_SAMPLES !\n");
1204 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1207 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1208 return MCIERR_BAD_TIME_FORMAT;
1211 if (dwFlags & MCI_SET_VIDEO) {
1212 TRACE("No support for video !\n");
1213 return MCIERR_UNSUPPORTED_FUNCTION;
1215 if (dwFlags & MCI_SET_DOOR_OPEN) {
1216 TRACE("No support for door open !\n");
1217 return MCIERR_UNSUPPORTED_FUNCTION;
1219 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1220 TRACE("No support for door close !\n");
1221 return MCIERR_UNSUPPORTED_FUNCTION;
1223 if (dwFlags & MCI_SET_AUDIO) {
1224 if (dwFlags & MCI_SET_ON) {
1225 TRACE("MCI_SET_ON audio !\n");
1226 } else if (dwFlags & MCI_SET_OFF) {
1227 TRACE("MCI_SET_OFF audio !\n");
1229 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1230 return MCIERR_BAD_INTEGER;
1233 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1234 TRACE("MCI_SET_AUDIO_ALL !\n");
1235 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1236 TRACE("MCI_SET_AUDIO_LEFT !\n");
1237 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1238 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1240 if (dwFlags & MCI_WAVE_INPUT)
1241 TRACE("MCI_WAVE_INPUT !\n");
1242 if (dwFlags & MCI_WAVE_OUTPUT)
1243 TRACE("MCI_WAVE_OUTPUT !\n");
1244 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1245 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1246 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1247 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1248 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1249 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1250 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1252 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1253 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1254 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1256 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1257 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1258 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1260 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1261 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1262 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1264 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1265 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1266 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1268 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1269 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1270 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1275 /**************************************************************************
1276 * WAVE_mciSave [internal]
1278 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1280 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1281 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1282 WPARAM wparam = MCI_NOTIFY_FAILURE;
1284 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1285 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1286 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1288 if (dwFlags & MCI_WAIT)
1290 FIXME("MCI_WAIT not implemented\n");
1293 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1294 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1297 ret = mmioClose(wmw->hFile, 0);
1300 If the destination file already exists, it has to be overwritten. (Behaviour
1301 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1302 my applications. We are making use of mmioRename, which WILL NOT overwrite
1303 the destination file (which is what Windows does, also verified in Win2K)
1304 So, lets delete the destination file before calling mmioRename. If the
1305 destination file DOESN'T exist, the delete will fail silently. Let's also be
1306 careful not to lose our previous error code.
1308 tmpRet = GetLastError();
1309 DeleteFileA (lpParms->lpfilename);
1310 SetLastError(tmpRet);
1312 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1313 ret = ERROR_SUCCESS;
1316 if (dwFlags & MCI_NOTIFY) {
1317 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1319 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1320 wmw->openParms.wDeviceID, wparam);
1326 /**************************************************************************
1327 * WAVE_mciStatus [internal]
1329 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1331 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1334 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1335 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1336 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1338 if (dwFlags & MCI_STATUS_ITEM) {
1339 switch (lpParms->dwItem) {
1340 case MCI_STATUS_CURRENT_TRACK:
1341 lpParms->dwReturn = 1;
1342 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1344 case MCI_STATUS_LENGTH:
1346 lpParms->dwReturn = 0;
1347 return MCIERR_UNSUPPORTED_FUNCTION;
1349 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1350 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1351 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1353 case MCI_STATUS_MODE:
1354 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1355 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1356 ret = MCI_RESOURCE_RETURNED;
1358 case MCI_STATUS_MEDIA_PRESENT:
1359 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1360 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1361 ret = MCI_RESOURCE_RETURNED;
1363 case MCI_STATUS_NUMBER_OF_TRACKS:
1364 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1365 lpParms->dwReturn = 1;
1366 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1368 case MCI_STATUS_POSITION:
1370 lpParms->dwReturn = 0;
1371 return MCIERR_UNSUPPORTED_FUNCTION;
1373 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1374 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1375 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1377 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1378 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1380 case MCI_STATUS_READY:
1381 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1382 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1383 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1384 ret = MCI_RESOURCE_RETURNED;
1386 case MCI_STATUS_TIME_FORMAT:
1387 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1388 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1389 ret = MCI_RESOURCE_RETURNED;
1391 case MCI_WAVE_INPUT:
1392 TRACE("MCI_WAVE_INPUT !\n");
1393 lpParms->dwReturn = 0;
1394 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1396 case MCI_WAVE_OUTPUT:
1397 TRACE("MCI_WAVE_OUTPUT !\n");
1400 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1401 lpParms->dwReturn = id;
1403 lpParms->dwReturn = 0;
1404 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1408 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1410 lpParms->dwReturn = 0;
1411 return MCIERR_UNSUPPORTED_FUNCTION;
1413 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1414 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1416 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1418 lpParms->dwReturn = 0;
1419 return MCIERR_UNSUPPORTED_FUNCTION;
1421 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1422 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1424 case MCI_WAVE_STATUS_BLOCKALIGN:
1426 lpParms->dwReturn = 0;
1427 return MCIERR_UNSUPPORTED_FUNCTION;
1429 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1430 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1432 case MCI_WAVE_STATUS_CHANNELS:
1434 lpParms->dwReturn = 0;
1435 return MCIERR_UNSUPPORTED_FUNCTION;
1437 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1438 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1440 case MCI_WAVE_STATUS_FORMATTAG:
1442 lpParms->dwReturn = 0;
1443 return MCIERR_UNSUPPORTED_FUNCTION;
1445 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1446 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1448 case MCI_WAVE_STATUS_LEVEL:
1449 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1450 lpParms->dwReturn = 0xAAAA5555;
1452 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1454 lpParms->dwReturn = 0;
1455 return MCIERR_UNSUPPORTED_FUNCTION;
1457 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1458 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1461 WARN("unknown command %08lX !\n", lpParms->dwItem);
1462 return MCIERR_UNRECOGNIZED_COMMAND;
1465 if (dwFlags & MCI_NOTIFY) {
1466 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1467 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1472 /**************************************************************************
1473 * WAVE_mciGetDevCaps [internal]
1475 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1476 LPMCI_GETDEVCAPS_PARMS lpParms)
1478 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1481 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1483 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1484 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1486 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1487 switch(lpParms->dwItem) {
1488 case MCI_GETDEVCAPS_DEVICE_TYPE:
1489 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1490 ret = MCI_RESOURCE_RETURNED;
1492 case MCI_GETDEVCAPS_HAS_AUDIO:
1493 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1494 ret = MCI_RESOURCE_RETURNED;
1496 case MCI_GETDEVCAPS_HAS_VIDEO:
1497 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1498 ret = MCI_RESOURCE_RETURNED;
1500 case MCI_GETDEVCAPS_USES_FILES:
1501 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1502 ret = MCI_RESOURCE_RETURNED;
1504 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1505 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1506 ret = MCI_RESOURCE_RETURNED;
1508 case MCI_GETDEVCAPS_CAN_RECORD:
1509 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1510 ret = MCI_RESOURCE_RETURNED;
1512 case MCI_GETDEVCAPS_CAN_EJECT:
1513 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1514 ret = MCI_RESOURCE_RETURNED;
1516 case MCI_GETDEVCAPS_CAN_PLAY:
1517 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1518 ret = MCI_RESOURCE_RETURNED;
1520 case MCI_GETDEVCAPS_CAN_SAVE:
1521 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1522 ret = MCI_RESOURCE_RETURNED;
1524 case MCI_WAVE_GETDEVCAPS_INPUTS:
1525 lpParms->dwReturn = 1;
1527 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1528 lpParms->dwReturn = 1;
1531 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1532 return MCIERR_UNRECOGNIZED_COMMAND;
1535 WARN("No GetDevCaps-Item !\n");
1536 return MCIERR_UNRECOGNIZED_COMMAND;
1541 /**************************************************************************
1542 * WAVE_mciInfo [internal]
1544 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1548 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1550 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1552 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1553 ret = MCIERR_NULL_PARAMETER_BLOCK;
1554 } else if (wmw == NULL) {
1555 ret = MCIERR_INVALID_DEVICE_ID;
1557 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1559 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1560 case MCI_INFO_PRODUCT:
1561 str = "Wine's audio player";
1564 str = wmw->openParms.lpstrElementName;
1566 case MCI_WAVE_INPUT:
1567 str = "Wine Wave In";
1569 case MCI_WAVE_OUTPUT:
1570 str = "Wine Wave Out";
1573 WARN("Don't know this info command (%lu)\n", dwFlags);
1574 ret = MCIERR_UNRECOGNIZED_COMMAND;
1578 if (strlen(str) + 1 > lpParms->dwRetSize) {
1579 ret = MCIERR_PARAM_OVERFLOW;
1581 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1584 lpParms->lpstrReturn[0] = 0;
1590 /**************************************************************************
1591 * DriverProc (MCIWAVE.@)
1593 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1594 DWORD dwParam1, DWORD dwParam2)
1596 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1597 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1600 case DRV_LOAD: return 1;
1601 case DRV_FREE: return 1;
1602 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1603 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1604 case DRV_ENABLE: return 1;
1605 case DRV_DISABLE: return 1;
1606 case DRV_QUERYCONFIGURE: return 1;
1607 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1608 case DRV_INSTALL: return DRVCNF_RESTART;
1609 case DRV_REMOVE: return DRVCNF_RESTART;
1612 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1615 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1616 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1617 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1618 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1619 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1620 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1621 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1622 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1623 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1624 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1625 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1626 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1627 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1628 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1629 /* commands that should be supported */
1644 FIXME("Unsupported yet command [%lu]\n", wMsg);
1647 TRACE("Unsupported command [%lu]\n", wMsg);
1649 /* option which can be silenced */
1654 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1657 FIXME("is probably wrong msg [%lu]\n", wMsg);
1658 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1660 return MCIERR_UNRECOGNIZED_COMMAND;