wininet: Support the Cache-Control max-age directive for setting url cache entry...
[wine/testsucceed.git] / dlls / mciwave / mciwave.c
blob9a62f8ffc18f6c02825a7afa228045895bab69a1
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, 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 at 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;
571 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
572 wmw->lpFileName = NULL;
574 return dwRet;
577 /**************************************************************************
578 * WAVE_mciCue [internal]
580 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
582 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
584 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
586 /* Tests on systems without sound drivers show that Cue, like
587 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
588 * The first Cue Notify does not immediately return the
589 * notification, as if a player or recorder thread is started.
590 * PAUSE mode is reported when successful, but this mode is
591 * different from the normal Pause, because a) Pause then returns
592 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
593 * still accepted, returning the original notification as ABORTED.
594 * I.e. Cue allows subsequent format changes, unlike Record or
595 * Open file, closes winmm if the format changes and stops this
596 * thread.
597 * Wine creates one player or recorder thread per async. Play or
598 * Record command. Notification behaviour suggests that MS-W*
599 * reuses a single thread to improve response times. Having Cue
600 * start this thread early helps to improve Play/Record's initial
601 * response time. In effect, Cue is a performance hint, which
602 * justifies our almost no-op implementation.
605 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
606 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
608 if ((dwFlags & MCI_NOTIFY) && lpParms)
609 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
611 return MMSYSERR_NOERROR;
614 /**************************************************************************
615 * WAVE_mciStop [internal]
617 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
619 DWORD dwRet = 0;
620 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
622 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
624 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
626 if (wmw->dwStatus != MCI_MODE_STOP) {
627 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
628 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
631 /* wait for playback thread (if any) to exit before processing further */
632 switch (wmw->dwStatus) {
633 case MCI_MODE_PAUSE:
634 case MCI_MODE_PLAY:
635 case MCI_MODE_RECORD:
637 int oldStat = wmw->dwStatus;
638 wmw->dwStatus = MCI_MODE_NOT_READY;
639 if (oldStat == MCI_MODE_PAUSE)
640 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
642 while (wmw->dwStatus != MCI_MODE_STOP)
643 Sleep(10);
644 break;
647 /* sanity resets */
648 wmw->dwStatus = MCI_MODE_STOP;
650 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
651 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
653 return dwRet;
656 /**************************************************************************
657 * WAVE_mciClose [internal]
659 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
661 DWORD dwRet = 0;
662 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
664 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
666 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
668 if (wmw->dwStatus != MCI_MODE_STOP) {
669 /* mciStop handles MCI_NOTIFY_ABORTED */
670 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
673 wmw->nUseCount--;
675 if (wmw->nUseCount == 0) {
676 if (wmw->hFile != 0) {
677 mmioClose(wmw->hFile, 0);
678 wmw->hFile = 0;
682 if (wmw->lpWaveFormat != &wmw->wfxRef)
683 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
684 wmw->lpWaveFormat = &wmw->wfxRef;
685 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
686 wmw->lpFileName = NULL;
688 if ((dwFlags & MCI_NOTIFY) && lpParms) {
689 WAVE_mciNotify(lpParms->dwCallback, wmw,
690 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
693 return 0;
696 /**************************************************************************
697 * WAVE_mciPlayCallback [internal]
699 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
700 DWORD_PTR dwInstance,
701 LPARAM dwParam1, LPARAM dwParam2)
703 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
705 switch (uMsg) {
706 case WOM_OPEN:
707 case WOM_CLOSE:
708 break;
709 case WOM_DONE:
710 InterlockedIncrement(&wmw->dwEventCount);
711 TRACE("Returning waveHdr=%lx\n", dwParam1);
712 SetEvent(wmw->hEvent);
713 break;
714 default:
715 ERR("Unknown uMsg=%d\n", uMsg);
719 /******************************************************************
720 * WAVE_mciPlayWaitDone [internal]
722 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
724 for (;;) {
725 ResetEvent(wmw->hEvent);
726 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
727 break;
729 InterlockedIncrement(&wmw->dwEventCount);
731 WaitForSingleObject(wmw->hEvent, INFINITE);
735 /**************************************************************************
736 * WAVE_mciPlay [internal]
738 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
740 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
741 DWORD end;
742 LONG bufsize, count, left;
743 DWORD dwRet;
744 LPWAVEHDR waveHdr = NULL;
745 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
746 HANDLE oldcb;
747 int whidx;
749 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
751 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
752 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
754 if (wmw->hFile == 0) {
755 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
756 return MCIERR_FILE_NOT_FOUND;
759 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
760 /* FIXME: parameters (start/end) in lpParams may not be used */
761 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
764 /** This function will be called again by a thread when async is used.
765 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
766 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
768 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
769 return MCIERR_INTERNAL;
772 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
773 if (wmw->lpWaveFormat->nBlockAlign !=
774 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
775 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
776 wmw->lpWaveFormat->nBlockAlign,
777 wmw->lpWaveFormat->nChannels *
778 wmw->lpWaveFormat->wBitsPerSample/8);
779 wmw->lpWaveFormat->nBlockAlign =
780 wmw->lpWaveFormat->nChannels *
781 wmw->lpWaveFormat->wBitsPerSample/8;
783 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
784 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
785 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
786 wmw->lpWaveFormat->nAvgBytesPerSec,
787 wmw->lpWaveFormat->nSamplesPerSec *
788 wmw->lpWaveFormat->nBlockAlign);
789 wmw->lpWaveFormat->nAvgBytesPerSec =
790 wmw->lpWaveFormat->nSamplesPerSec *
791 wmw->lpWaveFormat->nBlockAlign;
795 end = wmw->ckWaveData.cksize;
796 if (dwFlags & MCI_TO) {
797 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
798 if (position > end) return MCIERR_OUTOFRANGE;
799 end = position;
801 if (dwFlags & MCI_FROM) {
802 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
803 if (position > end) return MCIERR_OUTOFRANGE;
804 /* Seek rounds down, so do we. */
805 position /= wmw->lpWaveFormat->nBlockAlign;
806 position *= wmw->lpWaveFormat->nBlockAlign;
807 wmw->dwPosition = position;
809 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
810 left = end - wmw->dwPosition;
811 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
813 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
814 wmw->dwStatus = MCI_MODE_PLAY;
816 if (!(dwFlags & MCI_WAIT)) {
817 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
818 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
821 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
823 oldcb = InterlockedExchangePointer(&wmw->hCallback,
824 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
825 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
826 oldcb = NULL;
828 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
829 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
831 /* go back to beginning of chunk plus the requested position */
832 /* FIXME: I'm not sure this is correct, notably because some data linked to
833 * the decompression state machine will not be correctly initialized.
834 * try it this way (other way would be to decompress from 0 up to dwPosition
835 * and to start sending to hWave when dwPosition is reached)
837 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
839 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat,
840 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
842 if (dwRet != 0) {
843 TRACE("Can't open low level audio device %d\n", dwRet);
844 dwRet = MCIERR_DEVICE_OPEN;
845 wmw->hWave = 0;
846 goto cleanUp;
849 /* make it so that 3 buffers per second are needed */
850 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
852 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
853 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
854 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
855 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
856 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
857 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
858 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
859 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
860 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
861 dwRet = MCIERR_INTERNAL;
862 goto cleanUp;
865 whidx = 0;
866 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
867 wmw->dwEventCount = 1L; /* for first buffer */
869 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
870 if (hEvent) SetEvent(hEvent);
872 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
873 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
874 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
875 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
876 if (count < 1)
877 break;
878 /* count is always <= bufsize, so this is correct regarding the
879 * waveOutPrepareHeader function
881 waveHdr[whidx].dwBufferLength = count;
882 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
883 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
884 &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
885 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
886 if (dwRet) {
887 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
888 dwRet = MCIERR_HARDWARE;
889 break;
891 left -= count;
892 wmw->dwPosition += count;
893 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
894 /* InterlockedDecrement if and only if waveOutWrite is successful */
895 WAVE_mciPlayWaitDone(wmw);
896 whidx ^= 1;
899 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
901 /* just to get rid of some race conditions between play, stop and pause */
902 waveOutReset(wmw->hWave);
904 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
905 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
907 cleanUp:
908 if (dwFlags & MCI_NOTIFY)
909 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
911 HeapFree(GetProcessHeap(), 0, waveHdr);
913 if (wmw->hWave) {
914 waveOutClose(wmw->hWave);
915 wmw->hWave = 0;
917 CloseHandle(wmw->hEvent);
919 wmw->dwStatus = MCI_MODE_STOP;
921 /* Let the potentially asynchronous commands support FAILURE notification. */
922 if (oldcb) mciDriverNotify(oldcb, wDevID,
923 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
925 return dwRet;
928 /**************************************************************************
929 * WAVE_mciRecordCallback [internal]
931 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
932 DWORD_PTR dwInstance,
933 LPARAM dwParam1, LPARAM dwParam2)
935 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
936 LPWAVEHDR lpWaveHdr;
937 LONG count = 0;
939 switch (uMsg) {
940 case WIM_OPEN:
941 case WIM_CLOSE:
942 break;
943 case WIM_DATA:
944 lpWaveHdr = (LPWAVEHDR) dwParam1;
946 InterlockedIncrement(&wmw->dwEventCount);
948 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
950 lpWaveHdr->dwFlags &= ~WHDR_DONE;
951 if (count > 0)
952 wmw->dwPosition += count;
953 /* else error reporting ?? */
954 if (wmw->dwStatus == MCI_MODE_RECORD)
956 /* Only queue up another buffer if we are recording. We could receive this
957 message also when waveInReset() is called, since it notifies on all wave
958 buffers that are outstanding. Queueing up more sometimes causes waveInClose
959 to fail. */
960 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
961 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
964 SetEvent(wmw->hEvent);
965 break;
966 default:
967 ERR("Unknown uMsg=%d\n", uMsg);
971 /******************************************************************
972 * WAVE_mciRecordWaitDone [internal]
974 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
976 for (;;) {
977 ResetEvent(wmw->hEvent);
978 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
979 break;
981 InterlockedIncrement(&wmw->dwEventCount);
983 WaitForSingleObject(wmw->hEvent, INFINITE);
987 /**************************************************************************
988 * WAVE_mciRecord [internal]
990 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
992 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
993 DWORD end;
994 DWORD dwRet = MMSYSERR_NOERROR;
995 LONG bufsize;
996 LPWAVEHDR waveHdr = NULL;
997 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
998 HANDLE oldcb;
1000 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1002 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1003 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1005 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1006 /* FIXME: parameters (start/end) in lpParams may not be used */
1007 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1010 /** This function will be called again by a thread when async is used.
1011 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1012 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1014 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
1015 return MCIERR_INTERNAL;
1018 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1019 wmw->dwStatus = MCI_MODE_RECORD;
1021 if (!(dwFlags & MCI_WAIT)) {
1022 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1023 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1026 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1027 * we don't modify the wave part of an existing file (ie. we always erase an
1028 * existing content, we don't overwrite)
1030 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
1031 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1032 if (dwRet != 0) return dwRet;
1034 /* new RIFF file, lpWaveFormat now valid */
1035 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1036 if (dwRet != 0) return dwRet;
1038 if (dwFlags & MCI_TO) {
1039 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1040 } else end = 0xFFFFFFFF;
1041 if (dwFlags & MCI_FROM) {
1042 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1043 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1044 /* Seek rounds down, so do we. */
1045 position /= wmw->lpWaveFormat->nBlockAlign;
1046 position *= wmw->lpWaveFormat->nBlockAlign;
1047 wmw->dwPosition = position;
1049 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1051 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1053 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1054 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1055 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1056 oldcb = NULL;
1058 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1059 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1061 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1063 /* Go back to the beginning of the chunk plus the requested position */
1064 /* FIXME: I'm not sure this is correct, notably because some data linked to
1065 * the decompression state machine will not be correctly initialized.
1066 * Try it this way (other way would be to decompress from 0 up to dwPosition
1067 * and to start sending to hWave when dwPosition is reached).
1069 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1071 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
1072 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1074 if (dwRet != MMSYSERR_NOERROR) {
1075 TRACE("Can't open low level audio device %d\n", dwRet);
1076 dwRet = MCIERR_DEVICE_OPEN;
1077 wmw->hWave = 0;
1078 goto cleanUp;
1081 /* make it so that 3 buffers per second are needed */
1082 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1084 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1085 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1086 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1087 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1088 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1089 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1090 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1092 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1093 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1094 dwRet = MCIERR_INTERNAL;
1095 goto cleanUp;
1098 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1099 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1100 dwRet = MCIERR_INTERNAL;
1101 goto cleanUp;
1104 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1105 wmw->dwEventCount = 1L; /* for first buffer */
1107 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1109 dwRet = waveInStart(wmw->hWave);
1111 if (hEvent) SetEvent(hEvent);
1113 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1114 WAVE_mciRecordWaitDone(wmw);
1116 /* Grab callback before another thread kicks in after we change dwStatus. */
1117 if (dwFlags & MCI_NOTIFY) {
1118 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1119 dwFlags &= ~MCI_NOTIFY;
1121 /* needed so that the callback above won't add again the buffers returned by the reset */
1122 wmw->dwStatus = MCI_MODE_STOP;
1124 waveInReset(wmw->hWave);
1126 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1127 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1129 dwRet = 0;
1131 cleanUp:
1132 if (dwFlags & MCI_NOTIFY)
1133 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1135 HeapFree(GetProcessHeap(), 0, waveHdr);
1137 if (wmw->hWave) {
1138 waveInClose(wmw->hWave);
1139 wmw->hWave = 0;
1141 CloseHandle(wmw->hEvent);
1143 wmw->dwStatus = MCI_MODE_STOP;
1145 if (oldcb) mciDriverNotify(oldcb, wDevID,
1146 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1148 return dwRet;
1152 /**************************************************************************
1153 * WAVE_mciPause [internal]
1155 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1157 DWORD dwRet;
1158 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1160 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1162 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1164 switch (wmw->dwStatus) {
1165 case MCI_MODE_PLAY:
1166 dwRet = waveOutPause(wmw->hWave);
1167 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1168 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1169 ERR("waveOutPause error %d\n",dwRet);
1170 dwRet = MCIERR_INTERNAL;
1172 break;
1173 case MCI_MODE_RECORD:
1174 dwRet = waveInStop(wmw->hWave);
1175 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1176 else {
1177 ERR("waveInStop error %d\n",dwRet);
1178 dwRet = MCIERR_INTERNAL;
1180 break;
1181 case MCI_MODE_PAUSE:
1182 dwRet = MMSYSERR_NOERROR;
1183 break;
1184 default:
1185 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1187 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1188 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1189 return dwRet;
1192 /**************************************************************************
1193 * WAVE_mciResume [internal]
1195 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1197 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1198 DWORD dwRet;
1200 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1202 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1204 switch (wmw->dwStatus) {
1205 case MCI_MODE_PAUSE:
1206 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1207 if (wmw->fInput) {
1208 dwRet = waveInStart(wmw->hWave);
1209 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1210 else {
1211 ERR("waveInStart error %d\n",dwRet);
1212 dwRet = MCIERR_INTERNAL;
1214 } else {
1215 dwRet = waveOutRestart(wmw->hWave);
1216 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1217 else {
1218 ERR("waveOutRestart error %d\n",dwRet);
1219 dwRet = MCIERR_INTERNAL;
1222 break;
1223 case MCI_MODE_PLAY:
1224 case MCI_MODE_RECORD:
1225 dwRet = MMSYSERR_NOERROR;
1226 break;
1227 default:
1228 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1230 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1231 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1232 return dwRet;
1235 /**************************************************************************
1236 * WAVE_mciSeek [internal]
1238 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1240 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1241 DWORD position, dwRet;
1243 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1245 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1246 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1248 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1249 if (!position) return MCIERR_MISSING_PARAMETER;
1250 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
1252 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1253 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1254 if (dwRet != MMSYSERR_NOERROR) return dwRet;
1256 if (dwFlags & MCI_TO) {
1257 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1258 if (position > wmw->ckWaveData.cksize)
1259 return MCIERR_OUTOFRANGE;
1260 } else if (dwFlags & MCI_SEEK_TO_START) {
1261 position = 0;
1262 } else {
1263 position = wmw->ckWaveData.cksize;
1265 /* Seek rounds down, unless at end */
1266 if (position != wmw->ckWaveData.cksize) {
1267 position /= wmw->lpWaveFormat->nBlockAlign;
1268 position *= wmw->lpWaveFormat->nBlockAlign;
1270 wmw->dwPosition = position;
1271 TRACE("Seeking to position=%u bytes\n", position);
1273 if (dwFlags & MCI_NOTIFY)
1274 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1276 return MMSYSERR_NOERROR;
1279 /**************************************************************************
1280 * WAVE_mciSet [internal]
1282 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
1284 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1286 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1288 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1289 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1291 if (dwFlags & MCI_SET_TIME_FORMAT) {
1292 switch (lpParms->dwTimeFormat) {
1293 case MCI_FORMAT_MILLISECONDS:
1294 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1295 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1296 break;
1297 case MCI_FORMAT_BYTES:
1298 TRACE("MCI_FORMAT_BYTES !\n");
1299 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1300 break;
1301 case MCI_FORMAT_SAMPLES:
1302 TRACE("MCI_FORMAT_SAMPLES !\n");
1303 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1304 break;
1305 default:
1306 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1307 return MCIERR_BAD_TIME_FORMAT;
1310 if (dwFlags & MCI_SET_VIDEO) {
1311 TRACE("No support for video !\n");
1312 return MCIERR_UNSUPPORTED_FUNCTION;
1314 if (dwFlags & MCI_SET_DOOR_OPEN) {
1315 TRACE("No support for door open !\n");
1316 return MCIERR_UNSUPPORTED_FUNCTION;
1318 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1319 TRACE("No support for door close !\n");
1320 return MCIERR_UNSUPPORTED_FUNCTION;
1322 if (dwFlags & MCI_SET_AUDIO) {
1323 if (dwFlags & MCI_SET_ON) {
1324 TRACE("MCI_SET_ON audio !\n");
1325 } else if (dwFlags & MCI_SET_OFF) {
1326 TRACE("MCI_SET_OFF audio !\n");
1327 } else {
1328 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1329 return MCIERR_BAD_INTEGER;
1332 switch (lpParms->dwAudio)
1334 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1335 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1336 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1337 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1340 if (dwFlags & MCI_WAVE_INPUT) {
1341 TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
1342 if (lpParms->wInput >= waveInGetNumDevs())
1343 return MCIERR_OUTOFRANGE;
1344 if (wmw->wInput != (WORD)lpParms->wInput)
1345 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1346 wmw->wInput = lpParms->wInput;
1348 if (dwFlags & MCI_WAVE_OUTPUT) {
1349 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
1350 if (lpParms->wOutput >= waveOutGetNumDevs())
1351 return MCIERR_OUTOFRANGE;
1352 if (wmw->wOutput != (WORD)lpParms->wOutput)
1353 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1354 wmw->wOutput = lpParms->wOutput;
1356 if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
1357 TRACE("MCI_WAVE_SET_ANYINPUT\n");
1358 if (wmw->wInput != (WORD)lpParms->wInput)
1359 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1360 wmw->wInput = WAVE_MAPPER;
1362 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
1363 TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1364 if (wmw->wOutput != (WORD)lpParms->wOutput)
1365 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1366 wmw->wOutput = WAVE_MAPPER;
1368 /* Set wave format parameters is refused after Open or Record.*/
1369 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1370 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
1371 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1372 if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
1373 return MCIERR_OUTOFRANGE;
1375 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1376 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1377 wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
1378 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1380 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1381 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1382 wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
1383 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1385 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1386 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1387 wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
1388 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1390 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1391 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1392 wmw->wfxRef.nChannels = lpParms->nChannels;
1393 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1395 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1396 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1397 wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
1398 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1400 if (dwFlags & MCI_NOTIFY)
1401 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1402 return 0;
1405 /**************************************************************************
1406 * WAVE_mciSave [internal]
1408 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1410 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1411 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1413 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1414 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1415 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1417 if (dwFlags & MCI_WAIT)
1419 FIXME("MCI_WAIT not implemented\n");
1421 WAVE_mciStop(wDevID, 0, NULL);
1423 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1424 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1426 ret = mmioClose(wmw->hFile, 0);
1427 wmw->hFile = 0;
1430 If the destination file already exists, it has to be overwritten. (Behaviour
1431 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1432 my applications. We are making use of mmioRename, which WILL NOT overwrite
1433 the destination file (which is what Windows does, also verified in Win2K)
1434 So, lets delete the destination file before calling mmioRename. If the
1435 destination file DOESN'T exist, the delete will fail silently. Let's also be
1436 careful not to lose our previous error code.
1438 tmpRet = GetLastError();
1439 DeleteFileW (lpParms->lpfilename);
1440 SetLastError(tmpRet);
1442 /* FIXME: Open file.wav; Save; must not rename the original file.
1443 * Nor must Save a.wav; Save b.wav rename a. */
1444 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1445 ret = MMSYSERR_NOERROR;
1448 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1449 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1451 if (ret == MMSYSERR_NOERROR)
1452 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1454 return ret;
1457 /**************************************************************************
1458 * WAVE_mciStatus [internal]
1460 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1462 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1463 DWORD ret = 0;
1465 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1466 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1467 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1468 if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER;
1470 if (dwFlags & MCI_STATUS_ITEM) {
1471 switch (lpParms->dwItem) {
1472 case MCI_STATUS_CURRENT_TRACK:
1473 lpParms->dwReturn = 1;
1474 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1475 break;
1476 case MCI_STATUS_LENGTH:
1477 if (!wmw->hFile) {
1478 lpParms->dwReturn = 0;
1479 return MCIERR_UNSUPPORTED_FUNCTION;
1481 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1482 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1483 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1484 break;
1485 case MCI_STATUS_MODE:
1486 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1487 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1488 ret = MCI_RESOURCE_RETURNED;
1489 break;
1490 case MCI_STATUS_MEDIA_PRESENT:
1491 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1492 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1493 ret = MCI_RESOURCE_RETURNED;
1494 break;
1495 case MCI_STATUS_NUMBER_OF_TRACKS:
1496 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1497 lpParms->dwReturn = 1;
1498 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1499 break;
1500 case MCI_STATUS_POSITION:
1501 if (!wmw->hFile) {
1502 lpParms->dwReturn = 0;
1503 return MCIERR_UNSUPPORTED_FUNCTION;
1505 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1506 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1507 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1508 &ret);
1509 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1510 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1511 break;
1512 case MCI_STATUS_READY:
1513 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1514 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1515 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1516 ret = MCI_RESOURCE_RETURNED;
1517 break;
1518 case MCI_STATUS_TIME_FORMAT:
1519 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1520 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1521 ret = MCI_RESOURCE_RETURNED;
1522 break;
1523 case MCI_WAVE_INPUT:
1524 if (wmw->wInput != (WORD)WAVE_MAPPER)
1525 lpParms->dwReturn = wmw->wInput;
1526 else {
1527 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1528 ret = MCI_RESOURCE_RETURNED;
1530 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
1531 break;
1532 case MCI_WAVE_OUTPUT:
1533 if (wmw->wOutput != (WORD)WAVE_MAPPER)
1534 lpParms->dwReturn = wmw->wOutput;
1535 else {
1536 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1537 ret = MCI_RESOURCE_RETURNED;
1539 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
1540 break;
1541 /* It is always ok to query wave format parameters,
1542 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1543 case MCI_WAVE_STATUS_FORMATTAG:
1544 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
1545 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1546 else {
1547 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
1548 ret = MCI_RESOURCE_RETURNED;
1550 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1551 break;
1552 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1553 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1554 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1555 break;
1556 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1557 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1558 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1559 break;
1560 case MCI_WAVE_STATUS_BLOCKALIGN:
1561 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1562 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1563 break;
1564 case MCI_WAVE_STATUS_CHANNELS:
1565 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1566 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1567 break;
1568 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1569 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1570 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1571 break;
1572 case MCI_WAVE_STATUS_LEVEL:
1573 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1574 lpParms->dwReturn = 0xAAAA5555;
1575 break;
1576 default:
1577 WARN("unknown command %08X !\n", lpParms->dwItem);
1578 return MCIERR_UNSUPPORTED_FUNCTION;
1581 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1582 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1583 return ret;
1586 /**************************************************************************
1587 * WAVE_mciGetDevCaps [internal]
1589 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1590 LPMCI_GETDEVCAPS_PARMS lpParms)
1592 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1593 DWORD ret = 0;
1595 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1597 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1598 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1600 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1601 switch(lpParms->dwItem) {
1602 case MCI_GETDEVCAPS_DEVICE_TYPE:
1603 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1604 ret = MCI_RESOURCE_RETURNED;
1605 break;
1606 case MCI_GETDEVCAPS_HAS_AUDIO:
1607 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1608 ret = MCI_RESOURCE_RETURNED;
1609 break;
1610 case MCI_GETDEVCAPS_HAS_VIDEO:
1611 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1612 ret = MCI_RESOURCE_RETURNED;
1613 break;
1614 case MCI_GETDEVCAPS_USES_FILES:
1615 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1616 ret = MCI_RESOURCE_RETURNED;
1617 break;
1618 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1619 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1620 ret = MCI_RESOURCE_RETURNED;
1621 break;
1622 case MCI_GETDEVCAPS_CAN_RECORD:
1623 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1624 ret = MCI_RESOURCE_RETURNED;
1625 break;
1626 case MCI_GETDEVCAPS_CAN_EJECT:
1627 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1628 ret = MCI_RESOURCE_RETURNED;
1629 break;
1630 case MCI_GETDEVCAPS_CAN_PLAY:
1631 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1632 ret = MCI_RESOURCE_RETURNED;
1633 break;
1634 case MCI_GETDEVCAPS_CAN_SAVE:
1635 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1636 ret = MCI_RESOURCE_RETURNED;
1637 break;
1638 case MCI_WAVE_GETDEVCAPS_INPUTS:
1639 lpParms->dwReturn = waveInGetNumDevs();
1640 break;
1641 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1642 lpParms->dwReturn = waveOutGetNumDevs();
1643 break;
1644 default:
1645 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1646 return MCIERR_UNRECOGNIZED_COMMAND;
1648 } else {
1649 WARN("No GetDevCaps-Item !\n");
1650 return MCIERR_UNRECOGNIZED_COMMAND;
1652 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1653 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1654 return ret;
1657 /**************************************************************************
1658 * WAVE_mciInfo [internal]
1660 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1662 DWORD ret = 0;
1663 LPCWSTR str = 0;
1664 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1666 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1668 if (!lpParms || !lpParms->lpstrReturn)
1669 return MCIERR_NULL_PARAMETER_BLOCK;
1671 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1673 if (wmw == NULL) {
1674 ret = MCIERR_INVALID_DEVICE_ID;
1675 } else {
1676 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1677 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1678 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1680 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1681 case MCI_INFO_PRODUCT: str = wszAudio; break;
1682 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1683 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1684 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1685 default:
1686 WARN("Don't know this info command (%u)\n", dwFlags);
1687 ret = MCIERR_UNRECOGNIZED_KEYWORD;
1690 if (!ret) {
1691 if (lpParms->dwRetSize) {
1692 WCHAR zero = 0;
1693 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
1694 * to the number of characters written, excluding \0. */
1695 lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
1696 } else ret = MCIERR_PARAM_OVERFLOW;
1698 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1699 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1700 return ret;
1703 /**************************************************************************
1704 * DriverProc (MCIWAVE.@)
1706 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1707 LPARAM dwParam1, LPARAM dwParam2)
1709 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1710 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1712 switch (wMsg) {
1713 case DRV_LOAD: return 1;
1714 case DRV_FREE: return 1;
1715 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1716 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1717 case DRV_ENABLE: return 1;
1718 case DRV_DISABLE: return 1;
1719 case DRV_QUERYCONFIGURE: return 1;
1720 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1721 case DRV_INSTALL: return DRVCNF_RESTART;
1722 case DRV_REMOVE: return DRVCNF_RESTART;
1725 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1727 switch (wMsg) {
1728 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1729 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1730 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1731 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1732 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1733 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1734 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS) dwParam2);
1735 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1736 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1737 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1738 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1739 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1740 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1741 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1742 /* commands that should be supported */
1743 case MCI_LOAD:
1744 case MCI_FREEZE:
1745 case MCI_PUT:
1746 case MCI_REALIZE:
1747 case MCI_UNFREEZE:
1748 case MCI_UPDATE:
1749 case MCI_WHERE:
1750 case MCI_STEP:
1751 case MCI_SPIN:
1752 case MCI_ESCAPE:
1753 case MCI_COPY:
1754 case MCI_CUT:
1755 case MCI_DELETE:
1756 case MCI_PASTE:
1757 FIXME("Unsupported command [%u]\n", wMsg);
1758 break;
1759 case MCI_WINDOW:
1760 TRACE("Unsupported command [%u]\n", wMsg);
1761 break;
1762 /* option which can be silenced */
1763 case MCI_CONFIGURE:
1764 return 0;
1765 case MCI_OPEN:
1766 case MCI_CLOSE:
1767 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1768 break;
1769 default:
1770 FIXME("is probably wrong msg [%u]\n", wMsg);
1771 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1773 return MCIERR_UNRECOGNIZED_COMMAND;