1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
5 * Copyright 1994 Martin Ayotte
9 * - record/play should and must be done asynchronous
10 * - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
19 #include "multimedia.h"
21 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(mciwave)
28 int nUseCount; /* Incremented for each shared open */
29 BOOL16 fShareable; /* TRUE if first open was shareable */
30 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
31 HANDLE16 hCallback; /* Callback handle for pending notification */
32 HMMIO hFile; /* mmio file handle open as Element */
33 MCI_WAVE_OPEN_PARMSA openParms;
34 WAVEOPENDESC waveDesc;
35 PCMWAVEFORMAT WaveFormat;
37 BOOL16 fInput; /* FALSE = Output, TRUE = Input */
38 WORD dwStatus; /* one from MCI_MODE_xxxx */
39 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
40 DWORD dwFileOffset; /* Offset of chunk in mmio file */
41 DWORD dwLength; /* number of bytes in chunk for playing */
42 DWORD dwPosition; /* position in bytes in chunk for playing */
45 /*======================================================================*
46 * MCI WAVE implemantation *
47 *======================================================================*/
49 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
51 /**************************************************************************
52 * MCIWAVE_drvOpen [internal]
54 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
56 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
61 wmw->wDevID = modp->wDeviceID;
62 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
63 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
64 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
65 return modp->wDeviceID;
68 /**************************************************************************
69 * MCIWAVE_drvClose [internal]
71 static DWORD WAVE_drvClose(DWORD dwDevID)
73 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
76 HeapFree(GetProcessHeap(), 0, wmw);
77 mciSetDriverData(dwDevID, 0);
83 /**************************************************************************
84 * WAVE_mciGetOpenDev [internal]
86 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT16 wDevID)
88 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
90 if (wmw == NULL || wmw->nUseCount == 0) {
91 WARN("Invalid wDevID=%u\n", wDevID);
97 /**************************************************************************
98 * WAVE_ConvertByteToTimeFormat [internal]
100 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
104 switch (wmw->dwMciTimeFormat) {
105 case MCI_FORMAT_MILLISECONDS:
106 ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
108 case MCI_FORMAT_BYTES:
111 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
112 ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
115 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
117 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
122 /**************************************************************************
123 * WAVE_ConvertTimeFormatToByte [internal]
125 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
129 switch (wmw->dwMciTimeFormat) {
130 case MCI_FORMAT_MILLISECONDS:
131 ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
133 case MCI_FORMAT_BYTES:
136 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
137 ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
140 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
142 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
146 /**************************************************************************
147 * WAVE_mciReadFmt [internal]
149 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
153 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
154 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
155 return MCIERR_INVALID_FILE;
156 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
157 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
158 if (mmioRead(wmw->hFile, (HPSTR)&wmw->WaveFormat,
159 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
160 return MCIERR_INVALID_FILE;
162 TRACE("wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
163 TRACE("nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
164 TRACE("nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
165 TRACE("nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
166 TRACE("nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
167 TRACE("wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
169 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
170 mmioSeek(wmw->hFile, mmckInfo.dwDataOffset + ((mmckInfo.cksize + 1) & ~1), SEEK_SET);
171 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
172 TRACE("can't find data chunk\n");
173 return MCIERR_INVALID_FILE;
175 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
176 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
177 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
178 wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
179 wmw->dwLength = mmckInfo.cksize;
180 wmw->dwFileOffset = mmioSeek(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
184 /**************************************************************************
185 * WAVE_mciOpen [internal]
187 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
191 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
193 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
194 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
195 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
197 if (dwFlags & MCI_OPEN_SHAREABLE)
198 return MCIERR_HARDWARE;
200 if (wmw->nUseCount > 0) {
201 /* The driver is already opened on this channel
202 * Wave driver cannot be shared
204 return MCIERR_DEVICE_OPEN;
208 dwDeviceID = lpOpenParms->wDeviceID;
213 TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
215 if (dwFlags & MCI_OPEN_ELEMENT) {
216 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
217 /* could it be that (DWORD)lpOpenParms->lpstrElementName
218 * contains the hFile value ?
220 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
222 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
224 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
225 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
226 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
227 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL,
228 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
229 if (wmw->hFile == 0) {
230 WARN("can't find file='%s' !\n", lpstrElementName);
231 dwRet = MCIERR_FILE_NOT_FOUND;
238 TRACE("hFile=%u\n", wmw->hFile);
240 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
241 wmw->wNotifyDeviceID = dwDeviceID;
242 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
244 wmw->waveDesc.hWave = 0;
246 if (dwRet == 0 && wmw->hFile != 0) {
249 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
250 dwRet = MCIERR_INVALID_FILE;
252 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
253 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
254 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
255 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
256 dwRet = MCIERR_INVALID_FILE;
258 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
265 wmw->WaveFormat.wf.nAvgBytesPerSec =
266 wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
267 wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
270 wmw->dwStatus = MCI_MODE_STOP;
274 mmioClose(wmw->hFile, 0);
280 /**************************************************************************
281 * WAVE_mciCue [internal]
283 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
288 This routine is far from complete. At the moment only a check is done on the
289 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
292 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
297 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
299 TRACE("(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
301 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
303 /* always close elements ? */
304 if (wmw->hFile != 0) {
305 mmioClose(wmw->hFile, 0);
309 dwRet = MMSYSERR_NOERROR; /* assume success */
311 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
312 dwRet = wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
313 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
314 dwRet = widMessage(wmw->wWavID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
316 } else if (wmw->fInput) {
317 dwRet = widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L);
318 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
319 dwRet = wodMessage(wmw->wWavID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
322 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
325 /**************************************************************************
326 * WAVE_mciStop [internal]
328 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
331 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
333 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
335 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
336 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
338 wmw->dwStatus = MCI_MODE_STOP;
340 TRACE("wmw->dwStatus=%d\n", wmw->dwStatus);
343 dwRet = widMessage(wmw->wWavID, WIDM_RESET, 0, dwFlags, (DWORD)lpParms);
345 dwRet = wodMessage(wmw->wWavID, WODM_RESET, 0, dwFlags, (DWORD)lpParms);
347 if (dwFlags & MCI_NOTIFY) {
348 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
349 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
350 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
353 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
356 /**************************************************************************
357 * WAVE_mciClose [internal]
359 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
362 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
364 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
366 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
368 if (wmw->dwStatus != MCI_MODE_STOP) {
369 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
374 if (wmw->nUseCount == 0) {
376 if (wmw->hFile != 0) {
377 mmioClose(wmw->hFile, 0);
380 mmRet = (wmw->fInput) ? widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L) :
381 wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
383 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
386 if ((dwFlags & MCI_NOTIFY) && lpParms) {
387 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
388 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
389 wmw->wNotifyDeviceID,
390 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
395 /**************************************************************************
396 * WAVE_mciPlay [internal]
398 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
404 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
406 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
408 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
409 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
412 WARN("cannot play on input device\n");
413 return MCIERR_NONAPPLICABLE_FUNCTION;
416 if (wmw->hFile == 0) {
417 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
418 return MCIERR_FILE_NOT_FOUND;
421 if (!(dwFlags & MCI_WAIT)) {
422 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
423 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
426 if (wmw->dwStatus != MCI_MODE_STOP) {
427 if (wmw->dwStatus == MCI_MODE_PAUSE) {
428 /* FIXME: parameters (start/end) in lpParams may not be used */
429 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
431 return MCIERR_INTERNAL;
435 if (lpParms && (dwFlags & MCI_FROM)) {
436 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
438 if (lpParms && (dwFlags & MCI_TO)) {
439 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
442 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
444 /* go back to begining of chunk */
445 mmioSeek(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
447 /* By default the device will be opened for output, the MCI_CUE function is there to
448 * change from output to input and back
450 /* FIXME: how to choose between several output channels ? here 0 is forced */
451 dwRet = wodMessage(0, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
453 TRACE("Can't open low level audio device %ld\n", dwRet);
454 return MCIERR_DEVICE_OPEN;
457 /* at 22050 bytes per sec => 30 ms by block */
459 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
460 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
462 wmw->dwStatus = MCI_MODE_PLAY;
464 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
465 /* FIXME: use several WaveHdr for smoother playback */
466 /* FIXME: use only regular MMSYS functions, not calling directly the driver */
467 while (wmw->dwStatus != MCI_MODE_STOP) {
468 wmw->WaveHdr.dwUser = 0L;
469 wmw->WaveHdr.dwFlags = 0L;
470 wmw->WaveHdr.dwLoops = 0L;
471 count = mmioRead(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
472 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
475 dwRet = wodMessage(wmw->wWavID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
476 wmw->WaveHdr.dwBufferLength = count;
477 wmw->WaveHdr.dwBytesRecorded = 0;
479 wmw->WaveHdr.reserved = (DWORD)&wmw->WaveHdr;
480 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
481 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
482 dwRet = wodMessage(wmw->wWavID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
483 /* FIXME: should use callback mechanisms from audio driver */
485 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
488 wmw->dwPosition += count;
489 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
490 dwRet = wodMessage(wmw->wWavID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
493 if (wmw->WaveHdr.lpData != NULL) {
494 GlobalUnlock16(hData);
496 wmw->WaveHdr.lpData = NULL;
499 wodMessage(wmw->wWavID, WODM_RESET, 0, 0L, 0L);
500 wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
502 wmw->dwStatus = MCI_MODE_STOP;
503 if (lpParms && (dwFlags & MCI_NOTIFY)) {
504 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
505 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
506 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
511 /**************************************************************************
512 * WAVE_mciRecord [internal]
514 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
521 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
523 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
525 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
526 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
529 WARN("cannot record on output device\n");
530 return MCIERR_NONAPPLICABLE_FUNCTION;
533 if (wmw->hFile == 0) {
534 WARN("can't find file='%s' !\n",
535 wmw->openParms.lpstrElementName);
536 return MCIERR_FILE_NOT_FOUND;
538 start = 1; end = 99999;
539 if (dwFlags & MCI_FROM) {
540 start = lpParms->dwFrom;
541 TRACE("MCI_FROM=%d \n", start);
543 if (dwFlags & MCI_TO) {
545 TRACE("MCI_TO=%d \n", end);
548 lpWaveHdr = &wmw->WaveHdr;
549 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
550 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
551 lpWaveHdr->dwBufferLength = bufsize;
552 lpWaveHdr->dwUser = 0L;
553 lpWaveHdr->dwFlags = 0L;
554 lpWaveHdr->dwLoops = 0L;
555 dwRet = widMessage(wmw->wWavID, WIDM_PREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
556 TRACE("after WIDM_PREPARE \n");
558 lpWaveHdr->dwBytesRecorded = 0;
559 dwRet = widMessage(wmw->wWavID, WIDM_START, 0, 0L, 0L);
560 TRACE("after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
561 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
562 if (lpWaveHdr->dwBytesRecorded == 0) break;
564 TRACE("before WIDM_UNPREPARE \n");
565 dwRet = widMessage(wmw->wWavID, WIDM_UNPREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
566 TRACE("after WIDM_UNPREPARE \n");
567 if (lpWaveHdr->lpData != NULL) {
568 GlobalUnlock16(hData);
570 lpWaveHdr->lpData = NULL;
572 if (dwFlags & MCI_NOTIFY) {
573 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
574 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
575 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
580 /**************************************************************************
581 * WAVE_mciPause [internal]
583 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
586 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
588 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
590 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
591 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
593 if (wmw->dwStatus == MCI_MODE_PLAY) {
594 wmw->dwStatus = MCI_MODE_PAUSE;
597 if (wmw->fInput) dwRet = widMessage(wmw->wWavID, WIDM_PAUSE, 0, 0L, 0L);
598 else dwRet = wodMessage(wmw->wWavID, WODM_PAUSE, 0, 0L, 0L);
600 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
603 /**************************************************************************
604 * WAVE_mciResume [internal]
606 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
608 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
611 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
613 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
614 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
616 if (wmw->dwStatus == MCI_MODE_PAUSE) {
617 wmw->dwStatus = MCI_MODE_PLAY;
620 /* FIXME: I doubt WIDM_START is correct */
621 if (wmw->fInput) dwRet = widMessage(wmw->wWavID, WIDM_START, 0, 0L, 0L);
622 else dwRet = wodMessage(wmw->wWavID, WODM_RESTART, 0, 0L, 0L);
623 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
626 /**************************************************************************
627 * WAVE_mciSeek [internal]
629 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
632 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
634 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
636 if (lpParms == NULL) {
637 ret = MCIERR_NULL_PARAMETER_BLOCK;
638 } else if (wmw == NULL) {
639 ret = MCIERR_INVALID_DEVICE_ID;
641 WAVE_mciStop(wDevID, MCI_WAIT, 0);
643 if (dwFlags & MCI_SEEK_TO_START) {
645 } else if (dwFlags & MCI_SEEK_TO_END) {
646 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
647 } else if (dwFlags & MCI_TO) {
648 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
650 WARN("dwFlag doesn't tell where to seek to...\n");
651 return MCIERR_MISSING_PARAMETER;
654 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
656 if (dwFlags & MCI_NOTIFY) {
657 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
658 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
659 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
665 /**************************************************************************
666 * WAVE_mciSet [internal]
668 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
670 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
672 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
674 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
675 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
677 if (dwFlags & MCI_SET_TIME_FORMAT) {
678 switch (lpParms->dwTimeFormat) {
679 case MCI_FORMAT_MILLISECONDS:
680 TRACE("MCI_FORMAT_MILLISECONDS !\n");
681 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
683 case MCI_FORMAT_BYTES:
684 TRACE("MCI_FORMAT_BYTES !\n");
685 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
687 case MCI_FORMAT_SAMPLES:
688 TRACE("MCI_FORMAT_SAMPLES !\n");
689 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
692 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
693 return MCIERR_BAD_TIME_FORMAT;
696 if (dwFlags & MCI_SET_VIDEO) {
697 TRACE("No support for video !\n");
698 return MCIERR_UNSUPPORTED_FUNCTION;
700 if (dwFlags & MCI_SET_DOOR_OPEN) {
701 TRACE("No support for door open !\n");
702 return MCIERR_UNSUPPORTED_FUNCTION;
704 if (dwFlags & MCI_SET_DOOR_CLOSED) {
705 TRACE("No support for door close !\n");
706 return MCIERR_UNSUPPORTED_FUNCTION;
708 if (dwFlags & MCI_SET_AUDIO) {
709 if (dwFlags & MCI_SET_ON) {
710 TRACE("MCI_SET_ON audio !\n");
711 } else if (dwFlags & MCI_SET_OFF) {
712 TRACE("MCI_SET_OFF audio !\n");
714 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
715 return MCIERR_BAD_INTEGER;
718 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
719 TRACE("MCI_SET_AUDIO_ALL !\n");
720 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
721 TRACE("MCI_SET_AUDIO_LEFT !\n");
722 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
723 TRACE("MCI_SET_AUDIO_RIGHT !\n");
725 if (dwFlags & MCI_WAVE_INPUT)
726 TRACE("MCI_WAVE_INPUT !\n");
727 if (dwFlags & MCI_WAVE_OUTPUT)
728 TRACE("MCI_WAVE_OUTPUT !\n");
729 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
730 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
731 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
732 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
733 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
734 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
735 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
736 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
737 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
738 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
739 if (dwFlags & MCI_WAVE_SET_CHANNELS)
740 TRACE("MCI_WAVE_SET_CHANNELS !\n");
741 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
742 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
743 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
744 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
748 /**************************************************************************
749 * WAVE_mciStatus [internal]
751 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
753 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
756 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
757 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
758 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
760 if (dwFlags & MCI_STATUS_ITEM) {
761 switch(lpParms->dwItem) {
762 case MCI_STATUS_CURRENT_TRACK:
763 lpParms->dwReturn = 1;
764 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
766 case MCI_STATUS_LENGTH:
767 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
768 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength, &ret);
769 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
771 case MCI_STATUS_MODE:
772 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
773 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
774 ret = MCI_RESOURCE_RETURNED;
776 case MCI_STATUS_MEDIA_PRESENT:
777 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
778 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
779 ret = MCI_RESOURCE_RETURNED;
781 case MCI_STATUS_NUMBER_OF_TRACKS:
782 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
783 lpParms->dwReturn = 1;
784 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
786 case MCI_STATUS_POSITION:
787 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
788 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
789 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
791 TRACE("MCI_STATUS_POSITION %s => %lu\n",
792 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
794 case MCI_STATUS_READY:
795 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
796 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
797 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
798 ret = MCI_RESOURCE_RETURNED;
800 case MCI_STATUS_TIME_FORMAT:
801 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
802 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
803 ret = MCI_RESOURCE_RETURNED;
806 TRACE("MCI_WAVE_INPUT !\n");
807 lpParms->dwReturn = 0;
809 case MCI_WAVE_OUTPUT:
810 TRACE("MCI_WAVE_OUTPUT !\n");
811 lpParms->dwReturn = 0;
813 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
814 lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
815 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
817 case MCI_WAVE_STATUS_BITSPERSAMPLE:
818 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
819 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
821 case MCI_WAVE_STATUS_BLOCKALIGN:
822 lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
823 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
825 case MCI_WAVE_STATUS_CHANNELS:
826 lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
827 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
829 case MCI_WAVE_STATUS_FORMATTAG:
830 lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
831 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
833 case MCI_WAVE_STATUS_LEVEL:
834 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
835 lpParms->dwReturn = 0xAAAA5555;
837 case MCI_WAVE_STATUS_SAMPLESPERSEC:
838 lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
839 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
842 WARN("unknown command %08lX !\n", lpParms->dwItem);
843 return MCIERR_UNRECOGNIZED_COMMAND;
846 if (dwFlags & MCI_NOTIFY) {
847 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
848 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
849 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
854 /**************************************************************************
855 * WAVE_mciGetDevCaps [internal]
857 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
858 LPMCI_GETDEVCAPS_PARMS lpParms)
860 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
863 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
865 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
866 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
868 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
869 switch(lpParms->dwItem) {
870 case MCI_GETDEVCAPS_DEVICE_TYPE:
871 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
872 ret = MCI_RESOURCE_RETURNED;
874 case MCI_GETDEVCAPS_HAS_AUDIO:
875 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
876 ret = MCI_RESOURCE_RETURNED;
878 case MCI_GETDEVCAPS_HAS_VIDEO:
879 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
880 ret = MCI_RESOURCE_RETURNED;
882 case MCI_GETDEVCAPS_USES_FILES:
883 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
884 ret = MCI_RESOURCE_RETURNED;
886 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
887 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
888 ret = MCI_RESOURCE_RETURNED;
890 case MCI_GETDEVCAPS_CAN_RECORD:
891 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
892 ret = MCI_RESOURCE_RETURNED;
894 case MCI_GETDEVCAPS_CAN_EJECT:
895 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
896 ret = MCI_RESOURCE_RETURNED;
898 case MCI_GETDEVCAPS_CAN_PLAY:
899 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
900 ret = MCI_RESOURCE_RETURNED;
902 case MCI_GETDEVCAPS_CAN_SAVE:
903 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
904 ret = MCI_RESOURCE_RETURNED;
906 case MCI_WAVE_GETDEVCAPS_INPUTS:
907 lpParms->dwReturn = 1;
909 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
910 lpParms->dwReturn = 1;
913 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
914 return MCIERR_UNRECOGNIZED_COMMAND;
917 WARN("No GetDevCaps-Item !\n");
918 return MCIERR_UNRECOGNIZED_COMMAND;
923 /**************************************************************************
924 * WAVE_mciInfo [internal]
926 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
930 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
932 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
934 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
935 ret = MCIERR_NULL_PARAMETER_BLOCK;
936 } else if (wmw == NULL) {
937 ret = MCIERR_INVALID_DEVICE_ID;
939 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
942 case MCI_INFO_PRODUCT:
943 str = "Wine's audio player";
946 str = wmw->openParms.lpstrElementName;
949 str = "Wine Wave In";
951 case MCI_WAVE_OUTPUT:
952 str = "Wine Wave Out";
955 WARN("Don't know this info command (%lu)\n", dwFlags);
956 ret = MCIERR_UNRECOGNIZED_COMMAND;
960 if (strlen(str) + 1 > lpParms->dwRetSize) {
961 ret = MCIERR_PARAM_OVERFLOW;
963 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
966 lpParms->lpstrReturn[0] = 0;
972 /**************************************************************************
973 * MCIWAVE_DriverProc [sample driver]
975 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
976 DWORD dwParam1, DWORD dwParam2)
978 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
979 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
982 case DRV_LOAD: return 1;
983 case DRV_FREE: return 1;
984 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
985 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
986 case DRV_ENABLE: return 1;
987 case DRV_DISABLE: return 1;
988 case DRV_QUERYCONFIGURE: return 1;
989 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
990 case DRV_INSTALL: return DRVCNF_RESTART;
991 case DRV_REMOVE: return DRVCNF_RESTART;
992 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
993 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
994 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
995 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
996 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
997 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
998 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
999 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1000 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1001 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1002 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1003 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMS16) dwParam2);
1004 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1005 /* commands that should be supported */
1021 FIXME("Unsupported yet command=%s\n", MCI_MessageToString(wMsg));
1024 TRACE("Unsupported command=%s\n", MCI_MessageToString(wMsg));
1028 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1031 FIXME("is probably wrong msg=%s\n", MCI_MessageToString(wMsg));
1032 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1034 return MCIERR_UNRECOGNIZED_COMMAND;