mciwave: Implement Cue as no-op.
[wine/multimedia.git] / dlls / mciwave / mciwave.c
blob5437c58a5e6063cbe21b01349e3ca1e74a75420a
1 /*
2 * Wine Driver for MCI wave forms
4 * Copyright 1994 Martin Ayotte
5 * 1999,2000,2005 Eric Pouech
6 * 2000 Francois Jacques
7 * 2009 Jörg Höhle
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
24 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "mmddk.h"
31 #include "wownt32.h"
32 #include "digitalv.h"
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
38 typedef struct {
39 UINT wDevID;
40 HANDLE hWave;
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 */
45 WAVEFORMATEX wfxRef;
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 */
55 } WINE_MCIWAVE;
57 /* ===================================================================
58 * ===================================================================
59 * FIXME: should be using the new mmThreadXXXX functions from WINMM
60 * instead of those
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);
68 struct SCA {
69 async_cmd cmd;
70 HANDLE evt;
71 UINT wDevID;
72 DWORD_PTR dwParam1;
73 DWORD_PTR dwParam2;
76 /**************************************************************************
77 * MCI_SCAStarter [internal]
79 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
81 struct SCA* sca = (struct SCA*)arg;
82 DWORD ret;
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);
90 return ret;
93 /**************************************************************************
94 * MCI_SendCommandAsync [internal]
96 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
97 DWORD_PTR dwParam2, UINT size)
99 HANDLE handles[2];
100 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
102 if (sca == 0)
103 return MCIERR_OUT_OF_MEMORY;
105 sca->wDevID = wDevID;
106 sca->cmd = cmd;
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);
115 } else {
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]);
123 sca->evt = NULL;
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]);
135 return 0;
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)
149 WINE_MCIWAVE* wmw;
151 if (modp == NULL) return 0xFFFFFFFF;
153 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
155 if (!wmw)
156 return 0;
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);
181 if (wmw) {
182 HeapFree(GetProcessHeap(), 0, wmw);
183 mciSetDriverData(dwDevID, 0);
184 return 1;
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);
198 return 0;
200 return wmw;
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)
223 DWORD ret = 0;
225 switch (wmw->dwMciTimeFormat) {
226 case MCI_FORMAT_MILLISECONDS:
227 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
228 break;
229 case MCI_FORMAT_BYTES:
230 ret = val;
231 break;
232 case MCI_FORMAT_SAMPLES:
233 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
234 break;
235 default:
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);
239 *lpRet = 0;
240 return ret;
243 /**************************************************************************
244 * WAVE_ConvertTimeFormatToByte [internal]
246 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
248 DWORD ret = 0;
250 switch (wmw->dwMciTimeFormat) {
251 case MCI_FORMAT_MILLISECONDS:
252 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
253 break;
254 case MCI_FORMAT_BYTES:
255 ret = val;
256 break;
257 case MCI_FORMAT_SAMPLES:
258 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
259 break;
260 default:
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);
264 return ret;
267 /**************************************************************************
268 * WAVE_mciReadFmt [internal]
270 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
272 MMCKINFO mmckInfo;
273 long r;
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);
310 return 0;
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))
346 goto err;
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)
356 goto err;
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;
378 *pwfx = wmw->wfxRef;
379 wmw->lpWaveFormat = pwfx;
382 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
383 goto err;
385 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
386 goto err;
388 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
389 goto err;
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))
397 goto err;
399 return 0;
401 err:
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];
409 WCHAR szPrefix[4];
410 DWORD dwRet = MMSYSERR_NOERROR;
412 szPrefix[0] = 'M';
413 szPrefix[1] = 'C';
414 szPrefix[2] = 'I';
415 szPrefix[3] = '\0';
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(),
423 HEAP_ZERO_MEMORY,
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);
438 if (*hFile == 0) {
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;
445 return dwRet;
448 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
450 LRESULT dwRet = MMSYSERR_NOERROR;
451 WCHAR* fn;
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;
470 else
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;
480 } else {
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;
489 } else {
490 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
495 return dwRet;
498 /**************************************************************************
499 * WAVE_mciOpen [internal]
501 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
503 DWORD dwRet = 0;
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;
520 wmw->nUseCount++;
522 wmw->fInput = FALSE;
523 wmw->hWave = 0;
524 wmw->dwStatus = MCI_MODE_NOT_READY;
525 wmw->hFile = 0;
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;
540 } else {
541 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
544 TRACE("hFile=%p\n", wmw->hFile);
546 if (dwRet == 0) {
547 wmw->dwPosition = 0;
549 wmw->dwStatus = MCI_MODE_STOP;
551 if (dwFlags & MCI_NOTIFY)
552 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
553 } else {
554 wmw->nUseCount--;
555 if (wmw->hFile != 0)
556 mmioClose(wmw->hFile, 0);
557 wmw->hFile = 0;
559 return dwRet;
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
581 * thread.
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)
604 DWORD dwRet = 0;
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) {
618 case MCI_MODE_PAUSE:
619 case MCI_MODE_PLAY:
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)
628 Sleep(10);
629 break;
632 /* sanity resets */
633 wmw->dwStatus = MCI_MODE_STOP;
635 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
636 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
638 return dwRet;
641 /**************************************************************************
642 * WAVE_mciClose [internal]
644 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
646 DWORD dwRet = 0;
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);
658 wmw->nUseCount--;
660 if (wmw->nUseCount == 0) {
661 if (wmw->hFile != 0) {
662 mmioClose(wmw->hFile, 0);
663 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);
678 return 0;
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;
690 switch (uMsg) {
691 case WOM_OPEN:
692 case WOM_CLOSE:
693 break;
694 case WOM_DONE:
695 InterlockedIncrement(&wmw->dwEventCount);
696 TRACE("Returning waveHdr=%lx\n", dwParam1);
697 SetEvent(wmw->hEvent);
698 break;
699 default:
700 ERR("Unknown uMsg=%d\n", uMsg);
704 /******************************************************************
705 * WAVE_mciPlayWaitDone [internal]
707 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
709 for (;;) {
710 ResetEvent(wmw->hEvent);
711 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
712 break;
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;
726 DWORD end;
727 LONG bufsize, count, left;
728 DWORD dwRet;
729 LPWAVEHDR waveHdr = NULL;
730 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
731 HANDLE oldcb;
732 int whidx;
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);
749 wmw->fInput = FALSE;
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;
789 end = 0xFFFFFFFF;
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);
802 oldcb = NULL;
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);
830 if (dwRet != 0) {
831 TRACE("Can't open low level audio device %d\n", dwRet);
832 dwRet = MCIERR_DEVICE_OPEN;
833 wmw->hWave = 0;
834 goto cleanUp;
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;
850 goto cleanUp;
853 whidx = 0;
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);
865 if (count < 1)
866 break;
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));
876 left -= count;
877 wmw->dwPosition += count;
878 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
880 WAVE_mciPlayWaitDone(wmw);
881 whidx ^= 1;
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));
892 dwRet = 0;
894 cleanUp:
895 if (dwFlags & MCI_NOTIFY)
896 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
898 HeapFree(GetProcessHeap(), 0, waveHdr);
900 if (wmw->hWave) {
901 waveOutClose(wmw->hWave);
902 wmw->hWave = 0;
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);
912 return dwRet;
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;
923 LPWAVEHDR lpWaveHdr;
924 LONG count = 0;
926 switch (uMsg) {
927 case WIM_OPEN:
928 case WIM_CLOSE:
929 break;
930 case WIM_DATA:
931 lpWaveHdr = (LPWAVEHDR) dwParam1;
933 InterlockedIncrement(&wmw->dwEventCount);
935 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
937 lpWaveHdr->dwFlags &= ~WHDR_DONE;
938 if (count > 0)
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
946 to fail. */
947 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
948 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
951 SetEvent(wmw->hEvent);
952 break;
953 default:
954 ERR("Unknown uMsg=%d\n", uMsg);
958 /******************************************************************
959 * WAVE_mciRecordWaitDone [internal]
961 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
963 for (;;) {
964 ResetEvent(wmw->hEvent);
965 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
966 break;
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;
980 DWORD end;
981 DWORD dwRet = MMSYSERR_NOERROR;
982 LONG bufsize;
983 LPWAVEHDR waveHdr = NULL;
984 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
985 HANDLE oldcb;
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
1000 wmw->fInput = TRUE;
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;
1025 /* new RIFF file */
1026 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1027 if (dwRet != 0) return dwRet;
1029 end = 0xFFFFFFFF;
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);
1043 oldcb = NULL;
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;
1074 wmw->hWave = 0;
1075 goto cleanUp;
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;
1092 goto cleanUp;
1095 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1096 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1097 dwRet = MCIERR_INTERNAL;
1098 goto cleanUp;
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));
1126 dwRet = 0;
1128 cleanUp:
1129 if (dwFlags & MCI_NOTIFY)
1130 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1132 HeapFree(GetProcessHeap(), 0, waveHdr);
1134 if (wmw->hWave) {
1135 waveInClose(wmw->hWave);
1136 wmw->hWave = 0;
1138 CloseHandle(wmw->hEvent);
1140 wmw->dwStatus = MCI_MODE_STOP;
1142 if (oldcb) mciDriverNotify(oldcb, wDevID,
1143 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1145 return dwRet;
1149 /**************************************************************************
1150 * WAVE_mciPause [internal]
1152 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1154 DWORD dwRet;
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) {
1162 case MCI_MODE_PLAY:
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;
1169 break;
1170 case MCI_MODE_RECORD:
1171 dwRet = waveInStop(wmw->hWave);
1172 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1173 else {
1174 ERR("waveInStop error %d\n",dwRet);
1175 dwRet = MCIERR_INTERNAL;
1177 break;
1178 case MCI_MODE_PAUSE:
1179 dwRet = MMSYSERR_NOERROR;
1180 break;
1181 default:
1182 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1184 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1185 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1186 return dwRet;
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);
1195 DWORD dwRet;
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. */
1204 if (wmw->fInput) {
1205 dwRet = waveInStart(wmw->hWave);
1206 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1207 else {
1208 ERR("waveInStart error %d\n",dwRet);
1209 dwRet = MCIERR_INTERNAL;
1211 } else {
1212 dwRet = waveOutRestart(wmw->hWave);
1213 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1214 else {
1215 ERR("waveOutRestart error %d\n",dwRet);
1216 dwRet = MCIERR_INTERNAL;
1219 break;
1220 case MCI_MODE_PLAY:
1221 case MCI_MODE_RECORD:
1222 dwRet = MMSYSERR_NOERROR;
1223 break;
1224 default:
1225 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1227 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1228 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1229 return dwRet;
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);
1252 } else {
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;
1282 break;
1283 case MCI_FORMAT_BYTES:
1284 TRACE("MCI_FORMAT_BYTES !\n");
1285 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1286 break;
1287 case MCI_FORMAT_SAMPLES:
1288 TRACE("MCI_FORMAT_SAMPLES !\n");
1289 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1290 break;
1291 default:
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");
1313 } else {
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);
1368 return 0;
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);
1393 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);
1418 return ret;
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);
1427 DWORD ret = 0;
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);
1438 break;
1439 case MCI_STATUS_LENGTH:
1440 if (!wmw->hFile) {
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);
1447 break;
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;
1452 break;
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;
1457 break;
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);
1462 break;
1463 case MCI_STATUS_POSITION:
1464 if (!wmw->hFile) {
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,
1471 &ret);
1472 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1473 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1474 break;
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;
1480 break;
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;
1485 break;
1486 case MCI_WAVE_INPUT:
1487 TRACE("MCI_WAVE_INPUT !\n");
1488 lpParms->dwReturn = 0;
1489 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1490 break;
1491 case MCI_WAVE_OUTPUT:
1492 TRACE("MCI_WAVE_OUTPUT !\n");
1494 UINT id;
1495 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1496 lpParms->dwReturn = id;
1497 } else {
1498 lpParms->dwReturn = 0;
1499 ret = MCIERR_WAVE_OUTPUTUNSPECIFIED;
1502 break;
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);
1508 break;
1509 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1510 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1511 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1512 break;
1513 case MCI_WAVE_STATUS_BLOCKALIGN:
1514 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1515 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1516 break;
1517 case MCI_WAVE_STATUS_CHANNELS:
1518 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1519 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1520 break;
1521 case MCI_WAVE_STATUS_FORMATTAG:
1522 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1523 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1524 break;
1525 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1526 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1527 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1528 break;
1529 case MCI_WAVE_STATUS_LEVEL:
1530 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1531 lpParms->dwReturn = 0xAAAA5555;
1532 break;
1533 default:
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);
1540 return ret;
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);
1550 DWORD ret = 0;
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;
1562 break;
1563 case MCI_GETDEVCAPS_HAS_AUDIO:
1564 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1565 ret = MCI_RESOURCE_RETURNED;
1566 break;
1567 case MCI_GETDEVCAPS_HAS_VIDEO:
1568 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1569 ret = MCI_RESOURCE_RETURNED;
1570 break;
1571 case MCI_GETDEVCAPS_USES_FILES:
1572 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1573 ret = MCI_RESOURCE_RETURNED;
1574 break;
1575 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1576 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1577 ret = MCI_RESOURCE_RETURNED;
1578 break;
1579 case MCI_GETDEVCAPS_CAN_RECORD:
1580 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1581 ret = MCI_RESOURCE_RETURNED;
1582 break;
1583 case MCI_GETDEVCAPS_CAN_EJECT:
1584 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1585 ret = MCI_RESOURCE_RETURNED;
1586 break;
1587 case MCI_GETDEVCAPS_CAN_PLAY:
1588 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1589 ret = MCI_RESOURCE_RETURNED;
1590 break;
1591 case MCI_GETDEVCAPS_CAN_SAVE:
1592 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1593 ret = MCI_RESOURCE_RETURNED;
1594 break;
1595 case MCI_WAVE_GETDEVCAPS_INPUTS:
1596 lpParms->dwReturn = 1;
1597 break;
1598 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1599 lpParms->dwReturn = 1;
1600 break;
1601 default:
1602 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1603 return MCIERR_UNRECOGNIZED_COMMAND;
1605 } else {
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);
1611 return ret;
1614 /**************************************************************************
1615 * WAVE_mciInfo [internal]
1617 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1619 DWORD ret = 0;
1620 LPCWSTR str = 0;
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;
1628 if (wmw == NULL) {
1629 ret = MCIERR_INVALID_DEVICE_ID;
1630 } else {
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;
1642 default:
1643 WARN("Don't know this info command (%u)\n", dwFlags);
1644 ret = MCIERR_UNRECOGNIZED_COMMAND;
1647 if (str) {
1648 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1649 ret = MCIERR_PARAM_OVERFLOW;
1650 } else {
1651 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1653 } else {
1654 lpParms->lpstrReturn[0] = 0;
1656 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1657 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1658 return ret;
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);
1670 switch (wMsg) {
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;
1685 switch (wMsg) {
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 */
1701 case MCI_LOAD:
1702 case MCI_FREEZE:
1703 case MCI_PUT:
1704 case MCI_REALIZE:
1705 case MCI_UNFREEZE:
1706 case MCI_UPDATE:
1707 case MCI_WHERE:
1708 case MCI_STEP:
1709 case MCI_SPIN:
1710 case MCI_ESCAPE:
1711 case MCI_COPY:
1712 case MCI_CUT:
1713 case MCI_DELETE:
1714 case MCI_PASTE:
1715 FIXME("Unsupported yet command [%u]\n", wMsg);
1716 break;
1717 case MCI_WINDOW:
1718 TRACE("Unsupported command [%u]\n", wMsg);
1719 break;
1720 /* option which can be silenced */
1721 case MCI_CONFIGURE:
1722 return 0;
1723 case MCI_OPEN:
1724 case MCI_CLOSE:
1725 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1726 break;
1727 default:
1728 FIXME("is probably wrong msg [%u]\n", wMsg);
1729 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1731 return MCIERR_UNRECOGNIZED_COMMAND;