wineps: Continue printing if path drawing function returns error.
[wine.git] / dlls / mciwave / mciwave.c
blobb2582a56123ed870ea799920fbf5661351c17017
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"
36 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
38 typedef struct {
39 UINT wDevID;
40 HANDLE hWave;
41 int nUseCount; /* Incremented for each shared open */
42 HMMIO hFile; /* mmio file handle open as Element */
43 MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */
44 HANDLE hCallback; /* Callback handle for pending notification */
45 LPWSTR lpFileName; /* Name of file (if any) */
46 WAVEFORMATEX wfxRef;
47 LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */
48 BOOL fInput; /* FALSE = Output, TRUE = Input */
49 WORD wInput; /* wave input device */
50 WORD wOutput; /* wave output device */
51 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
52 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
53 DWORD dwPosition; /* position in bytes in chunk */
54 HANDLE hEvent; /* for synchronization */
55 LONG dwEventCount; /* for synchronization */
56 MMCKINFO ckMainRIFF; /* main RIFF chunk */
57 MMCKINFO ckWaveData; /* data chunk */
58 } WINE_MCIWAVE;
60 /* ===================================================================
61 * ===================================================================
62 * FIXME: should be using the new mmThreadXXXX functions from WINMM
63 * instead of those
64 * it would require to add a wine internal flag to mmThreadCreate
65 * in order to pass a 32 bit function instead of a 16 bit one
66 * ===================================================================
67 * =================================================================== */
69 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
71 struct SCA {
72 async_cmd cmd;
73 HANDLE evt;
74 UINT wDevID;
75 DWORD_PTR dwParam1;
76 DWORD_PTR dwParam2;
79 /**************************************************************************
80 * MCI_SCAStarter [internal]
82 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
84 struct SCA* sca = (struct SCA*)arg;
85 DWORD ret;
87 TRACE("In thread before async command (%08x,%08Ix,%08Ix)\n",
88 sca->wDevID, sca->dwParam1, sca->dwParam2);
89 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
90 TRACE("In thread after async command (%08x,%08Ix,%08Ix)\n",
91 sca->wDevID, sca->dwParam1, sca->dwParam2);
92 HeapFree(GetProcessHeap(), 0, sca);
93 return ret;
96 /**************************************************************************
97 * MCI_SendCommandAsync [internal]
99 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
100 DWORD_PTR dwParam2, UINT size)
102 HANDLE handles[2];
103 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
105 if (sca == 0)
106 return MCIERR_OUT_OF_MEMORY;
108 sca->wDevID = wDevID;
109 sca->cmd = cmd;
110 sca->dwParam1 = dwParam1;
112 if (size && dwParam2) {
113 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
114 /* copy structure passed by program in dwParam2 to be sure
115 * we can still use it whatever the program does
117 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
118 } else {
119 sca->dwParam2 = dwParam2;
122 if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
123 (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
124 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
125 if (handles[1]) CloseHandle(handles[1]);
126 sca->evt = NULL;
127 return MCI_SCAStarter(sca);
130 SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
131 /* wait until either:
132 * - the thread has finished (handles[0], likely an error)
133 * - init phase of async command is done (handles[1])
135 WaitForMultipleObjects(2, handles, FALSE, INFINITE);
136 CloseHandle(handles[0]);
137 CloseHandle(handles[1]);
138 return 0;
141 /*======================================================================*
142 * MCI WAVE implementation *
143 *======================================================================*/
145 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
147 /**************************************************************************
148 * MCIWAVE_drvOpen [internal]
150 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
152 WINE_MCIWAVE* wmw;
154 if (modp == NULL) return 0xFFFFFFFF;
156 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
158 if (!wmw)
159 return 0;
161 wmw->wDevID = modp->wDeviceID;
162 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
163 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
164 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
166 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
167 wmw->wfxRef.nChannels = 1; /* MONO */
168 wmw->wfxRef.nSamplesPerSec = 11025;
169 wmw->wfxRef.nAvgBytesPerSec = 11025;
170 wmw->wfxRef.nBlockAlign = 1;
171 wmw->wfxRef.wBitsPerSample = 8;
172 wmw->wfxRef.cbSize = 0; /* don't care */
174 return modp->wDeviceID;
177 /**************************************************************************
178 * MCIWAVE_drvClose [internal]
180 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
182 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
184 if (wmw) {
185 HeapFree(GetProcessHeap(), 0, wmw);
186 mciSetDriverData(dwDevID, 0);
187 return 1;
189 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
192 /**************************************************************************
193 * WAVE_mciGetOpenDev [internal]
195 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
197 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
199 if (wmw == NULL || wmw->nUseCount == 0) {
200 WARN("Invalid wDevID=%u\n", wDevID);
201 return 0;
203 return wmw;
206 /**************************************************************************
207 * WAVE_mciNotify [internal]
209 * Notifications in MCI work like a 1-element queue.
210 * Each new notification request supersedes the previous one.
211 * This affects Play and Record; other commands are immediate.
213 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
215 /* We simply save one parameter by not passing the wDevID local
216 * to the command. They are the same (via mciGetDriverData).
218 MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
219 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
220 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
221 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
224 /**************************************************************************
225 * WAVE_ConvertByteToTimeFormat [internal]
227 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
229 DWORD ret = 0;
231 switch (wmw->dwMciTimeFormat) {
232 case MCI_FORMAT_MILLISECONDS:
233 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
234 break;
235 case MCI_FORMAT_BYTES:
236 ret = val;
237 break;
238 case MCI_FORMAT_SAMPLES:
239 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
240 break;
241 default:
242 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
244 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
245 return ret;
248 /**************************************************************************
249 * WAVE_ConvertTimeFormatToByte [internal]
251 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
253 DWORD ret = 0;
255 switch (wmw->dwMciTimeFormat) {
256 case MCI_FORMAT_MILLISECONDS:
257 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
258 if (ret > wmw->ckWaveData.cksize &&
259 val == WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize))
260 ret = wmw->ckWaveData.cksize;
261 break;
262 case MCI_FORMAT_BYTES:
263 ret = val;
264 break;
265 case MCI_FORMAT_SAMPLES:
266 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
267 break;
268 default:
269 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
271 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
272 return ret;
275 /**************************************************************************
276 * WAVE_mciReadFmt [internal]
278 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
280 MMCKINFO mmckInfo;
281 LONG r;
282 LPWAVEFORMATEX pwfx;
284 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
285 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
286 return MCIERR_INVALID_FILE;
287 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX\n",
288 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
290 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
291 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
293 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
294 if (r < 0 || r < sizeof(PCMWAVEFORMAT)) {
295 HeapFree(GetProcessHeap(), 0, pwfx);
296 return MCIERR_INVALID_FILE;
298 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag);
299 TRACE("nChannels=%d\n", pwfx->nChannels);
300 TRACE("nSamplesPerSec=%ld\n", pwfx->nSamplesPerSec);
301 TRACE("nAvgBytesPerSec=%ld\n", pwfx->nAvgBytesPerSec);
302 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign);
303 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
304 if (r >= sizeof(WAVEFORMATEX))
305 TRACE("cbSize=%u !\n", pwfx->cbSize);
306 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
307 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
308 HeapFree(GetProcessHeap(), 0, pwfx);
309 return MCIERR_INVALID_FILE;
311 wmw->lpWaveFormat = pwfx;
313 mmioAscend(wmw->hFile, &mmckInfo, 0);
314 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
315 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
316 TRACE("can't find data chunk\n");
317 return MCIERR_INVALID_FILE;
319 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX\n",
320 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
321 return 0;
324 /**************************************************************************
325 * WAVE_mciDefaultFmt [internal]
327 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
328 * until either Open File or Record. It becomes immutable afterwards,
329 * i.e. Set wave format or channels etc. is subsequently refused.
331 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
333 wmw->lpWaveFormat = &wmw->wfxRef;
334 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
335 wmw->lpWaveFormat->nChannels = 1;
336 wmw->lpWaveFormat->nSamplesPerSec = 11025;
337 wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
338 wmw->lpWaveFormat->nBlockAlign = 1;
339 wmw->lpWaveFormat->wBitsPerSample = 8;
340 wmw->lpWaveFormat->cbSize = 0;
343 /**************************************************************************
344 * WAVE_mciCreateRIFFSkeleton [internal]
346 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
348 MMCKINFO ckWaveFormat;
349 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
350 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
352 lpckRIFF->ckid = FOURCC_RIFF;
353 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
354 lpckRIFF->cksize = 0;
356 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
357 goto err;
359 ckWaveFormat.fccType = 0;
360 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
361 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
363 /* Set wave format accepts PCM only, however open an
364 * existing ADPCM file, record into it and the MCI will
365 * happily save back in that format. */
366 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
367 if (wmw->lpWaveFormat->nBlockAlign !=
368 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
369 WORD size = wmw->lpWaveFormat->nChannels *
370 wmw->lpWaveFormat->wBitsPerSample/8;
371 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
372 wmw->lpWaveFormat->nBlockAlign, size);
373 wmw->lpWaveFormat->nBlockAlign = size;
375 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
376 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
377 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
378 wmw->lpWaveFormat->nBlockAlign;
379 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
380 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
381 wmw->lpWaveFormat->nAvgBytesPerSec = speed;
384 if (wmw->lpWaveFormat == &wmw->wfxRef) {
385 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
386 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
387 /* Set wave format accepts PCM only so the size is known. */
388 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
389 *pwfx = wmw->wfxRef;
390 wmw->lpWaveFormat = pwfx;
393 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
394 goto err;
396 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
397 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
398 goto err;
400 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
401 goto err;
403 lpckWaveData->cksize = 0;
404 lpckWaveData->fccType = 0;
405 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
407 /* create data chunk */
408 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
409 goto err;
411 return 0;
413 err:
414 /* mciClose takes care of wmw->lpWaveFormat. */
415 return MCIERR_INVALID_FILE;
418 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
420 WCHAR szTmpPath[MAX_PATH];
421 WCHAR szPrefix[4];
422 DWORD dwRet = MMSYSERR_NOERROR;
424 szPrefix[0] = 'M';
425 szPrefix[1] = 'C';
426 szPrefix[2] = 'I';
427 szPrefix[3] = '\0';
429 if (!GetTempPathW(ARRAY_SIZE(szTmpPath), szTmpPath)) {
430 WARN("can't retrieve temp path!\n");
431 *pszTmpFileName = NULL;
432 return MCIERR_FILE_NOT_FOUND;
435 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
436 HEAP_ZERO_MEMORY,
437 MAX_PATH * sizeof(WCHAR));
438 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
439 WARN("can't retrieve temp file name!\n");
440 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
441 return MCIERR_FILE_NOT_FOUND;
444 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
446 if (*pszTmpFileName && (*pszTmpFileName)[0]) {
448 *hFile = mmioOpenW(*pszTmpFileName, NULL,
449 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
451 if (*hFile == 0) {
452 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
453 /* temporary file could not be created. clean filename. */
454 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
455 dwRet = MCIERR_FILE_NOT_FOUND;
458 return dwRet;
461 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
463 LRESULT dwRet = MMSYSERR_NOERROR;
464 LPWSTR fn;
466 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
467 if (!fn) return MCIERR_OUT_OF_MEMORY;
468 lstrcpyW(fn, filename);
469 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
470 wmw->lpFileName = fn;
472 if (filename[0]) {
473 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
474 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
476 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
477 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
479 if (wmw->hFile == 0) {
480 WARN("can't find file=%s!\n", debugstr_w(filename));
481 dwRet = MCIERR_FILE_NOT_FOUND;
483 else
485 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
487 /* make sure we're at the beginning of the file */
488 mmioSeek(wmw->hFile, 0, SEEK_SET);
490 /* first reading of this file. read the waveformat chunk */
491 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
492 dwRet = MCIERR_INVALID_FILE;
493 } else {
494 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX\n",
495 (LPSTR)&(lpckMainRIFF->ckid),
496 (LPSTR) &(lpckMainRIFF->fccType),
497 (lpckMainRIFF->cksize));
499 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
500 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
501 dwRet = MCIERR_INVALID_FILE;
502 } else {
503 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
508 return dwRet;
511 /**************************************************************************
512 * WAVE_mciOpen [internal]
514 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
516 DWORD dwRet = 0;
517 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
519 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
520 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
521 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
523 if (dwFlags & MCI_OPEN_SHAREABLE)
524 return MCIERR_UNSUPPORTED_FUNCTION;
526 if (wmw->nUseCount > 0) {
527 /* The driver is already opened on this channel
528 * Wave driver cannot be shared
530 return MCIERR_DEVICE_OPEN;
533 wmw->nUseCount++;
535 wmw->wInput = wmw->wOutput = WAVE_MAPPER;
536 wmw->fInput = FALSE;
537 wmw->hWave = 0;
538 wmw->dwStatus = MCI_MODE_NOT_READY;
539 wmw->hFile = 0;
540 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
541 wmw->hCallback = NULL;
542 WAVE_mciDefaultFmt(wmw);
544 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
545 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
546 wmw->wNotifyDeviceID = wDevID;
548 if (dwFlags & MCI_OPEN_ELEMENT) {
549 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
550 /* could it be that (DWORD)lpOpenParms->lpstrElementName
551 * contains the hFile value ?
553 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
554 } else {
555 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
558 TRACE("hFile=%p\n", wmw->hFile);
560 if (dwRet == 0) {
561 wmw->dwPosition = 0;
563 wmw->dwStatus = MCI_MODE_STOP;
565 if (dwFlags & MCI_NOTIFY)
566 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
567 } else {
568 wmw->nUseCount--;
569 if (wmw->hFile != 0)
570 mmioClose(wmw->hFile, 0);
571 wmw->hFile = 0;
572 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
573 wmw->lpFileName = NULL;
575 return dwRet;
578 /**************************************************************************
579 * WAVE_mciCue [internal]
581 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
583 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
585 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
587 /* Tests on systems without sound drivers show that Cue, like
588 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
589 * The first Cue Notify does not immediately return the
590 * notification, as if a player or recorder thread is started.
591 * PAUSE mode is reported when successful, but this mode is
592 * different from the normal Pause, because a) Pause then returns
593 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
594 * still accepted, returning the original notification as ABORTED.
595 * I.e. Cue allows subsequent format changes, unlike Record or
596 * Open file, closes winmm if the format changes and stops this
597 * thread.
598 * Wine creates one player or recorder thread per async. Play or
599 * Record command. Notification behaviour suggests that MS-W*
600 * reuses a single thread to improve response times. Having Cue
601 * start this thread early helps to improve Play/Record's initial
602 * response time. In effect, Cue is a performance hint, which
603 * justifies our almost no-op implementation.
606 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
607 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
609 if ((dwFlags & MCI_NOTIFY) && lpParms)
610 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
612 return MMSYSERR_NOERROR;
615 /**************************************************************************
616 * WAVE_mciStop [internal]
618 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
620 DWORD dwRet = 0;
621 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
623 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
625 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
627 if (wmw->dwStatus != MCI_MODE_STOP) {
628 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
629 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
632 /* wait for playback thread (if any) to exit before processing further */
633 switch (wmw->dwStatus) {
634 case MCI_MODE_PAUSE:
635 case MCI_MODE_PLAY:
636 case MCI_MODE_RECORD:
638 int oldStat = wmw->dwStatus;
639 wmw->dwStatus = MCI_MODE_NOT_READY;
640 if (oldStat == MCI_MODE_PAUSE)
641 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
643 while (wmw->dwStatus != MCI_MODE_STOP)
644 Sleep(10);
645 break;
648 /* sanity resets */
649 wmw->dwStatus = MCI_MODE_STOP;
651 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
652 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
654 return dwRet;
657 /**************************************************************************
658 * WAVE_mciClose [internal]
660 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
662 DWORD dwRet = 0;
663 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
665 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
667 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
669 if (wmw->dwStatus != MCI_MODE_STOP) {
670 /* mciStop handles MCI_NOTIFY_ABORTED */
671 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
674 wmw->nUseCount--;
676 if (wmw->nUseCount == 0) {
677 if (wmw->hFile != 0) {
678 mmioClose(wmw->hFile, 0);
679 wmw->hFile = 0;
683 if (wmw->lpWaveFormat != &wmw->wfxRef)
684 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
685 wmw->lpWaveFormat = &wmw->wfxRef;
686 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
687 wmw->lpFileName = NULL;
689 if ((dwFlags & MCI_NOTIFY) && lpParms) {
690 WAVE_mciNotify(lpParms->dwCallback, wmw,
691 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
694 return 0;
697 /**************************************************************************
698 * WAVE_mciPlayCallback [internal]
700 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
701 DWORD_PTR dwInstance,
702 LPARAM dwParam1, LPARAM dwParam2)
704 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
706 switch (uMsg) {
707 case WOM_OPEN:
708 case WOM_CLOSE:
709 break;
710 case WOM_DONE:
711 InterlockedIncrement(&wmw->dwEventCount);
712 TRACE("Returning waveHdr=%Ix\n", dwParam1);
713 SetEvent(wmw->hEvent);
714 break;
715 default:
716 ERR("Unknown uMsg=%d\n", uMsg);
720 /******************************************************************
721 * WAVE_mciPlayWaitDone [internal]
723 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
725 for (;;) {
726 ResetEvent(wmw->hEvent);
727 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
728 break;
730 InterlockedIncrement(&wmw->dwEventCount);
732 WaitForSingleObject(wmw->hEvent, INFINITE);
736 /**************************************************************************
737 * WAVE_mciPlay [internal]
739 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
741 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
742 DWORD end;
743 LONG bufsize, count, left;
744 DWORD dwRet;
745 LPWAVEHDR waveHdr = NULL;
746 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
747 HANDLE oldcb;
748 int whidx;
750 TRACE("(%u, %08IX, %p);\n", wDevID, dwFlags, lpParms);
752 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
753 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
755 if (wmw->hFile == 0) {
756 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
757 return MCIERR_FILE_NOT_FOUND;
760 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) {
761 /* FIXME: notification is different with Resume than Play */
762 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
765 /** This function will be called again by a thread when async is used.
766 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
767 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
769 if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
770 !((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
771 /* FIXME: Check FROM/TO parameters first. */
772 /* FIXME: Play; Play [notify|wait] must hook into the running player. */
773 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL);
774 if (dwRet) return dwRet;
777 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
778 if (wmw->lpWaveFormat->nBlockAlign !=
779 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
780 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
781 wmw->lpWaveFormat->nBlockAlign,
782 wmw->lpWaveFormat->nChannels *
783 wmw->lpWaveFormat->wBitsPerSample/8);
784 wmw->lpWaveFormat->nBlockAlign =
785 wmw->lpWaveFormat->nChannels *
786 wmw->lpWaveFormat->wBitsPerSample/8;
788 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
789 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
790 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
791 wmw->lpWaveFormat->nAvgBytesPerSec,
792 wmw->lpWaveFormat->nSamplesPerSec *
793 wmw->lpWaveFormat->nBlockAlign);
794 wmw->lpWaveFormat->nAvgBytesPerSec =
795 wmw->lpWaveFormat->nSamplesPerSec *
796 wmw->lpWaveFormat->nBlockAlign;
800 end = wmw->ckWaveData.cksize;
801 if (dwFlags & MCI_TO) {
802 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
803 if (position > end) return MCIERR_OUTOFRANGE;
804 end = position;
806 if (dwFlags & MCI_FROM) {
807 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
808 if (position > end) return MCIERR_OUTOFRANGE;
809 /* Seek rounds down, so do we. */
810 position /= wmw->lpWaveFormat->nBlockAlign;
811 position *= wmw->lpWaveFormat->nBlockAlign;
812 wmw->dwPosition = position;
814 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
815 left = end - wmw->dwPosition;
816 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
818 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
819 wmw->dwStatus = MCI_MODE_PLAY;
821 if (!(dwFlags & MCI_WAIT)) {
822 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
823 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
826 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
828 oldcb = InterlockedExchangePointer(&wmw->hCallback,
829 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
830 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
831 oldcb = NULL;
833 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
834 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
836 /* go back to beginning of chunk plus the requested position */
837 /* FIXME: I'm not sure this is correct, notably because some data linked to
838 * the decompression state machine will not be correctly initialized.
839 * try it this way (other way would be to decompress from 0 up to dwPosition
840 * and to start sending to hWave when dwPosition is reached)
842 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
844 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat,
845 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
847 if (dwRet != 0) {
848 TRACE("Can't open low level audio device %ld\n", dwRet);
849 dwRet = MCIERR_DEVICE_OPEN;
850 wmw->hWave = 0;
851 goto cleanUp;
854 /* make it so that 3 buffers per second are needed */
855 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
857 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
858 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
859 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
860 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
861 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
862 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
863 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
864 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
865 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
866 dwRet = MCIERR_INTERNAL;
867 goto cleanUp;
870 whidx = 0;
871 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
872 if (!wmw->hEvent) {
873 dwRet = MCIERR_OUT_OF_MEMORY;
874 goto cleanUp;
876 wmw->dwEventCount = 1L; /* for first buffer */
878 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
879 if (hEvent) SetEvent(hEvent);
881 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
882 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
883 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
884 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
885 if (count < 1)
886 break;
887 /* count is always <= bufsize, so this is correct regarding the
888 * waveOutPrepareHeader function
890 waveHdr[whidx].dwBufferLength = count;
891 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
892 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu\n",
893 &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
894 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
895 if (dwRet) {
896 ERR("Aborting play loop, WODM_WRITE error %ld\n", dwRet);
897 dwRet = MCIERR_HARDWARE;
898 break;
900 left -= count;
901 wmw->dwPosition += count;
902 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
903 /* InterlockedDecrement if and only if waveOutWrite is successful */
904 WAVE_mciPlayWaitDone(wmw);
905 whidx ^= 1;
908 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
910 /* just to get rid of some race conditions between play, stop and pause */
911 waveOutReset(wmw->hWave);
913 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
914 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
916 cleanUp:
917 if (dwFlags & MCI_NOTIFY)
918 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
920 HeapFree(GetProcessHeap(), 0, waveHdr);
922 if (wmw->hWave) {
923 waveOutClose(wmw->hWave);
924 wmw->hWave = 0;
926 CloseHandle(wmw->hEvent);
927 wmw->hEvent = NULL;
929 wmw->dwStatus = MCI_MODE_STOP;
931 /* Let the potentially asynchronous commands support FAILURE notification. */
932 if (oldcb) mciDriverNotify(oldcb, wDevID,
933 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
935 return dwRet;
938 /**************************************************************************
939 * WAVE_mciRecordCallback [internal]
941 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
942 DWORD_PTR dwInstance,
943 LPARAM dwParam1, LPARAM dwParam2)
945 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
946 LPWAVEHDR lpWaveHdr;
947 LONG count = 0;
949 switch (uMsg) {
950 case WIM_OPEN:
951 case WIM_CLOSE:
952 break;
953 case WIM_DATA:
954 lpWaveHdr = (LPWAVEHDR) dwParam1;
956 InterlockedIncrement(&wmw->dwEventCount);
958 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
960 lpWaveHdr->dwFlags &= ~WHDR_DONE;
961 if (count > 0)
962 wmw->dwPosition += count;
963 /* else error reporting ?? */
964 if (wmw->dwStatus == MCI_MODE_RECORD)
966 /* Only queue up another buffer if we are recording. We could receive this
967 message also when waveInReset() is called, since it notifies on all wave
968 buffers that are outstanding. Queueing up more sometimes causes waveInClose
969 to fail. */
970 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
971 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
974 SetEvent(wmw->hEvent);
975 break;
976 default:
977 ERR("Unknown uMsg=%d\n", uMsg);
981 /******************************************************************
982 * WAVE_mciRecordWaitDone [internal]
984 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
986 for (;;) {
987 ResetEvent(wmw->hEvent);
988 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
989 break;
991 InterlockedIncrement(&wmw->dwEventCount);
993 WaitForSingleObject(wmw->hEvent, INFINITE);
997 /**************************************************************************
998 * WAVE_mciRecord [internal]
1000 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
1002 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
1003 DWORD end;
1004 DWORD dwRet = MMSYSERR_NOERROR;
1005 LONG bufsize;
1006 LPWAVEHDR waveHdr = NULL;
1007 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1008 HANDLE oldcb;
1010 TRACE("(%u, %08IX, %p);\n", wDevID, dwFlags, lpParms);
1012 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1013 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1015 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1016 /* FIXME: parameters (start/end) in lpParams may not be used */
1017 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1020 /** This function will be called again by a thread when async is used.
1021 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1022 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1024 if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
1025 !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
1026 return MCIERR_INTERNAL;
1029 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1030 wmw->dwStatus = MCI_MODE_RECORD;
1032 if (!(dwFlags & MCI_WAIT)) {
1033 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1034 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1037 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1038 * we don't modify the wave part of an existing file (ie. we always erase an
1039 * existing content, we don't overwrite)
1041 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
1042 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1043 if (dwRet != 0) return dwRet;
1045 /* new RIFF file, lpWaveFormat now valid */
1046 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1047 if (dwRet != 0) return dwRet;
1049 if (dwFlags & MCI_TO) {
1050 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1051 } else end = 0xFFFFFFFF;
1052 if (dwFlags & MCI_FROM) {
1053 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1054 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1055 /* Seek rounds down, so do we. */
1056 position /= wmw->lpWaveFormat->nBlockAlign;
1057 position *= wmw->lpWaveFormat->nBlockAlign;
1058 wmw->dwPosition = position;
1060 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1062 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
1064 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1065 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1066 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1067 oldcb = NULL;
1069 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1070 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1072 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1074 /* Go back to the beginning of the chunk plus the requested position */
1075 /* FIXME: I'm not sure this is correct, notably because some data linked to
1076 * the decompression state machine will not be correctly initialized.
1077 * Try it this way (other way would be to decompress from 0 up to dwPosition
1078 * and to start sending to hWave when dwPosition is reached).
1080 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1082 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
1083 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1085 if (dwRet != MMSYSERR_NOERROR) {
1086 TRACE("Can't open low level audio device %ld\n", dwRet);
1087 dwRet = MCIERR_DEVICE_OPEN;
1088 wmw->hWave = 0;
1089 goto cleanUp;
1092 /* make it so that 3 buffers per second are needed */
1093 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1095 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1096 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1097 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1098 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1099 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1100 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1101 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1103 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1104 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1105 dwRet = MCIERR_INTERNAL;
1106 goto cleanUp;
1109 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1110 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1111 dwRet = MCIERR_INTERNAL;
1112 goto cleanUp;
1115 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1116 wmw->dwEventCount = 1L; /* for first buffer */
1118 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1120 waveInStart(wmw->hWave);
1122 if (hEvent) SetEvent(hEvent);
1124 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1125 WAVE_mciRecordWaitDone(wmw);
1127 /* Grab callback before another thread kicks in after we change dwStatus. */
1128 if (dwFlags & MCI_NOTIFY) {
1129 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1130 dwFlags &= ~MCI_NOTIFY;
1132 /* needed so that the callback above won't add again the buffers returned by the reset */
1133 wmw->dwStatus = MCI_MODE_STOP;
1135 waveInReset(wmw->hWave);
1137 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1138 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1140 dwRet = 0;
1142 cleanUp:
1143 if (dwFlags & MCI_NOTIFY)
1144 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1146 HeapFree(GetProcessHeap(), 0, waveHdr);
1148 if (wmw->hWave) {
1149 waveInClose(wmw->hWave);
1150 wmw->hWave = 0;
1152 CloseHandle(wmw->hEvent);
1154 wmw->dwStatus = MCI_MODE_STOP;
1156 if (oldcb) mciDriverNotify(oldcb, wDevID,
1157 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1159 return dwRet;
1163 /**************************************************************************
1164 * WAVE_mciPause [internal]
1166 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1168 DWORD dwRet;
1169 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1171 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1173 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1175 switch (wmw->dwStatus) {
1176 case MCI_MODE_PLAY:
1177 dwRet = waveOutPause(wmw->hWave);
1178 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1179 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1180 ERR("waveOutPause error %ld\n",dwRet);
1181 dwRet = MCIERR_INTERNAL;
1183 break;
1184 case MCI_MODE_RECORD:
1185 dwRet = waveInStop(wmw->hWave);
1186 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1187 else {
1188 ERR("waveInStop error %ld\n",dwRet);
1189 dwRet = MCIERR_INTERNAL;
1191 break;
1192 case MCI_MODE_PAUSE:
1193 dwRet = MMSYSERR_NOERROR;
1194 break;
1195 default:
1196 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1198 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1199 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1200 return dwRet;
1203 /**************************************************************************
1204 * WAVE_mciResume [internal]
1206 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1208 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1209 DWORD dwRet;
1211 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1213 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1215 switch (wmw->dwStatus) {
1216 case MCI_MODE_PAUSE:
1217 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1218 if (wmw->fInput) {
1219 dwRet = waveInStart(wmw->hWave);
1220 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1221 else {
1222 ERR("waveInStart error %ld\n",dwRet);
1223 dwRet = MCIERR_INTERNAL;
1225 } else {
1226 dwRet = waveOutRestart(wmw->hWave);
1227 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1228 else {
1229 ERR("waveOutRestart error %ld\n",dwRet);
1230 dwRet = MCIERR_INTERNAL;
1233 break;
1234 case MCI_MODE_PLAY:
1235 case MCI_MODE_RECORD:
1236 dwRet = MMSYSERR_NOERROR;
1237 break;
1238 default:
1239 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1241 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1242 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1243 return dwRet;
1246 /**************************************************************************
1247 * WAVE_mciSeek [internal]
1249 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1251 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1252 DWORD position, dwRet;
1254 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1256 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1257 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1259 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1260 if (!position) return MCIERR_MISSING_PARAMETER;
1261 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
1263 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1264 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1265 if (dwRet != MMSYSERR_NOERROR) return dwRet;
1267 if (dwFlags & MCI_TO) {
1268 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1269 if (position > wmw->ckWaveData.cksize)
1270 return MCIERR_OUTOFRANGE;
1271 } else if (dwFlags & MCI_SEEK_TO_START) {
1272 position = 0;
1273 } else {
1274 position = wmw->ckWaveData.cksize;
1276 /* Seek rounds down, unless at end */
1277 if (position != wmw->ckWaveData.cksize) {
1278 position /= wmw->lpWaveFormat->nBlockAlign;
1279 position *= wmw->lpWaveFormat->nBlockAlign;
1281 wmw->dwPosition = position;
1282 TRACE("Seeking to position=%lu bytes\n", position);
1284 if (dwFlags & MCI_NOTIFY)
1285 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1287 return MMSYSERR_NOERROR;
1290 /**************************************************************************
1291 * WAVE_mciSet [internal]
1293 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
1295 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1297 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1299 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1300 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1302 if (dwFlags & MCI_SET_TIME_FORMAT) {
1303 switch (lpParms->dwTimeFormat) {
1304 case MCI_FORMAT_MILLISECONDS:
1305 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1306 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1307 break;
1308 case MCI_FORMAT_BYTES:
1309 TRACE("MCI_FORMAT_BYTES !\n");
1310 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1311 break;
1312 case MCI_FORMAT_SAMPLES:
1313 TRACE("MCI_FORMAT_SAMPLES !\n");
1314 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1315 break;
1316 default:
1317 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1318 return MCIERR_BAD_TIME_FORMAT;
1321 if (dwFlags & MCI_SET_VIDEO) {
1322 TRACE("No support for video !\n");
1323 return MCIERR_UNSUPPORTED_FUNCTION;
1325 if (dwFlags & MCI_SET_DOOR_OPEN) {
1326 TRACE("No support for door open !\n");
1327 return MCIERR_UNSUPPORTED_FUNCTION;
1329 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1330 TRACE("No support for door close !\n");
1331 return MCIERR_UNSUPPORTED_FUNCTION;
1333 if (dwFlags & MCI_SET_AUDIO) {
1334 if (dwFlags & MCI_SET_ON) {
1335 TRACE("MCI_SET_ON audio !\n");
1336 } else if (dwFlags & MCI_SET_OFF) {
1337 TRACE("MCI_SET_OFF audio !\n");
1338 } else {
1339 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1340 return MCIERR_BAD_INTEGER;
1343 switch (lpParms->dwAudio)
1345 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1346 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1347 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1348 default: WARN("Unknown audio channel %lu\n", lpParms->dwAudio); break;
1351 if (dwFlags & MCI_WAVE_INPUT) {
1352 TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
1353 if (lpParms->wInput >= waveInGetNumDevs())
1354 return MCIERR_OUTOFRANGE;
1355 if (wmw->wInput != (WORD)lpParms->wInput)
1356 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1357 wmw->wInput = lpParms->wInput;
1359 if (dwFlags & MCI_WAVE_OUTPUT) {
1360 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
1361 if (lpParms->wOutput >= waveOutGetNumDevs())
1362 return MCIERR_OUTOFRANGE;
1363 if (wmw->wOutput != (WORD)lpParms->wOutput)
1364 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1365 wmw->wOutput = lpParms->wOutput;
1367 if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
1368 TRACE("MCI_WAVE_SET_ANYINPUT\n");
1369 if (wmw->wInput != (WORD)lpParms->wInput)
1370 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1371 wmw->wInput = WAVE_MAPPER;
1373 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
1374 TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1375 if (wmw->wOutput != (WORD)lpParms->wOutput)
1376 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1377 wmw->wOutput = WAVE_MAPPER;
1379 /* Set wave format parameters is refused after Open or Record.*/
1380 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1381 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
1382 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1383 if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
1384 return MCIERR_OUTOFRANGE;
1386 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1387 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1388 wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
1389 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1391 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1392 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1393 wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
1394 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1396 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1397 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1398 wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
1399 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1401 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1402 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1403 wmw->wfxRef.nChannels = lpParms->nChannels;
1404 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1406 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1407 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1408 wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
1409 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1411 if (dwFlags & MCI_NOTIFY)
1412 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1413 return 0;
1416 /**************************************************************************
1417 * WAVE_mciSave [internal]
1419 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1421 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1422 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1424 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1425 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1426 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1428 if (dwFlags & MCI_WAIT)
1430 FIXME("MCI_WAIT not implemented\n");
1432 WAVE_mciStop(wDevID, 0, NULL);
1434 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1435 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1437 ret = mmioClose(wmw->hFile, 0);
1438 wmw->hFile = 0;
1441 If the destination file already exists, it has to be overwritten. (Behaviour
1442 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1443 my applications. We are making use of mmioRename, which WILL NOT overwrite
1444 the destination file (which is what Windows does, also verified in Win2K)
1445 So, lets delete the destination file before calling mmioRename. If the
1446 destination file DOESN'T exist, the delete will fail silently. Let's also be
1447 careful not to lose our previous error code.
1449 tmpRet = GetLastError();
1450 DeleteFileW (lpParms->lpfilename);
1451 SetLastError(tmpRet);
1453 /* FIXME: Open file.wav; Save; must not rename the original file.
1454 * Nor must Save a.wav; Save b.wav rename a. */
1455 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1456 ret = MMSYSERR_NOERROR;
1459 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1460 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1462 if (ret == MMSYSERR_NOERROR)
1463 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1465 return ret;
1468 /**************************************************************************
1469 * WAVE_mciStatus [internal]
1471 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1473 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1474 DWORD ret = 0;
1476 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1477 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1478 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1479 if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER;
1481 if (dwFlags & MCI_STATUS_ITEM) {
1482 switch (lpParms->dwItem) {
1483 case MCI_STATUS_CURRENT_TRACK:
1484 lpParms->dwReturn = 1;
1485 TRACE("MCI_STATUS_CURRENT_TRACK => %Iu\n", lpParms->dwReturn);
1486 break;
1487 case MCI_STATUS_LENGTH:
1488 if (!wmw->hFile) {
1489 lpParms->dwReturn = 0;
1490 return MCIERR_UNSUPPORTED_FUNCTION;
1492 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1493 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize);
1494 TRACE("MCI_STATUS_LENGTH => %Iu\n", lpParms->dwReturn);
1495 break;
1496 case MCI_STATUS_MODE:
1497 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1498 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1499 ret = MCI_RESOURCE_RETURNED;
1500 break;
1501 case MCI_STATUS_MEDIA_PRESENT:
1502 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1503 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1504 ret = MCI_RESOURCE_RETURNED;
1505 break;
1506 case MCI_STATUS_NUMBER_OF_TRACKS:
1507 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1508 lpParms->dwReturn = 1;
1509 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %Iu\n", lpParms->dwReturn);
1510 break;
1511 case MCI_STATUS_POSITION:
1512 if (!wmw->hFile) {
1513 lpParms->dwReturn = 0;
1514 return MCIERR_UNSUPPORTED_FUNCTION;
1516 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1517 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1518 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
1519 TRACE("MCI_STATUS_POSITION %s => %Iu\n",
1520 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1521 break;
1522 case MCI_STATUS_READY:
1523 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1524 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1525 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1526 ret = MCI_RESOURCE_RETURNED;
1527 break;
1528 case MCI_STATUS_TIME_FORMAT:
1529 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1530 TRACE("MCI_STATUS_TIME_FORMAT => %Iu\n", lpParms->dwReturn);
1531 ret = MCI_RESOURCE_RETURNED;
1532 break;
1533 case MCI_WAVE_INPUT:
1534 if (wmw->wInput != (WORD)WAVE_MAPPER)
1535 lpParms->dwReturn = wmw->wInput;
1536 else {
1537 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1538 ret = MCI_RESOURCE_RETURNED;
1540 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
1541 break;
1542 case MCI_WAVE_OUTPUT:
1543 if (wmw->wOutput != (WORD)WAVE_MAPPER)
1544 lpParms->dwReturn = wmw->wOutput;
1545 else {
1546 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1547 ret = MCI_RESOURCE_RETURNED;
1549 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
1550 break;
1551 /* It is always ok to query wave format parameters,
1552 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1553 case MCI_WAVE_STATUS_FORMATTAG:
1554 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
1555 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1556 else {
1557 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
1558 ret = MCI_RESOURCE_RETURNED;
1560 TRACE("MCI_WAVE_FORMATTAG => %Iu\n", lpParms->dwReturn);
1561 break;
1562 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1563 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1564 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %Iu\n", lpParms->dwReturn);
1565 break;
1566 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1567 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1568 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %Iu\n", lpParms->dwReturn);
1569 break;
1570 case MCI_WAVE_STATUS_BLOCKALIGN:
1571 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1572 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %Iu\n", lpParms->dwReturn);
1573 break;
1574 case MCI_WAVE_STATUS_CHANNELS:
1575 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1576 TRACE("MCI_WAVE_STATUS_CHANNELS => %Iu\n", lpParms->dwReturn);
1577 break;
1578 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1579 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1580 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %Iu\n", lpParms->dwReturn);
1581 break;
1582 case MCI_WAVE_STATUS_LEVEL:
1583 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1584 lpParms->dwReturn = 0xAAAA5555;
1585 break;
1586 default:
1587 WARN("unknown command %08lX !\n", lpParms->dwItem);
1588 return MCIERR_UNSUPPORTED_FUNCTION;
1591 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1592 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1593 return ret;
1596 /**************************************************************************
1597 * WAVE_mciGetDevCaps [internal]
1599 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1600 LPMCI_GETDEVCAPS_PARMS lpParms)
1602 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1603 DWORD ret = 0;
1605 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1607 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1608 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1610 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1611 switch(lpParms->dwItem) {
1612 case MCI_GETDEVCAPS_DEVICE_TYPE:
1613 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1614 ret = MCI_RESOURCE_RETURNED;
1615 break;
1616 case MCI_GETDEVCAPS_HAS_AUDIO:
1617 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1618 ret = MCI_RESOURCE_RETURNED;
1619 break;
1620 case MCI_GETDEVCAPS_HAS_VIDEO:
1621 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1622 ret = MCI_RESOURCE_RETURNED;
1623 break;
1624 case MCI_GETDEVCAPS_USES_FILES:
1625 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1626 ret = MCI_RESOURCE_RETURNED;
1627 break;
1628 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1629 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1630 ret = MCI_RESOURCE_RETURNED;
1631 break;
1632 case MCI_GETDEVCAPS_CAN_RECORD:
1633 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1634 ret = MCI_RESOURCE_RETURNED;
1635 break;
1636 case MCI_GETDEVCAPS_CAN_EJECT:
1637 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1638 ret = MCI_RESOURCE_RETURNED;
1639 break;
1640 case MCI_GETDEVCAPS_CAN_PLAY:
1641 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1642 ret = MCI_RESOURCE_RETURNED;
1643 break;
1644 case MCI_GETDEVCAPS_CAN_SAVE:
1645 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1646 ret = MCI_RESOURCE_RETURNED;
1647 break;
1648 case MCI_WAVE_GETDEVCAPS_INPUTS:
1649 lpParms->dwReturn = waveInGetNumDevs();
1650 break;
1651 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1652 lpParms->dwReturn = waveOutGetNumDevs();
1653 break;
1654 default:
1655 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1656 return MCIERR_UNRECOGNIZED_COMMAND;
1658 } else {
1659 WARN("No GetDevCaps-Item !\n");
1660 return MCIERR_UNRECOGNIZED_COMMAND;
1662 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1663 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1664 return ret;
1667 /**************************************************************************
1668 * WAVE_mciInfo [internal]
1670 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1672 DWORD ret = 0;
1673 LPCWSTR str = 0;
1674 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1676 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1678 if (!lpParms || !lpParms->lpstrReturn)
1679 return MCIERR_NULL_PARAMETER_BLOCK;
1681 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1683 if (wmw == NULL) {
1684 ret = MCIERR_INVALID_DEVICE_ID;
1685 } else {
1686 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1687 case MCI_INFO_PRODUCT: str = L"Wine's audio player"; break;
1688 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1689 case MCI_WAVE_INPUT: str = L"Wine Wave In"; break;
1690 case MCI_WAVE_OUTPUT: str = L"Wine Wave Out"; break;
1691 default:
1692 WARN("Don't know this info command (%lu)\n", dwFlags);
1693 ret = MCIERR_UNRECOGNIZED_KEYWORD;
1696 if (!ret) {
1697 if (lpParms->dwRetSize) {
1698 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
1699 * to the number of characters written, excluding \0. */
1700 lstrcpynW(lpParms->lpstrReturn, str ? str : L"", lpParms->dwRetSize);
1701 } else ret = MCIERR_PARAM_OVERFLOW;
1703 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1704 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1705 return ret;
1708 /**************************************************************************
1709 * DriverProc (MCIWAVE.@)
1711 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1712 LPARAM dwParam1, LPARAM dwParam2)
1714 TRACE("(%08IX, %p, %08X, %08IX, %08IX)\n",
1715 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1717 switch (wMsg) {
1718 case DRV_LOAD: return 1;
1719 case DRV_FREE: return 1;
1720 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1721 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1722 case DRV_ENABLE: return 1;
1723 case DRV_DISABLE: return 1;
1724 case DRV_QUERYCONFIGURE: return 1;
1725 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1726 case DRV_INSTALL: return DRVCNF_RESTART;
1727 case DRV_REMOVE: return DRVCNF_RESTART;
1730 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1732 switch (wMsg) {
1733 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1734 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1735 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1736 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1737 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1738 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1739 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS) dwParam2);
1740 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1741 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1742 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1743 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1744 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1745 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1746 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1747 /* commands that should be supported */
1748 case MCI_LOAD:
1749 case MCI_FREEZE:
1750 case MCI_PUT:
1751 case MCI_REALIZE:
1752 case MCI_UNFREEZE:
1753 case MCI_UPDATE:
1754 case MCI_WHERE:
1755 case MCI_STEP:
1756 case MCI_SPIN:
1757 case MCI_ESCAPE:
1758 case MCI_COPY:
1759 case MCI_CUT:
1760 case MCI_DELETE:
1761 case MCI_PASTE:
1762 FIXME("Unsupported command [%u]\n", wMsg);
1763 break;
1764 case MCI_WINDOW:
1765 TRACE("Unsupported command [%u]\n", wMsg);
1766 break;
1767 /* option which can be silenced */
1768 case MCI_CONFIGURE:
1769 return 0;
1770 case MCI_OPEN:
1771 case MCI_CLOSE:
1772 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1773 break;
1774 default:
1775 FIXME("is probably wrong msg [%u]\n", wMsg);
1776 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1778 return MCIERR_UNRECOGNIZED_COMMAND;