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 volatile WORD dwStatus
; /* one from MCI_MODE_xxxx */
51 DWORD dwMciTimeFormat
;/* One of the supported MCI_FORMAT_xxxx */
52 DWORD dwPosition
; /* position in bytes in chunk */
53 HANDLE hEvent
; /* for synchronization */
54 LONG dwEventCount
; /* for synchronization */
55 MMCKINFO ckMainRIFF
; /* main RIFF chunk */
56 MMCKINFO ckWaveData
; /* data chunk */
59 /* ===================================================================
60 * ===================================================================
61 * FIXME: should be using the new mmThreadXXXX functions from WINMM
63 * it would require to add a wine internal flag to mmThreadCreate
64 * in order to pass a 32 bit function instead of a 16 bit one
65 * ===================================================================
66 * =================================================================== */
68 typedef DWORD (*async_cmd
)(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE evt
);
78 /**************************************************************************
79 * MCI_SCAStarter [internal]
81 static DWORD CALLBACK
MCI_SCAStarter(LPVOID arg
)
83 struct SCA
* sca
= (struct SCA
*)arg
;
86 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
87 sca
->wDevID
, sca
->dwParam1
, sca
->dwParam2
);
88 ret
= sca
->cmd(sca
->wDevID
, sca
->dwParam1
| MCI_WAIT
, sca
->dwParam2
, sca
->evt
);
89 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
90 sca
->wDevID
, sca
->dwParam1
, sca
->dwParam2
);
91 HeapFree(GetProcessHeap(), 0, sca
);
95 /**************************************************************************
96 * MCI_SendCommandAsync [internal]
98 static DWORD
MCI_SendCommandAsync(UINT wDevID
, async_cmd cmd
, DWORD_PTR dwParam1
,
99 DWORD_PTR dwParam2
, UINT size
)
102 struct SCA
* sca
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA
) + size
);
105 return MCIERR_OUT_OF_MEMORY
;
107 sca
->wDevID
= wDevID
;
109 sca
->dwParam1
= dwParam1
;
111 if (size
&& dwParam2
) {
112 sca
->dwParam2
= (DWORD_PTR
)sca
+ sizeof(struct SCA
);
113 /* copy structure passed by program in dwParam2 to be sure
114 * we can still use it whatever the program does
116 memcpy((LPVOID
)sca
->dwParam2
, (LPVOID
)dwParam2
, size
);
118 sca
->dwParam2
= dwParam2
;
121 if ((sca
->evt
= handles
[1] = CreateEventW(NULL
, FALSE
, FALSE
, NULL
)) == NULL
||
122 (handles
[0] = CreateThread(NULL
, 0, MCI_SCAStarter
, sca
, 0, NULL
)) == 0) {
123 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
124 if (handles
[1]) CloseHandle(handles
[1]);
126 return MCI_SCAStarter(&sca
);
129 SetThreadPriority(handles
[0], THREAD_PRIORITY_TIME_CRITICAL
);
130 /* wait until either:
131 * - the thread has finished (handles[0], likely an error)
132 * - init phase of async command is done (handles[1])
134 WaitForMultipleObjects(2, handles
, FALSE
, INFINITE
);
135 CloseHandle(handles
[0]);
136 CloseHandle(handles
[1]);
140 /*======================================================================*
141 * MCI WAVE implementation *
142 *======================================================================*/
144 static DWORD
WAVE_mciResume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
146 /**************************************************************************
147 * MCIWAVE_drvOpen [internal]
149 static LRESULT
WAVE_drvOpen(LPCWSTR str
, LPMCI_OPEN_DRIVER_PARMSW modp
)
153 if (modp
== NULL
) return 0xFFFFFFFF;
155 wmw
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCIWAVE
));
160 wmw
->wDevID
= modp
->wDeviceID
;
161 mciSetDriverData(wmw
->wDevID
, (DWORD_PTR
)wmw
);
162 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
163 modp
->wType
= MCI_DEVTYPE_WAVEFORM_AUDIO
;
165 wmw
->wfxRef
.wFormatTag
= WAVE_FORMAT_PCM
;
166 wmw
->wfxRef
.nChannels
= 1; /* MONO */
167 wmw
->wfxRef
.nSamplesPerSec
= 11025;
168 wmw
->wfxRef
.nAvgBytesPerSec
= 11025;
169 wmw
->wfxRef
.nBlockAlign
= 1;
170 wmw
->wfxRef
.wBitsPerSample
= 8;
171 wmw
->wfxRef
.cbSize
= 0; /* don't care */
173 return modp
->wDeviceID
;
176 /**************************************************************************
177 * MCIWAVE_drvClose [internal]
179 static LRESULT
WAVE_drvClose(MCIDEVICEID dwDevID
)
181 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(dwDevID
);
184 HeapFree(GetProcessHeap(), 0, wmw
);
185 mciSetDriverData(dwDevID
, 0);
188 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
191 /**************************************************************************
192 * WAVE_mciGetOpenDev [internal]
194 static WINE_MCIWAVE
*WAVE_mciGetOpenDev(MCIDEVICEID wDevID
)
196 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
198 if (wmw
== NULL
|| wmw
->nUseCount
== 0) {
199 WARN("Invalid wDevID=%u\n", wDevID
);
205 /**************************************************************************
206 * WAVE_mciNotify [internal]
208 * Notifications in MCI work like a 1-element queue.
209 * Each new notification request supersedes the previous one.
210 * This affects Play and Record; other commands are immediate.
212 static void WAVE_mciNotify(DWORD_PTR hWndCallBack
, WINE_MCIWAVE
* wmw
, UINT wStatus
)
214 /* We simply save one parameter by not passing the wDevID local
215 * to the command. They are the same (via mciGetDriverData).
217 MCIDEVICEID wDevID
= wmw
->wNotifyDeviceID
;
218 HANDLE old
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
219 if (old
) mciDriverNotify(old
, wDevID
, MCI_NOTIFY_SUPERSEDED
);
220 mciDriverNotify(HWND_32(LOWORD(hWndCallBack
)), wDevID
, wStatus
);
223 /**************************************************************************
224 * WAVE_ConvertByteToTimeFormat [internal]
226 static DWORD
WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE
* wmw
, DWORD val
, LPDWORD lpRet
)
230 switch (wmw
->dwMciTimeFormat
) {
231 case MCI_FORMAT_MILLISECONDS
:
232 ret
= MulDiv(val
,1000,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
234 case MCI_FORMAT_BYTES
:
237 case MCI_FORMAT_SAMPLES
:
238 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nSamplesPerSec
,wmw
->lpWaveFormat
->nAvgBytesPerSec
);
241 WARN("Bad time format %u!\n", wmw
->dwMciTimeFormat
);
243 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
248 /**************************************************************************
249 * WAVE_ConvertTimeFormatToByte [internal]
251 static DWORD
WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE
* wmw
, DWORD val
)
255 switch (wmw
->dwMciTimeFormat
) {
256 case MCI_FORMAT_MILLISECONDS
:
257 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,1000);
259 case MCI_FORMAT_BYTES
:
262 case MCI_FORMAT_SAMPLES
:
263 ret
= MulDiv(val
,wmw
->lpWaveFormat
->nAvgBytesPerSec
,wmw
->lpWaveFormat
->nSamplesPerSec
);
266 WARN("Bad time format %u!\n", wmw
->dwMciTimeFormat
);
268 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val
, val
, wmw
->dwMciTimeFormat
, ret
);
272 /**************************************************************************
273 * WAVE_mciReadFmt [internal]
275 static DWORD
WAVE_mciReadFmt(WINE_MCIWAVE
* wmw
, const MMCKINFO
* pckMainRIFF
)
281 mmckInfo
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
282 if (mmioDescend(wmw
->hFile
, &mmckInfo
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0)
283 return MCIERR_INVALID_FILE
;
284 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
285 (LPSTR
)&mmckInfo
.ckid
, (LPSTR
)&mmckInfo
.fccType
, mmckInfo
.cksize
);
287 pwfx
= HeapAlloc(GetProcessHeap(), 0, mmckInfo
.cksize
);
288 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
290 r
= mmioRead(wmw
->hFile
, (HPSTR
)pwfx
, mmckInfo
.cksize
);
291 if (r
< sizeof(PCMWAVEFORMAT
)) {
292 HeapFree(GetProcessHeap(), 0, pwfx
);
293 return MCIERR_INVALID_FILE
;
295 TRACE("wFormatTag=%04X !\n", pwfx
->wFormatTag
);
296 TRACE("nChannels=%d\n", pwfx
->nChannels
);
297 TRACE("nSamplesPerSec=%d\n", pwfx
->nSamplesPerSec
);
298 TRACE("nAvgBytesPerSec=%d\n", pwfx
->nAvgBytesPerSec
);
299 TRACE("nBlockAlign=%d\n", pwfx
->nBlockAlign
);
300 TRACE("wBitsPerSample=%u !\n", pwfx
->wBitsPerSample
);
301 if (r
>= (long)sizeof(WAVEFORMATEX
))
302 TRACE("cbSize=%u !\n", pwfx
->cbSize
);
303 if ((pwfx
->wFormatTag
!= WAVE_FORMAT_PCM
)
304 && (r
< sizeof(WAVEFORMATEX
) || (r
< sizeof(WAVEFORMATEX
) + pwfx
->cbSize
))) {
305 HeapFree(GetProcessHeap(), 0, pwfx
);
306 return MCIERR_INVALID_FILE
;
308 wmw
->lpWaveFormat
= pwfx
;
310 mmioAscend(wmw
->hFile
, &mmckInfo
, 0);
311 wmw
->ckWaveData
.ckid
= mmioFOURCC('d', 'a', 't', 'a');
312 if (mmioDescend(wmw
->hFile
, &wmw
->ckWaveData
, pckMainRIFF
, MMIO_FINDCHUNK
) != 0) {
313 TRACE("can't find data chunk\n");
314 return MCIERR_INVALID_FILE
;
316 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
317 (LPSTR
)&wmw
->ckWaveData
.ckid
, (LPSTR
)&wmw
->ckWaveData
.fccType
, wmw
->ckWaveData
.cksize
);
321 /**************************************************************************
322 * WAVE_mciDefaultFmt [internal]
324 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
325 * until either Open File or Record. It becomes immutable afterwards,
326 * i.e. Set wave format or channels etc. is subsequently refused.
328 static void WAVE_mciDefaultFmt(WINE_MCIWAVE
* wmw
)
330 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
331 wmw
->lpWaveFormat
->wFormatTag
= WAVE_FORMAT_PCM
;
332 wmw
->lpWaveFormat
->nChannels
= 1;
333 wmw
->lpWaveFormat
->nSamplesPerSec
= 11025;
334 wmw
->lpWaveFormat
->nAvgBytesPerSec
= 11025;
335 wmw
->lpWaveFormat
->nBlockAlign
= 1;
336 wmw
->lpWaveFormat
->wBitsPerSample
= 8;
337 wmw
->lpWaveFormat
->cbSize
= 0;
340 /**************************************************************************
341 * WAVE_mciCreateRIFFSkeleton [internal]
343 static DWORD
WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE
* wmw
)
345 MMCKINFO ckWaveFormat
;
346 LPMMCKINFO lpckRIFF
= &(wmw
->ckMainRIFF
);
347 LPMMCKINFO lpckWaveData
= &(wmw
->ckWaveData
);
349 lpckRIFF
->ckid
= FOURCC_RIFF
;
350 lpckRIFF
->fccType
= mmioFOURCC('W', 'A', 'V', 'E');
351 lpckRIFF
->cksize
= 0;
353 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckRIFF
, MMIO_CREATERIFF
))
356 ckWaveFormat
.fccType
= 0;
357 ckWaveFormat
.ckid
= mmioFOURCC('f', 'm', 't', ' ');
358 ckWaveFormat
.cksize
= sizeof(PCMWAVEFORMAT
);
360 /* Set wave format accepts PCM only, however open an
361 * existing ADPCM file, record into it and the MCI will
362 * happily save back in that format. */
363 if (wmw
->lpWaveFormat
->wFormatTag
== WAVE_FORMAT_PCM
) {
364 if (wmw
->lpWaveFormat
->nBlockAlign
!=
365 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
366 WORD size
= wmw
->lpWaveFormat
->nChannels
*
367 wmw
->lpWaveFormat
->wBitsPerSample
/8;
368 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
369 wmw
->lpWaveFormat
->nBlockAlign
, size
);
370 wmw
->lpWaveFormat
->nBlockAlign
= size
;
372 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
373 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
374 DWORD speed
= wmw
->lpWaveFormat
->nSamplesPerSec
*
375 wmw
->lpWaveFormat
->nBlockAlign
;
376 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
377 wmw
->lpWaveFormat
->nAvgBytesPerSec
, speed
);
378 wmw
->lpWaveFormat
->nAvgBytesPerSec
= speed
;
381 if (wmw
->lpWaveFormat
== &wmw
->wfxRef
) {
382 LPWAVEFORMATEX pwfx
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WAVEFORMATEX
));
383 if (!pwfx
) return MCIERR_OUT_OF_MEMORY
;
384 /* Set wave format accepts PCM only so the size is known. */
385 assert(wmw
->wfxRef
.wFormatTag
== WAVE_FORMAT_PCM
);
387 wmw
->lpWaveFormat
= pwfx
;
390 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, &ckWaveFormat
, 0))
393 if (-1 == mmioWrite(wmw
->hFile
, (HPCSTR
)wmw
->lpWaveFormat
, (WAVE_FORMAT_PCM
==wmw
->lpWaveFormat
->wFormatTag
)
394 ? sizeof(PCMWAVEFORMAT
) : sizeof(WAVEFORMATEX
)+wmw
->lpWaveFormat
->cbSize
))
397 if (MMSYSERR_NOERROR
!= mmioAscend(wmw
->hFile
, &ckWaveFormat
, 0))
400 lpckWaveData
->cksize
= 0;
401 lpckWaveData
->fccType
= 0;
402 lpckWaveData
->ckid
= mmioFOURCC('d', 'a', 't', 'a');
404 /* create data chunk */
405 if (MMSYSERR_NOERROR
!= mmioCreateChunk(wmw
->hFile
, lpckWaveData
, 0))
411 /* mciClose takes care of wmw->lpWaveFormat. */
412 return MCIERR_INVALID_FILE
;
415 static DWORD
create_tmp_file(HMMIO
* hFile
, LPWSTR
* pszTmpFileName
)
417 WCHAR szTmpPath
[MAX_PATH
];
419 DWORD dwRet
= MMSYSERR_NOERROR
;
426 if (!GetTempPathW(sizeof(szTmpPath
)/sizeof(szTmpPath
[0]), szTmpPath
)) {
427 WARN("can't retrieve temp path!\n");
428 *pszTmpFileName
= NULL
;
429 return MCIERR_FILE_NOT_FOUND
;
432 *pszTmpFileName
= HeapAlloc(GetProcessHeap(),
434 MAX_PATH
* sizeof(WCHAR
));
435 if (!GetTempFileNameW(szTmpPath
, szPrefix
, 0, *pszTmpFileName
)) {
436 WARN("can't retrieve temp file name!\n");
437 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
438 return MCIERR_FILE_NOT_FOUND
;
441 TRACE("%s!\n", debugstr_w(*pszTmpFileName
));
443 if (*pszTmpFileName
&& (strlenW(*pszTmpFileName
) > 0)) {
445 *hFile
= mmioOpenW(*pszTmpFileName
, NULL
,
446 MMIO_ALLOCBUF
| MMIO_READWRITE
| MMIO_CREATE
);
449 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName
));
450 /* temporary file could not be created. clean filename. */
451 HeapFree(GetProcessHeap(), 0, *pszTmpFileName
);
452 dwRet
= MCIERR_FILE_NOT_FOUND
;
458 static LRESULT
WAVE_mciOpenFile(WINE_MCIWAVE
* wmw
, LPCWSTR filename
)
460 LRESULT dwRet
= MMSYSERR_NOERROR
;
463 fn
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename
) + 1) * sizeof(WCHAR
));
464 if (!fn
) return MCIERR_OUT_OF_MEMORY
;
465 strcpyW(fn
, filename
);
466 HeapFree(GetProcessHeap(), 0, (void*)wmw
->lpFileName
);
467 wmw
->lpFileName
= fn
;
469 if (strlenW(filename
) > 0) {
470 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
471 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename
));
473 wmw
->hFile
= mmioOpenW((LPWSTR
)filename
, NULL
,
474 MMIO_ALLOCBUF
| MMIO_DENYWRITE
| MMIO_READ
);
476 if (wmw
->hFile
== 0) {
477 WARN("can't find file=%s!\n", debugstr_w(filename
));
478 dwRet
= MCIERR_FILE_NOT_FOUND
;
482 LPMMCKINFO lpckMainRIFF
= &wmw
->ckMainRIFF
;
484 /* make sure we're are the beginning of the file */
485 mmioSeek(wmw
->hFile
, 0, SEEK_SET
);
487 /* first reading of this file. read the waveformat chunk */
488 if (mmioDescend(wmw
->hFile
, lpckMainRIFF
, NULL
, 0) != 0) {
489 dwRet
= MCIERR_INVALID_FILE
;
491 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
492 (LPSTR
)&(lpckMainRIFF
->ckid
),
493 (LPSTR
) &(lpckMainRIFF
->fccType
),
494 (lpckMainRIFF
->cksize
));
496 if ((lpckMainRIFF
->ckid
!= FOURCC_RIFF
) ||
497 lpckMainRIFF
->fccType
!= mmioFOURCC('W', 'A', 'V', 'E')) {
498 dwRet
= MCIERR_INVALID_FILE
;
500 dwRet
= WAVE_mciReadFmt(wmw
, lpckMainRIFF
);
508 /**************************************************************************
509 * WAVE_mciOpen [internal]
511 static LRESULT
WAVE_mciOpen(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_WAVE_OPEN_PARMSW lpOpenParms
)
514 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)mciGetDriverData(wDevID
);
516 TRACE("(%04X, %08X, %p)\n", wDevID
, dwFlags
, lpOpenParms
);
517 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
518 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
520 if (dwFlags
& MCI_OPEN_SHAREABLE
)
521 return MCIERR_UNSUPPORTED_FUNCTION
;
523 if (wmw
->nUseCount
> 0) {
524 /* The driver is already opened on this channel
525 * Wave driver cannot be shared
527 return MCIERR_DEVICE_OPEN
;
534 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
536 wmw
->lpFileName
= NULL
; /* will be set by WAVE_mciOpenFile */
537 wmw
->hCallback
= NULL
;
538 WAVE_mciDefaultFmt(wmw
);
540 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID
, lpOpenParms
->wDeviceID
);
541 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
542 wmw
->wNotifyDeviceID
= wDevID
;
544 if (dwFlags
& MCI_OPEN_ELEMENT
) {
545 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
546 /* could it be that (DWORD)lpOpenParms->lpstrElementName
547 * contains the hFile value ?
549 dwRet
= MCIERR_UNRECOGNIZED_COMMAND
;
551 dwRet
= WAVE_mciOpenFile(wmw
, lpOpenParms
->lpstrElementName
);
554 TRACE("hFile=%p\n", wmw
->hFile
);
559 wmw
->dwStatus
= MCI_MODE_STOP
;
561 if (dwFlags
& MCI_NOTIFY
)
562 WAVE_mciNotify(lpOpenParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
566 mmioClose(wmw
->hFile
, 0);
572 /**************************************************************************
573 * WAVE_mciCue [internal]
575 static DWORD
WAVE_mciCue(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
577 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
579 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
581 /* Tests on systems without sound drivers show that Cue, like
582 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
583 * The first Cue Notify does not immediately return the
584 * notification, as if a player or recorder thread is started.
585 * PAUSE mode is reported when successful, but this mode is
586 * different from the normal Pause, because a) Pause then returns
587 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
588 * still accepted, returning the original notification as ABORTED.
589 * I.e. Cue allows subsequent format changes, unlike Record or
590 * Open file, closes winmm if the format changes and stops this
592 * Wine creates one player or recorder thread per async. Play or
593 * Record command. Notification behaviour suggests that MS-W*
594 * reuses a single thread to improve response times. Having Cue
595 * start this thread early helps to improve Play/Record's initial
596 * response time. In effect, Cue is a performance hint, which
597 * justifies our almost no-op implementation.
600 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
601 if (wmw
->dwStatus
!= MCI_MODE_STOP
) return MCIERR_NONAPPLICABLE_FUNCTION
;
603 if ((dwFlags
& MCI_NOTIFY
) && lpParms
)
604 WAVE_mciNotify(lpParms
->dwCallback
,wmw
,MCI_NOTIFY_SUCCESSFUL
);
606 return MMSYSERR_NOERROR
;
609 /**************************************************************************
610 * WAVE_mciStop [internal]
612 static DWORD
WAVE_mciStop(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
615 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
617 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
619 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
621 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
622 HANDLE old
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
623 if (old
) mciDriverNotify(old
, wDevID
, MCI_NOTIFY_ABORTED
);
626 /* wait for playback thread (if any) to exit before processing further */
627 switch (wmw
->dwStatus
) {
630 case MCI_MODE_RECORD
:
632 int oldStat
= wmw
->dwStatus
;
633 wmw
->dwStatus
= MCI_MODE_NOT_READY
;
634 if (oldStat
== MCI_MODE_PAUSE
)
635 dwRet
= (wmw
->fInput
) ? waveInReset(wmw
->hWave
) : waveOutReset(wmw
->hWave
);
637 while (wmw
->dwStatus
!= MCI_MODE_STOP
)
643 wmw
->dwStatus
= MCI_MODE_STOP
;
645 if ((dwFlags
& MCI_NOTIFY
) && lpParms
&& MMSYSERR_NOERROR
==dwRet
)
646 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
651 /**************************************************************************
652 * WAVE_mciClose [internal]
654 static DWORD
WAVE_mciClose(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
657 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
659 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
661 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
663 if (wmw
->dwStatus
!= MCI_MODE_STOP
) {
664 /* mciStop handles MCI_NOTIFY_ABORTED */
665 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, lpParms
);
670 if (wmw
->nUseCount
== 0) {
671 if (wmw
->hFile
!= 0) {
672 mmioClose(wmw
->hFile
, 0);
677 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
)
678 HeapFree(GetProcessHeap(), 0, wmw
->lpWaveFormat
);
679 wmw
->lpWaveFormat
= &wmw
->wfxRef
;
680 HeapFree(GetProcessHeap(), 0, (void*)wmw
->lpFileName
);
681 wmw
->lpFileName
= NULL
;
683 if ((dwFlags
& MCI_NOTIFY
) && lpParms
) {
684 WAVE_mciNotify(lpParms
->dwCallback
, wmw
,
685 (dwRet
== 0) ? MCI_NOTIFY_SUCCESSFUL
: MCI_NOTIFY_FAILURE
);
691 /**************************************************************************
692 * WAVE_mciPlayCallback [internal]
694 static void CALLBACK
WAVE_mciPlayCallback(HWAVEOUT hwo
, UINT uMsg
,
695 DWORD_PTR dwInstance
,
696 LPARAM dwParam1
, LPARAM dwParam2
)
698 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
705 InterlockedIncrement(&wmw
->dwEventCount
);
706 TRACE("Returning waveHdr=%lx\n", dwParam1
);
707 SetEvent(wmw
->hEvent
);
710 ERR("Unknown uMsg=%d\n", uMsg
);
714 /******************************************************************
715 * WAVE_mciPlayWaitDone [internal]
717 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE
* wmw
)
720 ResetEvent(wmw
->hEvent
);
721 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
724 InterlockedIncrement(&wmw
->dwEventCount
);
726 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
730 /**************************************************************************
731 * WAVE_mciPlay [internal]
733 static DWORD
WAVE_mciPlay(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
735 LPMCI_PLAY_PARMS lpParms
= (void*)pmt
;
737 LONG bufsize
, count
, left
;
739 LPWAVEHDR waveHdr
= NULL
;
740 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
744 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
746 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
747 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
749 if (wmw
->hFile
== 0) {
750 WARN("Can't play: no file=%s!\n", debugstr_w(wmw
->lpFileName
));
751 return MCIERR_FILE_NOT_FOUND
;
754 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& !wmw
->fInput
) {
755 /* FIXME: parameters (start/end) in lpParams may not be used */
756 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
759 /** This function will be called again by a thread when async is used.
760 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
761 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
763 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_PLAY
) && (dwFlags
& MCI_WAIT
))) {
764 return MCIERR_INTERNAL
;
767 if (wmw
->lpWaveFormat
->wFormatTag
== WAVE_FORMAT_PCM
) {
768 if (wmw
->lpWaveFormat
->nBlockAlign
!=
769 wmw
->lpWaveFormat
->nChannels
* wmw
->lpWaveFormat
->wBitsPerSample
/8) {
770 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
771 wmw
->lpWaveFormat
->nBlockAlign
,
772 wmw
->lpWaveFormat
->nChannels
*
773 wmw
->lpWaveFormat
->wBitsPerSample
/8);
774 wmw
->lpWaveFormat
->nBlockAlign
=
775 wmw
->lpWaveFormat
->nChannels
*
776 wmw
->lpWaveFormat
->wBitsPerSample
/8;
778 if (wmw
->lpWaveFormat
->nAvgBytesPerSec
!=
779 wmw
->lpWaveFormat
->nSamplesPerSec
* wmw
->lpWaveFormat
->nBlockAlign
) {
780 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
781 wmw
->lpWaveFormat
->nAvgBytesPerSec
,
782 wmw
->lpWaveFormat
->nSamplesPerSec
*
783 wmw
->lpWaveFormat
->nBlockAlign
);
784 wmw
->lpWaveFormat
->nAvgBytesPerSec
=
785 wmw
->lpWaveFormat
->nSamplesPerSec
*
786 wmw
->lpWaveFormat
->nBlockAlign
;
790 end
= wmw
->ckWaveData
.cksize
;
791 if (lpParms
&& (dwFlags
& MCI_TO
)) {
792 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
793 if (position
> end
) return MCIERR_OUTOFRANGE
;
796 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
797 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
798 if (position
> end
) return MCIERR_OUTOFRANGE
;
799 /* Seek rounds down, so do we. */
800 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
801 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
802 wmw
->dwPosition
= position
;
804 if (end
< wmw
->dwPosition
) return MCIERR_OUTOFRANGE
;
805 left
= end
- wmw
->dwPosition
;
806 if (0==left
) return MMSYSERR_NOERROR
; /* FIXME: NOTIFY */
808 wmw
->fInput
= FALSE
; /* FIXME: waveInOpen may have been called. */
809 wmw
->dwStatus
= MCI_MODE_PLAY
;
811 if (!(dwFlags
& MCI_WAIT
)) {
812 return MCI_SendCommandAsync(wDevID
, WAVE_mciPlay
, dwFlags
,
813 (DWORD_PTR
)lpParms
, sizeof(MCI_PLAY_PARMS
));
816 TRACE("Playing from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
818 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
819 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
820 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
823 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
824 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
826 /* go back to beginning of chunk plus the requested position */
827 /* FIXME: I'm not sure this is correct, notably because some data linked to
828 * the decompression state machine will not be correctly initialized.
829 * try it this way (other way would be to decompress from 0 up to dwPosition
830 * and to start sending to hWave when dwPosition is reached)
832 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
834 /* FIXME: how to choose between several output channels ? here mapper is forced */
835 dwRet
= waveOutOpen((HWAVEOUT
*)&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
836 (DWORD_PTR
)WAVE_mciPlayCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
839 TRACE("Can't open low level audio device %d\n", dwRet
);
840 dwRet
= MCIERR_DEVICE_OPEN
;
845 /* make it so that 3 buffers per second are needed */
846 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
848 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
849 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
850 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
851 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
852 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
853 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
854 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
855 if (waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
856 waveOutPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
857 dwRet
= MCIERR_INTERNAL
;
862 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
863 wmw
->dwEventCount
= 1L; /* for first buffer */
865 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, left
);
866 if (hEvent
) SetEvent(hEvent
);
868 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
869 while (left
> 0 && wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
870 count
= mmioRead(wmw
->hFile
, waveHdr
[whidx
].lpData
, min(bufsize
, left
));
871 TRACE("mmioRead bufsize=%d count=%d\n", bufsize
, count
);
874 /* count is always <= bufsize, so this is correct regarding the
875 * waveOutPrepareHeader function
877 waveHdr
[whidx
].dwBufferLength
= count
;
878 waveHdr
[whidx
].dwFlags
&= ~WHDR_DONE
;
879 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
880 &waveHdr
[whidx
], waveHdr
[whidx
].dwBufferLength
);
881 dwRet
= waveOutWrite(wmw
->hWave
, &waveHdr
[whidx
], sizeof(WAVEHDR
));
883 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet
);
884 dwRet
= MCIERR_HARDWARE
;
888 wmw
->dwPosition
+= count
;
889 TRACE("after WODM_WRITE dwPosition=%u\n", wmw
->dwPosition
);
890 /* InterlockedDecrement if and only if waveOutWrite is successful */
891 WAVE_mciPlayWaitDone(wmw
);
895 WAVE_mciPlayWaitDone(wmw
); /* to balance first buffer */
897 /* just to get rid of some race conditions between play, stop and pause */
898 waveOutReset(wmw
->hWave
);
900 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
901 waveOutUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
904 if (dwFlags
& MCI_NOTIFY
)
905 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
907 HeapFree(GetProcessHeap(), 0, waveHdr
);
910 waveOutClose(wmw
->hWave
);
913 CloseHandle(wmw
->hEvent
);
915 wmw
->dwStatus
= MCI_MODE_STOP
;
917 /* Let the potentically asynchronous commands support FAILURE notification. */
918 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
919 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
924 /**************************************************************************
925 * WAVE_mciRecordCallback [internal]
927 static void CALLBACK
WAVE_mciRecordCallback(HWAVEOUT hwo
, UINT uMsg
,
928 DWORD_PTR dwInstance
,
929 LPARAM dwParam1
, LPARAM dwParam2
)
931 WINE_MCIWAVE
* wmw
= (WINE_MCIWAVE
*)dwInstance
;
940 lpWaveHdr
= (LPWAVEHDR
) dwParam1
;
942 InterlockedIncrement(&wmw
->dwEventCount
);
944 count
= mmioWrite(wmw
->hFile
, lpWaveHdr
->lpData
, lpWaveHdr
->dwBytesRecorded
);
946 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
948 wmw
->dwPosition
+= count
;
949 /* else error reporting ?? */
950 if (wmw
->dwStatus
== MCI_MODE_RECORD
)
952 /* Only queue up another buffer if we are recording. We could receive this
953 message also when waveInReset() is called, since it notifies on all wave
954 buffers that are outstanding. Queueing up more sometimes causes waveInClose
956 waveInAddBuffer(wmw
->hWave
, lpWaveHdr
, sizeof(*lpWaveHdr
));
957 TRACE("after mmioWrite dwPosition=%u\n", wmw
->dwPosition
);
960 SetEvent(wmw
->hEvent
);
963 ERR("Unknown uMsg=%d\n", uMsg
);
967 /******************************************************************
968 * WAVE_mciRecordWaitDone [internal]
970 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE
* wmw
)
973 ResetEvent(wmw
->hEvent
);
974 if (InterlockedDecrement(&wmw
->dwEventCount
) >= 0) {
977 InterlockedIncrement(&wmw
->dwEventCount
);
979 WaitForSingleObject(wmw
->hEvent
, INFINITE
);
983 /**************************************************************************
984 * WAVE_mciRecord [internal]
986 static DWORD
WAVE_mciRecord(MCIDEVICEID wDevID
, DWORD_PTR dwFlags
, DWORD_PTR pmt
, HANDLE hEvent
)
988 LPMCI_RECORD_PARMS lpParms
= (void*)pmt
;
990 DWORD dwRet
= MMSYSERR_NOERROR
;
992 LPWAVEHDR waveHdr
= NULL
;
993 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
996 TRACE("(%u, %08lX, %p);\n", wDevID
, dwFlags
, lpParms
);
998 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
999 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1001 if (wmw
->dwStatus
== MCI_MODE_PAUSE
&& wmw
->fInput
) {
1002 /* FIXME: parameters (start/end) in lpParams may not be used */
1003 return WAVE_mciResume(wDevID
, dwFlags
, (LPMCI_GENERIC_PARMS
)lpParms
);
1006 /** This function will be called again by a thread when async is used.
1007 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1008 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1010 if ((wmw
->dwStatus
!= MCI_MODE_STOP
) && ((wmw
->dwStatus
!= MCI_MODE_RECORD
) && (dwFlags
& MCI_WAIT
))) {
1011 return MCIERR_INTERNAL
;
1014 wmw
->fInput
= TRUE
; /* FIXME: waveOutOpen may have been called. */
1015 wmw
->dwStatus
= MCI_MODE_RECORD
;
1017 if (!(dwFlags
& MCI_WAIT
)) {
1018 return MCI_SendCommandAsync(wDevID
, WAVE_mciRecord
, dwFlags
,
1019 (DWORD_PTR
)lpParms
, sizeof(MCI_RECORD_PARMS
));
1022 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1023 * we don't modify the wave part of an existing file (ie. we always erase an
1024 * existing content, we don't overwrite)
1026 HeapFree(GetProcessHeap(), 0, (void*)wmw
->lpFileName
);
1027 dwRet
= create_tmp_file(&wmw
->hFile
, (WCHAR
**)&wmw
->lpFileName
);
1028 if (dwRet
!= 0) return dwRet
;
1030 /* new RIFF file, lpWaveFormat now valid */
1031 dwRet
= WAVE_mciCreateRIFFSkeleton(wmw
);
1032 if (dwRet
!= 0) return dwRet
;
1034 if (lpParms
&& (dwFlags
& MCI_TO
)) {
1035 end
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1036 } else end
= 0xFFFFFFFF;
1037 if (lpParms
&& (dwFlags
& MCI_FROM
)) {
1038 DWORD position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwFrom
);
1039 if (wmw
->ckWaveData
.cksize
< position
) return MCIERR_OUTOFRANGE
;
1040 /* Seek rounds down, so do we. */
1041 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
1042 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
1043 wmw
->dwPosition
= position
;
1045 if (end
==wmw
->dwPosition
) return MMSYSERR_NOERROR
; /* FIXME: NOTIFY */
1047 TRACE("Recording from byte=%u to byte=%u\n", wmw
->dwPosition
, end
);
1049 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
,
1050 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
1051 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_ABORTED
);
1054 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1055 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1057 wmw
->ckWaveData
.cksize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->ckWaveData
.cksize
);
1059 /* Go back to the beginning of the chunk plus the requested position */
1060 /* FIXME: I'm not sure this is correct, notably because some data linked to
1061 * the decompression state machine will not be correctly initialized.
1062 * Try it this way (other way would be to decompress from 0 up to dwPosition
1063 * and to start sending to hWave when dwPosition is reached).
1065 mmioSeek(wmw
->hFile
, wmw
->ckWaveData
.dwDataOffset
+ wmw
->dwPosition
, SEEK_SET
); /* >= 0 */
1067 /* By default the device will be opened for output, the MCI_CUE function is there to
1068 * change from output to input and back
1070 /* FIXME: how to choose between several output channels ? here mapper is forced */
1071 dwRet
= waveInOpen((HWAVEIN
*)&wmw
->hWave
, WAVE_MAPPER
, wmw
->lpWaveFormat
,
1072 (DWORD_PTR
)WAVE_mciRecordCallback
, (DWORD_PTR
)wmw
, CALLBACK_FUNCTION
);
1074 if (dwRet
!= MMSYSERR_NOERROR
) {
1075 TRACE("Can't open low level audio device %d\n", dwRet
);
1076 dwRet
= MCIERR_DEVICE_OPEN
;
1081 /* make it so that 3 buffers per second are needed */
1082 bufsize
= WAVE_ALIGN_ON_BLOCK(wmw
, wmw
->lpWaveFormat
->nAvgBytesPerSec
/ 3);
1084 waveHdr
= HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR
) + 2 * bufsize
);
1085 waveHdr
[0].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
);
1086 waveHdr
[1].lpData
= (char*)waveHdr
+ 2 * sizeof(WAVEHDR
) + bufsize
;
1087 waveHdr
[0].dwUser
= waveHdr
[1].dwUser
= 0L;
1088 waveHdr
[0].dwLoops
= waveHdr
[1].dwLoops
= 0L;
1089 waveHdr
[0].dwFlags
= waveHdr
[1].dwFlags
= 0L;
1090 waveHdr
[0].dwBufferLength
= waveHdr
[1].dwBufferLength
= bufsize
;
1092 if (waveInPrepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1093 waveInPrepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1094 dwRet
= MCIERR_INTERNAL
;
1098 if (waveInAddBuffer(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
)) ||
1099 waveInAddBuffer(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
))) {
1100 dwRet
= MCIERR_INTERNAL
;
1104 wmw
->hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1105 wmw
->dwEventCount
= 1L; /* for first buffer */
1107 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw
->dwPosition
, end
- wmw
->dwPosition
);
1109 dwRet
= waveInStart(wmw
->hWave
);
1111 if (hEvent
) SetEvent(hEvent
);
1113 while (wmw
->dwPosition
< end
&& wmw
->dwStatus
!= MCI_MODE_STOP
&& wmw
->dwStatus
!= MCI_MODE_NOT_READY
) {
1114 WAVE_mciRecordWaitDone(wmw
);
1116 /* Grab callback before another thread kicks in after we change dwStatus. */
1117 if (dwFlags
& MCI_NOTIFY
) {
1118 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1119 dwFlags
&= ~MCI_NOTIFY
;
1121 /* needed so that the callback above won't add again the buffers returned by the reset */
1122 wmw
->dwStatus
= MCI_MODE_STOP
;
1124 waveInReset(wmw
->hWave
);
1126 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[0], sizeof(WAVEHDR
));
1127 waveInUnprepareHeader(wmw
->hWave
, &waveHdr
[1], sizeof(WAVEHDR
));
1132 if (dwFlags
& MCI_NOTIFY
)
1133 oldcb
= InterlockedExchangePointer(&wmw
->hCallback
, NULL
);
1135 HeapFree(GetProcessHeap(), 0, waveHdr
);
1138 waveInClose(wmw
->hWave
);
1141 CloseHandle(wmw
->hEvent
);
1143 wmw
->dwStatus
= MCI_MODE_STOP
;
1145 if (oldcb
) mciDriverNotify(oldcb
, wDevID
,
1146 dwRet
? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1152 /**************************************************************************
1153 * WAVE_mciPause [internal]
1155 static DWORD
WAVE_mciPause(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1158 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1160 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1162 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1164 switch (wmw
->dwStatus
) {
1166 dwRet
= waveOutPause(wmw
->hWave
);
1167 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1168 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1169 ERR("waveOutPause error %d\n",dwRet
);
1170 dwRet
= MCIERR_INTERNAL
;
1173 case MCI_MODE_RECORD
:
1174 dwRet
= waveInStop(wmw
->hWave
);
1175 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PAUSE
;
1177 ERR("waveInStop error %d\n",dwRet
);
1178 dwRet
= MCIERR_INTERNAL
;
1181 case MCI_MODE_PAUSE
:
1182 dwRet
= MMSYSERR_NOERROR
;
1185 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1187 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1188 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1192 /**************************************************************************
1193 * WAVE_mciResume [internal]
1195 static DWORD
WAVE_mciResume(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1197 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1200 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1202 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1204 switch (wmw
->dwStatus
) {
1205 case MCI_MODE_PAUSE
:
1206 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1208 dwRet
= waveInStart(wmw
->hWave
);
1209 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_RECORD
;
1211 ERR("waveInStart error %d\n",dwRet
);
1212 dwRet
= MCIERR_INTERNAL
;
1215 dwRet
= waveOutRestart(wmw
->hWave
);
1216 if (dwRet
==MMSYSERR_NOERROR
) wmw
->dwStatus
= MCI_MODE_PLAY
;
1218 ERR("waveOutRestart error %d\n",dwRet
);
1219 dwRet
= MCIERR_INTERNAL
;
1224 case MCI_MODE_RECORD
:
1225 dwRet
= MMSYSERR_NOERROR
;
1228 dwRet
= MCIERR_NONAPPLICABLE_FUNCTION
;
1230 if (MMSYSERR_NOERROR
==dwRet
&& (dwFlags
& MCI_NOTIFY
) && lpParms
)
1231 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1235 /**************************************************************************
1236 * WAVE_mciSeek [internal]
1238 static DWORD
WAVE_mciSeek(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1240 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1241 DWORD position
, dwRet
;
1243 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1245 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1246 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1248 position
= dwFlags
& (MCI_SEEK_TO_START
|MCI_SEEK_TO_END
|MCI_TO
);
1249 if (!position
) return MCIERR_MISSING_PARAMETER
;
1250 if (position
&(position
-1)) return MCIERR_FLAGS_NOT_COMPATIBLE
;
1252 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1253 dwRet
= WAVE_mciStop(wDevID
, MCI_WAIT
, 0);
1254 if (dwRet
!= MMSYSERR_NOERROR
) return dwRet
;
1256 if (dwFlags
& MCI_TO
) {
1257 position
= WAVE_ConvertTimeFormatToByte(wmw
, lpParms
->dwTo
);
1258 if (position
> wmw
->ckWaveData
.cksize
)
1259 return MCIERR_OUTOFRANGE
;
1260 } else if (dwFlags
& MCI_SEEK_TO_START
) {
1263 position
= wmw
->ckWaveData
.cksize
;
1265 /* Seek rounds down, unless at end */
1266 if (position
!= wmw
->ckWaveData
.cksize
) {
1267 position
/= wmw
->lpWaveFormat
->nBlockAlign
;
1268 position
*= wmw
->lpWaveFormat
->nBlockAlign
;
1270 wmw
->dwPosition
= position
;
1271 TRACE("Seeking to position=%u bytes\n", position
);
1273 if (dwFlags
& MCI_NOTIFY
)
1274 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1276 return MMSYSERR_NOERROR
;
1279 /**************************************************************************
1280 * WAVE_mciSet [internal]
1282 static DWORD
WAVE_mciSet(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1284 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1286 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1288 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1289 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1291 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1292 switch (lpParms
->dwTimeFormat
) {
1293 case MCI_FORMAT_MILLISECONDS
:
1294 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1295 wmw
->dwMciTimeFormat
= MCI_FORMAT_MILLISECONDS
;
1297 case MCI_FORMAT_BYTES
:
1298 TRACE("MCI_FORMAT_BYTES !\n");
1299 wmw
->dwMciTimeFormat
= MCI_FORMAT_BYTES
;
1301 case MCI_FORMAT_SAMPLES
:
1302 TRACE("MCI_FORMAT_SAMPLES !\n");
1303 wmw
->dwMciTimeFormat
= MCI_FORMAT_SAMPLES
;
1306 WARN("Bad time format %u!\n", lpParms
->dwTimeFormat
);
1307 return MCIERR_BAD_TIME_FORMAT
;
1310 if (dwFlags
& MCI_SET_VIDEO
) {
1311 TRACE("No support for video !\n");
1312 return MCIERR_UNSUPPORTED_FUNCTION
;
1314 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1315 TRACE("No support for door open !\n");
1316 return MCIERR_UNSUPPORTED_FUNCTION
;
1318 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1319 TRACE("No support for door close !\n");
1320 return MCIERR_UNSUPPORTED_FUNCTION
;
1322 if (dwFlags
& MCI_SET_AUDIO
) {
1323 if (dwFlags
& MCI_SET_ON
) {
1324 TRACE("MCI_SET_ON audio !\n");
1325 } else if (dwFlags
& MCI_SET_OFF
) {
1326 TRACE("MCI_SET_OFF audio !\n");
1328 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1329 return MCIERR_BAD_INTEGER
;
1332 switch (lpParms
->dwAudio
)
1334 case MCI_SET_AUDIO_ALL
: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1335 case MCI_SET_AUDIO_LEFT
: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1336 case MCI_SET_AUDIO_RIGHT
: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1337 default: WARN("Unknown audio channel %u\n", lpParms
->dwAudio
); break;
1340 if (dwFlags
& MCI_WAVE_INPUT
)
1341 TRACE("MCI_WAVE_INPUT !\n");
1342 if (dwFlags
& MCI_WAVE_OUTPUT
)
1343 TRACE("MCI_WAVE_OUTPUT !\n");
1344 if (dwFlags
& MCI_WAVE_SET_ANYINPUT
)
1345 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1346 if (dwFlags
& MCI_WAVE_SET_ANYOUTPUT
)
1347 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1348 /* Set wave format parameters is refused after Open or Record.*/
1349 if (dwFlags
& MCI_WAVE_SET_FORMATTAG
) {
1350 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wFormatTag
);
1351 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1352 if (((LPMCI_WAVE_SET_PARMS
)lpParms
)->wFormatTag
!= WAVE_FORMAT_PCM
)
1353 return MCIERR_OUTOFRANGE
;
1355 if (dwFlags
& MCI_WAVE_SET_AVGBYTESPERSEC
) {
1356 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1357 wmw
->wfxRef
.nAvgBytesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nAvgBytesPerSec
;
1358 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw
->wfxRef
.nAvgBytesPerSec
);
1360 if (dwFlags
& MCI_WAVE_SET_BITSPERSAMPLE
) {
1361 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1362 wmw
->wfxRef
.wBitsPerSample
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->wBitsPerSample
;
1363 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw
->wfxRef
.wBitsPerSample
);
1365 if (dwFlags
& MCI_WAVE_SET_BLOCKALIGN
) {
1366 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1367 wmw
->wfxRef
.nBlockAlign
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nBlockAlign
;
1368 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw
->wfxRef
.nBlockAlign
);
1370 if (dwFlags
& MCI_WAVE_SET_CHANNELS
) {
1371 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1372 wmw
->wfxRef
.nChannels
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nChannels
;
1373 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw
->wfxRef
.nChannels
);
1375 if (dwFlags
& MCI_WAVE_SET_SAMPLESPERSEC
) {
1376 if (wmw
->lpWaveFormat
!= &wmw
->wfxRef
) return MCIERR_NONAPPLICABLE_FUNCTION
;
1377 wmw
->wfxRef
.nSamplesPerSec
= ((LPMCI_WAVE_SET_PARMS
)lpParms
)->nSamplesPerSec
;
1378 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw
->wfxRef
.nSamplesPerSec
);
1380 if (dwFlags
& MCI_NOTIFY
)
1381 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1385 /**************************************************************************
1386 * WAVE_mciSave [internal]
1388 static DWORD
WAVE_mciSave(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_SAVE_PARMSW lpParms
)
1390 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1391 DWORD ret
= MCIERR_FILE_NOT_SAVED
, tmpRet
;
1393 TRACE("%d, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1394 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1395 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1397 if (dwFlags
& MCI_WAIT
)
1399 FIXME("MCI_WAIT not implemented\n");
1401 WAVE_mciStop(wDevID
, 0, NULL
);
1403 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckWaveData
, 0);
1404 ret
= mmioAscend(wmw
->hFile
, &wmw
->ckMainRIFF
, 0);
1406 ret
= mmioClose(wmw
->hFile
, 0);
1410 If the destination file already exists, it has to be overwritten. (Behaviour
1411 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1412 my applications. We are making use of mmioRename, which WILL NOT overwrite
1413 the destination file (which is what Windows does, also verified in Win2K)
1414 So, lets delete the destination file before calling mmioRename. If the
1415 destination file DOESN'T exist, the delete will fail silently. Let's also be
1416 careful not to lose our previous error code.
1418 tmpRet
= GetLastError();
1419 DeleteFileW (lpParms
->lpfilename
);
1420 SetLastError(tmpRet
);
1422 /* FIXME: Open file.wav; Save; must not rename the original file.
1423 * Nor must Save a.wav; Save b.wav rename a. */
1424 if (0 == mmioRenameW(wmw
->lpFileName
, lpParms
->lpfilename
, 0, 0 )) {
1425 ret
= MMSYSERR_NOERROR
;
1428 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1429 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1431 if (ret
== MMSYSERR_NOERROR
)
1432 ret
= WAVE_mciOpenFile(wmw
, lpParms
->lpfilename
);
1437 /**************************************************************************
1438 * WAVE_mciStatus [internal]
1440 static DWORD
WAVE_mciStatus(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
1442 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1445 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1446 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1447 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1449 if (dwFlags
& MCI_STATUS_ITEM
) {
1450 switch (lpParms
->dwItem
) {
1451 case MCI_STATUS_CURRENT_TRACK
:
1452 lpParms
->dwReturn
= 1;
1453 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms
->dwReturn
);
1455 case MCI_STATUS_LENGTH
:
1457 lpParms
->dwReturn
= 0;
1458 return MCIERR_UNSUPPORTED_FUNCTION
;
1460 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1461 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
, wmw
->ckWaveData
.cksize
, &ret
);
1462 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms
->dwReturn
);
1464 case MCI_STATUS_MODE
:
1465 TRACE("MCI_STATUS_MODE => %u\n", wmw
->dwStatus
);
1466 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwStatus
, wmw
->dwStatus
);
1467 ret
= MCI_RESOURCE_RETURNED
;
1469 case MCI_STATUS_MEDIA_PRESENT
:
1470 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1471 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1472 ret
= MCI_RESOURCE_RETURNED
;
1474 case MCI_STATUS_NUMBER_OF_TRACKS
:
1475 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1476 lpParms
->dwReturn
= 1;
1477 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms
->dwReturn
);
1479 case MCI_STATUS_POSITION
:
1481 lpParms
->dwReturn
= 0;
1482 return MCIERR_UNSUPPORTED_FUNCTION
;
1484 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1485 lpParms
->dwReturn
= WAVE_ConvertByteToTimeFormat(wmw
,
1486 (dwFlags
& MCI_STATUS_START
) ? 0 : wmw
->dwPosition
,
1488 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1489 (dwFlags
& MCI_STATUS_START
) ? "start" : "current", lpParms
->dwReturn
);
1491 case MCI_STATUS_READY
:
1492 lpParms
->dwReturn
= (wmw
->dwStatus
== MCI_MODE_NOT_READY
) ?
1493 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1494 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms
->dwReturn
));
1495 ret
= MCI_RESOURCE_RETURNED
;
1497 case MCI_STATUS_TIME_FORMAT
:
1498 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmw
->dwMciTimeFormat
, MCI_FORMAT_RETURN_BASE
+ wmw
->dwMciTimeFormat
);
1499 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms
->dwReturn
);
1500 ret
= MCI_RESOURCE_RETURNED
;
1502 case MCI_WAVE_INPUT
:
1503 TRACE("MCI_WAVE_INPUT !\n");
1504 lpParms
->dwReturn
= 0;
1505 ret
= MCIERR_WAVE_INPUTUNSPECIFIED
;
1507 case MCI_WAVE_OUTPUT
:
1508 TRACE("MCI_WAVE_OUTPUT !\n");
1511 if (waveOutGetID(wmw
->hWave
, &id
) == MMSYSERR_NOERROR
) {
1512 lpParms
->dwReturn
= id
;
1514 lpParms
->dwReturn
= 0;
1515 ret
= MCIERR_WAVE_OUTPUTUNSPECIFIED
;
1519 /* It is always ok to query wave format parameters,
1520 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1521 case MCI_WAVE_STATUS_AVGBYTESPERSEC
:
1522 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nAvgBytesPerSec
;
1523 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms
->dwReturn
);
1525 case MCI_WAVE_STATUS_BITSPERSAMPLE
:
1526 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wBitsPerSample
;
1527 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms
->dwReturn
);
1529 case MCI_WAVE_STATUS_BLOCKALIGN
:
1530 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nBlockAlign
;
1531 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms
->dwReturn
);
1533 case MCI_WAVE_STATUS_CHANNELS
:
1534 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nChannels
;
1535 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms
->dwReturn
);
1537 case MCI_WAVE_STATUS_FORMATTAG
:
1538 lpParms
->dwReturn
= wmw
->lpWaveFormat
->wFormatTag
;
1539 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms
->dwReturn
);
1541 case MCI_WAVE_STATUS_SAMPLESPERSEC
:
1542 lpParms
->dwReturn
= wmw
->lpWaveFormat
->nSamplesPerSec
;
1543 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms
->dwReturn
);
1545 case MCI_WAVE_STATUS_LEVEL
:
1546 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1547 lpParms
->dwReturn
= 0xAAAA5555;
1550 WARN("unknown command %08X !\n", lpParms
->dwItem
);
1551 return MCIERR_UNRECOGNIZED_COMMAND
;
1554 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1555 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1559 /**************************************************************************
1560 * WAVE_mciGetDevCaps [internal]
1562 static DWORD
WAVE_mciGetDevCaps(MCIDEVICEID wDevID
, DWORD dwFlags
,
1563 LPMCI_GETDEVCAPS_PARMS lpParms
)
1565 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1568 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1570 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1571 if (wmw
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1573 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
1574 switch(lpParms
->dwItem
) {
1575 case MCI_GETDEVCAPS_DEVICE_TYPE
:
1576 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO
, MCI_DEVTYPE_WAVEFORM_AUDIO
);
1577 ret
= MCI_RESOURCE_RETURNED
;
1579 case MCI_GETDEVCAPS_HAS_AUDIO
:
1580 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1581 ret
= MCI_RESOURCE_RETURNED
;
1583 case MCI_GETDEVCAPS_HAS_VIDEO
:
1584 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1585 ret
= MCI_RESOURCE_RETURNED
;
1587 case MCI_GETDEVCAPS_USES_FILES
:
1588 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1589 ret
= MCI_RESOURCE_RETURNED
;
1591 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
1592 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1593 ret
= MCI_RESOURCE_RETURNED
;
1595 case MCI_GETDEVCAPS_CAN_RECORD
:
1596 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1597 ret
= MCI_RESOURCE_RETURNED
;
1599 case MCI_GETDEVCAPS_CAN_EJECT
:
1600 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
1601 ret
= MCI_RESOURCE_RETURNED
;
1603 case MCI_GETDEVCAPS_CAN_PLAY
:
1604 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1605 ret
= MCI_RESOURCE_RETURNED
;
1607 case MCI_GETDEVCAPS_CAN_SAVE
:
1608 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
1609 ret
= MCI_RESOURCE_RETURNED
;
1611 case MCI_WAVE_GETDEVCAPS_INPUTS
:
1612 lpParms
->dwReturn
= waveInGetNumDevs();
1614 case MCI_WAVE_GETDEVCAPS_OUTPUTS
:
1615 lpParms
->dwReturn
= waveOutGetNumDevs();
1618 FIXME("Unknown capability (%08x) !\n", lpParms
->dwItem
);
1619 return MCIERR_UNRECOGNIZED_COMMAND
;
1622 WARN("No GetDevCaps-Item !\n");
1623 return MCIERR_UNRECOGNIZED_COMMAND
;
1625 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
1626 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1630 /**************************************************************************
1631 * WAVE_mciInfo [internal]
1633 static DWORD
WAVE_mciInfo(MCIDEVICEID wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSW lpParms
)
1637 WINE_MCIWAVE
* wmw
= WAVE_mciGetOpenDev(wDevID
);
1639 TRACE("(%u, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1641 if (!lpParms
|| !lpParms
->lpstrReturn
)
1642 return MCIERR_NULL_PARAMETER_BLOCK
;
1645 ret
= MCIERR_INVALID_DEVICE_ID
;
1647 static const WCHAR wszAudio
[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1648 static const WCHAR wszWaveIn
[] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1649 static const WCHAR wszWaveOut
[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1651 TRACE("buf=%p, len=%u\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
1653 switch (dwFlags
& ~(MCI_WAIT
|MCI_NOTIFY
)) {
1654 case MCI_INFO_PRODUCT
: str
= wszAudio
; break;
1655 case MCI_INFO_FILE
: str
= wmw
->lpFileName
; break;
1656 case MCI_WAVE_INPUT
: str
= wszWaveIn
; break;
1657 case MCI_WAVE_OUTPUT
: str
= wszWaveOut
; break;
1659 WARN("Don't know this info command (%u)\n", dwFlags
);
1660 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
1664 if (strlenW(str
) + 1 > lpParms
->dwRetSize
) {
1665 ret
= MCIERR_PARAM_OVERFLOW
;
1667 lstrcpynW(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
1670 lpParms
->lpstrReturn
[0] = 0;
1672 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
1673 WAVE_mciNotify(lpParms
->dwCallback
, wmw
, MCI_NOTIFY_SUCCESSFUL
);
1677 /**************************************************************************
1678 * DriverProc (MCIWAVE.@)
1680 LRESULT CALLBACK
MCIWAVE_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
1681 LPARAM dwParam1
, LPARAM dwParam2
)
1683 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1684 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1687 case DRV_LOAD
: return 1;
1688 case DRV_FREE
: return 1;
1689 case DRV_OPEN
: return WAVE_drvOpen((LPCWSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSW
)dwParam2
);
1690 case DRV_CLOSE
: return WAVE_drvClose(dwDevID
);
1691 case DRV_ENABLE
: return 1;
1692 case DRV_DISABLE
: return 1;
1693 case DRV_QUERYCONFIGURE
: return 1;
1694 case DRV_CONFIGURE
: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK
); return 1;
1695 case DRV_INSTALL
: return DRVCNF_RESTART
;
1696 case DRV_REMOVE
: return DRVCNF_RESTART
;
1699 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1702 case MCI_OPEN_DRIVER
: return WAVE_mciOpen (dwDevID
, dwParam1
, (LPMCI_WAVE_OPEN_PARMSW
) dwParam2
);
1703 case MCI_CLOSE_DRIVER
: return WAVE_mciClose (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1704 case MCI_CUE
: return WAVE_mciCue (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1705 case MCI_PLAY
: return WAVE_mciPlay (dwDevID
, dwParam1
, dwParam2
, NULL
);
1706 case MCI_RECORD
: return WAVE_mciRecord (dwDevID
, dwParam1
, dwParam2
, NULL
);
1707 case MCI_STOP
: return WAVE_mciStop (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1708 case MCI_SET
: return WAVE_mciSet (dwDevID
, dwParam1
, (LPMCI_SET_PARMS
) dwParam2
);
1709 case MCI_PAUSE
: return WAVE_mciPause (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1710 case MCI_RESUME
: return WAVE_mciResume (dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
) dwParam2
);
1711 case MCI_STATUS
: return WAVE_mciStatus (dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
) dwParam2
);
1712 case MCI_GETDEVCAPS
: return WAVE_mciGetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
) dwParam2
);
1713 case MCI_INFO
: return WAVE_mciInfo (dwDevID
, dwParam1
, (LPMCI_INFO_PARMSW
) dwParam2
);
1714 case MCI_SEEK
: return WAVE_mciSeek (dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
) dwParam2
);
1715 case MCI_SAVE
: return WAVE_mciSave (dwDevID
, dwParam1
, (LPMCI_SAVE_PARMSW
) dwParam2
);
1716 /* commands that should be supported */
1731 FIXME("Unsupported yet command [%u]\n", wMsg
);
1734 TRACE("Unsupported command [%u]\n", wMsg
);
1736 /* option which can be silenced */
1741 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1744 FIXME("is probably wrong msg [%u]\n", wMsg
);
1745 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1747 return MCIERR_UNRECOGNIZED_COMMAND
;