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
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(mciwave
);
41 int nUseCount
; /* Incremented for each shared open */
42 HMMIO hFile
; /* mmio file handle open as Element */
43 MCI_WAVE_OPEN_PARMSW openParms
;
44 HANDLE hCallback
; /* Callback handle for pending notification */
46 LPWAVEFORMATEX lpWaveFormat
; /* Points to wfxRef until set by OPEN or RECORD */
47 BOOL fInput
; /* FALSE = Output, TRUE = Input */
48 volatile WORD dwStatus
; /* one from MCI_MODE_xxxx */
49 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
50 DWORD dwPosition
; /* position in bytes in chunk */
51 HANDLE hEvent
; /* for synchronization */
52 LONG dwEventCount
; /* for synchronization */
53 MMCKINFO ckMainRIFF
; /* main RIFF chunk */
54 MMCKINFO ckWaveData
; /* data chunk */
57 /* ===================================================================
58 * ===================================================================
59 * FIXME: should be using the new mmThreadXXXX functions from WINMM
61 * it would require to add a wine internal flag to mmThreadCreate
62 * in order to pass a 32 bit function instead of a 16 bit one
63 * ===================================================================
64 * =================================================================== */
66 typedef DWORD (*async_cmd
)(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE evt
);
76 /**************************************************************************
77 * MCI_SCAStarter [internal]
79 static DWORD CALLBACK
MCI_SCAStarter(LPVOID arg
)
81 struct SCA
* sca
= (struct SCA
*)arg
;
84 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
85 sca
->wDevID
, sca
->dwParam1
, sca
->dwParam2
);
86 ret
= sca
->cmd(sca
->wDevID
, sca
->dwParam1
| MCI_WAIT
, sca
->dwParam2
, sca
->evt
);
87 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
88 sca
->wDevID
, sca
->dwParam1
, sca
->dwParam2
);
89 HeapFree(GetProcessHeap(), 0, sca
);
93 /**************************************************************************
94 * MCI_SendCommandAsync [internal]
96 static DWORD
MCI_SendCommandAsync(UINT wDevID
, async_cmd cmd
, DWORD_PTR dwParam1
,
97 DWORD_PTR dwParam2
, UINT size
)
100 struct SCA
* sca
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA
) + size
);
103 return MCIERR_OUT_OF_MEMORY
;
105 sca
->wDevID
= wDevID
;
107 sca
->dwParam1
= dwParam1
;
109 if (size
&& dwParam2
) {
110 sca
->dwParam2
= (DWORD_PTR
)sca
+ sizeof(struct SCA
);
111 /* copy structure passed by program in dwParam2 to be sure
112 * we can still use it whatever the program does
114 memcpy((LPVOID
)sca
->dwParam2
, (LPVOID
)dwParam2
, size
);
116 sca
->dwParam2
= dwParam2
;
119 if ((sca
->evt
= handles
[1] = CreateEventW(NULL
, FALSE
, FALSE
, NULL
)) == NULL
||
120 (handles
[0] = CreateThread(NULL
, 0, MCI_SCAStarter
, sca
, 0, NULL
)) == 0) {
121 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
122 if (handles
[1]) CloseHandle(handles
[1]);
124 return MCI_SCAStarter(&sca
);
127 SetThreadPriority(handles
[0], THREAD_PRIORITY_TIME_CRITICAL
);
128 /* wait until either:
129 * - the thread has finished (handles[0], likely an error)
130 * - init phase of async command is done (handles[1])
132 WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
133 CloseHandle(handles
[0]);
134 CloseHandle(handles
[1]);
138 /*======================================================================*
139 * MCI WAVE implementation *
140 *======================================================================*/
142 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
144 /**************************************************************************
145 * MCIWAVE_drvOpen [internal]
147 static LRESULT
WAVE_drvOpen(LPCWSTR str
, LPMCI_OPEN_DRIVER_PARMSW modp
)
151 if (modp
== NULL
) return 0xFFFFFFFF;
153 wmw
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCIWAVE
));
158 wmw
->wDevID
= modp
->wDeviceID
;
159 mciSetDriverData(wmw
->wDevID
, (DWORD_PTR
)wmw
);
160 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
161 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
163 wmw
->wfxRef
.wFormatTag
= WAVE_FORMAT_PCM
;
164 wmw
->wfxRef
.nChannels
= 1; /* MONO */
165 wmw
->wfxRef
.nSamplesPerSec
= 11025;
166 wmw
->wfxRef
.nAvgBytesPerSec
= 11025;
167 wmw
->wfxRef
.nBlockAlign
= 1;
168 wmw
->wfxRef
.wBitsPerSample
= 8;
169 wmw
->wfxRef
.cbSize
= 0; /* don't care */
171 return modp
->wDeviceID
;
174 /**************************************************************************
175 * MCIWAVE_drvClose [internal]
177 static LRESULT
WAVE_drvClose(MCIDEVICEID dwDevID
)
179 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(dwDevID
);
182 HeapFree(GetProcessHeap(), 0, wmw
);
183 mciSetDriverData(dwDevID
, 0);
186 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
189 /**************************************************************************
190 * WAVE_mciGetOpenDev [internal]
192 static WINE_MCIWAVE
*WAVE_mciGetOpenDev(MCIDEVICEID wDevID
)
194 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
196 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
197 WARN("Invalid wDevID=%u\n", wDevID
);
203 /**************************************************************************
204 * WAVE_mciNotify [internal]
206 * Notifications in MCI work like a 1-element queue.
207 * Each new notification request supersedes the previous one.
208 * This affects Play and Record; other commands are immediate.
210 static void WAVE_mciNotify(DWORD_PTR hWndCallBack
, WINE_MCIWAVE
* wmw
, UINT wStatus
)
212 MCIDEVICEID wDevID
= wmw
->openParms
.wDeviceID
;
213 HANDLE old
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
214 if (old
) mciDriverNotify(old
, wDevID
, MCI_NOTIFY_SUPERSEDED
);
215 mciDriverNotify(HWND_32(LOWORD(hWndCallBack
)), wDevID
, wStatus
);
218 /**************************************************************************
219 * WAVE_ConvertByteToTimeFormat [internal]
221 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
, LPDWORD lpRet
)
225 switch (wmw
->dwMciTimeFormat
) {
226 case MCI_FORMAT_MILLISECONDS
:
227 ret
= MulDiv(val
,1000,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
229 case MCI_FORMAT_BYTES
:
232 case MCI_FORMAT_SAMPLES
:
233 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nSamplesPerSec
,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
236 WARN("Bad time format %u!\n", wmw
->dwMciTimeFormat
);
238 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
243 /**************************************************************************
244 * WAVE_ConvertTimeFormatToByte [internal]
246 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
250 switch (wmw
->dwMciTimeFormat
) {
251 case MCI_FORMAT_MILLISECONDS
:
252 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,1000);
254 case MCI_FORMAT_BYTES
:
257 case MCI_FORMAT_SAMPLES
:
258 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,wmw
->lpWaveFormat
->nSamplesPerSec
);
261 WARN("Bad time format %u!\n", wmw
->dwMciTimeFormat
);
263 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
267 /**************************************************************************
268 * WAVE_mciReadFmt [internal]
270 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, const MMCKINFO
* pckMainRIFF
)
275 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
276 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
277 return MCIERR_INVALID_FILE
;
278 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
279 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
282 LPWAVEFORMATEX pwfx
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
283 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
284 wmw
->lpWaveFormat
= pwfx
;
286 r
= mmioRead(wmw
->hFile
, (HPSTR
)wmw
->lpWaveFormat
, mmckInfo
.cksize
);
287 if (r
< sizeof(PCMWAVEFORMAT
))
288 return MCIERR_INVALID_FILE
;
290 TRACE("wFormatTag=%04X !\n", wmw
->lpWaveFormat
->wFormatTag
);
291 TRACE("nChannels=%d\n", wmw
->lpWaveFormat
->nChannels
);
292 TRACE("nSamplesPerSec=%d\n", wmw
->lpWaveFormat
->nSamplesPerSec
);
293 TRACE("nAvgBytesPerSec=%d\n", wmw
->lpWaveFormat
->nAvgBytesPerSec
);
294 TRACE("nBlockAlign=%d\n", wmw
->lpWaveFormat
->nBlockAlign
);
295 TRACE("wBitsPerSample=%u !\n", wmw
->lpWaveFormat
->wBitsPerSample
);
296 if (r
>= (long)sizeof(WAVEFORMATEX
))
297 TRACE("cbSize=%u !\n", wmw
->lpWaveFormat
->cbSize
);
299 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
300 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
301 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
302 TRACE("can't find data chunk\n");
303 return MCIERR_INVALID_FILE
;
305 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
306 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
307 TRACE("nChannels=%d nSamplesPerSec=%d\n",
308 wmw
->lpWaveFormat
->nChannels
, wmw
->lpWaveFormat
->nSamplesPerSec
);
313 /**************************************************************************
314 * WAVE_mciDefaultFmt [internal]
316 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
317 * until either Open File or Record. It becomes immutable afterwards,
318 * i.e. Set wave format or channels etc. is subsequently refused.
320 static void WAVE_mciDefaultFmt(WINE_MCIWAVE
* wmw
)
322 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
323 wmw
->lpWaveFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
324 wmw
->lpWaveFormat
->nChannels
= 1;
325 wmw
->lpWaveFormat
->nSamplesPerSec
= 11025;
326 wmw
->lpWaveFormat
->nAvgBytesPerSec
= 11025;
327 wmw
->lpWaveFormat
->nBlockAlign
= 1;
328 wmw
->lpWaveFormat
->wBitsPerSample
= 8;
329 wmw
->lpWaveFormat
->cbSize
= 0;
332 /**************************************************************************
333 * WAVE_mciCreateRIFFSkeleton [internal]
335 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
337 MMCKINFO ckWaveFormat
;
338 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
339 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
341 lpckRIFF
->ckid
= FOURCC_RIFF
;
342 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
343 lpckRIFF
->cksize
= 0;
345 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckRIFF
, MMIO_CREATERIFF
))
348 ckWaveFormat
.fccType
= 0;
349 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
350 ckWaveFormat
.cksize
= sizeof(PCMWAVEFORMAT
);
352 /* FIXME: Set wave format accepts PCM only, however open an
353 * existing ADPCM file, record into it and the MCI will
354 * happily save back in that format. */
355 if (wmw
->lpWaveFormat
->wFormatTag
!= WAVE_FORMAT_PCM
)
358 if (wmw
->lpWaveFormat
->nBlockAlign
!=
359 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
360 WORD size
= wmw
->lpWaveFormat
->nChannels
*
361 wmw
->lpWaveFormat
->wBitsPerSample
/8;
362 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
363 wmw
->lpWaveFormat
->nBlockAlign
, size
);
364 wmw
->lpWaveFormat
->nBlockAlign
= size
;
366 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
367 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
368 DWORD speed
= wmw
->lpWaveFormat
->nSamplesPerSec
*
369 wmw
->lpWaveFormat
->nBlockAlign
;
370 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
371 wmw
->lpWaveFormat
->nAvgBytesPerSec
, speed
);
372 wmw
->lpWaveFormat
->nAvgBytesPerSec
= speed
;
375 if (wmw
->lpWaveFormat
== &wmw
->wfxRef
) {
376 LPWAVEFORMATEX pwfx
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WAVEFORMATEX
));
377 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
379 wmw
->lpWaveFormat
= pwfx
;
382 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, &ckWaveFormat
, 0))
385 if (-1 == mmioWrite(wmw
->hFile
, (HPCSTR
)wmw
->lpWaveFormat
, sizeof(PCMWAVEFORMAT
)))
388 if (MMSYSERR_NOERROR
!= mmioAscend(wmw
->hFile
, &ckWaveFormat
, 0))
391 lpckWaveData
->cksize
= 0;
392 lpckWaveData
->fccType
= 0;
393 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
395 /* create data chunk */
396 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckWaveData
, 0))
402 /* mciClose takes care of wmw->lpWaveFormat. */
403 return MCIERR_INVALID_FILE
;
406 static DWORD
create_tmp_file(HMMIO
* hFile
, LPWSTR
* pszTmpFileName
)
408 WCHAR szTmpPath
[MAX_PATH
];
410 DWORD dwRet
= MMSYSERR_NOERROR
;
417 if (!GetTempPathW(sizeof(szTmpPath
)/sizeof(szTmpPath
[0]), szTmpPath
)) {
418 WARN("can't retrieve temp path!\n");
419 return MCIERR_FILE_NOT_FOUND
;
422 *pszTmpFileName
= HeapAlloc(GetProcessHeap(),
424 MAX_PATH
* sizeof(WCHAR
));
425 if (!GetTempFileNameW(szTmpPath
, szPrefix
, 0, *pszTmpFileName
)) {
426 WARN("can't retrieve temp file name!\n");
427 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
428 return MCIERR_FILE_NOT_FOUND
;
431 TRACE("%s!\n", debugstr_w(*pszTmpFileName
));
433 if (*pszTmpFileName
&& (strlenW(*pszTmpFileName
) > 0)) {
435 *hFile
= mmioOpenW(*pszTmpFileName
, NULL
,
436 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
439 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName
));
440 /* temporary file could not be created. clean filename. */
441 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
442 dwRet
= MCIERR_FILE_NOT_FOUND
;
448 static LRESULT
WAVE_mciOpenFile(WINE_MCIWAVE
* wmw
, const WCHAR
* filename
)
450 LRESULT dwRet
= MMSYSERR_NOERROR
;
453 fn
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename
) + 1) * sizeof(WCHAR
));
454 if (!fn
) return MCIERR_OUT_OF_MEMORY
;
455 strcpyW(fn
, filename
);
456 HeapFree(GetProcessHeap(), 0, (void*)wmw
->openParms
.lpstrElementName
);
457 wmw
->openParms
.lpstrElementName
= fn
;
459 if (strlenW(filename
) > 0) {
460 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
461 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename
));
463 wmw
->hFile
= mmioOpenW((LPWSTR
)filename
, NULL
,
464 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READ
);
466 if (wmw
->hFile
== 0) {
467 WARN("can't find file=%s!\n", debugstr_w(filename
));
468 dwRet
= MCIERR_FILE_NOT_FOUND
;
472 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
474 /* make sure we're are the beginning of the file */
475 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
477 /* first reading of this file. read the waveformat chunk */
478 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
479 dwRet
= MCIERR_INVALID_FILE
;
481 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
482 (LPSTR
)&(lpckMainRIFF
->ckid
),
483 (LPSTR
) &(lpckMainRIFF
->fccType
),
484 (lpckMainRIFF
->cksize
));
486 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
487 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
488 dwRet
= MCIERR_INVALID_FILE
;
490 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
498 /**************************************************************************
499 * WAVE_mciOpen [internal]
501 static LRESULT
WAVE_mciOpen(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSW lpOpenParms
)
504 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
506 TRACE("(%04X, %08X, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
507 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
508 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
510 if (dwFlags
& MCI_OPEN_SHAREABLE
)
511 return MCIERR_UNSUPPORTED_FUNCTION
;
513 if (wmw
->nUseCount
> 0) {
514 /* The driver is already opened on this channel
515 * Wave driver cannot be shared
517 return MCIERR_DEVICE_OPEN
;
524 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
526 memcpy(&wmw
->openParms
, lpOpenParms
, sizeof(MCI_WAVE_OPEN_PARMSA
));
527 /* will be set by WAVE_mciOpenFile */
528 wmw
->openParms
.lpstrElementName
= NULL
;
529 wmw
->hCallback
= NULL
;
530 WAVE_mciDefaultFmt(wmw
);
532 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
534 if (dwFlags
& MCI_OPEN_ELEMENT
) {
535 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
536 /* could it be that (DWORD)lpOpenParms->lpstrElementName
537 * contains the hFile value ?
539 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
541 dwRet
= WAVE_mciOpenFile(wmw
, lpOpenParms
->lpstrElementName
);
544 TRACE("hFile=%p\n", wmw
->hFile
);
549 wmw
->dwStatus
= MCI_MODE_STOP
;
551 if (dwFlags
& MCI_NOTIFY
)
552 WAVE_mciNotify(lpOpenParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
556 mmioClose(wmw
->hFile
, 0);
562 /**************************************************************************
563 * WAVE_mciCue [internal]
565 static DWORD
WAVE_mciCue(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
567 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
569 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
571 /* Tests on systems without sound drivers show that Cue, like
572 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
573 * The first Cue Notify does not immediately return the
574 * notification, as if a player or recorder thread is started.
575 * PAUSE mode is reported when successful, but this mode is
576 * different from the normal Pause, because a) Pause then returns
577 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
578 * still accepted, returning the original notification as ABORTED.
579 * I.e. Cue allows subsequent format changes, unlike Record or
580 * Open file, closes winmm if the format changes and stops this
582 * Wine creates one player or recorder thread per async. Play or
583 * Record command. Notification behaviour suggests that MS-W*
584 * reuses a single thread to improve response times. Having Cue
585 * start this thread early helps to improve Play/Record's initial
586 * response time. In effect, Cue is a performance hint, which
587 * justifies our almost no-op implementation.
590 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
591 if (wmw
->dwStatus
!= MCI_MODE_STOP
) return MCIERR_NONAPPLICABLE_FUNCTION
;
593 if ((dwFlags
& MCI_NOTIFY
) && lpParms
)
594 WAVE_mciNotify(lpParms
->dwCallback
,wmw
,MCI_NOTIFY_SUCCESSFUL
);
596 return MMSYSERR_NOERROR
;
599 /**************************************************************************
600 * WAVE_mciStop [internal]
602 static DWORD
WAVE_mciStop(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
605 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
607 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
609 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
611 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
612 HANDLE old
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
613 if (old
) mciDriverNotify(old
, wDevID
, MCI_NOTIFY_ABORTED
);
616 /* wait for playback thread (if any) to exit before processing further */
617 switch (wmw
->dwStatus
) {
620 case MCI_MODE_RECORD
:
622 int oldStat
= wmw
->dwStatus
;
623 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
624 if (oldStat
== MCI_MODE_PAUSE
)
625 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
627 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
633 wmw
->dwStatus
= MCI_MODE_STOP
;
635 if ((dwFlags
& MCI_NOTIFY
) && lpParms
&& MMSYSERR_NOERROR
==dwRet
)
636 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
641 /**************************************************************************
642 * WAVE_mciClose [internal]
644 static DWORD
WAVE_mciClose(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
647 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
649 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
651 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
653 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
654 /* mciStop handles MCI_NOTIFY_ABORTED */
655 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
660 if (wmw
->nUseCount
== 0) {
661 if (wmw
->hFile
!= 0) {
662 mmioClose(wmw
->hFile
, 0);
667 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
)
668 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
669 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
670 HeapFree(GetProcessHeap(), 0, (void*)wmw
->openParms
.lpstrElementName
);
671 wmw
->openParms
.lpstrElementName
= NULL
;
673 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
674 WAVE_mciNotify(lpParms
->dwCallback
, wmw
,
675 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
681 /**************************************************************************
682 * WAVE_mciPlayCallback [internal]
684 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
685 DWORD_PTR dwInstance
,
686 LPARAM dwParam1
, LPARAM dwParam2
)
688 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
695 InterlockedIncrement(&wmw
->dwEventCount
);
696 TRACE("Returning waveHdr=%lx\n", dwParam1
);
697 SetEvent(wmw
->hEvent
);
700 ERR("Unknown uMsg=%d\n", uMsg
);
704 /******************************************************************
705 * WAVE_mciPlayWaitDone [internal]
707 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
710 ResetEvent(wmw
->hEvent
);
711 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
714 InterlockedIncrement(&wmw
->dwEventCount
);
716 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
720 /**************************************************************************
721 * WAVE_mciPlay [internal]
723 static DWORD
WAVE_mciPlay(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
725 LPMCI_PLAY_PARMS lpParms
= (void*)pmt
;
727 LONG bufsize
, count
, left
;
729 LPWAVEHDR waveHdr
= NULL
;
730 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
734 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
736 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
737 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
739 if (wmw
->hFile
== 0) {
740 WARN("Can't play: no file=%s!\n", debugstr_w(wmw
->openParms
.lpstrElementName
));
741 return MCIERR_FILE_NOT_FOUND
;
744 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& !wmw
->fInput
) {
745 /* FIXME: parameters (start/end) in lpParams may not be used */
746 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
751 /** This function will be called again by a thread when async is used.
752 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
753 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
755 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
))) {
756 return MCIERR_INTERNAL
;
759 wmw
->dwStatus
= MCI_MODE_PLAY
;
761 if (!(dwFlags
& MCI_WAIT
)) {
762 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, WAVE_mciPlay
, dwFlags
,
763 (DWORD_PTR
)lpParms
, sizeof(MCI_PLAY_PARMS
));
766 if (wmw
->lpWaveFormat
->wFormatTag
== WAVE_FORMAT_PCM
) {
767 if (wmw
->lpWaveFormat
->nBlockAlign
!=
768 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
769 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
770 wmw
->lpWaveFormat
->nBlockAlign
,
771 wmw
->lpWaveFormat
->nChannels
*
772 wmw
->lpWaveFormat
->wBitsPerSample
/8);
773 wmw
->lpWaveFormat
->nBlockAlign
=
774 wmw
->lpWaveFormat
->nChannels
*
775 wmw
->lpWaveFormat
->wBitsPerSample
/8;
777 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
778 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
779 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
780 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
781 wmw
->lpWaveFormat
->nSamplesPerSec
*
782 wmw
->lpWaveFormat
->nBlockAlign
);
783 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
784 wmw
->lpWaveFormat
->nSamplesPerSec
*
785 wmw
->lpWaveFormat
->nBlockAlign
;
790 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
791 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
793 if (lpParms
&& (dwFlags
& MCI_TO
)) {
794 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
797 TRACE("Playing from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
799 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
800 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
801 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
804 if (end
<= wmw
->dwPosition
)
805 return MMSYSERR_NOERROR
;
808 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
809 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
811 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
812 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
815 /* go back to beginning of chunk plus the requested position */
816 /* FIXME: I'm not sure this is correct, notably because some data linked to
817 * the decompression state machine will not be correctly initialized.
818 * try it this way (other way would be to decompress from 0 up to dwPosition
819 * and to start sending to hWave when dwPosition is reached)
821 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
823 /* By default the device will be opened for output, the MCI_CUE function is there to
824 * change from output to input and back
826 /* FIXME: how to choose between several output channels ? here mapper is forced */
827 dwRet
= waveOutOpen((HWAVEOUT
*)&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
828 (DWORD_PTR
)WAVE_mciPlayCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
831 TRACE("Can't open low level audio device %d\n", dwRet
);
832 dwRet
= MCIERR_DEVICE_OPEN
;
837 /* make it so that 3 buffers per second are needed */
838 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
840 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
841 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
842 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
843 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
844 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
845 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
846 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
847 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
848 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
849 dwRet
= MCIERR_INTERNAL
;
854 left
= min(wmw
->ckWaveData
.cksize
, end
- wmw
->dwPosition
);
855 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
856 wmw
->dwEventCount
= 1L; /* for first buffer */
858 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, left
);
859 if (hEvent
) SetEvent(hEvent
);
861 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
862 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
863 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
864 TRACE("mmioRead bufsize=%d count=%d\n", bufsize
, count
);
867 /* count is always <= bufsize, so this is correct regarding the
868 * waveOutPrepareHeader function
870 waveHdr
[whidx
].dwBufferLength
= count
;
871 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
872 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
873 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
,
874 waveHdr
[whidx
].dwBytesRecorded
);
875 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
877 wmw
->dwPosition
+= count
;
878 TRACE("after WODM_WRITE dwPosition=%u\n", wmw
->dwPosition
);
880 WAVE_mciPlayWaitDone(wmw
);
884 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
886 /* just to get rid of some race conditions between play, stop and pause */
887 waveOutReset(wmw
->hWave
);
889 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
890 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
895 if (dwFlags
& MCI_NOTIFY
)
896 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
898 HeapFree(GetProcessHeap(), 0, waveHdr
);
901 waveOutClose(wmw
->hWave
);
904 CloseHandle(wmw
->hEvent
);
906 wmw
->dwStatus
= MCI_MODE_STOP
;
908 /* Let the potentically asynchronous commands support FAILURE notification. */
909 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
910 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
915 /**************************************************************************
916 * WAVE_mciRecordCallback [internal]
918 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
919 DWORD_PTR dwInstance
,
920 LPARAM dwParam1
, LPARAM dwParam2
)
922 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
931 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
933 InterlockedIncrement(&wmw
->dwEventCount
);
935 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
937 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
939 wmw
->dwPosition
+= count
;
940 /* else error reporting ?? */
941 if (wmw
->dwStatus
== MCI_MODE_RECORD
)
943 /* Only queue up another buffer if we are recording. We could receive this
944 message also when waveInReset() is called, since it notifies on all wave
945 buffers that are outstanding. Queueing up more sometimes causes waveInClose
947 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
948 TRACE("after mmioWrite dwPosition=%u\n", wmw
->dwPosition
);
951 SetEvent(wmw
->hEvent
);
954 ERR("Unknown uMsg=%d\n", uMsg
);
958 /******************************************************************
959 * WAVE_mciRecordWaitDone [internal]
961 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
964 ResetEvent(wmw
->hEvent
);
965 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
968 InterlockedIncrement(&wmw
->dwEventCount
);
970 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
974 /**************************************************************************
975 * WAVE_mciRecord [internal]
977 static DWORD
WAVE_mciRecord(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
979 LPMCI_RECORD_PARMS lpParms
= (void*)pmt
;
981 DWORD dwRet
= MMSYSERR_NOERROR
;
983 LPWAVEHDR waveHdr
= NULL
;
984 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
987 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
989 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
990 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
992 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& wmw
->fInput
) {
993 /* FIXME: parameters (start/end) in lpParams may not be used */
994 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
997 /* FIXME : since there is no way to determine in which mode the device is
998 * open (recording/playback) automatically switch from a mode to another
1002 /** This function will be called again by a thread when async is used.
1003 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1004 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1006 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
))) {
1007 return MCIERR_INTERNAL
;
1010 wmw
->dwStatus
= MCI_MODE_RECORD
;
1012 if (!(dwFlags
& MCI_WAIT
)) {
1013 return MCI_SendCommandAsync(wmw
->openParms
.wDeviceID
, WAVE_mciRecord
, dwFlags
,
1014 (DWORD_PTR
)lpParms
, sizeof(MCI_RECORD_PARMS
));
1017 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1018 * we don't modify the wave part of an existing file (ie. we always erase an
1019 * existing content, we don't overwrite)
1021 HeapFree(GetProcessHeap(), 0, (void*)wmw
->openParms
.lpstrElementName
);
1022 dwRet
= create_tmp_file(&wmw
->hFile
, (WCHAR
**)&wmw
->openParms
.lpstrElementName
);
1023 if (dwRet
!= 0) return dwRet
;
1026 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
1027 if (dwRet
!= 0) return dwRet
;
1030 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
1031 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
1034 if (lpParms
&& (dwFlags
& MCI_TO
)) {
1035 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1038 TRACE("Recording from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
1040 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
1041 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
1042 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
1045 if (end
<= wmw
->dwPosition
)
1047 return MMSYSERR_NOERROR
;
1050 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1051 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1053 wmw
->dwPosition
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->dwPosition
);
1054 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
1056 /* Go back to the beginning of the chunk plus the requested position */
1057 /* FIXME: I'm not sure this is correct, notably because some data linked to
1058 * the decompression state machine will not be correctly initialized.
1059 * Try it this way (other way would be to decompress from 0 up to dwPosition
1060 * and to start sending to hWave when dwPosition is reached).
1062 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
1064 /* By default the device will be opened for output, the MCI_CUE function is there to
1065 * change from output to input and back
1067 /* FIXME: how to choose between several output channels ? here mapper is forced */
1068 dwRet
= waveInOpen((HWAVEIN
*)&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
1069 (DWORD_PTR
)WAVE_mciRecordCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
1071 if (dwRet
!= MMSYSERR_NOERROR
) {
1072 TRACE("Can't open low level audio device %d\n", dwRet
);
1073 dwRet
= MCIERR_DEVICE_OPEN
;
1078 /* make it so that 3 buffers per second are needed */
1079 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
1081 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
1082 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
1083 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
1084 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
1085 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
1086 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1087 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1089 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1090 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1091 dwRet
= MCIERR_INTERNAL
;
1095 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1096 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1097 dwRet
= MCIERR_INTERNAL
;
1101 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1102 wmw
->dwEventCount
= 1L; /* for first buffer */
1104 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, end
- wmw
->dwPosition
);
1106 dwRet
= waveInStart(wmw
->hWave
);
1108 if (hEvent
) SetEvent(hEvent
);
1110 while (wmw
->dwPosition
< end
&& wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1111 WAVE_mciRecordWaitDone(wmw
);
1113 /* Grab callback before another thread kicks in after we change dwStatus. */
1114 if (dwFlags
& MCI_NOTIFY
) {
1115 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1116 dwFlags
&= ~MCI_NOTIFY
;
1118 /* needed so that the callback above won't add again the buffers returned by the reset */
1119 wmw
->dwStatus
= MCI_MODE_STOP
;
1121 waveInReset(wmw
->hWave
);
1123 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1124 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1129 if (dwFlags
& MCI_NOTIFY
)
1130 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1132 HeapFree(GetProcessHeap(), 0, waveHdr
);
1135 waveInClose(wmw
->hWave
);
1138 CloseHandle(wmw
->hEvent
);
1140 wmw
->dwStatus
= MCI_MODE_STOP
;
1142 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
1143 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1149 /**************************************************************************
1150 * WAVE_mciPause [internal]
1152 static DWORD
WAVE_mciPause(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1155 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1157 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1159 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1161 switch (wmw
->dwStatus
) {
1163 dwRet
= waveOutPause(wmw
->hWave
);
1164 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1165 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1166 ERR("waveOutPause error %d\n",dwRet
);
1167 dwRet
= MCIERR_INTERNAL
;
1170 case MCI_MODE_RECORD
:
1171 dwRet
= waveInStop(wmw
->hWave
);
1172 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1174 ERR("waveInStop error %d\n",dwRet
);
1175 dwRet
= MCIERR_INTERNAL
;
1178 case MCI_MODE_PAUSE
:
1179 dwRet
= MMSYSERR_NOERROR
;
1182 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1184 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1185 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1189 /**************************************************************************
1190 * WAVE_mciResume [internal]
1192 static DWORD
WAVE_mciResume(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1194 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1197 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1199 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1201 switch (wmw
->dwStatus
) {
1202 case MCI_MODE_PAUSE
:
1203 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1205 dwRet
= waveInStart(wmw
->hWave
);
1206 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_RECORD
;
1208 ERR("waveInStart error %d\n",dwRet
);
1209 dwRet
= MCIERR_INTERNAL
;
1212 dwRet
= waveOutRestart(wmw
->hWave
);
1213 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PLAY
;
1215 ERR("waveOutRestart error %d\n",dwRet
);
1216 dwRet
= MCIERR_INTERNAL
;
1221 case MCI_MODE_RECORD
:
1222 dwRet
= MMSYSERR_NOERROR
;
1225 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1227 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1228 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1232 /**************************************************************************
1233 * WAVE_mciSeek [internal]
1235 static DWORD
WAVE_mciSeek(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1237 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1239 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1241 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1242 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1244 WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1246 if (dwFlags
& MCI_SEEK_TO_START
) {
1247 wmw
->dwPosition
= 0;
1248 } else if (dwFlags
& MCI_SEEK_TO_END
) {
1249 wmw
->dwPosition
= wmw
->ckWaveData
.cksize
;
1250 } else if (dwFlags
& MCI_TO
) {
1251 wmw
->dwPosition
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1253 WARN("dwFlag doesn't tell where to seek to...\n");
1254 return MCIERR_MISSING_PARAMETER
;
1257 TRACE("Seeking to position=%u bytes\n", wmw
->dwPosition
);
1259 if (dwFlags
& MCI_NOTIFY
)
1260 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1262 return MMSYSERR_NOERROR
;
1265 /**************************************************************************
1266 * WAVE_mciSet [internal]
1268 static DWORD
WAVE_mciSet(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1270 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1272 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1274 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1275 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1277 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1278 switch (lpParms
->dwTimeFormat
) {
1279 case MCI_FORMAT_MILLISECONDS
:
1280 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1281 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1283 case MCI_FORMAT_BYTES
:
1284 TRACE("MCI_FORMAT_BYTES !\n");
1285 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1287 case MCI_FORMAT_SAMPLES
:
1288 TRACE("MCI_FORMAT_SAMPLES !\n");
1289 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1292 WARN("Bad time format %u!\n", lpParms
->dwTimeFormat
);
1293 return MCIERR_BAD_TIME_FORMAT
;
1296 if (dwFlags
& MCI_SET_VIDEO
) {
1297 TRACE("No support for video !\n");
1298 return MCIERR_UNSUPPORTED_FUNCTION
;
1300 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1301 TRACE("No support for door open !\n");
1302 return MCIERR_UNSUPPORTED_FUNCTION
;
1304 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1305 TRACE("No support for door close !\n");
1306 return MCIERR_UNSUPPORTED_FUNCTION
;
1308 if (dwFlags
& MCI_SET_AUDIO
) {
1309 if (dwFlags
& MCI_SET_ON
) {
1310 TRACE("MCI_SET_ON audio !\n");
1311 } else if (dwFlags
& MCI_SET_OFF
) {
1312 TRACE("MCI_SET_OFF audio !\n");
1314 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1315 return MCIERR_BAD_INTEGER
;
1318 switch (lpParms
->dwAudio
)
1320 case MCI_SET_AUDIO_ALL
: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1321 case MCI_SET_AUDIO_LEFT
: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1322 case MCI_SET_AUDIO_RIGHT
: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1323 default: WARN("Unknown audio channel %u\n", lpParms
->dwAudio
); break;
1326 if (dwFlags
& MCI_WAVE_INPUT
)
1327 TRACE("MCI_WAVE_INPUT !\n");
1328 if (dwFlags
& MCI_WAVE_OUTPUT
)
1329 TRACE("MCI_WAVE_OUTPUT !\n");
1330 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
1331 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1332 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
1333 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1334 /* Set wave format parameters is refused after Open or Record.*/
1335 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
) {
1336 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wFormatTag
);
1337 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1338 if (((LPMCI_WAVE_SET_PARMS
)lpParms
)->wFormatTag
!= WAVE_FORMAT_PCM
)
1339 return MCIERR_OUTOFRANGE
;
1341 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
) {
1342 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1343 wmw
->wfxRef
.nAvgBytesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nAvgBytesPerSec
;
1344 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw
->wfxRef
.nAvgBytesPerSec
);
1346 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
) {
1347 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1348 wmw
->wfxRef
.wBitsPerSample
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wBitsPerSample
;
1349 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw
->wfxRef
.wBitsPerSample
);
1351 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
) {
1352 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1353 wmw
->wfxRef
.nBlockAlign
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nBlockAlign
;
1354 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw
->wfxRef
.nBlockAlign
);
1356 if (dwFlags
& MCI_WAVE_SET_CHANNELS
) {
1357 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1358 wmw
->wfxRef
.nChannels
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nChannels
;
1359 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw
->wfxRef
.nChannels
);
1361 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
) {
1362 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1363 wmw
->wfxRef
.nSamplesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nSamplesPerSec
;
1364 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw
->wfxRef
.nSamplesPerSec
);
1366 if (dwFlags
& MCI_NOTIFY
)
1367 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1371 /**************************************************************************
1372 * WAVE_mciSave [internal]
1374 static DWORD
WAVE_mciSave(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMSW lpParms
)
1376 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1377 DWORD ret
= MCIERR_FILE_NOT_SAVED
, tmpRet
;
1379 TRACE("%d, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1380 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1381 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1383 if (dwFlags
& MCI_WAIT
)
1385 FIXME("MCI_WAIT not implemented\n");
1387 WAVE_mciStop(wDevID
, 0, NULL
);
1389 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1390 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1392 ret
= mmioClose(wmw
->hFile
, 0);
1396 If the destination file already exists, it has to be overwritten. (Behaviour
1397 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1398 my applications. We are making use of mmioRename, which WILL NOT overwrite
1399 the destination file (which is what Windows does, also verified in Win2K)
1400 So, lets delete the destination file before calling mmioRename. If the
1401 destination file DOESN'T exist, the delete will fail silently. Let's also be
1402 careful not to lose our previous error code.
1404 tmpRet
= GetLastError();
1405 DeleteFileW (lpParms
->lpfilename
);
1406 SetLastError(tmpRet
);
1408 if (0 == mmioRenameW(wmw
->openParms
.lpstrElementName
, lpParms
->lpfilename
, 0, 0 )) {
1409 ret
= MMSYSERR_NOERROR
;
1412 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1413 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1415 if (ret
== MMSYSERR_NOERROR
)
1416 ret
= WAVE_mciOpenFile(wmw
, lpParms
->lpfilename
);
1421 /**************************************************************************
1422 * WAVE_mciStatus [internal]
1424 static DWORD
WAVE_mciStatus(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1426 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1429 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1430 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1431 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1433 if (dwFlags
& MCI_STATUS_ITEM
) {
1434 switch (lpParms
->dwItem
) {
1435 case MCI_STATUS_CURRENT_TRACK
:
1436 lpParms
->dwReturn
= 1;
1437 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1439 case MCI_STATUS_LENGTH
:
1441 lpParms
->dwReturn
= 0;
1442 return MCIERR_UNSUPPORTED_FUNCTION
;
1444 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1445 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
, &ret
);
1446 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1448 case MCI_STATUS_MODE
:
1449 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1450 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1451 ret
= MCI_RESOURCE_RETURNED
;
1453 case MCI_STATUS_MEDIA_PRESENT
:
1454 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1455 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1456 ret
= MCI_RESOURCE_RETURNED
;
1458 case MCI_STATUS_NUMBER_OF_TRACKS
:
1459 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1460 lpParms
->dwReturn
= 1;
1461 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms
->dwReturn
);
1463 case MCI_STATUS_POSITION
:
1465 lpParms
->dwReturn
= 0;
1466 return MCIERR_UNSUPPORTED_FUNCTION
;
1468 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1469 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1470 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
,
1472 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1473 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1475 case MCI_STATUS_READY
:
1476 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1477 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1478 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1479 ret
= MCI_RESOURCE_RETURNED
;
1481 case MCI_STATUS_TIME_FORMAT
:
1482 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, MCI_FORMAT_RETURN_BASE
+ wmw
->dwMciTimeFormat
);
1483 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1484 ret
= MCI_RESOURCE_RETURNED
;
1486 case MCI_WAVE_INPUT
:
1487 TRACE("MCI_WAVE_INPUT !\n");
1488 lpParms
->dwReturn
= 0;
1489 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1491 case MCI_WAVE_OUTPUT
:
1492 TRACE("MCI_WAVE_OUTPUT !\n");
1495 if (waveOutGetID(wmw
->hWave
, &id
) == MMSYSERR_NOERROR
) {
1496 lpParms
->dwReturn
= id
;
1498 lpParms
->dwReturn
= 0;
1499 ret
= MCIERR_WAVE_OUTPUTUNSPECIFIED
;
1503 /* It is always ok to query wave format parameters,
1504 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1505 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1506 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1507 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms
->dwReturn
);
1509 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1510 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1511 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms
->dwReturn
);
1513 case MCI_WAVE_STATUS_BLOCKALIGN
:
1514 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1515 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms
->dwReturn
);
1517 case MCI_WAVE_STATUS_CHANNELS
:
1518 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1519 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms
->dwReturn
);
1521 case MCI_WAVE_STATUS_FORMATTAG
:
1522 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1523 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms
->dwReturn
);
1525 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1526 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1527 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms
->dwReturn
);
1529 case MCI_WAVE_STATUS_LEVEL
:
1530 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1531 lpParms
->dwReturn
= 0xAAAA5555;
1534 WARN("unknown command %08X !\n", lpParms
->dwItem
);
1535 return MCIERR_UNRECOGNIZED_COMMAND
;
1538 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1539 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1543 /**************************************************************************
1544 * WAVE_mciGetDevCaps [internal]
1546 static DWORD
WAVE_mciGetDevCaps(MCIDEVICEID wDevID
, DWORD dwFlags
,
1547 LPMCI_GETDEVCAPS_PARMS lpParms
)
1549 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1552 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1554 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1555 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1557 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1558 switch(lpParms
->dwItem
) {
1559 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1560 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1561 ret
= MCI_RESOURCE_RETURNED
;
1563 case MCI_GETDEVCAPS_HAS_AUDIO
:
1564 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1565 ret
= MCI_RESOURCE_RETURNED
;
1567 case MCI_GETDEVCAPS_HAS_VIDEO
:
1568 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1569 ret
= MCI_RESOURCE_RETURNED
;
1571 case MCI_GETDEVCAPS_USES_FILES
:
1572 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1573 ret
= MCI_RESOURCE_RETURNED
;
1575 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1576 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1577 ret
= MCI_RESOURCE_RETURNED
;
1579 case MCI_GETDEVCAPS_CAN_RECORD
:
1580 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1581 ret
= MCI_RESOURCE_RETURNED
;
1583 case MCI_GETDEVCAPS_CAN_EJECT
:
1584 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1585 ret
= MCI_RESOURCE_RETURNED
;
1587 case MCI_GETDEVCAPS_CAN_PLAY
:
1588 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1589 ret
= MCI_RESOURCE_RETURNED
;
1591 case MCI_GETDEVCAPS_CAN_SAVE
:
1592 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1593 ret
= MCI_RESOURCE_RETURNED
;
1595 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1596 lpParms
->dwReturn
= 1;
1598 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1599 lpParms
->dwReturn
= 1;
1602 FIXME("Unknown capability (%08x) !\n", lpParms
->dwItem
);
1603 return MCIERR_UNRECOGNIZED_COMMAND
;
1606 WARN("No GetDevCaps-Item !\n");
1607 return MCIERR_UNRECOGNIZED_COMMAND
;
1609 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1610 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1614 /**************************************************************************
1615 * WAVE_mciInfo [internal]
1617 static DWORD
WAVE_mciInfo(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSW lpParms
)
1621 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1623 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1625 if (!lpParms
|| !lpParms
->lpstrReturn
)
1626 return MCIERR_NULL_PARAMETER_BLOCK
;
1629 ret
= MCIERR_INVALID_DEVICE_ID
;
1631 static const WCHAR wszAudio
[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1632 static const WCHAR wszWaveIn
[] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1633 static const WCHAR wszWaveOut
[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1635 TRACE("buf=%p, len=%u\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1637 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1638 case MCI_INFO_PRODUCT
: str
= wszAudio
; break;
1639 case MCI_INFO_FILE
: str
= wmw
->openParms
.lpstrElementName
; break;
1640 case MCI_WAVE_INPUT
: str
= wszWaveIn
; break;
1641 case MCI_WAVE_OUTPUT
: str
= wszWaveOut
; break;
1643 WARN("Don't know this info command (%u)\n", dwFlags
);
1644 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
1648 if (strlenW(str
) + 1 > lpParms
->dwRetSize
) {
1649 ret
= MCIERR_PARAM_OVERFLOW
;
1651 lstrcpynW(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
1654 lpParms
->lpstrReturn
[0] = 0;
1656 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1657 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1661 /**************************************************************************
1662 * DriverProc (MCIWAVE.@)
1664 LRESULT CALLBACK
MCIWAVE_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
1665 LPARAM dwParam1
, LPARAM dwParam2
)
1667 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1668 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1671 case DRV_LOAD
: return 1;
1672 case DRV_FREE
: return 1;
1673 case DRV_OPEN
: return WAVE_drvOpen((LPCWSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSW
)dwParam2
);
1674 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1675 case DRV_ENABLE
: return 1;
1676 case DRV_DISABLE
: return 1;
1677 case DRV_QUERYCONFIGURE
: return 1;
1678 case DRV_CONFIGURE
: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK
); return 1;
1679 case DRV_INSTALL
: return DRVCNF_RESTART
;
1680 case DRV_REMOVE
: return DRVCNF_RESTART
;
1683 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1686 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSW
) dwParam2
);
1687 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1688 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1689 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, dwParam2
, NULL
);
1690 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, dwParam2
, NULL
);
1691 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1692 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
1693 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1694 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1695 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1696 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1697 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSW
) dwParam2
);
1698 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1699 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMSW
) dwParam2
);
1700 /* commands that should be supported */
1715 FIXME("Unsupported yet command [%u]\n", wMsg
);
1718 TRACE("Unsupported command [%u]\n", wMsg
);
1720 /* option which can be silenced */
1725 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1728 FIXME("is probably wrong msg [%u]\n", wMsg
);
1729 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1731 return MCIERR_UNRECOGNIZED_COMMAND
;