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
)
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
);
249 /**************************************************************************
250 * WAVE_ConvertTimeFormatToByte [internal]
252 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
256 switch (wmw
->dwMciTimeFormat
) {
257 case MCI_FORMAT_MILLISECONDS
:
258 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,1000);
259 if (ret
> wmw
->ckWaveData
.cksize
&&
260 val
== WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
))
261 ret
= wmw
->ckWaveData
.cksize
;
263 case MCI_FORMAT_BYTES
:
266 case MCI_FORMAT_SAMPLES
:
267 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,wmw
->lpWaveFormat
->nSamplesPerSec
);
270 WARN("Bad time format %u!\n", wmw
->dwMciTimeFormat
);
272 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
276 /**************************************************************************
277 * WAVE_mciReadFmt [internal]
279 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, const MMCKINFO
* pckMainRIFF
)
285 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
286 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
287 return MCIERR_INVALID_FILE
;
288 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
289 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
291 pwfx
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
292 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
294 r
= mmioRead(wmw
->hFile
, (HPSTR
)pwfx
, mmckInfo
.cksize
);
295 if (r
< sizeof(PCMWAVEFORMAT
)) {
296 HeapFree(GetProcessHeap(), 0, pwfx
);
297 return MCIERR_INVALID_FILE
;
299 TRACE("wFormatTag=%04X !\n", pwfx
->wFormatTag
);
300 TRACE("nChannels=%d\n", pwfx
->nChannels
);
301 TRACE("nSamplesPerSec=%d\n", pwfx
->nSamplesPerSec
);
302 TRACE("nAvgBytesPerSec=%d\n", pwfx
->nAvgBytesPerSec
);
303 TRACE("nBlockAlign=%d\n", pwfx
->nBlockAlign
);
304 TRACE("wBitsPerSample=%u !\n", pwfx
->wBitsPerSample
);
305 if (r
>= sizeof(WAVEFORMATEX
))
306 TRACE("cbSize=%u !\n", pwfx
->cbSize
);
307 if ((pwfx
->wFormatTag
!= WAVE_FORMAT_PCM
)
308 && (r
< sizeof(WAVEFORMATEX
) || (r
< sizeof(WAVEFORMATEX
) + pwfx
->cbSize
))) {
309 HeapFree(GetProcessHeap(), 0, pwfx
);
310 return MCIERR_INVALID_FILE
;
312 wmw
->lpWaveFormat
= pwfx
;
314 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
315 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
316 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
317 TRACE("can't find data chunk\n");
318 return MCIERR_INVALID_FILE
;
320 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
321 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
325 /**************************************************************************
326 * WAVE_mciDefaultFmt [internal]
328 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
329 * until either Open File or Record. It becomes immutable afterwards,
330 * i.e. Set wave format or channels etc. is subsequently refused.
332 static void WAVE_mciDefaultFmt(WINE_MCIWAVE
* wmw
)
334 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
335 wmw
->lpWaveFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
336 wmw
->lpWaveFormat
->nChannels
= 1;
337 wmw
->lpWaveFormat
->nSamplesPerSec
= 11025;
338 wmw
->lpWaveFormat
->nAvgBytesPerSec
= 11025;
339 wmw
->lpWaveFormat
->nBlockAlign
= 1;
340 wmw
->lpWaveFormat
->wBitsPerSample
= 8;
341 wmw
->lpWaveFormat
->cbSize
= 0;
344 /**************************************************************************
345 * WAVE_mciCreateRIFFSkeleton [internal]
347 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
349 MMCKINFO ckWaveFormat
;
350 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
351 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
353 lpckRIFF
->ckid
= FOURCC_RIFF
;
354 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
355 lpckRIFF
->cksize
= 0;
357 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckRIFF
, MMIO_CREATERIFF
))
360 ckWaveFormat
.fccType
= 0;
361 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
362 ckWaveFormat
.cksize
= sizeof(PCMWAVEFORMAT
);
364 /* Set wave format accepts PCM only, however open an
365 * existing ADPCM file, record into it and the MCI will
366 * happily save back in that format. */
367 if (wmw
->lpWaveFormat
->wFormatTag
== WAVE_FORMAT_PCM
) {
368 if (wmw
->lpWaveFormat
->nBlockAlign
!=
369 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
370 WORD size
= wmw
->lpWaveFormat
->nChannels
*
371 wmw
->lpWaveFormat
->wBitsPerSample
/8;
372 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
373 wmw
->lpWaveFormat
->nBlockAlign
, size
);
374 wmw
->lpWaveFormat
->nBlockAlign
= size
;
376 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
377 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
378 DWORD speed
= wmw
->lpWaveFormat
->nSamplesPerSec
*
379 wmw
->lpWaveFormat
->nBlockAlign
;
380 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
381 wmw
->lpWaveFormat
->nAvgBytesPerSec
, speed
);
382 wmw
->lpWaveFormat
->nAvgBytesPerSec
= speed
;
385 if (wmw
->lpWaveFormat
== &wmw
->wfxRef
) {
386 LPWAVEFORMATEX pwfx
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WAVEFORMATEX
));
387 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
388 /* Set wave format accepts PCM only so the size is known. */
389 assert(wmw
->wfxRef
.wFormatTag
== WAVE_FORMAT_PCM
);
391 wmw
->lpWaveFormat
= pwfx
;
394 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, &ckWaveFormat
, 0))
397 if (-1 == mmioWrite(wmw
->hFile
, (HPCSTR
)wmw
->lpWaveFormat
, (WAVE_FORMAT_PCM
==wmw
->lpWaveFormat
->wFormatTag
)
398 ? sizeof(PCMWAVEFORMAT
) : sizeof(WAVEFORMATEX
)+wmw
->lpWaveFormat
->cbSize
))
401 if (MMSYSERR_NOERROR
!= mmioAscend(wmw
->hFile
, &ckWaveFormat
, 0))
404 lpckWaveData
->cksize
= 0;
405 lpckWaveData
->fccType
= 0;
406 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
408 /* create data chunk */
409 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckWaveData
, 0))
415 /* mciClose takes care of wmw->lpWaveFormat. */
416 return MCIERR_INVALID_FILE
;
419 static DWORD
create_tmp_file(HMMIO
* hFile
, LPWSTR
* pszTmpFileName
)
421 WCHAR szTmpPath
[MAX_PATH
];
423 DWORD dwRet
= MMSYSERR_NOERROR
;
430 if (!GetTempPathW(sizeof(szTmpPath
)/sizeof(szTmpPath
[0]), szTmpPath
)) {
431 WARN("can't retrieve temp path!\n");
432 *pszTmpFileName
= NULL
;
433 return MCIERR_FILE_NOT_FOUND
;
436 *pszTmpFileName
= HeapAlloc(GetProcessHeap(),
438 MAX_PATH
* sizeof(WCHAR
));
439 if (!GetTempFileNameW(szTmpPath
, szPrefix
, 0, *pszTmpFileName
)) {
440 WARN("can't retrieve temp file name!\n");
441 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
442 return MCIERR_FILE_NOT_FOUND
;
445 TRACE("%s!\n", debugstr_w(*pszTmpFileName
));
447 if (*pszTmpFileName
&& (strlenW(*pszTmpFileName
) > 0)) {
449 *hFile
= mmioOpenW(*pszTmpFileName
, NULL
,
450 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
453 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName
));
454 /* temporary file could not be created. clean filename. */
455 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
456 dwRet
= MCIERR_FILE_NOT_FOUND
;
462 static LRESULT
WAVE_mciOpenFile(WINE_MCIWAVE
* wmw
, LPCWSTR filename
)
464 LRESULT dwRet
= MMSYSERR_NOERROR
;
467 fn
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename
) + 1) * sizeof(WCHAR
));
468 if (!fn
) return MCIERR_OUT_OF_MEMORY
;
469 strcpyW(fn
, filename
);
470 HeapFree(GetProcessHeap(), 0, wmw
->lpFileName
);
471 wmw
->lpFileName
= fn
;
473 if (strlenW(filename
) > 0) {
474 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
475 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename
));
477 wmw
->hFile
= mmioOpenW((LPWSTR
)filename
, NULL
,
478 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READ
);
480 if (wmw
->hFile
== 0) {
481 WARN("can't find file=%s!\n", debugstr_w(filename
));
482 dwRet
= MCIERR_FILE_NOT_FOUND
;
486 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
488 /* make sure we're at the beginning of the file */
489 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
491 /* first reading of this file. read the waveformat chunk */
492 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
493 dwRet
= MCIERR_INVALID_FILE
;
495 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
496 (LPSTR
)&(lpckMainRIFF
->ckid
),
497 (LPSTR
) &(lpckMainRIFF
->fccType
),
498 (lpckMainRIFF
->cksize
));
500 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
501 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
502 dwRet
= MCIERR_INVALID_FILE
;
504 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
512 /**************************************************************************
513 * WAVE_mciOpen [internal]
515 static LRESULT
WAVE_mciOpen(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSW lpOpenParms
)
518 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
520 TRACE("(%04X, %08X, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
521 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
522 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
524 if (dwFlags
& MCI_OPEN_SHAREABLE
)
525 return MCIERR_UNSUPPORTED_FUNCTION
;
527 if (wmw
->nUseCount
> 0) {
528 /* The driver is already opened on this channel
529 * Wave driver cannot be shared
531 return MCIERR_DEVICE_OPEN
;
536 wmw
->wInput
= wmw
->wOutput
= WAVE_MAPPER
;
539 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
541 wmw
->lpFileName
= NULL
; /* will be set by WAVE_mciOpenFile */
542 wmw
->hCallback
= NULL
;
543 WAVE_mciDefaultFmt(wmw
);
545 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
546 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
547 wmw
->wNotifyDeviceID
= wDevID
;
549 if (dwFlags
& MCI_OPEN_ELEMENT
) {
550 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
551 /* could it be that (DWORD)lpOpenParms->lpstrElementName
552 * contains the hFile value ?
554 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
556 dwRet
= WAVE_mciOpenFile(wmw
, lpOpenParms
->lpstrElementName
);
559 TRACE("hFile=%p\n", wmw
->hFile
);
564 wmw
->dwStatus
= MCI_MODE_STOP
;
566 if (dwFlags
& MCI_NOTIFY
)
567 WAVE_mciNotify(lpOpenParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
571 mmioClose(wmw
->hFile
, 0);
573 HeapFree(GetProcessHeap(), 0, wmw
->lpFileName
);
574 wmw
->lpFileName
= NULL
;
579 /**************************************************************************
580 * WAVE_mciCue [internal]
582 static DWORD
WAVE_mciCue(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
584 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
586 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
588 /* Tests on systems without sound drivers show that Cue, like
589 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
590 * The first Cue Notify does not immediately return the
591 * notification, as if a player or recorder thread is started.
592 * PAUSE mode is reported when successful, but this mode is
593 * different from the normal Pause, because a) Pause then returns
594 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
595 * still accepted, returning the original notification as ABORTED.
596 * I.e. Cue allows subsequent format changes, unlike Record or
597 * Open file, closes winmm if the format changes and stops this
599 * Wine creates one player or recorder thread per async. Play or
600 * Record command. Notification behaviour suggests that MS-W*
601 * reuses a single thread to improve response times. Having Cue
602 * start this thread early helps to improve Play/Record's initial
603 * response time. In effect, Cue is a performance hint, which
604 * justifies our almost no-op implementation.
607 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
608 if (wmw
->dwStatus
!= MCI_MODE_STOP
) return MCIERR_NONAPPLICABLE_FUNCTION
;
610 if ((dwFlags
& MCI_NOTIFY
) && lpParms
)
611 WAVE_mciNotify(lpParms
->dwCallback
,wmw
,MCI_NOTIFY_SUCCESSFUL
);
613 return MMSYSERR_NOERROR
;
616 /**************************************************************************
617 * WAVE_mciStop [internal]
619 static DWORD
WAVE_mciStop(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
622 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
624 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
626 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
628 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
629 HANDLE old
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
630 if (old
) mciDriverNotify(old
, wDevID
, MCI_NOTIFY_ABORTED
);
633 /* wait for playback thread (if any) to exit before processing further */
634 switch (wmw
->dwStatus
) {
637 case MCI_MODE_RECORD
:
639 int oldStat
= wmw
->dwStatus
;
640 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
641 if (oldStat
== MCI_MODE_PAUSE
)
642 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
644 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
650 wmw
->dwStatus
= MCI_MODE_STOP
;
652 if ((dwFlags
& MCI_NOTIFY
) && lpParms
&& MMSYSERR_NOERROR
==dwRet
)
653 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
658 /**************************************************************************
659 * WAVE_mciClose [internal]
661 static DWORD
WAVE_mciClose(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
664 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
666 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
668 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
670 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
671 /* mciStop handles MCI_NOTIFY_ABORTED */
672 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
677 if (wmw
->nUseCount
== 0) {
678 if (wmw
->hFile
!= 0) {
679 mmioClose(wmw
->hFile
, 0);
684 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
)
685 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
686 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
687 HeapFree(GetProcessHeap(), 0, wmw
->lpFileName
);
688 wmw
->lpFileName
= NULL
;
690 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
691 WAVE_mciNotify(lpParms
->dwCallback
, wmw
,
692 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
698 /**************************************************************************
699 * WAVE_mciPlayCallback [internal]
701 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
702 DWORD_PTR dwInstance
,
703 LPARAM dwParam1
, LPARAM dwParam2
)
705 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
712 InterlockedIncrement(&wmw
->dwEventCount
);
713 TRACE("Returning waveHdr=%lx\n", dwParam1
);
714 SetEvent(wmw
->hEvent
);
717 ERR("Unknown uMsg=%d\n", uMsg
);
721 /******************************************************************
722 * WAVE_mciPlayWaitDone [internal]
724 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
727 ResetEvent(wmw
->hEvent
);
728 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
731 InterlockedIncrement(&wmw
->dwEventCount
);
733 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
737 /**************************************************************************
738 * WAVE_mciPlay [internal]
740 static DWORD
WAVE_mciPlay(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
742 LPMCI_PLAY_PARMS lpParms
= (void*)pmt
;
744 LONG bufsize
, count
, left
;
746 LPWAVEHDR waveHdr
= NULL
;
747 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
751 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
753 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
754 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
756 if (wmw
->hFile
== 0) {
757 WARN("Can't play: no file=%s!\n", debugstr_w(wmw
->lpFileName
));
758 return MCIERR_FILE_NOT_FOUND
;
761 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& !wmw
->fInput
&& !(dwFlags
& (MCI_FROM
| MCI_TO
))) {
762 /* FIXME: notification is different with Resume than Play */
763 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
766 /** This function will be called again by a thread when async is used.
767 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
768 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
770 if ( !(wmw
->dwStatus
== MCI_MODE_STOP
) &&
771 !((wmw
->dwStatus
== MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
) && !wmw
->hWave
)) {
772 /* FIXME: Check FROM/TO parameters first. */
773 /* FIXME: Play; Play [notify|wait] must hook into the running player. */
774 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
775 if (dwRet
) return dwRet
;
778 if (wmw
->lpWaveFormat
->wFormatTag
== WAVE_FORMAT_PCM
) {
779 if (wmw
->lpWaveFormat
->nBlockAlign
!=
780 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
781 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
782 wmw
->lpWaveFormat
->nBlockAlign
,
783 wmw
->lpWaveFormat
->nChannels
*
784 wmw
->lpWaveFormat
->wBitsPerSample
/8);
785 wmw
->lpWaveFormat
->nBlockAlign
=
786 wmw
->lpWaveFormat
->nChannels
*
787 wmw
->lpWaveFormat
->wBitsPerSample
/8;
789 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
790 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
791 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
792 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
793 wmw
->lpWaveFormat
->nSamplesPerSec
*
794 wmw
->lpWaveFormat
->nBlockAlign
);
795 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
796 wmw
->lpWaveFormat
->nSamplesPerSec
*
797 wmw
->lpWaveFormat
->nBlockAlign
;
801 end
= wmw
->ckWaveData
.cksize
;
802 if (dwFlags
& MCI_TO
) {
803 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
804 if (position
> end
) return MCIERR_OUTOFRANGE
;
807 if (dwFlags
& MCI_FROM
) {
808 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
809 if (position
> end
) return MCIERR_OUTOFRANGE
;
810 /* Seek rounds down, so do we. */
811 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
812 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
813 wmw
->dwPosition
= position
;
815 if (end
< wmw
->dwPosition
) return MCIERR_OUTOFRANGE
;
816 left
= end
- wmw
->dwPosition
;
817 if (0==left
) return MMSYSERR_NOERROR
; /* FIXME: NOTIFY */
819 wmw
->fInput
= FALSE
; /* FIXME: waveInOpen may have been called. */
820 wmw
->dwStatus
= MCI_MODE_PLAY
;
822 if (!(dwFlags
& MCI_WAIT
)) {
823 return MCI_SendCommandAsync(wDevID
, WAVE_mciPlay
, dwFlags
,
824 (DWORD_PTR
)lpParms
, sizeof(MCI_PLAY_PARMS
));
827 TRACE("Playing from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
829 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
830 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
831 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
834 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
835 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
837 /* go back to beginning of chunk plus the requested position */
838 /* FIXME: I'm not sure this is correct, notably because some data linked to
839 * the decompression state machine will not be correctly initialized.
840 * try it this way (other way would be to decompress from 0 up to dwPosition
841 * and to start sending to hWave when dwPosition is reached)
843 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
845 dwRet
= waveOutOpen((HWAVEOUT
*)&wmw
->hWave
, wmw
->wOutput
, wmw
->lpWaveFormat
,
846 (DWORD_PTR
)WAVE_mciPlayCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
849 TRACE("Can't open low level audio device %d\n", dwRet
);
850 dwRet
= MCIERR_DEVICE_OPEN
;
855 /* make it so that 3 buffers per second are needed */
856 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
858 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
859 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
860 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
861 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
862 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
863 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
864 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
865 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
866 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
867 dwRet
= MCIERR_INTERNAL
;
872 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
874 dwRet
= MCIERR_OUT_OF_MEMORY
;
877 wmw
->dwEventCount
= 1L; /* for first buffer */
879 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, left
);
880 if (hEvent
) SetEvent(hEvent
);
882 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
883 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
884 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
885 TRACE("mmioRead bufsize=%d count=%d\n", bufsize
, count
);
888 /* count is always <= bufsize, so this is correct regarding the
889 * waveOutPrepareHeader function
891 waveHdr
[whidx
].dwBufferLength
= count
;
892 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
893 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
894 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
);
895 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
897 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet
);
898 dwRet
= MCIERR_HARDWARE
;
902 wmw
->dwPosition
+= count
;
903 TRACE("after WODM_WRITE dwPosition=%u\n", wmw
->dwPosition
);
904 /* InterlockedDecrement if and only if waveOutWrite is successful */
905 WAVE_mciPlayWaitDone(wmw
);
909 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
911 /* just to get rid of some race conditions between play, stop and pause */
912 waveOutReset(wmw
->hWave
);
914 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
915 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
918 if (dwFlags
& MCI_NOTIFY
)
919 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
921 HeapFree(GetProcessHeap(), 0, waveHdr
);
924 waveOutClose(wmw
->hWave
);
927 CloseHandle(wmw
->hEvent
);
930 wmw
->dwStatus
= MCI_MODE_STOP
;
932 /* Let the potentially asynchronous commands support FAILURE notification. */
933 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
934 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
939 /**************************************************************************
940 * WAVE_mciRecordCallback [internal]
942 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
943 DWORD_PTR dwInstance
,
944 LPARAM dwParam1
, LPARAM dwParam2
)
946 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
955 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
957 InterlockedIncrement(&wmw
->dwEventCount
);
959 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
961 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
963 wmw
->dwPosition
+= count
;
964 /* else error reporting ?? */
965 if (wmw
->dwStatus
== MCI_MODE_RECORD
)
967 /* Only queue up another buffer if we are recording. We could receive this
968 message also when waveInReset() is called, since it notifies on all wave
969 buffers that are outstanding. Queueing up more sometimes causes waveInClose
971 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
972 TRACE("after mmioWrite dwPosition=%u\n", wmw
->dwPosition
);
975 SetEvent(wmw
->hEvent
);
978 ERR("Unknown uMsg=%d\n", uMsg
);
982 /******************************************************************
983 * WAVE_mciRecordWaitDone [internal]
985 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
988 ResetEvent(wmw
->hEvent
);
989 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
992 InterlockedIncrement(&wmw
->dwEventCount
);
994 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
998 /**************************************************************************
999 * WAVE_mciRecord [internal]
1001 static DWORD
WAVE_mciRecord(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
1003 LPMCI_RECORD_PARMS lpParms
= (void*)pmt
;
1005 DWORD dwRet
= MMSYSERR_NOERROR
;
1007 LPWAVEHDR waveHdr
= NULL
;
1008 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1011 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
1013 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1014 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1016 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& wmw
->fInput
) {
1017 /* FIXME: parameters (start/end) in lpParams may not be used */
1018 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
1021 /** This function will be called again by a thread when async is used.
1022 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1023 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1025 if ( !(wmw
->dwStatus
== MCI_MODE_STOP
) &&
1026 !((wmw
->dwStatus
== MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
) && !wmw
->hWave
)) {
1027 return MCIERR_INTERNAL
;
1030 wmw
->fInput
= TRUE
; /* FIXME: waveOutOpen may have been called. */
1031 wmw
->dwStatus
= MCI_MODE_RECORD
;
1033 if (!(dwFlags
& MCI_WAIT
)) {
1034 return MCI_SendCommandAsync(wDevID
, WAVE_mciRecord
, dwFlags
,
1035 (DWORD_PTR
)lpParms
, sizeof(MCI_RECORD_PARMS
));
1038 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1039 * we don't modify the wave part of an existing file (ie. we always erase an
1040 * existing content, we don't overwrite)
1042 HeapFree(GetProcessHeap(), 0, wmw
->lpFileName
);
1043 dwRet
= create_tmp_file(&wmw
->hFile
, (WCHAR
**)&wmw
->lpFileName
);
1044 if (dwRet
!= 0) return dwRet
;
1046 /* new RIFF file, lpWaveFormat now valid */
1047 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
1048 if (dwRet
!= 0) return dwRet
;
1050 if (dwFlags
& MCI_TO
) {
1051 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1052 } else end
= 0xFFFFFFFF;
1053 if (dwFlags
& MCI_FROM
) {
1054 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
1055 if (wmw
->ckWaveData
.cksize
< position
) return MCIERR_OUTOFRANGE
;
1056 /* Seek rounds down, so do we. */
1057 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
1058 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
1059 wmw
->dwPosition
= position
;
1061 if (end
==wmw
->dwPosition
) return MMSYSERR_NOERROR
; /* FIXME: NOTIFY */
1063 TRACE("Recording from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
1065 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
1066 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
1067 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
1070 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1071 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1073 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
1075 /* Go back to the beginning of the chunk plus the requested position */
1076 /* FIXME: I'm not sure this is correct, notably because some data linked to
1077 * the decompression state machine will not be correctly initialized.
1078 * Try it this way (other way would be to decompress from 0 up to dwPosition
1079 * and to start sending to hWave when dwPosition is reached).
1081 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
1083 dwRet
= waveInOpen((HWAVEIN
*)&wmw
->hWave
, wmw
->wInput
, wmw
->lpWaveFormat
,
1084 (DWORD_PTR
)WAVE_mciRecordCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
1086 if (dwRet
!= MMSYSERR_NOERROR
) {
1087 TRACE("Can't open low level audio device %d\n", dwRet
);
1088 dwRet
= MCIERR_DEVICE_OPEN
;
1093 /* make it so that 3 buffers per second are needed */
1094 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
1096 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
1097 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
1098 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
1099 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
1100 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
1101 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1102 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1104 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1105 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1106 dwRet
= MCIERR_INTERNAL
;
1110 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1111 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1112 dwRet
= MCIERR_INTERNAL
;
1116 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1117 wmw
->dwEventCount
= 1L; /* for first buffer */
1119 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, end
- wmw
->dwPosition
);
1121 dwRet
= waveInStart(wmw
->hWave
);
1123 if (hEvent
) SetEvent(hEvent
);
1125 while (wmw
->dwPosition
< end
&& wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1126 WAVE_mciRecordWaitDone(wmw
);
1128 /* Grab callback before another thread kicks in after we change dwStatus. */
1129 if (dwFlags
& MCI_NOTIFY
) {
1130 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1131 dwFlags
&= ~MCI_NOTIFY
;
1133 /* needed so that the callback above won't add again the buffers returned by the reset */
1134 wmw
->dwStatus
= MCI_MODE_STOP
;
1136 waveInReset(wmw
->hWave
);
1138 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1139 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1144 if (dwFlags
& MCI_NOTIFY
)
1145 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1147 HeapFree(GetProcessHeap(), 0, waveHdr
);
1150 waveInClose(wmw
->hWave
);
1153 CloseHandle(wmw
->hEvent
);
1155 wmw
->dwStatus
= MCI_MODE_STOP
;
1157 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
1158 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1164 /**************************************************************************
1165 * WAVE_mciPause [internal]
1167 static DWORD
WAVE_mciPause(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1170 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1172 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1174 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1176 switch (wmw
->dwStatus
) {
1178 dwRet
= waveOutPause(wmw
->hWave
);
1179 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1180 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1181 ERR("waveOutPause error %d\n",dwRet
);
1182 dwRet
= MCIERR_INTERNAL
;
1185 case MCI_MODE_RECORD
:
1186 dwRet
= waveInStop(wmw
->hWave
);
1187 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1189 ERR("waveInStop error %d\n",dwRet
);
1190 dwRet
= MCIERR_INTERNAL
;
1193 case MCI_MODE_PAUSE
:
1194 dwRet
= MMSYSERR_NOERROR
;
1197 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1199 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1200 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1204 /**************************************************************************
1205 * WAVE_mciResume [internal]
1207 static DWORD
WAVE_mciResume(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1209 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1212 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1214 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1216 switch (wmw
->dwStatus
) {
1217 case MCI_MODE_PAUSE
:
1218 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1220 dwRet
= waveInStart(wmw
->hWave
);
1221 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_RECORD
;
1223 ERR("waveInStart error %d\n",dwRet
);
1224 dwRet
= MCIERR_INTERNAL
;
1227 dwRet
= waveOutRestart(wmw
->hWave
);
1228 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PLAY
;
1230 ERR("waveOutRestart error %d\n",dwRet
);
1231 dwRet
= MCIERR_INTERNAL
;
1236 case MCI_MODE_RECORD
:
1237 dwRet
= MMSYSERR_NOERROR
;
1240 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1242 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1243 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1247 /**************************************************************************
1248 * WAVE_mciSeek [internal]
1250 static DWORD
WAVE_mciSeek(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1252 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1253 DWORD position
, dwRet
;
1255 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1257 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1258 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1260 position
= dwFlags
& (MCI_SEEK_TO_START
|MCI_SEEK_TO_END
|MCI_TO
);
1261 if (!position
) return MCIERR_MISSING_PARAMETER
;
1262 if (position
&(position
-1)) return MCIERR_FLAGS_NOT_COMPATIBLE
;
1264 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1265 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1266 if (dwRet
!= MMSYSERR_NOERROR
) return dwRet
;
1268 if (dwFlags
& MCI_TO
) {
1269 position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1270 if (position
> wmw
->ckWaveData
.cksize
)
1271 return MCIERR_OUTOFRANGE
;
1272 } else if (dwFlags
& MCI_SEEK_TO_START
) {
1275 position
= wmw
->ckWaveData
.cksize
;
1277 /* Seek rounds down, unless at end */
1278 if (position
!= wmw
->ckWaveData
.cksize
) {
1279 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
1280 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
1282 wmw
->dwPosition
= position
;
1283 TRACE("Seeking to position=%u bytes\n", position
);
1285 if (dwFlags
& MCI_NOTIFY
)
1286 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1288 return MMSYSERR_NOERROR
;
1291 /**************************************************************************
1292 * WAVE_mciSet [internal]
1294 static DWORD
WAVE_mciSet(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_WAVE_SET_PARMS lpParms
)
1296 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1298 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1300 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1301 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1303 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1304 switch (lpParms
->dwTimeFormat
) {
1305 case MCI_FORMAT_MILLISECONDS
:
1306 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1307 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1309 case MCI_FORMAT_BYTES
:
1310 TRACE("MCI_FORMAT_BYTES !\n");
1311 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1313 case MCI_FORMAT_SAMPLES
:
1314 TRACE("MCI_FORMAT_SAMPLES !\n");
1315 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1318 WARN("Bad time format %u!\n", lpParms
->dwTimeFormat
);
1319 return MCIERR_BAD_TIME_FORMAT
;
1322 if (dwFlags
& MCI_SET_VIDEO
) {
1323 TRACE("No support for video !\n");
1324 return MCIERR_UNSUPPORTED_FUNCTION
;
1326 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1327 TRACE("No support for door open !\n");
1328 return MCIERR_UNSUPPORTED_FUNCTION
;
1330 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1331 TRACE("No support for door close !\n");
1332 return MCIERR_UNSUPPORTED_FUNCTION
;
1334 if (dwFlags
& MCI_SET_AUDIO
) {
1335 if (dwFlags
& MCI_SET_ON
) {
1336 TRACE("MCI_SET_ON audio !\n");
1337 } else if (dwFlags
& MCI_SET_OFF
) {
1338 TRACE("MCI_SET_OFF audio !\n");
1340 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1341 return MCIERR_BAD_INTEGER
;
1344 switch (lpParms
->dwAudio
)
1346 case MCI_SET_AUDIO_ALL
: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1347 case MCI_SET_AUDIO_LEFT
: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1348 case MCI_SET_AUDIO_RIGHT
: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1349 default: WARN("Unknown audio channel %u\n", lpParms
->dwAudio
); break;
1352 if (dwFlags
& MCI_WAVE_INPUT
) {
1353 TRACE("MCI_WAVE_INPUT = %d\n", lpParms
->wInput
);
1354 if (lpParms
->wInput
>= waveInGetNumDevs())
1355 return MCIERR_OUTOFRANGE
;
1356 if (wmw
->wInput
!= (WORD
)lpParms
->wInput
)
1357 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1358 wmw
->wInput
= lpParms
->wInput
;
1360 if (dwFlags
& MCI_WAVE_OUTPUT
) {
1361 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms
->wOutput
);
1362 if (lpParms
->wOutput
>= waveOutGetNumDevs())
1363 return MCIERR_OUTOFRANGE
;
1364 if (wmw
->wOutput
!= (WORD
)lpParms
->wOutput
)
1365 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1366 wmw
->wOutput
= lpParms
->wOutput
;
1368 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
) {
1369 TRACE("MCI_WAVE_SET_ANYINPUT\n");
1370 if (wmw
->wInput
!= (WORD
)lpParms
->wInput
)
1371 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1372 wmw
->wInput
= WAVE_MAPPER
;
1374 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
) {
1375 TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1376 if (wmw
->wOutput
!= (WORD
)lpParms
->wOutput
)
1377 WAVE_mciStop(wDevID
, MCI_WAIT
, NULL
);
1378 wmw
->wOutput
= WAVE_MAPPER
;
1380 /* Set wave format parameters is refused after Open or Record.*/
1381 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
) {
1382 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms
->wFormatTag
);
1383 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1384 if (lpParms
->wFormatTag
!= WAVE_FORMAT_PCM
)
1385 return MCIERR_OUTOFRANGE
;
1387 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
) {
1388 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1389 wmw
->wfxRef
.nAvgBytesPerSec
= lpParms
->nAvgBytesPerSec
;
1390 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw
->wfxRef
.nAvgBytesPerSec
);
1392 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
) {
1393 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1394 wmw
->wfxRef
.wBitsPerSample
= lpParms
->wBitsPerSample
;
1395 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw
->wfxRef
.wBitsPerSample
);
1397 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
) {
1398 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1399 wmw
->wfxRef
.nBlockAlign
= lpParms
->nBlockAlign
;
1400 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw
->wfxRef
.nBlockAlign
);
1402 if (dwFlags
& MCI_WAVE_SET_CHANNELS
) {
1403 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1404 wmw
->wfxRef
.nChannels
= lpParms
->nChannels
;
1405 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw
->wfxRef
.nChannels
);
1407 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
) {
1408 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1409 wmw
->wfxRef
.nSamplesPerSec
= lpParms
->nSamplesPerSec
;
1410 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw
->wfxRef
.nSamplesPerSec
);
1412 if (dwFlags
& MCI_NOTIFY
)
1413 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1417 /**************************************************************************
1418 * WAVE_mciSave [internal]
1420 static DWORD
WAVE_mciSave(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMSW lpParms
)
1422 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1423 DWORD ret
= MCIERR_FILE_NOT_SAVED
, tmpRet
;
1425 TRACE("%d, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1426 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1427 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1429 if (dwFlags
& MCI_WAIT
)
1431 FIXME("MCI_WAIT not implemented\n");
1433 WAVE_mciStop(wDevID
, 0, NULL
);
1435 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1436 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1438 ret
= mmioClose(wmw
->hFile
, 0);
1442 If the destination file already exists, it has to be overwritten. (Behaviour
1443 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1444 my applications. We are making use of mmioRename, which WILL NOT overwrite
1445 the destination file (which is what Windows does, also verified in Win2K)
1446 So, lets delete the destination file before calling mmioRename. If the
1447 destination file DOESN'T exist, the delete will fail silently. Let's also be
1448 careful not to lose our previous error code.
1450 tmpRet
= GetLastError();
1451 DeleteFileW (lpParms
->lpfilename
);
1452 SetLastError(tmpRet
);
1454 /* FIXME: Open file.wav; Save; must not rename the original file.
1455 * Nor must Save a.wav; Save b.wav rename a. */
1456 if (0 == mmioRenameW(wmw
->lpFileName
, lpParms
->lpfilename
, 0, 0 )) {
1457 ret
= MMSYSERR_NOERROR
;
1460 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1461 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1463 if (ret
== MMSYSERR_NOERROR
)
1464 ret
= WAVE_mciOpenFile(wmw
, lpParms
->lpfilename
);
1469 /**************************************************************************
1470 * WAVE_mciStatus [internal]
1472 static DWORD
WAVE_mciStatus(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1474 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1477 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1478 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1479 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1480 if (!(dwFlags
& MCI_STATUS_ITEM
)) return MCIERR_MISSING_PARAMETER
;
1482 if (dwFlags
& MCI_STATUS_ITEM
) {
1483 switch (lpParms
->dwItem
) {
1484 case MCI_STATUS_CURRENT_TRACK
:
1485 lpParms
->dwReturn
= 1;
1486 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1488 case MCI_STATUS_LENGTH
:
1490 lpParms
->dwReturn
= 0;
1491 return MCIERR_UNSUPPORTED_FUNCTION
;
1493 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1494 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
);
1495 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1497 case MCI_STATUS_MODE
:
1498 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1499 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1500 ret
= MCI_RESOURCE_RETURNED
;
1502 case MCI_STATUS_MEDIA_PRESENT
:
1503 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1504 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1505 ret
= MCI_RESOURCE_RETURNED
;
1507 case MCI_STATUS_NUMBER_OF_TRACKS
:
1508 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1509 lpParms
->dwReturn
= 1;
1510 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms
->dwReturn
);
1512 case MCI_STATUS_POSITION
:
1514 lpParms
->dwReturn
= 0;
1515 return MCIERR_UNSUPPORTED_FUNCTION
;
1517 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1518 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1519 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
);
1520 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1521 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1523 case MCI_STATUS_READY
:
1524 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1525 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1526 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1527 ret
= MCI_RESOURCE_RETURNED
;
1529 case MCI_STATUS_TIME_FORMAT
:
1530 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, MCI_FORMAT_RETURN_BASE
+ wmw
->dwMciTimeFormat
);
1531 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1532 ret
= MCI_RESOURCE_RETURNED
;
1534 case MCI_WAVE_INPUT
:
1535 if (wmw
->wInput
!= (WORD
)WAVE_MAPPER
)
1536 lpParms
->dwReturn
= wmw
->wInput
;
1538 lpParms
->dwReturn
= MAKEMCIRESOURCE(WAVE_MAPPER
, WAVE_MAPPER_S
);
1539 ret
= MCI_RESOURCE_RETURNED
;
1541 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw
->wInput
);
1543 case MCI_WAVE_OUTPUT
:
1544 if (wmw
->wOutput
!= (WORD
)WAVE_MAPPER
)
1545 lpParms
->dwReturn
= wmw
->wOutput
;
1547 lpParms
->dwReturn
= MAKEMCIRESOURCE(WAVE_MAPPER
, WAVE_MAPPER_S
);
1548 ret
= MCI_RESOURCE_RETURNED
;
1550 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw
->wOutput
);
1552 /* It is always ok to query wave format parameters,
1553 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1554 case MCI_WAVE_STATUS_FORMATTAG
:
1555 if (wmw
->lpWaveFormat
->wFormatTag
!= WAVE_FORMAT_PCM
)
1556 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1558 lpParms
->dwReturn
= MAKEMCIRESOURCE(WAVE_FORMAT_PCM
, WAVE_FORMAT_PCM_S
);
1559 ret
= MCI_RESOURCE_RETURNED
;
1561 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms
->dwReturn
);
1563 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1564 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1565 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms
->dwReturn
);
1567 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1568 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1569 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms
->dwReturn
);
1571 case MCI_WAVE_STATUS_BLOCKALIGN
:
1572 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1573 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms
->dwReturn
);
1575 case MCI_WAVE_STATUS_CHANNELS
:
1576 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1577 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms
->dwReturn
);
1579 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1580 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1581 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms
->dwReturn
);
1583 case MCI_WAVE_STATUS_LEVEL
:
1584 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1585 lpParms
->dwReturn
= 0xAAAA5555;
1588 WARN("unknown command %08X !\n", lpParms
->dwItem
);
1589 return MCIERR_UNSUPPORTED_FUNCTION
;
1592 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1593 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1597 /**************************************************************************
1598 * WAVE_mciGetDevCaps [internal]
1600 static DWORD
WAVE_mciGetDevCaps(MCIDEVICEID wDevID
, DWORD dwFlags
,
1601 LPMCI_GETDEVCAPS_PARMS lpParms
)
1603 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1606 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1608 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1609 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1611 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1612 switch(lpParms
->dwItem
) {
1613 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1614 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1615 ret
= MCI_RESOURCE_RETURNED
;
1617 case MCI_GETDEVCAPS_HAS_AUDIO
:
1618 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1619 ret
= MCI_RESOURCE_RETURNED
;
1621 case MCI_GETDEVCAPS_HAS_VIDEO
:
1622 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1623 ret
= MCI_RESOURCE_RETURNED
;
1625 case MCI_GETDEVCAPS_USES_FILES
:
1626 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1627 ret
= MCI_RESOURCE_RETURNED
;
1629 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1630 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1631 ret
= MCI_RESOURCE_RETURNED
;
1633 case MCI_GETDEVCAPS_CAN_RECORD
:
1634 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1635 ret
= MCI_RESOURCE_RETURNED
;
1637 case MCI_GETDEVCAPS_CAN_EJECT
:
1638 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1639 ret
= MCI_RESOURCE_RETURNED
;
1641 case MCI_GETDEVCAPS_CAN_PLAY
:
1642 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1643 ret
= MCI_RESOURCE_RETURNED
;
1645 case MCI_GETDEVCAPS_CAN_SAVE
:
1646 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1647 ret
= MCI_RESOURCE_RETURNED
;
1649 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1650 lpParms
->dwReturn
= waveInGetNumDevs();
1652 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1653 lpParms
->dwReturn
= waveOutGetNumDevs();
1656 FIXME("Unknown capability (%08x) !\n", lpParms
->dwItem
);
1657 return MCIERR_UNRECOGNIZED_COMMAND
;
1660 WARN("No GetDevCaps-Item !\n");
1661 return MCIERR_UNRECOGNIZED_COMMAND
;
1663 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1664 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1668 /**************************************************************************
1669 * WAVE_mciInfo [internal]
1671 static DWORD
WAVE_mciInfo(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSW lpParms
)
1675 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1677 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1679 if (!lpParms
|| !lpParms
->lpstrReturn
)
1680 return MCIERR_NULL_PARAMETER_BLOCK
;
1682 TRACE("buf=%p, len=%u\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1685 ret
= MCIERR_INVALID_DEVICE_ID
;
1687 static const WCHAR wszAudio
[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1688 static const WCHAR wszWaveIn
[] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1689 static const WCHAR wszWaveOut
[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1691 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1692 case MCI_INFO_PRODUCT
: str
= wszAudio
; break;
1693 case MCI_INFO_FILE
: str
= wmw
->lpFileName
; break;
1694 case MCI_WAVE_INPUT
: str
= wszWaveIn
; break;
1695 case MCI_WAVE_OUTPUT
: str
= wszWaveOut
; break;
1697 WARN("Don't know this info command (%u)\n", dwFlags
);
1698 ret
= MCIERR_UNRECOGNIZED_KEYWORD
;
1702 if (lpParms
->dwRetSize
) {
1704 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
1705 * to the number of characters written, excluding \0. */
1706 lstrcpynW(lpParms
->lpstrReturn
, str
? str
: &zero
, lpParms
->dwRetSize
);
1707 } else ret
= MCIERR_PARAM_OVERFLOW
;
1709 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1710 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1714 /**************************************************************************
1715 * DriverProc (MCIWAVE.@)
1717 LRESULT CALLBACK
MCIWAVE_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
1718 LPARAM dwParam1
, LPARAM dwParam2
)
1720 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1721 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1724 case DRV_LOAD
: return 1;
1725 case DRV_FREE
: return 1;
1726 case DRV_OPEN
: return WAVE_drvOpen((LPCWSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSW
)dwParam2
);
1727 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1728 case DRV_ENABLE
: return 1;
1729 case DRV_DISABLE
: return 1;
1730 case DRV_QUERYCONFIGURE
: return 1;
1731 case DRV_CONFIGURE
: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK
); return 1;
1732 case DRV_INSTALL
: return DRVCNF_RESTART
;
1733 case DRV_REMOVE
: return DRVCNF_RESTART
;
1736 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1739 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSW
) dwParam2
);
1740 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1741 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1742 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, dwParam2
, NULL
);
1743 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, dwParam2
, NULL
);
1744 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1745 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_WAVE_SET_PARMS
) dwParam2
);
1746 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1747 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1748 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1749 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1750 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSW
) dwParam2
);
1751 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1752 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMSW
) dwParam2
);
1753 /* commands that should be supported */
1768 FIXME("Unsupported command [%u]\n", wMsg
);
1771 TRACE("Unsupported command [%u]\n", wMsg
);
1773 /* option which can be silenced */
1778 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1781 FIXME("is probably wrong msg [%u]\n", wMsg
);
1782 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1784 return MCIERR_UNRECOGNIZED_COMMAND
;