1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
6 * 1999,2000 Eric Pouech
7 * 2000 Francois Jacques
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(mciwave
);
35 #define HWND_32(h16) ((HWND)(ULONG_PTR)(h16))
40 int nUseCount
; /* Incremented for each shared open */
41 BOOL fShareable
; /* TRUE if first open was shareable */
42 HMMIO hFile
; /* mmio file handle open as Element */
43 MCI_WAVE_OPEN_PARMSA openParms
;
45 LPWAVEFORMATEX lpWaveFormat
;
46 BOOL fInput
; /* FALSE = Output, TRUE = Input */
47 volatile WORD dwStatus
; /* one from MCI_MODE_xxxx */
48 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
49 DWORD dwRemaining
; /* remaining bytes to play or record */
50 DWORD dwPosition
; /* position in bytes in chunk */
51 HANDLE hEvent
; /* for synchronization */
52 DWORD dwEventCount
; /* for synchronization */
53 BOOL bTemporaryFile
; /* temporary file (MCI_RECORD) */
54 MMCKINFO ckMainRIFF
; /* main RIFF chunk */
55 MMCKINFO ckWaveData
; /* data chunk */
58 /* ===================================================================
59 * ===================================================================
60 * FIXME: should be using the new mmThreadXXXX functions from WINMM
62 * it would require to add a wine internal flag to mmThreadCreate
63 * in order to pass a 32 bit function instead of a 16 bit one
64 * ===================================================================
65 * =================================================================== */
74 /**************************************************************************
75 * MCI_SCAStarter [internal]
77 static DWORD CALLBACK
MCI_SCAStarter(LPVOID arg
)
79 struct SCA
* sca
= (struct SCA
*)arg
;
82 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
83 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
84 ret
= mciSendCommandA(sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
| MCI_WAIT
, sca
->dwParam2
);
85 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
86 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
87 HeapFree(GetProcessHeap(), 0, sca
);
89 WARN("Should not happen ? what's wrong \n");
90 /* should not go after this point */
94 /**************************************************************************
95 * MCI_SendCommandAsync [internal]
97 static DWORD
MCI_SendCommandAsync(UINT wDevID
, UINT wMsg
, DWORD dwParam1
,
98 DWORD dwParam2
, UINT size
)
100 struct SCA
* sca
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA
) + size
);
103 return MCIERR_OUT_OF_MEMORY
;
105 sca
->wDevID
= wDevID
;
107 sca
->dwParam1
= dwParam1
;
109 if (size
&& dwParam2
) {
110 sca
->dwParam2
= (DWORD
)sca
+ sizeof(struct SCA
);
111 /* copy structure passed by program in dwParam2 to be sure
112 * we can still use it whatever the program does
114 memcpy((LPVOID
)sca
->dwParam2
, (LPVOID
)dwParam2
, size
);
116 sca
->dwParam2
= dwParam2
;
119 if (CreateThread(NULL
, 0, MCI_SCAStarter
, sca
, 0, NULL
) == 0) {
120 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
121 return MCI_SCAStarter(&sca
);
126 /*======================================================================*
127 * MCI WAVE implemantation *
128 *======================================================================*/
130 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
132 /**************************************************************************
133 * MCIWAVE_drvOpen [internal]
135 static DWORD
WAVE_drvOpen(LPSTR str
, LPMCI_OPEN_DRIVER_PARMSA modp
)
139 if (modp
== NULL
) return 0xFFFFFFFF;
141 wmw
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCIWAVE
));
146 wmw
->wDevID
= modp
->wDeviceID
;
147 mciSetDriverData(wmw
->wDevID
, (DWORD
)wmw
);
148 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
149 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
151 wmw
->wfxRef
.wFormatTag
= WAVE_FORMAT_PCM
;
152 wmw
->wfxRef
.nChannels
= 1; /* MONO */
153 wmw
->wfxRef
.nSamplesPerSec
= 11025;
154 wmw
->wfxRef
.nAvgBytesPerSec
= 11025;
155 wmw
->wfxRef
.nBlockAlign
= 1;
156 wmw
->wfxRef
.wBitsPerSample
= 8;
157 wmw
->wfxRef
.cbSize
= 0; /* don't care */
159 return modp
->wDeviceID
;
162 /**************************************************************************
163 * MCIWAVE_drvClose [internal]
165 static DWORD
WAVE_drvClose(DWORD dwDevID
)
167 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(dwDevID
);
170 HeapFree(GetProcessHeap(), 0, wmw
);
171 mciSetDriverData(dwDevID
, 0);
174 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
177 /**************************************************************************
178 * WAVE_mciGetOpenDev [internal]
180 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT wDevID
)
182 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
184 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
185 WARN("Invalid wDevID=%u\n", wDevID
);
191 /**************************************************************************
192 * WAVE_ConvertByteToTimeFormat [internal]
194 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
, LPDWORD lpRet
)
198 switch (wmw
->dwMciTimeFormat
) {
199 case MCI_FORMAT_MILLISECONDS
:
200 ret
= MulDiv(val
,1000,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
202 case MCI_FORMAT_BYTES
:
205 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
206 ret
= (val
* 8) / wmw
->lpWaveFormat
->wBitsPerSample
;
209 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
211 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
216 /**************************************************************************
217 * WAVE_ConvertTimeFormatToByte [internal]
219 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
223 switch (wmw
->dwMciTimeFormat
) {
224 case MCI_FORMAT_MILLISECONDS
:
225 ret
= (val
* wmw
->lpWaveFormat
->nAvgBytesPerSec
) / 1000;
227 case MCI_FORMAT_BYTES
:
230 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
231 ret
= (val
* wmw
->lpWaveFormat
->wBitsPerSample
) / 8;
234 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
236 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
240 /**************************************************************************
241 * WAVE_mciReadFmt [internal]
243 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, MMCKINFO
* pckMainRIFF
)
248 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
249 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
250 return MCIERR_INVALID_FILE
;
251 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
252 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
254 wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
255 r
= mmioRead(wmw
->hFile
, (HPSTR
)wmw
->lpWaveFormat
, mmckInfo
.cksize
);
256 if (r
< sizeof(WAVEFORMAT
))
257 return MCIERR_INVALID_FILE
;
259 TRACE("wFormatTag=%04X !\n", wmw
->lpWaveFormat
->wFormatTag
);
260 TRACE("nChannels=%d \n", wmw
->lpWaveFormat
->nChannels
);
261 TRACE("nSamplesPerSec=%ld\n", wmw
->lpWaveFormat
->nSamplesPerSec
);
262 TRACE("nAvgBytesPerSec=%ld\n", wmw
->lpWaveFormat
->nAvgBytesPerSec
);
263 TRACE("nBlockAlign=%d \n", wmw
->lpWaveFormat
->nBlockAlign
);
264 TRACE("wBitsPerSample=%u !\n", wmw
->lpWaveFormat
->wBitsPerSample
);
265 if (r
>= (long)sizeof(WAVEFORMATEX
))
266 TRACE("cbSize=%u !\n", wmw
->lpWaveFormat
->cbSize
);
268 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
269 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
270 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
271 TRACE("can't find data chunk\n");
272 return MCIERR_INVALID_FILE
;
274 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
275 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
276 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
277 wmw
->lpWaveFormat
->nChannels
, wmw
->lpWaveFormat
->nSamplesPerSec
);
282 /**************************************************************************
283 * WAVE_mciCreateRIFFSkeleton [internal]
285 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
287 MMCKINFO ckWaveFormat
;
289 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
290 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
291 LPWAVEFORMATEX lpWaveFormat
= wmw
->lpWaveFormat
;
294 HMMIO hmmio
= wmw
->hFile
;
296 lpckRIFF
->ckid
= FOURCC_RIFF
;
297 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
298 lpckRIFF
->cksize
= 0;
300 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, lpckRIFF
, MMIO_CREATERIFF
))
303 ckWaveFormat
.fccType
= 0;
304 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
305 ckWaveFormat
.cksize
= 16;
309 /* FIXME: for non PCM formats, the size of the waveFormat has to be
312 lpWaveFormat
= wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*lpWaveFormat
));
314 memcpy(lpWaveFormat
, &wmw
->wfxRef
, sizeof(wmw
->wfxRef
));
317 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, &ckWaveFormat
, 0))
320 /* only the first 16 bytes are serialized */
321 /* wrong... for non PCM, the whole waveFormat is stored
323 if (-1 == mmioWrite(hmmio
, (HPCSTR
) lpWaveFormat
, 16))
326 if (MMSYSERR_NOERROR
!= mmioAscend(hmmio
, &ckWaveFormat
, 0))
329 lpckWaveData
->cksize
= 0;
330 lpckWaveData
->fccType
= 0;
331 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
333 /* create data chunk */
334 if (MMSYSERR_NOERROR
!= mmioCreateChunk(hmmio
, lpckWaveData
, 0))
340 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
341 return MCIERR_INVALID_FILE
;
344 /**************************************************************************
345 * WAVE_mciOpen [internal]
347 static DWORD
WAVE_mciOpen(UINT wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSA lpOpenParms
)
350 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
351 CHAR
* pszTmpFileName
= 0;
353 TRACE("(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
354 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
355 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
357 if (dwFlags
& MCI_OPEN_SHAREABLE
)
358 return MCIERR_HARDWARE
;
360 if (wmw
->nUseCount
> 0) {
361 /* The driver is already opened on this channel
362 * Wave driver cannot be shared
364 return MCIERR_DEVICE_OPEN
;
371 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
373 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
375 if (dwFlags
& MCI_OPEN_ELEMENT
) {
376 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
377 /* could it be that (DWORD)lpOpenParms->lpstrElementName
378 * contains the hFile value ?
380 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
382 if (strlen(lpOpenParms
->lpstrElementName
) > 0) {
383 lpOpenParms
->lpstrElementName
= lpOpenParms
->lpstrElementName
;
385 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
386 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms
->lpstrElementName
);
388 if (lpOpenParms
->lpstrElementName
&& (strlen(lpOpenParms
->lpstrElementName
) > 0)) {
389 wmw
->hFile
= mmioOpenA((LPSTR
)lpOpenParms
->lpstrElementName
, NULL
,
390 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READWRITE
);
392 if (wmw
->hFile
== 0) {
393 WARN("can't find file='%s' !\n", lpOpenParms
->lpstrElementName
);
394 dwRet
= MCIERR_FILE_NOT_FOUND
;
398 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
400 /* make sure we're are the beginning of the file */
401 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
403 /* first reading of this file. read the waveformat chunk */
404 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
405 dwRet
= MCIERR_INVALID_FILE
;
407 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
408 (LPSTR
)&(lpckMainRIFF
->ckid
),
409 (LPSTR
) &(lpckMainRIFF
->fccType
),
410 (lpckMainRIFF
->cksize
));
412 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
413 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
414 dwRet
= MCIERR_INVALID_FILE
;
416 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
426 CHAR szTmpPath
[MAX_PATH
];
427 CHAR szPrefix
[4] = "TMP\0";
429 pszTmpFileName
= HeapAlloc(GetProcessHeap(),
431 MAX_PATH
* sizeof(*pszTmpFileName
));
433 if (!GetTempPathA(sizeof(szTmpPath
), szTmpPath
)) {
434 WARN("can't retrieve temp path!\n");
435 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
436 return MCIERR_FILE_NOT_FOUND
;
439 if (!GetTempFileNameA(szTmpPath
, szPrefix
, 0, pszTmpFileName
)) {
440 WARN("can't retrieve temp file name!\n");
441 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
442 return MCIERR_FILE_NOT_FOUND
;
445 wmw
->bTemporaryFile
= TRUE
;
447 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName
);
449 if (pszTmpFileName
&& (strlen(pszTmpFileName
) > 0)) {
451 wmw
->hFile
= mmioOpenA(pszTmpFileName
, NULL
,
452 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
454 if (wmw
->hFile
== 0) {
455 /* temporary file could not be created. clean filename. */
456 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
457 WARN("can't create file='%s' !\n", pszTmpFileName
);
458 dwRet
= MCIERR_FILE_NOT_FOUND
;
465 TRACE("hFile=%u\n", wmw
->hFile
);
467 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
469 if (wmw
->bTemporaryFile
== TRUE
)
471 /* Additional openParms is temporary file's name */
472 wmw
->openParms
.lpstrElementName
= pszTmpFileName
;
476 if (wmw
->lpWaveFormat
) {
477 switch (wmw
->lpWaveFormat
->wFormatTag
) {
478 case WAVE_FORMAT_PCM
:
479 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
480 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
481 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
482 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
483 wmw
->lpWaveFormat
->nSamplesPerSec
*
484 wmw
->lpWaveFormat
->nBlockAlign
);
485 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
486 wmw
->lpWaveFormat
->nSamplesPerSec
*
487 wmw
->lpWaveFormat
->nBlockAlign
;
494 wmw
->dwStatus
= MCI_MODE_STOP
;
498 mmioClose(wmw
->hFile
, 0);
504 /**************************************************************************
505 * WAVE_mciCue [internal]
507 static DWORD
WAVE_mciCue(UINT wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
512 This routine is far from complete. At the moment only a check is done on the
513 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
516 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
521 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
523 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID
, dwParam
, lpParms
);
525 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
527 /* always close elements ? */
528 if (wmw
->hFile
!= 0) {
529 mmioClose(wmw
->hFile
, 0);
533 dwRet
= MMSYSERR_NOERROR
; /* assume success */
535 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
536 dwRet
= waveOutClose(wmw
->hWave
);
537 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
539 } else if (wmw
->fInput
) {
540 dwRet
= waveInClose(wmw
->hWave
);
541 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
545 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
548 /**************************************************************************
549 * WAVE_mciStop [internal]
551 static DWORD
WAVE_mciStop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
554 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
556 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
558 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
560 /* wait for playback thread (if any) to exit before processing further */
561 switch (wmw
->dwStatus
) {
564 case MCI_MODE_RECORD
:
566 int oldStat
= wmw
->dwStatus
;
567 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
568 if (oldStat
== MCI_MODE_PAUSE
)
569 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
571 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
579 wmw
->dwStatus
= MCI_MODE_STOP
;
581 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
582 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
583 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
589 /**************************************************************************
590 * WAVE_mciClose [internal]
592 static DWORD
WAVE_mciClose(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
595 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
597 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
599 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
601 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
602 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
607 if (wmw
->nUseCount
== 0) {
608 if (wmw
->hFile
!= 0) {
609 mmioClose(wmw
->hFile
, 0);
614 /* That string got allocated in mciOpen because no filename was specified
615 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
616 * allocated by mciOpen, *NOT* the application.
618 if (wmw
->bTemporaryFile
)
620 HeapFree(GetProcessHeap(), 0, (CHAR
*) wmw
->openParms
.lpstrElementName
);
621 wmw
->openParms
.lpstrElementName
= NULL
;
624 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
625 wmw
->lpWaveFormat
= NULL
;
627 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
628 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
629 wmw
->openParms
.wDeviceID
,
630 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
636 /**************************************************************************
637 * WAVE_mciPlayCallback [internal]
639 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
641 DWORD dwParam1
, DWORD dwParam2
)
643 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
650 InterlockedIncrement(&wmw
->dwEventCount
);
651 TRACE("Returning waveHdr=%lx\n", dwParam1
);
652 SetEvent(wmw
->hEvent
);
655 ERR("Unknown uMsg=%d\n", uMsg
);
659 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
662 ResetEvent(wmw
->hEvent
);
663 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
666 InterlockedIncrement(&wmw
->dwEventCount
);
668 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
672 /**************************************************************************
673 * WAVE_mciPlay [internal]
675 static DWORD
WAVE_mciPlay(UINT wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
678 LONG bufsize
, count
, left
;
680 LPWAVEHDR waveHdr
= NULL
;
681 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
684 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
686 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
687 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
689 /* FIXME : since there is no way to determine in which mode the device is
690 * open (recording/playback) automatically switch from a mode to another
695 WARN("cannot play on input device\n");
696 return MCIERR_NONAPPLICABLE_FUNCTION
;
699 if (wmw
->hFile
== 0) {
700 WARN("Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
701 return MCIERR_FILE_NOT_FOUND
;
704 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
705 /* FIXME: parameters (start/end) in lpParams may not be used */
706 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
709 /** This function will be called again by a thread when async is used.
710 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
711 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
713 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
))) {
714 return MCIERR_INTERNAL
;
717 wmw
->dwStatus
= MCI_MODE_PLAY
;
719 if (!(dwFlags
& MCI_WAIT
)) {
720 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_PLAY
, dwFlags
,
721 (DWORD
)lpParms
, sizeof(MCI_PLAY_PARMS
));
725 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
726 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
728 if (lpParms
&& (dwFlags
& MCI_TO
)) {
729 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
732 TRACE("Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
734 if (end
<= wmw
->dwPosition
)
738 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
739 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
741 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
742 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
745 if (wmw
->lpWaveFormat
) {
746 switch (wmw
->lpWaveFormat
->wFormatTag
) {
747 case WAVE_FORMAT_PCM
:
748 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
749 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
750 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
751 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
752 wmw
->lpWaveFormat
->nSamplesPerSec
*
753 wmw
->lpWaveFormat
->nBlockAlign
);
754 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
755 wmw
->lpWaveFormat
->nSamplesPerSec
*
756 wmw
->lpWaveFormat
->nBlockAlign
;
762 TRACE("can't retrieve wave format %ld\n", dwRet
);
767 /* go back to beginning of chunk plus the requested position */
768 /* FIXME: I'm not sure this is correct, notably because some data linked to
769 * the decompression state machine will not be correcly initialized.
770 * try it this way (other way would be to decompress from 0 up to dwPosition
771 * and to start sending to hWave when dwPosition is reached)
773 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
775 /* By default the device will be opened for output, the MCI_CUE function is there to
776 * change from output to input and back
778 /* FIXME: how to choose between several output channels ? here mapper is forced */
779 dwRet
= waveOutOpen(&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
780 (DWORD
)WAVE_mciPlayCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
783 TRACE("Can't open low level audio device %ld\n", dwRet
);
784 dwRet
= MCIERR_DEVICE_OPEN
;
789 /* make it so that 3 buffers per second are needed */
790 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
792 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
793 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
794 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
795 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
796 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
797 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
798 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
799 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
800 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
801 dwRet
= MCIERR_INTERNAL
;
806 left
= min(wmw
->ckWaveData
.cksize
, end
- wmw
->dwPosition
);
807 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
808 wmw
->dwEventCount
= 1L; /* for first buffer */
810 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, left
);
812 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
813 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
814 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
815 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
818 /* count is always <= bufsize, so this is correct regarding the
819 * waveOutPrepareHeader function
821 waveHdr
[whidx
].dwBufferLength
= count
;
822 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
823 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
824 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
,
825 waveHdr
[whidx
].dwBytesRecorded
);
826 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
828 wmw
->dwPosition
+= count
;
829 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
831 WAVE_mciPlayWaitDone(wmw
);
835 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
837 /* just to get rid of some race conditions between play, stop and pause */
838 waveOutReset(wmw
->hWave
);
840 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
841 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
846 HeapFree(GetProcessHeap(), 0, waveHdr
);
849 waveOutClose(wmw
->hWave
);
852 CloseHandle(wmw
->hEvent
);
854 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
855 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
856 wmw
->openParms
.wDeviceID
,
857 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
860 wmw
->dwStatus
= MCI_MODE_STOP
;
865 /**************************************************************************
866 * WAVE_mciPlayCallback [internal]
868 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
870 DWORD dwParam1
, DWORD dwParam2
)
872 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
873 LPWAVEHDR lpWaveHdr
= NULL
;
880 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
882 InterlockedIncrement(&wmw
->dwEventCount
);
884 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
886 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
887 wmw
->dwPosition
+= count
;
888 wmw
->dwRemaining
-= count
;
890 if (wmw
->dwStatus
== MCI_MODE_RECORD
)
892 /* Only queue up another buffer if we are recording. We could receive this
893 message also when waveInReset() is called, since it notifies on all wave
894 buffers that are outstanding. Queueing up more sometimes causes waveInClose
896 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
897 TRACE("after mmioWrite dwPosition=%lu\n", wmw
->dwPosition
);
900 SetEvent(wmw
->hEvent
);
903 ERR("Unknown uMsg=%d\n", uMsg
);
907 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
910 ResetEvent(wmw
->hEvent
);
911 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
914 InterlockedIncrement(&wmw
->dwEventCount
);
916 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
920 /**************************************************************************
921 * WAVE_mciRecord [internal]
923 static DWORD
WAVE_mciRecord(UINT wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
928 LPWAVEHDR waveHdr
= NULL
;
929 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
932 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
934 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
935 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
937 /* FIXME : since there is no way to determine in which mode the device is
938 * open (recording/playback) automatically switch from a mode to another
943 WARN("cannot record on output device\n");
944 return MCIERR_NONAPPLICABLE_FUNCTION
;
947 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
948 /* FIXME: parameters (start/end) in lpParams may not be used */
949 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
952 /** This function will be called again by a thread when async is used.
953 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
954 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
956 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
))) {
957 return MCIERR_INTERNAL
;
960 wmw
->dwStatus
= MCI_MODE_RECORD
;
962 if (!(dwFlags
& MCI_WAIT
)) {
963 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_RECORD
, dwFlags
,
964 (DWORD
)lpParms
, sizeof(MCI_RECORD_PARMS
));
967 if (!wmw
->lpWaveFormat
)
970 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
973 FIXME("Should descend into data chunk. Please report.\n");
977 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
978 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
981 if (lpParms
&& (dwFlags
& MCI_TO
)) {
982 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
985 TRACE("Recording from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
987 if (end
<= wmw
->dwPosition
)
992 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
993 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
995 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
996 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
998 /* go back to beginning of chunk plus the requested position */
999 /* FIXME: I'm not sure this is correct, notably because some data linked to
1000 * the decompression state machine will not be correcly initialized.
1001 * try it this way (other way would be to decompress from 0 up to dwPosition
1002 * and to start sending to hWave when dwPosition is reached)
1004 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
1006 /* By default the device will be opened for output, the MCI_CUE function is there to
1007 * change from output to input and back
1009 /* FIXME: how to choose between several output channels ? here mapper is forced */
1010 dwRet
= waveInOpen(&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
1011 (DWORD
)WAVE_mciRecordCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
1014 TRACE("Can't open low level audio device %ld\n", dwRet
);
1015 dwRet
= MCIERR_DEVICE_OPEN
;
1020 /* make it so that 3 buffers per second are needed */
1021 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
1023 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
1024 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
1025 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
1026 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
1027 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
1028 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1029 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1031 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1032 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1033 dwRet
= MCIERR_INTERNAL
;
1037 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1038 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1039 dwRet
= MCIERR_INTERNAL
;
1043 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
1044 wmw
->dwEventCount
= 1L; /* for first buffer */
1046 wmw
->dwRemaining
= end
- wmw
->dwPosition
;
1048 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, wmw
->dwRemaining
);
1050 dwRet
= waveInStart(wmw
->hWave
);
1052 while ( wmw
->dwRemaining
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1053 WAVE_mciRecordWaitDone(wmw
);
1056 /* needed so that the callback above won't add again the buffers returned by the reset */
1057 wmw
->dwStatus
= MCI_MODE_STOP
;
1059 waveInReset(wmw
->hWave
);
1061 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1062 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1067 HeapFree(GetProcessHeap(), 0, waveHdr
);
1070 waveInClose(wmw
->hWave
);
1073 CloseHandle(wmw
->hEvent
);
1075 /* need to update the size of the data chunk */
1076 if (mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0) != MMSYSERR_NOERROR
) {
1077 TRACE("failed on ascend\n");
1080 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
1081 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1082 wmw
->openParms
.wDeviceID
,
1083 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1086 wmw
->dwStatus
= MCI_MODE_STOP
;
1092 /**************************************************************************
1093 * WAVE_mciPause [internal]
1095 static DWORD
WAVE_mciPause(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1098 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1100 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1102 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1103 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1105 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
1106 wmw
->dwStatus
= MCI_MODE_PAUSE
;
1109 if (wmw
->fInput
) dwRet
= waveInStop(wmw
->hWave
);
1110 else dwRet
= waveOutPause(wmw
->hWave
);
1112 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1115 /**************************************************************************
1116 * WAVE_mciResume [internal]
1118 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1120 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1123 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1125 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1127 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
1128 wmw
->dwStatus
= MCI_MODE_PLAY
;
1131 if (wmw
->fInput
) dwRet
= waveInStart(wmw
->hWave
);
1132 else dwRet
= waveOutRestart(wmw
->hWave
);
1133 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1136 /**************************************************************************
1137 * WAVE_mciSeek [internal]
1139 static DWORD
WAVE_mciSeek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1142 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1144 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1146 if (lpParms
== NULL
) {
1147 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1148 } else if (wmw
== NULL
) {
1149 ret
= MCIERR_INVALID_DEVICE_ID
;
1151 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1153 if (dwFlags
& MCI_SEEK_TO_START
) {
1154 wmw
->dwPosition
= 0;
1155 } else if (dwFlags
& MCI_SEEK_TO_END
) {
1156 wmw
->dwPosition
= wmw
->ckWaveData
.cksize
;
1157 } else if (dwFlags
& MCI_TO
) {
1158 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1160 WARN("dwFlag doesn't tell where to seek to...\n");
1161 return MCIERR_MISSING_PARAMETER
;
1164 TRACE("Seeking to position=%lu bytes\n", wmw
->dwPosition
);
1166 if (dwFlags
& MCI_NOTIFY
) {
1167 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1168 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1174 /**************************************************************************
1175 * WAVE_mciSet [internal]
1177 static DWORD
WAVE_mciSet(UINT wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1179 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1181 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1183 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1184 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1186 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1187 switch (lpParms
->dwTimeFormat
) {
1188 case MCI_FORMAT_MILLISECONDS
:
1189 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1190 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1192 case MCI_FORMAT_BYTES
:
1193 TRACE("MCI_FORMAT_BYTES !\n");
1194 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1196 case MCI_FORMAT_SAMPLES
:
1197 TRACE("MCI_FORMAT_SAMPLES !\n");
1198 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1201 WARN("Bad time format %lu!\n", lpParms
->dwTimeFormat
);
1202 return MCIERR_BAD_TIME_FORMAT
;
1205 if (dwFlags
& MCI_SET_VIDEO
) {
1206 TRACE("No support for video !\n");
1207 return MCIERR_UNSUPPORTED_FUNCTION
;
1209 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1210 TRACE("No support for door open !\n");
1211 return MCIERR_UNSUPPORTED_FUNCTION
;
1213 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1214 TRACE("No support for door close !\n");
1215 return MCIERR_UNSUPPORTED_FUNCTION
;
1217 if (dwFlags
& MCI_SET_AUDIO
) {
1218 if (dwFlags
& MCI_SET_ON
) {
1219 TRACE("MCI_SET_ON audio !\n");
1220 } else if (dwFlags
& MCI_SET_OFF
) {
1221 TRACE("MCI_SET_OFF audio !\n");
1223 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1224 return MCIERR_BAD_INTEGER
;
1227 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
1228 TRACE("MCI_SET_AUDIO_ALL !\n");
1229 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
1230 TRACE("MCI_SET_AUDIO_LEFT !\n");
1231 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
1232 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1234 if (dwFlags
& MCI_WAVE_INPUT
)
1235 TRACE("MCI_WAVE_INPUT !\n");
1236 if (dwFlags
& MCI_WAVE_OUTPUT
)
1237 TRACE("MCI_WAVE_OUTPUT !\n");
1238 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
1239 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1240 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
1241 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1242 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
) {
1243 wmw
->wfxRef
.nAvgBytesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nAvgBytesPerSec
;
1244 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw
->wfxRef
.nAvgBytesPerSec
);
1246 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
) {
1247 wmw
->wfxRef
.wBitsPerSample
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wBitsPerSample
;
1248 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw
->wfxRef
.wBitsPerSample
);
1250 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
) {
1251 wmw
->wfxRef
.nBlockAlign
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nBlockAlign
;
1252 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw
->wfxRef
.nBlockAlign
);
1254 if (dwFlags
& MCI_WAVE_SET_CHANNELS
) {
1255 wmw
->wfxRef
.nChannels
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nChannels
;
1256 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw
->wfxRef
.nChannels
);
1258 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
) {
1259 wmw
->wfxRef
.wFormatTag
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wFormatTag
;
1260 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw
->wfxRef
.wFormatTag
);
1262 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
) {
1263 wmw
->wfxRef
.nSamplesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nSamplesPerSec
;
1264 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw
->wfxRef
.nSamplesPerSec
);
1269 /**************************************************************************
1270 * WAVE_mciSave [internal]
1272 static DWORD
WAVE_mciSave(UINT wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMS lpParms
)
1274 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1275 DWORD ret
= MCIERR_FILE_NOT_SAVED
, tmpRet
;
1276 WPARAM wparam
= MCI_NOTIFY_FAILURE
;
1278 TRACE("%d, %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_WAIT
)
1284 FIXME("MCI_WAIT not implemented\n");
1287 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1288 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1291 ret
= mmioClose(wmw
->hFile
, 0);
1294 If the destination file already exists, it has to be overwritten. (Behaviour
1295 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1296 my applications. We are making use of mmioRename, which WILL NOT overwrite
1297 the destination file (which is what Windows does, also verified in Win2K)
1298 So, lets delete the destination file before calling mmioRename. If the
1299 destination file DOESN'T exist, the delete will fail silently. Let's also be
1300 careful not to lose our previous error code.
1302 tmpRet
= GetLastError();
1303 DeleteFileA (lpParms
->lpfilename
);
1304 SetLastError(tmpRet
);
1306 if (0 == mmioRenameA(wmw
->openParms
.lpstrElementName
, lpParms
->lpfilename
, 0, 0 )) {
1307 ret
= ERROR_SUCCESS
;
1310 if (dwFlags
& MCI_NOTIFY
) {
1311 if (ret
== ERROR_SUCCESS
) wparam
= MCI_NOTIFY_SUCCESSFUL
;
1313 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1314 wmw
->openParms
.wDeviceID
, wparam
);
1320 /**************************************************************************
1321 * WAVE_mciStatus [internal]
1323 static DWORD
WAVE_mciStatus(UINT wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1325 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1328 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1329 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1330 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1332 if (dwFlags
& MCI_STATUS_ITEM
) {
1333 switch (lpParms
->dwItem
) {
1334 case MCI_STATUS_CURRENT_TRACK
:
1335 lpParms
->dwReturn
= 1;
1336 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1338 case MCI_STATUS_LENGTH
:
1340 lpParms
->dwReturn
= 0;
1341 return MCIERR_UNSUPPORTED_FUNCTION
;
1343 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1344 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
, &ret
);
1345 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1347 case MCI_STATUS_MODE
:
1348 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1349 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1350 ret
= MCI_RESOURCE_RETURNED
;
1352 case MCI_STATUS_MEDIA_PRESENT
:
1353 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1354 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1355 ret
= MCI_RESOURCE_RETURNED
;
1357 case MCI_STATUS_NUMBER_OF_TRACKS
:
1358 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1359 lpParms
->dwReturn
= 1;
1360 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
1362 case MCI_STATUS_POSITION
:
1364 lpParms
->dwReturn
= 0;
1365 return MCIERR_UNSUPPORTED_FUNCTION
;
1367 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1368 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1369 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
,
1371 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1372 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1374 case MCI_STATUS_READY
:
1375 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1376 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1377 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1378 ret
= MCI_RESOURCE_RETURNED
;
1380 case MCI_STATUS_TIME_FORMAT
:
1381 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, wmw
->dwMciTimeFormat
);
1382 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1383 ret
= MCI_RESOURCE_RETURNED
;
1385 case MCI_WAVE_INPUT
:
1386 TRACE("MCI_WAVE_INPUT !\n");
1387 lpParms
->dwReturn
= 0;
1388 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1390 case MCI_WAVE_OUTPUT
:
1391 TRACE("MCI_WAVE_OUTPUT !\n");
1394 if (waveOutGetID(wmw
->hWave
, &id
) == MMSYSERR_NOERROR
) {
1395 lpParms
->dwReturn
= id
;
1397 lpParms
->dwReturn
= 0;
1398 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1402 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1404 lpParms
->dwReturn
= 0;
1405 return MCIERR_UNSUPPORTED_FUNCTION
;
1407 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1408 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
1410 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1412 lpParms
->dwReturn
= 0;
1413 return MCIERR_UNSUPPORTED_FUNCTION
;
1415 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1416 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
1418 case MCI_WAVE_STATUS_BLOCKALIGN
:
1420 lpParms
->dwReturn
= 0;
1421 return MCIERR_UNSUPPORTED_FUNCTION
;
1423 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1424 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
1426 case MCI_WAVE_STATUS_CHANNELS
:
1428 lpParms
->dwReturn
= 0;
1429 return MCIERR_UNSUPPORTED_FUNCTION
;
1431 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1432 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
1434 case MCI_WAVE_STATUS_FORMATTAG
:
1436 lpParms
->dwReturn
= 0;
1437 return MCIERR_UNSUPPORTED_FUNCTION
;
1439 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1440 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
1442 case MCI_WAVE_STATUS_LEVEL
:
1443 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1444 lpParms
->dwReturn
= 0xAAAA5555;
1446 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1448 lpParms
->dwReturn
= 0;
1449 return MCIERR_UNSUPPORTED_FUNCTION
;
1451 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1452 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
1455 WARN("unknown command %08lX !\n", lpParms
->dwItem
);
1456 return MCIERR_UNRECOGNIZED_COMMAND
;
1459 if (dwFlags
& MCI_NOTIFY
) {
1460 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1461 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1466 /**************************************************************************
1467 * WAVE_mciGetDevCaps [internal]
1469 static DWORD
WAVE_mciGetDevCaps(UINT wDevID
, DWORD dwFlags
,
1470 LPMCI_GETDEVCAPS_PARMS lpParms
)
1472 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1475 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1477 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1478 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1480 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1481 switch(lpParms
->dwItem
) {
1482 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1483 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1484 ret
= MCI_RESOURCE_RETURNED
;
1486 case MCI_GETDEVCAPS_HAS_AUDIO
:
1487 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1488 ret
= MCI_RESOURCE_RETURNED
;
1490 case MCI_GETDEVCAPS_HAS_VIDEO
:
1491 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1492 ret
= MCI_RESOURCE_RETURNED
;
1494 case MCI_GETDEVCAPS_USES_FILES
:
1495 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1496 ret
= MCI_RESOURCE_RETURNED
;
1498 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1499 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1500 ret
= MCI_RESOURCE_RETURNED
;
1502 case MCI_GETDEVCAPS_CAN_RECORD
:
1503 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1504 ret
= MCI_RESOURCE_RETURNED
;
1506 case MCI_GETDEVCAPS_CAN_EJECT
:
1507 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1508 ret
= MCI_RESOURCE_RETURNED
;
1510 case MCI_GETDEVCAPS_CAN_PLAY
:
1511 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1512 ret
= MCI_RESOURCE_RETURNED
;
1514 case MCI_GETDEVCAPS_CAN_SAVE
:
1515 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1516 ret
= MCI_RESOURCE_RETURNED
;
1518 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1519 lpParms
->dwReturn
= 1;
1521 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1522 lpParms
->dwReturn
= 1;
1525 FIXME("Unknown capability (%08lx) !\n", lpParms
->dwItem
);
1526 return MCIERR_UNRECOGNIZED_COMMAND
;
1529 WARN("No GetDevCaps-Item !\n");
1530 return MCIERR_UNRECOGNIZED_COMMAND
;
1535 /**************************************************************************
1536 * WAVE_mciInfo [internal]
1538 static DWORD
WAVE_mciInfo(UINT wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSA lpParms
)
1542 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1544 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1546 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
1547 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1548 } else if (wmw
== NULL
) {
1549 ret
= MCIERR_INVALID_DEVICE_ID
;
1551 TRACE("buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1553 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1554 case MCI_INFO_PRODUCT
:
1555 str
= "Wine's audio player";
1558 str
= wmw
->openParms
.lpstrElementName
;
1560 case MCI_WAVE_INPUT
:
1561 str
= "Wine Wave In";
1563 case MCI_WAVE_OUTPUT
:
1564 str
= "Wine Wave Out";
1567 WARN("Don't know this info command (%lu)\n", dwFlags
);
1568 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
1572 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
1573 ret
= MCIERR_PARAM_OVERFLOW
;
1575 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
1578 lpParms
->lpstrReturn
[0] = 0;
1584 /**************************************************************************
1585 * DriverProc (MCIWAVE.@)
1587 LONG CALLBACK
MCIWAVE_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
1588 DWORD dwParam1
, DWORD dwParam2
)
1590 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1591 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1594 case DRV_LOAD
: return 1;
1595 case DRV_FREE
: return 1;
1596 case DRV_OPEN
: return WAVE_drvOpen((LPSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSA
)dwParam2
);
1597 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1598 case DRV_ENABLE
: return 1;
1599 case DRV_DISABLE
: return 1;
1600 case DRV_QUERYCONFIGURE
: return 1;
1601 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK
); return 1;
1602 case DRV_INSTALL
: return DRVCNF_RESTART
;
1603 case DRV_REMOVE
: return DRVCNF_RESTART
;
1606 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1609 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSA
) dwParam2
);
1610 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1611 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1612 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
1613 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
1614 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1615 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
1616 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1617 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1618 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1619 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1620 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSA
) dwParam2
);
1621 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1622 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMS
) dwParam2
);
1623 /* commands that should be supported */
1638 FIXME("Unsupported yet command [%lu]\n", wMsg
);
1641 TRACE("Unsupported command [%lu]\n", wMsg
);
1643 /* option which can be silenced */
1648 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1651 FIXME("is probably wrong msg [%lu]\n", wMsg
);
1652 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1654 return MCIERR_UNRECOGNIZED_COMMAND
;