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>
23 #include "wine/winuser16.h"
25 #include "multimedia.h"
30 DEFAULT_DEBUG_CHANNEL(mciwave
)
32 #define MAX_MCIWAVEDRV (1)
35 int nUseCount
; /* Incremented for each shared open */
36 BOOL16 fShareable
; /* TRUE if first open was shareable */
37 WORD wNotifyDeviceID
;/* MCI device ID with a pending notification */
38 HANDLE16 hCallback
; /* Callback handle for pending notification */
39 HMMIO hFile
; /* mmio file handle open as Element */
40 MCI_WAVE_OPEN_PARMSA openParms
;
41 WAVEOPENDESC waveDesc
;
42 PCMWAVEFORMAT WaveFormat
;
44 BOOL16 fInput
; /* FALSE = Output, TRUE = Input */
45 WORD dwStatus
; /* one from MCI_MODE_xxxx */
46 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
47 DWORD dwFileOffset
; /* Offset of chunk in mmio file */
48 DWORD dwLength
; /* number of bytes in chunk for playing */
49 DWORD dwPosition
; /* position in bytes in chunk for playing */
52 static WINE_MCIWAVE MCIWaveDev
[MAX_MCIWAVEDRV
];
54 /*======================================================================*
55 * MCI WAVE implemantation *
56 *======================================================================*/
58 /**************************************************************************
59 * WAVE_mciGetOpenDev [internal]
61 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT16 wDevID
)
63 if (wDevID
>= MAX_MCIWAVEDRV
|| MCIWaveDev
[wDevID
].nUseCount
== 0) {
64 WARN(mciwave
, "Invalid wDevID=%u\n", wDevID
);
67 return &MCIWaveDev
[wDevID
];
70 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
)
74 switch (wmw
->dwMciTimeFormat
) {
75 case MCI_FORMAT_MILLISECONDS
:
76 ret
= (val
* 1000) / wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
78 case MCI_FORMAT_BYTES
:
81 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
82 ret
= (val
* 8) / wmw
->WaveFormat
.wBitsPerSample
;
85 WARN(mciwave
, "Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
87 TRACE(mciwave
, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
91 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
95 switch (wmw
->dwMciTimeFormat
) {
96 case MCI_FORMAT_MILLISECONDS
:
97 ret
= (val
* wmw
->WaveFormat
.wf
.nAvgBytesPerSec
) / 1000;
99 case MCI_FORMAT_BYTES
:
102 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
103 ret
= (val
* wmw
->WaveFormat
.wBitsPerSample
) / 8;
106 WARN(mciwave
, "Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
108 TRACE(mciwave
, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
112 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, MMCKINFO
* pckMainRIFF
)
116 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
117 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
118 return MCIERR_INVALID_FILE
;
119 TRACE(mciwave
, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
120 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
121 if (mmioRead(wmw
->hFile
, (HPSTR
)&wmw
->WaveFormat
,
122 (long)sizeof(PCMWAVEFORMAT
)) != (long)sizeof(PCMWAVEFORMAT
))
123 return MCIERR_INVALID_FILE
;
125 TRACE(mciwave
, "wFormatTag=%04X !\n", wmw
->WaveFormat
.wf
.wFormatTag
);
126 TRACE(mciwave
, "nChannels=%d \n", wmw
->WaveFormat
.wf
.nChannels
);
127 TRACE(mciwave
, "nSamplesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nSamplesPerSec
);
128 TRACE(mciwave
, "nAvgBytesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nAvgBytesPerSec
);
129 TRACE(mciwave
, "nBlockAlign=%d \n", wmw
->WaveFormat
.wf
.nBlockAlign
);
130 TRACE(mciwave
, "wBitsPerSample=%u !\n", wmw
->WaveFormat
.wBitsPerSample
);
131 mmckInfo
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
132 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
133 return MCIERR_INVALID_FILE
;
134 TRACE(mciwave
, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
135 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
136 TRACE(mciwave
, "nChannels=%d nSamplesPerSec=%ld\n",
137 wmw
->WaveFormat
.wf
.nChannels
, wmw
->WaveFormat
.wf
.nSamplesPerSec
);
138 wmw
->dwLength
= mmckInfo
.cksize
;
139 wmw
->dwFileOffset
= mmioSeek(wmw
->hFile
, 0, SEEK_CUR
); /* >= 0 */
143 /**************************************************************************
144 * WAVE_mciOpen [internal]
146 static DWORD
WAVE_mciOpen(UINT16 wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSA lpOpenParms
)
152 TRACE(mciwave
, "(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
153 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
155 if (wDevID
>= MAX_MCIWAVEDRV
) {
156 WARN(mciwave
, "Invalid wDevID=%u\n", wDevID
);
157 return MCIERR_INVALID_DEVICE_ID
;
159 if (dwFlags
& MCI_OPEN_SHAREABLE
)
160 return MCIERR_HARDWARE
;
162 wmw
= &MCIWaveDev
[wDevID
];
164 if (wmw
->nUseCount
> 0) {
165 /* The driver is already opened on this channel
166 * Wave driver cannot be shared
168 return MCIERR_DEVICE_OPEN
;
172 dwDeviceID
= lpOpenParms
->wDeviceID
;
176 TRACE(mciwave
, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID
, dwDeviceID
);
178 if (dwFlags
& MCI_OPEN_ELEMENT
) {
179 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
180 /* could it be that (DWORD)lpOpenParms->lpstrElementName
181 * contains the hFile value ?
183 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
185 LPCSTR lpstrElementName
= lpOpenParms
->lpstrElementName
;
187 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
188 TRACE(mciwave
, "MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName
);
189 if (lpstrElementName
&& (strlen(lpstrElementName
) > 0)) {
190 wmw
->hFile
= mmioOpenA((LPSTR
)lpstrElementName
, NULL
,
191 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_EXCLUSIVE
);
192 if (wmw
->hFile
== 0) {
193 WARN(mciwave
, "can't find file='%s' !\n", lpstrElementName
);
194 dwRet
= MCIERR_FILE_NOT_FOUND
;
201 TRACE(mciwave
, "hFile=%u\n", wmw
->hFile
);
203 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
204 wmw
->wNotifyDeviceID
= dwDeviceID
;
205 wmw
->dwStatus
= MCI_MODE_NOT_READY
; /* while loading file contents */
207 wmw
->waveDesc
.hWave
= 0;
209 if (dwRet
== 0 && wmw
->hFile
!= 0) {
212 if (mmioDescend(wmw
->hFile
, &ckMainRIFF
, NULL
, 0) != 0) {
213 dwRet
= MCIERR_INVALID_FILE
;
215 TRACE(mciwave
, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
216 (LPSTR
)&ckMainRIFF
.ckid
, (LPSTR
)&ckMainRIFF
.fccType
, ckMainRIFF
.cksize
);
217 if ((ckMainRIFF
.ckid
!= FOURCC_RIFF
) ||
218 (ckMainRIFF
.fccType
!= mmioFOURCC('W', 'A', 'V', 'E'))) {
219 dwRet
= MCIERR_INVALID_FILE
;
221 dwRet
= WAVE_mciReadFmt(wmw
, &ckMainRIFF
);
228 wmw
->WaveFormat
.wf
.nAvgBytesPerSec
=
229 wmw
->WaveFormat
.wf
.nSamplesPerSec
* wmw
->WaveFormat
.wf
.nBlockAlign
;
230 wmw
->waveDesc
.lpFormat
= (LPWAVEFORMAT
)&wmw
->WaveFormat
;
233 wmw
->dwStatus
= MCI_MODE_STOP
;
237 mmioClose(wmw
->hFile
, 0);
243 /**************************************************************************
244 * WAVE_mciCue [internal]
246 static DWORD
WAVE_mciCue(UINT16 wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
251 This routine is far from complete. At the moment only a check is done on the
252 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
255 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
260 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
262 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwParam
, lpParms
);
264 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
266 /* always close elements ? */
267 if (wmw
->hFile
!= 0) {
268 mmioClose(wmw
->hFile
, 0);
272 dwRet
= MMSYSERR_NOERROR
; /* assume success */
274 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
275 dwRet
= wodMessage(wDevID
, WODM_CLOSE
, 0, 0L, 0L);
276 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
277 dwRet
= widMessage(wDevID
, WIDM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
279 } else if (wmw
->fInput
) {
280 dwRet
= widMessage(wDevID
, WIDM_CLOSE
, 0, 0L, 0L);
281 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
282 dwRet
= wodMessage(wDevID
, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
285 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
288 /**************************************************************************
289 * WAVE_mciStop [internal]
291 static DWORD
WAVE_mciStop(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
294 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
296 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
298 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
299 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
301 wmw
->dwStatus
= MCI_MODE_STOP
;
303 TRACE(mciwave
, "wmw->dwStatus=%d\n", wmw
->dwStatus
);
306 dwRet
= widMessage(wDevID
, WIDM_STOP
, 0, dwFlags
, (DWORD
)lpParms
);
308 dwRet
= wodMessage(wDevID
, WODM_STOP
, 0, dwFlags
, (DWORD
)lpParms
);
310 if (dwFlags
& MCI_NOTIFY
) {
311 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
312 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
313 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
316 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
319 /**************************************************************************
320 * WAVE_mciClose [internal]
322 static DWORD
WAVE_mciClose(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
325 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
327 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
329 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
331 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
332 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
337 if (wmw
->nUseCount
== 0) {
339 if (wmw
->hFile
!= 0) {
340 mmioClose(wmw
->hFile
, 0);
343 mmRet
= (wmw
->fInput
) ? widMessage(wDevID
, WIDM_CLOSE
, 0, 0L, 0L) :
344 wodMessage(wDevID
, WODM_CLOSE
, 0, 0L, 0L);
346 if (mmRet
!= MMSYSERR_NOERROR
) dwRet
= MCIERR_INTERNAL
;
349 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
350 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
351 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
352 wmw
->wNotifyDeviceID
,
353 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
358 /**************************************************************************
359 * WAVE_mciPlay [internal]
361 static DWORD
WAVE_mciPlay(UINT16 wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
367 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
369 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
371 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
372 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
375 WARN(mciwave
, "cannot play on input device\n");
376 return MCIERR_NONAPPLICABLE_FUNCTION
;
379 if (wmw
->hFile
== 0) {
380 WARN(mciwave
, "Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
381 return MCIERR_FILE_NOT_FOUND
;
384 if (!(dwFlags
& MCI_WAIT
)) {
385 return MCI_SendCommandAsync(wmw
->wNotifyDeviceID
, MCI_PLAY
, dwFlags
,
386 (DWORD
)lpParms
, sizeof(MCI_PLAY_PARMS
));
390 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
391 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
393 if (lpParms
&& (dwFlags
& MCI_TO
)) {
394 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
397 TRACE(mciwave
, "Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
399 /* go back to begining of chunk */
400 mmioSeek(wmw
->hFile
, wmw
->dwFileOffset
, SEEK_SET
); /* >= 0 */
402 /* By default the device will be opened for output, the MCI_CUE function is there to
403 * change from output to input and back
405 dwRet
= wodMessage(wDevID
, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
407 TRACE(mciwave
, "Can't open low level audio device %ld\n", dwRet
);
408 return MCIERR_DEVICE_OPEN
;
411 /* at 22050 bytes per sec => 30 ms by block */
413 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
414 wmw
->WaveHdr
.lpData
= (LPSTR
)GlobalLock16(hData
);
416 wmw
->dwStatus
= MCI_MODE_PLAY
;
418 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
419 while (wmw
->dwStatus
!= MCI_MODE_STOP
) {
420 wmw
->WaveHdr
.dwUser
= 0L;
421 wmw
->WaveHdr
.dwFlags
= 0L;
422 wmw
->WaveHdr
.dwLoops
= 0L;
423 count
= mmioRead(wmw
->hFile
, wmw
->WaveHdr
.lpData
, bufsize
);
424 TRACE(mciwave
, "mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
427 dwRet
= wodMessage(wDevID
, WODM_PREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
428 wmw
->WaveHdr
.dwBufferLength
= count
;
429 wmw
->WaveHdr
.dwBytesRecorded
= 0;
430 TRACE(mciwave
, "before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
431 &wmw
->WaveHdr
, wmw
->WaveHdr
.dwBufferLength
, wmw
->WaveHdr
.dwBytesRecorded
);
432 dwRet
= wodMessage(wDevID
, WODM_WRITE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
433 /* FIXME: should use callback mechanisms from audio driver */
435 while (!(wmw
->WaveHdr
.dwFlags
& WHDR_DONE
))
438 wmw
->dwPosition
+= count
;
439 TRACE(mciwave
, "after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
440 dwRet
= wodMessage(wDevID
, WODM_UNPREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
443 if (wmw
->WaveHdr
.lpData
!= NULL
) {
444 GlobalUnlock16(hData
);
446 wmw
->WaveHdr
.lpData
= NULL
;
449 wodMessage(wDevID
, WODM_STOP
, 0, 0L, 0L);
450 wodMessage(wDevID
, WODM_CLOSE
, 0, 0L, 0L);
452 wmw
->dwStatus
= MCI_MODE_STOP
;
453 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
454 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
455 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
456 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
461 /**************************************************************************
462 * WAVE_mciRecord [internal]
464 static DWORD
WAVE_mciRecord(UINT16 wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
471 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
473 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
475 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
476 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
479 WARN(mciwave
, "cannot record on output device\n");
480 return MCIERR_NONAPPLICABLE_FUNCTION
;
483 if (wmw
->hFile
== 0) {
484 WARN(mciwave
, "can't find file='%s' !\n",
485 wmw
->openParms
.lpstrElementName
);
486 return MCIERR_FILE_NOT_FOUND
;
488 start
= 1; end
= 99999;
489 if (dwFlags
& MCI_FROM
) {
490 start
= lpParms
->dwFrom
;
491 TRACE(mciwave
, "MCI_FROM=%d \n", start
);
493 if (dwFlags
& MCI_TO
) {
495 TRACE(mciwave
, "MCI_TO=%d \n", end
);
498 lpWaveHdr
= &wmw
->WaveHdr
;
499 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
500 lpWaveHdr
->lpData
= (LPSTR
)GlobalLock16(hData
);
501 lpWaveHdr
->dwBufferLength
= bufsize
;
502 lpWaveHdr
->dwUser
= 0L;
503 lpWaveHdr
->dwFlags
= 0L;
504 lpWaveHdr
->dwLoops
= 0L;
505 dwRet
= widMessage(wDevID
,WIDM_PREPARE
,0,(DWORD
)lpWaveHdr
,sizeof(WAVEHDR
));
506 TRACE(mciwave
, "after WIDM_PREPARE \n");
508 lpWaveHdr
->dwBytesRecorded
= 0;
509 dwRet
= widMessage(wDevID
, WIDM_START
, 0, 0L, 0L);
510 TRACE(mciwave
, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
511 lpWaveHdr
, lpWaveHdr
->dwBytesRecorded
);
512 if (lpWaveHdr
->dwBytesRecorded
== 0) break;
514 TRACE(mciwave
, "before WIDM_UNPREPARE \n");
515 dwRet
= widMessage(wDevID
,WIDM_UNPREPARE
,0,(DWORD
)lpWaveHdr
,sizeof(WAVEHDR
));
516 TRACE(mciwave
, "after WIDM_UNPREPARE \n");
517 if (lpWaveHdr
->lpData
!= NULL
) {
518 GlobalUnlock16(hData
);
520 lpWaveHdr
->lpData
= NULL
;
522 if (dwFlags
& MCI_NOTIFY
) {
523 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
524 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
525 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
530 /**************************************************************************
531 * WAVE_mciPause [internal]
533 static DWORD
WAVE_mciPause(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
536 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
538 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
540 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
541 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
543 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
544 wmw
->dwStatus
= MCI_MODE_PAUSE
;
547 if (wmw
->fInput
) dwRet
= widMessage(wDevID
, WIDM_PAUSE
, 0, dwFlags
, (DWORD
)lpParms
);
548 else dwRet
= wodMessage(wDevID
, WODM_PAUSE
, 0, dwFlags
, (DWORD
)lpParms
);
550 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
553 /**************************************************************************
554 * WAVE_mciResume [internal]
556 static DWORD
WAVE_mciResume(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
558 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
561 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
563 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
564 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
566 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
567 wmw
->dwStatus
= MCI_MODE_PLAY
;
571 if (wmw
->fInput
) dwRet
= widMessage(wDevID
, WIDM_PLAY
, 0, dwFlags
, (DWORD
)lpParms
);
572 else dwRet
= wodMessage(wDevID
, WODM_PLAY
, 0, dwFlags
, (DWORD
)lpParms
);
573 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
580 /**************************************************************************
581 * WAVE_mciSeek [internal]
583 static DWORD
WAVE_mciSeek(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
586 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
588 TRACE(mciwave
, "(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
590 if (lpParms
== NULL
) {
591 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
592 } else if (wmw
== NULL
) {
593 ret
= MCIERR_INVALID_DEVICE_ID
;
595 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
597 if (dwFlags
& MCI_SEEK_TO_START
) {
599 } else if (dwFlags
& MCI_SEEK_TO_END
) {
600 wmw
->dwPosition
= 0xFFFFFFFF; /* fixme */
601 } else if (dwFlags
& MCI_TO
) {
602 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
604 WARN(mciwave
, "dwFlag doesn't tell where to seek to...\n");
605 return MCIERR_MISSING_PARAMETER
;
608 TRACE(mciwave
, "Seeking to position=%lu bytes\n", wmw
->dwPosition
);
610 if (dwFlags
& MCI_NOTIFY
) {
611 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
612 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
613 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
619 /**************************************************************************
620 * WAVE_mciSet [internal]
622 static DWORD
WAVE_mciSet(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
624 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
626 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
628 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
629 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
631 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
632 switch (lpParms
->dwTimeFormat
) {
633 case MCI_FORMAT_MILLISECONDS
:
634 TRACE(mciwave
, "MCI_FORMAT_MILLISECONDS !\n");
635 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
637 case MCI_FORMAT_BYTES
:
638 TRACE(mciwave
, "MCI_FORMAT_BYTES !\n");
639 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
641 case MCI_FORMAT_SAMPLES
:
642 TRACE(mciwave
, "MCI_FORMAT_SAMPLES !\n");
643 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
646 WARN(mciwave
, "Bad time format %lu!\n", lpParms
->dwTimeFormat
);
647 return MCIERR_BAD_TIME_FORMAT
;
650 if (dwFlags
& MCI_SET_VIDEO
) {
651 TRACE(mciwave
, "No support for video !\n");
652 return MCIERR_UNSUPPORTED_FUNCTION
;
654 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
655 TRACE(mciwave
, "No support for door open !\n");
656 return MCIERR_UNSUPPORTED_FUNCTION
;
658 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
659 TRACE(mciwave
, "No support for door close !\n");
660 return MCIERR_UNSUPPORTED_FUNCTION
;
662 if (dwFlags
& MCI_SET_AUDIO
) {
663 if (dwFlags
& MCI_SET_ON
) {
664 TRACE(mciwave
, "MCI_SET_ON audio !\n");
665 } else if (dwFlags
& MCI_SET_OFF
) {
666 TRACE(mciwave
, "MCI_SET_OFF audio !\n");
668 WARN(mciwave
, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
669 return MCIERR_BAD_INTEGER
;
672 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
673 TRACE(mciwave
, "MCI_SET_AUDIO_ALL !\n");
674 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
675 TRACE(mciwave
, "MCI_SET_AUDIO_LEFT !\n");
676 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
677 TRACE(mciwave
, "MCI_SET_AUDIO_RIGHT !\n");
679 if (dwFlags
& MCI_WAVE_INPUT
)
680 TRACE(mciwave
, "MCI_WAVE_INPUT !\n");
681 if (dwFlags
& MCI_WAVE_OUTPUT
)
682 TRACE(mciwave
, "MCI_WAVE_OUTPUT !\n");
683 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
684 TRACE(mciwave
, "MCI_WAVE_SET_ANYINPUT !\n");
685 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
686 TRACE(mciwave
, "MCI_WAVE_SET_ANYOUTPUT !\n");
687 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
)
688 TRACE(mciwave
, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
689 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
)
690 TRACE(mciwave
, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
691 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
)
692 TRACE(mciwave
, "MCI_WAVE_SET_BLOCKALIGN !\n");
693 if (dwFlags
& MCI_WAVE_SET_CHANNELS
)
694 TRACE(mciwave
, "MCI_WAVE_SET_CHANNELS !\n");
695 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
)
696 TRACE(mciwave
, "MCI_WAVE_SET_FORMATTAG !\n");
697 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
)
698 TRACE(mciwave
, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
702 /**************************************************************************
703 * WAVE_mciStatus [internal]
705 static DWORD
WAVE_mciStatus(UINT16 wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
707 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
709 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
710 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
711 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
713 if (dwFlags
& MCI_STATUS_ITEM
) {
714 switch(lpParms
->dwItem
) {
715 case MCI_STATUS_CURRENT_TRACK
:
716 lpParms
->dwReturn
= 1;
717 TRACE(mciwave
, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
719 case MCI_STATUS_LENGTH
:
720 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
721 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->dwLength
);
722 TRACE(mciwave
, "MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
724 case MCI_STATUS_MODE
:
725 lpParms
->dwReturn
= wmw
->dwStatus
;
726 TRACE(mciwave
, "MCI_STATUS_MODE => %lu\n", lpParms
->dwReturn
);
728 case MCI_STATUS_MEDIA_PRESENT
:
729 TRACE(mciwave
, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
730 lpParms
->dwReturn
= TRUE
;
732 case MCI_STATUS_NUMBER_OF_TRACKS
:
733 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
734 lpParms
->dwReturn
= 1;
735 TRACE(mciwave
, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
737 case MCI_STATUS_POSITION
:
738 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
739 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
740 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
);
741 TRACE(mciwave
, "MCI_STATUS_POSITION %s => %lu\n",
742 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
744 case MCI_STATUS_READY
:
745 lpParms
->dwReturn
= (wmw
->dwStatus
!= MCI_MODE_NOT_READY
);
746 TRACE(mciwave
, "MCI_STATUS_READY => %lu!\n", lpParms
->dwReturn
);
748 case MCI_STATUS_TIME_FORMAT
:
749 lpParms
->dwReturn
= wmw
->dwMciTimeFormat
;
750 TRACE(mciwave
, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
753 TRACE(mciwave
, "MCI_WAVE_INPUT !\n");
754 lpParms
->dwReturn
= 0;
756 case MCI_WAVE_OUTPUT
:
757 TRACE(mciwave
, "MCI_WAVE_OUTPUT !\n");
758 lpParms
->dwReturn
= 0;
760 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
761 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
762 TRACE(mciwave
, "MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
764 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
765 lpParms
->dwReturn
= wmw
->WaveFormat
.wBitsPerSample
;
766 TRACE(mciwave
, "MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
768 case MCI_WAVE_STATUS_BLOCKALIGN
:
769 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nBlockAlign
;
770 TRACE(mciwave
, "MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
772 case MCI_WAVE_STATUS_CHANNELS
:
773 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nChannels
;
774 TRACE(mciwave
, "MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
776 case MCI_WAVE_STATUS_FORMATTAG
:
777 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.wFormatTag
;
778 TRACE(mciwave
, "MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
780 case MCI_WAVE_STATUS_LEVEL
:
781 TRACE(mciwave
, "MCI_WAVE_STATUS_LEVEL !\n");
782 lpParms
->dwReturn
= 0xAAAA5555;
784 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
785 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nSamplesPerSec
;
786 TRACE(mciwave
, "MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
789 WARN(mciwave
, "unknown command %08lX !\n", lpParms
->dwItem
);
790 return MCIERR_UNRECOGNIZED_COMMAND
;
793 if (dwFlags
& MCI_NOTIFY
) {
794 TRACE(mciwave
, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
795 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
796 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
801 /**************************************************************************
802 * WAVE_mciGetDevCaps [internal]
804 static DWORD
WAVE_mciGetDevCaps(UINT16 wDevID
, DWORD dwFlags
,
805 LPMCI_GETDEVCAPS_PARMS lpParms
)
807 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
809 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
811 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
812 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
814 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
815 switch(lpParms
->dwItem
) {
816 case MCI_GETDEVCAPS_DEVICE_TYPE
:
817 lpParms
->dwReturn
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
819 case MCI_GETDEVCAPS_HAS_AUDIO
:
820 lpParms
->dwReturn
= TRUE
;
822 case MCI_GETDEVCAPS_HAS_VIDEO
:
823 lpParms
->dwReturn
= FALSE
;
825 case MCI_GETDEVCAPS_USES_FILES
:
826 lpParms
->dwReturn
= TRUE
;
828 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
829 lpParms
->dwReturn
= TRUE
;
831 case MCI_GETDEVCAPS_CAN_RECORD
:
832 lpParms
->dwReturn
= TRUE
;
834 case MCI_GETDEVCAPS_CAN_EJECT
:
835 lpParms
->dwReturn
= FALSE
;
837 case MCI_GETDEVCAPS_CAN_PLAY
:
838 lpParms
->dwReturn
= TRUE
;
840 case MCI_GETDEVCAPS_CAN_SAVE
:
841 lpParms
->dwReturn
= TRUE
;
843 case MCI_WAVE_GETDEVCAPS_INPUTS
:
844 lpParms
->dwReturn
= 1;
846 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
847 lpParms
->dwReturn
= 1;
850 TRACE(mciwave
, "Unknown capability (%08lx) !\n", lpParms
->dwItem
);
851 return MCIERR_UNRECOGNIZED_COMMAND
;
857 /**************************************************************************
858 * WAVE_mciInfo [internal]
860 static DWORD
WAVE_mciInfo(UINT16 wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMS16 lpParms
)
864 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
866 TRACE(mciwave
, "(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
868 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
869 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
870 } else if (wmw
== NULL
) {
871 ret
= MCIERR_INVALID_DEVICE_ID
;
873 TRACE(mciwave
, "buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
876 case MCI_INFO_PRODUCT
:
877 str
= "Wine's audio player";
880 str
= wmw
->openParms
.lpstrElementName
;
883 str
= "Wine Wave In";
885 case MCI_WAVE_OUTPUT
:
886 str
= "Wine Wave Out";
889 WARN(mciwave
, "Don't know this info command (%lu)\n", dwFlags
);
890 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
894 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
895 ret
= MCIERR_PARAM_OVERFLOW
;
897 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
900 lpParms
->lpstrReturn
[0] = 0;
906 /**************************************************************************
907 * MCIWAVE_DriverProc [sample driver]
909 LONG
MCIWAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
910 DWORD dwParam1
, DWORD dwParam2
)
912 TRACE(mciwave
, "(%08lX, %04X, %08lX, %08lX, %08lX)\n",
913 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
916 case DRV_LOAD
: return 1;
917 case DRV_FREE
: return 1;
918 case DRV_OPEN
: return 1;
919 case DRV_CLOSE
: return 1;
920 case DRV_ENABLE
: return 1;
921 case DRV_DISABLE
: return 1;
922 case DRV_QUERYCONFIGURE
: return 1;
923 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
924 case DRV_INSTALL
: return DRVCNF_RESTART
;
925 case DRV_REMOVE
: return DRVCNF_RESTART
;
926 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSA
) dwParam2
);
927 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
928 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
929 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
930 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
931 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
932 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
933 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
934 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
935 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
936 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
937 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMS16
) dwParam2
);
938 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
955 WARN(mciwave
, "Unsupported command=%s\n", MCI_CommandToString(wMsg
));
959 FIXME(mciwave
, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
962 FIXME(mciwave
, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg
));
963 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
965 return MCIERR_UNRECOGNIZED_COMMAND
;