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"
28 #include "debugtools.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 static DWORD
WAVE_mciResume(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
62 /**************************************************************************
63 * MCIWAVE_drvGetDrv [internal]
65 static WINE_MCIWAVE
* WAVE_drvGetDrv(UINT16 wDevID
)
69 for (i
= 0; i
< MAX_MCIWAVEDRV
; i
++) {
70 if (MCIWaveDev
[i
].wDevID
== wDevID
) {
71 return &MCIWaveDev
[i
];
77 /**************************************************************************
78 * MCIWAVE_drvOpen [internal]
80 static DWORD
WAVE_drvOpen(LPSTR str
, LPMCI_OPEN_DRIVER_PARMSA modp
)
84 for (i
= 0; i
< MAX_MCIWAVEDRV
; i
++) {
85 if (MCIWaveDev
[i
].wDevID
== 0) {
86 MCIWaveDev
[i
].wDevID
= modp
->wDeviceID
;
87 modp
->wCustomCommandTable
= -1;
88 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
89 return modp
->wDeviceID
;
95 /**************************************************************************
96 * MCIWAVE_drvClose [internal]
98 static DWORD
WAVE_drvClose(DWORD dwDevID
)
100 WINE_MCIWAVE
* wmcda
= WAVE_drvGetDrv(dwDevID
);
109 /**************************************************************************
110 * WAVE_mciGetOpenDev [internal]
112 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT16 wDevID
)
114 WINE_MCIWAVE
* wmw
= WAVE_drvGetDrv(wDevID
);
116 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
117 WARN("Invalid wDevID=%u\n", wDevID
);
123 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
)
127 switch (wmw
->dwMciTimeFormat
) {
128 case MCI_FORMAT_MILLISECONDS
:
129 ret
= (val
* 1000) / wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
131 case MCI_FORMAT_BYTES
:
134 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
135 ret
= (val
* 8) / wmw
->WaveFormat
.wBitsPerSample
;
138 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
140 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
144 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
148 switch (wmw
->dwMciTimeFormat
) {
149 case MCI_FORMAT_MILLISECONDS
:
150 ret
= (val
* wmw
->WaveFormat
.wf
.nAvgBytesPerSec
) / 1000;
152 case MCI_FORMAT_BYTES
:
155 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
156 ret
= (val
* wmw
->WaveFormat
.wBitsPerSample
) / 8;
159 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
161 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
165 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, MMCKINFO
* pckMainRIFF
)
169 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
170 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
171 return MCIERR_INVALID_FILE
;
172 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
173 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
174 if (mmioRead(wmw
->hFile
, (HPSTR
)&wmw
->WaveFormat
,
175 (long)sizeof(PCMWAVEFORMAT
)) != (long)sizeof(PCMWAVEFORMAT
))
176 return MCIERR_INVALID_FILE
;
178 TRACE("wFormatTag=%04X !\n", wmw
->WaveFormat
.wf
.wFormatTag
);
179 TRACE("nChannels=%d \n", wmw
->WaveFormat
.wf
.nChannels
);
180 TRACE("nSamplesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nSamplesPerSec
);
181 TRACE("nAvgBytesPerSec=%ld\n", wmw
->WaveFormat
.wf
.nAvgBytesPerSec
);
182 TRACE("nBlockAlign=%d \n", wmw
->WaveFormat
.wf
.nBlockAlign
);
183 TRACE("wBitsPerSample=%u !\n", wmw
->WaveFormat
.wBitsPerSample
);
184 mmckInfo
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
185 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
186 return MCIERR_INVALID_FILE
;
187 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
188 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
189 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
190 wmw
->WaveFormat
.wf
.nChannels
, wmw
->WaveFormat
.wf
.nSamplesPerSec
);
191 wmw
->dwLength
= mmckInfo
.cksize
;
192 wmw
->dwFileOffset
= mmioSeek(wmw
->hFile
, 0, SEEK_CUR
); /* >= 0 */
196 /**************************************************************************
197 * WAVE_mciOpen [internal]
199 static DWORD
WAVE_mciOpen(UINT16 wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSA lpOpenParms
)
203 WINE_MCIWAVE
* wmw
= WAVE_drvGetDrv(wDevID
);
205 TRACE("(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
206 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
207 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
209 if (dwFlags
& MCI_OPEN_SHAREABLE
)
210 return MCIERR_HARDWARE
;
212 if (wmw
->nUseCount
> 0) {
213 /* The driver is already opened on this channel
214 * Wave driver cannot be shared
216 return MCIERR_DEVICE_OPEN
;
220 dwDeviceID
= lpOpenParms
->wDeviceID
;
225 TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID
, dwDeviceID
);
227 if (dwFlags
& MCI_OPEN_ELEMENT
) {
228 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
229 /* could it be that (DWORD)lpOpenParms->lpstrElementName
230 * contains the hFile value ?
232 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
234 LPCSTR lpstrElementName
= lpOpenParms
->lpstrElementName
;
236 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
237 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName
);
238 if (lpstrElementName
&& (strlen(lpstrElementName
) > 0)) {
239 wmw
->hFile
= mmioOpenA((LPSTR
)lpstrElementName
, NULL
,
240 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_DENYWRITE
);
241 if (wmw
->hFile
== 0) {
242 WARN("can't find file='%s' !\n", lpstrElementName
);
243 dwRet
= MCIERR_FILE_NOT_FOUND
;
250 TRACE("hFile=%u\n", wmw
->hFile
);
252 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
253 wmw
->wNotifyDeviceID
= dwDeviceID
;
254 wmw
->dwStatus
= MCI_MODE_NOT_READY
; /* while loading file contents */
256 wmw
->waveDesc
.hWave
= 0;
258 if (dwRet
== 0 && wmw
->hFile
!= 0) {
261 if (mmioDescend(wmw
->hFile
, &ckMainRIFF
, NULL
, 0) != 0) {
262 dwRet
= MCIERR_INVALID_FILE
;
264 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
265 (LPSTR
)&ckMainRIFF
.ckid
, (LPSTR
)&ckMainRIFF
.fccType
, ckMainRIFF
.cksize
);
266 if ((ckMainRIFF
.ckid
!= FOURCC_RIFF
) ||
267 (ckMainRIFF
.fccType
!= mmioFOURCC('W', 'A', 'V', 'E'))) {
268 dwRet
= MCIERR_INVALID_FILE
;
270 dwRet
= WAVE_mciReadFmt(wmw
, &ckMainRIFF
);
277 wmw
->WaveFormat
.wf
.nAvgBytesPerSec
=
278 wmw
->WaveFormat
.wf
.nSamplesPerSec
* wmw
->WaveFormat
.wf
.nBlockAlign
;
279 wmw
->waveDesc
.lpFormat
= (LPWAVEFORMAT
)&wmw
->WaveFormat
;
282 wmw
->dwStatus
= MCI_MODE_STOP
;
286 mmioClose(wmw
->hFile
, 0);
292 /**************************************************************************
293 * WAVE_mciCue [internal]
295 static DWORD
WAVE_mciCue(UINT16 wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
300 This routine is far from complete. At the moment only a check is done on the
301 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
304 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
309 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
311 TRACE("(%u, %08lX, %p);\n", wDevID
, dwParam
, lpParms
);
313 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
315 /* always close elements ? */
316 if (wmw
->hFile
!= 0) {
317 mmioClose(wmw
->hFile
, 0);
321 dwRet
= MMSYSERR_NOERROR
; /* assume success */
323 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
324 dwRet
= wodMessage(wmw
->wWavID
, WODM_CLOSE
, 0, 0L, 0L);
325 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
326 dwRet
= widMessage(wmw
->wWavID
, WIDM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
328 } else if (wmw
->fInput
) {
329 dwRet
= widMessage(wmw
->wWavID
, WIDM_CLOSE
, 0, 0L, 0L);
330 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
331 dwRet
= wodMessage(wmw
->wWavID
, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
334 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
337 /**************************************************************************
338 * WAVE_mciStop [internal]
340 static DWORD
WAVE_mciStop(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
343 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
345 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
347 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
348 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
350 wmw
->dwStatus
= MCI_MODE_STOP
;
352 TRACE("wmw->dwStatus=%d\n", wmw
->dwStatus
);
355 dwRet
= widMessage(wmw
->wWavID
, WIDM_RESET
, 0, dwFlags
, (DWORD
)lpParms
);
357 dwRet
= wodMessage(wmw
->wWavID
, WODM_RESET
, 0, dwFlags
, (DWORD
)lpParms
);
359 if (dwFlags
& MCI_NOTIFY
) {
360 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
361 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
362 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
365 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
368 /**************************************************************************
369 * WAVE_mciClose [internal]
371 static DWORD
WAVE_mciClose(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
374 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
376 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
378 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
380 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
381 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
386 if (wmw
->nUseCount
== 0) {
388 if (wmw
->hFile
!= 0) {
389 mmioClose(wmw
->hFile
, 0);
392 mmRet
= (wmw
->fInput
) ? widMessage(wmw
->wWavID
, WIDM_CLOSE
, 0, 0L, 0L) :
393 wodMessage(wmw
->wWavID
, WODM_CLOSE
, 0, 0L, 0L);
395 if (mmRet
!= MMSYSERR_NOERROR
) dwRet
= MCIERR_INTERNAL
;
398 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
399 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
400 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
401 wmw
->wNotifyDeviceID
,
402 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
407 /**************************************************************************
408 * WAVE_mciPlay [internal]
410 static DWORD
WAVE_mciPlay(UINT16 wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
416 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
418 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
420 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
421 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
424 WARN("cannot play on input device\n");
425 return MCIERR_NONAPPLICABLE_FUNCTION
;
428 if (wmw
->hFile
== 0) {
429 WARN("Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
430 return MCIERR_FILE_NOT_FOUND
;
433 if (!(dwFlags
& MCI_WAIT
)) {
434 return MCI_SendCommandAsync(wmw
->wNotifyDeviceID
, MCI_PLAY
, dwFlags
,
435 (DWORD
)lpParms
, sizeof(MCI_PLAY_PARMS
));
438 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
439 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
440 /* FIXME: parameters (start/end) in lpParams may not be used */
441 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
443 return MCIERR_INTERNAL
;
447 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
448 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
450 if (lpParms
&& (dwFlags
& MCI_TO
)) {
451 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
454 TRACE("Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
456 /* go back to begining of chunk */
457 mmioSeek(wmw
->hFile
, wmw
->dwFileOffset
, SEEK_SET
); /* >= 0 */
459 /* By default the device will be opened for output, the MCI_CUE function is there to
460 * change from output to input and back
462 /* FIXME: how to choose between several output channels ? here 0 is forced */
463 dwRet
= wodMessage(0, WODM_OPEN
, 0, (DWORD
)&wmw
->waveDesc
, CALLBACK_NULL
);
465 TRACE("Can't open low level audio device %ld\n", dwRet
);
466 return MCIERR_DEVICE_OPEN
;
469 /* at 22050 bytes per sec => 30 ms by block */
471 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
472 wmw
->WaveHdr
.lpData
= (LPSTR
)GlobalLock16(hData
);
474 wmw
->dwStatus
= MCI_MODE_PLAY
;
476 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
477 /* FIXME: use several WaveHdr for smoother playback */
478 /* FIXME: use only regular MMSYS functions, not calling directly the driver */
479 while (wmw
->dwStatus
!= MCI_MODE_STOP
) {
480 wmw
->WaveHdr
.dwUser
= 0L;
481 wmw
->WaveHdr
.dwFlags
= 0L;
482 wmw
->WaveHdr
.dwLoops
= 0L;
483 count
= mmioRead(wmw
->hFile
, wmw
->WaveHdr
.lpData
, bufsize
);
484 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
487 dwRet
= wodMessage(wmw
->wWavID
, WODM_PREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
488 wmw
->WaveHdr
.dwBufferLength
= count
;
489 wmw
->WaveHdr
.dwBytesRecorded
= 0;
491 wmw
->WaveHdr
.reserved
= (DWORD
)&wmw
->WaveHdr
;
492 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
493 &wmw
->WaveHdr
, wmw
->WaveHdr
.dwBufferLength
, wmw
->WaveHdr
.dwBytesRecorded
);
494 dwRet
= wodMessage(wmw
->wWavID
, WODM_WRITE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
495 /* FIXME: should use callback mechanisms from audio driver */
497 while (!(wmw
->WaveHdr
.dwFlags
& WHDR_DONE
))
500 wmw
->dwPosition
+= count
;
501 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
502 dwRet
= wodMessage(wmw
->wWavID
, WODM_UNPREPARE
, 0, (DWORD
)&wmw
->WaveHdr
, sizeof(WAVEHDR
));
505 if (wmw
->WaveHdr
.lpData
!= NULL
) {
506 GlobalUnlock16(hData
);
508 wmw
->WaveHdr
.lpData
= NULL
;
511 wodMessage(wmw
->wWavID
, WODM_RESET
, 0, 0L, 0L);
512 wodMessage(wmw
->wWavID
, WODM_CLOSE
, 0, 0L, 0L);
514 wmw
->dwStatus
= MCI_MODE_STOP
;
515 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
516 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
517 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
518 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
523 /**************************************************************************
524 * WAVE_mciRecord [internal]
526 static DWORD
WAVE_mciRecord(UINT16 wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
533 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
535 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
537 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
538 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
541 WARN("cannot record on output device\n");
542 return MCIERR_NONAPPLICABLE_FUNCTION
;
545 if (wmw
->hFile
== 0) {
546 WARN("can't find file='%s' !\n",
547 wmw
->openParms
.lpstrElementName
);
548 return MCIERR_FILE_NOT_FOUND
;
550 start
= 1; end
= 99999;
551 if (dwFlags
& MCI_FROM
) {
552 start
= lpParms
->dwFrom
;
553 TRACE("MCI_FROM=%d \n", start
);
555 if (dwFlags
& MCI_TO
) {
557 TRACE("MCI_TO=%d \n", end
);
560 lpWaveHdr
= &wmw
->WaveHdr
;
561 hData
= GlobalAlloc16(GMEM_MOVEABLE
, bufsize
);
562 lpWaveHdr
->lpData
= (LPSTR
)GlobalLock16(hData
);
563 lpWaveHdr
->dwBufferLength
= bufsize
;
564 lpWaveHdr
->dwUser
= 0L;
565 lpWaveHdr
->dwFlags
= 0L;
566 lpWaveHdr
->dwLoops
= 0L;
567 dwRet
= widMessage(wmw
->wWavID
, WIDM_PREPARE
, 0, (DWORD
)lpWaveHdr
, sizeof(WAVEHDR
));
568 TRACE("after WIDM_PREPARE \n");
570 lpWaveHdr
->dwBytesRecorded
= 0;
571 dwRet
= widMessage(wmw
->wWavID
, WIDM_START
, 0, 0L, 0L);
572 TRACE("after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
573 lpWaveHdr
, lpWaveHdr
->dwBytesRecorded
);
574 if (lpWaveHdr
->dwBytesRecorded
== 0) break;
576 TRACE("before WIDM_UNPREPARE \n");
577 dwRet
= widMessage(wmw
->wWavID
, WIDM_UNPREPARE
, 0, (DWORD
)lpWaveHdr
, sizeof(WAVEHDR
));
578 TRACE("after WIDM_UNPREPARE \n");
579 if (lpWaveHdr
->lpData
!= NULL
) {
580 GlobalUnlock16(hData
);
582 lpWaveHdr
->lpData
= NULL
;
584 if (dwFlags
& MCI_NOTIFY
) {
585 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
586 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
587 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
592 /**************************************************************************
593 * WAVE_mciPause [internal]
595 static DWORD
WAVE_mciPause(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
598 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
600 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
602 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
603 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
605 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
606 wmw
->dwStatus
= MCI_MODE_PAUSE
;
609 if (wmw
->fInput
) dwRet
= widMessage(wmw
->wWavID
, WIDM_PAUSE
, 0, 0L, 0L);
610 else dwRet
= wodMessage(wmw
->wWavID
, WODM_PAUSE
, 0, 0L, 0L);
612 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
615 /**************************************************************************
616 * WAVE_mciResume [internal]
618 static DWORD
WAVE_mciResume(UINT16 wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
620 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
623 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
625 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
626 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
628 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
629 wmw
->dwStatus
= MCI_MODE_PLAY
;
632 /* FIXME: I doubt WIDM_START is correct */
633 if (wmw
->fInput
) dwRet
= widMessage(wmw
->wWavID
, WIDM_START
, 0, 0L, 0L);
634 else dwRet
= wodMessage(wmw
->wWavID
, WODM_RESTART
, 0, 0L, 0L);
635 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
638 /**************************************************************************
639 * WAVE_mciSeek [internal]
641 static DWORD
WAVE_mciSeek(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
644 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
646 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
648 if (lpParms
== NULL
) {
649 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
650 } else if (wmw
== NULL
) {
651 ret
= MCIERR_INVALID_DEVICE_ID
;
653 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
655 if (dwFlags
& MCI_SEEK_TO_START
) {
657 } else if (dwFlags
& MCI_SEEK_TO_END
) {
658 wmw
->dwPosition
= 0xFFFFFFFF; /* fixme */
659 } else if (dwFlags
& MCI_TO
) {
660 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
662 WARN("dwFlag doesn't tell where to seek to...\n");
663 return MCIERR_MISSING_PARAMETER
;
666 TRACE("Seeking to position=%lu bytes\n", wmw
->dwPosition
);
668 if (dwFlags
& MCI_NOTIFY
) {
669 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
670 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
671 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
677 /**************************************************************************
678 * WAVE_mciSet [internal]
680 static DWORD
WAVE_mciSet(UINT16 wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
682 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
684 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
686 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
687 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
689 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
690 switch (lpParms
->dwTimeFormat
) {
691 case MCI_FORMAT_MILLISECONDS
:
692 TRACE("MCI_FORMAT_MILLISECONDS !\n");
693 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
695 case MCI_FORMAT_BYTES
:
696 TRACE("MCI_FORMAT_BYTES !\n");
697 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
699 case MCI_FORMAT_SAMPLES
:
700 TRACE("MCI_FORMAT_SAMPLES !\n");
701 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
704 WARN("Bad time format %lu!\n", lpParms
->dwTimeFormat
);
705 return MCIERR_BAD_TIME_FORMAT
;
708 if (dwFlags
& MCI_SET_VIDEO
) {
709 TRACE("No support for video !\n");
710 return MCIERR_UNSUPPORTED_FUNCTION
;
712 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
713 TRACE("No support for door open !\n");
714 return MCIERR_UNSUPPORTED_FUNCTION
;
716 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
717 TRACE("No support for door close !\n");
718 return MCIERR_UNSUPPORTED_FUNCTION
;
720 if (dwFlags
& MCI_SET_AUDIO
) {
721 if (dwFlags
& MCI_SET_ON
) {
722 TRACE("MCI_SET_ON audio !\n");
723 } else if (dwFlags
& MCI_SET_OFF
) {
724 TRACE("MCI_SET_OFF audio !\n");
726 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
727 return MCIERR_BAD_INTEGER
;
730 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
731 TRACE("MCI_SET_AUDIO_ALL !\n");
732 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
733 TRACE("MCI_SET_AUDIO_LEFT !\n");
734 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
735 TRACE("MCI_SET_AUDIO_RIGHT !\n");
737 if (dwFlags
& MCI_WAVE_INPUT
)
738 TRACE("MCI_WAVE_INPUT !\n");
739 if (dwFlags
& MCI_WAVE_OUTPUT
)
740 TRACE("MCI_WAVE_OUTPUT !\n");
741 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
742 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
743 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
744 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
745 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
)
746 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
747 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
)
748 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
749 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
)
750 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
751 if (dwFlags
& MCI_WAVE_SET_CHANNELS
)
752 TRACE("MCI_WAVE_SET_CHANNELS !\n");
753 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
)
754 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
755 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
)
756 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
760 /**************************************************************************
761 * WAVE_mciStatus [internal]
763 static DWORD
WAVE_mciStatus(UINT16 wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
765 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
767 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
768 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
769 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
771 if (dwFlags
& MCI_STATUS_ITEM
) {
772 switch(lpParms
->dwItem
) {
773 case MCI_STATUS_CURRENT_TRACK
:
774 lpParms
->dwReturn
= 1;
775 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
777 case MCI_STATUS_LENGTH
:
778 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
779 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->dwLength
);
780 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
782 case MCI_STATUS_MODE
:
783 lpParms
->dwReturn
= wmw
->dwStatus
;
784 TRACE("MCI_STATUS_MODE => %lu\n", lpParms
->dwReturn
);
786 case MCI_STATUS_MEDIA_PRESENT
:
787 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
788 lpParms
->dwReturn
= TRUE
;
790 case MCI_STATUS_NUMBER_OF_TRACKS
:
791 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
792 lpParms
->dwReturn
= 1;
793 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
795 case MCI_STATUS_POSITION
:
796 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
797 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
798 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
);
799 TRACE("MCI_STATUS_POSITION %s => %lu\n",
800 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
802 case MCI_STATUS_READY
:
803 lpParms
->dwReturn
= (wmw
->dwStatus
!= MCI_MODE_NOT_READY
);
804 TRACE("MCI_STATUS_READY => %lu!\n", lpParms
->dwReturn
);
806 case MCI_STATUS_TIME_FORMAT
:
807 lpParms
->dwReturn
= wmw
->dwMciTimeFormat
;
808 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
811 TRACE("MCI_WAVE_INPUT !\n");
812 lpParms
->dwReturn
= 0;
814 case MCI_WAVE_OUTPUT
:
815 TRACE("MCI_WAVE_OUTPUT !\n");
816 lpParms
->dwReturn
= 0;
818 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
819 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nAvgBytesPerSec
;
820 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
822 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
823 lpParms
->dwReturn
= wmw
->WaveFormat
.wBitsPerSample
;
824 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
826 case MCI_WAVE_STATUS_BLOCKALIGN
:
827 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nBlockAlign
;
828 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
830 case MCI_WAVE_STATUS_CHANNELS
:
831 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nChannels
;
832 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
834 case MCI_WAVE_STATUS_FORMATTAG
:
835 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.wFormatTag
;
836 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
838 case MCI_WAVE_STATUS_LEVEL
:
839 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
840 lpParms
->dwReturn
= 0xAAAA5555;
842 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
843 lpParms
->dwReturn
= wmw
->WaveFormat
.wf
.nSamplesPerSec
;
844 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
847 WARN("unknown command %08lX !\n", lpParms
->dwItem
);
848 return MCIERR_UNRECOGNIZED_COMMAND
;
851 if (dwFlags
& MCI_NOTIFY
) {
852 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
853 mciDriverNotify16((HWND16
)LOWORD(lpParms
->dwCallback
),
854 wmw
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
859 /**************************************************************************
860 * WAVE_mciGetDevCaps [internal]
862 static DWORD
WAVE_mciGetDevCaps(UINT16 wDevID
, DWORD dwFlags
,
863 LPMCI_GETDEVCAPS_PARMS lpParms
)
865 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
867 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
869 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
870 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
872 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
873 switch(lpParms
->dwItem
) {
874 case MCI_GETDEVCAPS_DEVICE_TYPE
:
875 lpParms
->dwReturn
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
877 case MCI_GETDEVCAPS_HAS_AUDIO
:
878 lpParms
->dwReturn
= TRUE
;
880 case MCI_GETDEVCAPS_HAS_VIDEO
:
881 lpParms
->dwReturn
= FALSE
;
883 case MCI_GETDEVCAPS_USES_FILES
:
884 lpParms
->dwReturn
= TRUE
;
886 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
887 lpParms
->dwReturn
= TRUE
;
889 case MCI_GETDEVCAPS_CAN_RECORD
:
890 lpParms
->dwReturn
= TRUE
;
892 case MCI_GETDEVCAPS_CAN_EJECT
:
893 lpParms
->dwReturn
= FALSE
;
895 case MCI_GETDEVCAPS_CAN_PLAY
:
896 lpParms
->dwReturn
= TRUE
;
898 case MCI_GETDEVCAPS_CAN_SAVE
:
899 lpParms
->dwReturn
= TRUE
;
901 case MCI_WAVE_GETDEVCAPS_INPUTS
:
902 lpParms
->dwReturn
= 1;
904 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
905 lpParms
->dwReturn
= 1;
908 TRACE("Unknown capability (%08lx) !\n", lpParms
->dwItem
);
909 return MCIERR_UNRECOGNIZED_COMMAND
;
915 /**************************************************************************
916 * WAVE_mciInfo [internal]
918 static DWORD
WAVE_mciInfo(UINT16 wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMS16 lpParms
)
922 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
924 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
926 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
927 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
928 } else if (wmw
== NULL
) {
929 ret
= MCIERR_INVALID_DEVICE_ID
;
931 TRACE("buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
934 case MCI_INFO_PRODUCT
:
935 str
= "Wine's audio player";
938 str
= wmw
->openParms
.lpstrElementName
;
941 str
= "Wine Wave In";
943 case MCI_WAVE_OUTPUT
:
944 str
= "Wine Wave Out";
947 WARN("Don't know this info command (%lu)\n", dwFlags
);
948 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
952 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
953 ret
= MCIERR_PARAM_OVERFLOW
;
955 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
958 lpParms
->lpstrReturn
[0] = 0;
964 /**************************************************************************
965 * MCIWAVE_DriverProc [sample driver]
967 LONG CALLBACK
MCIWAVE_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
968 DWORD dwParam1
, DWORD dwParam2
)
970 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
971 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
974 case DRV_LOAD
: return 1;
975 case DRV_FREE
: return 1;
976 case DRV_OPEN
: return WAVE_drvOpen((LPSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSA
)dwParam2
);
977 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
978 case DRV_ENABLE
: return 1;
979 case DRV_DISABLE
: return 1;
980 case DRV_QUERYCONFIGURE
: return 1;
981 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK
); return 1;
982 case DRV_INSTALL
: return DRVCNF_RESTART
;
983 case DRV_REMOVE
: return DRVCNF_RESTART
;
984 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSA
) dwParam2
);
985 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
986 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
987 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
988 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
989 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
990 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
991 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
992 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
993 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
994 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
995 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMS16
) dwParam2
);
996 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1013 WARN("Unsupported command=%s\n", MCI_CommandToString(wMsg
));
1017 FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1020 FIXME("is probably wrong msg=%s\n", MCI_CommandToString(wMsg
));
1021 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1023 return MCIERR_UNRECOGNIZED_COMMAND
;