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