push 149f0a5527ac85057a8ef03858d34d91c36f97e8
[wine/hacks.git] / dlls / mciwave / mciwave.c
blob456936c3362816e0db5ce8014e6f0131a5bab451
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 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
51 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
52 DWORD dwPosition; /* position in bytes in chunk */
53 HANDLE hEvent; /* for synchronization */
54 LONG dwEventCount; /* for synchronization */
55 MMCKINFO ckMainRIFF; /* main RIFF chunk */
56 MMCKINFO ckWaveData; /* data chunk */
57 } WINE_MCIWAVE;
59 /* ===================================================================
60 * ===================================================================
61 * FIXME: should be using the new mmThreadXXXX functions from WINMM
62 * instead of those
63 * it would require to add a wine internal flag to mmThreadCreate
64 * in order to pass a 32 bit function instead of a 16 bit one
65 * ===================================================================
66 * =================================================================== */
68 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
70 struct SCA {
71 async_cmd cmd;
72 HANDLE evt;
73 UINT wDevID;
74 DWORD_PTR dwParam1;
75 DWORD_PTR dwParam2;
78 /**************************************************************************
79 * MCI_SCAStarter [internal]
81 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
83 struct SCA* sca = (struct SCA*)arg;
84 DWORD ret;
86 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
87 sca->wDevID, sca->dwParam1, sca->dwParam2);
88 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
89 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
90 sca->wDevID, sca->dwParam1, sca->dwParam2);
91 HeapFree(GetProcessHeap(), 0, sca);
92 return ret;
95 /**************************************************************************
96 * MCI_SendCommandAsync [internal]
98 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
99 DWORD_PTR dwParam2, UINT size)
101 HANDLE handles[2];
102 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
104 if (sca == 0)
105 return MCIERR_OUT_OF_MEMORY;
107 sca->wDevID = wDevID;
108 sca->cmd = cmd;
109 sca->dwParam1 = dwParam1;
111 if (size && dwParam2) {
112 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
113 /* copy structure passed by program in dwParam2 to be sure
114 * we can still use it whatever the program does
116 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
117 } else {
118 sca->dwParam2 = dwParam2;
121 if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
122 (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
123 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
124 if (handles[1]) CloseHandle(handles[1]);
125 sca->evt = NULL;
126 return MCI_SCAStarter(&sca);
129 SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
130 /* wait until either:
131 * - the thread has finished (handles[0], likely an error)
132 * - init phase of async command is done (handles[1])
134 WaitForMultipleObjects(2, handles, FALSE, INFINITE);
135 CloseHandle(handles[0]);
136 CloseHandle(handles[1]);
137 return 0;
140 /*======================================================================*
141 * MCI WAVE implementation *
142 *======================================================================*/
144 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
146 /**************************************************************************
147 * MCIWAVE_drvOpen [internal]
149 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
151 WINE_MCIWAVE* wmw;
153 if (modp == NULL) return 0xFFFFFFFF;
155 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
157 if (!wmw)
158 return 0;
160 wmw->wDevID = modp->wDeviceID;
161 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
162 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
163 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
165 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
166 wmw->wfxRef.nChannels = 1; /* MONO */
167 wmw->wfxRef.nSamplesPerSec = 11025;
168 wmw->wfxRef.nAvgBytesPerSec = 11025;
169 wmw->wfxRef.nBlockAlign = 1;
170 wmw->wfxRef.wBitsPerSample = 8;
171 wmw->wfxRef.cbSize = 0; /* don't care */
173 return modp->wDeviceID;
176 /**************************************************************************
177 * MCIWAVE_drvClose [internal]
179 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
181 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
183 if (wmw) {
184 HeapFree(GetProcessHeap(), 0, wmw);
185 mciSetDriverData(dwDevID, 0);
186 return 1;
188 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
191 /**************************************************************************
192 * WAVE_mciGetOpenDev [internal]
194 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
196 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
198 if (wmw == NULL || wmw->nUseCount == 0) {
199 WARN("Invalid wDevID=%u\n", wDevID);
200 return 0;
202 return wmw;
205 /**************************************************************************
206 * WAVE_mciNotify [internal]
208 * Notifications in MCI work like a 1-element queue.
209 * Each new notification request supersedes the previous one.
210 * This affects Play and Record; other commands are immediate.
212 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
214 /* We simply save one parameter by not passing the wDevID local
215 * to the command. They are the same (via mciGetDriverData).
217 MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
218 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
219 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
220 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
223 /**************************************************************************
224 * WAVE_ConvertByteToTimeFormat [internal]
226 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
228 DWORD ret = 0;
230 switch (wmw->dwMciTimeFormat) {
231 case MCI_FORMAT_MILLISECONDS:
232 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
233 break;
234 case MCI_FORMAT_BYTES:
235 ret = val;
236 break;
237 case MCI_FORMAT_SAMPLES:
238 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
239 break;
240 default:
241 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
243 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
244 *lpRet = 0;
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 break;
259 case MCI_FORMAT_BYTES:
260 ret = val;
261 break;
262 case MCI_FORMAT_SAMPLES:
263 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
264 break;
265 default:
266 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
268 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
269 return ret;
272 /**************************************************************************
273 * WAVE_mciReadFmt [internal]
275 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
277 MMCKINFO mmckInfo;
278 long r;
279 LPWAVEFORMATEX pwfx;
281 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
282 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
283 return MCIERR_INVALID_FILE;
284 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
285 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
287 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
288 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
290 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
291 if (r < sizeof(PCMWAVEFORMAT)) {
292 HeapFree(GetProcessHeap(), 0, pwfx);
293 return MCIERR_INVALID_FILE;
295 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag);
296 TRACE("nChannels=%d\n", pwfx->nChannels);
297 TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec);
298 TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec);
299 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign);
300 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
301 if (r >= (long)sizeof(WAVEFORMATEX))
302 TRACE("cbSize=%u !\n", pwfx->cbSize);
303 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
304 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
305 HeapFree(GetProcessHeap(), 0, pwfx);
306 return MCIERR_INVALID_FILE;
308 wmw->lpWaveFormat = pwfx;
310 mmioAscend(wmw->hFile, &mmckInfo, 0);
311 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
312 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
313 TRACE("can't find data chunk\n");
314 return MCIERR_INVALID_FILE;
316 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
317 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
318 return 0;
321 /**************************************************************************
322 * WAVE_mciDefaultFmt [internal]
324 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
325 * until either Open File or Record. It becomes immutable afterwards,
326 * i.e. Set wave format or channels etc. is subsequently refused.
328 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
330 wmw->lpWaveFormat = &wmw->wfxRef;
331 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
332 wmw->lpWaveFormat->nChannels = 1;
333 wmw->lpWaveFormat->nSamplesPerSec = 11025;
334 wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
335 wmw->lpWaveFormat->nBlockAlign = 1;
336 wmw->lpWaveFormat->wBitsPerSample = 8;
337 wmw->lpWaveFormat->cbSize = 0;
340 /**************************************************************************
341 * WAVE_mciCreateRIFFSkeleton [internal]
343 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
345 MMCKINFO ckWaveFormat;
346 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
347 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
349 lpckRIFF->ckid = FOURCC_RIFF;
350 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
351 lpckRIFF->cksize = 0;
353 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
354 goto err;
356 ckWaveFormat.fccType = 0;
357 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
358 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
360 /* Set wave format accepts PCM only, however open an
361 * existing ADPCM file, record into it and the MCI will
362 * happily save back in that format. */
363 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
364 if (wmw->lpWaveFormat->nBlockAlign !=
365 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
366 WORD size = wmw->lpWaveFormat->nChannels *
367 wmw->lpWaveFormat->wBitsPerSample/8;
368 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
369 wmw->lpWaveFormat->nBlockAlign, size);
370 wmw->lpWaveFormat->nBlockAlign = size;
372 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
373 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
374 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
375 wmw->lpWaveFormat->nBlockAlign;
376 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
377 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
378 wmw->lpWaveFormat->nAvgBytesPerSec = speed;
381 if (wmw->lpWaveFormat == &wmw->wfxRef) {
382 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
383 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
384 /* Set wave format accepts PCM only so the size is known. */
385 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
386 *pwfx = wmw->wfxRef;
387 wmw->lpWaveFormat = pwfx;
390 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
391 goto err;
393 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
394 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
395 goto err;
397 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
398 goto err;
400 lpckWaveData->cksize = 0;
401 lpckWaveData->fccType = 0;
402 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
404 /* create data chunk */
405 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
406 goto err;
408 return 0;
410 err:
411 /* mciClose takes care of wmw->lpWaveFormat. */
412 return MCIERR_INVALID_FILE;
415 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
417 WCHAR szTmpPath[MAX_PATH];
418 WCHAR szPrefix[4];
419 DWORD dwRet = MMSYSERR_NOERROR;
421 szPrefix[0] = 'M';
422 szPrefix[1] = 'C';
423 szPrefix[2] = 'I';
424 szPrefix[3] = '\0';
426 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
427 WARN("can't retrieve temp path!\n");
428 *pszTmpFileName = NULL;
429 return MCIERR_FILE_NOT_FOUND;
432 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
433 HEAP_ZERO_MEMORY,
434 MAX_PATH * sizeof(WCHAR));
435 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
436 WARN("can't retrieve temp file name!\n");
437 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
438 return MCIERR_FILE_NOT_FOUND;
441 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
443 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
445 *hFile = mmioOpenW(*pszTmpFileName, NULL,
446 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
448 if (*hFile == 0) {
449 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
450 /* temporary file could not be created. clean filename. */
451 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
452 dwRet = MCIERR_FILE_NOT_FOUND;
455 return dwRet;
458 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
460 LRESULT dwRet = MMSYSERR_NOERROR;
461 LPWSTR fn;
463 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
464 if (!fn) return MCIERR_OUT_OF_MEMORY;
465 strcpyW(fn, filename);
466 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
467 wmw->lpFileName = fn;
469 if (strlenW(filename) > 0) {
470 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
471 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
473 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
474 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
476 if (wmw->hFile == 0) {
477 WARN("can't find file=%s!\n", debugstr_w(filename));
478 dwRet = MCIERR_FILE_NOT_FOUND;
480 else
482 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
484 /* make sure we're are the beginning of the file */
485 mmioSeek(wmw->hFile, 0, SEEK_SET);
487 /* first reading of this file. read the waveformat chunk */
488 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
489 dwRet = MCIERR_INVALID_FILE;
490 } else {
491 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
492 (LPSTR)&(lpckMainRIFF->ckid),
493 (LPSTR) &(lpckMainRIFF->fccType),
494 (lpckMainRIFF->cksize));
496 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
497 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
498 dwRet = MCIERR_INVALID_FILE;
499 } else {
500 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
505 return dwRet;
508 /**************************************************************************
509 * WAVE_mciOpen [internal]
511 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
513 DWORD dwRet = 0;
514 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
516 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
517 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
518 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
520 if (dwFlags & MCI_OPEN_SHAREABLE)
521 return MCIERR_UNSUPPORTED_FUNCTION;
523 if (wmw->nUseCount > 0) {
524 /* The driver is already opened on this channel
525 * Wave driver cannot be shared
527 return MCIERR_DEVICE_OPEN;
530 wmw->nUseCount++;
532 wmw->fInput = FALSE;
533 wmw->hWave = 0;
534 wmw->dwStatus = MCI_MODE_NOT_READY;
535 wmw->hFile = 0;
536 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
537 wmw->hCallback = NULL;
538 WAVE_mciDefaultFmt(wmw);
540 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
541 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
542 wmw->wNotifyDeviceID = wDevID;
544 if (dwFlags & MCI_OPEN_ELEMENT) {
545 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
546 /* could it be that (DWORD)lpOpenParms->lpstrElementName
547 * contains the hFile value ?
549 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
550 } else {
551 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
554 TRACE("hFile=%p\n", wmw->hFile);
556 if (dwRet == 0) {
557 wmw->dwPosition = 0;
559 wmw->dwStatus = MCI_MODE_STOP;
561 if (dwFlags & MCI_NOTIFY)
562 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
563 } else {
564 wmw->nUseCount--;
565 if (wmw->hFile != 0)
566 mmioClose(wmw->hFile, 0);
567 wmw->hFile = 0;
569 return dwRet;
572 /**************************************************************************
573 * WAVE_mciCue [internal]
575 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
577 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
579 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
581 /* Tests on systems without sound drivers show that Cue, like
582 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
583 * The first Cue Notify does not immediately return the
584 * notification, as if a player or recorder thread is started.
585 * PAUSE mode is reported when successful, but this mode is
586 * different from the normal Pause, because a) Pause then returns
587 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
588 * still accepted, returning the original notification as ABORTED.
589 * I.e. Cue allows subsequent format changes, unlike Record or
590 * Open file, closes winmm if the format changes and stops this
591 * thread.
592 * Wine creates one player or recorder thread per async. Play or
593 * Record command. Notification behaviour suggests that MS-W*
594 * reuses a single thread to improve response times. Having Cue
595 * start this thread early helps to improve Play/Record's initial
596 * response time. In effect, Cue is a performance hint, which
597 * justifies our almost no-op implementation.
600 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
601 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
603 if ((dwFlags & MCI_NOTIFY) && lpParms)
604 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
606 return MMSYSERR_NOERROR;
609 /**************************************************************************
610 * WAVE_mciStop [internal]
612 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
614 DWORD dwRet = 0;
615 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
617 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
619 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
621 if (wmw->dwStatus != MCI_MODE_STOP) {
622 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
623 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
626 /* wait for playback thread (if any) to exit before processing further */
627 switch (wmw->dwStatus) {
628 case MCI_MODE_PAUSE:
629 case MCI_MODE_PLAY:
630 case MCI_MODE_RECORD:
632 int oldStat = wmw->dwStatus;
633 wmw->dwStatus = MCI_MODE_NOT_READY;
634 if (oldStat == MCI_MODE_PAUSE)
635 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
637 while (wmw->dwStatus != MCI_MODE_STOP)
638 Sleep(10);
639 break;
642 /* sanity resets */
643 wmw->dwStatus = MCI_MODE_STOP;
645 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
646 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
648 return dwRet;
651 /**************************************************************************
652 * WAVE_mciClose [internal]
654 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
656 DWORD dwRet = 0;
657 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
659 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
661 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
663 if (wmw->dwStatus != MCI_MODE_STOP) {
664 /* mciStop handles MCI_NOTIFY_ABORTED */
665 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
668 wmw->nUseCount--;
670 if (wmw->nUseCount == 0) {
671 if (wmw->hFile != 0) {
672 mmioClose(wmw->hFile, 0);
673 wmw->hFile = 0;
677 if (wmw->lpWaveFormat != &wmw->wfxRef)
678 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
679 wmw->lpWaveFormat = &wmw->wfxRef;
680 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
681 wmw->lpFileName = NULL;
683 if ((dwFlags & MCI_NOTIFY) && lpParms) {
684 WAVE_mciNotify(lpParms->dwCallback, wmw,
685 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
688 return 0;
691 /**************************************************************************
692 * WAVE_mciPlayCallback [internal]
694 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
695 DWORD_PTR dwInstance,
696 LPARAM dwParam1, LPARAM dwParam2)
698 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
700 switch (uMsg) {
701 case WOM_OPEN:
702 case WOM_CLOSE:
703 break;
704 case WOM_DONE:
705 InterlockedIncrement(&wmw->dwEventCount);
706 TRACE("Returning waveHdr=%lx\n", dwParam1);
707 SetEvent(wmw->hEvent);
708 break;
709 default:
710 ERR("Unknown uMsg=%d\n", uMsg);
714 /******************************************************************
715 * WAVE_mciPlayWaitDone [internal]
717 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
719 for (;;) {
720 ResetEvent(wmw->hEvent);
721 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
722 break;
724 InterlockedIncrement(&wmw->dwEventCount);
726 WaitForSingleObject(wmw->hEvent, INFINITE);
730 /**************************************************************************
731 * WAVE_mciPlay [internal]
733 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
735 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
736 DWORD end;
737 LONG bufsize, count, left;
738 DWORD dwRet;
739 LPWAVEHDR waveHdr = NULL;
740 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
741 HANDLE oldcb;
742 int whidx;
744 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
746 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
747 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
749 if (wmw->hFile == 0) {
750 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
751 return MCIERR_FILE_NOT_FOUND;
754 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput) {
755 /* FIXME: parameters (start/end) in lpParams may not be used */
756 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
759 /** This function will be called again by a thread when async is used.
760 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
761 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
763 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
764 return MCIERR_INTERNAL;
767 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
768 if (wmw->lpWaveFormat->nBlockAlign !=
769 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
770 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
771 wmw->lpWaveFormat->nBlockAlign,
772 wmw->lpWaveFormat->nChannels *
773 wmw->lpWaveFormat->wBitsPerSample/8);
774 wmw->lpWaveFormat->nBlockAlign =
775 wmw->lpWaveFormat->nChannels *
776 wmw->lpWaveFormat->wBitsPerSample/8;
778 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
779 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
780 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
781 wmw->lpWaveFormat->nAvgBytesPerSec,
782 wmw->lpWaveFormat->nSamplesPerSec *
783 wmw->lpWaveFormat->nBlockAlign);
784 wmw->lpWaveFormat->nAvgBytesPerSec =
785 wmw->lpWaveFormat->nSamplesPerSec *
786 wmw->lpWaveFormat->nBlockAlign;
790 end = wmw->ckWaveData.cksize;
791 if (lpParms && (dwFlags & MCI_TO)) {
792 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
793 if (position > end) return MCIERR_OUTOFRANGE;
794 end = position;
796 if (lpParms && (dwFlags & MCI_FROM)) {
797 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
798 if (position > end) return MCIERR_OUTOFRANGE;
799 /* Seek rounds down, so do we. */
800 position /= wmw->lpWaveFormat->nBlockAlign;
801 position *= wmw->lpWaveFormat->nBlockAlign;
802 wmw->dwPosition = position;
804 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
805 left = end - wmw->dwPosition;
806 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
808 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
809 wmw->dwStatus = MCI_MODE_PLAY;
811 if (!(dwFlags & MCI_WAIT)) {
812 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
813 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
816 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
818 oldcb = InterlockedExchangePointer(&wmw->hCallback,
819 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
820 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
821 oldcb = NULL;
823 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
824 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
826 /* go back to beginning of chunk plus the requested position */
827 /* FIXME: I'm not sure this is correct, notably because some data linked to
828 * the decompression state machine will not be correctly initialized.
829 * try it this way (other way would be to decompress from 0 up to dwPosition
830 * and to start sending to hWave when dwPosition is reached)
832 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
834 /* FIXME: how to choose between several output channels ? here mapper is forced */
835 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
836 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
838 if (dwRet != 0) {
839 TRACE("Can't open low level audio device %d\n", dwRet);
840 dwRet = MCIERR_DEVICE_OPEN;
841 wmw->hWave = 0;
842 goto cleanUp;
845 /* make it so that 3 buffers per second are needed */
846 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
848 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
849 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
850 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
851 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
852 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
853 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
854 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
855 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
856 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
857 dwRet = MCIERR_INTERNAL;
858 goto cleanUp;
861 whidx = 0;
862 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
863 wmw->dwEventCount = 1L; /* for first buffer */
865 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
866 if (hEvent) SetEvent(hEvent);
868 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
869 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
870 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
871 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
872 if (count < 1)
873 break;
874 /* count is always <= bufsize, so this is correct regarding the
875 * waveOutPrepareHeader function
877 waveHdr[whidx].dwBufferLength = count;
878 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
879 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
880 &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
881 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
882 if (dwRet) {
883 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
884 dwRet = MCIERR_HARDWARE;
885 break;
887 left -= count;
888 wmw->dwPosition += count;
889 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
890 /* InterlockedDecrement if and only if waveOutWrite is successful */
891 WAVE_mciPlayWaitDone(wmw);
892 whidx ^= 1;
895 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
897 /* just to get rid of some race conditions between play, stop and pause */
898 waveOutReset(wmw->hWave);
900 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
901 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
903 cleanUp:
904 if (dwFlags & MCI_NOTIFY)
905 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
907 HeapFree(GetProcessHeap(), 0, waveHdr);
909 if (wmw->hWave) {
910 waveOutClose(wmw->hWave);
911 wmw->hWave = 0;
913 CloseHandle(wmw->hEvent);
915 wmw->dwStatus = MCI_MODE_STOP;
917 /* Let the potentically asynchronous commands support FAILURE notification. */
918 if (oldcb) mciDriverNotify(oldcb, wDevID,
919 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
921 return dwRet;
924 /**************************************************************************
925 * WAVE_mciRecordCallback [internal]
927 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
928 DWORD_PTR dwInstance,
929 LPARAM dwParam1, LPARAM dwParam2)
931 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
932 LPWAVEHDR lpWaveHdr;
933 LONG count = 0;
935 switch (uMsg) {
936 case WIM_OPEN:
937 case WIM_CLOSE:
938 break;
939 case WIM_DATA:
940 lpWaveHdr = (LPWAVEHDR) dwParam1;
942 InterlockedIncrement(&wmw->dwEventCount);
944 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
946 lpWaveHdr->dwFlags &= ~WHDR_DONE;
947 if (count > 0)
948 wmw->dwPosition += count;
949 /* else error reporting ?? */
950 if (wmw->dwStatus == MCI_MODE_RECORD)
952 /* Only queue up another buffer if we are recording. We could receive this
953 message also when waveInReset() is called, since it notifies on all wave
954 buffers that are outstanding. Queueing up more sometimes causes waveInClose
955 to fail. */
956 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
957 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
960 SetEvent(wmw->hEvent);
961 break;
962 default:
963 ERR("Unknown uMsg=%d\n", uMsg);
967 /******************************************************************
968 * WAVE_mciRecordWaitDone [internal]
970 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
972 for (;;) {
973 ResetEvent(wmw->hEvent);
974 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
975 break;
977 InterlockedIncrement(&wmw->dwEventCount);
979 WaitForSingleObject(wmw->hEvent, INFINITE);
983 /**************************************************************************
984 * WAVE_mciRecord [internal]
986 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
988 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
989 DWORD end;
990 DWORD dwRet = MMSYSERR_NOERROR;
991 LONG bufsize;
992 LPWAVEHDR waveHdr = NULL;
993 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
994 HANDLE oldcb;
996 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
998 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
999 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1001 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1002 /* FIXME: parameters (start/end) in lpParams may not be used */
1003 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1006 /** This function will be called again by a thread when async is used.
1007 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1008 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1010 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
1011 return MCIERR_INTERNAL;
1014 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1015 wmw->dwStatus = MCI_MODE_RECORD;
1017 if (!(dwFlags & MCI_WAIT)) {
1018 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1019 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1022 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1023 * we don't modify the wave part of an existing file (ie. we always erase an
1024 * existing content, we don't overwrite)
1026 HeapFree(GetProcessHeap(), 0, (void*)wmw->lpFileName);
1027 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1028 if (dwRet != 0) return dwRet;
1030 /* new RIFF file, lpWaveFormat now valid */
1031 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1032 if (dwRet != 0) return dwRet;
1034 if (lpParms && (dwFlags & MCI_TO)) {
1035 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1036 } else end = 0xFFFFFFFF;
1037 if (lpParms && (dwFlags & MCI_FROM)) {
1038 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1039 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1040 /* Seek rounds down, so do we. */
1041 position /= wmw->lpWaveFormat->nBlockAlign;
1042 position *= wmw->lpWaveFormat->nBlockAlign;
1043 wmw->dwPosition = position;
1045 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1047 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1049 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1050 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1051 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1052 oldcb = NULL;
1054 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1055 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1057 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1059 /* Go back to the beginning of the chunk plus the requested position */
1060 /* FIXME: I'm not sure this is correct, notably because some data linked to
1061 * the decompression state machine will not be correctly initialized.
1062 * Try it this way (other way would be to decompress from 0 up to dwPosition
1063 * and to start sending to hWave when dwPosition is reached).
1065 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1067 /* By default the device will be opened for output, the MCI_CUE function is there to
1068 * change from output to input and back
1070 /* FIXME: how to choose between several output channels ? here mapper is forced */
1071 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1072 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1074 if (dwRet != MMSYSERR_NOERROR) {
1075 TRACE("Can't open low level audio device %d\n", dwRet);
1076 dwRet = MCIERR_DEVICE_OPEN;
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_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 !\n");
1342 if (dwFlags & MCI_WAVE_OUTPUT)
1343 TRACE("MCI_WAVE_OUTPUT !\n");
1344 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1345 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1346 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1347 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1348 /* Set wave format parameters is refused after Open or Record.*/
1349 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1350 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag);
1351 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1352 if (((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag != WAVE_FORMAT_PCM)
1353 return MCIERR_OUTOFRANGE;
1355 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1356 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1357 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1358 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1360 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1361 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1362 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1363 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1365 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1366 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1367 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1368 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1370 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1371 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1372 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1373 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1375 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1376 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1377 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1378 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1380 if (dwFlags & MCI_NOTIFY)
1381 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1382 return 0;
1385 /**************************************************************************
1386 * WAVE_mciSave [internal]
1388 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1390 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1391 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1393 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1394 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1395 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1397 if (dwFlags & MCI_WAIT)
1399 FIXME("MCI_WAIT not implemented\n");
1401 WAVE_mciStop(wDevID, 0, NULL);
1403 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1404 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1406 ret = mmioClose(wmw->hFile, 0);
1407 wmw->hFile = 0;
1410 If the destination file already exists, it has to be overwritten. (Behaviour
1411 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1412 my applications. We are making use of mmioRename, which WILL NOT overwrite
1413 the destination file (which is what Windows does, also verified in Win2K)
1414 So, lets delete the destination file before calling mmioRename. If the
1415 destination file DOESN'T exist, the delete will fail silently. Let's also be
1416 careful not to lose our previous error code.
1418 tmpRet = GetLastError();
1419 DeleteFileW (lpParms->lpfilename);
1420 SetLastError(tmpRet);
1422 /* FIXME: Open file.wav; Save; must not rename the original file.
1423 * Nor must Save a.wav; Save b.wav rename a. */
1424 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1425 ret = MMSYSERR_NOERROR;
1428 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1429 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1431 if (ret == MMSYSERR_NOERROR)
1432 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1434 return ret;
1437 /**************************************************************************
1438 * WAVE_mciStatus [internal]
1440 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1442 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1443 DWORD ret = 0;
1445 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1446 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1447 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1449 if (dwFlags & MCI_STATUS_ITEM) {
1450 switch (lpParms->dwItem) {
1451 case MCI_STATUS_CURRENT_TRACK:
1452 lpParms->dwReturn = 1;
1453 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1454 break;
1455 case MCI_STATUS_LENGTH:
1456 if (!wmw->hFile) {
1457 lpParms->dwReturn = 0;
1458 return MCIERR_UNSUPPORTED_FUNCTION;
1460 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1461 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1462 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1463 break;
1464 case MCI_STATUS_MODE:
1465 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1466 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1467 ret = MCI_RESOURCE_RETURNED;
1468 break;
1469 case MCI_STATUS_MEDIA_PRESENT:
1470 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1471 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1472 ret = MCI_RESOURCE_RETURNED;
1473 break;
1474 case MCI_STATUS_NUMBER_OF_TRACKS:
1475 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1476 lpParms->dwReturn = 1;
1477 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1478 break;
1479 case MCI_STATUS_POSITION:
1480 if (!wmw->hFile) {
1481 lpParms->dwReturn = 0;
1482 return MCIERR_UNSUPPORTED_FUNCTION;
1484 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1485 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1486 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1487 &ret);
1488 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1489 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1490 break;
1491 case MCI_STATUS_READY:
1492 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1493 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1494 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1495 ret = MCI_RESOURCE_RETURNED;
1496 break;
1497 case MCI_STATUS_TIME_FORMAT:
1498 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1499 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1500 ret = MCI_RESOURCE_RETURNED;
1501 break;
1502 case MCI_WAVE_INPUT:
1503 TRACE("MCI_WAVE_INPUT !\n");
1504 lpParms->dwReturn = 0;
1505 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1506 break;
1507 case MCI_WAVE_OUTPUT:
1508 TRACE("MCI_WAVE_OUTPUT !\n");
1510 UINT id;
1511 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1512 lpParms->dwReturn = id;
1513 } else {
1514 lpParms->dwReturn = 0;
1515 ret = MCIERR_WAVE_OUTPUTUNSPECIFIED;
1518 break;
1519 /* It is always ok to query wave format parameters,
1520 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1521 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1522 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1523 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1524 break;
1525 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1526 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1527 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1528 break;
1529 case MCI_WAVE_STATUS_BLOCKALIGN:
1530 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1531 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1532 break;
1533 case MCI_WAVE_STATUS_CHANNELS:
1534 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1535 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1536 break;
1537 case MCI_WAVE_STATUS_FORMATTAG:
1538 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1539 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1540 break;
1541 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1542 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1543 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1544 break;
1545 case MCI_WAVE_STATUS_LEVEL:
1546 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1547 lpParms->dwReturn = 0xAAAA5555;
1548 break;
1549 default:
1550 WARN("unknown command %08X !\n", lpParms->dwItem);
1551 return MCIERR_UNRECOGNIZED_COMMAND;
1554 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1555 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1556 return ret;
1559 /**************************************************************************
1560 * WAVE_mciGetDevCaps [internal]
1562 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1563 LPMCI_GETDEVCAPS_PARMS lpParms)
1565 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1566 DWORD ret = 0;
1568 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1570 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1571 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1573 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1574 switch(lpParms->dwItem) {
1575 case MCI_GETDEVCAPS_DEVICE_TYPE:
1576 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1577 ret = MCI_RESOURCE_RETURNED;
1578 break;
1579 case MCI_GETDEVCAPS_HAS_AUDIO:
1580 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1581 ret = MCI_RESOURCE_RETURNED;
1582 break;
1583 case MCI_GETDEVCAPS_HAS_VIDEO:
1584 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1585 ret = MCI_RESOURCE_RETURNED;
1586 break;
1587 case MCI_GETDEVCAPS_USES_FILES:
1588 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1589 ret = MCI_RESOURCE_RETURNED;
1590 break;
1591 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1592 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1593 ret = MCI_RESOURCE_RETURNED;
1594 break;
1595 case MCI_GETDEVCAPS_CAN_RECORD:
1596 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1597 ret = MCI_RESOURCE_RETURNED;
1598 break;
1599 case MCI_GETDEVCAPS_CAN_EJECT:
1600 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1601 ret = MCI_RESOURCE_RETURNED;
1602 break;
1603 case MCI_GETDEVCAPS_CAN_PLAY:
1604 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1605 ret = MCI_RESOURCE_RETURNED;
1606 break;
1607 case MCI_GETDEVCAPS_CAN_SAVE:
1608 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1609 ret = MCI_RESOURCE_RETURNED;
1610 break;
1611 case MCI_WAVE_GETDEVCAPS_INPUTS:
1612 lpParms->dwReturn = 1;
1613 break;
1614 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1615 lpParms->dwReturn = 1;
1616 break;
1617 default:
1618 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1619 return MCIERR_UNRECOGNIZED_COMMAND;
1621 } else {
1622 WARN("No GetDevCaps-Item !\n");
1623 return MCIERR_UNRECOGNIZED_COMMAND;
1625 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1626 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1627 return ret;
1630 /**************************************************************************
1631 * WAVE_mciInfo [internal]
1633 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1635 DWORD ret = 0;
1636 LPCWSTR str = 0;
1637 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1639 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1641 if (!lpParms || !lpParms->lpstrReturn)
1642 return MCIERR_NULL_PARAMETER_BLOCK;
1644 if (wmw == NULL) {
1645 ret = MCIERR_INVALID_DEVICE_ID;
1646 } else {
1647 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1648 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1649 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1651 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1653 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1654 case MCI_INFO_PRODUCT: str = wszAudio; break;
1655 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1656 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1657 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1658 default:
1659 WARN("Don't know this info command (%u)\n", dwFlags);
1660 ret = MCIERR_UNRECOGNIZED_COMMAND;
1663 if (str) {
1664 if (strlenW(str) + 1 > lpParms->dwRetSize) {
1665 ret = MCIERR_PARAM_OVERFLOW;
1666 } else {
1667 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1669 } else {
1670 lpParms->lpstrReturn[0] = 0;
1672 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1673 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1674 return ret;
1677 /**************************************************************************
1678 * DriverProc (MCIWAVE.@)
1680 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1681 LPARAM dwParam1, LPARAM dwParam2)
1683 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1684 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1686 switch (wMsg) {
1687 case DRV_LOAD: return 1;
1688 case DRV_FREE: return 1;
1689 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1690 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1691 case DRV_ENABLE: return 1;
1692 case DRV_DISABLE: return 1;
1693 case DRV_QUERYCONFIGURE: return 1;
1694 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1695 case DRV_INSTALL: return DRVCNF_RESTART;
1696 case DRV_REMOVE: return DRVCNF_RESTART;
1699 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1701 switch (wMsg) {
1702 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1703 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1704 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1705 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1706 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1707 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1708 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1709 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1710 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1711 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1712 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1713 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1714 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1715 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1716 /* commands that should be supported */
1717 case MCI_LOAD:
1718 case MCI_FREEZE:
1719 case MCI_PUT:
1720 case MCI_REALIZE:
1721 case MCI_UNFREEZE:
1722 case MCI_UPDATE:
1723 case MCI_WHERE:
1724 case MCI_STEP:
1725 case MCI_SPIN:
1726 case MCI_ESCAPE:
1727 case MCI_COPY:
1728 case MCI_CUT:
1729 case MCI_DELETE:
1730 case MCI_PASTE:
1731 FIXME("Unsupported yet command [%u]\n", wMsg);
1732 break;
1733 case MCI_WINDOW:
1734 TRACE("Unsupported command [%u]\n", wMsg);
1735 break;
1736 /* option which can be silenced */
1737 case MCI_CONFIGURE:
1738 return 0;
1739 case MCI_OPEN:
1740 case MCI_CLOSE:
1741 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1742 break;
1743 default:
1744 FIXME("is probably wrong msg [%u]\n", wMsg);
1745 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1747 return MCIERR_UNRECOGNIZED_COMMAND;