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
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(mciwave
);
39 int nUseCount
; /* Incremented for each shared open */
40 BOOL fShareable
; /* TRUE if first open was shareable */
41 HMMIO hFile
; /* mmio file handle open as Element */
42 MCI_WAVE_OPEN_PARMSA openParms
;
44 LPWAVEFORMATEX lpWaveFormat
;
45 BOOL fInput
; /* FALSE = Output, TRUE = Input */
46 volatile WORD dwStatus
; /* one from MCI_MODE_xxxx */
47 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
48 DWORD dwPosition
; /* position in bytes in chunk */
49 HANDLE hEvent
; /* for synchronization */
50 DWORD dwEventCount
; /* for synchronization */
51 BOOL bTemporaryFile
; /* temporary file (MCI_RECORD) */
52 MMCKINFO ckMainRIFF
; /* main RIFF chunk */
53 MMCKINFO ckWaveData
; /* data chunk */
56 /* ===================================================================
57 * ===================================================================
58 * FIXME: should be using the new mmThreadXXXX functions from WINMM
60 * it would require to add a wine internal flag to mmThreadCreate
61 * in order to pass a 32 bit function instead of a 16 bit one
62 * ===================================================================
63 * =================================================================== */
72 /**************************************************************************
73 * MCI_SCAStarter [internal]
75 static DWORD CALLBACK
MCI_SCAStarter(LPVOID arg
)
77 struct SCA
* sca
= (struct SCA
*)arg
;
80 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
81 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
82 ret
= mciSendCommandA(sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
| MCI_WAIT
, sca
->dwParam2
);
83 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
84 sca
->wDevID
, sca
->wMsg
, sca
->dwParam1
, sca
->dwParam2
);
85 HeapFree(GetProcessHeap(), 0, sca
);
87 WARN("Should not happen ? what's wrong \n");
88 /* should not go after this point */
92 /**************************************************************************
93 * MCI_SendCommandAsync [internal]
95 static DWORD
MCI_SendCommandAsync(UINT wDevID
, UINT wMsg
, DWORD dwParam1
,
96 DWORD dwParam2
, UINT size
)
98 struct SCA
* sca
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA
) + size
);
101 return MCIERR_OUT_OF_MEMORY
;
103 sca
->wDevID
= wDevID
;
105 sca
->dwParam1
= dwParam1
;
107 if (size
&& dwParam2
) {
108 sca
->dwParam2
= (DWORD
)sca
+ sizeof(struct SCA
);
109 /* copy structure passed by program in dwParam2 to be sure
110 * we can still use it whatever the program does
112 memcpy((LPVOID
)sca
->dwParam2
, (LPVOID
)dwParam2
, size
);
114 sca
->dwParam2
= dwParam2
;
117 if (CreateThread(NULL
, 0, MCI_SCAStarter
, sca
, 0, NULL
) == 0) {
118 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
119 return MCI_SCAStarter(&sca
);
124 /*======================================================================*
125 * MCI WAVE implemantation *
126 *======================================================================*/
128 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
130 /**************************************************************************
131 * MCIWAVE_drvOpen [internal]
133 static DWORD
WAVE_drvOpen(LPSTR str
, LPMCI_OPEN_DRIVER_PARMSA modp
)
137 if (modp
== NULL
) return 0xFFFFFFFF;
139 wmw
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCIWAVE
));
144 wmw
->wDevID
= modp
->wDeviceID
;
145 mciSetDriverData(wmw
->wDevID
, (DWORD
)wmw
);
146 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
147 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
149 wmw
->wfxRef
.wFormatTag
= WAVE_FORMAT_PCM
;
150 wmw
->wfxRef
.nChannels
= 1; /* MONO */
151 wmw
->wfxRef
.nSamplesPerSec
= 11025;
152 wmw
->wfxRef
.nAvgBytesPerSec
= 11025;
153 wmw
->wfxRef
.nBlockAlign
= 1;
154 wmw
->wfxRef
.wBitsPerSample
= 8;
155 wmw
->wfxRef
.cbSize
= 0; /* don't care */
157 return modp
->wDeviceID
;
160 /**************************************************************************
161 * MCIWAVE_drvClose [internal]
163 static DWORD
WAVE_drvClose(DWORD dwDevID
)
165 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(dwDevID
);
168 HeapFree(GetProcessHeap(), 0, wmw
);
169 mciSetDriverData(dwDevID
, 0);
172 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
175 /**************************************************************************
176 * WAVE_mciGetOpenDev [internal]
178 static WINE_MCIWAVE
* WAVE_mciGetOpenDev(UINT wDevID
)
180 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
182 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
183 WARN("Invalid wDevID=%u\n", wDevID
);
189 /**************************************************************************
190 * WAVE_ConvertByteToTimeFormat [internal]
192 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
, LPDWORD lpRet
)
196 switch (wmw
->dwMciTimeFormat
) {
197 case MCI_FORMAT_MILLISECONDS
:
198 ret
= MulDiv(val
,1000,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
200 case MCI_FORMAT_BYTES
:
203 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
204 ret
= (val
* 8) / wmw
->lpWaveFormat
->wBitsPerSample
;
207 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
209 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
214 /**************************************************************************
215 * WAVE_ConvertTimeFormatToByte [internal]
217 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
221 switch (wmw
->dwMciTimeFormat
) {
222 case MCI_FORMAT_MILLISECONDS
:
223 ret
= (val
* wmw
->lpWaveFormat
->nAvgBytesPerSec
) / 1000;
225 case MCI_FORMAT_BYTES
:
228 case MCI_FORMAT_SAMPLES
: /* FIXME: is this correct ? */
229 ret
= (val
* wmw
->lpWaveFormat
->wBitsPerSample
) / 8;
232 WARN("Bad time format %lu!\n", wmw
->dwMciTimeFormat
);
234 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
238 /**************************************************************************
239 * WAVE_mciReadFmt [internal]
241 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, MMCKINFO
* pckMainRIFF
)
246 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
247 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
248 return MCIERR_INVALID_FILE
;
249 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
250 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
252 wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
253 if (!wmw
->lpWaveFormat
) return MMSYSERR_NOMEM
;
254 r
= mmioRead(wmw
->hFile
, (HPSTR
)wmw
->lpWaveFormat
, mmckInfo
.cksize
);
255 if (r
< sizeof(WAVEFORMAT
))
256 return MCIERR_INVALID_FILE
;
258 TRACE("wFormatTag=%04X !\n", wmw
->lpWaveFormat
->wFormatTag
);
259 TRACE("nChannels=%d \n", wmw
->lpWaveFormat
->nChannels
);
260 TRACE("nSamplesPerSec=%ld\n", wmw
->lpWaveFormat
->nSamplesPerSec
);
261 TRACE("nAvgBytesPerSec=%ld\n", wmw
->lpWaveFormat
->nAvgBytesPerSec
);
262 TRACE("nBlockAlign=%d \n", wmw
->lpWaveFormat
->nBlockAlign
);
263 TRACE("wBitsPerSample=%u !\n", wmw
->lpWaveFormat
->wBitsPerSample
);
264 if (r
>= (long)sizeof(WAVEFORMATEX
))
265 TRACE("cbSize=%u !\n", wmw
->lpWaveFormat
->cbSize
);
267 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
268 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
269 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
270 TRACE("can't find data chunk\n");
271 return MCIERR_INVALID_FILE
;
273 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
274 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
275 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
276 wmw
->lpWaveFormat
->nChannels
, wmw
->lpWaveFormat
->nSamplesPerSec
);
281 /**************************************************************************
282 * WAVE_mciCreateRIFFSkeleton [internal]
284 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
286 MMCKINFO ckWaveFormat
;
287 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
288 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
290 lpckRIFF
->ckid
= FOURCC_RIFF
;
291 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
292 lpckRIFF
->cksize
= 0;
294 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckRIFF
, MMIO_CREATERIFF
))
297 ckWaveFormat
.fccType
= 0;
298 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
299 ckWaveFormat
.cksize
= sizeof(PCMWAVEFORMAT
);
301 if (!wmw
->lpWaveFormat
)
303 wmw
->lpWaveFormat
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*wmw
->lpWaveFormat
));
304 if (!wmw
->lpWaveFormat
) return MMSYSERR_NOMEM
;
305 memcpy(wmw
->lpWaveFormat
, &wmw
->wfxRef
, sizeof(wmw
->wfxRef
));
308 /* we can only record PCM files... there is no way in the MCI API to specify
309 * the necessary data to initialize the extra bytes of the WAVEFORMATEX
312 if (wmw
->lpWaveFormat
->wFormatTag
!= WAVE_FORMAT_PCM
)
315 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, &ckWaveFormat
, 0))
318 if (-1 == mmioWrite(wmw
->hFile
, (HPCSTR
)wmw
->lpWaveFormat
, sizeof(PCMWAVEFORMAT
)))
321 if (MMSYSERR_NOERROR
!= mmioAscend(wmw
->hFile
, &ckWaveFormat
, 0))
324 lpckWaveData
->cksize
= 0;
325 lpckWaveData
->fccType
= 0;
326 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
328 /* create data chunk */
329 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckWaveData
, 0))
335 if (wmw
->lpWaveFormat
)
336 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
337 wmw
->lpWaveFormat
= NULL
;
338 return MCIERR_INVALID_FILE
;
341 /**************************************************************************
342 * WAVE_mciOpen [internal]
344 static DWORD
WAVE_mciOpen(UINT wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSA lpOpenParms
)
347 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
348 CHAR
* pszTmpFileName
= 0;
350 TRACE("(%04X, %08lX, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
351 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
352 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
354 if (dwFlags
& MCI_OPEN_SHAREABLE
)
355 return MCIERR_HARDWARE
;
357 if (wmw
->nUseCount
> 0) {
358 /* The driver is already opened on this channel
359 * Wave driver cannot be shared
361 return MCIERR_DEVICE_OPEN
;
368 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
370 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
372 if (dwFlags
& MCI_OPEN_ELEMENT
) {
373 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
374 /* could it be that (DWORD)lpOpenParms->lpstrElementName
375 * contains the hFile value ?
377 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
379 if (strlen(lpOpenParms
->lpstrElementName
) > 0) {
380 lpOpenParms
->lpstrElementName
= lpOpenParms
->lpstrElementName
;
382 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
383 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms
->lpstrElementName
);
385 if (lpOpenParms
->lpstrElementName
&& (strlen(lpOpenParms
->lpstrElementName
) > 0)) {
386 wmw
->hFile
= mmioOpenA((LPSTR
)lpOpenParms
->lpstrElementName
, NULL
,
387 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READWRITE
);
389 if (wmw
->hFile
== 0) {
390 WARN("can't find file='%s' !\n", lpOpenParms
->lpstrElementName
);
391 dwRet
= MCIERR_FILE_NOT_FOUND
;
395 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
397 /* make sure we're are the beginning of the file */
398 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
400 /* first reading of this file. read the waveformat chunk */
401 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
402 dwRet
= MCIERR_INVALID_FILE
;
404 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
405 (LPSTR
)&(lpckMainRIFF
->ckid
),
406 (LPSTR
) &(lpckMainRIFF
->fccType
),
407 (lpckMainRIFF
->cksize
));
409 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
410 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
411 dwRet
= MCIERR_INVALID_FILE
;
413 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
423 CHAR szTmpPath
[MAX_PATH
];
424 CHAR szPrefix
[4] = "TMP\0";
426 pszTmpFileName
= HeapAlloc(GetProcessHeap(),
428 MAX_PATH
* sizeof(*pszTmpFileName
));
430 if (!GetTempPathA(sizeof(szTmpPath
), szTmpPath
)) {
431 WARN("can't retrieve temp path!\n");
432 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
433 return MCIERR_FILE_NOT_FOUND
;
436 if (!GetTempFileNameA(szTmpPath
, szPrefix
, 0, pszTmpFileName
)) {
437 WARN("can't retrieve temp file name!\n");
438 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
439 return MCIERR_FILE_NOT_FOUND
;
442 wmw
->bTemporaryFile
= TRUE
;
444 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName
);
446 if (pszTmpFileName
&& (strlen(pszTmpFileName
) > 0)) {
448 wmw
->hFile
= mmioOpenA(pszTmpFileName
, NULL
,
449 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
451 if (wmw
->hFile
== 0) {
452 /* temporary file could not be created. clean filename. */
453 HeapFree(GetProcessHeap(), 0, pszTmpFileName
);
454 WARN("can't create file='%s' !\n", pszTmpFileName
);
455 dwRet
= MCIERR_FILE_NOT_FOUND
;
462 TRACE("hFile=%p\n", wmw
->hFile
);
464 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
466 if (wmw
->bTemporaryFile
== TRUE
)
468 /* Additional openParms is temporary file's name */
469 wmw
->openParms
.lpstrElementName
= pszTmpFileName
;
473 if (wmw
->lpWaveFormat
) {
474 switch (wmw
->lpWaveFormat
->wFormatTag
) {
475 case WAVE_FORMAT_PCM
:
476 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
477 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
478 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
479 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
480 wmw
->lpWaveFormat
->nSamplesPerSec
*
481 wmw
->lpWaveFormat
->nBlockAlign
);
482 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
483 wmw
->lpWaveFormat
->nSamplesPerSec
*
484 wmw
->lpWaveFormat
->nBlockAlign
;
491 wmw
->dwStatus
= MCI_MODE_STOP
;
495 mmioClose(wmw
->hFile
, 0);
501 /**************************************************************************
502 * WAVE_mciCue [internal]
504 static DWORD
WAVE_mciCue(UINT wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
509 This routine is far from complete. At the moment only a check is done on the
510 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
513 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
518 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
520 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID
, dwParam
, lpParms
);
522 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
524 /* always close elements ? */
525 if (wmw
->hFile
!= 0) {
526 mmioClose(wmw
->hFile
, 0);
530 dwRet
= MMSYSERR_NOERROR
; /* assume success */
532 if ((dwParam
& MCI_WAVE_INPUT
) && !wmw
->fInput
) {
533 dwRet
= waveOutClose(wmw
->hWave
);
534 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
536 } else if (wmw
->fInput
) {
537 dwRet
= waveInClose(wmw
->hWave
);
538 if (dwRet
!= MMSYSERR_NOERROR
) return MCIERR_INTERNAL
;
542 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
545 /**************************************************************************
546 * WAVE_mciStop [internal]
548 static DWORD
WAVE_mciStop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
551 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
553 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
555 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
557 /* wait for playback thread (if any) to exit before processing further */
558 switch (wmw
->dwStatus
) {
561 case MCI_MODE_RECORD
:
563 int oldStat
= wmw
->dwStatus
;
564 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
565 if (oldStat
== MCI_MODE_PAUSE
)
566 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
568 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
576 wmw
->dwStatus
= MCI_MODE_STOP
;
578 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
579 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
580 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
586 /**************************************************************************
587 * WAVE_mciClose [internal]
589 static DWORD
WAVE_mciClose(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
592 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
594 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
596 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
598 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
599 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
604 if (wmw
->nUseCount
== 0) {
605 if (wmw
->hFile
!= 0) {
606 mmioClose(wmw
->hFile
, 0);
611 /* That string got allocated in mciOpen because no filename was specified
612 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
613 * allocated by mciOpen, *NOT* the application.
615 if (wmw
->bTemporaryFile
)
617 HeapFree(GetProcessHeap(), 0, (char*)wmw
->openParms
.lpstrElementName
);
618 wmw
->openParms
.lpstrElementName
= NULL
;
621 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
622 wmw
->lpWaveFormat
= NULL
;
624 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
625 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
626 wmw
->openParms
.wDeviceID
,
627 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
633 /**************************************************************************
634 * WAVE_mciPlayCallback [internal]
636 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
638 DWORD dwParam1
, DWORD dwParam2
)
640 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
647 InterlockedIncrement(&wmw
->dwEventCount
);
648 TRACE("Returning waveHdr=%lx\n", dwParam1
);
649 SetEvent(wmw
->hEvent
);
652 ERR("Unknown uMsg=%d\n", uMsg
);
656 /******************************************************************
657 * WAVE_mciPlayWaitDone
661 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
664 ResetEvent(wmw
->hEvent
);
665 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
668 InterlockedIncrement(&wmw
->dwEventCount
);
670 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
674 /**************************************************************************
675 * WAVE_mciPlay [internal]
677 static DWORD
WAVE_mciPlay(UINT wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
680 LONG bufsize
, count
, left
;
682 LPWAVEHDR waveHdr
= NULL
;
683 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
686 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
688 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
689 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
691 /* FIXME : since there is no way to determine in which mode the device is
692 * open (recording/playback) automatically switch from a mode to another
697 WARN("cannot play on input device\n");
698 return MCIERR_NONAPPLICABLE_FUNCTION
;
701 if (wmw
->hFile
== 0) {
702 WARN("Can't play: no file='%s' !\n", wmw
->openParms
.lpstrElementName
);
703 return MCIERR_FILE_NOT_FOUND
;
706 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
707 /* FIXME: parameters (start/end) in lpParams may not be used */
708 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
711 /** This function will be called again by a thread when async is used.
712 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
713 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
715 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
))) {
716 return MCIERR_INTERNAL
;
719 wmw
->dwStatus
= MCI_MODE_PLAY
;
721 if (!(dwFlags
& MCI_WAIT
)) {
722 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_PLAY
, dwFlags
,
723 (DWORD
)lpParms
, sizeof(MCI_PLAY_PARMS
));
727 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
728 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
730 if (lpParms
&& (dwFlags
& MCI_TO
)) {
731 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
734 TRACE("Playing from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
736 if (end
<= wmw
->dwPosition
)
740 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
741 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
743 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
744 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
747 if (wmw
->lpWaveFormat
) {
748 switch (wmw
->lpWaveFormat
->wFormatTag
) {
749 case WAVE_FORMAT_PCM
:
750 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
751 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
752 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
753 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
754 wmw
->lpWaveFormat
->nSamplesPerSec
*
755 wmw
->lpWaveFormat
->nBlockAlign
);
756 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
757 wmw
->lpWaveFormat
->nSamplesPerSec
*
758 wmw
->lpWaveFormat
->nBlockAlign
;
764 TRACE("can't retrieve wave format %ld\n", dwRet
);
769 /* go back to beginning of chunk plus the requested position */
770 /* FIXME: I'm not sure this is correct, notably because some data linked to
771 * the decompression state machine will not be correcly initialized.
772 * try it this way (other way would be to decompress from 0 up to dwPosition
773 * and to start sending to hWave when dwPosition is reached)
775 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
777 /* By default the device will be opened for output, the MCI_CUE function is there to
778 * change from output to input and back
780 /* FIXME: how to choose between several output channels ? here mapper is forced */
781 dwRet
= waveOutOpen((HWAVEOUT
*)&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
782 (DWORD
)WAVE_mciPlayCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
785 TRACE("Can't open low level audio device %ld\n", dwRet
);
786 dwRet
= MCIERR_DEVICE_OPEN
;
791 /* make it so that 3 buffers per second are needed */
792 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
794 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
795 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
796 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
797 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
798 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
799 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
800 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
801 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
802 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
803 dwRet
= MCIERR_INTERNAL
;
808 left
= min(wmw
->ckWaveData
.cksize
, end
- wmw
->dwPosition
);
809 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
810 wmw
->dwEventCount
= 1L; /* for first buffer */
812 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, left
);
814 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
815 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
816 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
817 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize
, count
);
820 /* count is always <= bufsize, so this is correct regarding the
821 * waveOutPrepareHeader function
823 waveHdr
[whidx
].dwBufferLength
= count
;
824 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
825 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
826 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
,
827 waveHdr
[whidx
].dwBytesRecorded
);
828 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
830 wmw
->dwPosition
+= count
;
831 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw
->dwPosition
);
833 WAVE_mciPlayWaitDone(wmw
);
837 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
839 /* just to get rid of some race conditions between play, stop and pause */
840 waveOutReset(wmw
->hWave
);
842 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
843 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
848 HeapFree(GetProcessHeap(), 0, waveHdr
);
851 waveOutClose(wmw
->hWave
);
854 CloseHandle(wmw
->hEvent
);
856 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
857 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
858 wmw
->openParms
.wDeviceID
,
859 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
862 wmw
->dwStatus
= MCI_MODE_STOP
;
867 /**************************************************************************
868 * WAVE_mciPlayCallback [internal]
870 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
872 DWORD dwParam1
, DWORD dwParam2
)
874 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
883 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
885 InterlockedIncrement(&wmw
->dwEventCount
);
887 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
889 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
891 wmw
->dwPosition
+= count
;
892 /* else error reporting ?? */
893 if (wmw
->dwStatus
== MCI_MODE_RECORD
)
895 /* Only queue up another buffer if we are recording. We could receive this
896 message also when waveInReset() is called, since it notifies on all wave
897 buffers that are outstanding. Queueing up more sometimes causes waveInClose
899 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
900 TRACE("after mmioWrite dwPosition=%lu\n", wmw
->dwPosition
);
903 SetEvent(wmw
->hEvent
);
906 ERR("Unknown uMsg=%d\n", uMsg
);
910 /******************************************************************
911 * bWAVE_mciRecordWaitDone
914 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
917 ResetEvent(wmw
->hEvent
);
918 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
921 InterlockedIncrement(&wmw
->dwEventCount
);
923 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
927 /**************************************************************************
928 * WAVE_mciRecord [internal]
930 static DWORD
WAVE_mciRecord(UINT wDevID
, DWORD dwFlags
, LPMCI_RECORD_PARMS lpParms
)
933 DWORD dwRet
= MMSYSERR_NOERROR
;
935 LPWAVEHDR waveHdr
= NULL
;
936 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
939 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
941 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
942 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
944 /* FIXME : since there is no way to determine in which mode the device is
945 * open (recording/playback) automatically switch from a mode to another
950 WARN("cannot record on output device\n");
951 return MCIERR_NONAPPLICABLE_FUNCTION
;
954 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
955 /* FIXME: parameters (start/end) in lpParams may not be used */
956 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
959 /** This function will be called again by a thread when async is used.
960 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
961 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
963 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
))) {
964 return MCIERR_INTERNAL
;
967 wmw
->dwStatus
= MCI_MODE_RECORD
;
969 if (!(dwFlags
& MCI_WAIT
)) {
970 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, MCI_RECORD
, dwFlags
,
971 (DWORD
)lpParms
, sizeof(MCI_RECORD_PARMS
));
974 if (!wmw
->lpWaveFormat
) {
976 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
978 FIXME("Should descend into data chunk. Please report.\n");
982 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
983 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
986 if (lpParms
&& (dwFlags
& MCI_TO
)) {
987 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
990 TRACE("Recording from byte=%lu to byte=%lu\n", wmw
->dwPosition
, end
);
992 if (end
<= wmw
->dwPosition
)
997 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
998 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1000 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
1001 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
1003 /* go back to beginning of chunk plus the requested position */
1004 /* FIXME: I'm not sure this is correct, notably because some data linked to
1005 * the decompression state machine will not be correcly initialized.
1006 * try it this way (other way would be to decompress from 0 up to dwPosition
1007 * and to start sending to hWave when dwPosition is reached)
1009 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
1011 /* By default the device will be opened for output, the MCI_CUE function is there to
1012 * change from output to input and back
1014 /* FIXME: how to choose between several output channels ? here mapper is forced */
1015 dwRet
= waveInOpen((HWAVEIN
*)&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
1016 (DWORD
)WAVE_mciRecordCallback
, (DWORD
)wmw
, CALLBACK_FUNCTION
);
1018 if (dwRet
!= MMSYSERR_NOERROR
) {
1019 TRACE("Can't open low level audio device %ld\n", dwRet
);
1020 dwRet
= MCIERR_DEVICE_OPEN
;
1025 /* make it so that 3 buffers per second are needed */
1026 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
1028 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
1029 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
1030 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
1031 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
1032 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
1033 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1034 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1036 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1037 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1038 dwRet
= MCIERR_INTERNAL
;
1042 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1043 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1044 dwRet
= MCIERR_INTERNAL
;
1048 wmw
->hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
1049 wmw
->dwEventCount
= 1L; /* for first buffer */
1051 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw
->dwPosition
, end
- wmw
->dwPosition
);
1053 dwRet
= waveInStart(wmw
->hWave
);
1055 while (wmw
->dwPosition
< end
&& wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1056 WAVE_mciRecordWaitDone(wmw
);
1059 /* needed so that the callback above won't add again the buffers returned by the reset */
1060 wmw
->dwStatus
= MCI_MODE_STOP
;
1062 waveInReset(wmw
->hWave
);
1064 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1065 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1070 HeapFree(GetProcessHeap(), 0, waveHdr
);
1073 waveInClose(wmw
->hWave
);
1076 CloseHandle(wmw
->hEvent
);
1078 /* need to update the size of the data chunk */
1079 if (mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0) != MMSYSERR_NOERROR
) {
1080 TRACE("failed on ascend\n");
1083 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
1084 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1085 wmw
->openParms
.wDeviceID
,
1086 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1089 wmw
->dwStatus
= MCI_MODE_STOP
;
1095 /**************************************************************************
1096 * WAVE_mciPause [internal]
1098 static DWORD
WAVE_mciPause(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1101 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1103 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1105 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1106 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1108 if (wmw
->dwStatus
== MCI_MODE_PLAY
) {
1109 wmw
->dwStatus
= MCI_MODE_PAUSE
;
1112 if (wmw
->fInput
) dwRet
= waveInStop(wmw
->hWave
);
1113 else dwRet
= waveOutPause(wmw
->hWave
);
1115 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1118 /**************************************************************************
1119 * WAVE_mciResume [internal]
1121 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1123 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1126 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1128 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1130 if (wmw
->dwStatus
== MCI_MODE_PAUSE
) {
1131 wmw
->dwStatus
= MCI_MODE_PLAY
;
1134 if (wmw
->fInput
) dwRet
= waveInStart(wmw
->hWave
);
1135 else dwRet
= waveOutRestart(wmw
->hWave
);
1136 return (dwRet
== MMSYSERR_NOERROR
) ? 0 : MCIERR_INTERNAL
;
1139 /**************************************************************************
1140 * WAVE_mciSeek [internal]
1142 static DWORD
WAVE_mciSeek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1145 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1147 TRACE("(%04X, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1149 if (lpParms
== NULL
) {
1150 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1151 } else if (wmw
== NULL
) {
1152 ret
= MCIERR_INVALID_DEVICE_ID
;
1154 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1156 if (dwFlags
& MCI_SEEK_TO_START
) {
1157 wmw
->dwPosition
= 0;
1158 } else if (dwFlags
& MCI_SEEK_TO_END
) {
1159 wmw
->dwPosition
= wmw
->ckWaveData
.cksize
;
1160 } else if (dwFlags
& MCI_TO
) {
1161 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1163 WARN("dwFlag doesn't tell where to seek to...\n");
1164 return MCIERR_MISSING_PARAMETER
;
1167 TRACE("Seeking to position=%lu bytes\n", wmw
->dwPosition
);
1169 if (dwFlags
& MCI_NOTIFY
) {
1170 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1171 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1177 /**************************************************************************
1178 * WAVE_mciSet [internal]
1180 static DWORD
WAVE_mciSet(UINT wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1182 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1184 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1186 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1187 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1189 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1190 switch (lpParms
->dwTimeFormat
) {
1191 case MCI_FORMAT_MILLISECONDS
:
1192 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1193 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1195 case MCI_FORMAT_BYTES
:
1196 TRACE("MCI_FORMAT_BYTES !\n");
1197 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1199 case MCI_FORMAT_SAMPLES
:
1200 TRACE("MCI_FORMAT_SAMPLES !\n");
1201 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1204 WARN("Bad time format %lu!\n", lpParms
->dwTimeFormat
);
1205 return MCIERR_BAD_TIME_FORMAT
;
1208 if (dwFlags
& MCI_SET_VIDEO
) {
1209 TRACE("No support for video !\n");
1210 return MCIERR_UNSUPPORTED_FUNCTION
;
1212 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1213 TRACE("No support for door open !\n");
1214 return MCIERR_UNSUPPORTED_FUNCTION
;
1216 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1217 TRACE("No support for door close !\n");
1218 return MCIERR_UNSUPPORTED_FUNCTION
;
1220 if (dwFlags
& MCI_SET_AUDIO
) {
1221 if (dwFlags
& MCI_SET_ON
) {
1222 TRACE("MCI_SET_ON audio !\n");
1223 } else if (dwFlags
& MCI_SET_OFF
) {
1224 TRACE("MCI_SET_OFF audio !\n");
1226 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1227 return MCIERR_BAD_INTEGER
;
1230 if (lpParms
->dwAudio
& MCI_SET_AUDIO_ALL
)
1231 TRACE("MCI_SET_AUDIO_ALL !\n");
1232 if (lpParms
->dwAudio
& MCI_SET_AUDIO_LEFT
)
1233 TRACE("MCI_SET_AUDIO_LEFT !\n");
1234 if (lpParms
->dwAudio
& MCI_SET_AUDIO_RIGHT
)
1235 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1237 if (dwFlags
& MCI_WAVE_INPUT
)
1238 TRACE("MCI_WAVE_INPUT !\n");
1239 if (dwFlags
& MCI_WAVE_OUTPUT
)
1240 TRACE("MCI_WAVE_OUTPUT !\n");
1241 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
1242 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1243 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
1244 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1245 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
) {
1246 wmw
->wfxRef
.nAvgBytesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nAvgBytesPerSec
;
1247 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw
->wfxRef
.nAvgBytesPerSec
);
1249 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
) {
1250 wmw
->wfxRef
.wBitsPerSample
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wBitsPerSample
;
1251 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw
->wfxRef
.wBitsPerSample
);
1253 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
) {
1254 wmw
->wfxRef
.nBlockAlign
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nBlockAlign
;
1255 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw
->wfxRef
.nBlockAlign
);
1257 if (dwFlags
& MCI_WAVE_SET_CHANNELS
) {
1258 wmw
->wfxRef
.nChannels
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nChannels
;
1259 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw
->wfxRef
.nChannels
);
1261 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
) {
1262 wmw
->wfxRef
.wFormatTag
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wFormatTag
;
1263 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw
->wfxRef
.wFormatTag
);
1265 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
) {
1266 wmw
->wfxRef
.nSamplesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nSamplesPerSec
;
1267 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw
->wfxRef
.nSamplesPerSec
);
1272 /**************************************************************************
1273 * WAVE_mciSave [internal]
1275 static DWORD
WAVE_mciSave(UINT wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMS lpParms
)
1277 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1278 DWORD ret
= MCIERR_FILE_NOT_SAVED
, tmpRet
;
1279 WPARAM wparam
= MCI_NOTIFY_FAILURE
;
1281 TRACE("%d, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1282 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1283 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1285 if (dwFlags
& MCI_WAIT
)
1287 FIXME("MCI_WAIT not implemented\n");
1290 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1291 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1294 ret
= mmioClose(wmw
->hFile
, 0);
1297 If the destination file already exists, it has to be overwritten. (Behaviour
1298 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1299 my applications. We are making use of mmioRename, which WILL NOT overwrite
1300 the destination file (which is what Windows does, also verified in Win2K)
1301 So, lets delete the destination file before calling mmioRename. If the
1302 destination file DOESN'T exist, the delete will fail silently. Let's also be
1303 careful not to lose our previous error code.
1305 tmpRet
= GetLastError();
1306 DeleteFileA (lpParms
->lpfilename
);
1307 SetLastError(tmpRet
);
1309 if (0 == mmioRenameA(wmw
->openParms
.lpstrElementName
, lpParms
->lpfilename
, 0, 0 )) {
1310 ret
= ERROR_SUCCESS
;
1313 if (dwFlags
& MCI_NOTIFY
) {
1314 if (ret
== ERROR_SUCCESS
) wparam
= MCI_NOTIFY_SUCCESSFUL
;
1316 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1317 wmw
->openParms
.wDeviceID
, wparam
);
1323 /**************************************************************************
1324 * WAVE_mciStatus [internal]
1326 static DWORD
WAVE_mciStatus(UINT wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1328 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1331 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1332 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1333 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1335 if (dwFlags
& MCI_STATUS_ITEM
) {
1336 switch (lpParms
->dwItem
) {
1337 case MCI_STATUS_CURRENT_TRACK
:
1338 lpParms
->dwReturn
= 1;
1339 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1341 case MCI_STATUS_LENGTH
:
1343 lpParms
->dwReturn
= 0;
1344 return MCIERR_UNSUPPORTED_FUNCTION
;
1346 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1347 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
, &ret
);
1348 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1350 case MCI_STATUS_MODE
:
1351 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1352 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1353 ret
= MCI_RESOURCE_RETURNED
;
1355 case MCI_STATUS_MEDIA_PRESENT
:
1356 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1357 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1358 ret
= MCI_RESOURCE_RETURNED
;
1360 case MCI_STATUS_NUMBER_OF_TRACKS
:
1361 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1362 lpParms
->dwReturn
= 1;
1363 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms
->dwReturn
);
1365 case MCI_STATUS_POSITION
:
1367 lpParms
->dwReturn
= 0;
1368 return MCIERR_UNSUPPORTED_FUNCTION
;
1370 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1371 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1372 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
,
1374 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1375 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1377 case MCI_STATUS_READY
:
1378 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1379 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1380 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1381 ret
= MCI_RESOURCE_RETURNED
;
1383 case MCI_STATUS_TIME_FORMAT
:
1384 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, wmw
->dwMciTimeFormat
);
1385 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1386 ret
= MCI_RESOURCE_RETURNED
;
1388 case MCI_WAVE_INPUT
:
1389 TRACE("MCI_WAVE_INPUT !\n");
1390 lpParms
->dwReturn
= 0;
1391 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1393 case MCI_WAVE_OUTPUT
:
1394 TRACE("MCI_WAVE_OUTPUT !\n");
1397 if (waveOutGetID(wmw
->hWave
, &id
) == MMSYSERR_NOERROR
) {
1398 lpParms
->dwReturn
= id
;
1400 lpParms
->dwReturn
= 0;
1401 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1405 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1407 lpParms
->dwReturn
= 0;
1408 return MCIERR_UNSUPPORTED_FUNCTION
;
1410 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1411 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms
->dwReturn
);
1413 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1415 lpParms
->dwReturn
= 0;
1416 return MCIERR_UNSUPPORTED_FUNCTION
;
1418 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1419 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms
->dwReturn
);
1421 case MCI_WAVE_STATUS_BLOCKALIGN
:
1423 lpParms
->dwReturn
= 0;
1424 return MCIERR_UNSUPPORTED_FUNCTION
;
1426 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1427 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms
->dwReturn
);
1429 case MCI_WAVE_STATUS_CHANNELS
:
1431 lpParms
->dwReturn
= 0;
1432 return MCIERR_UNSUPPORTED_FUNCTION
;
1434 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1435 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms
->dwReturn
);
1437 case MCI_WAVE_STATUS_FORMATTAG
:
1439 lpParms
->dwReturn
= 0;
1440 return MCIERR_UNSUPPORTED_FUNCTION
;
1442 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1443 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms
->dwReturn
);
1445 case MCI_WAVE_STATUS_LEVEL
:
1446 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1447 lpParms
->dwReturn
= 0xAAAA5555;
1449 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1451 lpParms
->dwReturn
= 0;
1452 return MCIERR_UNSUPPORTED_FUNCTION
;
1454 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1455 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms
->dwReturn
);
1458 WARN("unknown command %08lX !\n", lpParms
->dwItem
);
1459 return MCIERR_UNRECOGNIZED_COMMAND
;
1462 if (dwFlags
& MCI_NOTIFY
) {
1463 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1464 wmw
->openParms
.wDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1469 /**************************************************************************
1470 * WAVE_mciGetDevCaps [internal]
1472 static DWORD
WAVE_mciGetDevCaps(UINT wDevID
, DWORD dwFlags
,
1473 LPMCI_GETDEVCAPS_PARMS lpParms
)
1475 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1478 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1480 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1481 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1483 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1484 switch(lpParms
->dwItem
) {
1485 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1486 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1487 ret
= MCI_RESOURCE_RETURNED
;
1489 case MCI_GETDEVCAPS_HAS_AUDIO
:
1490 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1491 ret
= MCI_RESOURCE_RETURNED
;
1493 case MCI_GETDEVCAPS_HAS_VIDEO
:
1494 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1495 ret
= MCI_RESOURCE_RETURNED
;
1497 case MCI_GETDEVCAPS_USES_FILES
:
1498 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1499 ret
= MCI_RESOURCE_RETURNED
;
1501 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1502 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1503 ret
= MCI_RESOURCE_RETURNED
;
1505 case MCI_GETDEVCAPS_CAN_RECORD
:
1506 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1507 ret
= MCI_RESOURCE_RETURNED
;
1509 case MCI_GETDEVCAPS_CAN_EJECT
:
1510 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1511 ret
= MCI_RESOURCE_RETURNED
;
1513 case MCI_GETDEVCAPS_CAN_PLAY
:
1514 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1515 ret
= MCI_RESOURCE_RETURNED
;
1517 case MCI_GETDEVCAPS_CAN_SAVE
:
1518 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1519 ret
= MCI_RESOURCE_RETURNED
;
1521 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1522 lpParms
->dwReturn
= 1;
1524 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1525 lpParms
->dwReturn
= 1;
1528 FIXME("Unknown capability (%08lx) !\n", lpParms
->dwItem
);
1529 return MCIERR_UNRECOGNIZED_COMMAND
;
1532 WARN("No GetDevCaps-Item !\n");
1533 return MCIERR_UNRECOGNIZED_COMMAND
;
1538 /**************************************************************************
1539 * WAVE_mciInfo [internal]
1541 static DWORD
WAVE_mciInfo(UINT wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSA lpParms
)
1545 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1547 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1549 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
) {
1550 ret
= MCIERR_NULL_PARAMETER_BLOCK
;
1551 } else if (wmw
== NULL
) {
1552 ret
= MCIERR_INVALID_DEVICE_ID
;
1554 TRACE("buf=%p, len=%lu\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1556 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1557 case MCI_INFO_PRODUCT
:
1558 str
= "Wine's audio player";
1561 str
= wmw
->openParms
.lpstrElementName
;
1563 case MCI_WAVE_INPUT
:
1564 str
= "Wine Wave In";
1566 case MCI_WAVE_OUTPUT
:
1567 str
= "Wine Wave Out";
1570 WARN("Don't know this info command (%lu)\n", dwFlags
);
1571 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
1575 if (strlen(str
) + 1 > lpParms
->dwRetSize
) {
1576 ret
= MCIERR_PARAM_OVERFLOW
;
1578 lstrcpynA(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
1581 lpParms
->lpstrReturn
[0] = 0;
1587 /**************************************************************************
1588 * DriverProc (MCIWAVE.@)
1590 LONG CALLBACK
MCIWAVE_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
1591 DWORD dwParam1
, DWORD dwParam2
)
1593 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1594 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1597 case DRV_LOAD
: return 1;
1598 case DRV_FREE
: return 1;
1599 case DRV_OPEN
: return WAVE_drvOpen((LPSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSA
)dwParam2
);
1600 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1601 case DRV_ENABLE
: return 1;
1602 case DRV_DISABLE
: return 1;
1603 case DRV_QUERYCONFIGURE
: return 1;
1604 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK
); return 1;
1605 case DRV_INSTALL
: return DRVCNF_RESTART
;
1606 case DRV_REMOVE
: return DRVCNF_RESTART
;
1609 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1612 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSA
) dwParam2
);
1613 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1614 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1615 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
) dwParam2
);
1616 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, (LPMCI_RECORD_PARMS
) dwParam2
);
1617 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1618 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
1619 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1620 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1621 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1622 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1623 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSA
) dwParam2
);
1624 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1625 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMS
) dwParam2
);
1626 /* commands that should be supported */
1641 FIXME("Unsupported yet command [%lu]\n", wMsg
);
1644 TRACE("Unsupported command [%lu]\n", wMsg
);
1646 /* option which can be silenced */
1651 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1654 FIXME("is probably wrong msg [%lu]\n", wMsg
);
1655 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1657 return MCIERR_UNRECOGNIZED_COMMAND
;