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)
22 #include <sys/ioctl.h>
25 #include "multimedia.h"
30 DEFAULT_DEBUG_CHANNEL(mciwave)
32 #define MAX_MCIWAVEDRV (1)
37 int nUseCount; /* Incremented for each shared open */
38 BOOL16 fShareable; /* TRUE if first open was shareable */
39 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
40 HANDLE16 hCallback; /* Callback handle for pending notification */
41 HMMIO hFile; /* mmio file handle open as Element */
42 MCI_WAVE_OPEN_PARMSA openParms;
43 WAVEOPENDESC waveDesc;
44 PCMWAVEFORMAT WaveFormat;
46 BOOL16 fInput; /* FALSE = Output, TRUE = Input */
47 WORD dwStatus; /* one from MCI_MODE_xxxx */
48 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
49 DWORD dwFileOffset; /* Offset of chunk in mmio file */
50 DWORD dwLength; /* number of bytes in chunk for playing */
51 DWORD dwPosition; /* position in bytes in chunk for playing */
54 static WINE_MCIWAVE MCIWaveDev[MAX_MCIWAVEDRV];
56 /*======================================================================*
57 * MCI WAVE implemantation *
58 *======================================================================*/
60 /**************************************************************************
61 * MCIWAVE_drvGetDrv [internal]
63 static WINE_MCIWAVE* WAVE_drvGetDrv(UINT16 wDevID)
67 for (i = 0; i < MAX_MCIWAVEDRV; i++) {
68 if (MCIWaveDev[i].wDevID == wDevID) {
69 return &MCIWaveDev[i];
75 /**************************************************************************
76 * MCIWAVE_drvOpen [internal]
78 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
82 for (i = 0; i < MAX_MCIWAVEDRV; i++) {
83 if (MCIWaveDev[i].wDevID == 0) {
84 MCIWaveDev[i].wDevID = modp->wDeviceID;
85 modp->wCustomCommandTable = -1;
86 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
87 return modp->wDeviceID;
93 /**************************************************************************
94 * MCIWAVE_drvClose [internal]
96 static DWORD WAVE_drvClose(DWORD dwDevID)
98 WINE_MCIWAVE* wmcda = WAVE_drvGetDrv(dwDevID);
107 /**************************************************************************
108 * WAVE_mciGetOpenDev [internal]
110 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT16 wDevID)
112 WINE_MCIWAVE* wmw = WAVE_drvGetDrv(wDevID);
114 if (wmw == NULL || wmw->nUseCount == 0) {
115 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
121 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
125 switch (wmw->dwMciTimeFormat) {
126 case MCI_FORMAT_MILLISECONDS:
127 ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
129 case MCI_FORMAT_BYTES:
132 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
133 ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
136 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
138 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
142 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
146 switch (wmw->dwMciTimeFormat) {
147 case MCI_FORMAT_MILLISECONDS:
148 ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
150 case MCI_FORMAT_BYTES:
153 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
154 ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
157 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
159 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
163 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
167 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
168 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
169 return MCIERR_INVALID_FILE;
170 TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
171 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
172 if (mmioRead(wmw->hFile, (HPSTR)&wmw->WaveFormat,
173 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
174 return MCIERR_INVALID_FILE;
176 TRACE(mciwave, "wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
177 TRACE(mciwave, "nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
178 TRACE(mciwave, "nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
179 TRACE(mciwave, "nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
180 TRACE(mciwave, "nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
181 TRACE(mciwave, "wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
182 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
183 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
184 return MCIERR_INVALID_FILE;
185 TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
186 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
187 TRACE(mciwave, "nChannels=%d nSamplesPerSec=%ld\n",
188 wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
189 wmw->dwLength = mmckInfo.cksize;
190 wmw->dwFileOffset = mmioSeek(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
194 /**************************************************************************
195 * WAVE_mciOpen [internal]
197 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
201 WINE_MCIWAVE* wmw = WAVE_drvGetDrv(wDevID);
203 TRACE(mciwave, "(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
204 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
205 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
207 if (dwFlags & MCI_OPEN_SHAREABLE)
208 return MCIERR_HARDWARE;
210 if (wmw->nUseCount > 0) {
211 /* The driver is already opened on this channel
212 * Wave driver cannot be shared
214 return MCIERR_DEVICE_OPEN;
218 dwDeviceID = lpOpenParms->wDeviceID;
223 TRACE(mciwave, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
225 if (dwFlags & MCI_OPEN_ELEMENT) {
226 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
227 /* could it be that (DWORD)lpOpenParms->lpstrElementName
228 * contains the hFile value ?
230 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
232 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
234 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
235 TRACE(mciwave, "MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
236 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
237 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL,
238 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
239 if (wmw->hFile == 0) {
240 WARN(mciwave, "can't find file='%s' !\n", lpstrElementName);
241 dwRet = MCIERR_FILE_NOT_FOUND;
248 TRACE(mciwave, "hFile=%u\n", wmw->hFile);
250 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
251 wmw->wNotifyDeviceID = dwDeviceID;
252 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
254 wmw->waveDesc.hWave = 0;
256 if (dwRet == 0 && wmw->hFile != 0) {
259 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
260 dwRet = MCIERR_INVALID_FILE;
262 TRACE(mciwave, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
263 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
264 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
265 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
266 dwRet = MCIERR_INVALID_FILE;
268 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
275 wmw->WaveFormat.wf.nAvgBytesPerSec =
276 wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
277 wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
280 wmw->dwStatus = MCI_MODE_STOP;
284 mmioClose(wmw->hFile, 0);
290 /**************************************************************************
291 * WAVE_mciCue [internal]
293 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
298 This routine is far from complete. At the moment only a check is done on the
299 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
302 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
307 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
309 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
311 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
313 /* always close elements ? */
314 if (wmw->hFile != 0) {
315 mmioClose(wmw->hFile, 0);
319 dwRet = MMSYSERR_NOERROR; /* assume success */
321 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
322 dwRet = wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
323 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
324 dwRet = widMessage(wmw->wWavID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
326 } else if (wmw->fInput) {
327 dwRet = widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L);
328 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
329 dwRet = wodMessage(wmw->wWavID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
332 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
335 /**************************************************************************
336 * WAVE_mciStop [internal]
338 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
341 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
343 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
345 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
346 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
348 wmw->dwStatus = MCI_MODE_STOP;
350 TRACE(mciwave, "wmw->dwStatus=%d\n", wmw->dwStatus);
353 dwRet = widMessage(wmw->wWavID, WIDM_STOP, 0, dwFlags, (DWORD)lpParms);
355 dwRet = wodMessage(wmw->wWavID, WODM_STOP, 0, dwFlags, (DWORD)lpParms);
357 if (dwFlags & MCI_NOTIFY) {
358 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
359 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
360 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
363 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
366 /**************************************************************************
367 * WAVE_mciClose [internal]
369 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
372 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
374 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
376 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
378 if (wmw->dwStatus != MCI_MODE_STOP) {
379 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
384 if (wmw->nUseCount == 0) {
386 if (wmw->hFile != 0) {
387 mmioClose(wmw->hFile, 0);
390 mmRet = (wmw->fInput) ? widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L) :
391 wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
393 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
396 if ((dwFlags & MCI_NOTIFY) && lpParms) {
397 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
398 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
399 wmw->wNotifyDeviceID,
400 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
405 /**************************************************************************
406 * WAVE_mciPlay [internal]
408 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
414 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
416 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
418 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
419 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
422 WARN(mciwave, "cannot play on input device\n");
423 return MCIERR_NONAPPLICABLE_FUNCTION;
426 if (wmw->hFile == 0) {
427 WARN(mciwave, "Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
428 return MCIERR_FILE_NOT_FOUND;
431 if (!(dwFlags & MCI_WAIT)) {
432 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
433 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
437 if (lpParms && (dwFlags & MCI_FROM)) {
438 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
440 if (lpParms && (dwFlags & MCI_TO)) {
441 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
444 TRACE(mciwave, "Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
446 /* go back to begining of chunk */
447 mmioSeek(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
449 /* By default the device will be opened for output, the MCI_CUE function is there to
450 * change from output to input and back
452 /* FIXME: how to choose between several output channels ? here 0 is forced */
453 dwRet = wodMessage(0, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
455 TRACE(mciwave, "Can't open low level audio device %ld\n", dwRet);
456 return MCIERR_DEVICE_OPEN;
459 /* at 22050 bytes per sec => 30 ms by block */
461 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
462 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
464 wmw->dwStatus = MCI_MODE_PLAY;
466 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
467 /* FIXME: use several WaveHdr for smoother playback */
468 /* FIXME: use only regular MMSYS functions, not calling directly the driver */
469 while (wmw->dwStatus != MCI_MODE_STOP) {
470 wmw->WaveHdr.dwUser = 0L;
471 wmw->WaveHdr.dwFlags = 0L;
472 wmw->WaveHdr.dwLoops = 0L;
473 count = mmioRead(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
474 TRACE(mciwave, "mmioRead bufsize=%ld count=%ld\n", bufsize, count);
477 dwRet = wodMessage(wmw->wWavID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
478 wmw->WaveHdr.dwBufferLength = count;
479 wmw->WaveHdr.dwBytesRecorded = 0;
481 wmw->WaveHdr.reserved = (DWORD)&wmw->WaveHdr;
482 TRACE(mciwave, "before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
483 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
484 dwRet = wodMessage(wmw->wWavID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
485 /* FIXME: should use callback mechanisms from audio driver */
487 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
490 wmw->dwPosition += count;
491 TRACE(mciwave, "after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
492 dwRet = wodMessage(wmw->wWavID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
495 if (wmw->WaveHdr.lpData != NULL) {
496 GlobalUnlock16(hData);
498 wmw->WaveHdr.lpData = NULL;
501 wodMessage(wmw->wWavID, WODM_STOP, 0, 0L, 0L);
502 wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
504 wmw->dwStatus = MCI_MODE_STOP;
505 if (lpParms && (dwFlags & MCI_NOTIFY)) {
506 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
507 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
508 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
513 /**************************************************************************
514 * WAVE_mciRecord [internal]
516 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
523 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
525 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
527 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
528 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
531 WARN(mciwave, "cannot record on output device\n");
532 return MCIERR_NONAPPLICABLE_FUNCTION;
535 if (wmw->hFile == 0) {
536 WARN(mciwave, "can't find file='%s' !\n",
537 wmw->openParms.lpstrElementName);
538 return MCIERR_FILE_NOT_FOUND;
540 start = 1; end = 99999;
541 if (dwFlags & MCI_FROM) {
542 start = lpParms->dwFrom;
543 TRACE(mciwave, "MCI_FROM=%d \n", start);
545 if (dwFlags & MCI_TO) {
547 TRACE(mciwave, "MCI_TO=%d \n", end);
550 lpWaveHdr = &wmw->WaveHdr;
551 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
552 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
553 lpWaveHdr->dwBufferLength = bufsize;
554 lpWaveHdr->dwUser = 0L;
555 lpWaveHdr->dwFlags = 0L;
556 lpWaveHdr->dwLoops = 0L;
557 dwRet = widMessage(wmw->wWavID,WIDM_PREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
558 TRACE(mciwave, "after WIDM_PREPARE \n");
560 lpWaveHdr->dwBytesRecorded = 0;
561 dwRet = widMessage(wmw->wWavID, WIDM_START, 0, 0L, 0L);
562 TRACE(mciwave, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
563 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
564 if (lpWaveHdr->dwBytesRecorded == 0) break;
566 TRACE(mciwave, "before WIDM_UNPREPARE \n");
567 dwRet = widMessage(wmw->wWavID,WIDM_UNPREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
568 TRACE(mciwave, "after WIDM_UNPREPARE \n");
569 if (lpWaveHdr->lpData != NULL) {
570 GlobalUnlock16(hData);
572 lpWaveHdr->lpData = NULL;
574 if (dwFlags & MCI_NOTIFY) {
575 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
576 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
577 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
582 /**************************************************************************
583 * WAVE_mciPause [internal]
585 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
588 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
590 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
592 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
593 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
595 if (wmw->dwStatus == MCI_MODE_PLAY) {
596 wmw->dwStatus = MCI_MODE_PAUSE;
599 if (wmw->fInput) dwRet = widMessage(wmw->wWavID, WIDM_PAUSE, 0, dwFlags, (DWORD)lpParms);
600 else dwRet = wodMessage(wmw->wWavID, WODM_PAUSE, 0, dwFlags, (DWORD)lpParms);
602 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
605 /**************************************************************************
606 * WAVE_mciResume [internal]
608 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
610 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
613 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
615 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
616 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
618 if (wmw->dwStatus == MCI_MODE_PAUSE) {
619 wmw->dwStatus = MCI_MODE_PLAY;
623 if (wmw->fInput) dwRet = widMessage(wmw->wWavID, WIDM_PLAY, 0, dwFlags, (DWORD)lpParms);
624 else dwRet = wodMessage(wmw->wWavID, WODM_PLAY, 0, dwFlags, (DWORD)lpParms);
625 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
632 /**************************************************************************
633 * WAVE_mciSeek [internal]
635 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
638 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
640 TRACE(mciwave, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
642 if (lpParms == NULL) {
643 ret = MCIERR_NULL_PARAMETER_BLOCK;
644 } else if (wmw == NULL) {
645 ret = MCIERR_INVALID_DEVICE_ID;
647 WAVE_mciStop(wDevID, MCI_WAIT, 0);
649 if (dwFlags & MCI_SEEK_TO_START) {
651 } else if (dwFlags & MCI_SEEK_TO_END) {
652 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
653 } else if (dwFlags & MCI_TO) {
654 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
656 WARN(mciwave, "dwFlag doesn't tell where to seek to...\n");
657 return MCIERR_MISSING_PARAMETER;
660 TRACE(mciwave, "Seeking to position=%lu bytes\n", wmw->dwPosition);
662 if (dwFlags & MCI_NOTIFY) {
663 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
664 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
665 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
671 /**************************************************************************
672 * WAVE_mciSet [internal]
674 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
676 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
678 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
680 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
681 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
683 if (dwFlags & MCI_SET_TIME_FORMAT) {
684 switch (lpParms->dwTimeFormat) {
685 case MCI_FORMAT_MILLISECONDS:
686 TRACE(mciwave, "MCI_FORMAT_MILLISECONDS !\n");
687 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
689 case MCI_FORMAT_BYTES:
690 TRACE(mciwave, "MCI_FORMAT_BYTES !\n");
691 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
693 case MCI_FORMAT_SAMPLES:
694 TRACE(mciwave, "MCI_FORMAT_SAMPLES !\n");
695 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
698 WARN(mciwave, "Bad time format %lu!\n", lpParms->dwTimeFormat);
699 return MCIERR_BAD_TIME_FORMAT;
702 if (dwFlags & MCI_SET_VIDEO) {
703 TRACE(mciwave, "No support for video !\n");
704 return MCIERR_UNSUPPORTED_FUNCTION;
706 if (dwFlags & MCI_SET_DOOR_OPEN) {
707 TRACE(mciwave, "No support for door open !\n");
708 return MCIERR_UNSUPPORTED_FUNCTION;
710 if (dwFlags & MCI_SET_DOOR_CLOSED) {
711 TRACE(mciwave, "No support for door close !\n");
712 return MCIERR_UNSUPPORTED_FUNCTION;
714 if (dwFlags & MCI_SET_AUDIO) {
715 if (dwFlags & MCI_SET_ON) {
716 TRACE(mciwave, "MCI_SET_ON audio !\n");
717 } else if (dwFlags & MCI_SET_OFF) {
718 TRACE(mciwave, "MCI_SET_OFF audio !\n");
720 WARN(mciwave, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
721 return MCIERR_BAD_INTEGER;
724 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
725 TRACE(mciwave, "MCI_SET_AUDIO_ALL !\n");
726 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
727 TRACE(mciwave, "MCI_SET_AUDIO_LEFT !\n");
728 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
729 TRACE(mciwave, "MCI_SET_AUDIO_RIGHT !\n");
731 if (dwFlags & MCI_WAVE_INPUT)
732 TRACE(mciwave, "MCI_WAVE_INPUT !\n");
733 if (dwFlags & MCI_WAVE_OUTPUT)
734 TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
735 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
736 TRACE(mciwave, "MCI_WAVE_SET_ANYINPUT !\n");
737 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
738 TRACE(mciwave, "MCI_WAVE_SET_ANYOUTPUT !\n");
739 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
740 TRACE(mciwave, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
741 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
742 TRACE(mciwave, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
743 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
744 TRACE(mciwave, "MCI_WAVE_SET_BLOCKALIGN !\n");
745 if (dwFlags & MCI_WAVE_SET_CHANNELS)
746 TRACE(mciwave, "MCI_WAVE_SET_CHANNELS !\n");
747 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
748 TRACE(mciwave, "MCI_WAVE_SET_FORMATTAG !\n");
749 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
750 TRACE(mciwave, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
754 /**************************************************************************
755 * WAVE_mciStatus [internal]
757 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
759 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
761 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
762 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
763 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
765 if (dwFlags & MCI_STATUS_ITEM) {
766 switch(lpParms->dwItem) {
767 case MCI_STATUS_CURRENT_TRACK:
768 lpParms->dwReturn = 1;
769 TRACE(mciwave, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
771 case MCI_STATUS_LENGTH:
772 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
773 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
774 TRACE(mciwave, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
776 case MCI_STATUS_MODE:
777 lpParms->dwReturn = wmw->dwStatus;
778 TRACE(mciwave, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
780 case MCI_STATUS_MEDIA_PRESENT:
781 TRACE(mciwave, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
782 lpParms->dwReturn = TRUE;
784 case MCI_STATUS_NUMBER_OF_TRACKS:
785 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
786 lpParms->dwReturn = 1;
787 TRACE(mciwave, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
789 case MCI_STATUS_POSITION:
790 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
791 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
792 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
793 TRACE(mciwave, "MCI_STATUS_POSITION %s => %lu\n",
794 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
796 case MCI_STATUS_READY:
797 lpParms->dwReturn = (wmw->dwStatus != MCI_MODE_NOT_READY);
798 TRACE(mciwave, "MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
800 case MCI_STATUS_TIME_FORMAT:
801 lpParms->dwReturn = wmw->dwMciTimeFormat;
802 TRACE(mciwave, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
805 TRACE(mciwave, "MCI_WAVE_INPUT !\n");
806 lpParms->dwReturn = 0;
808 case MCI_WAVE_OUTPUT:
809 TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
810 lpParms->dwReturn = 0;
812 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
813 lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
814 TRACE(mciwave, "MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
816 case MCI_WAVE_STATUS_BITSPERSAMPLE:
817 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
818 TRACE(mciwave, "MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
820 case MCI_WAVE_STATUS_BLOCKALIGN:
821 lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
822 TRACE(mciwave, "MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
824 case MCI_WAVE_STATUS_CHANNELS:
825 lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
826 TRACE(mciwave, "MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
828 case MCI_WAVE_STATUS_FORMATTAG:
829 lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
830 TRACE(mciwave, "MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
832 case MCI_WAVE_STATUS_LEVEL:
833 TRACE(mciwave, "MCI_WAVE_STATUS_LEVEL !\n");
834 lpParms->dwReturn = 0xAAAA5555;
836 case MCI_WAVE_STATUS_SAMPLESPERSEC:
837 lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
838 TRACE(mciwave, "MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
841 WARN(mciwave, "unknown command %08lX !\n", lpParms->dwItem);
842 return MCIERR_UNRECOGNIZED_COMMAND;
845 if (dwFlags & MCI_NOTIFY) {
846 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
847 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
848 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
853 /**************************************************************************
854 * WAVE_mciGetDevCaps [internal]
856 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
857 LPMCI_GETDEVCAPS_PARMS lpParms)
859 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
861 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
863 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
864 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
866 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
867 switch(lpParms->dwItem) {
868 case MCI_GETDEVCAPS_DEVICE_TYPE:
869 lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
871 case MCI_GETDEVCAPS_HAS_AUDIO:
872 lpParms->dwReturn = TRUE;
874 case MCI_GETDEVCAPS_HAS_VIDEO:
875 lpParms->dwReturn = FALSE;
877 case MCI_GETDEVCAPS_USES_FILES:
878 lpParms->dwReturn = TRUE;
880 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
881 lpParms->dwReturn = TRUE;
883 case MCI_GETDEVCAPS_CAN_RECORD:
884 lpParms->dwReturn = TRUE;
886 case MCI_GETDEVCAPS_CAN_EJECT:
887 lpParms->dwReturn = FALSE;
889 case MCI_GETDEVCAPS_CAN_PLAY:
890 lpParms->dwReturn = TRUE;
892 case MCI_GETDEVCAPS_CAN_SAVE:
893 lpParms->dwReturn = TRUE;
895 case MCI_WAVE_GETDEVCAPS_INPUTS:
896 lpParms->dwReturn = 1;
898 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
899 lpParms->dwReturn = 1;
902 TRACE(mciwave, "Unknown capability (%08lx) !\n", lpParms->dwItem);
903 return MCIERR_UNRECOGNIZED_COMMAND;
909 /**************************************************************************
910 * WAVE_mciInfo [internal]
912 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
916 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
918 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
920 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
921 ret = MCIERR_NULL_PARAMETER_BLOCK;
922 } else if (wmw == NULL) {
923 ret = MCIERR_INVALID_DEVICE_ID;
925 TRACE(mciwave, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
928 case MCI_INFO_PRODUCT:
929 str = "Wine's audio player";
932 str = wmw->openParms.lpstrElementName;
935 str = "Wine Wave In";
937 case MCI_WAVE_OUTPUT:
938 str = "Wine Wave Out";
941 WARN(mciwave, "Don't know this info command (%lu)\n", dwFlags);
942 ret = MCIERR_UNRECOGNIZED_COMMAND;
946 if (strlen(str) + 1 > lpParms->dwRetSize) {
947 ret = MCIERR_PARAM_OVERFLOW;
949 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
952 lpParms->lpstrReturn[0] = 0;
958 /**************************************************************************
959 * MCIWAVE_DriverProc [sample driver]
961 LONG MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
962 DWORD dwParam1, DWORD dwParam2)
964 TRACE(mciwave, "(%08lX, %04X, %08lX, %08lX, %08lX)\n",
965 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
968 case DRV_LOAD: return 1;
969 case DRV_FREE: return 1;
970 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
971 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
972 case DRV_ENABLE: return 1;
973 case DRV_DISABLE: return 1;
974 case DRV_QUERYCONFIGURE: return 1;
975 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
976 case DRV_INSTALL: return DRVCNF_RESTART;
977 case DRV_REMOVE: return DRVCNF_RESTART;
978 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
979 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
980 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
981 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
982 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
983 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
984 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
985 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
986 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
987 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
988 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
989 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMS16) dwParam2);
990 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1007 WARN(mciwave, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
1011 FIXME(mciwave, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
1014 FIXME(mciwave, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
1015 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1017 return MCIERR_UNRECOGNIZED_COMMAND;