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>
29 #include "multimedia.h"
31 #define MAX_MCIWAVEDRV (1)
34 int nUseCount
; /* Incremented for each shared open */
35 BOOL16 fShareable
; /* TRUE if first open was shareable */
36 WORD wNotifyDeviceID
;/* MCI device ID with a pending notification */
37 HANDLE16 hCallback
; /* Callback handle for pending notification */
38 HMMIO32 hFile
; /* mmio file handle open as Element */
39 MCI_WAVE_OPEN_PARMS32A openParms
;
40 WAVEOPENDESC waveDesc
;
41 PCMWAVEFORMAT WaveFormat
;
43 BOOL16 fInput
; /* FALSE = Output, TRUE = Input */
44 WORD dwStatus
; /* one from MCI_MODE_xxxx */
45 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
46 DWORD dwFileOffset
; /* Offset of chunk in mmio file */
47 DWORD dwLength
; /* number of bytes in chunk for playing */
48 DWORD dwPosition
; /* position in bytes in chunk for playing */
51 static WINE_MCIWAVE MCIWaveDev
[MAX_MCIWAVEDRV
];
53 /*======================================================================*
54 * MCI WAVE implemantation *
55 *======================================================================*/
57 /**************************************************************************
58 * WAVE_mciGetOpenDev [internal]
60 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT16 wDevID
)
62 if (wDevID
>= MAX_MCIWAVEDRV
|| MCIWaveDev
[wDevID
].nUseCount
== 0) {
63 WARN(mciwave
, "Invalid wDevID=%u\n", wDevID
);
66 return &MCIWaveDev
[wDevID
];
69 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
)
73 switch (wmw
->dwMciTimeFormat
) {
74 case MCI_FORMAT_MILLISECONDS
:
75 ret
= (val
* 1000) / wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
77 case MCI_FORMAT_BYTES
:
80 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
81 ret
= (val
* 8) / wmw
->WaveFormat
.wBitsPerSample
;
84 WARN(mciwave
, "Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
86 TRACE(mciwave
, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
90 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
94 switch (wmw
->dwMciTimeFormat
) {
95 case MCI_FORMAT_MILLISECONDS
:
96 ret
= (val
* wmw
->WaveFormat
.wf
.nAvgBytesPerSec
) / 1000;
98 case MCI_FORMAT_BYTES
:
101 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
102 ret
= (val
* wmw
->WaveFormat
.wBitsPerSample
) / 8;
105 WARN(mciwave
, "Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
107 TRACE(mciwave
, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
111 /**************************************************************************
112 * WAVE_mciOpen [internal]
114 static DWORD
WAVE_mciOpen(UINT16 wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMS32A lpOpenParms
)
120 TRACE(mciwave
,"(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
121 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
123 if (wDevID
>= MAX_MCIWAVEDRV
) {
124 WARN(mciwave
, "Invalid wDevID=%u\n", wDevID
);
125 return MCIERR_INVALID_DEVICE_ID
;
128 wmw
= &MCIWaveDev
[wDevID
];
130 if (wmw
->nUseCount
> 0) {
131 /* The driver already open on this channel */
132 /* If the driver was opened shareable before and this open specifies */
133 /* shareable then increment the use count */
134 if (wmw
->fShareable
&& (dwFlags
& MCI_OPEN_SHAREABLE
))
137 return MCIERR_MUST_USE_SHAREABLE
;
140 wmw
->fShareable
= dwFlags
& MCI_OPEN_SHAREABLE
;
143 dwDeviceID
= lpOpenParms
->wDeviceID
;
147 TRACE(mciwave
, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID
, dwDeviceID
);
149 if (dwFlags
& MCI_OPEN_ELEMENT
) {
150 LPCSTR lpstrElementName
;
152 lpstrElementName
= lpOpenParms
->lpstrElementName
;
154 TRACE(mciwave
,"MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName
);
155 if (lpstrElementName
&& (strlen(lpstrElementName
) > 0)) {
156 wmw
->hFile
= mmioOpen32A(lpstrElementName
, NULL
,
157 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_EXCLUSIVE
);
158 if (wmw
->hFile
== 0) {
159 WARN(mciwave
, "can't find file='%s' !\n", lpstrElementName
);
160 return MCIERR_FILE_NOT_FOUND
;
166 TRACE(mciwave
,"hFile=%u\n", wmw
->hFile
);
168 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMS32A
));
169 wmw
->wNotifyDeviceID
= dwDeviceID
;
170 wmw
->dwStatus
= MCI_MODE_NOT_READY
; /* while loading file contents */
172 wmw
->waveDesc
.hWave
= 0;
174 if (wmw
->hFile
!= 0) {
178 if (mmioDescend(wmw
->hFile
, &ckMainRIFF
, NULL
, 0) != 0)
179 return MCIERR_INTERNAL
;
180 TRACE(mciwave
, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
181 (LPSTR
)&ckMainRIFF
.ckid
, (LPSTR
)&ckMainRIFF
.fccType
, ckMainRIFF
.cksize
);
182 if ((ckMainRIFF
.ckid
!= FOURCC_RIFF
) ||
183 (ckMainRIFF
.fccType
!= mmioFOURCC('W', 'A', 'V', 'E')))
184 return MCIERR_INTERNAL
;
185 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
186 if (mmioDescend(wmw
->hFile
, &mmckInfo
, &ckMainRIFF
, MMIO_FINDCHUNK
) != 0)
187 return MCIERR_INTERNAL
;
188 TRACE(mciwave
, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
189 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
190 if (mmioRead32(wmw
->hFile
, (HPSTR
)&wmw
->WaveFormat
,
191 (long)sizeof(PCMWAVEFORMAT
)) != (long)sizeof(PCMWAVEFORMAT
))
192 return MCIERR_INTERNAL
;
193 TRACE(mciwave
, "wFormatTag=%04X !\n", wmw
->WaveFormat
.wf
.wFormatTag
);
194 TRACE(mciwave
, "nChannels=%d \n", wmw
->WaveFormat
.wf
.nChannels
);
195 TRACE(mciwave
, "nSamplesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nSamplesPerSec
);
196 TRACE(mciwave
, "nAvgBytesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nAvgBytesPerSec
);
197 TRACE(mciwave
, "nBlockAlign=%d \n", wmw
->WaveFormat
.wf
.nBlockAlign
);
198 TRACE(mciwave
, "wBitsPerSample=%u !\n", wmw
->WaveFormat
.wBitsPerSample
);
199 mmckInfo
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
200 if (mmioDescend(wmw
->hFile
, &mmckInfo
, &ckMainRIFF
, MMIO_FINDCHUNK
) != 0)
201 return MCIERR_INTERNAL
;
202 TRACE(mciwave
,"Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
203 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
204 TRACE(mciwave
, "nChannels=%d nSamplesPerSec=%ld\n",
205 wmw
->WaveFormat
.wf
.nChannels
, wmw
->WaveFormat
.wf
.nSamplesPerSec
);
206 wmw
->dwLength
= mmckInfo
.cksize
;
207 wmw
->dwFileOffset
= mmioSeek32(wmw
->hFile
, 0, SEEK_CUR
); /* >= 0 */
211 wmw
->WaveFormat
.wf
.nAvgBytesPerSec
=
212 wmw
->WaveFormat
.wf
.nSamplesPerSec
* wmw
->WaveFormat
.wf
.nBlockAlign
;
213 wmw
->waveDesc
.lpFormat
= (LPWAVEFORMAT
)&wmw
->WaveFormat
;
216 /* By default the device will be opened for output, the MCI_CUE function is there to
217 * change from output to input and back
219 dwRet
= wodMessage(wDevID
, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
220 wmw
->dwStatus
= MCI_MODE_STOP
;
224 /**************************************************************************
225 * WAVE_mciCue [internal]
227 static DWORD
WAVE_mciCue(UINT16 wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
232 This routine is far from complete. At the moment only a check is done on the
233 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
236 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
241 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
243 TRACE(mciwave
,"(%u, %08lX, %p);\n", wDevID
, dwParam
, lpParms
);
245 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
247 /* always close elements ? */
248 if (wmw
->hFile
!= 0) {
249 mmioClose32(wmw
->hFile
, 0);
253 dwRet
= MMSYSERR_NOERROR
; /* assume success */
255 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
256 dwRet
= wodMessage(wDevID
, WODM_CLOSE
, 0, 0L, 0L);
257 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
258 dwRet
= widMessage(wDevID
, WIDM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
260 } else if (wmw
->fInput
) {
261 dwRet
= widMessage(wDevID
, WIDM_CLOSE
, 0, 0L, 0L);
262 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
263 dwRet
= wodMessage(wDevID
, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
266 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
269 /**************************************************************************
270 * WAVE_mciStop [internal]
272 static DWORD
WAVE_mciStop(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
275 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
277 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
279 if (lpParms
== NULL
) return MCIERR_INTERNAL
;
280 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
282 wmw
->dwStatus
= MCI_MODE_STOP
;
284 TRACE(mciwave
, "wmw->dwStatus=%d\n", wmw
->dwStatus
);
287 dwRet
= widMessage(wDevID
, WIDM_STOP
, 0, dwFlags
, (DWORD
)lpParms
);
289 dwRet
= wodMessage(wDevID
, WODM_STOP
, 0, dwFlags
, (DWORD
)lpParms
);
291 if (dwFlags
& MCI_NOTIFY
) {
292 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
293 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
294 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
297 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
300 /**************************************************************************
301 * WAVE_mciClose [internal]
303 static DWORD
WAVE_mciClose(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
306 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
308 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
310 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
312 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
313 WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
316 wmw
->dwStatus
= MCI_MODE_STOP
;
319 if (wmw
->nUseCount
== 0) {
320 if (wmw
->hFile
!= 0) {
321 mmioClose32(wmw
->hFile
, 0);
324 if (wmw
->fInput
) dwRet
= widMessage(wDevID
, WIDM_CLOSE
, 0, 0L, 0L);
325 else dwRet
= wodMessage(wDevID
, WODM_CLOSE
, 0, 0L, 0L);
327 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
330 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
331 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
332 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
333 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
338 /**************************************************************************
339 * WAVE_mciPlay [internal]
341 static DWORD
WAVE_mciPlay(UINT16 wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
347 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
349 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
351 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
354 WARN(mciwave
, "cannot play on input device\n");
355 return MCIERR_NONAPPLICABLE_FUNCTION
;
358 if (wmw
->hFile
== 0) {
359 WARN(mciwave
, "Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
360 return MCIERR_FILE_NOT_FOUND
;
363 if (!(dwFlags
& MCI_WAIT
)) {
364 /** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */
365 return MCI_SendCommandAsync32(wmw
->wNotifyDeviceID
, MCI_PLAY
, dwFlags
, (DWORD
)lpParms
);
369 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
370 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
372 if (lpParms
&& (dwFlags
& MCI_TO
)) {
373 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
376 TRACE(mciwave
, "Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
378 /* go back to begining of chunk */
379 mmioSeek32(wmw
->hFile
, wmw
->dwFileOffset
, SEEK_SET
); /* >= 0 */
381 /* at 22050 bytes per sec => 30 ms by block */
383 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
384 wmw
->WaveHdr
.lpData
= (LPSTR
)GlobalLock16(hData
);
386 wmw
->dwStatus
= MCI_MODE_PLAY
;
388 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
389 while (wmw
->dwStatus
!= MCI_MODE_STOP
) {
390 wmw
->WaveHdr
.dwUser
= 0L;
391 wmw
->WaveHdr
.dwFlags
= 0L;
392 wmw
->WaveHdr
.dwLoops
= 0L;
393 dwRet
= wodMessage(wDevID
, WODM_PREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
394 count
= mmioRead32(wmw
->hFile
, wmw
->WaveHdr
.lpData
, bufsize
);
395 TRACE(mciwave
,"mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
396 if (count
< 1) break;
397 wmw
->WaveHdr
.dwBufferLength
= count
;
398 wmw
->WaveHdr
.dwBytesRecorded
= 0;
399 TRACE(mciwave
,"before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
400 &wmw
->WaveHdr
, wmw
->WaveHdr
.dwBufferLength
, wmw
->WaveHdr
.dwBytesRecorded
);
401 dwRet
= wodMessage(wDevID
, WODM_WRITE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
402 /* FIXME: should use callback mechanisms from audio driver */
404 while (!(wmw
->WaveHdr
.dwFlags
& WHDR_DONE
))
407 wmw
->dwPosition
+= count
;
408 TRACE(mciwave
,"after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
409 dwRet
= wodMessage(wDevID
, WODM_UNPREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
412 if (wmw
->WaveHdr
.lpData
!= NULL
) {
413 GlobalUnlock16(hData
);
415 wmw
->WaveHdr
.lpData
= NULL
;
417 wmw
->dwStatus
= MCI_MODE_STOP
;
418 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
419 TRACE(mciwave
,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
420 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
421 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
427 /**************************************************************************
428 * WAVE_mciRecord [internal]
430 static DWORD
WAVE_mciRecord(UINT16 wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
437 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
439 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
441 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
444 WARN(mciwave
, "cannot record on output device\n");
445 return MCIERR_NONAPPLICABLE_FUNCTION
;
448 if (wmw
->hFile
== 0) {
449 WARN(mciwave
, "can't find file='%s' !\n",
450 wmw
->openParms
.lpstrElementName
);
451 return MCIERR_FILE_NOT_FOUND
;
453 start
= 1; end
= 99999;
454 if (dwFlags
& MCI_FROM
) {
455 start
= lpParms
->dwFrom
;
456 TRACE(mciwave
, "MCI_FROM=%d \n", start
);
458 if (dwFlags
& MCI_TO
) {
460 TRACE(mciwave
,"MCI_TO=%d \n", end
);
463 lpWaveHdr
= &wmw
->WaveHdr
;
464 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
465 lpWaveHdr
->lpData
= (LPSTR
)GlobalLock16(hData
);
466 lpWaveHdr
->dwBufferLength
= bufsize
;
467 lpWaveHdr
->dwUser
= 0L;
468 lpWaveHdr
->dwFlags
= 0L;
469 lpWaveHdr
->dwLoops
= 0L;
470 dwRet
=widMessage(wDevID
,WIDM_PREPARE
,0,(DWORD
)lpWaveHdr
,sizeof(WAVEHDR
));
471 TRACE(mciwave
,"after WIDM_PREPARE \n");
473 lpWaveHdr
->dwBytesRecorded
= 0;
474 dwRet
= widMessage(wDevID
, WIDM_START
, 0, 0L, 0L);
475 TRACE(mciwave
, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
476 lpWaveHdr
, lpWaveHdr
->dwBytesRecorded
);
477 if (lpWaveHdr
->dwBytesRecorded
== 0) break;
479 TRACE(mciwave
,"before WIDM_UNPREPARE \n");
480 dwRet
= widMessage(wDevID
,WIDM_UNPREPARE
,0,(DWORD
)lpWaveHdr
,sizeof(WAVEHDR
));
481 TRACE(mciwave
,"after WIDM_UNPREPARE \n");
482 if (lpWaveHdr
->lpData
!= NULL
) {
483 GlobalUnlock16(hData
);
485 lpWaveHdr
->lpData
= NULL
;
487 if (dwFlags
& MCI_NOTIFY
) {
488 TRACE(mciwave
,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
489 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
490 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
495 /**************************************************************************
496 * WAVE_mciPause [internal]
498 static DWORD
WAVE_mciPause(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
501 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
503 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
505 if (lpParms
== NULL
) return MCIERR_INTERNAL
;
506 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
508 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
509 wmw
->dwStatus
= MCI_MODE_PAUSE
;
512 if (wmw
->fInput
) dwRet
= widMessage(wDevID
, WIDM_PAUSE
, 0, dwFlags
, (DWORD
)lpParms
);
513 else dwRet
= wodMessage(wDevID
, WODM_PAUSE
, 0, dwFlags
, (DWORD
)lpParms
);
515 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
519 /**************************************************************************
520 * WAVE_mciResume [internal]
522 static DWORD
WAVE_mciResume(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
524 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
527 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
529 if (lpParms
== NULL
) return MCIERR_INTERNAL
;
530 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
532 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
533 wmw
->dwStatus
= MCI_MODE_PLAY
;
537 if (wmw
->fInput
) dwRet
= widMessage(wDevID
, WIDM_PLAY
, 0, dwFlags
, (DWORD
)lpParms
);
538 else dwRet
= wodMessage(wDevID
, WODM_PLAY
, 0, dwFlags
, (DWORD
)lpParms
);
539 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
546 /**************************************************************************
547 * WAVE_mciSeek [internal]
549 static DWORD
WAVE_mciSeek(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
552 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
554 TRACE(mciwave
, "(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
556 if (lpParms
== NULL
) {
557 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
558 } else if (wmw
== NULL
) {
559 ret
= MCIERR_INVALID_DEVICE_ID
;
561 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
563 if (dwFlags
& MCI_SEEK_TO_START
) {
565 } else if (dwFlags
& MCI_SEEK_TO_END
) {
566 wmw
->dwPosition
= 0xFFFFFFFF; /* fixme */
567 } else if (dwFlags
& MCI_TO
) {
568 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
570 WARN(mciwave
, "dwFlag doesn't tell where to seek to...\n");
571 return MCIERR_MISSING_PARAMETER
;
574 TRACE(mciwave
, "Seeking to position=%lu bytes\n", wmw
->dwPosition
);
576 if (dwFlags
& MCI_NOTIFY
) {
577 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
578 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
579 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
585 /**************************************************************************
586 * WAVE_mciSet [internal]
588 static DWORD
WAVE_mciSet(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
590 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
592 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
593 if (lpParms
== NULL
) return MCIERR_INTERNAL
;
594 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
596 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
597 switch (lpParms
->dwTimeFormat
) {
598 case MCI_FORMAT_MILLISECONDS
:
599 TRACE(mciwave
, "MCI_FORMAT_MILLISECONDS !\n");
600 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
602 case MCI_FORMAT_BYTES
:
603 TRACE(mciwave
, "MCI_FORMAT_BYTES !\n");
604 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
606 case MCI_FORMAT_SAMPLES
:
607 TRACE(mciwave
, "MCI_FORMAT_SAMPLES !\n");
608 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
611 WARN(mciwave
,"Bad time format %lu!\n", lpParms
->dwTimeFormat
);
612 return MCIERR_BAD_TIME_FORMAT
;
615 if (dwFlags
& MCI_SET_VIDEO
) {
616 TRACE(mciwave
, "No support for video !\n");
617 return MCIERR_UNSUPPORTED_FUNCTION
;
619 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
620 TRACE(mciwave
, "No support for door open !\n");
621 return MCIERR_UNSUPPORTED_FUNCTION
;
623 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
624 TRACE(mciwave
, "No support for door close !\n");
625 return MCIERR_UNSUPPORTED_FUNCTION
;
627 if (dwFlags
& MCI_SET_AUDIO
) {
628 if (dwFlags
& MCI_SET_ON
) {
629 TRACE(mciwave
, "MCI_SET_ON audio !\n");
630 } else if (dwFlags
& MCI_SET_OFF
) {
631 TRACE(mciwave
, "MCI_SET_OFF audio !\n");
633 WARN(mciwave
, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
634 return MCIERR_BAD_INTEGER
;
637 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
638 TRACE(mciwave
, "MCI_SET_AUDIO_ALL !\n");
639 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
640 TRACE(mciwave
, "MCI_SET_AUDIO_LEFT !\n");
641 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
642 TRACE(mciwave
, "MCI_SET_AUDIO_RIGHT !\n");
644 if (dwFlags
& MCI_WAVE_INPUT
)
645 TRACE(mciwave
, "MCI_WAVE_INPUT !\n");
646 if (dwFlags
& MCI_WAVE_OUTPUT
)
647 TRACE(mciwave
, "MCI_WAVE_OUTPUT !\n");
648 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
649 TRACE(mciwave
, "MCI_WAVE_SET_ANYINPUT !\n");
650 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
651 TRACE(mciwave
, "MCI_WAVE_SET_ANYOUTPUT !\n");
652 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
)
653 TRACE(mciwave
, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
654 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
)
655 TRACE(mciwave
, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
656 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
)
657 TRACE(mciwave
, "MCI_WAVE_SET_BLOCKALIGN !\n");
658 if (dwFlags
& MCI_WAVE_SET_CHANNELS
)
659 TRACE(mciwave
, "MCI_WAVE_SET_CHANNELS !\n");
660 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
)
661 TRACE(mciwave
, "MCI_WAVE_SET_FORMATTAG !\n");
662 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
)
663 TRACE(mciwave
, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
668 /**************************************************************************
669 * WAVE_mciStatus [internal]
671 static DWORD
WAVE_mciStatus(UINT16 wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
673 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
675 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
676 if (lpParms
== NULL
) return MCIERR_INTERNAL
;
677 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
679 if (dwFlags
& MCI_STATUS_ITEM
) {
680 switch(lpParms
->dwItem
) {
681 case MCI_STATUS_CURRENT_TRACK
:
682 lpParms
->dwReturn
= 1;
683 TRACE(mciwave
, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
685 case MCI_STATUS_LENGTH
:
686 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
687 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->dwLength
);
688 TRACE(mciwave
, "MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
690 case MCI_STATUS_MODE
:
691 lpParms
->dwReturn
= wmw
->dwStatus
;
692 TRACE(mciwave
, "MCI_STATUS_MODE => %lu\n", lpParms
->dwReturn
);
694 case MCI_STATUS_MEDIA_PRESENT
:
695 TRACE(mciwave
, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
696 lpParms
->dwReturn
= TRUE
;
698 case MCI_STATUS_NUMBER_OF_TRACKS
:
699 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
700 lpParms
->dwReturn
= 1;
701 TRACE(mciwave
, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
703 case MCI_STATUS_POSITION
:
704 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
705 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
706 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
);
707 TRACE(mciwave
, "MCI_STATUS_POSITION %s => %lu\n",
708 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
710 case MCI_STATUS_READY
:
711 lpParms
->dwReturn
= (wmw
->dwStatus
!= MCI_MODE_NOT_READY
);
712 TRACE(mciwave
,"MCI_STATUS_READY => %lu!\n", lpParms
->dwReturn
);
714 case MCI_STATUS_TIME_FORMAT
:
715 lpParms
->dwReturn
= wmw
->dwMciTimeFormat
;
716 TRACE(mciwave
, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
719 TRACE(mciwave
,"MCI_WAVE_INPUT !\n");
720 lpParms
->dwReturn
= 0;
722 case MCI_WAVE_OUTPUT
:
723 TRACE(mciwave
,"MCI_WAVE_OUTPUT !\n");
724 lpParms
->dwReturn
= 0;
726 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
727 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
728 TRACE(mciwave
,"MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
730 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
731 lpParms
->dwReturn
= wmw
->WaveFormat
.wBitsPerSample
;
732 TRACE(mciwave
,"MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
734 case MCI_WAVE_STATUS_BLOCKALIGN
:
735 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nBlockAlign
;
736 TRACE(mciwave
,"MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
738 case MCI_WAVE_STATUS_CHANNELS
:
739 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nChannels
;
740 TRACE(mciwave
,"MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
742 case MCI_WAVE_STATUS_FORMATTAG
:
743 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.
745 TRACE(mciwave
,"MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
747 case MCI_WAVE_STATUS_LEVEL
:
748 TRACE(mciwave
,"MCI_WAVE_STATUS_LEVEL !\n");
749 lpParms
->dwReturn
= 0xAAAA5555;
751 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
752 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nSamplesPerSec
;
753 TRACE(mciwave
,"MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
756 WARN(mciwave
,"unknown command %08lX !\n", lpParms
->dwItem
);
757 return MCIERR_UNRECOGNIZED_COMMAND
;
760 if (dwFlags
& MCI_NOTIFY
) {
761 TRACE(mciwave
,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
762 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
763 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
768 /**************************************************************************
769 * WAVE_mciGetDevCaps [internal]
771 static DWORD
WAVE_mciGetDevCaps(UINT16 wDevID
, DWORD dwFlags
,
772 LPMCI_GETDEVCAPS_PARMS lpParms
)
774 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
776 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
778 if (lpParms
== NULL
) return MCIERR_INTERNAL
;
779 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
781 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
782 switch(lpParms
->dwItem
) {
783 case MCI_GETDEVCAPS_DEVICE_TYPE
:
784 lpParms
->dwReturn
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
786 case MCI_GETDEVCAPS_HAS_AUDIO
:
787 lpParms
->dwReturn
= TRUE
;
789 case MCI_GETDEVCAPS_HAS_VIDEO
:
790 lpParms
->dwReturn
= FALSE
;
792 case MCI_GETDEVCAPS_USES_FILES
:
793 lpParms
->dwReturn
= TRUE
;
795 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
796 lpParms
->dwReturn
= TRUE
;
798 case MCI_GETDEVCAPS_CAN_RECORD
:
799 lpParms
->dwReturn
= TRUE
;
801 case MCI_GETDEVCAPS_CAN_EJECT
:
802 lpParms
->dwReturn
= FALSE
;
804 case MCI_GETDEVCAPS_CAN_PLAY
:
805 lpParms
->dwReturn
= TRUE
;
807 case MCI_GETDEVCAPS_CAN_SAVE
:
808 lpParms
->dwReturn
= TRUE
;
810 case MCI_WAVE_GETDEVCAPS_INPUTS
:
811 lpParms
->dwReturn
= 1;
813 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
814 lpParms
->dwReturn
= 1;
817 TRACE(mciwave
, "Unknown capability (%08lx) !\n", lpParms
->dwItem
);
818 return MCIERR_UNRECOGNIZED_COMMAND
;
824 /**************************************************************************
825 * WAVE_mciInfo [internal]
827 static DWORD
WAVE_mciInfo(UINT16 wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMS16 lpParms
)
831 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
833 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
835 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
836 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
837 } else if (wmw
== NULL
) {
838 ret
= MCIERR_INVALID_DEVICE_ID
;
840 TRACE(mciwave
, "buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
843 case MCI_INFO_PRODUCT
:
844 str
= "Wine's audio player";
847 str
= wmw
->openParms
.lpstrElementName
;
850 str
= "Wine Wave In";
852 case MCI_WAVE_OUTPUT
:
853 str
= "Wine Wave Out";
856 WARN(mciwave
, "Don't know this info command (%lu)\n", dwFlags
);
857 return MCIERR_UNRECOGNIZED_COMMAND
;
861 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
862 ret
= MCIERR_PARAM_OVERFLOW
;
864 lstrcpyn32A(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
867 lpParms
->lpstrReturn
[0] = 0;
873 /**************************************************************************
874 * WAVE_DriverProc32 [sample driver]
876 LONG
MCIWAVE_DriverProc32(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
877 DWORD dwParam1
, DWORD dwParam2
)
879 TRACE(mciwave
,"(%08lX, %04X, %08lX, %08lX, %08lX)\n",
880 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
883 case DRV_LOAD
: return 1;
884 case DRV_FREE
: return 1;
885 case DRV_OPEN
: return 1;
886 case DRV_CLOSE
: return 1;
887 case DRV_ENABLE
: return 1;
888 case DRV_DISABLE
: return 1;
889 case DRV_QUERYCONFIGURE
: return 1;
890 case DRV_CONFIGURE
: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
891 case DRV_INSTALL
: return DRVCNF_RESTART
;
892 case DRV_REMOVE
: return DRVCNF_RESTART
;
893 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMS32A
)dwParam2
);
894 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
895 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
896 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
897 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
898 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
899 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
900 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
901 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
902 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
903 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
904 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMS16
) dwParam2
);
905 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
922 WARN(mciwave
, "Unsupported command=%s\n", MCI_CommandToString(wMsg
));
926 FIXME(mciwave
, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
929 FIXME(mciwave
, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg
));
930 return DefDriverProc32(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
932 return MCIERR_UNRECOGNIZED_COMMAND
;