regedit: Convert the hex edit code to Unicode.
[wine/multimedia.git] / dlls / mciwave / mciwave.c
blobeffecf43069e8ead4e8217012e9427a279cba409
1 /*
2 * Wine Driver for MCI wave forms
4 * Copyright 1994 Martin Ayotte
5 * 1999,2000,2005 Eric Pouech
6 * 2000 Francois Jacques
7 * 2009 Jörg Höhle
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include <assert.h>
25 #include <stdarg.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "mmddk.h"
32 #include "wownt32.h"
33 #include "digitalv.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
39 typedef struct {
40 UINT wDevID;
41 HANDLE hWave;
42 int nUseCount; /* Incremented for each shared open */
43 HMMIO hFile; /* mmio file handle open as Element */
44 MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */
45 HANDLE hCallback; /* Callback handle for pending notification */
46 LPWSTR lpFileName; /* Name of file (if any) */
47 WAVEFORMATEX wfxRef;
48 LPWAVEFORMATEX lpWaveFormat; /* Points to wfxRef until set by OPEN or RECORD */
49 BOOL fInput; /* FALSE = Output, TRUE = Input */
50 WORD wInput; /* wave input device */
51 WORD wOutput; /* wave output device */
52 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
53 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
54 DWORD dwPosition; /* position in bytes in chunk */
55 HANDLE hEvent; /* for synchronization */
56 LONG dwEventCount; /* for synchronization */
57 MMCKINFO ckMainRIFF; /* main RIFF chunk */
58 MMCKINFO ckWaveData; /* data chunk */
59 } WINE_MCIWAVE;
61 /* ===================================================================
62 * ===================================================================
63 * FIXME: should be using the new mmThreadXXXX functions from WINMM
64 * instead of those
65 * it would require to add a wine internal flag to mmThreadCreate
66 * in order to pass a 32 bit function instead of a 16 bit one
67 * ===================================================================
68 * =================================================================== */
70 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
72 struct SCA {
73 async_cmd cmd;
74 HANDLE evt;
75 UINT wDevID;
76 DWORD_PTR dwParam1;
77 DWORD_PTR dwParam2;
80 /**************************************************************************
81 * MCI_SCAStarter [internal]
83 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
85 struct SCA* sca = (struct SCA*)arg;
86 DWORD ret;
88 TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
89 sca->wDevID, sca->dwParam1, sca->dwParam2);
90 ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
91 TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
92 sca->wDevID, sca->dwParam1, sca->dwParam2);
93 HeapFree(GetProcessHeap(), 0, sca);
94 return ret;
97 /**************************************************************************
98 * MCI_SendCommandAsync [internal]
100 static DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
101 DWORD_PTR dwParam2, UINT size)
103 HANDLE handles[2];
104 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
106 if (sca == 0)
107 return MCIERR_OUT_OF_MEMORY;
109 sca->wDevID = wDevID;
110 sca->cmd = cmd;
111 sca->dwParam1 = dwParam1;
113 if (size && dwParam2) {
114 sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
115 /* copy structure passed by program in dwParam2 to be sure
116 * we can still use it whatever the program does
118 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
119 } else {
120 sca->dwParam2 = dwParam2;
123 if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
124 (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
125 WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
126 if (handles[1]) CloseHandle(handles[1]);
127 sca->evt = NULL;
128 return MCI_SCAStarter(&sca);
131 SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
132 /* wait until either:
133 * - the thread has finished (handles[0], likely an error)
134 * - init phase of async command is done (handles[1])
136 WaitForMultipleObjects(2, handles, FALSE, INFINITE);
137 CloseHandle(handles[0]);
138 CloseHandle(handles[1]);
139 return 0;
142 /*======================================================================*
143 * MCI WAVE implementation *
144 *======================================================================*/
146 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
148 /**************************************************************************
149 * MCIWAVE_drvOpen [internal]
151 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
153 WINE_MCIWAVE* wmw;
155 if (modp == NULL) return 0xFFFFFFFF;
157 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
159 if (!wmw)
160 return 0;
162 wmw->wDevID = modp->wDeviceID;
163 mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
164 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
165 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
167 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
168 wmw->wfxRef.nChannels = 1; /* MONO */
169 wmw->wfxRef.nSamplesPerSec = 11025;
170 wmw->wfxRef.nAvgBytesPerSec = 11025;
171 wmw->wfxRef.nBlockAlign = 1;
172 wmw->wfxRef.wBitsPerSample = 8;
173 wmw->wfxRef.cbSize = 0; /* don't care */
175 return modp->wDeviceID;
178 /**************************************************************************
179 * MCIWAVE_drvClose [internal]
181 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
183 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
185 if (wmw) {
186 HeapFree(GetProcessHeap(), 0, wmw);
187 mciSetDriverData(dwDevID, 0);
188 return 1;
190 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
193 /**************************************************************************
194 * WAVE_mciGetOpenDev [internal]
196 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
198 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
200 if (wmw == NULL || wmw->nUseCount == 0) {
201 WARN("Invalid wDevID=%u\n", wDevID);
202 return 0;
204 return wmw;
207 /**************************************************************************
208 * WAVE_mciNotify [internal]
210 * Notifications in MCI work like a 1-element queue.
211 * Each new notification request supersedes the previous one.
212 * This affects Play and Record; other commands are immediate.
214 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
216 /* We simply save one parameter by not passing the wDevID local
217 * to the command. They are the same (via mciGetDriverData).
219 MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
220 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
221 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
222 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
225 /**************************************************************************
226 * WAVE_ConvertByteToTimeFormat [internal]
228 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
230 DWORD ret = 0;
232 switch (wmw->dwMciTimeFormat) {
233 case MCI_FORMAT_MILLISECONDS:
234 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
235 break;
236 case MCI_FORMAT_BYTES:
237 ret = val;
238 break;
239 case MCI_FORMAT_SAMPLES:
240 ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
241 break;
242 default:
243 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
245 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
246 *lpRet = 0;
247 return ret;
250 /**************************************************************************
251 * WAVE_ConvertTimeFormatToByte [internal]
253 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
255 DWORD ret = 0;
257 switch (wmw->dwMciTimeFormat) {
258 case MCI_FORMAT_MILLISECONDS:
259 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
260 break;
261 case MCI_FORMAT_BYTES:
262 ret = val;
263 break;
264 case MCI_FORMAT_SAMPLES:
265 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
266 break;
267 default:
268 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
270 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
271 return ret;
274 /**************************************************************************
275 * WAVE_mciReadFmt [internal]
277 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
279 MMCKINFO mmckInfo;
280 LONG r;
281 LPWAVEFORMATEX pwfx;
283 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
284 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
285 return MCIERR_INVALID_FILE;
286 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
287 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
289 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
290 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
292 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
293 if (r < sizeof(PCMWAVEFORMAT)) {
294 HeapFree(GetProcessHeap(), 0, pwfx);
295 return MCIERR_INVALID_FILE;
297 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag);
298 TRACE("nChannels=%d\n", pwfx->nChannels);
299 TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec);
300 TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec);
301 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign);
302 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
303 if (r >= sizeof(WAVEFORMATEX))
304 TRACE("cbSize=%u !\n", pwfx->cbSize);
305 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
306 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
307 HeapFree(GetProcessHeap(), 0, pwfx);
308 return MCIERR_INVALID_FILE;
310 wmw->lpWaveFormat = pwfx;
312 mmioAscend(wmw->hFile, &mmckInfo, 0);
313 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
314 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
315 TRACE("can't find data chunk\n");
316 return MCIERR_INVALID_FILE;
318 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
319 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
320 return 0;
323 /**************************************************************************
324 * WAVE_mciDefaultFmt [internal]
326 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
327 * until either Open File or Record. It becomes immutable afterwards,
328 * i.e. Set wave format or channels etc. is subsequently refused.
330 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
332 wmw->lpWaveFormat = &wmw->wfxRef;
333 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
334 wmw->lpWaveFormat->nChannels = 1;
335 wmw->lpWaveFormat->nSamplesPerSec = 11025;
336 wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
337 wmw->lpWaveFormat->nBlockAlign = 1;
338 wmw->lpWaveFormat->wBitsPerSample = 8;
339 wmw->lpWaveFormat->cbSize = 0;
342 /**************************************************************************
343 * WAVE_mciCreateRIFFSkeleton [internal]
345 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
347 MMCKINFO ckWaveFormat;
348 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
349 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
351 lpckRIFF->ckid = FOURCC_RIFF;
352 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
353 lpckRIFF->cksize = 0;
355 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
356 goto err;
358 ckWaveFormat.fccType = 0;
359 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
360 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
362 /* Set wave format accepts PCM only, however open an
363 * existing ADPCM file, record into it and the MCI will
364 * happily save back in that format. */
365 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
366 if (wmw->lpWaveFormat->nBlockAlign !=
367 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
368 WORD size = wmw->lpWaveFormat->nChannels *
369 wmw->lpWaveFormat->wBitsPerSample/8;
370 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
371 wmw->lpWaveFormat->nBlockAlign, size);
372 wmw->lpWaveFormat->nBlockAlign = size;
374 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
375 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
376 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
377 wmw->lpWaveFormat->nBlockAlign;
378 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
379 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
380 wmw->lpWaveFormat->nAvgBytesPerSec = speed;
383 if (wmw->lpWaveFormat == &wmw->wfxRef) {
384 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
385 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
386 /* Set wave format accepts PCM only so the size is known. */
387 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
388 *pwfx = wmw->wfxRef;
389 wmw->lpWaveFormat = pwfx;
392 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
393 goto err;
395 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
396 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
397 goto err;
399 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
400 goto err;
402 lpckWaveData->cksize = 0;
403 lpckWaveData->fccType = 0;
404 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
406 /* create data chunk */
407 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
408 goto err;
410 return 0;
412 err:
413 /* mciClose takes care of wmw->lpWaveFormat. */
414 return MCIERR_INVALID_FILE;
417 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
419 WCHAR szTmpPath[MAX_PATH];
420 WCHAR szPrefix[4];
421 DWORD dwRet = MMSYSERR_NOERROR;
423 szPrefix[0] = 'M';
424 szPrefix[1] = 'C';
425 szPrefix[2] = 'I';
426 szPrefix[3] = '\0';
428 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
429 WARN("can't retrieve temp path!\n");
430 *pszTmpFileName = NULL;
431 return MCIERR_FILE_NOT_FOUND;
434 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
435 HEAP_ZERO_MEMORY,
436 MAX_PATH * sizeof(WCHAR));
437 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
438 WARN("can't retrieve temp file name!\n");
439 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
440 return MCIERR_FILE_NOT_FOUND;
443 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
445 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
447 *hFile = mmioOpenW(*pszTmpFileName, NULL,
448 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
450 if (*hFile == 0) {
451 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
452 /* temporary file could not be created. clean filename. */
453 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
454 dwRet = MCIERR_FILE_NOT_FOUND;
457 return dwRet;
460 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
462 LRESULT dwRet = MMSYSERR_NOERROR;
463 LPWSTR fn;
465 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
466 if (!fn) return MCIERR_OUT_OF_MEMORY;
467 strcpyW(fn, filename);
468 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
469 wmw->lpFileName = fn;
471 if (strlenW(filename) > 0) {
472 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
473 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
475 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
476 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
478 if (wmw->hFile == 0) {
479 WARN("can't find file=%s!\n", debugstr_w(filename));
480 dwRet = MCIERR_FILE_NOT_FOUND;
482 else
484 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
486 /* make sure we're at the beginning of the file */
487 mmioSeek(wmw->hFile, 0, SEEK_SET);
489 /* first reading of this file. read the waveformat chunk */
490 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
491 dwRet = MCIERR_INVALID_FILE;
492 } else {
493 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
494 (LPSTR)&(lpckMainRIFF->ckid),
495 (LPSTR) &(lpckMainRIFF->fccType),
496 (lpckMainRIFF->cksize));
498 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
499 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
500 dwRet = MCIERR_INVALID_FILE;
501 } else {
502 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
507 return dwRet;
510 /**************************************************************************
511 * WAVE_mciOpen [internal]
513 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
515 DWORD dwRet = 0;
516 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
518 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
519 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
520 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
522 if (dwFlags & MCI_OPEN_SHAREABLE)
523 return MCIERR_UNSUPPORTED_FUNCTION;
525 if (wmw->nUseCount > 0) {
526 /* The driver is already opened on this channel
527 * Wave driver cannot be shared
529 return MCIERR_DEVICE_OPEN;
532 wmw->nUseCount++;
534 wmw->wInput = wmw->wOutput = WAVE_MAPPER;
535 wmw->fInput = FALSE;
536 wmw->hWave = 0;
537 wmw->dwStatus = MCI_MODE_NOT_READY;
538 wmw->hFile = 0;
539 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
540 wmw->hCallback = NULL;
541 WAVE_mciDefaultFmt(wmw);
543 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
544 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
545 wmw->wNotifyDeviceID = wDevID;
547 if (dwFlags & MCI_OPEN_ELEMENT) {
548 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
549 /* could it be that (DWORD)lpOpenParms->lpstrElementName
550 * contains the hFile value ?
552 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
553 } else {
554 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
557 TRACE("hFile=%p\n", wmw->hFile);
559 if (dwRet == 0) {
560 wmw->dwPosition = 0;
562 wmw->dwStatus = MCI_MODE_STOP;
564 if (dwFlags & MCI_NOTIFY)
565 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
566 } else {
567 wmw->nUseCount--;
568 if (wmw->hFile != 0)
569 mmioClose(wmw->hFile, 0);
570 wmw->hFile = 0;
571 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
572 wmw->lpFileName = NULL;
574 return dwRet;
577 /**************************************************************************
578 * WAVE_mciCue [internal]
580 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
582 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
584 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
586 /* Tests on systems without sound drivers show that Cue, like
587 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
588 * The first Cue Notify does not immediately return the
589 * notification, as if a player or recorder thread is started.
590 * PAUSE mode is reported when successful, but this mode is
591 * different from the normal Pause, because a) Pause then returns
592 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
593 * still accepted, returning the original notification as ABORTED.
594 * I.e. Cue allows subsequent format changes, unlike Record or
595 * Open file, closes winmm if the format changes and stops this
596 * thread.
597 * Wine creates one player or recorder thread per async. Play or
598 * Record command. Notification behaviour suggests that MS-W*
599 * reuses a single thread to improve response times. Having Cue
600 * start this thread early helps to improve Play/Record's initial
601 * response time. In effect, Cue is a performance hint, which
602 * justifies our almost no-op implementation.
605 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
606 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
608 if ((dwFlags & MCI_NOTIFY) && lpParms)
609 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
611 return MMSYSERR_NOERROR;
614 /**************************************************************************
615 * WAVE_mciStop [internal]
617 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
619 DWORD dwRet = 0;
620 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
622 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
624 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
626 if (wmw->dwStatus != MCI_MODE_STOP) {
627 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
628 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
631 /* wait for playback thread (if any) to exit before processing further */
632 switch (wmw->dwStatus) {
633 case MCI_MODE_PAUSE:
634 case MCI_MODE_PLAY:
635 case MCI_MODE_RECORD:
637 int oldStat = wmw->dwStatus;
638 wmw->dwStatus = MCI_MODE_NOT_READY;
639 if (oldStat == MCI_MODE_PAUSE)
640 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
642 while (wmw->dwStatus != MCI_MODE_STOP)
643 Sleep(10);
644 break;
647 /* sanity resets */
648 wmw->dwStatus = MCI_MODE_STOP;
650 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
651 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
653 return dwRet;
656 /**************************************************************************
657 * WAVE_mciClose [internal]
659 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
661 DWORD dwRet = 0;
662 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
664 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
666 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
668 if (wmw->dwStatus != MCI_MODE_STOP) {
669 /* mciStop handles MCI_NOTIFY_ABORTED */
670 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
673 wmw->nUseCount--;
675 if (wmw->nUseCount == 0) {
676 if (wmw->hFile != 0) {
677 mmioClose(wmw->hFile, 0);
678 wmw->hFile = 0;
682 if (wmw->lpWaveFormat != &wmw->wfxRef)
683 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
684 wmw->lpWaveFormat = &wmw->wfxRef;
685 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
686 wmw->lpFileName = NULL;
688 if ((dwFlags & MCI_NOTIFY) && lpParms) {
689 WAVE_mciNotify(lpParms->dwCallback, wmw,
690 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
693 return 0;
696 /**************************************************************************
697 * WAVE_mciPlayCallback [internal]
699 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
700 DWORD_PTR dwInstance,
701 LPARAM dwParam1, LPARAM dwParam2)
703 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
705 switch (uMsg) {
706 case WOM_OPEN:
707 case WOM_CLOSE:
708 break;
709 case WOM_DONE:
710 InterlockedIncrement(&wmw->dwEventCount);
711 TRACE("Returning waveHdr=%lx\n", dwParam1);
712 SetEvent(wmw->hEvent);
713 break;
714 default:
715 ERR("Unknown uMsg=%d\n", uMsg);
719 /******************************************************************
720 * WAVE_mciPlayWaitDone [internal]
722 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
724 for (;;) {
725 ResetEvent(wmw->hEvent);
726 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
727 break;
729 InterlockedIncrement(&wmw->dwEventCount);
731 WaitForSingleObject(wmw->hEvent, INFINITE);
735 /**************************************************************************
736 * WAVE_mciPlay [internal]
738 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
740 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
741 DWORD end;
742 LONG bufsize, count, left;
743 DWORD dwRet;
744 LPWAVEHDR waveHdr = NULL;
745 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
746 HANDLE oldcb;
747 int whidx;
749 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
751 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
752 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
754 if (wmw->hFile == 0) {
755 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
756 return MCIERR_FILE_NOT_FOUND;
759 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) {
760 /* FIXME: notification is different with Resume than Play */
761 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
764 /** This function will be called again by a thread when async is used.
765 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
766 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
768 if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
769 !((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
770 /* FIXME: Check FROM/TO parameters first. */
771 /* FIXME: Play; Play [notify|wait] must hook into the running player. */
772 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL);
773 if (dwRet) return dwRet;
776 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
777 if (wmw->lpWaveFormat->nBlockAlign !=
778 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
779 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
780 wmw->lpWaveFormat->nBlockAlign,
781 wmw->lpWaveFormat->nChannels *
782 wmw->lpWaveFormat->wBitsPerSample/8);
783 wmw->lpWaveFormat->nBlockAlign =
784 wmw->lpWaveFormat->nChannels *
785 wmw->lpWaveFormat->wBitsPerSample/8;
787 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
788 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
789 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
790 wmw->lpWaveFormat->nAvgBytesPerSec,
791 wmw->lpWaveFormat->nSamplesPerSec *
792 wmw->lpWaveFormat->nBlockAlign);
793 wmw->lpWaveFormat->nAvgBytesPerSec =
794 wmw->lpWaveFormat->nSamplesPerSec *
795 wmw->lpWaveFormat->nBlockAlign;
799 end = wmw->ckWaveData.cksize;
800 if (dwFlags & MCI_TO) {
801 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
802 if (position > end) return MCIERR_OUTOFRANGE;
803 end = position;
805 if (dwFlags & MCI_FROM) {
806 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
807 if (position > end) return MCIERR_OUTOFRANGE;
808 /* Seek rounds down, so do we. */
809 position /= wmw->lpWaveFormat->nBlockAlign;
810 position *= wmw->lpWaveFormat->nBlockAlign;
811 wmw->dwPosition = position;
813 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
814 left = end - wmw->dwPosition;
815 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
817 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
818 wmw->dwStatus = MCI_MODE_PLAY;
820 if (!(dwFlags & MCI_WAIT)) {
821 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
822 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
825 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
827 oldcb = InterlockedExchangePointer(&wmw->hCallback,
828 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
829 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
830 oldcb = NULL;
832 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
833 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
835 /* go back to beginning of chunk plus the requested position */
836 /* FIXME: I'm not sure this is correct, notably because some data linked to
837 * the decompression state machine will not be correctly initialized.
838 * try it this way (other way would be to decompress from 0 up to dwPosition
839 * and to start sending to hWave when dwPosition is reached)
841 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
843 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat,
844 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
846 if (dwRet != 0) {
847 TRACE("Can't open low level audio device %d\n", dwRet);
848 dwRet = MCIERR_DEVICE_OPEN;
849 wmw->hWave = 0;
850 goto cleanUp;
853 /* make it so that 3 buffers per second are needed */
854 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
856 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
857 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
858 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
859 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
860 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
861 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
862 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
863 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
864 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
865 dwRet = MCIERR_INTERNAL;
866 goto cleanUp;
869 whidx = 0;
870 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
871 wmw->dwEventCount = 1L; /* for first buffer */
873 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
874 if (hEvent) SetEvent(hEvent);
876 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
877 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
878 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
879 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
880 if (count < 1)
881 break;
882 /* count is always <= bufsize, so this is correct regarding the
883 * waveOutPrepareHeader function
885 waveHdr[whidx].dwBufferLength = count;
886 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
887 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
888 &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
889 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
890 if (dwRet) {
891 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
892 dwRet = MCIERR_HARDWARE;
893 break;
895 left -= count;
896 wmw->dwPosition += count;
897 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
898 /* InterlockedDecrement if and only if waveOutWrite is successful */
899 WAVE_mciPlayWaitDone(wmw);
900 whidx ^= 1;
903 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
905 /* just to get rid of some race conditions between play, stop and pause */
906 waveOutReset(wmw->hWave);
908 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
909 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
911 cleanUp:
912 if (dwFlags & MCI_NOTIFY)
913 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
915 HeapFree(GetProcessHeap(), 0, waveHdr);
917 if (wmw->hWave) {
918 waveOutClose(wmw->hWave);
919 wmw->hWave = 0;
921 CloseHandle(wmw->hEvent);
923 wmw->dwStatus = MCI_MODE_STOP;
925 /* Let the potentially asynchronous commands support FAILURE notification. */
926 if (oldcb) mciDriverNotify(oldcb, wDevID,
927 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
929 return dwRet;
932 /**************************************************************************
933 * WAVE_mciRecordCallback [internal]
935 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
936 DWORD_PTR dwInstance,
937 LPARAM dwParam1, LPARAM dwParam2)
939 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
940 LPWAVEHDR lpWaveHdr;
941 LONG count = 0;
943 switch (uMsg) {
944 case WIM_OPEN:
945 case WIM_CLOSE:
946 break;
947 case WIM_DATA:
948 lpWaveHdr = (LPWAVEHDR) dwParam1;
950 InterlockedIncrement(&wmw->dwEventCount);
952 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
954 lpWaveHdr->dwFlags &= ~WHDR_DONE;
955 if (count > 0)
956 wmw->dwPosition += count;
957 /* else error reporting ?? */
958 if (wmw->dwStatus == MCI_MODE_RECORD)
960 /* Only queue up another buffer if we are recording. We could receive this
961 message also when waveInReset() is called, since it notifies on all wave
962 buffers that are outstanding. Queueing up more sometimes causes waveInClose
963 to fail. */
964 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
965 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
968 SetEvent(wmw->hEvent);
969 break;
970 default:
971 ERR("Unknown uMsg=%d\n", uMsg);
975 /******************************************************************
976 * WAVE_mciRecordWaitDone [internal]
978 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
980 for (;;) {
981 ResetEvent(wmw->hEvent);
982 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
983 break;
985 InterlockedIncrement(&wmw->dwEventCount);
987 WaitForSingleObject(wmw->hEvent, INFINITE);
991 /**************************************************************************
992 * WAVE_mciRecord [internal]
994 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
996 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
997 DWORD end;
998 DWORD dwRet = MMSYSERR_NOERROR;
999 LONG bufsize;
1000 LPWAVEHDR waveHdr = NULL;
1001 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1002 HANDLE oldcb;
1004 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1006 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1007 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1009 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1010 /* FIXME: parameters (start/end) in lpParams may not be used */
1011 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1014 /** This function will be called again by a thread when async is used.
1015 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1016 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1018 if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
1019 !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
1020 return MCIERR_INTERNAL;
1023 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1024 wmw->dwStatus = MCI_MODE_RECORD;
1026 if (!(dwFlags & MCI_WAIT)) {
1027 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1028 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1031 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1032 * we don't modify the wave part of an existing file (ie. we always erase an
1033 * existing content, we don't overwrite)
1035 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
1036 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1037 if (dwRet != 0) return dwRet;
1039 /* new RIFF file, lpWaveFormat now valid */
1040 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1041 if (dwRet != 0) return dwRet;
1043 if (dwFlags & MCI_TO) {
1044 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1045 } else end = 0xFFFFFFFF;
1046 if (dwFlags & MCI_FROM) {
1047 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1048 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1049 /* Seek rounds down, so do we. */
1050 position /= wmw->lpWaveFormat->nBlockAlign;
1051 position *= wmw->lpWaveFormat->nBlockAlign;
1052 wmw->dwPosition = position;
1054 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1056 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1058 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1059 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1060 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1061 oldcb = NULL;
1063 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1064 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1066 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1068 /* Go back to the beginning of the chunk plus the requested position */
1069 /* FIXME: I'm not sure this is correct, notably because some data linked to
1070 * the decompression state machine will not be correctly initialized.
1071 * Try it this way (other way would be to decompress from 0 up to dwPosition
1072 * and to start sending to hWave when dwPosition is reached).
1074 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1076 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
1077 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1079 if (dwRet != MMSYSERR_NOERROR) {
1080 TRACE("Can't open low level audio device %d\n", dwRet);
1081 dwRet = MCIERR_DEVICE_OPEN;
1082 wmw->hWave = 0;
1083 goto cleanUp;
1086 /* make it so that 3 buffers per second are needed */
1087 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1089 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1090 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1091 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1092 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1093 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1094 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1095 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1097 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1098 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1099 dwRet = MCIERR_INTERNAL;
1100 goto cleanUp;
1103 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1104 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1105 dwRet = MCIERR_INTERNAL;
1106 goto cleanUp;
1109 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1110 wmw->dwEventCount = 1L; /* for first buffer */
1112 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1114 dwRet = waveInStart(wmw->hWave);
1116 if (hEvent) SetEvent(hEvent);
1118 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1119 WAVE_mciRecordWaitDone(wmw);
1121 /* Grab callback before another thread kicks in after we change dwStatus. */
1122 if (dwFlags & MCI_NOTIFY) {
1123 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1124 dwFlags &= ~MCI_NOTIFY;
1126 /* needed so that the callback above won't add again the buffers returned by the reset */
1127 wmw->dwStatus = MCI_MODE_STOP;
1129 waveInReset(wmw->hWave);
1131 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1132 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1134 dwRet = 0;
1136 cleanUp:
1137 if (dwFlags & MCI_NOTIFY)
1138 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1140 HeapFree(GetProcessHeap(), 0, waveHdr);
1142 if (wmw->hWave) {
1143 waveInClose(wmw->hWave);
1144 wmw->hWave = 0;
1146 CloseHandle(wmw->hEvent);
1148 wmw->dwStatus = MCI_MODE_STOP;
1150 if (oldcb) mciDriverNotify(oldcb, wDevID,
1151 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1153 return dwRet;
1157 /**************************************************************************
1158 * WAVE_mciPause [internal]
1160 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1162 DWORD dwRet;
1163 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1165 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1167 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1169 switch (wmw->dwStatus) {
1170 case MCI_MODE_PLAY:
1171 dwRet = waveOutPause(wmw->hWave);
1172 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1173 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1174 ERR("waveOutPause error %d\n",dwRet);
1175 dwRet = MCIERR_INTERNAL;
1177 break;
1178 case MCI_MODE_RECORD:
1179 dwRet = waveInStop(wmw->hWave);
1180 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1181 else {
1182 ERR("waveInStop error %d\n",dwRet);
1183 dwRet = MCIERR_INTERNAL;
1185 break;
1186 case MCI_MODE_PAUSE:
1187 dwRet = MMSYSERR_NOERROR;
1188 break;
1189 default:
1190 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1192 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1193 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1194 return dwRet;
1197 /**************************************************************************
1198 * WAVE_mciResume [internal]
1200 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1202 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1203 DWORD dwRet;
1205 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1207 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1209 switch (wmw->dwStatus) {
1210 case MCI_MODE_PAUSE:
1211 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1212 if (wmw->fInput) {
1213 dwRet = waveInStart(wmw->hWave);
1214 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1215 else {
1216 ERR("waveInStart error %d\n",dwRet);
1217 dwRet = MCIERR_INTERNAL;
1219 } else {
1220 dwRet = waveOutRestart(wmw->hWave);
1221 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1222 else {
1223 ERR("waveOutRestart error %d\n",dwRet);
1224 dwRet = MCIERR_INTERNAL;
1227 break;
1228 case MCI_MODE_PLAY:
1229 case MCI_MODE_RECORD:
1230 dwRet = MMSYSERR_NOERROR;
1231 break;
1232 default:
1233 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1235 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1236 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1237 return dwRet;
1240 /**************************************************************************
1241 * WAVE_mciSeek [internal]
1243 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1245 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1246 DWORD position, dwRet;
1248 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1250 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1251 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1253 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1254 if (!position) return MCIERR_MISSING_PARAMETER;
1255 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
1257 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1258 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1259 if (dwRet != MMSYSERR_NOERROR) return dwRet;
1261 if (dwFlags & MCI_TO) {
1262 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1263 if (position > wmw->ckWaveData.cksize)
1264 return MCIERR_OUTOFRANGE;
1265 } else if (dwFlags & MCI_SEEK_TO_START) {
1266 position = 0;
1267 } else {
1268 position = wmw->ckWaveData.cksize;
1270 /* Seek rounds down, unless at end */
1271 if (position != wmw->ckWaveData.cksize) {
1272 position /= wmw->lpWaveFormat->nBlockAlign;
1273 position *= wmw->lpWaveFormat->nBlockAlign;
1275 wmw->dwPosition = position;
1276 TRACE("Seeking to position=%u bytes\n", position);
1278 if (dwFlags & MCI_NOTIFY)
1279 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1281 return MMSYSERR_NOERROR;
1284 /**************************************************************************
1285 * WAVE_mciSet [internal]
1287 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
1289 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1291 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1293 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1294 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1296 if (dwFlags & MCI_SET_TIME_FORMAT) {
1297 switch (lpParms->dwTimeFormat) {
1298 case MCI_FORMAT_MILLISECONDS:
1299 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1300 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1301 break;
1302 case MCI_FORMAT_BYTES:
1303 TRACE("MCI_FORMAT_BYTES !\n");
1304 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1305 break;
1306 case MCI_FORMAT_SAMPLES:
1307 TRACE("MCI_FORMAT_SAMPLES !\n");
1308 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1309 break;
1310 default:
1311 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1312 return MCIERR_BAD_TIME_FORMAT;
1315 if (dwFlags & MCI_SET_VIDEO) {
1316 TRACE("No support for video !\n");
1317 return MCIERR_UNSUPPORTED_FUNCTION;
1319 if (dwFlags & MCI_SET_DOOR_OPEN) {
1320 TRACE("No support for door open !\n");
1321 return MCIERR_UNSUPPORTED_FUNCTION;
1323 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1324 TRACE("No support for door close !\n");
1325 return MCIERR_UNSUPPORTED_FUNCTION;
1327 if (dwFlags & MCI_SET_AUDIO) {
1328 if (dwFlags & MCI_SET_ON) {
1329 TRACE("MCI_SET_ON audio !\n");
1330 } else if (dwFlags & MCI_SET_OFF) {
1331 TRACE("MCI_SET_OFF audio !\n");
1332 } else {
1333 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1334 return MCIERR_BAD_INTEGER;
1337 switch (lpParms->dwAudio)
1339 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1340 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1341 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1342 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1345 if (dwFlags & MCI_WAVE_INPUT) {
1346 TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
1347 if (lpParms->wInput >= waveInGetNumDevs())
1348 return MCIERR_OUTOFRANGE;
1349 if (wmw->wInput != (WORD)lpParms->wInput)
1350 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1351 wmw->wInput = lpParms->wInput;
1353 if (dwFlags & MCI_WAVE_OUTPUT) {
1354 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
1355 if (lpParms->wOutput >= waveOutGetNumDevs())
1356 return MCIERR_OUTOFRANGE;
1357 if (wmw->wOutput != (WORD)lpParms->wOutput)
1358 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1359 wmw->wOutput = lpParms->wOutput;
1361 if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
1362 TRACE("MCI_WAVE_SET_ANYINPUT\n");
1363 if (wmw->wInput != (WORD)lpParms->wInput)
1364 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1365 wmw->wInput = WAVE_MAPPER;
1367 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
1368 TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1369 if (wmw->wOutput != (WORD)lpParms->wOutput)
1370 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1371 wmw->wOutput = WAVE_MAPPER;
1373 /* Set wave format parameters is refused after Open or Record.*/
1374 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1375 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
1376 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1377 if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
1378 return MCIERR_OUTOFRANGE;
1380 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1381 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1382 wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
1383 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1385 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1386 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1387 wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
1388 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1390 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1391 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1392 wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
1393 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1395 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1396 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1397 wmw->wfxRef.nChannels = lpParms->nChannels;
1398 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1400 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1401 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1402 wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
1403 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1405 if (dwFlags & MCI_NOTIFY)
1406 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1407 return 0;
1410 /**************************************************************************
1411 * WAVE_mciSave [internal]
1413 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1415 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1416 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1418 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1419 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1420 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1422 if (dwFlags & MCI_WAIT)
1424 FIXME("MCI_WAIT not implemented\n");
1426 WAVE_mciStop(wDevID, 0, NULL);
1428 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1429 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1431 ret = mmioClose(wmw->hFile, 0);
1432 wmw->hFile = 0;
1435 If the destination file already exists, it has to be overwritten. (Behaviour
1436 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1437 my applications. We are making use of mmioRename, which WILL NOT overwrite
1438 the destination file (which is what Windows does, also verified in Win2K)
1439 So, lets delete the destination file before calling mmioRename. If the
1440 destination file DOESN'T exist, the delete will fail silently. Let's also be
1441 careful not to lose our previous error code.
1443 tmpRet = GetLastError();
1444 DeleteFileW (lpParms->lpfilename);
1445 SetLastError(tmpRet);
1447 /* FIXME: Open file.wav; Save; must not rename the original file.
1448 * Nor must Save a.wav; Save b.wav rename a. */
1449 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1450 ret = MMSYSERR_NOERROR;
1453 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1454 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1456 if (ret == MMSYSERR_NOERROR)
1457 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1459 return ret;
1462 /**************************************************************************
1463 * WAVE_mciStatus [internal]
1465 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1467 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1468 DWORD ret = 0;
1470 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1471 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1472 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1473 if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER;
1475 if (dwFlags & MCI_STATUS_ITEM) {
1476 switch (lpParms->dwItem) {
1477 case MCI_STATUS_CURRENT_TRACK:
1478 lpParms->dwReturn = 1;
1479 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1480 break;
1481 case MCI_STATUS_LENGTH:
1482 if (!wmw->hFile) {
1483 lpParms->dwReturn = 0;
1484 return MCIERR_UNSUPPORTED_FUNCTION;
1486 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1487 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1488 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1489 break;
1490 case MCI_STATUS_MODE:
1491 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1492 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1493 ret = MCI_RESOURCE_RETURNED;
1494 break;
1495 case MCI_STATUS_MEDIA_PRESENT:
1496 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1497 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1498 ret = MCI_RESOURCE_RETURNED;
1499 break;
1500 case MCI_STATUS_NUMBER_OF_TRACKS:
1501 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1502 lpParms->dwReturn = 1;
1503 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1504 break;
1505 case MCI_STATUS_POSITION:
1506 if (!wmw->hFile) {
1507 lpParms->dwReturn = 0;
1508 return MCIERR_UNSUPPORTED_FUNCTION;
1510 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1511 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1512 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1513 &ret);
1514 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1515 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1516 break;
1517 case MCI_STATUS_READY:
1518 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1519 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1520 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1521 ret = MCI_RESOURCE_RETURNED;
1522 break;
1523 case MCI_STATUS_TIME_FORMAT:
1524 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1525 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1526 ret = MCI_RESOURCE_RETURNED;
1527 break;
1528 case MCI_WAVE_INPUT:
1529 if (wmw->wInput != (WORD)WAVE_MAPPER)
1530 lpParms->dwReturn = wmw->wInput;
1531 else {
1532 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1533 ret = MCI_RESOURCE_RETURNED;
1535 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
1536 break;
1537 case MCI_WAVE_OUTPUT:
1538 if (wmw->wOutput != (WORD)WAVE_MAPPER)
1539 lpParms->dwReturn = wmw->wOutput;
1540 else {
1541 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1542 ret = MCI_RESOURCE_RETURNED;
1544 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
1545 break;
1546 /* It is always ok to query wave format parameters,
1547 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1548 case MCI_WAVE_STATUS_FORMATTAG:
1549 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
1550 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1551 else {
1552 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
1553 ret = MCI_RESOURCE_RETURNED;
1555 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1556 break;
1557 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1558 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1559 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1560 break;
1561 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1562 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1563 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1564 break;
1565 case MCI_WAVE_STATUS_BLOCKALIGN:
1566 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1567 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1568 break;
1569 case MCI_WAVE_STATUS_CHANNELS:
1570 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1571 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1572 break;
1573 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1574 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1575 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1576 break;
1577 case MCI_WAVE_STATUS_LEVEL:
1578 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1579 lpParms->dwReturn = 0xAAAA5555;
1580 break;
1581 default:
1582 WARN("unknown command %08X !\n", lpParms->dwItem);
1583 return MCIERR_UNSUPPORTED_FUNCTION;
1586 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1587 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1588 return ret;
1591 /**************************************************************************
1592 * WAVE_mciGetDevCaps [internal]
1594 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1595 LPMCI_GETDEVCAPS_PARMS lpParms)
1597 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1598 DWORD ret = 0;
1600 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1602 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1603 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1605 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1606 switch(lpParms->dwItem) {
1607 case MCI_GETDEVCAPS_DEVICE_TYPE:
1608 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1609 ret = MCI_RESOURCE_RETURNED;
1610 break;
1611 case MCI_GETDEVCAPS_HAS_AUDIO:
1612 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1613 ret = MCI_RESOURCE_RETURNED;
1614 break;
1615 case MCI_GETDEVCAPS_HAS_VIDEO:
1616 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1617 ret = MCI_RESOURCE_RETURNED;
1618 break;
1619 case MCI_GETDEVCAPS_USES_FILES:
1620 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1621 ret = MCI_RESOURCE_RETURNED;
1622 break;
1623 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1624 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1625 ret = MCI_RESOURCE_RETURNED;
1626 break;
1627 case MCI_GETDEVCAPS_CAN_RECORD:
1628 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1629 ret = MCI_RESOURCE_RETURNED;
1630 break;
1631 case MCI_GETDEVCAPS_CAN_EJECT:
1632 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1633 ret = MCI_RESOURCE_RETURNED;
1634 break;
1635 case MCI_GETDEVCAPS_CAN_PLAY:
1636 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1637 ret = MCI_RESOURCE_RETURNED;
1638 break;
1639 case MCI_GETDEVCAPS_CAN_SAVE:
1640 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1641 ret = MCI_RESOURCE_RETURNED;
1642 break;
1643 case MCI_WAVE_GETDEVCAPS_INPUTS:
1644 lpParms->dwReturn = waveInGetNumDevs();
1645 break;
1646 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1647 lpParms->dwReturn = waveOutGetNumDevs();
1648 break;
1649 default:
1650 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1651 return MCIERR_UNRECOGNIZED_COMMAND;
1653 } else {
1654 WARN("No GetDevCaps-Item !\n");
1655 return MCIERR_UNRECOGNIZED_COMMAND;
1657 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1658 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1659 return ret;
1662 /**************************************************************************
1663 * WAVE_mciInfo [internal]
1665 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1667 DWORD ret = 0;
1668 LPCWSTR str = 0;
1669 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1671 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1673 if (!lpParms || !lpParms->lpstrReturn)
1674 return MCIERR_NULL_PARAMETER_BLOCK;
1676 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1678 if (wmw == NULL) {
1679 ret = MCIERR_INVALID_DEVICE_ID;
1680 } else {
1681 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1682 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1683 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1685 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1686 case MCI_INFO_PRODUCT: str = wszAudio; break;
1687 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1688 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1689 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1690 default:
1691 WARN("Don't know this info command (%u)\n", dwFlags);
1692 ret = MCIERR_UNRECOGNIZED_KEYWORD;
1695 if (!ret) {
1696 if (lpParms->dwRetSize) {
1697 WCHAR zero = 0;
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 : &zero, 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("(%08lX, %p, %08X, %08lX, %08lX)\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;