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)
21 #include <sys/ioctl.h>
22 #include "wine/winuser16.h"
24 #include "multimedia.h"
29 #define MAX_MCIWAVEDRV (1)
32 int nUseCount
; /* Incremented for each shared open */
33 BOOL16 fShareable
; /* TRUE if first open was shareable */
34 WORD wNotifyDeviceID
;/* MCI device ID with a pending notification */
35 HANDLE16 hCallback
; /* Callback handle for pending notification */
36 HMMIO hFile
; /* mmio file handle open as Element */
37 MCI_WAVE_OPEN_PARMSA openParms
;
38 WAVEOPENDESC waveDesc
;
39 PCMWAVEFORMAT WaveFormat
;
41 BOOL16 fInput
; /* FALSE = Output, TRUE = Input */
42 WORD dwStatus
; /* one from MCI_MODE_xxxx */
43 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
44 DWORD dwFileOffset
; /* Offset of chunk in mmio file */
45 DWORD dwLength
; /* number of bytes in chunk for playing */
46 DWORD dwPosition
; /* position in bytes in chunk for playing */
49 static WINE_MCIWAVE MCIWaveDev
[MAX_MCIWAVEDRV
];
51 /*======================================================================*
52 * MCI WAVE implemantation *
53 *======================================================================*/
55 /**************************************************************************
56 * WAVE_mciGetOpenDev [internal]
58 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT16 wDevID
)
60 if (wDevID
>= MAX_MCIWAVEDRV
|| MCIWaveDev
[wDevID
].nUseCount
== 0) {
61 WARN(mciwave
, "Invalid wDevID=%u\n", wDevID
);
64 return &MCIWaveDev
[wDevID
];
67 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
)
71 switch (wmw
->dwMciTimeFormat
) {
72 case MCI_FORMAT_MILLISECONDS
:
73 ret
= (val
* 1000) / wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
75 case MCI_FORMAT_BYTES
:
78 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
79 ret
= (val
* 8) / wmw
->WaveFormat
.wBitsPerSample
;
82 WARN(mciwave
, "Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
84 TRACE(mciwave
, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
88 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
92 switch (wmw
->dwMciTimeFormat
) {
93 case MCI_FORMAT_MILLISECONDS
:
94 ret
= (val
* wmw
->WaveFormat
.wf
.nAvgBytesPerSec
) / 1000;
96 case MCI_FORMAT_BYTES
:
99 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
100 ret
= (val
* wmw
->WaveFormat
.wBitsPerSample
) / 8;
103 WARN(mciwave
, "Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
105 TRACE(mciwave
, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
109 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, MMCKINFO
* pckMainRIFF
)
113 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
114 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
115 return MCIERR_INVALID_FILE
;
116 TRACE(mciwave
, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
117 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
118 if (mmioRead(wmw
->hFile
, (HPSTR
)&wmw
->WaveFormat
,
119 (long)sizeof(PCMWAVEFORMAT
)) != (long)sizeof(PCMWAVEFORMAT
))
120 return MCIERR_INVALID_FILE
;
122 TRACE(mciwave
, "wFormatTag=%04X !\n", wmw
->WaveFormat
.wf
.wFormatTag
);
123 TRACE(mciwave
, "nChannels=%d \n", wmw
->WaveFormat
.wf
.nChannels
);
124 TRACE(mciwave
, "nSamplesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nSamplesPerSec
);
125 TRACE(mciwave
, "nAvgBytesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nAvgBytesPerSec
);
126 TRACE(mciwave
, "nBlockAlign=%d \n", wmw
->WaveFormat
.wf
.nBlockAlign
);
127 TRACE(mciwave
, "wBitsPerSample=%u !\n", wmw
->WaveFormat
.wBitsPerSample
);
128 mmckInfo
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
129 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
130 return MCIERR_INVALID_FILE
;
131 TRACE(mciwave
,"Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
132 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
133 TRACE(mciwave
, "nChannels=%d nSamplesPerSec=%ld\n",
134 wmw
->WaveFormat
.wf
.nChannels
, wmw
->WaveFormat
.wf
.nSamplesPerSec
);
135 wmw
->dwLength
= mmckInfo
.cksize
;
136 wmw
->dwFileOffset
= mmioSeek(wmw
->hFile
, 0, SEEK_CUR
); /* >= 0 */
140 /**************************************************************************
141 * WAVE_mciOpen [internal]
143 static DWORD
WAVE_mciOpen(UINT16 wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSA lpOpenParms
)
149 TRACE(mciwave
,"(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
150 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
152 if (wDevID
>= MAX_MCIWAVEDRV
) {
153 WARN(mciwave
, "Invalid wDevID=%u\n", wDevID
);
154 return MCIERR_INVALID_DEVICE_ID
;
156 if (dwFlags
& MCI_OPEN_SHAREABLE
)
157 return MCIERR_HARDWARE
;
159 wmw
= &MCIWaveDev
[wDevID
];
161 if (wmw
->nUseCount
> 0) {
162 /* The driver is already opened on this channel
163 * Wave driver cannot be shared
165 return MCIERR_DEVICE_OPEN
;
169 dwDeviceID
= lpOpenParms
->wDeviceID
;
173 TRACE(mciwave
, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID
, dwDeviceID
);
175 if (dwFlags
& MCI_OPEN_ELEMENT
) {
176 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
177 /* could it be that (DWORD)lpOpenParms->lpstrElementName
178 * contains the hFile value ?
180 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
182 LPCSTR lpstrElementName
= lpOpenParms
->lpstrElementName
;
184 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
185 TRACE(mciwave
,"MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName
);
186 if (lpstrElementName
&& (strlen(lpstrElementName
) > 0)) {
187 wmw
->hFile
= mmioOpenA((LPSTR
)lpstrElementName
, NULL
,
188 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_EXCLUSIVE
);
189 if (wmw
->hFile
== 0) {
190 WARN(mciwave
, "can't find file='%s' !\n", lpstrElementName
);
191 dwRet
= MCIERR_FILE_NOT_FOUND
;
198 TRACE(mciwave
,"hFile=%u\n", wmw
->hFile
);
200 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
201 wmw
->wNotifyDeviceID
= dwDeviceID
;
202 wmw
->dwStatus
= MCI_MODE_NOT_READY
; /* while loading file contents */
204 wmw
->waveDesc
.hWave
= 0;
206 if (dwRet
== 0 && wmw
->hFile
!= 0) {
209 if (mmioDescend(wmw
->hFile
, &ckMainRIFF
, NULL
, 0) != 0) {
210 dwRet
= MCIERR_INVALID_FILE
;
212 TRACE(mciwave
, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
213 (LPSTR
)&ckMainRIFF
.ckid
, (LPSTR
)&ckMainRIFF
.fccType
, ckMainRIFF
.cksize
);
214 if ((ckMainRIFF
.ckid
!= FOURCC_RIFF
) ||
215 (ckMainRIFF
.fccType
!= mmioFOURCC('W', 'A', 'V', 'E'))) {
216 dwRet
= MCIERR_INVALID_FILE
;
218 dwRet
= WAVE_mciReadFmt(wmw
, &ckMainRIFF
);
225 wmw
->WaveFormat
.wf
.nAvgBytesPerSec
=
226 wmw
->WaveFormat
.wf
.nSamplesPerSec
* wmw
->WaveFormat
.wf
.nBlockAlign
;
227 wmw
->waveDesc
.lpFormat
= (LPWAVEFORMAT
)&wmw
->WaveFormat
;
230 /* By default the device will be opened for output, the MCI_CUE function is there to
231 * change from output to input and back
233 dwRet
= wodMessage(wDevID
, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
234 wmw
->dwStatus
= MCI_MODE_STOP
;
238 mmioClose(wmw
->hFile
, 0);
244 /**************************************************************************
245 * WAVE_mciCue [internal]
247 static DWORD
WAVE_mciCue(UINT16 wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
252 This routine is far from complete. At the moment only a check is done on the
253 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
256 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
261 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
263 TRACE(mciwave
,"(%u, %08lX, %p);\n", wDevID
, dwParam
, lpParms
);
265 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
267 /* always close elements ? */
268 if (wmw
->hFile
!= 0) {
269 mmioClose(wmw
->hFile
, 0);
273 dwRet
= MMSYSERR_NOERROR
; /* assume success */
275 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
276 dwRet
= wodMessage(wDevID
, WODM_CLOSE
, 0, 0L, 0L);
277 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
278 dwRet
= widMessage(wDevID
, WIDM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
280 } else if (wmw
->fInput
) {
281 dwRet
= widMessage(wDevID
, WIDM_CLOSE
, 0, 0L, 0L);
282 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
283 dwRet
= wodMessage(wDevID
, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
286 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
289 /**************************************************************************
290 * WAVE_mciStop [internal]
292 static DWORD
WAVE_mciStop(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
295 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
297 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
299 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
300 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
302 wmw
->dwStatus
= MCI_MODE_STOP
;
304 TRACE(mciwave
, "wmw->dwStatus=%d\n", wmw
->dwStatus
);
307 dwRet
= widMessage(wDevID
, WIDM_STOP
, 0, dwFlags
, (DWORD
)lpParms
);
309 dwRet
= wodMessage(wDevID
, WODM_STOP
, 0, dwFlags
, (DWORD
)lpParms
);
311 if (dwFlags
& MCI_NOTIFY
) {
312 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
313 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
314 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
317 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
320 /**************************************************************************
321 * WAVE_mciClose [internal]
323 static DWORD
WAVE_mciClose(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
326 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
328 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
330 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
331 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
333 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
334 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
337 wmw
->dwStatus
= MCI_MODE_STOP
;
340 if (wmw
->nUseCount
== 0) {
342 if (wmw
->hFile
!= 0) {
343 mmioClose(wmw
->hFile
, 0);
346 mmRet
= (wmw
->fInput
) ? widMessage(wDevID
, WIDM_CLOSE
, 0, 0L, 0L) :
347 wodMessage(wDevID
, WODM_CLOSE
, 0, 0L, 0L);
349 if (mmRet
!= MMSYSERR_NOERROR
) dwRet
= MCIERR_INTERNAL
;
352 if (dwFlags
& MCI_NOTIFY
) {
353 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
354 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
355 wmw
->wNotifyDeviceID
,
356 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
361 /**************************************************************************
362 * WAVE_mciPlay [internal]
364 static DWORD
WAVE_mciPlay(UINT16 wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
370 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
372 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
374 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
375 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
378 WARN(mciwave
, "cannot play on input device\n");
379 return MCIERR_NONAPPLICABLE_FUNCTION
;
382 if (wmw
->hFile
== 0) {
383 WARN(mciwave
, "Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
384 return MCIERR_FILE_NOT_FOUND
;
387 if (!(dwFlags
& MCI_WAIT
)) {
388 return MCI_SendCommandAsync(wmw
->wNotifyDeviceID
, MCI_PLAY
, dwFlags
,
389 (DWORD
)lpParms
, sizeof(MCI_PLAY_PARMS
));
393 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
394 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
396 if (lpParms
&& (dwFlags
& MCI_TO
)) {
397 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
400 TRACE(mciwave
, "Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
402 /* go back to begining of chunk */
403 mmioSeek(wmw
->hFile
, wmw
->dwFileOffset
, SEEK_SET
); /* >= 0 */
405 /* at 22050 bytes per sec => 30 ms by block */
407 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
408 wmw
->WaveHdr
.lpData
= (LPSTR
)GlobalLock16(hData
);
410 wmw
->dwStatus
= MCI_MODE_PLAY
;
412 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
413 while (wmw
->dwStatus
!= MCI_MODE_STOP
) {
414 wmw
->WaveHdr
.dwUser
= 0L;
415 wmw
->WaveHdr
.dwFlags
= 0L;
416 wmw
->WaveHdr
.dwLoops
= 0L;
417 dwRet
= wodMessage(wDevID
, WODM_PREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
418 count
= mmioRead(wmw
->hFile
, wmw
->WaveHdr
.lpData
, bufsize
);
419 TRACE(mciwave
,"mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
420 if (count
< 1) break;
421 wmw
->WaveHdr
.dwBufferLength
= count
;
422 wmw
->WaveHdr
.dwBytesRecorded
= 0;
423 TRACE(mciwave
,"before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
424 &wmw
->WaveHdr
, wmw
->WaveHdr
.dwBufferLength
, wmw
->WaveHdr
.dwBytesRecorded
);
425 dwRet
= wodMessage(wDevID
, WODM_WRITE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
426 /* FIXME: should use callback mechanisms from audio driver */
428 while (!(wmw
->WaveHdr
.dwFlags
& WHDR_DONE
))
431 wmw
->dwPosition
+= count
;
432 TRACE(mciwave
,"after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
433 dwRet
= wodMessage(wDevID
, WODM_UNPREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
436 if (wmw
->WaveHdr
.lpData
!= NULL
) {
437 GlobalUnlock16(hData
);
439 wmw
->WaveHdr
.lpData
= NULL
;
441 wmw
->dwStatus
= MCI_MODE_STOP
;
442 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
443 TRACE(mciwave
,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
444 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
445 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
450 /**************************************************************************
451 * WAVE_mciRecord [internal]
453 static DWORD
WAVE_mciRecord(UINT16 wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
460 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
462 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
464 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
465 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
468 WARN(mciwave
, "cannot record on output device\n");
469 return MCIERR_NONAPPLICABLE_FUNCTION
;
472 if (wmw
->hFile
== 0) {
473 WARN(mciwave
, "can't find file='%s' !\n",
474 wmw
->openParms
.lpstrElementName
);
475 return MCIERR_FILE_NOT_FOUND
;
477 start
= 1; end
= 99999;
478 if (dwFlags
& MCI_FROM
) {
479 start
= lpParms
->dwFrom
;
480 TRACE(mciwave
, "MCI_FROM=%d \n", start
);
482 if (dwFlags
& MCI_TO
) {
484 TRACE(mciwave
,"MCI_TO=%d \n", end
);
487 lpWaveHdr
= &wmw
->WaveHdr
;
488 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
489 lpWaveHdr
->lpData
= (LPSTR
)GlobalLock16(hData
);
490 lpWaveHdr
->dwBufferLength
= bufsize
;
491 lpWaveHdr
->dwUser
= 0L;
492 lpWaveHdr
->dwFlags
= 0L;
493 lpWaveHdr
->dwLoops
= 0L;
494 dwRet
=widMessage(wDevID
,WIDM_PREPARE
,0,(DWORD
)lpWaveHdr
,sizeof(WAVEHDR
));
495 TRACE(mciwave
,"after WIDM_PREPARE \n");
497 lpWaveHdr
->dwBytesRecorded
= 0;
498 dwRet
= widMessage(wDevID
, WIDM_START
, 0, 0L, 0L);
499 TRACE(mciwave
, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
500 lpWaveHdr
, lpWaveHdr
->dwBytesRecorded
);
501 if (lpWaveHdr
->dwBytesRecorded
== 0) break;
503 TRACE(mciwave
,"before WIDM_UNPREPARE \n");
504 dwRet
= widMessage(wDevID
,WIDM_UNPREPARE
,0,(DWORD
)lpWaveHdr
,sizeof(WAVEHDR
));
505 TRACE(mciwave
,"after WIDM_UNPREPARE \n");
506 if (lpWaveHdr
->lpData
!= NULL
) {
507 GlobalUnlock16(hData
);
509 lpWaveHdr
->lpData
= NULL
;
511 if (dwFlags
& MCI_NOTIFY
) {
512 TRACE(mciwave
,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
513 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
514 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
519 /**************************************************************************
520 * WAVE_mciPause [internal]
522 static DWORD
WAVE_mciPause(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
525 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
527 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
529 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
530 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
532 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
533 wmw
->dwStatus
= MCI_MODE_PAUSE
;
536 if (wmw
->fInput
) dwRet
= widMessage(wDevID
, WIDM_PAUSE
, 0, dwFlags
, (DWORD
)lpParms
);
537 else dwRet
= wodMessage(wDevID
, WODM_PAUSE
, 0, dwFlags
, (DWORD
)lpParms
);
539 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
542 /**************************************************************************
543 * WAVE_mciResume [internal]
545 static DWORD
WAVE_mciResume(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
547 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
550 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
552 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
553 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
555 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
556 wmw
->dwStatus
= MCI_MODE_PLAY
;
560 if (wmw
->fInput
) dwRet
= widMessage(wDevID
, WIDM_PLAY
, 0, dwFlags
, (DWORD
)lpParms
);
561 else dwRet
= wodMessage(wDevID
, WODM_PLAY
, 0, dwFlags
, (DWORD
)lpParms
);
562 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
569 /**************************************************************************
570 * WAVE_mciSeek [internal]
572 static DWORD
WAVE_mciSeek(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
575 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
577 TRACE(mciwave
, "(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
579 if (lpParms
== NULL
) {
580 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
581 } else if (wmw
== NULL
) {
582 ret
= MCIERR_INVALID_DEVICE_ID
;
584 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
586 if (dwFlags
& MCI_SEEK_TO_START
) {
588 } else if (dwFlags
& MCI_SEEK_TO_END
) {
589 wmw
->dwPosition
= 0xFFFFFFFF; /* fixme */
590 } else if (dwFlags
& MCI_TO
) {
591 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
593 WARN(mciwave
, "dwFlag doesn't tell where to seek to...\n");
594 return MCIERR_MISSING_PARAMETER
;
597 TRACE(mciwave
, "Seeking to position=%lu bytes\n", wmw
->dwPosition
);
599 if (dwFlags
& MCI_NOTIFY
) {
600 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
601 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
602 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
608 /**************************************************************************
609 * WAVE_mciSet [internal]
611 static DWORD
WAVE_mciSet(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
613 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
615 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
617 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
618 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
620 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
621 switch (lpParms
->dwTimeFormat
) {
622 case MCI_FORMAT_MILLISECONDS
:
623 TRACE(mciwave
, "MCI_FORMAT_MILLISECONDS !\n");
624 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
626 case MCI_FORMAT_BYTES
:
627 TRACE(mciwave
, "MCI_FORMAT_BYTES !\n");
628 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
630 case MCI_FORMAT_SAMPLES
:
631 TRACE(mciwave
, "MCI_FORMAT_SAMPLES !\n");
632 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
635 WARN(mciwave
,"Bad time format %lu!\n", lpParms
->dwTimeFormat
);
636 return MCIERR_BAD_TIME_FORMAT
;
639 if (dwFlags
& MCI_SET_VIDEO
) {
640 TRACE(mciwave
, "No support for video !\n");
641 return MCIERR_UNSUPPORTED_FUNCTION
;
643 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
644 TRACE(mciwave
, "No support for door open !\n");
645 return MCIERR_UNSUPPORTED_FUNCTION
;
647 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
648 TRACE(mciwave
, "No support for door close !\n");
649 return MCIERR_UNSUPPORTED_FUNCTION
;
651 if (dwFlags
& MCI_SET_AUDIO
) {
652 if (dwFlags
& MCI_SET_ON
) {
653 TRACE(mciwave
, "MCI_SET_ON audio !\n");
654 } else if (dwFlags
& MCI_SET_OFF
) {
655 TRACE(mciwave
, "MCI_SET_OFF audio !\n");
657 WARN(mciwave
, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
658 return MCIERR_BAD_INTEGER
;
661 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
662 TRACE(mciwave
, "MCI_SET_AUDIO_ALL !\n");
663 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
664 TRACE(mciwave
, "MCI_SET_AUDIO_LEFT !\n");
665 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
666 TRACE(mciwave
, "MCI_SET_AUDIO_RIGHT !\n");
668 if (dwFlags
& MCI_WAVE_INPUT
)
669 TRACE(mciwave
, "MCI_WAVE_INPUT !\n");
670 if (dwFlags
& MCI_WAVE_OUTPUT
)
671 TRACE(mciwave
, "MCI_WAVE_OUTPUT !\n");
672 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
673 TRACE(mciwave
, "MCI_WAVE_SET_ANYINPUT !\n");
674 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
675 TRACE(mciwave
, "MCI_WAVE_SET_ANYOUTPUT !\n");
676 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
)
677 TRACE(mciwave
, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
678 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
)
679 TRACE(mciwave
, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
680 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
)
681 TRACE(mciwave
, "MCI_WAVE_SET_BLOCKALIGN !\n");
682 if (dwFlags
& MCI_WAVE_SET_CHANNELS
)
683 TRACE(mciwave
, "MCI_WAVE_SET_CHANNELS !\n");
684 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
)
685 TRACE(mciwave
, "MCI_WAVE_SET_FORMATTAG !\n");
686 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
)
687 TRACE(mciwave
, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
691 /**************************************************************************
692 * WAVE_mciStatus [internal]
694 static DWORD
WAVE_mciStatus(UINT16 wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
696 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
698 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
699 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
700 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
702 if (dwFlags
& MCI_STATUS_ITEM
) {
703 switch(lpParms
->dwItem
) {
704 case MCI_STATUS_CURRENT_TRACK
:
705 lpParms
->dwReturn
= 1;
706 TRACE(mciwave
, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
708 case MCI_STATUS_LENGTH
:
709 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
710 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->dwLength
);
711 TRACE(mciwave
, "MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
713 case MCI_STATUS_MODE
:
714 lpParms
->dwReturn
= wmw
->dwStatus
;
715 TRACE(mciwave
, "MCI_STATUS_MODE => %lu\n", lpParms
->dwReturn
);
717 case MCI_STATUS_MEDIA_PRESENT
:
718 TRACE(mciwave
, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
719 lpParms
->dwReturn
= TRUE
;
721 case MCI_STATUS_NUMBER_OF_TRACKS
:
722 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
723 lpParms
->dwReturn
= 1;
724 TRACE(mciwave
, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
726 case MCI_STATUS_POSITION
:
727 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
728 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
729 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
);
730 TRACE(mciwave
, "MCI_STATUS_POSITION %s => %lu\n",
731 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
733 case MCI_STATUS_READY
:
734 lpParms
->dwReturn
= (wmw
->dwStatus
!= MCI_MODE_NOT_READY
);
735 TRACE(mciwave
,"MCI_STATUS_READY => %lu!\n", lpParms
->dwReturn
);
737 case MCI_STATUS_TIME_FORMAT
:
738 lpParms
->dwReturn
= wmw
->dwMciTimeFormat
;
739 TRACE(mciwave
, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
742 TRACE(mciwave
,"MCI_WAVE_INPUT !\n");
743 lpParms
->dwReturn
= 0;
745 case MCI_WAVE_OUTPUT
:
746 TRACE(mciwave
,"MCI_WAVE_OUTPUT !\n");
747 lpParms
->dwReturn
= 0;
749 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
750 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
751 TRACE(mciwave
,"MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
753 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
754 lpParms
->dwReturn
= wmw
->WaveFormat
.wBitsPerSample
;
755 TRACE(mciwave
,"MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
757 case MCI_WAVE_STATUS_BLOCKALIGN
:
758 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nBlockAlign
;
759 TRACE(mciwave
,"MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
761 case MCI_WAVE_STATUS_CHANNELS
:
762 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nChannels
;
763 TRACE(mciwave
,"MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
765 case MCI_WAVE_STATUS_FORMATTAG
:
766 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.wFormatTag
;
767 TRACE(mciwave
,"MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
769 case MCI_WAVE_STATUS_LEVEL
:
770 TRACE(mciwave
,"MCI_WAVE_STATUS_LEVEL !\n");
771 lpParms
->dwReturn
= 0xAAAA5555;
773 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
774 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nSamplesPerSec
;
775 TRACE(mciwave
,"MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
778 WARN(mciwave
,"unknown command %08lX !\n", lpParms
->dwItem
);
779 return MCIERR_UNRECOGNIZED_COMMAND
;
782 if (dwFlags
& MCI_NOTIFY
) {
783 TRACE(mciwave
,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
784 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
785 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
790 /**************************************************************************
791 * WAVE_mciGetDevCaps [internal]
793 static DWORD
WAVE_mciGetDevCaps(UINT16 wDevID
, DWORD dwFlags
,
794 LPMCI_GETDEVCAPS_PARMS lpParms
)
796 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
798 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
800 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
801 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
803 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
804 switch(lpParms
->dwItem
) {
805 case MCI_GETDEVCAPS_DEVICE_TYPE
:
806 lpParms
->dwReturn
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
808 case MCI_GETDEVCAPS_HAS_AUDIO
:
809 lpParms
->dwReturn
= TRUE
;
811 case MCI_GETDEVCAPS_HAS_VIDEO
:
812 lpParms
->dwReturn
= FALSE
;
814 case MCI_GETDEVCAPS_USES_FILES
:
815 lpParms
->dwReturn
= TRUE
;
817 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
818 lpParms
->dwReturn
= TRUE
;
820 case MCI_GETDEVCAPS_CAN_RECORD
:
821 lpParms
->dwReturn
= TRUE
;
823 case MCI_GETDEVCAPS_CAN_EJECT
:
824 lpParms
->dwReturn
= FALSE
;
826 case MCI_GETDEVCAPS_CAN_PLAY
:
827 lpParms
->dwReturn
= TRUE
;
829 case MCI_GETDEVCAPS_CAN_SAVE
:
830 lpParms
->dwReturn
= TRUE
;
832 case MCI_WAVE_GETDEVCAPS_INPUTS
:
833 lpParms
->dwReturn
= 1;
835 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
836 lpParms
->dwReturn
= 1;
839 TRACE(mciwave
, "Unknown capability (%08lx) !\n", lpParms
->dwItem
);
840 return MCIERR_UNRECOGNIZED_COMMAND
;
846 /**************************************************************************
847 * WAVE_mciInfo [internal]
849 static DWORD
WAVE_mciInfo(UINT16 wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMS16 lpParms
)
853 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
855 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
857 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
858 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
859 } else if (wmw
== NULL
) {
860 ret
= MCIERR_INVALID_DEVICE_ID
;
862 TRACE(mciwave
, "buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
865 case MCI_INFO_PRODUCT
:
866 str
= "Wine's audio player";
869 str
= wmw
->openParms
.lpstrElementName
;
872 str
= "Wine Wave In";
874 case MCI_WAVE_OUTPUT
:
875 str
= "Wine Wave Out";
878 WARN(mciwave
, "Don't know this info command (%lu)\n", dwFlags
);
879 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
883 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
884 ret
= MCIERR_PARAM_OVERFLOW
;
886 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
889 lpParms
->lpstrReturn
[0] = 0;
895 /**************************************************************************
896 * WAVE_DriverProc32 [sample driver]
898 LONG
MCIWAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
899 DWORD dwParam1
, DWORD dwParam2
)
901 TRACE(mciwave
,"(%08lX, %04X, %08lX, %08lX, %08lX)\n",
902 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
905 case DRV_LOAD
: return 1;
906 case DRV_FREE
: return 1;
907 case DRV_OPEN
: return 1;
908 case DRV_CLOSE
: return 1;
909 case DRV_ENABLE
: return 1;
910 case DRV_DISABLE
: return 1;
911 case DRV_QUERYCONFIGURE
: return 1;
912 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
913 case DRV_INSTALL
: return DRVCNF_RESTART
;
914 case DRV_REMOVE
: return DRVCNF_RESTART
;
915 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSA
)dwParam2
);
916 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
917 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
918 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
919 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
920 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
921 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
922 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
923 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
924 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
925 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
926 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMS16
) dwParam2
);
927 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
944 WARN(mciwave
, "Unsupported command=%s\n", MCI_CommandToString(wMsg
));
948 FIXME(mciwave
, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
951 FIXME(mciwave
, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg
));
952 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
954 return MCIERR_UNRECOGNIZED_COMMAND
;