1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
7 * 2000 Francois Jacques
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(mciwave);
25 int nUseCount; /* Incremented for each shared open */
26 BOOL fShareable; /* TRUE if first open was shareable */
27 HMMIO hFile; /* mmio file handle open as Element */
28 MCI_WAVE_OPEN_PARMSA openParms;
29 LPWAVEFORMATEX lpWaveFormat;
30 BOOL fInput; /* FALSE = Output, TRUE = Input */
31 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
32 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
33 DWORD dwRemaining; /* remaining bytes to play or record */
34 DWORD dwPosition; /* position in bytes in chunk */
35 HANDLE hEvent; /* for synchronization */
36 DWORD dwEventCount; /* for synchronization */
37 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
38 MMCKINFO ckMainRIFF; /* main RIFF chunk */
39 MMCKINFO ckWaveData; /* data chunk */
42 /* ===================================================================
43 * ===================================================================
44 * FIXME: should be using the new mmThreadXXXX functions from WINMM
46 * it would require to add a wine internal flag to mmThreadCreate
47 * in order to pass a 32 bit function instead of a 16 bit one
48 * ===================================================================
49 * =================================================================== */
58 /**************************************************************************
59 * MCI_SCAStarter [internal]
61 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
63 struct SCA* sca = (struct SCA*)arg;
66 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
67 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
68 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
69 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
70 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
71 HeapFree(GetProcessHeap(), 0, sca);
73 WARN("Should not happen ? what's wrong \n");
74 /* should not go after this point */
78 /**************************************************************************
79 * MCI_SendCommandAsync [internal]
81 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
82 DWORD dwParam2, UINT size)
84 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
87 return MCIERR_OUT_OF_MEMORY;
91 sca->dwParam1 = dwParam1;
93 if (size && dwParam2) {
94 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
95 /* copy structure passed by program in dwParam2 to be sure
96 * we can still use it whatever the program does
98 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
100 sca->dwParam2 = dwParam2;
103 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
104 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
105 return MCI_SCAStarter(&sca);
110 /*======================================================================*
111 * MCI WAVE implemantation *
112 *======================================================================*/
114 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
116 /**************************************************************************
117 * MCIWAVE_drvOpen [internal]
119 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
121 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
126 wmw->wDevID = modp->wDeviceID;
127 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
128 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
129 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
130 return modp->wDeviceID;
133 /**************************************************************************
134 * MCIWAVE_drvClose [internal]
136 static DWORD WAVE_drvClose(DWORD dwDevID)
138 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
141 HeapFree(GetProcessHeap(), 0, wmw);
142 mciSetDriverData(dwDevID, 0);
148 /**************************************************************************
149 * WAVE_mciGetOpenDev [internal]
151 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
153 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
155 if (wmw == NULL || wmw->nUseCount == 0) {
156 WARN("Invalid wDevID=%u\n", wDevID);
162 /**************************************************************************
163 * WAVE_ConvertByteToTimeFormat [internal]
165 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
169 switch (wmw->dwMciTimeFormat) {
170 case MCI_FORMAT_MILLISECONDS:
171 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
173 case MCI_FORMAT_BYTES:
176 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
177 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
180 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
182 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
187 /**************************************************************************
188 * WAVE_ConvertTimeFormatToByte [internal]
190 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
194 switch (wmw->dwMciTimeFormat) {
195 case MCI_FORMAT_MILLISECONDS:
196 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
198 case MCI_FORMAT_BYTES:
201 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
202 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
205 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
207 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
211 /**************************************************************************
212 * WAVE_mciReadFmt [internal]
214 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
219 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
220 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
221 return MCIERR_INVALID_FILE;
222 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
223 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
225 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
226 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
227 if (r < sizeof(WAVEFORMAT))
228 return MCIERR_INVALID_FILE;
230 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
231 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
232 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
233 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
234 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
235 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
236 if (r >= (long)sizeof(WAVEFORMATEX))
237 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
239 mmioAscend(wmw->hFile, &mmckInfo, 0);
240 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
241 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
242 TRACE("can't find data chunk\n");
243 return MCIERR_INVALID_FILE;
245 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
246 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
247 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
248 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
253 /**************************************************************************
254 * WAVE_mciCreateRIFFSkeleton [internal]
256 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
258 MMCKINFO ckWaveFormat;
260 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
261 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
262 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
265 HMMIO hmmio = wmw->hFile;
267 lpckRIFF->ckid = FOURCC_RIFF;
268 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
269 lpckRIFF->cksize = 0;
271 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
274 ckWaveFormat.fccType = 0;
275 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
276 ckWaveFormat.cksize = 16;
280 TRACE("allocating waveformat with default waveformat 11khz/8bit/mono \n");
282 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
284 lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
285 lpWaveFormat->nChannels = 1; /* MONO */
286 lpWaveFormat->nSamplesPerSec = 11025;
287 lpWaveFormat->nAvgBytesPerSec = 11025;
288 lpWaveFormat->nBlockAlign = 1;
289 lpWaveFormat->wBitsPerSample = 8;
290 lpWaveFormat->cbSize = 0; /* don't care */
293 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
296 /* only the first 16 bytes are serialized */
297 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
300 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
303 lpckWaveData->cksize = 0;
304 lpckWaveData->fccType = 0;
305 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
307 /* create data chunk */
308 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
314 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
315 return MCIERR_INVALID_FILE;
318 /**************************************************************************
319 * WAVE_mciOpen [internal]
321 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
324 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
325 CHAR* pszTmpFileName = 0;
327 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
328 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
329 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
331 if (dwFlags & MCI_OPEN_SHAREABLE)
332 return MCIERR_HARDWARE;
334 if (wmw->nUseCount > 0) {
335 /* The driver is already opened on this channel
336 * Wave driver cannot be shared
338 return MCIERR_DEVICE_OPEN;
345 wmw->dwStatus = MCI_MODE_NOT_READY;
347 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
349 if (dwFlags & MCI_OPEN_ELEMENT) {
350 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
351 /* could it be that (DWORD)lpOpenParms->lpstrElementName
352 * contains the hFile value ?
354 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
356 if (strlen(lpOpenParms->lpstrElementName) > 0) {
357 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
359 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
360 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
362 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
363 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
364 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
366 if (wmw->hFile == 0) {
367 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
368 dwRet = MCIERR_FILE_NOT_FOUND;
372 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
374 /* make sure we're are the beginning of the file */
375 mmioSeek(wmw->hFile, 0, SEEK_SET);
377 /* first reading of this file. read the waveformat chunk */
378 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
379 dwRet = MCIERR_INVALID_FILE;
381 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
382 (LPSTR)&(lpckMainRIFF->ckid),
383 (LPSTR) &(lpckMainRIFF->fccType),
384 (lpckMainRIFF->cksize));
386 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
387 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
388 dwRet = MCIERR_INVALID_FILE;
390 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
400 CHAR szTmpPath[MAX_PATH];
401 CHAR szPrefix[4] = "TMP\0";
403 pszTmpFileName = HeapAlloc(GetProcessHeap(),
405 MAX_PATH * sizeof(*pszTmpFileName));
407 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
408 WARN("can't retrieve temp path!\n");
409 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
410 return MCIERR_FILE_NOT_FOUND;
413 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
414 WARN("can't retrieve temp file name!\n");
415 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
416 return MCIERR_FILE_NOT_FOUND;
419 wmw->bTemporaryFile = TRUE;
421 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
423 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
425 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
426 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
428 if (wmw->hFile == 0) {
429 /* temporary file could not be created. clean filename. */
430 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
431 WARN("can't create file='%s' !\n", pszTmpFileName);
432 dwRet = MCIERR_FILE_NOT_FOUND;
439 TRACE("hFile=%u\n", wmw->hFile);
441 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
443 if (wmw->bTemporaryFile == TRUE)
445 /* Additional openParms is temporary file's name */
446 wmw->openParms.lpstrElementName = pszTmpFileName;
450 if (wmw->lpWaveFormat) {
451 switch (wmw->lpWaveFormat->wFormatTag) {
452 case WAVE_FORMAT_PCM:
453 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
454 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
455 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
456 wmw->lpWaveFormat->nAvgBytesPerSec,
457 wmw->lpWaveFormat->nSamplesPerSec *
458 wmw->lpWaveFormat->nBlockAlign);
459 wmw->lpWaveFormat->nAvgBytesPerSec =
460 wmw->lpWaveFormat->nSamplesPerSec *
461 wmw->lpWaveFormat->nBlockAlign;
468 wmw->dwStatus = MCI_MODE_STOP;
472 mmioClose(wmw->hFile, 0);
478 /**************************************************************************
479 * WAVE_mciCue [internal]
481 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
486 This routine is far from complete. At the moment only a check is done on the
487 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
490 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
495 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
497 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
499 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
501 /* always close elements ? */
502 if (wmw->hFile != 0) {
503 mmioClose(wmw->hFile, 0);
507 dwRet = MMSYSERR_NOERROR; /* assume success */
509 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
510 dwRet = waveOutClose(wmw->hWave);
511 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
513 } else if (wmw->fInput) {
514 dwRet = waveInClose(wmw->hWave);
515 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
519 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
522 /**************************************************************************
523 * WAVE_mciStop [internal]
525 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
528 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
530 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
532 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
534 /* wait for playback thread (if any) to exit before processing further */
535 switch (wmw->dwStatus) {
538 case MCI_MODE_RECORD:
540 int oldStat = wmw->dwStatus;
541 wmw->dwStatus = MCI_MODE_NOT_READY;
542 if (oldStat == MCI_MODE_PAUSE)
543 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
545 while (wmw->dwStatus != MCI_MODE_STOP)
553 wmw->dwStatus = MCI_MODE_STOP;
555 if ((dwFlags & MCI_NOTIFY) && lpParms) {
556 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
557 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
563 /**************************************************************************
564 * WAVE_mciClose [internal]
566 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
569 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
571 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
573 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
575 if (wmw->dwStatus != MCI_MODE_STOP) {
576 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
581 if (wmw->nUseCount == 0) {
582 if (wmw->hFile != 0) {
583 mmioClose(wmw->hFile, 0);
588 /* That string got allocated in mciOpen because no filename was specified
589 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
590 * allocated by mciOpen, *NOT* the application.
592 if (wmw->bTemporaryFile)
594 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
595 wmw->openParms.lpstrElementName = NULL;
598 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
599 wmw->lpWaveFormat = NULL;
601 if ((dwFlags & MCI_NOTIFY) && lpParms) {
602 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
603 wmw->openParms.wDeviceID,
604 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
610 /**************************************************************************
611 * WAVE_mciPlayCallback [internal]
613 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
615 DWORD dwParam1, DWORD dwParam2)
617 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
624 InterlockedIncrement(&wmw->dwEventCount);
625 TRACE("Returning waveHdr=%lx\n", dwParam1);
626 SetEvent(wmw->hEvent);
629 ERR("Unknown uMsg=%d\n", uMsg);
633 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
636 ResetEvent(wmw->hEvent);
637 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
640 InterlockedIncrement(&wmw->dwEventCount);
642 WaitForSingleObject(wmw->hEvent, INFINITE);
646 /**************************************************************************
647 * WAVE_mciPlay [internal]
649 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
652 LONG bufsize, count, left;
654 LPWAVEHDR waveHdr = NULL;
655 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
658 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
660 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
661 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
663 /* FIXME : since there is no way to determine in which mode the device is
664 * open (recording/playback) automatically switch from a mode to another
669 WARN("cannot play on input device\n");
670 return MCIERR_NONAPPLICABLE_FUNCTION;
673 if (wmw->hFile == 0) {
674 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
675 return MCIERR_FILE_NOT_FOUND;
678 if (wmw->dwStatus == MCI_MODE_PAUSE) {
679 /* FIXME: parameters (start/end) in lpParams may not be used */
680 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
683 /** This function will be called again by a thread when async is used.
684 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
685 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
687 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
688 return MCIERR_INTERNAL;
691 wmw->dwStatus = MCI_MODE_PLAY;
693 if (!(dwFlags & MCI_WAIT)) {
694 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
695 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
699 if (lpParms && (dwFlags & MCI_FROM)) {
700 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
702 if (lpParms && (dwFlags & MCI_TO)) {
703 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
706 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
708 if (end <= wmw->dwPosition)
712 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
713 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
715 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
716 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
719 if (wmw->lpWaveFormat) {
720 switch (wmw->lpWaveFormat->wFormatTag) {
721 case WAVE_FORMAT_PCM:
722 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
723 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
724 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
725 wmw->lpWaveFormat->nAvgBytesPerSec,
726 wmw->lpWaveFormat->nSamplesPerSec *
727 wmw->lpWaveFormat->nBlockAlign);
728 wmw->lpWaveFormat->nAvgBytesPerSec =
729 wmw->lpWaveFormat->nSamplesPerSec *
730 wmw->lpWaveFormat->nBlockAlign;
736 TRACE("can't retrieve wave format %ld\n", dwRet);
741 /* go back to begining of chunk plus the requested position */
742 /* FIXME: I'm not sure this is correct, notably because some data linked to
743 * the decompression state machine will not be correcly initialized.
744 * try it this way (other way would be to decompress from 0 up to dwPosition
745 * and to start sending to hWave when dwPosition is reached)
747 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
749 /* By default the device will be opened for output, the MCI_CUE function is there to
750 * change from output to input and back
752 /* FIXME: how to choose between several output channels ? here mapper is forced */
753 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
754 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
757 TRACE("Can't open low level audio device %ld\n", dwRet);
758 dwRet = MCIERR_DEVICE_OPEN;
763 /* make it so that 3 buffers per second are needed */
764 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
766 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
767 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
768 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
769 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
770 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
771 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
772 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
773 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
774 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
775 dwRet = MCIERR_INTERNAL;
780 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
781 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
782 wmw->dwEventCount = 1L; /* for first buffer */
784 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
786 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
787 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
788 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
789 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
792 /* count is always <= bufsize, so this is correct regarding the
793 * waveOutPrepareHeader function
795 waveHdr[whidx].dwBufferLength = count;
796 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
797 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
798 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
799 waveHdr[whidx].dwBytesRecorded);
800 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
802 wmw->dwPosition += count;
803 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
805 WAVE_mciPlayWaitDone(wmw);
809 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
811 /* just to get rid of some race conditions between play, stop and pause */
812 waveOutReset(wmw->hWave);
814 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
815 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
820 HeapFree(GetProcessHeap(), 0, waveHdr);
823 waveOutClose(wmw->hWave);
826 CloseHandle(wmw->hEvent);
828 if (lpParms && (dwFlags & MCI_NOTIFY)) {
829 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
830 wmw->openParms.wDeviceID,
831 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
834 wmw->dwStatus = MCI_MODE_STOP;
839 /**************************************************************************
840 * WAVE_mciPlayCallback [internal]
842 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
844 DWORD dwParam1, DWORD dwParam2)
846 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
847 LPWAVEHDR lpWaveHdr = NULL;
854 lpWaveHdr = (LPWAVEHDR) dwParam1;
856 InterlockedIncrement(&wmw->dwEventCount);
858 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
860 lpWaveHdr->dwFlags &= ~WHDR_DONE;
861 wmw->dwPosition += count;
862 wmw->dwRemaining -= count;
864 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
865 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
867 SetEvent(wmw->hEvent);
870 ERR("Unknown uMsg=%d\n", uMsg);
874 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
877 ResetEvent(wmw->hEvent);
878 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
881 InterlockedIncrement(&wmw->dwEventCount);
883 WaitForSingleObject(wmw->hEvent, INFINITE);
887 /**************************************************************************
888 * WAVE_mciRecord [internal]
890 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
895 LPWAVEHDR waveHdr = NULL;
896 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
899 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
901 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
902 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
904 /* FIXME : since there is no way to determine in which mode the device is
905 * open (recording/playback) automatically switch from a mode to another
910 WARN("cannot record on output device\n");
911 return MCIERR_NONAPPLICABLE_FUNCTION;
914 if (wmw->dwStatus == MCI_MODE_PAUSE) {
915 /* FIXME: parameters (start/end) in lpParams may not be used */
916 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
919 /** This function will be called again by a thread when async is used.
920 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
921 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
923 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
924 return MCIERR_INTERNAL;
927 wmw->dwStatus = MCI_MODE_RECORD;
929 if (!(dwFlags & MCI_WAIT)) {
930 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
931 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
934 if (!wmw->lpWaveFormat)
937 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
941 if (lpParms && (dwFlags & MCI_FROM)) {
942 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
945 if (lpParms && (dwFlags & MCI_TO)) {
946 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
949 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
951 if (end <= wmw->dwPosition)
956 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
957 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
959 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
960 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
962 /* go back to begining of chunk plus the requested position */
963 /* FIXME: I'm not sure this is correct, notably because some data linked to
964 * the decompression state machine will not be correcly initialized.
965 * try it this way (other way would be to decompress from 0 up to dwPosition
966 * and to start sending to hWave when dwPosition is reached)
968 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
970 /* By default the device will be opened for output, the MCI_CUE function is there to
971 * change from output to input and back
973 /* FIXME: how to choose between several output channels ? here mapper is forced */
974 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
975 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
978 TRACE("Can't open low level audio device %ld\n", dwRet);
979 dwRet = MCIERR_DEVICE_OPEN;
984 /* make it so that 3 buffers per second are needed */
985 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
987 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
988 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
989 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
990 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
991 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
992 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
993 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
995 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
996 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
997 dwRet = MCIERR_INTERNAL;
1001 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1002 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1003 dwRet = MCIERR_INTERNAL;
1007 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1008 wmw->dwEventCount = 1L; /* for first buffer */
1010 wmw->dwRemaining = end - wmw->dwPosition;
1012 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1014 dwRet = waveInStart(wmw->hWave);
1016 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1017 WAVE_mciRecordWaitDone(wmw);
1020 waveInReset(wmw->hWave);
1021 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1022 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1027 HeapFree(GetProcessHeap(), 0, waveHdr);
1030 waveInClose(wmw->hWave);
1033 CloseHandle(wmw->hEvent);
1035 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1036 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1037 wmw->openParms.wDeviceID,
1038 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1041 wmw->dwStatus = MCI_MODE_STOP;
1047 /**************************************************************************
1048 * WAVE_mciPause [internal]
1050 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1053 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1055 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1057 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1058 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1060 if (wmw->dwStatus == MCI_MODE_PLAY) {
1061 wmw->dwStatus = MCI_MODE_PAUSE;
1064 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1065 else dwRet = waveOutPause(wmw->hWave);
1067 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1070 /**************************************************************************
1071 * WAVE_mciResume [internal]
1073 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1075 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1078 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1080 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1082 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1083 wmw->dwStatus = MCI_MODE_PLAY;
1086 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1087 else dwRet = waveOutRestart(wmw->hWave);
1088 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1091 /**************************************************************************
1092 * WAVE_mciSeek [internal]
1094 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1097 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1099 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1101 if (lpParms == NULL) {
1102 ret = MCIERR_NULL_PARAMETER_BLOCK;
1103 } else if (wmw == NULL) {
1104 ret = MCIERR_INVALID_DEVICE_ID;
1106 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1108 if (dwFlags & MCI_SEEK_TO_START) {
1109 wmw->dwPosition = 0;
1110 } else if (dwFlags & MCI_SEEK_TO_END) {
1111 wmw->dwPosition = wmw->ckWaveData.cksize;
1112 } else if (dwFlags & MCI_TO) {
1113 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1115 WARN("dwFlag doesn't tell where to seek to...\n");
1116 return MCIERR_MISSING_PARAMETER;
1119 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1121 if (dwFlags & MCI_NOTIFY) {
1122 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1123 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1129 /**************************************************************************
1130 * WAVE_mciSet [internal]
1132 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1134 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1136 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1138 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1139 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1141 if (dwFlags & MCI_SET_TIME_FORMAT) {
1142 switch (lpParms->dwTimeFormat) {
1143 case MCI_FORMAT_MILLISECONDS:
1144 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1145 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1147 case MCI_FORMAT_BYTES:
1148 TRACE("MCI_FORMAT_BYTES !\n");
1149 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1151 case MCI_FORMAT_SAMPLES:
1152 TRACE("MCI_FORMAT_SAMPLES !\n");
1153 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1156 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1157 return MCIERR_BAD_TIME_FORMAT;
1160 if (dwFlags & MCI_SET_VIDEO) {
1161 TRACE("No support for video !\n");
1162 return MCIERR_UNSUPPORTED_FUNCTION;
1164 if (dwFlags & MCI_SET_DOOR_OPEN) {
1165 TRACE("No support for door open !\n");
1166 return MCIERR_UNSUPPORTED_FUNCTION;
1168 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1169 TRACE("No support for door close !\n");
1170 return MCIERR_UNSUPPORTED_FUNCTION;
1172 if (dwFlags & MCI_SET_AUDIO) {
1173 if (dwFlags & MCI_SET_ON) {
1174 TRACE("MCI_SET_ON audio !\n");
1175 } else if (dwFlags & MCI_SET_OFF) {
1176 TRACE("MCI_SET_OFF audio !\n");
1178 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1179 return MCIERR_BAD_INTEGER;
1182 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1183 TRACE("MCI_SET_AUDIO_ALL !\n");
1184 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1185 TRACE("MCI_SET_AUDIO_LEFT !\n");
1186 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1187 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1189 if (dwFlags & MCI_WAVE_INPUT)
1190 TRACE("MCI_WAVE_INPUT !\n");
1191 if (dwFlags & MCI_WAVE_OUTPUT)
1192 TRACE("MCI_WAVE_OUTPUT !\n");
1193 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1194 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1195 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1196 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1197 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
1198 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
1199 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
1200 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
1201 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
1202 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
1203 if (dwFlags & MCI_WAVE_SET_CHANNELS)
1204 TRACE("MCI_WAVE_SET_CHANNELS !\n");
1205 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
1206 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
1207 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
1208 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
1212 /**************************************************************************
1213 * WAVE_mciSave [internal]
1215 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1217 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1218 DWORD ret = MCIERR_FILE_NOT_SAVED;
1219 WPARAM wparam = MCI_NOTIFY_FAILURE;
1221 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1222 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1223 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1225 if (dwFlags & MCI_WAIT)
1227 FIXME("MCI_WAIT not implemented\n");
1230 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1231 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1234 ret = mmioClose(wmw->hFile, 0);
1236 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1237 ret = ERROR_SUCCESS;
1240 if (dwFlags & MCI_NOTIFY) {
1241 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1243 mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback),
1244 wmw->openParms.wDeviceID, wparam);
1250 /**************************************************************************
1251 * WAVE_mciStatus [internal]
1253 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1255 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1258 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1259 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1260 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1262 if (dwFlags & MCI_STATUS_ITEM) {
1263 switch (lpParms->dwItem) {
1264 case MCI_STATUS_CURRENT_TRACK:
1265 lpParms->dwReturn = 1;
1266 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1268 case MCI_STATUS_LENGTH:
1270 lpParms->dwReturn = 0;
1271 return MCIERR_UNSUPPORTED_FUNCTION;
1273 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1274 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1275 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1277 case MCI_STATUS_MODE:
1278 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1279 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1280 ret = MCI_RESOURCE_RETURNED;
1282 case MCI_STATUS_MEDIA_PRESENT:
1283 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1284 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1285 ret = MCI_RESOURCE_RETURNED;
1287 case MCI_STATUS_NUMBER_OF_TRACKS:
1288 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1289 lpParms->dwReturn = 1;
1290 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1292 case MCI_STATUS_POSITION:
1294 lpParms->dwReturn = 0;
1295 return MCIERR_UNSUPPORTED_FUNCTION;
1297 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1298 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1299 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1301 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1302 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1304 case MCI_STATUS_READY:
1305 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1306 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1307 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1308 ret = MCI_RESOURCE_RETURNED;
1310 case MCI_STATUS_TIME_FORMAT:
1311 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1312 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1313 ret = MCI_RESOURCE_RETURNED;
1315 case MCI_WAVE_INPUT:
1316 TRACE("MCI_WAVE_INPUT !\n");
1317 lpParms->dwReturn = 0;
1318 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1320 case MCI_WAVE_OUTPUT:
1321 TRACE("MCI_WAVE_OUTPUT !\n");
1324 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1325 lpParms->dwReturn = id;
1327 lpParms->dwReturn = 0;
1328 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1332 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1334 lpParms->dwReturn = 0;
1335 return MCIERR_UNSUPPORTED_FUNCTION;
1337 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1338 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1340 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1342 lpParms->dwReturn = 0;
1343 return MCIERR_UNSUPPORTED_FUNCTION;
1345 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1346 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1348 case MCI_WAVE_STATUS_BLOCKALIGN:
1350 lpParms->dwReturn = 0;
1351 return MCIERR_UNSUPPORTED_FUNCTION;
1353 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1354 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1356 case MCI_WAVE_STATUS_CHANNELS:
1358 lpParms->dwReturn = 0;
1359 return MCIERR_UNSUPPORTED_FUNCTION;
1361 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1362 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1364 case MCI_WAVE_STATUS_FORMATTAG:
1366 lpParms->dwReturn = 0;
1367 return MCIERR_UNSUPPORTED_FUNCTION;
1369 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1370 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1372 case MCI_WAVE_STATUS_LEVEL:
1373 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1374 lpParms->dwReturn = 0xAAAA5555;
1376 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1378 lpParms->dwReturn = 0;
1379 return MCIERR_UNSUPPORTED_FUNCTION;
1381 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1382 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1385 WARN("unknown command %08lX !\n", lpParms->dwItem);
1386 return MCIERR_UNRECOGNIZED_COMMAND;
1389 if (dwFlags & MCI_NOTIFY) {
1390 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1391 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1396 /**************************************************************************
1397 * WAVE_mciGetDevCaps [internal]
1399 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1400 LPMCI_GETDEVCAPS_PARMS lpParms)
1402 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1405 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1407 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1408 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1410 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1411 switch(lpParms->dwItem) {
1412 case MCI_GETDEVCAPS_DEVICE_TYPE:
1413 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1414 ret = MCI_RESOURCE_RETURNED;
1416 case MCI_GETDEVCAPS_HAS_AUDIO:
1417 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1418 ret = MCI_RESOURCE_RETURNED;
1420 case MCI_GETDEVCAPS_HAS_VIDEO:
1421 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1422 ret = MCI_RESOURCE_RETURNED;
1424 case MCI_GETDEVCAPS_USES_FILES:
1425 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1426 ret = MCI_RESOURCE_RETURNED;
1428 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1429 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1430 ret = MCI_RESOURCE_RETURNED;
1432 case MCI_GETDEVCAPS_CAN_RECORD:
1433 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1434 ret = MCI_RESOURCE_RETURNED;
1436 case MCI_GETDEVCAPS_CAN_EJECT:
1437 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1438 ret = MCI_RESOURCE_RETURNED;
1440 case MCI_GETDEVCAPS_CAN_PLAY:
1441 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1442 ret = MCI_RESOURCE_RETURNED;
1444 case MCI_GETDEVCAPS_CAN_SAVE:
1445 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1446 ret = MCI_RESOURCE_RETURNED;
1448 case MCI_WAVE_GETDEVCAPS_INPUTS:
1449 lpParms->dwReturn = 1;
1451 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1452 lpParms->dwReturn = 1;
1455 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1456 return MCIERR_UNRECOGNIZED_COMMAND;
1459 WARN("No GetDevCaps-Item !\n");
1460 return MCIERR_UNRECOGNIZED_COMMAND;
1465 /**************************************************************************
1466 * WAVE_mciInfo [internal]
1468 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1472 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1474 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1476 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1477 ret = MCIERR_NULL_PARAMETER_BLOCK;
1478 } else if (wmw == NULL) {
1479 ret = MCIERR_INVALID_DEVICE_ID;
1481 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1483 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1484 case MCI_INFO_PRODUCT:
1485 str = "Wine's audio player";
1488 str = wmw->openParms.lpstrElementName;
1490 case MCI_WAVE_INPUT:
1491 str = "Wine Wave In";
1493 case MCI_WAVE_OUTPUT:
1494 str = "Wine Wave Out";
1497 WARN("Don't know this info command (%lu)\n", dwFlags);
1498 ret = MCIERR_UNRECOGNIZED_COMMAND;
1502 if (strlen(str) + 1 > lpParms->dwRetSize) {
1503 ret = MCIERR_PARAM_OVERFLOW;
1505 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1508 lpParms->lpstrReturn[0] = 0;
1514 /**************************************************************************
1515 * MCIWAVE_DriverProc [sample driver]
1517 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1518 DWORD dwParam1, DWORD dwParam2)
1520 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1521 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1524 case DRV_LOAD: return 1;
1525 case DRV_FREE: return 1;
1526 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1527 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1528 case DRV_ENABLE: return 1;
1529 case DRV_DISABLE: return 1;
1530 case DRV_QUERYCONFIGURE: return 1;
1531 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1532 case DRV_INSTALL: return DRVCNF_RESTART;
1533 case DRV_REMOVE: return DRVCNF_RESTART;
1534 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1535 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1536 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1537 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1538 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1539 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1540 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1541 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1542 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1543 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1544 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1545 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1546 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1547 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1548 /* commands that should be supported */
1563 FIXME("Unsupported yet command [%lu]\n", wMsg);
1566 TRACE("Unsupported command [%lu]\n", wMsg);
1568 /* option which can be silenced */
1573 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1576 FIXME("is probably wrong msg [%lu]\n", wMsg);
1577 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1579 return MCIERR_UNRECOGNIZED_COMMAND;