1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
7 * 2000 Francois Jacques
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(mciwave
);
25 int nUseCount
; /* Incremented for each shared open */
26 BOOL fShareable
; /* TRUE if first open was shareable */
27 HMMIO hFile
; /* mmio file handle open as Element */
28 MCI_WAVE_OPEN_PARMSA openParms
;
29 LPWAVEFORMATEX lpWaveFormat
;
30 BOOL fInput
; /* FALSE = Output, TRUE = Input */
31 volatile WORD dwStatus
; /* one from MCI_MODE_xxxx */
32 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
33 DWORD dwRemaining
; /* remaining bytes to play or record */
34 DWORD dwPosition
; /* position in bytes in chunk */
35 HANDLE hEvent
; /* for synchronization */
36 DWORD dwEventCount
; /* for synchronization */
37 BOOL bTemporaryFile
; /* temporary file (MCI_RECORD) */
38 MMCKINFO ckMainRIFF
; /* main RIFF chunk */
39 MMCKINFO ckWaveData
; /* data chunk */
42 /* ===================================================================
43 * ===================================================================
44 * FIXME: should be using the new mmThreadXXXX functions from WINMM
46 * it would require to add a wine internal flag to mmThreadCreate
47 * in order to pass a 32 bit function instead of a 16 bit one
48 * ===================================================================
49 * =================================================================== */
59 /**************************************************************************
60 * MCI_SCAStarter [internal]
62 static DWORD CALLBACK
MCI_SCAStarter(LPVOID arg
)
64 struct SCA
* sca
= (struct SCA
*)arg
;
67 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
68 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
69 ret
= mciSendCommandA(sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
| MCI_WAIT
, sca
->dwParam2
);
70 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
71 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
72 if (sca
->allocatedCopy
)
73 HeapFree(GetProcessHeap(), 0, (LPVOID
)sca
->dwParam2
);
74 HeapFree(GetProcessHeap(), 0, sca
);
76 WARN("Should not happen ? what's wrong \n");
77 /* should not go after this point */
81 /**************************************************************************
82 * MCI_SendCommandAsync [internal]
84 static DWORD
MCI_SendCommandAsync(UINT wDevID
, UINT wMsg
, DWORD dwParam1
,
85 DWORD dwParam2
, UINT size
)
87 struct SCA
* sca
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA
));
90 return MCIERR_OUT_OF_MEMORY
;
94 sca
->dwParam1
= dwParam1
;
97 sca
->dwParam2
= (DWORD
)HeapAlloc(GetProcessHeap(), 0, size
);
98 if (sca
->dwParam2
== 0) {
99 HeapFree(GetProcessHeap(), 0, sca
);
100 return MCIERR_OUT_OF_MEMORY
;
102 sca
->allocatedCopy
= TRUE
;
103 /* copy structure passed by program in dwParam2 to be sure
104 * we can still use it whatever the program does
106 memcpy((LPVOID
)sca
->dwParam2
, (LPVOID
)dwParam2
, size
);
108 sca
->dwParam2
= dwParam2
;
109 sca
->allocatedCopy
= FALSE
;
112 if (CreateThread(NULL
, 0, MCI_SCAStarter
, sca
, 0, NULL
) == 0) {
113 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
114 return MCI_SCAStarter(&sca
);
119 /*======================================================================*
120 * MCI WAVE implemantation *
121 *======================================================================*/
123 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
125 /**************************************************************************
126 * MCIWAVE_drvOpen [internal]
128 static DWORD
WAVE_drvOpen(LPSTR str
, LPMCI_OPEN_DRIVER_PARMSA modp
)
130 WINE_MCIWAVE
* wmw
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCIWAVE
));
135 wmw
->wDevID
= modp
->wDeviceID
;
136 mciSetDriverData(wmw
->wDevID
, (DWORD
)wmw
);
137 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
138 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
139 return modp
->wDeviceID
;
142 /**************************************************************************
143 * MCIWAVE_drvClose [internal]
145 static DWORD
WAVE_drvClose(DWORD dwDevID
)
147 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(dwDevID
);
150 HeapFree(GetProcessHeap(), 0, wmw
);
151 mciSetDriverData(dwDevID
, 0);
157 /**************************************************************************
158 * WAVE_mciGetOpenDev [internal]
160 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT wDevID
)
162 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
164 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
165 WARN("Invalid wDevID=%u\n", wDevID
);
171 /**************************************************************************
172 * WAVE_ConvertByteToTimeFormat [internal]
174 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
, LPDWORD lpRet
)
178 switch (wmw
->dwMciTimeFormat
) {
179 case MCI_FORMAT_MILLISECONDS
:
180 ret
= (val
* 1000) / wmw
->lpWaveFormat
->nAvgBytesPerSec
;
182 case MCI_FORMAT_BYTES
:
185 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
186 ret
= (val
* 8) / wmw
->lpWaveFormat
->wBitsPerSample
;
189 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
191 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
196 /**************************************************************************
197 * WAVE_ConvertTimeFormatToByte [internal]
199 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
203 switch (wmw
->dwMciTimeFormat
) {
204 case MCI_FORMAT_MILLISECONDS
:
205 ret
= (val
* wmw
->lpWaveFormat
->nAvgBytesPerSec
) / 1000;
207 case MCI_FORMAT_BYTES
:
210 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
211 ret
= (val
* wmw
->lpWaveFormat
->wBitsPerSample
) / 8;
214 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
216 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
220 /**************************************************************************
221 * WAVE_mciReadFmt [internal]
223 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, MMCKINFO
* pckMainRIFF
)
228 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
229 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
230 return MCIERR_INVALID_FILE
;
231 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
232 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
234 wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
235 r
= mmioRead(wmw
->hFile
, (HPSTR
)wmw
->lpWaveFormat
, mmckInfo
.cksize
);
236 if (r
< sizeof(WAVEFORMAT
))
237 return MCIERR_INVALID_FILE
;
239 TRACE("wFormatTag=%04X !\n", wmw
->lpWaveFormat
->wFormatTag
);
240 TRACE("nChannels=%d \n", wmw
->lpWaveFormat
->nChannels
);
241 TRACE("nSamplesPerSec=%ld\n", wmw
->lpWaveFormat
->nSamplesPerSec
);
242 TRACE("nAvgBytesPerSec=%ld\n", wmw
->lpWaveFormat
->nAvgBytesPerSec
);
243 TRACE("nBlockAlign=%d \n", wmw
->lpWaveFormat
->nBlockAlign
);
244 TRACE("wBitsPerSample=%u !\n", wmw
->lpWaveFormat
->wBitsPerSample
);
245 if (r
>= (long)sizeof(WAVEFORMATEX
))
246 TRACE("cbSize=%u !\n", wmw
->lpWaveFormat
->cbSize
);
248 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
249 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
250 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
251 TRACE("can't find data chunk\n");
252 return MCIERR_INVALID_FILE
;
254 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
255 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
256 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
257 wmw
->lpWaveFormat
->nChannels
, wmw
->lpWaveFormat
->nSamplesPerSec
);
262 /**************************************************************************
263 * WAVE_mciCreateRIFFSkeleton [internal]
265 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
267 MMCKINFO ckWaveFormat
;
269 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
270 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
271 LPWAVEFORMATEX lpWaveFormat
= wmw
->lpWaveFormat
;
274 HMMIO hmmio
= wmw
->hFile
;
276 lpckRIFF
->ckid
= FOURCC_RIFF
;
277 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
278 lpckRIFF
->cksize
= 0;
280 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, lpckRIFF
, MMIO_CREATERIFF
))
283 ckWaveFormat
.fccType
= 0;
284 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
285 ckWaveFormat
.cksize
= 16;
289 TRACE("allocating waveformat with default waveformat 11khz/8bit/mono \n");
291 lpWaveFormat
= wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*lpWaveFormat
));
293 lpWaveFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
294 lpWaveFormat
->nChannels
= 1; /* MONO */
295 lpWaveFormat
->nSamplesPerSec
= 11025;
296 lpWaveFormat
->nAvgBytesPerSec
= 11025;
297 lpWaveFormat
->nBlockAlign
= 1;
298 lpWaveFormat
->wBitsPerSample
= 8;
299 lpWaveFormat
->cbSize
= 0; /* don't care */
302 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, &ckWaveFormat
, 0))
305 /* only the first 16 bytes are serialized */
306 if (-1 == mmioWrite(hmmio
, (HPCSTR
) lpWaveFormat
, 16))
309 if (MMSYSERR_NOERROR
!= mmioAscend(hmmio
, &ckWaveFormat
, 0))
312 lpckWaveData
->cksize
= 0;
313 lpckWaveData
->fccType
= 0;
314 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
316 /* create data chunk */
317 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, lpckWaveData
, 0))
323 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
324 return MCIERR_INVALID_FILE
;
327 /**************************************************************************
328 * WAVE_mciOpen [internal]
330 static DWORD
WAVE_mciOpen(UINT wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSA lpOpenParms
)
333 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
334 CHAR
* pszTmpFileName
= 0;
336 TRACE("(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
337 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
338 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
340 if (dwFlags
& MCI_OPEN_SHAREABLE
)
341 return MCIERR_HARDWARE
;
343 if (wmw
->nUseCount
> 0) {
344 /* The driver is already opened on this channel
345 * Wave driver cannot be shared
347 return MCIERR_DEVICE_OPEN
;
354 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
356 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
358 if (dwFlags
& MCI_OPEN_ELEMENT
) {
359 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
360 /* could it be that (DWORD)lpOpenParms->lpstrElementName
361 * contains the hFile value ?
363 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
365 if (strlen(lpOpenParms
->lpstrElementName
) > 0) {
366 lpOpenParms
->lpstrElementName
= lpOpenParms
->lpstrElementName
;
368 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
369 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms
->lpstrElementName
);
371 if (lpOpenParms
->lpstrElementName
&& (strlen(lpOpenParms
->lpstrElementName
) > 0)) {
372 wmw
->hFile
= mmioOpenA((LPSTR
)lpOpenParms
->lpstrElementName
, NULL
,
373 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READWRITE
);
375 if (wmw
->hFile
== 0) {
376 WARN("can't find file='%s' !\n", lpOpenParms
->lpstrElementName
);
377 dwRet
= MCIERR_FILE_NOT_FOUND
;
381 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
383 /* make sure we're are the beginning of the file */
384 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
386 /* first reading of this file. read the waveformat chunk */
387 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
388 dwRet
= MCIERR_INVALID_FILE
;
390 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
391 (LPSTR
)&(lpckMainRIFF
->ckid
),
392 (LPSTR
) &(lpckMainRIFF
->fccType
),
393 (lpckMainRIFF
->cksize
));
395 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
396 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
397 dwRet
= MCIERR_INVALID_FILE
;
399 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
409 CHAR szTmpPath
[MAX_PATH
];
410 CHAR szPrefix
[4] = "TMP\0";
412 pszTmpFileName
= HeapAlloc(GetProcessHeap(),
414 MAX_PATH
* sizeof(*pszTmpFileName
));
416 if (!GetTempPathA(sizeof(szTmpPath
), szTmpPath
)) {
417 WARN("can't retrieve temp path!\n");
418 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
419 return MCIERR_FILE_NOT_FOUND
;
422 if (!GetTempFileNameA(szTmpPath
, szPrefix
, 0, pszTmpFileName
)) {
423 WARN("can't retrieve temp file name!\n");
424 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
425 return MCIERR_FILE_NOT_FOUND
;
428 wmw
->bTemporaryFile
= TRUE
;
430 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName
);
432 if (pszTmpFileName
&& (strlen(pszTmpFileName
) > 0)) {
434 wmw
->hFile
= mmioOpenA(pszTmpFileName
, NULL
,
435 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
437 if (wmw
->hFile
== 0) {
438 /* temporary file could not be created. clean filename. */
439 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
440 WARN("can't create file='%s' !\n", pszTmpFileName
);
441 dwRet
= MCIERR_FILE_NOT_FOUND
;
448 TRACE("hFile=%u\n", wmw
->hFile
);
450 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
452 if (wmw
->bTemporaryFile
== TRUE
)
454 /* Additional openParms is temporary file's name */
455 wmw
->openParms
.lpstrElementName
= pszTmpFileName
;
459 if (wmw
->lpWaveFormat
) {
460 switch (wmw
->lpWaveFormat
->wFormatTag
) {
461 case WAVE_FORMAT_PCM
:
462 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
463 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
464 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
465 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
466 wmw
->lpWaveFormat
->nSamplesPerSec
*
467 wmw
->lpWaveFormat
->nBlockAlign
);
468 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
469 wmw
->lpWaveFormat
->nSamplesPerSec
*
470 wmw
->lpWaveFormat
->nBlockAlign
;
477 wmw
->dwStatus
= MCI_MODE_STOP
;
481 mmioClose(wmw
->hFile
, 0);
487 /**************************************************************************
488 * WAVE_mciCue [internal]
490 static DWORD
WAVE_mciCue(UINT wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
495 This routine is far from complete. At the moment only a check is done on the
496 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
499 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
504 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
506 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID
, dwParam
, lpParms
);
508 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
510 /* always close elements ? */
511 if (wmw
->hFile
!= 0) {
512 mmioClose(wmw
->hFile
, 0);
516 dwRet
= MMSYSERR_NOERROR
; /* assume success */
518 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
519 dwRet
= waveOutClose(wmw
->hWave
);
520 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
522 } else if (wmw
->fInput
) {
523 dwRet
= waveInClose(wmw
->hWave
);
524 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
528 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
531 /**************************************************************************
532 * WAVE_mciStop [internal]
534 static DWORD
WAVE_mciStop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
537 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
539 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
541 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
543 /* wait for playback thread (if any) to exit before processing further */
544 switch (wmw
->dwStatus
) {
547 case MCI_MODE_RECORD
:
549 int oldStat
= wmw
->dwStatus
;
550 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
551 if (oldStat
== MCI_MODE_PAUSE
)
552 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
554 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
562 wmw
->dwStatus
= MCI_MODE_STOP
;
564 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
565 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
566 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
572 /**************************************************************************
573 * WAVE_mciClose [internal]
575 static DWORD
WAVE_mciClose(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
578 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
580 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
582 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
584 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
585 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
590 if (wmw
->nUseCount
== 0) {
591 if (wmw
->hFile
!= 0) {
592 mmioClose(wmw
->hFile
, 0);
597 /* That string got allocated in mciOpen because no filename was specified
598 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
599 * allocated by mciOpen, *NOT* the application.
601 if (wmw
->bTemporaryFile
)
603 HeapFree(GetProcessHeap(), 0, (CHAR
*) wmw
->openParms
.lpstrElementName
);
604 wmw
->openParms
.lpstrElementName
= NULL
;
607 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
608 wmw
->lpWaveFormat
= NULL
;
610 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
611 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
612 wmw
->openParms
.wDeviceID
,
613 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
619 /**************************************************************************
620 * WAVE_mciPlayCallback [internal]
622 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
624 DWORD dwParam1
, DWORD dwParam2
)
626 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
633 InterlockedIncrement(&wmw
->dwEventCount
);
634 TRACE("Returning waveHdr=%lx\n", dwParam1
);
635 SetEvent(wmw
->hEvent
);
638 ERR("Unknown uMsg=%d\n", uMsg
);
642 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
645 ResetEvent(wmw
->hEvent
);
646 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
649 InterlockedIncrement(&wmw
->dwEventCount
);
651 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
655 /**************************************************************************
656 * WAVE_mciPlay [internal]
658 static DWORD
WAVE_mciPlay(UINT wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
661 LONG bufsize
, count
, left
;
663 LPWAVEHDR waveHdr
= NULL
;
664 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
667 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
669 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
670 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
672 /* FIXME : since there is no way to determine in which mode the device is
673 * open (recording/playback) automatically switch from a mode to another
678 WARN("cannot play on input device\n");
679 return MCIERR_NONAPPLICABLE_FUNCTION
;
682 if (wmw
->hFile
== 0) {
683 WARN("Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
684 return MCIERR_FILE_NOT_FOUND
;
687 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
688 /* FIXME: parameters (start/end) in lpParams may not be used */
689 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
692 /** This function will be called again by a thread when async is used.
693 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
694 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
696 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
))) {
697 return MCIERR_INTERNAL
;
700 wmw
->dwStatus
= MCI_MODE_PLAY
;
702 if (!(dwFlags
& MCI_WAIT
)) {
703 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_PLAY
, dwFlags
,
704 (DWORD
)lpParms
, sizeof(MCI_PLAY_PARMS
));
708 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
709 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
711 if (lpParms
&& (dwFlags
& MCI_TO
)) {
712 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
715 TRACE("Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
717 if (end
<= wmw
->dwPosition
)
721 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
722 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
724 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
725 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
728 if (wmw
->lpWaveFormat
) {
729 switch (wmw
->lpWaveFormat
->wFormatTag
) {
730 case WAVE_FORMAT_PCM
:
731 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
732 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
733 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
734 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
735 wmw
->lpWaveFormat
->nSamplesPerSec
*
736 wmw
->lpWaveFormat
->nBlockAlign
);
737 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
738 wmw
->lpWaveFormat
->nSamplesPerSec
*
739 wmw
->lpWaveFormat
->nBlockAlign
;
745 TRACE("can't retrieve wave format %ld\n", dwRet
);
750 /* go back to begining of chunk plus the requested position */
751 /* FIXME: I'm not sure this is correct, notably because some data linked to
752 * the decompression state machine will not be correcly initialized.
753 * try it this way (other way would be to decompress from 0 up to dwPosition
754 * and to start sending to hWave when dwPosition is reached)
756 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
758 /* By default the device will be opened for output, the MCI_CUE function is there to
759 * change from output to input and back
761 /* FIXME: how to choose between several output channels ? here mapper is forced */
762 dwRet
= waveOutOpen(&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
763 (DWORD
)WAVE_mciPlayCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
766 TRACE("Can't open low level audio device %ld\n", dwRet
);
767 dwRet
= MCIERR_DEVICE_OPEN
;
772 /* make it so that 3 buffers per second are needed */
773 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
775 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
776 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
777 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
778 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
779 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
780 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
781 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
782 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
783 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
784 dwRet
= MCIERR_INTERNAL
;
789 left
= min(wmw
->ckWaveData
.cksize
, end
- wmw
->dwPosition
);
790 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
791 wmw
->dwEventCount
= 1L; /* for first buffer */
793 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, left
);
795 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
796 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
797 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
798 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
801 /* count is always <= bufsize, so this is correct regarding the
802 * waveOutPrepareHeader function
804 waveHdr
[whidx
].dwBufferLength
= count
;
805 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
806 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
807 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
,
808 waveHdr
[whidx
].dwBytesRecorded
);
809 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
811 wmw
->dwPosition
+= count
;
812 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
814 WAVE_mciPlayWaitDone(wmw
);
818 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
820 /* just to get rid of some race conditions between play, stop and pause */
821 waveOutReset(wmw
->hWave
);
823 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
824 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
829 HeapFree(GetProcessHeap(), 0, waveHdr
);
832 waveOutClose(wmw
->hWave
);
835 CloseHandle(wmw
->hEvent
);
837 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
838 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
839 wmw
->openParms
.wDeviceID
,
840 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
843 wmw
->dwStatus
= MCI_MODE_STOP
;
848 /**************************************************************************
849 * WAVE_mciPlayCallback [internal]
851 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
853 DWORD dwParam1
, DWORD dwParam2
)
855 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
856 LPWAVEHDR lpWaveHdr
= NULL
;
863 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
865 InterlockedIncrement(&wmw
->dwEventCount
);
867 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
869 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
870 wmw
->dwPosition
+= count
;
871 wmw
->dwRemaining
-= count
;
873 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
874 TRACE("after mmioWrite dwPosition=%lu\n", wmw
->dwPosition
);
876 SetEvent(wmw
->hEvent
);
879 ERR("Unknown uMsg=%d\n", uMsg
);
883 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
886 ResetEvent(wmw
->hEvent
);
887 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
890 InterlockedIncrement(&wmw
->dwEventCount
);
892 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
896 /**************************************************************************
897 * WAVE_mciRecord [internal]
899 static DWORD
WAVE_mciRecord(UINT wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
904 LPWAVEHDR waveHdr
= NULL
;
905 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
908 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
910 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
911 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
913 /* FIXME : since there is no way to determine in which mode the device is
914 * open (recording/playback) automatically switch from a mode to another
919 WARN("cannot record on output device\n");
920 return MCIERR_NONAPPLICABLE_FUNCTION
;
923 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
924 /* FIXME: parameters (start/end) in lpParams may not be used */
925 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
928 /** This function will be called again by a thread when async is used.
929 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
930 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
932 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
))) {
933 return MCIERR_INTERNAL
;
936 wmw
->dwStatus
= MCI_MODE_RECORD
;
938 if (!(dwFlags
& MCI_WAIT
)) {
939 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_RECORD
, dwFlags
,
940 (DWORD
)lpParms
, sizeof(MCI_RECORD_PARMS
));
943 if (!wmw
->lpWaveFormat
)
946 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
950 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
951 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
954 if (lpParms
&& (dwFlags
& MCI_TO
)) {
955 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
958 TRACE("Recording from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
960 if (end
<= wmw
->dwPosition
)
965 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
966 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
968 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
969 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
971 /* go back to begining of chunk plus the requested position */
972 /* FIXME: I'm not sure this is correct, notably because some data linked to
973 * the decompression state machine will not be correcly initialized.
974 * try it this way (other way would be to decompress from 0 up to dwPosition
975 * and to start sending to hWave when dwPosition is reached)
977 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
979 /* By default the device will be opened for output, the MCI_CUE function is there to
980 * change from output to input and back
982 /* FIXME: how to choose between several output channels ? here mapper is forced */
983 dwRet
= waveInOpen(&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
984 (DWORD
)WAVE_mciRecordCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
987 TRACE("Can't open low level audio device %ld\n", dwRet
);
988 dwRet
= MCIERR_DEVICE_OPEN
;
993 /* make it so that 3 buffers per second are needed */
994 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
996 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
997 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
998 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
999 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
1000 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
1001 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1002 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1004 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1005 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1006 dwRet
= MCIERR_INTERNAL
;
1010 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1011 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1012 dwRet
= MCIERR_INTERNAL
;
1016 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
1017 wmw
->dwEventCount
= 1L; /* for first buffer */
1019 wmw
->dwRemaining
= end
- wmw
->dwPosition
;
1021 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, wmw
->dwRemaining
);
1023 dwRet
= waveInStart(wmw
->hWave
);
1025 while ( wmw
->dwRemaining
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1026 WAVE_mciRecordWaitDone(wmw
);
1029 waveInReset(wmw
->hWave
);
1030 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1031 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1036 HeapFree(GetProcessHeap(), 0, waveHdr
);
1039 waveInClose(wmw
->hWave
);
1042 CloseHandle(wmw
->hEvent
);
1044 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
1045 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
1046 wmw
->openParms
.wDeviceID
,
1047 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1050 wmw
->dwStatus
= MCI_MODE_STOP
;
1056 /**************************************************************************
1057 * WAVE_mciPause [internal]
1059 static DWORD
WAVE_mciPause(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1062 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1064 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1066 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1067 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1069 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
1070 wmw
->dwStatus
= MCI_MODE_PAUSE
;
1073 if (wmw
->fInput
) dwRet
= waveInStop(wmw
->hWave
);
1074 else dwRet
= waveOutPause(wmw
->hWave
);
1076 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1079 /**************************************************************************
1080 * WAVE_mciResume [internal]
1082 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1084 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1087 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1089 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1091 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
1092 wmw
->dwStatus
= MCI_MODE_PLAY
;
1095 if (wmw
->fInput
) dwRet
= waveInStart(wmw
->hWave
);
1096 else dwRet
= waveOutRestart(wmw
->hWave
);
1097 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1100 /**************************************************************************
1101 * WAVE_mciSeek [internal]
1103 static DWORD
WAVE_mciSeek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1106 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1108 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1110 if (lpParms
== NULL
) {
1111 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1112 } else if (wmw
== NULL
) {
1113 ret
= MCIERR_INVALID_DEVICE_ID
;
1115 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1117 if (dwFlags
& MCI_SEEK_TO_START
) {
1118 wmw
->dwPosition
= 0;
1119 } else if (dwFlags
& MCI_SEEK_TO_END
) {
1120 wmw
->dwPosition
= wmw
->ckWaveData
.cksize
;
1121 } else if (dwFlags
& MCI_TO
) {
1122 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1124 WARN("dwFlag doesn't tell where to seek to...\n");
1125 return MCIERR_MISSING_PARAMETER
;
1128 TRACE("Seeking to position=%lu bytes\n", wmw
->dwPosition
);
1130 if (dwFlags
& MCI_NOTIFY
) {
1131 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
1132 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1138 /**************************************************************************
1139 * WAVE_mciSet [internal]
1141 static DWORD
WAVE_mciSet(UINT wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1143 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1145 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1147 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1148 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1150 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1151 switch (lpParms
->dwTimeFormat
) {
1152 case MCI_FORMAT_MILLISECONDS
:
1153 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1154 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1156 case MCI_FORMAT_BYTES
:
1157 TRACE("MCI_FORMAT_BYTES !\n");
1158 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1160 case MCI_FORMAT_SAMPLES
:
1161 TRACE("MCI_FORMAT_SAMPLES !\n");
1162 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1165 WARN("Bad time format %lu!\n", lpParms
->dwTimeFormat
);
1166 return MCIERR_BAD_TIME_FORMAT
;
1169 if (dwFlags
& MCI_SET_VIDEO
) {
1170 TRACE("No support for video !\n");
1171 return MCIERR_UNSUPPORTED_FUNCTION
;
1173 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1174 TRACE("No support for door open !\n");
1175 return MCIERR_UNSUPPORTED_FUNCTION
;
1177 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1178 TRACE("No support for door close !\n");
1179 return MCIERR_UNSUPPORTED_FUNCTION
;
1181 if (dwFlags
& MCI_SET_AUDIO
) {
1182 if (dwFlags
& MCI_SET_ON
) {
1183 TRACE("MCI_SET_ON audio !\n");
1184 } else if (dwFlags
& MCI_SET_OFF
) {
1185 TRACE("MCI_SET_OFF audio !\n");
1187 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1188 return MCIERR_BAD_INTEGER
;
1191 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
1192 TRACE("MCI_SET_AUDIO_ALL !\n");
1193 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
1194 TRACE("MCI_SET_AUDIO_LEFT !\n");
1195 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
1196 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1198 if (dwFlags
& MCI_WAVE_INPUT
)
1199 TRACE("MCI_WAVE_INPUT !\n");
1200 if (dwFlags
& MCI_WAVE_OUTPUT
)
1201 TRACE("MCI_WAVE_OUTPUT !\n");
1202 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
1203 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1204 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
1205 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1206 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
)
1207 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
1208 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
)
1209 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
1210 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
)
1211 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
1212 if (dwFlags
& MCI_WAVE_SET_CHANNELS
)
1213 TRACE("MCI_WAVE_SET_CHANNELS !\n");
1214 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
)
1215 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
1216 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
)
1217 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
1221 /**************************************************************************
1222 * WAVE_mciSave [internal]
1224 static DWORD
WAVE_mciSave(UINT wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMS lpParms
)
1226 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1227 DWORD ret
= MCIERR_FILE_NOT_SAVED
;
1228 WPARAM wparam
= MCI_NOTIFY_FAILURE
;
1230 TRACE("%d, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1231 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1232 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1234 if (dwFlags
& MCI_WAIT
)
1236 FIXME("MCI_WAIT not implemented\n");
1239 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1240 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1243 ret
= mmioClose(wmw
->hFile
, 0);
1245 if (0 == mmioRenameA(wmw
->openParms
.lpstrElementName
, lpParms
->lpfilename
, 0, 0 )) {
1246 ret
= ERROR_SUCCESS
;
1249 if (dwFlags
& MCI_NOTIFY
) {
1250 if (ret
== ERROR_SUCCESS
) wparam
= MCI_NOTIFY_SUCCESSFUL
;
1252 mciDriverNotify( (HWND
) LOWORD(lpParms
->dwCallback
),
1253 wmw
->openParms
.wDeviceID
, wparam
);
1259 /**************************************************************************
1260 * WAVE_mciStatus [internal]
1262 static DWORD
WAVE_mciStatus(UINT wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1264 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1267 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1268 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1269 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1271 if (dwFlags
& MCI_STATUS_ITEM
) {
1272 switch (lpParms
->dwItem
) {
1273 case MCI_STATUS_CURRENT_TRACK
:
1274 lpParms
->dwReturn
= 1;
1275 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1277 case MCI_STATUS_LENGTH
:
1279 lpParms
->dwReturn
= 0;
1280 return MCIERR_UNSUPPORTED_FUNCTION
;
1282 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1283 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
, &ret
);
1284 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1286 case MCI_STATUS_MODE
:
1287 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1288 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1289 ret
= MCI_RESOURCE_RETURNED
;
1291 case MCI_STATUS_MEDIA_PRESENT
:
1292 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1293 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1294 ret
= MCI_RESOURCE_RETURNED
;
1296 case MCI_STATUS_NUMBER_OF_TRACKS
:
1297 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1298 lpParms
->dwReturn
= 1;
1299 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
1301 case MCI_STATUS_POSITION
:
1303 lpParms
->dwReturn
= 0;
1304 return MCIERR_UNSUPPORTED_FUNCTION
;
1306 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1307 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1308 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
,
1310 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1311 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1313 case MCI_STATUS_READY
:
1314 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1315 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1316 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1317 ret
= MCI_RESOURCE_RETURNED
;
1319 case MCI_STATUS_TIME_FORMAT
:
1320 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, wmw
->dwMciTimeFormat
);
1321 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1322 ret
= MCI_RESOURCE_RETURNED
;
1324 case MCI_WAVE_INPUT
:
1325 TRACE("MCI_WAVE_INPUT !\n");
1326 lpParms
->dwReturn
= 0;
1327 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1329 case MCI_WAVE_OUTPUT
:
1330 TRACE("MCI_WAVE_OUTPUT !\n");
1333 if (waveOutGetID(wmw
->hWave
, &id
) == MMSYSERR_NOERROR
) {
1334 lpParms
->dwReturn
= id
;
1336 lpParms
->dwReturn
= 0;
1337 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1341 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1343 lpParms
->dwReturn
= 0;
1344 return MCIERR_UNSUPPORTED_FUNCTION
;
1346 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1347 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
1349 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1351 lpParms
->dwReturn
= 0;
1352 return MCIERR_UNSUPPORTED_FUNCTION
;
1354 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1355 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
1357 case MCI_WAVE_STATUS_BLOCKALIGN
:
1359 lpParms
->dwReturn
= 0;
1360 return MCIERR_UNSUPPORTED_FUNCTION
;
1362 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1363 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
1365 case MCI_WAVE_STATUS_CHANNELS
:
1367 lpParms
->dwReturn
= 0;
1368 return MCIERR_UNSUPPORTED_FUNCTION
;
1370 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1371 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
1373 case MCI_WAVE_STATUS_FORMATTAG
:
1375 lpParms
->dwReturn
= 0;
1376 return MCIERR_UNSUPPORTED_FUNCTION
;
1378 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1379 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
1381 case MCI_WAVE_STATUS_LEVEL
:
1382 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1383 lpParms
->dwReturn
= 0xAAAA5555;
1385 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1387 lpParms
->dwReturn
= 0;
1388 return MCIERR_UNSUPPORTED_FUNCTION
;
1390 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1391 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
1394 WARN("unknown command %08lX !\n", lpParms
->dwItem
);
1395 return MCIERR_UNRECOGNIZED_COMMAND
;
1398 if (dwFlags
& MCI_NOTIFY
) {
1399 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
1400 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1405 /**************************************************************************
1406 * WAVE_mciGetDevCaps [internal]
1408 static DWORD
WAVE_mciGetDevCaps(UINT wDevID
, DWORD dwFlags
,
1409 LPMCI_GETDEVCAPS_PARMS lpParms
)
1411 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1414 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1416 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1417 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1419 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1420 switch(lpParms
->dwItem
) {
1421 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1422 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1423 ret
= MCI_RESOURCE_RETURNED
;
1425 case MCI_GETDEVCAPS_HAS_AUDIO
:
1426 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1427 ret
= MCI_RESOURCE_RETURNED
;
1429 case MCI_GETDEVCAPS_HAS_VIDEO
:
1430 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1431 ret
= MCI_RESOURCE_RETURNED
;
1433 case MCI_GETDEVCAPS_USES_FILES
:
1434 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1435 ret
= MCI_RESOURCE_RETURNED
;
1437 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1438 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1439 ret
= MCI_RESOURCE_RETURNED
;
1441 case MCI_GETDEVCAPS_CAN_RECORD
:
1442 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1443 ret
= MCI_RESOURCE_RETURNED
;
1445 case MCI_GETDEVCAPS_CAN_EJECT
:
1446 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1447 ret
= MCI_RESOURCE_RETURNED
;
1449 case MCI_GETDEVCAPS_CAN_PLAY
:
1450 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1451 ret
= MCI_RESOURCE_RETURNED
;
1453 case MCI_GETDEVCAPS_CAN_SAVE
:
1454 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1455 ret
= MCI_RESOURCE_RETURNED
;
1457 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1458 lpParms
->dwReturn
= 1;
1460 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1461 lpParms
->dwReturn
= 1;
1464 FIXME("Unknown capability (%08lx) !\n", lpParms
->dwItem
);
1465 return MCIERR_UNRECOGNIZED_COMMAND
;
1468 WARN("No GetDevCaps-Item !\n");
1469 return MCIERR_UNRECOGNIZED_COMMAND
;
1474 /**************************************************************************
1475 * WAVE_mciInfo [internal]
1477 static DWORD
WAVE_mciInfo(UINT wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSA lpParms
)
1481 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1483 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1485 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
1486 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1487 } else if (wmw
== NULL
) {
1488 ret
= MCIERR_INVALID_DEVICE_ID
;
1490 TRACE("buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1492 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1493 case MCI_INFO_PRODUCT
:
1494 str
= "Wine's audio player";
1497 str
= wmw
->openParms
.lpstrElementName
;
1499 case MCI_WAVE_INPUT
:
1500 str
= "Wine Wave In";
1502 case MCI_WAVE_OUTPUT
:
1503 str
= "Wine Wave Out";
1506 WARN("Don't know this info command (%lu)\n", dwFlags
);
1507 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
1511 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
1512 ret
= MCIERR_PARAM_OVERFLOW
;
1514 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
1517 lpParms
->lpstrReturn
[0] = 0;
1523 /**************************************************************************
1524 * MCIWAVE_DriverProc [sample driver]
1526 LONG CALLBACK
MCIWAVE_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
1527 DWORD dwParam1
, DWORD dwParam2
)
1529 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1530 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1533 case DRV_LOAD
: return 1;
1534 case DRV_FREE
: return 1;
1535 case DRV_OPEN
: return WAVE_drvOpen((LPSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSA
)dwParam2
);
1536 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1537 case DRV_ENABLE
: return 1;
1538 case DRV_DISABLE
: return 1;
1539 case DRV_QUERYCONFIGURE
: return 1;
1540 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK
); return 1;
1541 case DRV_INSTALL
: return DRVCNF_RESTART
;
1542 case DRV_REMOVE
: return DRVCNF_RESTART
;
1543 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSA
) dwParam2
);
1544 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1545 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1546 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
1547 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
1548 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1549 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
1550 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1551 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1552 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1553 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1554 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSA
) dwParam2
);
1555 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1556 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMS
) dwParam2
);
1557 /* commands that should be supported */
1572 FIXME("Unsupported yet command [%lu]\n", wMsg
);
1575 TRACE("Unsupported command [%lu]\n", wMsg
);
1577 /* option which can be silenced */
1582 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1585 FIXME("is probably wrong msg [%lu]\n", wMsg
);
1586 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1588 return MCIERR_UNRECOGNIZED_COMMAND
;