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 * =================================================================== */
58 /**************************************************************************
59 * MCI_SCAStarter [internal]
61 static DWORD CALLBACK
MCI_SCAStarter(LPVOID arg
)
63 struct SCA
* sca
= (struct SCA
*)arg
;
66 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
67 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
68 ret
= mciSendCommandA(sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
| MCI_WAIT
, sca
->dwParam2
);
69 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
70 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
71 HeapFree(GetProcessHeap(), 0, sca
);
73 WARN("Should not happen ? what's wrong \n");
74 /* should not go after this point */
78 /**************************************************************************
79 * MCI_SendCommandAsync [internal]
81 static DWORD
MCI_SendCommandAsync(UINT wDevID
, UINT wMsg
, DWORD dwParam1
,
82 DWORD dwParam2
, UINT size
)
84 struct SCA
* sca
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA
) + size
);
87 return MCIERR_OUT_OF_MEMORY
;
91 sca
->dwParam1
= dwParam1
;
93 if (size
&& dwParam2
) {
94 sca
->dwParam2
= (DWORD
)sca
+ sizeof(struct SCA
);
95 /* copy structure passed by program in dwParam2 to be sure
96 * we can still use it whatever the program does
98 memcpy((LPVOID
)sca
->dwParam2
, (LPVOID
)dwParam2
, size
);
100 sca
->dwParam2
= dwParam2
;
103 if (CreateThread(NULL
, 0, MCI_SCAStarter
, sca
, 0, NULL
) == 0) {
104 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
105 return MCI_SCAStarter(&sca
);
110 /*======================================================================*
111 * MCI WAVE implemantation *
112 *======================================================================*/
114 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
116 /**************************************************************************
117 * MCIWAVE_drvOpen [internal]
119 static DWORD
WAVE_drvOpen(LPSTR str
, LPMCI_OPEN_DRIVER_PARMSA modp
)
121 WINE_MCIWAVE
* wmw
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCIWAVE
));
126 wmw
->wDevID
= modp
->wDeviceID
;
127 mciSetDriverData(wmw
->wDevID
, (DWORD
)wmw
);
128 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
129 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
130 return modp
->wDeviceID
;
133 /**************************************************************************
134 * MCIWAVE_drvClose [internal]
136 static DWORD
WAVE_drvClose(DWORD dwDevID
)
138 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(dwDevID
);
141 HeapFree(GetProcessHeap(), 0, wmw
);
142 mciSetDriverData(dwDevID
, 0);
148 /**************************************************************************
149 * WAVE_mciGetOpenDev [internal]
151 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT wDevID
)
153 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
155 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
156 WARN("Invalid wDevID=%u\n", wDevID
);
162 /**************************************************************************
163 * WAVE_ConvertByteToTimeFormat [internal]
165 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
, LPDWORD lpRet
)
169 switch (wmw
->dwMciTimeFormat
) {
170 case MCI_FORMAT_MILLISECONDS
:
171 ret
= (val
* 1000) / wmw
->lpWaveFormat
->nAvgBytesPerSec
;
173 case MCI_FORMAT_BYTES
:
176 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
177 ret
= (val
* 8) / wmw
->lpWaveFormat
->wBitsPerSample
;
180 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
182 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
187 /**************************************************************************
188 * WAVE_ConvertTimeFormatToByte [internal]
190 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
194 switch (wmw
->dwMciTimeFormat
) {
195 case MCI_FORMAT_MILLISECONDS
:
196 ret
= (val
* wmw
->lpWaveFormat
->nAvgBytesPerSec
) / 1000;
198 case MCI_FORMAT_BYTES
:
201 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
202 ret
= (val
* wmw
->lpWaveFormat
->wBitsPerSample
) / 8;
205 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
207 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
211 /**************************************************************************
212 * WAVE_mciReadFmt [internal]
214 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, MMCKINFO
* pckMainRIFF
)
219 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
220 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
221 return MCIERR_INVALID_FILE
;
222 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
223 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
225 wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
226 r
= mmioRead(wmw
->hFile
, (HPSTR
)wmw
->lpWaveFormat
, mmckInfo
.cksize
);
227 if (r
< sizeof(WAVEFORMAT
))
228 return MCIERR_INVALID_FILE
;
230 TRACE("wFormatTag=%04X !\n", wmw
->lpWaveFormat
->wFormatTag
);
231 TRACE("nChannels=%d \n", wmw
->lpWaveFormat
->nChannels
);
232 TRACE("nSamplesPerSec=%ld\n", wmw
->lpWaveFormat
->nSamplesPerSec
);
233 TRACE("nAvgBytesPerSec=%ld\n", wmw
->lpWaveFormat
->nAvgBytesPerSec
);
234 TRACE("nBlockAlign=%d \n", wmw
->lpWaveFormat
->nBlockAlign
);
235 TRACE("wBitsPerSample=%u !\n", wmw
->lpWaveFormat
->wBitsPerSample
);
236 if (r
>= (long)sizeof(WAVEFORMATEX
))
237 TRACE("cbSize=%u !\n", wmw
->lpWaveFormat
->cbSize
);
239 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
240 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
241 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
242 TRACE("can't find data chunk\n");
243 return MCIERR_INVALID_FILE
;
245 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
246 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
247 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
248 wmw
->lpWaveFormat
->nChannels
, wmw
->lpWaveFormat
->nSamplesPerSec
);
253 /**************************************************************************
254 * WAVE_mciCreateRIFFSkeleton [internal]
256 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
258 MMCKINFO ckWaveFormat
;
260 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
261 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
262 LPWAVEFORMATEX lpWaveFormat
= wmw
->lpWaveFormat
;
265 HMMIO hmmio
= wmw
->hFile
;
267 lpckRIFF
->ckid
= FOURCC_RIFF
;
268 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
269 lpckRIFF
->cksize
= 0;
271 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, lpckRIFF
, MMIO_CREATERIFF
))
274 ckWaveFormat
.fccType
= 0;
275 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
276 ckWaveFormat
.cksize
= 16;
280 TRACE("allocating waveformat with default waveformat 11khz/8bit/mono \n");
282 lpWaveFormat
= wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*lpWaveFormat
));
284 lpWaveFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
285 lpWaveFormat
->nChannels
= 1; /* MONO */
286 lpWaveFormat
->nSamplesPerSec
= 11025;
287 lpWaveFormat
->nAvgBytesPerSec
= 11025;
288 lpWaveFormat
->nBlockAlign
= 1;
289 lpWaveFormat
->wBitsPerSample
= 8;
290 lpWaveFormat
->cbSize
= 0; /* don't care */
293 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, &ckWaveFormat
, 0))
296 /* only the first 16 bytes are serialized */
297 if (-1 == mmioWrite(hmmio
, (HPCSTR
) lpWaveFormat
, 16))
300 if (MMSYSERR_NOERROR
!= mmioAscend(hmmio
, &ckWaveFormat
, 0))
303 lpckWaveData
->cksize
= 0;
304 lpckWaveData
->fccType
= 0;
305 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
307 /* create data chunk */
308 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, lpckWaveData
, 0))
314 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
315 return MCIERR_INVALID_FILE
;
318 /**************************************************************************
319 * WAVE_mciOpen [internal]
321 static DWORD
WAVE_mciOpen(UINT wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSA lpOpenParms
)
324 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
325 CHAR
* pszTmpFileName
= 0;
327 TRACE("(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
328 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
329 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
331 if (dwFlags
& MCI_OPEN_SHAREABLE
)
332 return MCIERR_HARDWARE
;
334 if (wmw
->nUseCount
> 0) {
335 /* The driver is already opened on this channel
336 * Wave driver cannot be shared
338 return MCIERR_DEVICE_OPEN
;
345 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
347 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
349 if (dwFlags
& MCI_OPEN_ELEMENT
) {
350 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
351 /* could it be that (DWORD)lpOpenParms->lpstrElementName
352 * contains the hFile value ?
354 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
356 if (strlen(lpOpenParms
->lpstrElementName
) > 0) {
357 lpOpenParms
->lpstrElementName
= lpOpenParms
->lpstrElementName
;
359 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
360 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms
->lpstrElementName
);
362 if (lpOpenParms
->lpstrElementName
&& (strlen(lpOpenParms
->lpstrElementName
) > 0)) {
363 wmw
->hFile
= mmioOpenA((LPSTR
)lpOpenParms
->lpstrElementName
, NULL
,
364 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READWRITE
);
366 if (wmw
->hFile
== 0) {
367 WARN("can't find file='%s' !\n", lpOpenParms
->lpstrElementName
);
368 dwRet
= MCIERR_FILE_NOT_FOUND
;
372 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
374 /* make sure we're are the beginning of the file */
375 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
377 /* first reading of this file. read the waveformat chunk */
378 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
379 dwRet
= MCIERR_INVALID_FILE
;
381 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
382 (LPSTR
)&(lpckMainRIFF
->ckid
),
383 (LPSTR
) &(lpckMainRIFF
->fccType
),
384 (lpckMainRIFF
->cksize
));
386 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
387 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
388 dwRet
= MCIERR_INVALID_FILE
;
390 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
400 CHAR szTmpPath
[MAX_PATH
];
401 CHAR szPrefix
[4] = "TMP\0";
403 pszTmpFileName
= HeapAlloc(GetProcessHeap(),
405 MAX_PATH
* sizeof(*pszTmpFileName
));
407 if (!GetTempPathA(sizeof(szTmpPath
), szTmpPath
)) {
408 WARN("can't retrieve temp path!\n");
409 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
410 return MCIERR_FILE_NOT_FOUND
;
413 if (!GetTempFileNameA(szTmpPath
, szPrefix
, 0, pszTmpFileName
)) {
414 WARN("can't retrieve temp file name!\n");
415 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
416 return MCIERR_FILE_NOT_FOUND
;
419 wmw
->bTemporaryFile
= TRUE
;
421 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName
);
423 if (pszTmpFileName
&& (strlen(pszTmpFileName
) > 0)) {
425 wmw
->hFile
= mmioOpenA(pszTmpFileName
, NULL
,
426 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
428 if (wmw
->hFile
== 0) {
429 /* temporary file could not be created. clean filename. */
430 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
431 WARN("can't create file='%s' !\n", pszTmpFileName
);
432 dwRet
= MCIERR_FILE_NOT_FOUND
;
439 TRACE("hFile=%u\n", wmw
->hFile
);
441 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
443 if (wmw
->bTemporaryFile
== TRUE
)
445 /* Additional openParms is temporary file's name */
446 wmw
->openParms
.lpstrElementName
= pszTmpFileName
;
450 if (wmw
->lpWaveFormat
) {
451 switch (wmw
->lpWaveFormat
->wFormatTag
) {
452 case WAVE_FORMAT_PCM
:
453 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
454 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
455 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
456 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
457 wmw
->lpWaveFormat
->nSamplesPerSec
*
458 wmw
->lpWaveFormat
->nBlockAlign
);
459 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
460 wmw
->lpWaveFormat
->nSamplesPerSec
*
461 wmw
->lpWaveFormat
->nBlockAlign
;
468 wmw
->dwStatus
= MCI_MODE_STOP
;
472 mmioClose(wmw
->hFile
, 0);
478 /**************************************************************************
479 * WAVE_mciCue [internal]
481 static DWORD
WAVE_mciCue(UINT wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
486 This routine is far from complete. At the moment only a check is done on the
487 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
490 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
495 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
497 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID
, dwParam
, lpParms
);
499 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
501 /* always close elements ? */
502 if (wmw
->hFile
!= 0) {
503 mmioClose(wmw
->hFile
, 0);
507 dwRet
= MMSYSERR_NOERROR
; /* assume success */
509 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
510 dwRet
= waveOutClose(wmw
->hWave
);
511 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
513 } else if (wmw
->fInput
) {
514 dwRet
= waveInClose(wmw
->hWave
);
515 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
519 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
522 /**************************************************************************
523 * WAVE_mciStop [internal]
525 static DWORD
WAVE_mciStop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
528 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
530 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
532 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
534 /* wait for playback thread (if any) to exit before processing further */
535 switch (wmw
->dwStatus
) {
538 case MCI_MODE_RECORD
:
540 int oldStat
= wmw
->dwStatus
;
541 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
542 if (oldStat
== MCI_MODE_PAUSE
)
543 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
545 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
553 wmw
->dwStatus
= MCI_MODE_STOP
;
555 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
556 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
557 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
563 /**************************************************************************
564 * WAVE_mciClose [internal]
566 static DWORD
WAVE_mciClose(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
569 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
571 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
573 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
575 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
576 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
581 if (wmw
->nUseCount
== 0) {
582 if (wmw
->hFile
!= 0) {
583 mmioClose(wmw
->hFile
, 0);
588 /* That string got allocated in mciOpen because no filename was specified
589 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
590 * allocated by mciOpen, *NOT* the application.
592 if (wmw
->bTemporaryFile
)
594 HeapFree(GetProcessHeap(), 0, (CHAR
*) wmw
->openParms
.lpstrElementName
);
595 wmw
->openParms
.lpstrElementName
= NULL
;
598 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
599 wmw
->lpWaveFormat
= NULL
;
601 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
602 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
603 wmw
->openParms
.wDeviceID
,
604 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
610 /**************************************************************************
611 * WAVE_mciPlayCallback [internal]
613 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
615 DWORD dwParam1
, DWORD dwParam2
)
617 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
624 InterlockedIncrement(&wmw
->dwEventCount
);
625 TRACE("Returning waveHdr=%lx\n", dwParam1
);
626 SetEvent(wmw
->hEvent
);
629 ERR("Unknown uMsg=%d\n", uMsg
);
633 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
636 ResetEvent(wmw
->hEvent
);
637 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
640 InterlockedIncrement(&wmw
->dwEventCount
);
642 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
646 /**************************************************************************
647 * WAVE_mciPlay [internal]
649 static DWORD
WAVE_mciPlay(UINT wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
652 LONG bufsize
, count
, left
;
654 LPWAVEHDR waveHdr
= NULL
;
655 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
658 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
660 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
661 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
663 /* FIXME : since there is no way to determine in which mode the device is
664 * open (recording/playback) automatically switch from a mode to another
669 WARN("cannot play on input device\n");
670 return MCIERR_NONAPPLICABLE_FUNCTION
;
673 if (wmw
->hFile
== 0) {
674 WARN("Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
675 return MCIERR_FILE_NOT_FOUND
;
678 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
679 /* FIXME: parameters (start/end) in lpParams may not be used */
680 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
683 /** This function will be called again by a thread when async is used.
684 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
685 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
687 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
))) {
688 return MCIERR_INTERNAL
;
691 wmw
->dwStatus
= MCI_MODE_PLAY
;
693 if (!(dwFlags
& MCI_WAIT
)) {
694 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_PLAY
, dwFlags
,
695 (DWORD
)lpParms
, sizeof(MCI_PLAY_PARMS
));
699 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
700 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
702 if (lpParms
&& (dwFlags
& MCI_TO
)) {
703 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
706 TRACE("Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
708 if (end
<= wmw
->dwPosition
)
712 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
713 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
715 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
716 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
719 if (wmw
->lpWaveFormat
) {
720 switch (wmw
->lpWaveFormat
->wFormatTag
) {
721 case WAVE_FORMAT_PCM
:
722 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
723 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
724 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
725 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
726 wmw
->lpWaveFormat
->nSamplesPerSec
*
727 wmw
->lpWaveFormat
->nBlockAlign
);
728 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
729 wmw
->lpWaveFormat
->nSamplesPerSec
*
730 wmw
->lpWaveFormat
->nBlockAlign
;
736 TRACE("can't retrieve wave format %ld\n", dwRet
);
741 /* go back to begining of chunk plus the requested position */
742 /* FIXME: I'm not sure this is correct, notably because some data linked to
743 * the decompression state machine will not be correcly initialized.
744 * try it this way (other way would be to decompress from 0 up to dwPosition
745 * and to start sending to hWave when dwPosition is reached)
747 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
749 /* By default the device will be opened for output, the MCI_CUE function is there to
750 * change from output to input and back
752 /* FIXME: how to choose between several output channels ? here mapper is forced */
753 dwRet
= waveOutOpen(&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
754 (DWORD
)WAVE_mciPlayCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
757 TRACE("Can't open low level audio device %ld\n", dwRet
);
758 dwRet
= MCIERR_DEVICE_OPEN
;
763 /* make it so that 3 buffers per second are needed */
764 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
766 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
767 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
768 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
769 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
770 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
771 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
772 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
773 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
774 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
775 dwRet
= MCIERR_INTERNAL
;
780 left
= min(wmw
->ckWaveData
.cksize
, end
- wmw
->dwPosition
);
781 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
782 wmw
->dwEventCount
= 1L; /* for first buffer */
784 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, left
);
786 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
787 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
788 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
789 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
792 /* count is always <= bufsize, so this is correct regarding the
793 * waveOutPrepareHeader function
795 waveHdr
[whidx
].dwBufferLength
= count
;
796 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
797 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
798 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
,
799 waveHdr
[whidx
].dwBytesRecorded
);
800 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
802 wmw
->dwPosition
+= count
;
803 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
805 WAVE_mciPlayWaitDone(wmw
);
809 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
811 /* just to get rid of some race conditions between play, stop and pause */
812 waveOutReset(wmw
->hWave
);
814 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
815 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
820 HeapFree(GetProcessHeap(), 0, waveHdr
);
823 waveOutClose(wmw
->hWave
);
826 CloseHandle(wmw
->hEvent
);
828 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
829 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
830 wmw
->openParms
.wDeviceID
,
831 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
834 wmw
->dwStatus
= MCI_MODE_STOP
;
839 /**************************************************************************
840 * WAVE_mciPlayCallback [internal]
842 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
844 DWORD dwParam1
, DWORD dwParam2
)
846 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
847 LPWAVEHDR lpWaveHdr
= NULL
;
854 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
856 InterlockedIncrement(&wmw
->dwEventCount
);
858 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
860 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
861 wmw
->dwPosition
+= count
;
862 wmw
->dwRemaining
-= count
;
864 if (wmw
->dwStatus
== MCI_MODE_RECORD
)
866 /* Only queue up another buffer if we are recording. We could receive this
867 message also when waveInReset() is called, since it notifies on all wave
868 buffers that are outstanding. Queueing up more sometimes causes waveInClose
870 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
871 TRACE("after mmioWrite dwPosition=%lu\n", wmw
->dwPosition
);
874 SetEvent(wmw
->hEvent
);
877 ERR("Unknown uMsg=%d\n", uMsg
);
881 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
884 ResetEvent(wmw
->hEvent
);
885 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
888 InterlockedIncrement(&wmw
->dwEventCount
);
890 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
894 /**************************************************************************
895 * WAVE_mciRecord [internal]
897 static DWORD
WAVE_mciRecord(UINT wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
902 LPWAVEHDR waveHdr
= NULL
;
903 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
906 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
908 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
909 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
911 /* FIXME : since there is no way to determine in which mode the device is
912 * open (recording/playback) automatically switch from a mode to another
917 WARN("cannot record on output device\n");
918 return MCIERR_NONAPPLICABLE_FUNCTION
;
921 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
922 /* FIXME: parameters (start/end) in lpParams may not be used */
923 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
926 /** This function will be called again by a thread when async is used.
927 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
928 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
930 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
))) {
931 return MCIERR_INTERNAL
;
934 wmw
->dwStatus
= MCI_MODE_RECORD
;
936 if (!(dwFlags
& MCI_WAIT
)) {
937 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_RECORD
, dwFlags
,
938 (DWORD
)lpParms
, sizeof(MCI_RECORD_PARMS
));
941 if (!wmw
->lpWaveFormat
)
944 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
948 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
949 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
952 if (lpParms
&& (dwFlags
& MCI_TO
)) {
953 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
956 TRACE("Recording from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
958 if (end
<= wmw
->dwPosition
)
963 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
964 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
966 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
967 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
969 /* go back to begining of chunk plus the requested position */
970 /* FIXME: I'm not sure this is correct, notably because some data linked to
971 * the decompression state machine will not be correcly initialized.
972 * try it this way (other way would be to decompress from 0 up to dwPosition
973 * and to start sending to hWave when dwPosition is reached)
975 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
977 /* By default the device will be opened for output, the MCI_CUE function is there to
978 * change from output to input and back
980 /* FIXME: how to choose between several output channels ? here mapper is forced */
981 dwRet
= waveInOpen(&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
982 (DWORD
)WAVE_mciRecordCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
985 TRACE("Can't open low level audio device %ld\n", dwRet
);
986 dwRet
= MCIERR_DEVICE_OPEN
;
991 /* make it so that 3 buffers per second are needed */
992 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
994 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
995 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
996 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
997 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
998 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
999 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1000 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1002 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1003 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1004 dwRet
= MCIERR_INTERNAL
;
1008 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1009 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1010 dwRet
= MCIERR_INTERNAL
;
1014 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
1015 wmw
->dwEventCount
= 1L; /* for first buffer */
1017 wmw
->dwRemaining
= end
- wmw
->dwPosition
;
1019 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, wmw
->dwRemaining
);
1021 dwRet
= waveInStart(wmw
->hWave
);
1023 while ( wmw
->dwRemaining
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1024 WAVE_mciRecordWaitDone(wmw
);
1027 waveInReset(wmw
->hWave
);
1028 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1029 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1034 HeapFree(GetProcessHeap(), 0, waveHdr
);
1037 waveInClose(wmw
->hWave
);
1040 CloseHandle(wmw
->hEvent
);
1042 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
1043 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
1044 wmw
->openParms
.wDeviceID
,
1045 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1048 wmw
->dwStatus
= MCI_MODE_STOP
;
1054 /**************************************************************************
1055 * WAVE_mciPause [internal]
1057 static DWORD
WAVE_mciPause(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1060 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1062 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1064 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1065 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1067 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
1068 wmw
->dwStatus
= MCI_MODE_PAUSE
;
1071 if (wmw
->fInput
) dwRet
= waveInStop(wmw
->hWave
);
1072 else dwRet
= waveOutPause(wmw
->hWave
);
1074 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1077 /**************************************************************************
1078 * WAVE_mciResume [internal]
1080 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1082 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1085 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1087 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1089 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
1090 wmw
->dwStatus
= MCI_MODE_PLAY
;
1093 if (wmw
->fInput
) dwRet
= waveInStart(wmw
->hWave
);
1094 else dwRet
= waveOutRestart(wmw
->hWave
);
1095 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1098 /**************************************************************************
1099 * WAVE_mciSeek [internal]
1101 static DWORD
WAVE_mciSeek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1104 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1106 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1108 if (lpParms
== NULL
) {
1109 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1110 } else if (wmw
== NULL
) {
1111 ret
= MCIERR_INVALID_DEVICE_ID
;
1113 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1115 if (dwFlags
& MCI_SEEK_TO_START
) {
1116 wmw
->dwPosition
= 0;
1117 } else if (dwFlags
& MCI_SEEK_TO_END
) {
1118 wmw
->dwPosition
= wmw
->ckWaveData
.cksize
;
1119 } else if (dwFlags
& MCI_TO
) {
1120 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1122 WARN("dwFlag doesn't tell where to seek to...\n");
1123 return MCIERR_MISSING_PARAMETER
;
1126 TRACE("Seeking to position=%lu bytes\n", wmw
->dwPosition
);
1128 if (dwFlags
& MCI_NOTIFY
) {
1129 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
1130 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1136 /**************************************************************************
1137 * WAVE_mciSet [internal]
1139 static DWORD
WAVE_mciSet(UINT wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1141 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1143 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1145 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1146 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1148 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1149 switch (lpParms
->dwTimeFormat
) {
1150 case MCI_FORMAT_MILLISECONDS
:
1151 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1152 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1154 case MCI_FORMAT_BYTES
:
1155 TRACE("MCI_FORMAT_BYTES !\n");
1156 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1158 case MCI_FORMAT_SAMPLES
:
1159 TRACE("MCI_FORMAT_SAMPLES !\n");
1160 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1163 WARN("Bad time format %lu!\n", lpParms
->dwTimeFormat
);
1164 return MCIERR_BAD_TIME_FORMAT
;
1167 if (dwFlags
& MCI_SET_VIDEO
) {
1168 TRACE("No support for video !\n");
1169 return MCIERR_UNSUPPORTED_FUNCTION
;
1171 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1172 TRACE("No support for door open !\n");
1173 return MCIERR_UNSUPPORTED_FUNCTION
;
1175 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1176 TRACE("No support for door close !\n");
1177 return MCIERR_UNSUPPORTED_FUNCTION
;
1179 if (dwFlags
& MCI_SET_AUDIO
) {
1180 if (dwFlags
& MCI_SET_ON
) {
1181 TRACE("MCI_SET_ON audio !\n");
1182 } else if (dwFlags
& MCI_SET_OFF
) {
1183 TRACE("MCI_SET_OFF audio !\n");
1185 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1186 return MCIERR_BAD_INTEGER
;
1189 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
1190 TRACE("MCI_SET_AUDIO_ALL !\n");
1191 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
1192 TRACE("MCI_SET_AUDIO_LEFT !\n");
1193 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
1194 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1196 if (dwFlags
& MCI_WAVE_INPUT
)
1197 TRACE("MCI_WAVE_INPUT !\n");
1198 if (dwFlags
& MCI_WAVE_OUTPUT
)
1199 TRACE("MCI_WAVE_OUTPUT !\n");
1200 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
1201 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1202 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
1203 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1204 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
)
1205 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
1206 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
)
1207 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
1208 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
)
1209 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
1210 if (dwFlags
& MCI_WAVE_SET_CHANNELS
)
1211 TRACE("MCI_WAVE_SET_CHANNELS !\n");
1212 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
)
1213 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
1214 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
)
1215 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
1219 /**************************************************************************
1220 * WAVE_mciSave [internal]
1222 static DWORD
WAVE_mciSave(UINT wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMS lpParms
)
1224 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1225 DWORD ret
= MCIERR_FILE_NOT_SAVED
, tmpRet
;
1226 WPARAM wparam
= MCI_NOTIFY_FAILURE
;
1228 TRACE("%d, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1229 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1230 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1232 if (dwFlags
& MCI_WAIT
)
1234 FIXME("MCI_WAIT not implemented\n");
1237 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1238 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1241 ret
= mmioClose(wmw
->hFile
, 0);
1244 If the destination file already exists, it has to be overwritten. (Behaviour
1245 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1246 my applications. We are making use of mmioRename, which WILL NOT overwrite
1247 the destination file (which is what Windows does, also verified in Win2K)
1248 So, lets delete the destination file before calling mmioRename. If the
1249 destination file DOESN'T exist, the delete will fail silently. Let's also be
1250 careful not to lose our previous error code.
1252 tmpRet
= GetLastError();
1253 DeleteFileA (lpParms
->lpfilename
);
1254 SetLastError(tmpRet
);
1256 if (0 == mmioRenameA(wmw
->openParms
.lpstrElementName
, lpParms
->lpfilename
, 0, 0 )) {
1257 ret
= ERROR_SUCCESS
;
1260 if (dwFlags
& MCI_NOTIFY
) {
1261 if (ret
== ERROR_SUCCESS
) wparam
= MCI_NOTIFY_SUCCESSFUL
;
1263 mciDriverNotify( (HWND
) LOWORD(lpParms
->dwCallback
),
1264 wmw
->openParms
.wDeviceID
, wparam
);
1270 /**************************************************************************
1271 * WAVE_mciStatus [internal]
1273 static DWORD
WAVE_mciStatus(UINT wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1275 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1278 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1279 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1280 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1282 if (dwFlags
& MCI_STATUS_ITEM
) {
1283 switch (lpParms
->dwItem
) {
1284 case MCI_STATUS_CURRENT_TRACK
:
1285 lpParms
->dwReturn
= 1;
1286 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1288 case MCI_STATUS_LENGTH
:
1290 lpParms
->dwReturn
= 0;
1291 return MCIERR_UNSUPPORTED_FUNCTION
;
1293 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1294 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
, &ret
);
1295 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1297 case MCI_STATUS_MODE
:
1298 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1299 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1300 ret
= MCI_RESOURCE_RETURNED
;
1302 case MCI_STATUS_MEDIA_PRESENT
:
1303 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1304 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1305 ret
= MCI_RESOURCE_RETURNED
;
1307 case MCI_STATUS_NUMBER_OF_TRACKS
:
1308 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1309 lpParms
->dwReturn
= 1;
1310 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
1312 case MCI_STATUS_POSITION
:
1314 lpParms
->dwReturn
= 0;
1315 return MCIERR_UNSUPPORTED_FUNCTION
;
1317 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1318 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1319 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
,
1321 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1322 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1324 case MCI_STATUS_READY
:
1325 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1326 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1327 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1328 ret
= MCI_RESOURCE_RETURNED
;
1330 case MCI_STATUS_TIME_FORMAT
:
1331 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, wmw
->dwMciTimeFormat
);
1332 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1333 ret
= MCI_RESOURCE_RETURNED
;
1335 case MCI_WAVE_INPUT
:
1336 TRACE("MCI_WAVE_INPUT !\n");
1337 lpParms
->dwReturn
= 0;
1338 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1340 case MCI_WAVE_OUTPUT
:
1341 TRACE("MCI_WAVE_OUTPUT !\n");
1344 if (waveOutGetID(wmw
->hWave
, &id
) == MMSYSERR_NOERROR
) {
1345 lpParms
->dwReturn
= id
;
1347 lpParms
->dwReturn
= 0;
1348 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1352 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1354 lpParms
->dwReturn
= 0;
1355 return MCIERR_UNSUPPORTED_FUNCTION
;
1357 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1358 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
1360 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1362 lpParms
->dwReturn
= 0;
1363 return MCIERR_UNSUPPORTED_FUNCTION
;
1365 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1366 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
1368 case MCI_WAVE_STATUS_BLOCKALIGN
:
1370 lpParms
->dwReturn
= 0;
1371 return MCIERR_UNSUPPORTED_FUNCTION
;
1373 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1374 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
1376 case MCI_WAVE_STATUS_CHANNELS
:
1378 lpParms
->dwReturn
= 0;
1379 return MCIERR_UNSUPPORTED_FUNCTION
;
1381 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1382 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
1384 case MCI_WAVE_STATUS_FORMATTAG
:
1386 lpParms
->dwReturn
= 0;
1387 return MCIERR_UNSUPPORTED_FUNCTION
;
1389 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1390 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
1392 case MCI_WAVE_STATUS_LEVEL
:
1393 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1394 lpParms
->dwReturn
= 0xAAAA5555;
1396 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1398 lpParms
->dwReturn
= 0;
1399 return MCIERR_UNSUPPORTED_FUNCTION
;
1401 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1402 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
1405 WARN("unknown command %08lX !\n", lpParms
->dwItem
);
1406 return MCIERR_UNRECOGNIZED_COMMAND
;
1409 if (dwFlags
& MCI_NOTIFY
) {
1410 mciDriverNotify((HWND
)LOWORD(lpParms
->dwCallback
),
1411 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1416 /**************************************************************************
1417 * WAVE_mciGetDevCaps [internal]
1419 static DWORD
WAVE_mciGetDevCaps(UINT wDevID
, DWORD dwFlags
,
1420 LPMCI_GETDEVCAPS_PARMS lpParms
)
1422 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1425 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1427 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1428 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1430 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1431 switch(lpParms
->dwItem
) {
1432 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1433 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1434 ret
= MCI_RESOURCE_RETURNED
;
1436 case MCI_GETDEVCAPS_HAS_AUDIO
:
1437 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1438 ret
= MCI_RESOURCE_RETURNED
;
1440 case MCI_GETDEVCAPS_HAS_VIDEO
:
1441 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1442 ret
= MCI_RESOURCE_RETURNED
;
1444 case MCI_GETDEVCAPS_USES_FILES
:
1445 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1446 ret
= MCI_RESOURCE_RETURNED
;
1448 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1449 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1450 ret
= MCI_RESOURCE_RETURNED
;
1452 case MCI_GETDEVCAPS_CAN_RECORD
:
1453 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1454 ret
= MCI_RESOURCE_RETURNED
;
1456 case MCI_GETDEVCAPS_CAN_EJECT
:
1457 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1458 ret
= MCI_RESOURCE_RETURNED
;
1460 case MCI_GETDEVCAPS_CAN_PLAY
:
1461 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1462 ret
= MCI_RESOURCE_RETURNED
;
1464 case MCI_GETDEVCAPS_CAN_SAVE
:
1465 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1466 ret
= MCI_RESOURCE_RETURNED
;
1468 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1469 lpParms
->dwReturn
= 1;
1471 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1472 lpParms
->dwReturn
= 1;
1475 FIXME("Unknown capability (%08lx) !\n", lpParms
->dwItem
);
1476 return MCIERR_UNRECOGNIZED_COMMAND
;
1479 WARN("No GetDevCaps-Item !\n");
1480 return MCIERR_UNRECOGNIZED_COMMAND
;
1485 /**************************************************************************
1486 * WAVE_mciInfo [internal]
1488 static DWORD
WAVE_mciInfo(UINT wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSA lpParms
)
1492 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1494 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1496 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
1497 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1498 } else if (wmw
== NULL
) {
1499 ret
= MCIERR_INVALID_DEVICE_ID
;
1501 TRACE("buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1503 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1504 case MCI_INFO_PRODUCT
:
1505 str
= "Wine's audio player";
1508 str
= wmw
->openParms
.lpstrElementName
;
1510 case MCI_WAVE_INPUT
:
1511 str
= "Wine Wave In";
1513 case MCI_WAVE_OUTPUT
:
1514 str
= "Wine Wave Out";
1517 WARN("Don't know this info command (%lu)\n", dwFlags
);
1518 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
1522 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
1523 ret
= MCIERR_PARAM_OVERFLOW
;
1525 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
1528 lpParms
->lpstrReturn
[0] = 0;
1534 /**************************************************************************
1535 * MCIWAVE_DriverProc [sample driver]
1537 LONG CALLBACK
MCIWAVE_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
1538 DWORD dwParam1
, DWORD dwParam2
)
1540 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1541 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1544 case DRV_LOAD
: return 1;
1545 case DRV_FREE
: return 1;
1546 case DRV_OPEN
: return WAVE_drvOpen((LPSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSA
)dwParam2
);
1547 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1548 case DRV_ENABLE
: return 1;
1549 case DRV_DISABLE
: return 1;
1550 case DRV_QUERYCONFIGURE
: return 1;
1551 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK
); return 1;
1552 case DRV_INSTALL
: return DRVCNF_RESTART
;
1553 case DRV_REMOVE
: return DRVCNF_RESTART
;
1554 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSA
) dwParam2
);
1555 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1556 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1557 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
1558 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
1559 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1560 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
1561 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1562 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1563 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1564 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1565 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSA
) dwParam2
);
1566 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1567 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMS
) dwParam2
);
1568 /* commands that should be supported */
1583 FIXME("Unsupported yet command [%lu]\n", wMsg
);
1586 TRACE("Unsupported command [%lu]\n", wMsg
);
1588 /* option which can be silenced */
1593 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1596 FIXME("is probably wrong msg [%lu]\n", wMsg
);
1597 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1599 return MCIERR_UNRECOGNIZED_COMMAND
;