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"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
42 int nUseCount; /* Incremented for each shared open */
43 BOOL fShareable; /* TRUE if first open was shareable */
44 HMMIO hFile; /* mmio file handle open as Element */
45 MCI_WAVE_OPEN_PARMSW openParms;
47 LPWAVEFORMATEX lpWaveFormat;
48 BOOL fInput; /* FALSE = Output, TRUE = Input */
49 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
50 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
51 DWORD dwPosition; /* position in bytes in chunk */
52 HANDLE hEvent; /* for synchronization */
53 DWORD dwEventCount; /* for synchronization */
54 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
55 MMCKINFO ckMainRIFF; /* main RIFF chunk */
56 MMCKINFO ckWaveData; /* data chunk */
59 /* ===================================================================
60 * ===================================================================
61 * FIXME: should be using the new mmThreadXXXX functions from WINMM
63 * it would require to add a wine internal flag to mmThreadCreate
64 * in order to pass a 32 bit function instead of a 16 bit one
65 * ===================================================================
66 * =================================================================== */
75 /**************************************************************************
76 * MCI_SCAStarter [internal]
78 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
80 struct SCA* sca = (struct SCA*)arg;
83 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
84 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
85 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
86 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
87 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
88 HeapFree(GetProcessHeap(), 0, sca);
90 WARN("Should not happen ? what's wrong \n");
91 /* should not go after this point */
95 /**************************************************************************
96 * MCI_SendCommandAsync [internal]
98 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
99 DWORD dwParam2, UINT size)
102 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
105 return MCIERR_OUT_OF_MEMORY;
107 sca->wDevID = wDevID;
109 sca->dwParam1 = dwParam1;
111 if (size && dwParam2) {
112 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
113 /* copy structure passed by program in dwParam2 to be sure
114 * we can still use it whatever the program does
116 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
118 sca->dwParam2 = dwParam2;
121 if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
122 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
123 return MCI_SCAStarter(&sca);
129 /*======================================================================*
130 * MCI WAVE implemantation *
131 *======================================================================*/
133 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
135 /**************************************************************************
136 * MCIWAVE_drvOpen [internal]
138 static DWORD WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
142 if (modp == NULL) return 0xFFFFFFFF;
144 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
149 wmw->wDevID = modp->wDeviceID;
150 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
151 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
152 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
154 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
155 wmw->wfxRef.nChannels = 1; /* MONO */
156 wmw->wfxRef.nSamplesPerSec = 11025;
157 wmw->wfxRef.nAvgBytesPerSec = 11025;
158 wmw->wfxRef.nBlockAlign = 1;
159 wmw->wfxRef.wBitsPerSample = 8;
160 wmw->wfxRef.cbSize = 0; /* don't care */
162 return modp->wDeviceID;
165 /**************************************************************************
166 * MCIWAVE_drvClose [internal]
168 static DWORD WAVE_drvClose(DWORD dwDevID)
170 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
173 HeapFree(GetProcessHeap(), 0, wmw);
174 mciSetDriverData(dwDevID, 0);
177 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
180 /**************************************************************************
181 * WAVE_mciGetOpenDev [internal]
183 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
185 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
187 if (wmw == NULL || wmw->nUseCount == 0) {
188 WARN("Invalid wDevID=%u\n", wDevID);
194 /**************************************************************************
195 * WAVE_ConvertByteToTimeFormat [internal]
197 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
201 switch (wmw->dwMciTimeFormat) {
202 case MCI_FORMAT_MILLISECONDS:
203 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
205 case MCI_FORMAT_BYTES:
208 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
209 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
212 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
214 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
219 /**************************************************************************
220 * WAVE_ConvertTimeFormatToByte [internal]
222 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
226 switch (wmw->dwMciTimeFormat) {
227 case MCI_FORMAT_MILLISECONDS:
228 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
230 case MCI_FORMAT_BYTES:
233 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
234 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
237 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
239 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
243 /**************************************************************************
244 * WAVE_mciReadFmt [internal]
246 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
251 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
252 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
253 return MCIERR_INVALID_FILE;
254 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
255 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
257 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
258 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
259 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
260 if (r < sizeof(WAVEFORMAT))
261 return MCIERR_INVALID_FILE;
263 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
264 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
265 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
266 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
267 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
268 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
269 if (r >= (long)sizeof(WAVEFORMATEX))
270 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
272 mmioAscend(wmw->hFile, &mmckInfo, 0);
273 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
274 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
275 TRACE("can't find data chunk\n");
276 return MCIERR_INVALID_FILE;
278 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
279 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
280 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
281 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
286 /**************************************************************************
287 * WAVE_mciCreateRIFFSkeleton [internal]
289 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
291 MMCKINFO ckWaveFormat;
292 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
293 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
295 lpckRIFF->ckid = FOURCC_RIFF;
296 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
297 lpckRIFF->cksize = 0;
299 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
302 ckWaveFormat.fccType = 0;
303 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
304 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
306 if (!wmw->lpWaveFormat)
308 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
309 if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
310 memcpy(wmw->lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
313 /* we can only record PCM files... there is no way in the MCI API to specify
314 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
317 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
320 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
323 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
326 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
329 lpckWaveData->cksize = 0;
330 lpckWaveData->fccType = 0;
331 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
333 /* create data chunk */
334 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
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_PARMSW lpOpenParms)
351 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
352 WCHAR* 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 (strlenW(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", debugstr_w(lpOpenParms->lpstrElementName));
389 if (lpOpenParms->lpstrElementName && (strlenW(lpOpenParms->lpstrElementName) > 0)) {
390 wmw->hFile = mmioOpenW((LPWSTR)lpOpenParms->lpstrElementName, NULL,
391 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
393 if (wmw->hFile == 0) {
394 WARN("can't find file=%s!\n", debugstr_w(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 WCHAR szTmpPath[MAX_PATH];
434 pszTmpFileName = HeapAlloc(GetProcessHeap(),
436 MAX_PATH * sizeof(*pszTmpFileName));
438 if (!GetTempPathW(sizeof(szTmpPath), szTmpPath)) {
439 WARN("can't retrieve temp path!\n");
440 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
441 return MCIERR_FILE_NOT_FOUND;
444 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, pszTmpFileName)) {
445 WARN("can't retrieve temp file name!\n");
446 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
447 return MCIERR_FILE_NOT_FOUND;
450 wmw->bTemporaryFile = TRUE;
452 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(pszTmpFileName));
454 if (pszTmpFileName && (strlenW(pszTmpFileName) > 0)) {
456 wmw->hFile = mmioOpenW(pszTmpFileName, NULL,
457 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
459 if (wmw->hFile == 0) {
460 WARN("can't create file=%s!\n", debugstr_w(pszTmpFileName));
461 /* temporary file could not be created. clean filename. */
462 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
463 dwRet = MCIERR_FILE_NOT_FOUND;
470 TRACE("hFile=%p\n", wmw->hFile);
472 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
474 if (wmw->bTemporaryFile)
476 /* Additional openParms is temporary file's name */
477 wmw->openParms.lpstrElementName = pszTmpFileName;
481 if (wmw->lpWaveFormat) {
482 switch (wmw->lpWaveFormat->wFormatTag) {
483 case WAVE_FORMAT_PCM:
484 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
485 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
486 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
487 wmw->lpWaveFormat->nAvgBytesPerSec,
488 wmw->lpWaveFormat->nSamplesPerSec *
489 wmw->lpWaveFormat->nBlockAlign);
490 wmw->lpWaveFormat->nAvgBytesPerSec =
491 wmw->lpWaveFormat->nSamplesPerSec *
492 wmw->lpWaveFormat->nBlockAlign;
499 wmw->dwStatus = MCI_MODE_STOP;
503 mmioClose(wmw->hFile, 0);
509 /**************************************************************************
510 * WAVE_mciCue [internal]
512 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
517 This routine is far from complete. At the moment only a check is done on the
518 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
521 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
526 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
528 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
530 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
532 /* always close elements ? */
533 if (wmw->hFile != 0) {
534 mmioClose(wmw->hFile, 0);
538 dwRet = MMSYSERR_NOERROR; /* assume success */
540 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
541 dwRet = waveOutClose(wmw->hWave);
542 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
544 } else if (wmw->fInput) {
545 dwRet = waveInClose(wmw->hWave);
546 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
550 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
553 /**************************************************************************
554 * WAVE_mciStop [internal]
556 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
559 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
561 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
563 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
565 /* wait for playback thread (if any) to exit before processing further */
566 switch (wmw->dwStatus) {
569 case MCI_MODE_RECORD:
571 int oldStat = wmw->dwStatus;
572 wmw->dwStatus = MCI_MODE_NOT_READY;
573 if (oldStat == MCI_MODE_PAUSE)
574 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
576 while (wmw->dwStatus != MCI_MODE_STOP)
584 wmw->dwStatus = MCI_MODE_STOP;
586 if ((dwFlags & MCI_NOTIFY) && lpParms) {
587 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
588 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
594 /**************************************************************************
595 * WAVE_mciClose [internal]
597 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
600 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
602 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
604 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
606 if (wmw->dwStatus != MCI_MODE_STOP) {
607 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
612 if (wmw->nUseCount == 0) {
613 if (wmw->hFile != 0) {
614 mmioClose(wmw->hFile, 0);
619 /* That string got allocated in mciOpen because no filename was specified
620 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
621 * allocated by mciOpen, *NOT* the application.
623 if (wmw->bTemporaryFile)
625 HeapFree(GetProcessHeap(), 0, (WCHAR*)wmw->openParms.lpstrElementName);
626 wmw->openParms.lpstrElementName = NULL;
629 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
630 wmw->lpWaveFormat = NULL;
632 if ((dwFlags & MCI_NOTIFY) && lpParms) {
633 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
634 wmw->openParms.wDeviceID,
635 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
641 /**************************************************************************
642 * WAVE_mciPlayCallback [internal]
644 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
646 DWORD dwParam1, DWORD dwParam2)
648 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
655 InterlockedIncrement(&wmw->dwEventCount);
656 TRACE("Returning waveHdr=%lx\n", dwParam1);
657 SetEvent(wmw->hEvent);
660 ERR("Unknown uMsg=%d\n", uMsg);
664 /******************************************************************
665 * WAVE_mciPlayWaitDone
669 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
672 ResetEvent(wmw->hEvent);
673 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
676 InterlockedIncrement(&wmw->dwEventCount);
678 WaitForSingleObject(wmw->hEvent, INFINITE);
682 /**************************************************************************
683 * WAVE_mciPlay [internal]
685 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
688 LONG bufsize, count, left;
690 LPWAVEHDR waveHdr = NULL;
691 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
694 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
696 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
697 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
699 /* FIXME : since there is no way to determine in which mode the device is
700 * open (recording/playback) automatically switch from a mode to another
705 WARN("cannot play on input device\n");
706 return MCIERR_NONAPPLICABLE_FUNCTION;
709 if (wmw->hFile == 0) {
710 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
711 return MCIERR_FILE_NOT_FOUND;
714 if (wmw->dwStatus == MCI_MODE_PAUSE) {
715 /* FIXME: parameters (start/end) in lpParams may not be used */
716 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
719 /** This function will be called again by a thread when async is used.
720 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
721 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
723 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
724 return MCIERR_INTERNAL;
727 wmw->dwStatus = MCI_MODE_PLAY;
729 if (!(dwFlags & MCI_WAIT)) {
730 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
731 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
735 if (lpParms && (dwFlags & MCI_FROM)) {
736 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
738 if (lpParms && (dwFlags & MCI_TO)) {
739 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
742 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
744 if (end <= wmw->dwPosition)
748 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
749 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
751 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
752 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
755 if (wmw->lpWaveFormat) {
756 switch (wmw->lpWaveFormat->wFormatTag) {
757 case WAVE_FORMAT_PCM:
758 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
759 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
760 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
761 wmw->lpWaveFormat->nAvgBytesPerSec,
762 wmw->lpWaveFormat->nSamplesPerSec *
763 wmw->lpWaveFormat->nBlockAlign);
764 wmw->lpWaveFormat->nAvgBytesPerSec =
765 wmw->lpWaveFormat->nSamplesPerSec *
766 wmw->lpWaveFormat->nBlockAlign;
772 TRACE("can't retrieve wave format %ld\n", dwRet);
777 /* go back to beginning of chunk plus the requested position */
778 /* FIXME: I'm not sure this is correct, notably because some data linked to
779 * the decompression state machine will not be correcly initialized.
780 * try it this way (other way would be to decompress from 0 up to dwPosition
781 * and to start sending to hWave when dwPosition is reached)
783 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
785 /* By default the device will be opened for output, the MCI_CUE function is there to
786 * change from output to input and back
788 /* FIXME: how to choose between several output channels ? here mapper is forced */
789 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
790 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
793 TRACE("Can't open low level audio device %ld\n", dwRet);
794 dwRet = MCIERR_DEVICE_OPEN;
799 /* make it so that 3 buffers per second are needed */
800 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
802 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
803 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
804 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
805 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
806 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
807 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
808 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
809 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
810 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
811 dwRet = MCIERR_INTERNAL;
816 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
817 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
818 wmw->dwEventCount = 1L; /* for first buffer */
820 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
822 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
823 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
824 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
825 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
828 /* count is always <= bufsize, so this is correct regarding the
829 * waveOutPrepareHeader function
831 waveHdr[whidx].dwBufferLength = count;
832 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
833 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
834 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
835 waveHdr[whidx].dwBytesRecorded);
836 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
838 wmw->dwPosition += count;
839 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
841 WAVE_mciPlayWaitDone(wmw);
845 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
847 /* just to get rid of some race conditions between play, stop and pause */
848 waveOutReset(wmw->hWave);
850 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
851 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
856 HeapFree(GetProcessHeap(), 0, waveHdr);
859 waveOutClose(wmw->hWave);
862 CloseHandle(wmw->hEvent);
864 if (lpParms && (dwFlags & MCI_NOTIFY)) {
865 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
866 wmw->openParms.wDeviceID,
867 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
870 wmw->dwStatus = MCI_MODE_STOP;
875 /**************************************************************************
876 * WAVE_mciPlayCallback [internal]
878 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
880 DWORD dwParam1, DWORD dwParam2)
882 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
891 lpWaveHdr = (LPWAVEHDR) dwParam1;
893 InterlockedIncrement(&wmw->dwEventCount);
895 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
897 lpWaveHdr->dwFlags &= ~WHDR_DONE;
899 wmw->dwPosition += count;
900 /* else error reporting ?? */
901 if (wmw->dwStatus == MCI_MODE_RECORD)
903 /* Only queue up another buffer if we are recording. We could receive this
904 message also when waveInReset() is called, since it notifies on all wave
905 buffers that are outstanding. Queueing up more sometimes causes waveInClose
907 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
908 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
911 SetEvent(wmw->hEvent);
914 ERR("Unknown uMsg=%d\n", uMsg);
918 /******************************************************************
919 * bWAVE_mciRecordWaitDone
922 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
925 ResetEvent(wmw->hEvent);
926 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
929 InterlockedIncrement(&wmw->dwEventCount);
931 WaitForSingleObject(wmw->hEvent, INFINITE);
935 /**************************************************************************
936 * WAVE_mciRecord [internal]
938 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
941 DWORD dwRet = MMSYSERR_NOERROR;
943 LPWAVEHDR waveHdr = NULL;
944 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
947 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
949 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
950 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
952 /* FIXME : since there is no way to determine in which mode the device is
953 * open (recording/playback) automatically switch from a mode to another
958 WARN("cannot record on output device\n");
959 return MCIERR_NONAPPLICABLE_FUNCTION;
962 if (wmw->dwStatus == MCI_MODE_PAUSE) {
963 /* FIXME: parameters (start/end) in lpParams may not be used */
964 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
967 /** This function will be called again by a thread when async is used.
968 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
969 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
971 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
972 return MCIERR_INTERNAL;
975 wmw->dwStatus = MCI_MODE_RECORD;
977 if (!(dwFlags & MCI_WAIT)) {
978 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
979 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
982 if (!wmw->lpWaveFormat) {
984 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
986 FIXME("Should descend into data chunk. Please report.\n");
990 if (lpParms && (dwFlags & MCI_FROM)) {
991 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
994 if (lpParms && (dwFlags & MCI_TO)) {
995 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
998 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
1000 if (end <= wmw->dwPosition)
1005 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1006 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1008 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
1009 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1011 /* go back to beginning of chunk plus the requested position */
1012 /* FIXME: I'm not sure this is correct, notably because some data linked to
1013 * the decompression state machine will not be correcly initialized.
1014 * try it this way (other way would be to decompress from 0 up to dwPosition
1015 * and to start sending to hWave when dwPosition is reached)
1017 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1019 /* By default the device will be opened for output, the MCI_CUE function is there to
1020 * change from output to input and back
1022 /* FIXME: how to choose between several output channels ? here mapper is forced */
1023 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1024 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1026 if (dwRet != MMSYSERR_NOERROR) {
1027 TRACE("Can't open low level audio device %ld\n", dwRet);
1028 dwRet = MCIERR_DEVICE_OPEN;
1033 /* make it so that 3 buffers per second are needed */
1034 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1036 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1037 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1038 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1039 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1040 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1041 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1042 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1044 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1045 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1046 dwRet = MCIERR_INTERNAL;
1050 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1051 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1052 dwRet = MCIERR_INTERNAL;
1056 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1057 wmw->dwEventCount = 1L; /* for first buffer */
1059 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1061 dwRet = waveInStart(wmw->hWave);
1063 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1064 WAVE_mciRecordWaitDone(wmw);
1067 /* needed so that the callback above won't add again the buffers returned by the reset */
1068 wmw->dwStatus = MCI_MODE_STOP;
1070 waveInReset(wmw->hWave);
1072 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1073 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1078 HeapFree(GetProcessHeap(), 0, waveHdr);
1081 waveInClose(wmw->hWave);
1084 CloseHandle(wmw->hEvent);
1086 /* need to update the size of the data chunk */
1087 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1088 TRACE("failed on ascend\n");
1091 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1092 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1093 wmw->openParms.wDeviceID,
1094 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1097 wmw->dwStatus = MCI_MODE_STOP;
1103 /**************************************************************************
1104 * WAVE_mciPause [internal]
1106 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1109 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1111 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1113 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1114 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1116 if (wmw->dwStatus == MCI_MODE_PLAY) {
1117 wmw->dwStatus = MCI_MODE_PAUSE;
1120 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1121 else dwRet = waveOutPause(wmw->hWave);
1123 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1126 /**************************************************************************
1127 * WAVE_mciResume [internal]
1129 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1131 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1134 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1136 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1138 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1139 wmw->dwStatus = MCI_MODE_PLAY;
1142 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1143 else dwRet = waveOutRestart(wmw->hWave);
1144 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1147 /**************************************************************************
1148 * WAVE_mciSeek [internal]
1150 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1153 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1155 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1157 if (lpParms == NULL) {
1158 ret = MCIERR_NULL_PARAMETER_BLOCK;
1159 } else if (wmw == NULL) {
1160 ret = MCIERR_INVALID_DEVICE_ID;
1162 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1164 if (dwFlags & MCI_SEEK_TO_START) {
1165 wmw->dwPosition = 0;
1166 } else if (dwFlags & MCI_SEEK_TO_END) {
1167 wmw->dwPosition = wmw->ckWaveData.cksize;
1168 } else if (dwFlags & MCI_TO) {
1169 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1171 WARN("dwFlag doesn't tell where to seek to...\n");
1172 return MCIERR_MISSING_PARAMETER;
1175 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1177 if (dwFlags & MCI_NOTIFY) {
1178 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1179 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1185 /**************************************************************************
1186 * WAVE_mciSet [internal]
1188 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1190 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1192 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1194 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1195 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1197 if (dwFlags & MCI_SET_TIME_FORMAT) {
1198 switch (lpParms->dwTimeFormat) {
1199 case MCI_FORMAT_MILLISECONDS:
1200 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1201 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1203 case MCI_FORMAT_BYTES:
1204 TRACE("MCI_FORMAT_BYTES !\n");
1205 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1207 case MCI_FORMAT_SAMPLES:
1208 TRACE("MCI_FORMAT_SAMPLES !\n");
1209 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1212 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1213 return MCIERR_BAD_TIME_FORMAT;
1216 if (dwFlags & MCI_SET_VIDEO) {
1217 TRACE("No support for video !\n");
1218 return MCIERR_UNSUPPORTED_FUNCTION;
1220 if (dwFlags & MCI_SET_DOOR_OPEN) {
1221 TRACE("No support for door open !\n");
1222 return MCIERR_UNSUPPORTED_FUNCTION;
1224 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1225 TRACE("No support for door close !\n");
1226 return MCIERR_UNSUPPORTED_FUNCTION;
1228 if (dwFlags & MCI_SET_AUDIO) {
1229 if (dwFlags & MCI_SET_ON) {
1230 TRACE("MCI_SET_ON audio !\n");
1231 } else if (dwFlags & MCI_SET_OFF) {
1232 TRACE("MCI_SET_OFF audio !\n");
1234 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1235 return MCIERR_BAD_INTEGER;
1238 switch (lpParms->dwAudio)
1240 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1241 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1242 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1243 default: WARN("Unknown audio channel %lu\n", lpParms->dwAudio); break;
1246 if (dwFlags & MCI_WAVE_INPUT)
1247 TRACE("MCI_WAVE_INPUT !\n");
1248 if (dwFlags & MCI_WAVE_OUTPUT)
1249 TRACE("MCI_WAVE_OUTPUT !\n");
1250 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1251 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1252 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1253 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1254 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1255 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1256 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1258 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1259 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1260 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1262 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1263 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1264 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1266 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1267 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1268 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1270 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1271 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1272 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1274 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1275 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1276 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1281 /**************************************************************************
1282 * WAVE_mciSave [internal]
1284 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1286 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1287 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1288 WPARAM wparam = MCI_NOTIFY_FAILURE;
1290 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1291 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1292 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1294 if (dwFlags & MCI_WAIT)
1296 FIXME("MCI_WAIT not implemented\n");
1299 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1300 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1303 ret = mmioClose(wmw->hFile, 0);
1306 If the destination file already exists, it has to be overwritten. (Behaviour
1307 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1308 my applications. We are making use of mmioRename, which WILL NOT overwrite
1309 the destination file (which is what Windows does, also verified in Win2K)
1310 So, lets delete the destination file before calling mmioRename. If the
1311 destination file DOESN'T exist, the delete will fail silently. Let's also be
1312 careful not to lose our previous error code.
1314 tmpRet = GetLastError();
1315 DeleteFileW (lpParms->lpfilename);
1316 SetLastError(tmpRet);
1318 if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1319 ret = ERROR_SUCCESS;
1322 if (dwFlags & MCI_NOTIFY) {
1323 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1325 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1326 wmw->openParms.wDeviceID, wparam);
1332 /**************************************************************************
1333 * WAVE_mciStatus [internal]
1335 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1337 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1340 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1341 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1342 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1344 if (dwFlags & MCI_STATUS_ITEM) {
1345 switch (lpParms->dwItem) {
1346 case MCI_STATUS_CURRENT_TRACK:
1347 lpParms->dwReturn = 1;
1348 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1350 case MCI_STATUS_LENGTH:
1352 lpParms->dwReturn = 0;
1353 return MCIERR_UNSUPPORTED_FUNCTION;
1355 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1356 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1357 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1359 case MCI_STATUS_MODE:
1360 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1361 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1362 ret = MCI_RESOURCE_RETURNED;
1364 case MCI_STATUS_MEDIA_PRESENT:
1365 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1366 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1367 ret = MCI_RESOURCE_RETURNED;
1369 case MCI_STATUS_NUMBER_OF_TRACKS:
1370 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1371 lpParms->dwReturn = 1;
1372 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1374 case MCI_STATUS_POSITION:
1376 lpParms->dwReturn = 0;
1377 return MCIERR_UNSUPPORTED_FUNCTION;
1379 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1380 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1381 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1383 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1384 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1386 case MCI_STATUS_READY:
1387 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1388 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1389 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1390 ret = MCI_RESOURCE_RETURNED;
1392 case MCI_STATUS_TIME_FORMAT:
1393 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1394 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1395 ret = MCI_RESOURCE_RETURNED;
1397 case MCI_WAVE_INPUT:
1398 TRACE("MCI_WAVE_INPUT !\n");
1399 lpParms->dwReturn = 0;
1400 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1402 case MCI_WAVE_OUTPUT:
1403 TRACE("MCI_WAVE_OUTPUT !\n");
1406 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1407 lpParms->dwReturn = id;
1409 lpParms->dwReturn = 0;
1410 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1414 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1416 lpParms->dwReturn = 0;
1417 return MCIERR_UNSUPPORTED_FUNCTION;
1419 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1420 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1422 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1424 lpParms->dwReturn = 0;
1425 return MCIERR_UNSUPPORTED_FUNCTION;
1427 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1428 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1430 case MCI_WAVE_STATUS_BLOCKALIGN:
1432 lpParms->dwReturn = 0;
1433 return MCIERR_UNSUPPORTED_FUNCTION;
1435 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1436 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1438 case MCI_WAVE_STATUS_CHANNELS:
1440 lpParms->dwReturn = 0;
1441 return MCIERR_UNSUPPORTED_FUNCTION;
1443 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1444 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1446 case MCI_WAVE_STATUS_FORMATTAG:
1448 lpParms->dwReturn = 0;
1449 return MCIERR_UNSUPPORTED_FUNCTION;
1451 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1452 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1454 case MCI_WAVE_STATUS_LEVEL:
1455 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1456 lpParms->dwReturn = 0xAAAA5555;
1458 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1460 lpParms->dwReturn = 0;
1461 return MCIERR_UNSUPPORTED_FUNCTION;
1463 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1464 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1467 WARN("unknown command %08lX !\n", lpParms->dwItem);
1468 return MCIERR_UNRECOGNIZED_COMMAND;
1471 if (dwFlags & MCI_NOTIFY) {
1472 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1473 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1478 /**************************************************************************
1479 * WAVE_mciGetDevCaps [internal]
1481 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1482 LPMCI_GETDEVCAPS_PARMS lpParms)
1484 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1487 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1489 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1490 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1492 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1493 switch(lpParms->dwItem) {
1494 case MCI_GETDEVCAPS_DEVICE_TYPE:
1495 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1496 ret = MCI_RESOURCE_RETURNED;
1498 case MCI_GETDEVCAPS_HAS_AUDIO:
1499 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1500 ret = MCI_RESOURCE_RETURNED;
1502 case MCI_GETDEVCAPS_HAS_VIDEO:
1503 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1504 ret = MCI_RESOURCE_RETURNED;
1506 case MCI_GETDEVCAPS_USES_FILES:
1507 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1508 ret = MCI_RESOURCE_RETURNED;
1510 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1511 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1512 ret = MCI_RESOURCE_RETURNED;
1514 case MCI_GETDEVCAPS_CAN_RECORD:
1515 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1516 ret = MCI_RESOURCE_RETURNED;
1518 case MCI_GETDEVCAPS_CAN_EJECT:
1519 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1520 ret = MCI_RESOURCE_RETURNED;
1522 case MCI_GETDEVCAPS_CAN_PLAY:
1523 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1524 ret = MCI_RESOURCE_RETURNED;
1526 case MCI_GETDEVCAPS_CAN_SAVE:
1527 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1528 ret = MCI_RESOURCE_RETURNED;
1530 case MCI_WAVE_GETDEVCAPS_INPUTS:
1531 lpParms->dwReturn = 1;
1533 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1534 lpParms->dwReturn = 1;
1537 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1538 return MCIERR_UNRECOGNIZED_COMMAND;
1541 WARN("No GetDevCaps-Item !\n");
1542 return MCIERR_UNRECOGNIZED_COMMAND;
1547 /**************************************************************************
1548 * WAVE_mciInfo [internal]
1550 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1554 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1556 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1558 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1559 ret = MCIERR_NULL_PARAMETER_BLOCK;
1560 } else if (wmw == NULL) {
1561 ret = MCIERR_INVALID_DEVICE_ID;
1563 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1564 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1565 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1567 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1569 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1570 case MCI_INFO_PRODUCT: str = wszAudio; break;
1571 case MCI_INFO_FILE: str = wmw->openParms.lpstrElementName; break;
1572 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1573 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1575 WARN("Don't know this info command (%lu)\n", dwFlags);
1576 ret = MCIERR_UNRECOGNIZED_COMMAND;
1580 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1581 ret = MCIERR_PARAM_OVERFLOW;
1583 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1586 lpParms->lpstrReturn[0] = 0;
1592 /**************************************************************************
1593 * DriverProc (MCIWAVE.@)
1595 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1596 DWORD dwParam1, DWORD dwParam2)
1598 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1599 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1602 case DRV_LOAD: return 1;
1603 case DRV_FREE: return 1;
1604 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1605 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1606 case DRV_ENABLE: return 1;
1607 case DRV_DISABLE: return 1;
1608 case DRV_QUERYCONFIGURE: return 1;
1609 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1610 case DRV_INSTALL: return DRVCNF_RESTART;
1611 case DRV_REMOVE: return DRVCNF_RESTART;
1614 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1617 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1618 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1619 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1620 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1621 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1622 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1623 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1624 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1625 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1626 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1627 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1628 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1629 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1630 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1631 /* commands that should be supported */
1646 FIXME("Unsupported yet command [%lu]\n", wMsg);
1649 TRACE("Unsupported command [%lu]\n", wMsg);
1651 /* option which can be silenced */
1656 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1659 FIXME("is probably wrong msg [%lu]\n", wMsg);
1660 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1662 return MCIERR_UNRECOGNIZED_COMMAND;