1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
10 * - record/play should and must be done asynchronous
11 * - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
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 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
28 HANDLE hCallback; /* Callback handle for pending notification */
29 HMMIO hFile; /* mmio file handle open as Element */
30 MCI_WAVE_OPEN_PARMSA openParms;
31 WAVEOPENDESC waveDesc;
32 WAVEFORMATEX WaveFormat;
34 BOOL fInput; /* FALSE = Output, TRUE = Input */
35 WORD dwStatus; /* one from MCI_MODE_xxxx */
36 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
37 DWORD dwFileOffset; /* Offset of chunk in mmio file */
38 DWORD dwLength; /* number of bytes in chunk for playing */
39 DWORD dwPosition; /* position in bytes in chunk for playing */
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
48 * ===================================================================
49 * =================================================================== */
59 /* EPP DWORD WINAPI mciSendCommandA(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2); */
61 /**************************************************************************
62 * MCI_SCAStarter [internal]
64 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
66 struct SCA* sca = (struct SCA*)arg;
69 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
70 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
71 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
72 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
73 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
74 if (sca->allocatedCopy)
75 HeapFree(GetProcessHeap(), 0, (LPVOID)sca->dwParam2);
76 HeapFree(GetProcessHeap(), 0, sca);
78 WARN("Should not happen ? what's wrong \n");
79 /* should not go after this point */
83 /**************************************************************************
84 * MCI_SendCommandAsync [internal]
86 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
87 DWORD dwParam2, UINT size)
89 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA));
92 return MCIERR_OUT_OF_MEMORY;
96 sca->dwParam1 = dwParam1;
99 sca->dwParam2 = (DWORD)HeapAlloc(GetProcessHeap(), 0, size);
100 if (sca->dwParam2 == 0) {
101 HeapFree(GetProcessHeap(), 0, sca);
102 return MCIERR_OUT_OF_MEMORY;
104 sca->allocatedCopy = TRUE;
105 /* copy structure passed by program in dwParam2 to be sure
106 * we can still use it whatever the program does
108 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
110 sca->dwParam2 = dwParam2;
111 sca->allocatedCopy = FALSE;
114 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
115 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
116 return MCI_SCAStarter(&sca);
121 /*======================================================================*
122 * MCI WAVE implemantation *
123 *======================================================================*/
125 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
127 /**************************************************************************
128 * MCIWAVE_drvOpen [internal]
130 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
132 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
137 wmw->wDevID = modp->wDeviceID;
138 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
139 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
140 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
141 return modp->wDeviceID;
144 /**************************************************************************
145 * MCIWAVE_drvClose [internal]
147 static DWORD WAVE_drvClose(DWORD dwDevID)
149 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
152 HeapFree(GetProcessHeap(), 0, wmw);
153 mciSetDriverData(dwDevID, 0);
159 /**************************************************************************
160 * WAVE_mciGetOpenDev [internal]
162 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
164 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
166 if (wmw == NULL || wmw->nUseCount == 0) {
167 WARN("Invalid wDevID=%u\n", wDevID);
173 /**************************************************************************
174 * WAVE_ConvertByteToTimeFormat [internal]
176 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
180 switch (wmw->dwMciTimeFormat) {
181 case MCI_FORMAT_MILLISECONDS:
182 ret = (val * 1000) / wmw->WaveFormat.nAvgBytesPerSec;
184 case MCI_FORMAT_BYTES:
187 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
188 ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
191 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
193 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
198 /**************************************************************************
199 * WAVE_ConvertTimeFormatToByte [internal]
201 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
205 switch (wmw->dwMciTimeFormat) {
206 case MCI_FORMAT_MILLISECONDS:
207 ret = (val * wmw->WaveFormat.nAvgBytesPerSec) / 1000;
209 case MCI_FORMAT_BYTES:
212 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
213 ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
216 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
218 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
222 /**************************************************************************
223 * WAVE_mciReadFmt [internal]
225 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
229 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
230 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
231 return MCIERR_INVALID_FILE;
232 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
233 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
234 if (mmioRead(wmw->hFile, (HPSTR)&wmw->WaveFormat,
235 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
236 return MCIERR_INVALID_FILE;
238 TRACE("wFormatTag=%04X !\n", wmw->WaveFormat.wFormatTag);
239 TRACE("nChannels=%d \n", wmw->WaveFormat.nChannels);
240 TRACE("nSamplesPerSec=%ld\n", wmw->WaveFormat.nSamplesPerSec);
241 TRACE("nAvgBytesPerSec=%ld\n", wmw->WaveFormat.nAvgBytesPerSec);
242 TRACE("nBlockAlign=%d \n", wmw->WaveFormat.nBlockAlign);
243 TRACE("wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
245 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
246 mmioSeek(wmw->hFile, mmckInfo.dwDataOffset + ((mmckInfo.cksize + 1) & ~1), SEEK_SET);
247 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
248 TRACE("can't find data chunk\n");
249 return MCIERR_INVALID_FILE;
251 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
252 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
253 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
254 wmw->WaveFormat.nChannels, wmw->WaveFormat.nSamplesPerSec);
255 wmw->dwLength = mmckInfo.cksize;
256 wmw->dwFileOffset = mmioSeek(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
260 /**************************************************************************
261 * WAVE_mciOpen [internal]
263 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
267 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
269 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
270 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
271 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
273 if (dwFlags & MCI_OPEN_SHAREABLE)
274 return MCIERR_HARDWARE;
276 if (wmw->nUseCount > 0) {
277 /* The driver is already opened on this channel
278 * Wave driver cannot be shared
280 return MCIERR_DEVICE_OPEN;
284 dwDeviceID = lpOpenParms->wDeviceID;
289 TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
291 if (dwFlags & MCI_OPEN_ELEMENT) {
292 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
293 /* could it be that (DWORD)lpOpenParms->lpstrElementName
294 * contains the hFile value ?
296 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
298 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
300 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
301 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
302 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
303 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL,
304 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
305 if (wmw->hFile == 0) {
306 WARN("can't find file='%s' !\n", lpstrElementName);
307 dwRet = MCIERR_FILE_NOT_FOUND;
314 TRACE("hFile=%u\n", wmw->hFile);
316 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
317 wmw->wNotifyDeviceID = dwDeviceID;
318 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
320 wmw->waveDesc.hWave = 0;
322 if (dwRet == 0 && wmw->hFile != 0) {
325 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
326 dwRet = MCIERR_INVALID_FILE;
328 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
329 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
330 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
331 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
332 dwRet = MCIERR_INVALID_FILE;
334 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
341 wmw->WaveFormat.nAvgBytesPerSec =
342 wmw->WaveFormat.nSamplesPerSec * wmw->WaveFormat.nBlockAlign;
343 wmw->waveDesc.lpFormat = &wmw->WaveFormat;
346 wmw->dwStatus = MCI_MODE_STOP;
350 mmioClose(wmw->hFile, 0);
356 /**************************************************************************
357 * WAVE_mciCue [internal]
359 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
364 This routine is far from complete. At the moment only a check is done on the
365 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
368 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
373 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
375 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
377 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
379 /* always close elements ? */
380 if (wmw->hFile != 0) {
381 mmioClose(wmw->hFile, 0);
385 dwRet = MMSYSERR_NOERROR; /* assume success */
387 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
388 dwRet = waveOutClose(wmw->hWave);
389 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
391 } else if (wmw->fInput) {
392 dwRet = waveInClose(wmw->hWave);
393 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
397 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
400 /**************************************************************************
401 * WAVE_mciStop [internal]
403 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
406 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
408 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
410 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
411 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
413 wmw->dwStatus = MCI_MODE_STOP;
415 TRACE("wmw->dwStatus=%d\n", wmw->dwStatus);
418 dwRet = waveInReset(wmw->hWave);
420 dwRet = waveOutReset(wmw->hWave);
422 if (dwFlags & MCI_NOTIFY) {
423 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
424 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
425 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
428 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
431 /**************************************************************************
432 * WAVE_mciClose [internal]
434 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
437 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
439 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
441 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
443 if (wmw->dwStatus != MCI_MODE_STOP) {
444 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
449 if (wmw->nUseCount == 0) {
451 if (wmw->hFile != 0) {
452 mmioClose(wmw->hFile, 0);
455 mmRet = (wmw->fInput) ? waveInClose(wmw->hWave) : waveOutClose(wmw->hWave);
457 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
460 if ((dwFlags & MCI_NOTIFY) && lpParms) {
461 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
462 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
463 wmw->wNotifyDeviceID,
464 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
469 /**************************************************************************
470 * WAVE_mciPlay [internal]
472 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
478 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
480 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
482 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
483 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
486 WARN("cannot play on input device\n");
487 return MCIERR_NONAPPLICABLE_FUNCTION;
490 if (wmw->hFile == 0) {
491 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
492 return MCIERR_FILE_NOT_FOUND;
495 if (!(dwFlags & MCI_WAIT)) {
496 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
497 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
500 if (wmw->dwStatus != MCI_MODE_STOP) {
501 if (wmw->dwStatus == MCI_MODE_PAUSE) {
502 /* FIXME: parameters (start/end) in lpParams may not be used */
503 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
505 return MCIERR_INTERNAL;
509 if (lpParms && (dwFlags & MCI_FROM)) {
510 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
512 if (lpParms && (dwFlags & MCI_TO)) {
513 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
516 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
518 /* go back to begining of chunk */
519 mmioSeek(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
521 /* By default the device will be opened for output, the MCI_CUE function is there to
522 * change from output to input and back
524 /* FIXME: how to choose between several output channels ? here 0 is forced */
525 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, (LPWAVEFORMATEX)&wmw->WaveFormat, 0L, 0L, CALLBACK_NULL);
527 TRACE("Can't open low level audio device %ld\n", dwRet);
528 return MCIERR_DEVICE_OPEN;
531 /* at 22050 bytes per sec => 30 ms by block */
533 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
534 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
536 wmw->dwStatus = MCI_MODE_PLAY;
538 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
539 /* FIXME: use several WaveHdr for smoother playback */
540 /* FIXME: use only regular MMSYS functions, not calling directly the driver */
541 while (wmw->dwStatus != MCI_MODE_STOP) {
542 wmw->WaveHdr.dwUser = 0L;
543 wmw->WaveHdr.dwFlags = 0L;
544 wmw->WaveHdr.dwLoops = 0L;
545 count = mmioRead(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
546 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
549 wmw->WaveHdr.dwBufferLength = count;
550 dwRet = waveOutPrepareHeader(wmw->hWave, &wmw->WaveHdr, sizeof(WAVEHDR));
551 wmw->WaveHdr.dwBytesRecorded = 0;
552 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
553 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
554 dwRet = waveOutWrite(wmw->hWave, &wmw->WaveHdr, sizeof(WAVEHDR));
555 /* FIXME: should use callback mechanisms from audio driver */
557 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
560 wmw->dwPosition += count;
561 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
562 dwRet = waveOutUnprepareHeader(wmw->hWave, &wmw->WaveHdr, sizeof(WAVEHDR));
565 if (wmw->WaveHdr.lpData != NULL) {
566 GlobalUnlock16(hData);
568 wmw->WaveHdr.lpData = NULL;
571 waveOutReset(wmw->hWave);
572 waveOutClose(wmw->hWave);
574 wmw->dwStatus = MCI_MODE_STOP;
575 if (lpParms && (dwFlags & MCI_NOTIFY)) {
576 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
577 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
578 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
583 /**************************************************************************
584 * WAVE_mciRecord [internal]
586 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
593 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
595 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
597 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
598 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
601 WARN("cannot record on output device\n");
602 return MCIERR_NONAPPLICABLE_FUNCTION;
605 if (wmw->hFile == 0) {
606 WARN("can't find file='%s' !\n",
607 wmw->openParms.lpstrElementName);
608 return MCIERR_FILE_NOT_FOUND;
610 start = 1; end = 99999;
611 if (dwFlags & MCI_FROM) {
612 start = lpParms->dwFrom;
613 TRACE("MCI_FROM=%d \n", start);
615 if (dwFlags & MCI_TO) {
617 TRACE("MCI_TO=%d \n", end);
620 lpWaveHdr = &wmw->WaveHdr;
621 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
622 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
623 lpWaveHdr->dwBufferLength = bufsize;
624 lpWaveHdr->dwUser = 0L;
625 lpWaveHdr->dwFlags = 0L;
626 lpWaveHdr->dwLoops = 0L;
627 dwRet = waveInPrepareHeader(wmw->hWave, lpWaveHdr, sizeof(WAVEHDR));
629 for (;;) { /* FIXME: I don't see any waveInAddBuffer ? */
630 lpWaveHdr->dwBytesRecorded = 0;
631 dwRet = waveInStart(wmw->hWave);
632 TRACE("waveInStart => lpWaveHdr=%p dwBytesRecorded=%lu\n",
633 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
634 if (lpWaveHdr->dwBytesRecorded == 0) break;
636 dwRet = waveInUnprepareHeader(wmw->hWave, lpWaveHdr, sizeof(WAVEHDR));
637 if (lpWaveHdr->lpData != NULL) {
638 GlobalUnlock16(hData);
640 lpWaveHdr->lpData = NULL;
642 if (dwFlags & MCI_NOTIFY) {
643 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
644 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
645 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
650 /**************************************************************************
651 * WAVE_mciPause [internal]
653 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
656 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
658 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
660 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
661 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
663 if (wmw->dwStatus == MCI_MODE_PLAY) {
664 wmw->dwStatus = MCI_MODE_PAUSE;
667 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
668 else dwRet = waveOutPause(wmw->hWave);
670 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
673 /**************************************************************************
674 * WAVE_mciResume [internal]
676 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
678 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
681 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
683 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
684 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
686 if (wmw->dwStatus == MCI_MODE_PAUSE) {
687 wmw->dwStatus = MCI_MODE_PLAY;
690 /* FIXME: I doubt WIDM_START is correct */
691 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
692 else dwRet = waveOutRestart(wmw->hWave);
693 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
696 /**************************************************************************
697 * WAVE_mciSeek [internal]
699 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
702 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
704 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
706 if (lpParms == NULL) {
707 ret = MCIERR_NULL_PARAMETER_BLOCK;
708 } else if (wmw == NULL) {
709 ret = MCIERR_INVALID_DEVICE_ID;
711 WAVE_mciStop(wDevID, MCI_WAIT, 0);
713 if (dwFlags & MCI_SEEK_TO_START) {
715 } else if (dwFlags & MCI_SEEK_TO_END) {
716 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
717 } else if (dwFlags & MCI_TO) {
718 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
720 WARN("dwFlag doesn't tell where to seek to...\n");
721 return MCIERR_MISSING_PARAMETER;
724 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
726 if (dwFlags & MCI_NOTIFY) {
727 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
728 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
729 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
735 /**************************************************************************
736 * WAVE_mciSet [internal]
738 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
740 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
742 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
744 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
745 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
747 if (dwFlags & MCI_SET_TIME_FORMAT) {
748 switch (lpParms->dwTimeFormat) {
749 case MCI_FORMAT_MILLISECONDS:
750 TRACE("MCI_FORMAT_MILLISECONDS !\n");
751 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
753 case MCI_FORMAT_BYTES:
754 TRACE("MCI_FORMAT_BYTES !\n");
755 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
757 case MCI_FORMAT_SAMPLES:
758 TRACE("MCI_FORMAT_SAMPLES !\n");
759 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
762 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
763 return MCIERR_BAD_TIME_FORMAT;
766 if (dwFlags & MCI_SET_VIDEO) {
767 TRACE("No support for video !\n");
768 return MCIERR_UNSUPPORTED_FUNCTION;
770 if (dwFlags & MCI_SET_DOOR_OPEN) {
771 TRACE("No support for door open !\n");
772 return MCIERR_UNSUPPORTED_FUNCTION;
774 if (dwFlags & MCI_SET_DOOR_CLOSED) {
775 TRACE("No support for door close !\n");
776 return MCIERR_UNSUPPORTED_FUNCTION;
778 if (dwFlags & MCI_SET_AUDIO) {
779 if (dwFlags & MCI_SET_ON) {
780 TRACE("MCI_SET_ON audio !\n");
781 } else if (dwFlags & MCI_SET_OFF) {
782 TRACE("MCI_SET_OFF audio !\n");
784 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
785 return MCIERR_BAD_INTEGER;
788 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
789 TRACE("MCI_SET_AUDIO_ALL !\n");
790 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
791 TRACE("MCI_SET_AUDIO_LEFT !\n");
792 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
793 TRACE("MCI_SET_AUDIO_RIGHT !\n");
795 if (dwFlags & MCI_WAVE_INPUT)
796 TRACE("MCI_WAVE_INPUT !\n");
797 if (dwFlags & MCI_WAVE_OUTPUT)
798 TRACE("MCI_WAVE_OUTPUT !\n");
799 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
800 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
801 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
802 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
803 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
804 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
805 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
806 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
807 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
808 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
809 if (dwFlags & MCI_WAVE_SET_CHANNELS)
810 TRACE("MCI_WAVE_SET_CHANNELS !\n");
811 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
812 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
813 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
814 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
818 /**************************************************************************
819 * WAVE_mciStatus [internal]
821 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
823 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
826 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
827 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
828 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
830 if (dwFlags & MCI_STATUS_ITEM) {
831 switch (lpParms->dwItem) {
832 case MCI_STATUS_CURRENT_TRACK:
833 lpParms->dwReturn = 1;
834 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
836 case MCI_STATUS_LENGTH:
837 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
838 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength, &ret);
839 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
841 case MCI_STATUS_MODE:
842 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
843 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
844 ret = MCI_RESOURCE_RETURNED;
846 case MCI_STATUS_MEDIA_PRESENT:
847 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
848 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
849 ret = MCI_RESOURCE_RETURNED;
851 case MCI_STATUS_NUMBER_OF_TRACKS:
852 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
853 lpParms->dwReturn = 1;
854 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
856 case MCI_STATUS_POSITION:
857 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
858 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
859 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
861 TRACE("MCI_STATUS_POSITION %s => %lu\n",
862 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
864 case MCI_STATUS_READY:
865 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
866 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
867 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
868 ret = MCI_RESOURCE_RETURNED;
870 case MCI_STATUS_TIME_FORMAT:
871 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
872 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
873 ret = MCI_RESOURCE_RETURNED;
876 TRACE("MCI_WAVE_INPUT !\n");
877 lpParms->dwReturn = 0;
879 case MCI_WAVE_OUTPUT:
880 TRACE("MCI_WAVE_OUTPUT !\n");
881 lpParms->dwReturn = 0;
883 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
884 lpParms->dwReturn = wmw->WaveFormat.nAvgBytesPerSec;
885 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
887 case MCI_WAVE_STATUS_BITSPERSAMPLE:
888 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
889 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
891 case MCI_WAVE_STATUS_BLOCKALIGN:
892 lpParms->dwReturn = wmw->WaveFormat.nBlockAlign;
893 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
895 case MCI_WAVE_STATUS_CHANNELS:
896 lpParms->dwReturn = wmw->WaveFormat.nChannels;
897 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
899 case MCI_WAVE_STATUS_FORMATTAG:
900 lpParms->dwReturn = wmw->WaveFormat.wFormatTag;
901 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
903 case MCI_WAVE_STATUS_LEVEL:
904 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
905 lpParms->dwReturn = 0xAAAA5555;
907 case MCI_WAVE_STATUS_SAMPLESPERSEC:
908 lpParms->dwReturn = wmw->WaveFormat.nSamplesPerSec;
909 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
912 WARN("unknown command %08lX !\n", lpParms->dwItem);
913 return MCIERR_UNRECOGNIZED_COMMAND;
916 if (dwFlags & MCI_NOTIFY) {
917 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
918 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
919 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
924 /**************************************************************************
925 * WAVE_mciGetDevCaps [internal]
927 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
928 LPMCI_GETDEVCAPS_PARMS lpParms)
930 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
933 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
935 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
936 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
938 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
939 switch(lpParms->dwItem) {
940 case MCI_GETDEVCAPS_DEVICE_TYPE:
941 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
942 ret = MCI_RESOURCE_RETURNED;
944 case MCI_GETDEVCAPS_HAS_AUDIO:
945 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
946 ret = MCI_RESOURCE_RETURNED;
948 case MCI_GETDEVCAPS_HAS_VIDEO:
949 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
950 ret = MCI_RESOURCE_RETURNED;
952 case MCI_GETDEVCAPS_USES_FILES:
953 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
954 ret = MCI_RESOURCE_RETURNED;
956 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
957 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
958 ret = MCI_RESOURCE_RETURNED;
960 case MCI_GETDEVCAPS_CAN_RECORD:
961 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
962 ret = MCI_RESOURCE_RETURNED;
964 case MCI_GETDEVCAPS_CAN_EJECT:
965 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
966 ret = MCI_RESOURCE_RETURNED;
968 case MCI_GETDEVCAPS_CAN_PLAY:
969 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
970 ret = MCI_RESOURCE_RETURNED;
972 case MCI_GETDEVCAPS_CAN_SAVE:
973 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
974 ret = MCI_RESOURCE_RETURNED;
976 case MCI_WAVE_GETDEVCAPS_INPUTS:
977 lpParms->dwReturn = 1;
979 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
980 lpParms->dwReturn = 1;
983 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
984 return MCIERR_UNRECOGNIZED_COMMAND;
987 WARN("No GetDevCaps-Item !\n");
988 return MCIERR_UNRECOGNIZED_COMMAND;
993 /**************************************************************************
994 * WAVE_mciInfo [internal]
996 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1000 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1002 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1004 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1005 ret = MCIERR_NULL_PARAMETER_BLOCK;
1006 } else if (wmw == NULL) {
1007 ret = MCIERR_INVALID_DEVICE_ID;
1009 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1012 case MCI_INFO_PRODUCT:
1013 str = "Wine's audio player";
1016 str = wmw->openParms.lpstrElementName;
1018 case MCI_WAVE_INPUT:
1019 str = "Wine Wave In";
1021 case MCI_WAVE_OUTPUT:
1022 str = "Wine Wave Out";
1025 WARN("Don't know this info command (%lu)\n", dwFlags);
1026 ret = MCIERR_UNRECOGNIZED_COMMAND;
1030 if (strlen(str) + 1 > lpParms->dwRetSize) {
1031 ret = MCIERR_PARAM_OVERFLOW;
1033 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1036 lpParms->lpstrReturn[0] = 0;
1042 /**************************************************************************
1043 * MCIWAVE_DriverProc [sample driver]
1045 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1046 DWORD dwParam1, DWORD dwParam2)
1048 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1049 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1052 case DRV_LOAD: return 1;
1053 case DRV_FREE: return 1;
1054 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1055 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1056 case DRV_ENABLE: return 1;
1057 case DRV_DISABLE: return 1;
1058 case DRV_QUERYCONFIGURE: return 1;
1059 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1060 case DRV_INSTALL: return DRVCNF_RESTART;
1061 case DRV_REMOVE: return DRVCNF_RESTART;
1062 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1063 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1064 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1065 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1066 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1067 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1068 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1069 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1070 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1071 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1072 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1073 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1074 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1075 /* commands that should be supported */
1091 FIXME("Unsupported yet command [%lu]\n", wMsg);
1094 TRACE("Unsupported command [%lu]\n", wMsg);
1098 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1101 FIXME("is probably wrong msg [%lu]\n", wMsg);
1102 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1104 return MCIERR_UNRECOGNIZED_COMMAND;