2 * Wine Driver for MCI wave forms
4 * Copyright 1994 Martin Ayotte
5 * 1999,2000,2005 Eric Pouech
6 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave
);
42 int nUseCount
; /* Incremented for each shared open */
43 HMMIO hFile
; /* mmio file handle open as Element */
44 MCIDEVICEID wNotifyDeviceID
; /* MCI device ID with a pending notification */
45 HANDLE hCallback
; /* Callback handle for pending notification */
46 LPWSTR lpFileName
; /* Name of file (if any) */
48 LPWAVEFORMATEX lpWaveFormat
; /* Points to wfxRef until set by OPEN or RECORD */
49 BOOL fInput
; /* FALSE = Output, TRUE = Input */
50 WORD wInput
; /* wave input device */
51 WORD wOutput
; /* wave output device */
52 volatile WORD dwStatus
; /* one from MCI_MODE_xxxx */
53 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
54 DWORD dwPosition
; /* position in bytes in chunk */
55 HANDLE hEvent
; /* for synchronization */
56 LONG dwEventCount
; /* for synchronization */
57 MMCKINFO ckMainRIFF
; /* main RIFF chunk */
58 MMCKINFO ckWaveData
; /* data chunk */
61 /* ===================================================================
62 * ===================================================================
63 * FIXME: should be using the new mmThreadXXXX functions from WINMM
65 * it would require to add a wine internal flag to mmThreadCreate
66 * in order to pass a 32 bit function instead of a 16 bit one
67 * ===================================================================
68 * =================================================================== */
70 typedef DWORD (*async_cmd
)(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE evt
);
80 /**************************************************************************
81 * MCI_SCAStarter [internal]
83 static DWORD CALLBACK
MCI_SCAStarter(LPVOID arg
)
85 struct SCA
* sca
= (struct SCA
*)arg
;
88 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
89 sca
->wDevID
, sca
->dwParam1
, sca
->dwParam2
);
90 ret
= sca
->cmd(sca
->wDevID
, sca
->dwParam1
| MCI_WAIT
, sca
->dwParam2
, sca
->evt
);
91 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
92 sca
->wDevID
, sca
->dwParam1
, sca
->dwParam2
);
93 HeapFree(GetProcessHeap(), 0, sca
);
97 /**************************************************************************
98 * MCI_SendCommandAsync [internal]
100 static DWORD
MCI_SendCommandAsync(UINT wDevID
, async_cmd cmd
, DWORD_PTR dwParam1
,
101 DWORD_PTR dwParam2
, UINT size
)
104 struct SCA
* sca
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA
) + size
);
107 return MCIERR_OUT_OF_MEMORY
;
109 sca
->wDevID
= wDevID
;
111 sca
->dwParam1
= dwParam1
;
113 if (size
&& dwParam2
) {
114 sca
->dwParam2
= (DWORD_PTR
)sca
+ sizeof(struct SCA
);
115 /* copy structure passed by program in dwParam2 to be sure
116 * we can still use it whatever the program does
118 memcpy((LPVOID
)sca
->dwParam2
, (LPVOID
)dwParam2
, size
);
120 sca
->dwParam2
= dwParam2
;
123 if ((sca
->evt
= handles
[1] = CreateEventW(NULL
, FALSE
, FALSE
, NULL
)) == NULL
||
124 (handles
[0] = CreateThread(NULL
, 0, MCI_SCAStarter
, sca
, 0, NULL
)) == 0) {
125 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
126 if (handles
[1]) CloseHandle(handles
[1]);
128 return MCI_SCAStarter(&sca
);
131 SetThreadPriority(handles
[0], THREAD_PRIORITY_TIME_CRITICAL
);
132 /* wait until either:
133 * - the thread has finished (handles[0], likely an error)
134 * - init phase of async command is done (handles[1])
136 WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
137 CloseHandle(handles
[0]);
138 CloseHandle(handles
[1]);
142 /*======================================================================*
143 * MCI WAVE implementation *
144 *======================================================================*/
146 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
148 /**************************************************************************
149 * MCIWAVE_drvOpen [internal]
151 static LRESULT
WAVE_drvOpen(LPCWSTR str
, LPMCI_OPEN_DRIVER_PARMSW modp
)
155 if (modp
== NULL
) return 0xFFFFFFFF;
157 wmw
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCIWAVE
));
162 wmw
->wDevID
= modp
->wDeviceID
;
163 mciSetDriverData(wmw
->wDevID
, (DWORD_PTR
)wmw
);
164 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
165 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
167 wmw
->wfxRef
.wFormatTag
= WAVE_FORMAT_PCM
;
168 wmw
->wfxRef
.nChannels
= 1; /* MONO */
169 wmw
->wfxRef
.nSamplesPerSec
= 11025;
170 wmw
->wfxRef
.nAvgBytesPerSec
= 11025;
171 wmw
->wfxRef
.nBlockAlign
= 1;
172 wmw
->wfxRef
.wBitsPerSample
= 8;
173 wmw
->wfxRef
.cbSize
= 0; /* don't care */
175 return modp
->wDeviceID
;
178 /**************************************************************************
179 * MCIWAVE_drvClose [internal]
181 static LRESULT
WAVE_drvClose(MCIDEVICEID dwDevID
)
183 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(dwDevID
);
186 HeapFree(GetProcessHeap(), 0, wmw
);
187 mciSetDriverData(dwDevID
, 0);
190 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
193 /**************************************************************************
194 * WAVE_mciGetOpenDev [internal]
196 static WINE_MCIWAVE
*WAVE_mciGetOpenDev(MCIDEVICEID wDevID
)
198 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
200 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
201 WARN("Invalid wDevID=%u\n", wDevID
);
207 /**************************************************************************
208 * WAVE_mciNotify [internal]
210 * Notifications in MCI work like a 1-element queue.
211 * Each new notification request supersedes the previous one.
212 * This affects Play and Record; other commands are immediate.
214 static void WAVE_mciNotify(DWORD_PTR hWndCallBack
, WINE_MCIWAVE
* wmw
, UINT wStatus
)
216 /* We simply save one parameter by not passing the wDevID local
217 * to the command. They are the same (via mciGetDriverData).
219 MCIDEVICEID wDevID
= wmw
->wNotifyDeviceID
;
220 HANDLE old
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
221 if (old
) mciDriverNotify(old
, wDevID
, MCI_NOTIFY_SUPERSEDED
);
222 mciDriverNotify(HWND_32(LOWORD(hWndCallBack
)), wDevID
, wStatus
);
225 /**************************************************************************
226 * WAVE_ConvertByteToTimeFormat [internal]
228 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
, LPDWORD lpRet
)
232 switch (wmw
->dwMciTimeFormat
) {
233 case MCI_FORMAT_MILLISECONDS
:
234 ret
= MulDiv(val
,1000,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
236 case MCI_FORMAT_BYTES
:
239 case MCI_FORMAT_SAMPLES
:
240 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nSamplesPerSec
,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
243 WARN("Bad time format %u!\n", wmw
->dwMciTimeFormat
);
245 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
250 /**************************************************************************
251 * WAVE_ConvertTimeFormatToByte [internal]
253 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
257 switch (wmw
->dwMciTimeFormat
) {
258 case MCI_FORMAT_MILLISECONDS
:
259 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,1000);
261 case MCI_FORMAT_BYTES
:
264 case MCI_FORMAT_SAMPLES
:
265 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,wmw
->lpWaveFormat
->nSamplesPerSec
);
268 WARN("Bad time format %u!\n", wmw
->dwMciTimeFormat
);
270 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
274 /**************************************************************************
275 * WAVE_mciReadFmt [internal]
277 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, const MMCKINFO
* pckMainRIFF
)
283 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
284 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
285 return MCIERR_INVALID_FILE
;
286 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
287 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
289 pwfx
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
290 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
292 r
= mmioRead(wmw
->hFile
, (HPSTR
)pwfx
, mmckInfo
.cksize
);
293 if (r
< sizeof(PCMWAVEFORMAT
)) {
294 HeapFree(GetProcessHeap(), 0, pwfx
);
295 return MCIERR_INVALID_FILE
;
297 TRACE("wFormatTag=%04X !\n", pwfx
->wFormatTag
);
298 TRACE("nChannels=%d\n", pwfx
->nChannels
);
299 TRACE("nSamplesPerSec=%d\n", pwfx
->nSamplesPerSec
);
300 TRACE("nAvgBytesPerSec=%d\n", pwfx
->nAvgBytesPerSec
);
301 TRACE("nBlockAlign=%d\n", pwfx
->nBlockAlign
);
302 TRACE("wBitsPerSample=%u !\n", pwfx
->wBitsPerSample
);
303 if (r
>= (long)sizeof(WAVEFORMATEX
))
304 TRACE("cbSize=%u !\n", pwfx
->cbSize
);
305 if ((pwfx
->wFormatTag
!= WAVE_FORMAT_PCM
)
306 && (r
< sizeof(WAVEFORMATEX
) || (r
< sizeof(WAVEFORMATEX
) + pwfx
->cbSize
))) {
307 HeapFree(GetProcessHeap(), 0, pwfx
);
308 return MCIERR_INVALID_FILE
;
310 wmw
->lpWaveFormat
= pwfx
;
312 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
313 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
314 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
315 TRACE("can't find data chunk\n");
316 return MCIERR_INVALID_FILE
;
318 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
319 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
323 /**************************************************************************
324 * WAVE_mciDefaultFmt [internal]
326 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
327 * until either Open File or Record. It becomes immutable afterwards,
328 * i.e. Set wave format or channels etc. is subsequently refused.
330 static void WAVE_mciDefaultFmt(WINE_MCIWAVE
* wmw
)
332 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
333 wmw
->lpWaveFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
334 wmw
->lpWaveFormat
->nChannels
= 1;
335 wmw
->lpWaveFormat
->nSamplesPerSec
= 11025;
336 wmw
->lpWaveFormat
->nAvgBytesPerSec
= 11025;
337 wmw
->lpWaveFormat
->nBlockAlign
= 1;
338 wmw
->lpWaveFormat
->wBitsPerSample
= 8;
339 wmw
->lpWaveFormat
->cbSize
= 0;
342 /**************************************************************************
343 * WAVE_mciCreateRIFFSkeleton [internal]
345 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
347 MMCKINFO ckWaveFormat
;
348 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
349 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
351 lpckRIFF
->ckid
= FOURCC_RIFF
;
352 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
353 lpckRIFF
->cksize
= 0;
355 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckRIFF
, MMIO_CREATERIFF
))
358 ckWaveFormat
.fccType
= 0;
359 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
360 ckWaveFormat
.cksize
= sizeof(PCMWAVEFORMAT
);
362 /* Set wave format accepts PCM only, however open an
363 * existing ADPCM file, record into it and the MCI will
364 * happily save back in that format. */
365 if (wmw
->lpWaveFormat
->wFormatTag
== WAVE_FORMAT_PCM
) {
366 if (wmw
->lpWaveFormat
->nBlockAlign
!=
367 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
368 WORD size
= wmw
->lpWaveFormat
->nChannels
*
369 wmw
->lpWaveFormat
->wBitsPerSample
/8;
370 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
371 wmw
->lpWaveFormat
->nBlockAlign
, size
);
372 wmw
->lpWaveFormat
->nBlockAlign
= size
;
374 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
375 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
376 DWORD speed
= wmw
->lpWaveFormat
->nSamplesPerSec
*
377 wmw
->lpWaveFormat
->nBlockAlign
;
378 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
379 wmw
->lpWaveFormat
->nAvgBytesPerSec
, speed
);
380 wmw
->lpWaveFormat
->nAvgBytesPerSec
= speed
;
383 if (wmw
->lpWaveFormat
== &wmw
->wfxRef
) {
384 LPWAVEFORMATEX pwfx
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WAVEFORMATEX
));
385 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
386 /* Set wave format accepts PCM only so the size is known. */
387 assert(wmw
->wfxRef
.wFormatTag
== WAVE_FORMAT_PCM
);
389 wmw
->lpWaveFormat
= pwfx
;
392 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, &ckWaveFormat
, 0))
395 if (-1 == mmioWrite(wmw
->hFile
, (HPCSTR
)wmw
->lpWaveFormat
, (WAVE_FORMAT_PCM
==wmw
->lpWaveFormat
->wFormatTag
)
396 ? sizeof(PCMWAVEFORMAT
) : sizeof(WAVEFORMATEX
)+wmw
->lpWaveFormat
->cbSize
))
399 if (MMSYSERR_NOERROR
!= mmioAscend(wmw
->hFile
, &ckWaveFormat
, 0))
402 lpckWaveData
->cksize
= 0;
403 lpckWaveData
->fccType
= 0;
404 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
406 /* create data chunk */
407 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckWaveData
, 0))
413 /* mciClose takes care of wmw->lpWaveFormat. */
414 return MCIERR_INVALID_FILE
;
417 static DWORD
create_tmp_file(HMMIO
* hFile
, LPWSTR
* pszTmpFileName
)
419 WCHAR szTmpPath
[MAX_PATH
];
421 DWORD dwRet
= MMSYSERR_NOERROR
;
428 if (!GetTempPathW(sizeof(szTmpPath
)/sizeof(szTmpPath
[0]), szTmpPath
)) {
429 WARN("can't retrieve temp path!\n");
430 *pszTmpFileName
= NULL
;
431 return MCIERR_FILE_NOT_FOUND
;
434 *pszTmpFileName
= HeapAlloc(GetProcessHeap(),
436 MAX_PATH
* sizeof(WCHAR
));
437 if (!GetTempFileNameW(szTmpPath
, szPrefix
, 0, *pszTmpFileName
)) {
438 WARN("can't retrieve temp file name!\n");
439 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
440 return MCIERR_FILE_NOT_FOUND
;
443 TRACE("%s!\n", debugstr_w(*pszTmpFileName
));
445 if (*pszTmpFileName
&& (strlenW(*pszTmpFileName
) > 0)) {
447 *hFile
= mmioOpenW(*pszTmpFileName
, NULL
,
448 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
451 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName
));
452 /* temporary file could not be created. clean filename. */
453 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
454 dwRet
= MCIERR_FILE_NOT_FOUND
;
460 static LRESULT
WAVE_mciOpenFile(WINE_MCIWAVE
* wmw
, LPCWSTR filename
)
462 LRESULT dwRet
= MMSYSERR_NOERROR
;
465 fn
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename
) + 1) * sizeof(WCHAR
));
466 if (!fn
) return MCIERR_OUT_OF_MEMORY
;
467 strcpyW(fn
, filename
);
468 HeapFree(GetProcessHeap(), 0, (void*)wmw
->lpFileName
);
469 wmw
->lpFileName
= fn
;
471 if (strlenW(filename
) > 0) {
472 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
473 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename
));
475 wmw
->hFile
= mmioOpenW((LPWSTR
)filename
, NULL
,
476 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READ
);
478 if (wmw
->hFile
== 0) {
479 WARN("can't find file=%s!\n", debugstr_w(filename
));
480 dwRet
= MCIERR_FILE_NOT_FOUND
;
484 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
486 /* make sure we're are the beginning of the file */
487 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
489 /* first reading of this file. read the waveformat chunk */
490 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
491 dwRet
= MCIERR_INVALID_FILE
;
493 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
494 (LPSTR
)&(lpckMainRIFF
->ckid
),
495 (LPSTR
) &(lpckMainRIFF
->fccType
),
496 (lpckMainRIFF
->cksize
));
498 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
499 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
500 dwRet
= MCIERR_INVALID_FILE
;
502 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
510 /**************************************************************************
511 * WAVE_mciOpen [internal]
513 static LRESULT
WAVE_mciOpen(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSW lpOpenParms
)
516 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
518 TRACE("(%04X, %08X, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
519 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
520 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
522 if (dwFlags
& MCI_OPEN_SHAREABLE
)
523 return MCIERR_UNSUPPORTED_FUNCTION
;
525 if (wmw
->nUseCount
> 0) {
526 /* The driver is already opened on this channel
527 * Wave driver cannot be shared
529 return MCIERR_DEVICE_OPEN
;
534 wmw
->wInput
= wmw
->wOutput
= WAVE_MAPPER
;
537 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
539 wmw
->lpFileName
= NULL
; /* will be set by WAVE_mciOpenFile */
540 wmw
->hCallback
= NULL
;
541 WAVE_mciDefaultFmt(wmw
);
543 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
544 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
545 wmw
->wNotifyDeviceID
= wDevID
;
547 if (dwFlags
& MCI_OPEN_ELEMENT
) {
548 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
549 /* could it be that (DWORD)lpOpenParms->lpstrElementName
550 * contains the hFile value ?
552 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
554 dwRet
= WAVE_mciOpenFile(wmw
, lpOpenParms
->lpstrElementName
);
557 TRACE("hFile=%p\n", wmw
->hFile
);
562 wmw
->dwStatus
= MCI_MODE_STOP
;
564 if (dwFlags
& MCI_NOTIFY
)
565 WAVE_mciNotify(lpOpenParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
569 mmioClose(wmw
->hFile
, 0);
575 /**************************************************************************
576 * WAVE_mciCue [internal]
578 static DWORD
WAVE_mciCue(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
580 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
582 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
584 /* Tests on systems without sound drivers show that Cue, like
585 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
586 * The first Cue Notify does not immediately return the
587 * notification, as if a player or recorder thread is started.
588 * PAUSE mode is reported when successful, but this mode is
589 * different from the normal Pause, because a) Pause then returns
590 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
591 * still accepted, returning the original notification as ABORTED.
592 * I.e. Cue allows subsequent format changes, unlike Record or
593 * Open file, closes winmm if the format changes and stops this
595 * Wine creates one player or recorder thread per async. Play or
596 * Record command. Notification behaviour suggests that MS-W*
597 * reuses a single thread to improve response times. Having Cue
598 * start this thread early helps to improve Play/Record's initial
599 * response time. In effect, Cue is a performance hint, which
600 * justifies our almost no-op implementation.
603 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
604 if (wmw
->dwStatus
!= MCI_MODE_STOP
) return MCIERR_NONAPPLICABLE_FUNCTION
;
606 if ((dwFlags
& MCI_NOTIFY
) && lpParms
)
607 WAVE_mciNotify(lpParms
->dwCallback
,wmw
,MCI_NOTIFY_SUCCESSFUL
);
609 return MMSYSERR_NOERROR
;
612 /**************************************************************************
613 * WAVE_mciStop [internal]
615 static DWORD
WAVE_mciStop(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
618 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
620 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
622 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
624 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
625 HANDLE old
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
626 if (old
) mciDriverNotify(old
, wDevID
, MCI_NOTIFY_ABORTED
);
629 /* wait for playback thread (if any) to exit before processing further */
630 switch (wmw
->dwStatus
) {
633 case MCI_MODE_RECORD
:
635 int oldStat
= wmw
->dwStatus
;
636 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
637 if (oldStat
== MCI_MODE_PAUSE
)
638 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
640 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
646 wmw
->dwStatus
= MCI_MODE_STOP
;
648 if ((dwFlags
& MCI_NOTIFY
) && lpParms
&& MMSYSERR_NOERROR
==dwRet
)
649 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
654 /**************************************************************************
655 * WAVE_mciClose [internal]
657 static DWORD
WAVE_mciClose(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
660 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
662 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
664 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
666 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
667 /* mciStop handles MCI_NOTIFY_ABORTED */
668 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
673 if (wmw
->nUseCount
== 0) {
674 if (wmw
->hFile
!= 0) {
675 mmioClose(wmw
->hFile
, 0);
680 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
)
681 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
682 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
683 HeapFree(GetProcessHeap(), 0, (void*)wmw
->lpFileName
);
684 wmw
->lpFileName
= NULL
;
686 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
687 WAVE_mciNotify(lpParms
->dwCallback
, wmw
,
688 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
694 /**************************************************************************
695 * WAVE_mciPlayCallback [internal]
697 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
698 DWORD_PTR dwInstance
,
699 LPARAM dwParam1
, LPARAM dwParam2
)
701 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
708 InterlockedIncrement(&wmw
->dwEventCount
);
709 TRACE("Returning waveHdr=%lx\n", dwParam1
);
710 SetEvent(wmw
->hEvent
);
713 ERR("Unknown uMsg=%d\n", uMsg
);
717 /******************************************************************
718 * WAVE_mciPlayWaitDone [internal]
720 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
723 ResetEvent(wmw
->hEvent
);
724 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
727 InterlockedIncrement(&wmw
->dwEventCount
);
729 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
733 /**************************************************************************
734 * WAVE_mciPlay [internal]
736 static DWORD
WAVE_mciPlay(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
738 LPMCI_PLAY_PARMS lpParms
= (void*)pmt
;
740 LONG bufsize
, count
, left
;
742 LPWAVEHDR waveHdr
= NULL
;
743 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
747 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
749 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
750 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
752 if (wmw
->hFile
== 0) {
753 WARN("Can't play: no file=%s!\n", debugstr_w(wmw
->lpFileName
));
754 return MCIERR_FILE_NOT_FOUND
;
757 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& !wmw
->fInput
) {
758 /* FIXME: parameters (start/end) in lpParams may not be used */
759 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
762 /** This function will be called again by a thread when async is used.
763 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
764 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
766 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
))) {
767 return MCIERR_INTERNAL
;
770 if (wmw
->lpWaveFormat
->wFormatTag
== WAVE_FORMAT_PCM
) {
771 if (wmw
->lpWaveFormat
->nBlockAlign
!=
772 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
773 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
774 wmw
->lpWaveFormat
->nBlockAlign
,
775 wmw
->lpWaveFormat
->nChannels
*
776 wmw
->lpWaveFormat
->wBitsPerSample
/8);
777 wmw
->lpWaveFormat
->nBlockAlign
=
778 wmw
->lpWaveFormat
->nChannels
*
779 wmw
->lpWaveFormat
->wBitsPerSample
/8;
781 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
782 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
783 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
784 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
785 wmw
->lpWaveFormat
->nSamplesPerSec
*
786 wmw
->lpWaveFormat
->nBlockAlign
);
787 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
788 wmw
->lpWaveFormat
->nSamplesPerSec
*
789 wmw
->lpWaveFormat
->nBlockAlign
;
793 end
= wmw
->ckWaveData
.cksize
;
794 if (lpParms
&& (dwFlags
& MCI_TO
)) {
795 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
796 if (position
> end
) return MCIERR_OUTOFRANGE
;
799 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
800 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
801 if (position
> end
) return MCIERR_OUTOFRANGE
;
802 /* Seek rounds down, so do we. */
803 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
804 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
805 wmw
->dwPosition
= position
;
807 if (end
< wmw
->dwPosition
) return MCIERR_OUTOFRANGE
;
808 left
= end
- wmw
->dwPosition
;
809 if (0==left
) return MMSYSERR_NOERROR
; /* FIXME: NOTIFY */
811 wmw
->fInput
= FALSE
; /* FIXME: waveInOpen may have been called. */
812 wmw
->dwStatus
= MCI_MODE_PLAY
;
814 if (!(dwFlags
& MCI_WAIT
)) {
815 return MCI_SendCommandAsync(wDevID
, WAVE_mciPlay
, dwFlags
,
816 (DWORD_PTR
)lpParms
, sizeof(MCI_PLAY_PARMS
));
819 TRACE("Playing from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
821 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
822 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
823 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
826 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
827 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
829 /* go back to beginning of chunk plus the requested position */
830 /* FIXME: I'm not sure this is correct, notably because some data linked to
831 * the decompression state machine will not be correctly initialized.
832 * try it this way (other way would be to decompress from 0 up to dwPosition
833 * and to start sending to hWave when dwPosition is reached)
835 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
837 dwRet
= waveOutOpen((HWAVEOUT
*)&wmw
->hWave
, wmw
->wOutput
, wmw
->lpWaveFormat
,
838 (DWORD_PTR
)WAVE_mciPlayCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
841 TRACE("Can't open low level audio device %d\n", dwRet
);
842 dwRet
= MCIERR_DEVICE_OPEN
;
847 /* make it so that 3 buffers per second are needed */
848 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
850 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
851 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
852 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
853 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
854 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
855 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
856 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
857 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
858 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
859 dwRet
= MCIERR_INTERNAL
;
864 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
865 wmw
->dwEventCount
= 1L; /* for first buffer */
867 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, left
);
868 if (hEvent
) SetEvent(hEvent
);
870 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
871 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
872 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
873 TRACE("mmioRead bufsize=%d count=%d\n", bufsize
, count
);
876 /* count is always <= bufsize, so this is correct regarding the
877 * waveOutPrepareHeader function
879 waveHdr
[whidx
].dwBufferLength
= count
;
880 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
881 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
882 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
);
883 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
885 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet
);
886 dwRet
= MCIERR_HARDWARE
;
890 wmw
->dwPosition
+= count
;
891 TRACE("after WODM_WRITE dwPosition=%u\n", wmw
->dwPosition
);
892 /* InterlockedDecrement if and only if waveOutWrite is successful */
893 WAVE_mciPlayWaitDone(wmw
);
897 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
899 /* just to get rid of some race conditions between play, stop and pause */
900 waveOutReset(wmw
->hWave
);
902 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
903 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
906 if (dwFlags
& MCI_NOTIFY
)
907 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
909 HeapFree(GetProcessHeap(), 0, waveHdr
);
912 waveOutClose(wmw
->hWave
);
915 CloseHandle(wmw
->hEvent
);
917 wmw
->dwStatus
= MCI_MODE_STOP
;
919 /* Let the potentically asynchronous commands support FAILURE notification. */
920 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
921 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
926 /**************************************************************************
927 * WAVE_mciRecordCallback [internal]
929 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
930 DWORD_PTR dwInstance
,
931 LPARAM dwParam1
, LPARAM dwParam2
)
933 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
942 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
944 InterlockedIncrement(&wmw
->dwEventCount
);
946 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
948 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
950 wmw
->dwPosition
+= count
;
951 /* else error reporting ?? */
952 if (wmw
->dwStatus
== MCI_MODE_RECORD
)
954 /* Only queue up another buffer if we are recording. We could receive this
955 message also when waveInReset() is called, since it notifies on all wave
956 buffers that are outstanding. Queueing up more sometimes causes waveInClose
958 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
959 TRACE("after mmioWrite dwPosition=%u\n", wmw
->dwPosition
);
962 SetEvent(wmw
->hEvent
);
965 ERR("Unknown uMsg=%d\n", uMsg
);
969 /******************************************************************
970 * WAVE_mciRecordWaitDone [internal]
972 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
975 ResetEvent(wmw
->hEvent
);
976 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
979 InterlockedIncrement(&wmw
->dwEventCount
);
981 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
985 /**************************************************************************
986 * WAVE_mciRecord [internal]
988 static DWORD
WAVE_mciRecord(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
990 LPMCI_RECORD_PARMS lpParms
= (void*)pmt
;
992 DWORD dwRet
= MMSYSERR_NOERROR
;
994 LPWAVEHDR waveHdr
= NULL
;
995 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
998 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1000 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1001 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1003 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& wmw
->fInput
) {
1004 /* FIXME: parameters (start/end) in lpParams may not be used */
1005 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
1008 /** This function will be called again by a thread when async is used.
1009 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1010 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1012 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
))) {
1013 return MCIERR_INTERNAL
;
1016 wmw
->fInput
= TRUE
; /* FIXME: waveOutOpen may have been called. */
1017 wmw
->dwStatus
= MCI_MODE_RECORD
;
1019 if (!(dwFlags
& MCI_WAIT
)) {
1020 return MCI_SendCommandAsync(wDevID
, WAVE_mciRecord
, dwFlags
,
1021 (DWORD_PTR
)lpParms
, sizeof(MCI_RECORD_PARMS
));
1024 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1025 * we don't modify the wave part of an existing file (ie. we always erase an
1026 * existing content, we don't overwrite)
1028 HeapFree(GetProcessHeap(), 0, (void*)wmw
->lpFileName
);
1029 dwRet
= create_tmp_file(&wmw
->hFile
, (WCHAR
**)&wmw
->lpFileName
);
1030 if (dwRet
!= 0) return dwRet
;
1032 /* new RIFF file, lpWaveFormat now valid */
1033 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
1034 if (dwRet
!= 0) return dwRet
;
1036 if (lpParms
&& (dwFlags
& MCI_TO
)) {
1037 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1038 } else end
= 0xFFFFFFFF;
1039 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
1040 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
1041 if (wmw
->ckWaveData
.cksize
< position
) return MCIERR_OUTOFRANGE
;
1042 /* Seek rounds down, so do we. */
1043 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
1044 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
1045 wmw
->dwPosition
= position
;
1047 if (end
==wmw
->dwPosition
) return MMSYSERR_NOERROR
; /* FIXME: NOTIFY */
1049 TRACE("Recording from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
1051 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
1052 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
1053 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
1056 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1057 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1059 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
1061 /* Go back to the beginning of the chunk plus the requested position */
1062 /* FIXME: I'm not sure this is correct, notably because some data linked to
1063 * the decompression state machine will not be correctly initialized.
1064 * Try it this way (other way would be to decompress from 0 up to dwPosition
1065 * and to start sending to hWave when dwPosition is reached).
1067 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
1069 dwRet
= waveInOpen((HWAVEIN
*)&wmw
->hWave
, wmw
->wInput
, wmw
->lpWaveFormat
,
1070 (DWORD_PTR
)WAVE_mciRecordCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
1072 if (dwRet
!= MMSYSERR_NOERROR
) {
1073 TRACE("Can't open low level audio device %d\n", dwRet
);
1074 dwRet
= MCIERR_DEVICE_OPEN
;
1079 /* make it so that 3 buffers per second are needed */
1080 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
1082 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
1083 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
1084 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
1085 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
1086 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
1087 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1088 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1090 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1091 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1092 dwRet
= MCIERR_INTERNAL
;
1096 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1097 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1098 dwRet
= MCIERR_INTERNAL
;
1102 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1103 wmw
->dwEventCount
= 1L; /* for first buffer */
1105 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, end
- wmw
->dwPosition
);
1107 dwRet
= waveInStart(wmw
->hWave
);
1109 if (hEvent
) SetEvent(hEvent
);
1111 while (wmw
->dwPosition
< end
&& wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1112 WAVE_mciRecordWaitDone(wmw
);
1114 /* Grab callback before another thread kicks in after we change dwStatus. */
1115 if (dwFlags
& MCI_NOTIFY
) {
1116 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1117 dwFlags
&= ~MCI_NOTIFY
;
1119 /* needed so that the callback above won't add again the buffers returned by the reset */
1120 wmw
->dwStatus
= MCI_MODE_STOP
;
1122 waveInReset(wmw
->hWave
);
1124 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1125 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1130 if (dwFlags
& MCI_NOTIFY
)
1131 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1133 HeapFree(GetProcessHeap(), 0, waveHdr
);
1136 waveInClose(wmw
->hWave
);
1139 CloseHandle(wmw
->hEvent
);
1141 wmw
->dwStatus
= MCI_MODE_STOP
;
1143 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
1144 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1150 /**************************************************************************
1151 * WAVE_mciPause [internal]
1153 static DWORD
WAVE_mciPause(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1156 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1158 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1160 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1162 switch (wmw
->dwStatus
) {
1164 dwRet
= waveOutPause(wmw
->hWave
);
1165 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1166 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1167 ERR("waveOutPause error %d\n",dwRet
);
1168 dwRet
= MCIERR_INTERNAL
;
1171 case MCI_MODE_RECORD
:
1172 dwRet
= waveInStop(wmw
->hWave
);
1173 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1175 ERR("waveInStop error %d\n",dwRet
);
1176 dwRet
= MCIERR_INTERNAL
;
1179 case MCI_MODE_PAUSE
:
1180 dwRet
= MMSYSERR_NOERROR
;
1183 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1185 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1186 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1190 /**************************************************************************
1191 * WAVE_mciResume [internal]
1193 static DWORD
WAVE_mciResume(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1195 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1198 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1200 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1202 switch (wmw
->dwStatus
) {
1203 case MCI_MODE_PAUSE
:
1204 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1206 dwRet
= waveInStart(wmw
->hWave
);
1207 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_RECORD
;
1209 ERR("waveInStart error %d\n",dwRet
);
1210 dwRet
= MCIERR_INTERNAL
;
1213 dwRet
= waveOutRestart(wmw
->hWave
);
1214 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PLAY
;
1216 ERR("waveOutRestart error %d\n",dwRet
);
1217 dwRet
= MCIERR_INTERNAL
;
1222 case MCI_MODE_RECORD
:
1223 dwRet
= MMSYSERR_NOERROR
;
1226 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1228 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1229 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1233 /**************************************************************************
1234 * WAVE_mciSeek [internal]
1236 static DWORD
WAVE_mciSeek(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1238 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1239 DWORD position
, dwRet
;
1241 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1243 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1244 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1246 position
= dwFlags
& (MCI_SEEK_TO_START
|MCI_SEEK_TO_END
|MCI_TO
);
1247 if (!position
) return MCIERR_MISSING_PARAMETER
;
1248 if (position
&(position
-1)) return MCIERR_FLAGS_NOT_COMPATIBLE
;
1250 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1251 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1252 if (dwRet
!= MMSYSERR_NOERROR
) return dwRet
;
1254 if (dwFlags
& MCI_TO
) {
1255 position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1256 if (position
> wmw
->ckWaveData
.cksize
)
1257 return MCIERR_OUTOFRANGE
;
1258 } else if (dwFlags
& MCI_SEEK_TO_START
) {
1261 position
= wmw
->ckWaveData
.cksize
;
1263 /* Seek rounds down, unless at end */
1264 if (position
!= wmw
->ckWaveData
.cksize
) {
1265 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
1266 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
1268 wmw
->dwPosition
= position
;
1269 TRACE("Seeking to position=%u bytes\n", position
);
1271 if (dwFlags
& MCI_NOTIFY
)
1272 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1274 return MMSYSERR_NOERROR
;
1277 /**************************************************************************
1278 * WAVE_mciSet [internal]
1280 static DWORD
WAVE_mciSet(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_WAVE_SET_PARMS lpParms
)
1282 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1284 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1286 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1287 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1289 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1290 switch (lpParms
->dwTimeFormat
) {
1291 case MCI_FORMAT_MILLISECONDS
:
1292 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1293 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1295 case MCI_FORMAT_BYTES
:
1296 TRACE("MCI_FORMAT_BYTES !\n");
1297 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1299 case MCI_FORMAT_SAMPLES
:
1300 TRACE("MCI_FORMAT_SAMPLES !\n");
1301 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1304 WARN("Bad time format %u!\n", lpParms
->dwTimeFormat
);
1305 return MCIERR_BAD_TIME_FORMAT
;
1308 if (dwFlags
& MCI_SET_VIDEO
) {
1309 TRACE("No support for video !\n");
1310 return MCIERR_UNSUPPORTED_FUNCTION
;
1312 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1313 TRACE("No support for door open !\n");
1314 return MCIERR_UNSUPPORTED_FUNCTION
;
1316 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1317 TRACE("No support for door close !\n");
1318 return MCIERR_UNSUPPORTED_FUNCTION
;
1320 if (dwFlags
& MCI_SET_AUDIO
) {
1321 if (dwFlags
& MCI_SET_ON
) {
1322 TRACE("MCI_SET_ON audio !\n");
1323 } else if (dwFlags
& MCI_SET_OFF
) {
1324 TRACE("MCI_SET_OFF audio !\n");
1326 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1327 return MCIERR_BAD_INTEGER
;
1330 switch (lpParms
->dwAudio
)
1332 case MCI_SET_AUDIO_ALL
: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1333 case MCI_SET_AUDIO_LEFT
: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1334 case MCI_SET_AUDIO_RIGHT
: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1335 default: WARN("Unknown audio channel %u\n", lpParms
->dwAudio
); break;
1338 if (dwFlags
& MCI_WAVE_INPUT
) {
1339 TRACE("MCI_WAVE_INPUT = %d\n", lpParms
->wInput
);
1340 if (lpParms
->wInput
>= waveInGetNumDevs())
1341 return MCIERR_OUTOFRANGE
;
1342 if (wmw
->wInput
!= (WORD
)lpParms
->wInput
)
1343 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1344 wmw
->wInput
= lpParms
->wInput
;
1346 if (dwFlags
& MCI_WAVE_OUTPUT
) {
1347 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms
->wOutput
);
1348 if (lpParms
->wOutput
>= waveOutGetNumDevs())
1349 return MCIERR_OUTOFRANGE
;
1350 if (wmw
->wOutput
!= (WORD
)lpParms
->wOutput
)
1351 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1352 wmw
->wOutput
= lpParms
->wOutput
;
1354 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
) {
1355 TRACE("MCI_WAVE_SET_ANYINPUT\n");
1356 if (wmw
->wInput
!= (WORD
)lpParms
->wInput
)
1357 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1358 wmw
->wInput
= WAVE_MAPPER
;
1360 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
) {
1361 TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1362 if (wmw
->wOutput
!= (WORD
)lpParms
->wOutput
)
1363 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1364 wmw
->wOutput
= WAVE_MAPPER
;
1366 /* Set wave format parameters is refused after Open or Record.*/
1367 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
) {
1368 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms
->wFormatTag
);
1369 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1370 if (lpParms
->wFormatTag
!= WAVE_FORMAT_PCM
)
1371 return MCIERR_OUTOFRANGE
;
1373 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
) {
1374 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1375 wmw
->wfxRef
.nAvgBytesPerSec
= lpParms
->nAvgBytesPerSec
;
1376 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw
->wfxRef
.nAvgBytesPerSec
);
1378 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
) {
1379 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1380 wmw
->wfxRef
.wBitsPerSample
= lpParms
->wBitsPerSample
;
1381 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw
->wfxRef
.wBitsPerSample
);
1383 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
) {
1384 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1385 wmw
->wfxRef
.nBlockAlign
= lpParms
->nBlockAlign
;
1386 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw
->wfxRef
.nBlockAlign
);
1388 if (dwFlags
& MCI_WAVE_SET_CHANNELS
) {
1389 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1390 wmw
->wfxRef
.nChannels
= lpParms
->nChannels
;
1391 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw
->wfxRef
.nChannels
);
1393 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
) {
1394 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1395 wmw
->wfxRef
.nSamplesPerSec
= lpParms
->nSamplesPerSec
;
1396 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw
->wfxRef
.nSamplesPerSec
);
1398 if (dwFlags
& MCI_NOTIFY
)
1399 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1403 /**************************************************************************
1404 * WAVE_mciSave [internal]
1406 static DWORD
WAVE_mciSave(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMSW lpParms
)
1408 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1409 DWORD ret
= MCIERR_FILE_NOT_SAVED
, tmpRet
;
1411 TRACE("%d, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1412 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1413 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1415 if (dwFlags
& MCI_WAIT
)
1417 FIXME("MCI_WAIT not implemented\n");
1419 WAVE_mciStop(wDevID
, 0, NULL
);
1421 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1422 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1424 ret
= mmioClose(wmw
->hFile
, 0);
1428 If the destination file already exists, it has to be overwritten. (Behaviour
1429 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1430 my applications. We are making use of mmioRename, which WILL NOT overwrite
1431 the destination file (which is what Windows does, also verified in Win2K)
1432 So, lets delete the destination file before calling mmioRename. If the
1433 destination file DOESN'T exist, the delete will fail silently. Let's also be
1434 careful not to lose our previous error code.
1436 tmpRet
= GetLastError();
1437 DeleteFileW (lpParms
->lpfilename
);
1438 SetLastError(tmpRet
);
1440 /* FIXME: Open file.wav; Save; must not rename the original file.
1441 * Nor must Save a.wav; Save b.wav rename a. */
1442 if (0 == mmioRenameW(wmw
->lpFileName
, lpParms
->lpfilename
, 0, 0 )) {
1443 ret
= MMSYSERR_NOERROR
;
1446 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1447 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1449 if (ret
== MMSYSERR_NOERROR
)
1450 ret
= WAVE_mciOpenFile(wmw
, lpParms
->lpfilename
);
1455 /**************************************************************************
1456 * WAVE_mciStatus [internal]
1458 static DWORD
WAVE_mciStatus(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1460 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1463 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1464 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1465 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1467 if (dwFlags
& MCI_STATUS_ITEM
) {
1468 switch (lpParms
->dwItem
) {
1469 case MCI_STATUS_CURRENT_TRACK
:
1470 lpParms
->dwReturn
= 1;
1471 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1473 case MCI_STATUS_LENGTH
:
1475 lpParms
->dwReturn
= 0;
1476 return MCIERR_UNSUPPORTED_FUNCTION
;
1478 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1479 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
, &ret
);
1480 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1482 case MCI_STATUS_MODE
:
1483 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1484 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1485 ret
= MCI_RESOURCE_RETURNED
;
1487 case MCI_STATUS_MEDIA_PRESENT
:
1488 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1489 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1490 ret
= MCI_RESOURCE_RETURNED
;
1492 case MCI_STATUS_NUMBER_OF_TRACKS
:
1493 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1494 lpParms
->dwReturn
= 1;
1495 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms
->dwReturn
);
1497 case MCI_STATUS_POSITION
:
1499 lpParms
->dwReturn
= 0;
1500 return MCIERR_UNSUPPORTED_FUNCTION
;
1502 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1503 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1504 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
,
1506 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1507 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1509 case MCI_STATUS_READY
:
1510 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1511 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1512 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1513 ret
= MCI_RESOURCE_RETURNED
;
1515 case MCI_STATUS_TIME_FORMAT
:
1516 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, MCI_FORMAT_RETURN_BASE
+ wmw
->dwMciTimeFormat
);
1517 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1518 ret
= MCI_RESOURCE_RETURNED
;
1520 case MCI_WAVE_INPUT
:
1521 if (wmw
->wInput
!= (WORD
)WAVE_MAPPER
)
1522 lpParms
->dwReturn
= wmw
->wInput
;
1524 lpParms
->dwReturn
= MAKEMCIRESOURCE(WAVE_MAPPER
, WAVE_MAPPER_S
);
1525 ret
= MCI_RESOURCE_RETURNED
;
1527 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw
->wInput
);
1529 case MCI_WAVE_OUTPUT
:
1530 if (wmw
->wOutput
!= (WORD
)WAVE_MAPPER
)
1531 lpParms
->dwReturn
= wmw
->wOutput
;
1533 lpParms
->dwReturn
= MAKEMCIRESOURCE(WAVE_MAPPER
, WAVE_MAPPER_S
);
1534 ret
= MCI_RESOURCE_RETURNED
;
1536 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw
->wOutput
);
1538 /* It is always ok to query wave format parameters,
1539 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1540 case MCI_WAVE_STATUS_FORMATTAG
:
1541 if (wmw
->lpWaveFormat
->wFormatTag
!= WAVE_FORMAT_PCM
)
1542 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1544 lpParms
->dwReturn
= MAKEMCIRESOURCE(WAVE_FORMAT_PCM
, WAVE_FORMAT_PCM_S
);
1545 ret
= MCI_RESOURCE_RETURNED
;
1547 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms
->dwReturn
);
1549 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1550 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1551 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms
->dwReturn
);
1553 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1554 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1555 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms
->dwReturn
);
1557 case MCI_WAVE_STATUS_BLOCKALIGN
:
1558 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1559 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms
->dwReturn
);
1561 case MCI_WAVE_STATUS_CHANNELS
:
1562 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1563 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms
->dwReturn
);
1565 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1566 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1567 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms
->dwReturn
);
1569 case MCI_WAVE_STATUS_LEVEL
:
1570 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1571 lpParms
->dwReturn
= 0xAAAA5555;
1574 WARN("unknown command %08X !\n", lpParms
->dwItem
);
1575 return MCIERR_UNRECOGNIZED_COMMAND
;
1578 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1579 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1583 /**************************************************************************
1584 * WAVE_mciGetDevCaps [internal]
1586 static DWORD
WAVE_mciGetDevCaps(MCIDEVICEID wDevID
, DWORD dwFlags
,
1587 LPMCI_GETDEVCAPS_PARMS lpParms
)
1589 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1592 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1594 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1595 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1597 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1598 switch(lpParms
->dwItem
) {
1599 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1600 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1601 ret
= MCI_RESOURCE_RETURNED
;
1603 case MCI_GETDEVCAPS_HAS_AUDIO
:
1604 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1605 ret
= MCI_RESOURCE_RETURNED
;
1607 case MCI_GETDEVCAPS_HAS_VIDEO
:
1608 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1609 ret
= MCI_RESOURCE_RETURNED
;
1611 case MCI_GETDEVCAPS_USES_FILES
:
1612 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1613 ret
= MCI_RESOURCE_RETURNED
;
1615 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1616 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1617 ret
= MCI_RESOURCE_RETURNED
;
1619 case MCI_GETDEVCAPS_CAN_RECORD
:
1620 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1621 ret
= MCI_RESOURCE_RETURNED
;
1623 case MCI_GETDEVCAPS_CAN_EJECT
:
1624 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1625 ret
= MCI_RESOURCE_RETURNED
;
1627 case MCI_GETDEVCAPS_CAN_PLAY
:
1628 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1629 ret
= MCI_RESOURCE_RETURNED
;
1631 case MCI_GETDEVCAPS_CAN_SAVE
:
1632 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1633 ret
= MCI_RESOURCE_RETURNED
;
1635 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1636 lpParms
->dwReturn
= waveInGetNumDevs();
1638 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1639 lpParms
->dwReturn
= waveOutGetNumDevs();
1642 FIXME("Unknown capability (%08x) !\n", lpParms
->dwItem
);
1643 return MCIERR_UNRECOGNIZED_COMMAND
;
1646 WARN("No GetDevCaps-Item !\n");
1647 return MCIERR_UNRECOGNIZED_COMMAND
;
1649 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1650 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1654 /**************************************************************************
1655 * WAVE_mciInfo [internal]
1657 static DWORD
WAVE_mciInfo(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSW lpParms
)
1661 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1663 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1665 if (!lpParms
|| !lpParms
->lpstrReturn
)
1666 return MCIERR_NULL_PARAMETER_BLOCK
;
1669 ret
= MCIERR_INVALID_DEVICE_ID
;
1671 static const WCHAR wszAudio
[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1672 static const WCHAR wszWaveIn
[] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1673 static const WCHAR wszWaveOut
[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1675 TRACE("buf=%p, len=%u\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1677 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1678 case MCI_INFO_PRODUCT
: str
= wszAudio
; break;
1679 case MCI_INFO_FILE
: str
= wmw
->lpFileName
; break;
1680 case MCI_WAVE_INPUT
: str
= wszWaveIn
; break;
1681 case MCI_WAVE_OUTPUT
: str
= wszWaveOut
; break;
1683 WARN("Don't know this info command (%u)\n", dwFlags
);
1684 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
1688 if (strlenW(str
) + 1 > lpParms
->dwRetSize
) {
1689 ret
= MCIERR_PARAM_OVERFLOW
;
1691 lstrcpynW(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
1694 lpParms
->lpstrReturn
[0] = 0;
1696 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1697 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1701 /**************************************************************************
1702 * DriverProc (MCIWAVE.@)
1704 LRESULT CALLBACK
MCIWAVE_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
1705 LPARAM dwParam1
, LPARAM dwParam2
)
1707 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1708 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1711 case DRV_LOAD
: return 1;
1712 case DRV_FREE
: return 1;
1713 case DRV_OPEN
: return WAVE_drvOpen((LPCWSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSW
)dwParam2
);
1714 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1715 case DRV_ENABLE
: return 1;
1716 case DRV_DISABLE
: return 1;
1717 case DRV_QUERYCONFIGURE
: return 1;
1718 case DRV_CONFIGURE
: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK
); return 1;
1719 case DRV_INSTALL
: return DRVCNF_RESTART
;
1720 case DRV_REMOVE
: return DRVCNF_RESTART
;
1723 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1726 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSW
) dwParam2
);
1727 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1728 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1729 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, dwParam2
, NULL
);
1730 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, dwParam2
, NULL
);
1731 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1732 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_WAVE_SET_PARMS
) dwParam2
);
1733 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1734 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1735 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1736 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1737 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSW
) dwParam2
);
1738 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1739 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMSW
) dwParam2
);
1740 /* commands that should be supported */
1755 FIXME("Unsupported yet command [%u]\n", wMsg
);
1758 TRACE("Unsupported command [%u]\n", wMsg
);
1760 /* option which can be silenced */
1765 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1768 FIXME("is probably wrong msg [%u]\n", wMsg
);
1769 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1771 return MCIERR_UNRECOGNIZED_COMMAND
;