netstat: Implement -a option.
[wine.git] / dlls / mciwave / mciwave.c
blob4f24169f480f97afc50dd25c1f1fe532d008ef9e
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)
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 return ret;
249 /**************************************************************************
250 * WAVE_ConvertTimeFormatToByte [internal]
252 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
254 DWORD ret = 0;
256 switch (wmw->dwMciTimeFormat) {
257 case MCI_FORMAT_MILLISECONDS:
258 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
259 if (ret > wmw->ckWaveData.cksize &&
260 val == WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize))
261 ret = wmw->ckWaveData.cksize;
262 break;
263 case MCI_FORMAT_BYTES:
264 ret = val;
265 break;
266 case MCI_FORMAT_SAMPLES:
267 ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
268 break;
269 default:
270 WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
272 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
273 return ret;
276 /**************************************************************************
277 * WAVE_mciReadFmt [internal]
279 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
281 MMCKINFO mmckInfo;
282 LONG r;
283 LPWAVEFORMATEX pwfx;
285 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
286 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
287 return MCIERR_INVALID_FILE;
288 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
289 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
291 pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
292 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
294 r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
295 if (r < sizeof(PCMWAVEFORMAT)) {
296 HeapFree(GetProcessHeap(), 0, pwfx);
297 return MCIERR_INVALID_FILE;
299 TRACE("wFormatTag=%04X !\n", pwfx->wFormatTag);
300 TRACE("nChannels=%d\n", pwfx->nChannels);
301 TRACE("nSamplesPerSec=%d\n", pwfx->nSamplesPerSec);
302 TRACE("nAvgBytesPerSec=%d\n", pwfx->nAvgBytesPerSec);
303 TRACE("nBlockAlign=%d\n", pwfx->nBlockAlign);
304 TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
305 if (r >= sizeof(WAVEFORMATEX))
306 TRACE("cbSize=%u !\n", pwfx->cbSize);
307 if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
308 && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
309 HeapFree(GetProcessHeap(), 0, pwfx);
310 return MCIERR_INVALID_FILE;
312 wmw->lpWaveFormat = pwfx;
314 mmioAscend(wmw->hFile, &mmckInfo, 0);
315 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
316 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
317 TRACE("can't find data chunk\n");
318 return MCIERR_INVALID_FILE;
320 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
321 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
322 return 0;
325 /**************************************************************************
326 * WAVE_mciDefaultFmt [internal]
328 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
329 * until either Open File or Record. It becomes immutable afterwards,
330 * i.e. Set wave format or channels etc. is subsequently refused.
332 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
334 wmw->lpWaveFormat = &wmw->wfxRef;
335 wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
336 wmw->lpWaveFormat->nChannels = 1;
337 wmw->lpWaveFormat->nSamplesPerSec = 11025;
338 wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
339 wmw->lpWaveFormat->nBlockAlign = 1;
340 wmw->lpWaveFormat->wBitsPerSample = 8;
341 wmw->lpWaveFormat->cbSize = 0;
344 /**************************************************************************
345 * WAVE_mciCreateRIFFSkeleton [internal]
347 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
349 MMCKINFO ckWaveFormat;
350 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
351 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
353 lpckRIFF->ckid = FOURCC_RIFF;
354 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
355 lpckRIFF->cksize = 0;
357 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
358 goto err;
360 ckWaveFormat.fccType = 0;
361 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
362 ckWaveFormat.cksize = sizeof(PCMWAVEFORMAT);
364 /* Set wave format accepts PCM only, however open an
365 * existing ADPCM file, record into it and the MCI will
366 * happily save back in that format. */
367 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
368 if (wmw->lpWaveFormat->nBlockAlign !=
369 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
370 WORD size = wmw->lpWaveFormat->nChannels *
371 wmw->lpWaveFormat->wBitsPerSample/8;
372 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
373 wmw->lpWaveFormat->nBlockAlign, size);
374 wmw->lpWaveFormat->nBlockAlign = size;
376 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
377 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
378 DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
379 wmw->lpWaveFormat->nBlockAlign;
380 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
381 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
382 wmw->lpWaveFormat->nAvgBytesPerSec = speed;
385 if (wmw->lpWaveFormat == &wmw->wfxRef) {
386 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
387 if (!pwfx) return MCIERR_OUT_OF_MEMORY;
388 /* Set wave format accepts PCM only so the size is known. */
389 assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
390 *pwfx = wmw->wfxRef;
391 wmw->lpWaveFormat = pwfx;
394 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
395 goto err;
397 if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
398 ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
399 goto err;
401 if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
402 goto err;
404 lpckWaveData->cksize = 0;
405 lpckWaveData->fccType = 0;
406 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
408 /* create data chunk */
409 if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
410 goto err;
412 return 0;
414 err:
415 /* mciClose takes care of wmw->lpWaveFormat. */
416 return MCIERR_INVALID_FILE;
419 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
421 WCHAR szTmpPath[MAX_PATH];
422 WCHAR szPrefix[4];
423 DWORD dwRet = MMSYSERR_NOERROR;
425 szPrefix[0] = 'M';
426 szPrefix[1] = 'C';
427 szPrefix[2] = 'I';
428 szPrefix[3] = '\0';
430 if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
431 WARN("can't retrieve temp path!\n");
432 *pszTmpFileName = NULL;
433 return MCIERR_FILE_NOT_FOUND;
436 *pszTmpFileName = HeapAlloc(GetProcessHeap(),
437 HEAP_ZERO_MEMORY,
438 MAX_PATH * sizeof(WCHAR));
439 if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
440 WARN("can't retrieve temp file name!\n");
441 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
442 return MCIERR_FILE_NOT_FOUND;
445 TRACE("%s!\n", debugstr_w(*pszTmpFileName));
447 if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
449 *hFile = mmioOpenW(*pszTmpFileName, NULL,
450 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
452 if (*hFile == 0) {
453 WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
454 /* temporary file could not be created. clean filename. */
455 HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
456 dwRet = MCIERR_FILE_NOT_FOUND;
459 return dwRet;
462 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
464 LRESULT dwRet = MMSYSERR_NOERROR;
465 LPWSTR fn;
467 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
468 if (!fn) return MCIERR_OUT_OF_MEMORY;
469 strcpyW(fn, filename);
470 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
471 wmw->lpFileName = fn;
473 if (strlenW(filename) > 0) {
474 /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
475 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
477 wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
478 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
480 if (wmw->hFile == 0) {
481 WARN("can't find file=%s!\n", debugstr_w(filename));
482 dwRet = MCIERR_FILE_NOT_FOUND;
484 else
486 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
488 /* make sure we're at the beginning of the file */
489 mmioSeek(wmw->hFile, 0, SEEK_SET);
491 /* first reading of this file. read the waveformat chunk */
492 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
493 dwRet = MCIERR_INVALID_FILE;
494 } else {
495 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
496 (LPSTR)&(lpckMainRIFF->ckid),
497 (LPSTR) &(lpckMainRIFF->fccType),
498 (lpckMainRIFF->cksize));
500 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
501 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
502 dwRet = MCIERR_INVALID_FILE;
503 } else {
504 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
509 return dwRet;
512 /**************************************************************************
513 * WAVE_mciOpen [internal]
515 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
517 DWORD dwRet = 0;
518 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
520 TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
521 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
522 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
524 if (dwFlags & MCI_OPEN_SHAREABLE)
525 return MCIERR_UNSUPPORTED_FUNCTION;
527 if (wmw->nUseCount > 0) {
528 /* The driver is already opened on this channel
529 * Wave driver cannot be shared
531 return MCIERR_DEVICE_OPEN;
534 wmw->nUseCount++;
536 wmw->wInput = wmw->wOutput = WAVE_MAPPER;
537 wmw->fInput = FALSE;
538 wmw->hWave = 0;
539 wmw->dwStatus = MCI_MODE_NOT_READY;
540 wmw->hFile = 0;
541 wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
542 wmw->hCallback = NULL;
543 WAVE_mciDefaultFmt(wmw);
545 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
546 /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
547 wmw->wNotifyDeviceID = wDevID;
549 if (dwFlags & MCI_OPEN_ELEMENT) {
550 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
551 /* could it be that (DWORD)lpOpenParms->lpstrElementName
552 * contains the hFile value ?
554 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
555 } else {
556 dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
559 TRACE("hFile=%p\n", wmw->hFile);
561 if (dwRet == 0) {
562 wmw->dwPosition = 0;
564 wmw->dwStatus = MCI_MODE_STOP;
566 if (dwFlags & MCI_NOTIFY)
567 WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
568 } else {
569 wmw->nUseCount--;
570 if (wmw->hFile != 0)
571 mmioClose(wmw->hFile, 0);
572 wmw->hFile = 0;
573 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
574 wmw->lpFileName = NULL;
576 return dwRet;
579 /**************************************************************************
580 * WAVE_mciCue [internal]
582 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
584 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
586 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
588 /* Tests on systems without sound drivers show that Cue, like
589 * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
590 * The first Cue Notify does not immediately return the
591 * notification, as if a player or recorder thread is started.
592 * PAUSE mode is reported when successful, but this mode is
593 * different from the normal Pause, because a) Pause then returns
594 * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
595 * still accepted, returning the original notification as ABORTED.
596 * I.e. Cue allows subsequent format changes, unlike Record or
597 * Open file, closes winmm if the format changes and stops this
598 * thread.
599 * Wine creates one player or recorder thread per async. Play or
600 * Record command. Notification behaviour suggests that MS-W*
601 * reuses a single thread to improve response times. Having Cue
602 * start this thread early helps to improve Play/Record's initial
603 * response time. In effect, Cue is a performance hint, which
604 * justifies our almost no-op implementation.
607 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
608 if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
610 if ((dwFlags & MCI_NOTIFY) && lpParms)
611 WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
613 return MMSYSERR_NOERROR;
616 /**************************************************************************
617 * WAVE_mciStop [internal]
619 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
621 DWORD dwRet = 0;
622 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
624 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
626 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
628 if (wmw->dwStatus != MCI_MODE_STOP) {
629 HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
630 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
633 /* wait for playback thread (if any) to exit before processing further */
634 switch (wmw->dwStatus) {
635 case MCI_MODE_PAUSE:
636 case MCI_MODE_PLAY:
637 case MCI_MODE_RECORD:
639 int oldStat = wmw->dwStatus;
640 wmw->dwStatus = MCI_MODE_NOT_READY;
641 if (oldStat == MCI_MODE_PAUSE)
642 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
644 while (wmw->dwStatus != MCI_MODE_STOP)
645 Sleep(10);
646 break;
649 /* sanity resets */
650 wmw->dwStatus = MCI_MODE_STOP;
652 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
653 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
655 return dwRet;
658 /**************************************************************************
659 * WAVE_mciClose [internal]
661 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
663 DWORD dwRet = 0;
664 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
666 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
668 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
670 if (wmw->dwStatus != MCI_MODE_STOP) {
671 /* mciStop handles MCI_NOTIFY_ABORTED */
672 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
675 wmw->nUseCount--;
677 if (wmw->nUseCount == 0) {
678 if (wmw->hFile != 0) {
679 mmioClose(wmw->hFile, 0);
680 wmw->hFile = 0;
684 if (wmw->lpWaveFormat != &wmw->wfxRef)
685 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
686 wmw->lpWaveFormat = &wmw->wfxRef;
687 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
688 wmw->lpFileName = NULL;
690 if ((dwFlags & MCI_NOTIFY) && lpParms) {
691 WAVE_mciNotify(lpParms->dwCallback, wmw,
692 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
695 return 0;
698 /**************************************************************************
699 * WAVE_mciPlayCallback [internal]
701 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
702 DWORD_PTR dwInstance,
703 LPARAM dwParam1, LPARAM dwParam2)
705 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
707 switch (uMsg) {
708 case WOM_OPEN:
709 case WOM_CLOSE:
710 break;
711 case WOM_DONE:
712 InterlockedIncrement(&wmw->dwEventCount);
713 TRACE("Returning waveHdr=%lx\n", dwParam1);
714 SetEvent(wmw->hEvent);
715 break;
716 default:
717 ERR("Unknown uMsg=%d\n", uMsg);
721 /******************************************************************
722 * WAVE_mciPlayWaitDone [internal]
724 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
726 for (;;) {
727 ResetEvent(wmw->hEvent);
728 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
729 break;
731 InterlockedIncrement(&wmw->dwEventCount);
733 WaitForSingleObject(wmw->hEvent, INFINITE);
737 /**************************************************************************
738 * WAVE_mciPlay [internal]
740 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
742 LPMCI_PLAY_PARMS lpParms = (void*)pmt;
743 DWORD end;
744 LONG bufsize, count, left;
745 DWORD dwRet;
746 LPWAVEHDR waveHdr = NULL;
747 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
748 HANDLE oldcb;
749 int whidx;
751 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
753 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
754 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
756 if (wmw->hFile == 0) {
757 WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
758 return MCIERR_FILE_NOT_FOUND;
761 if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) {
762 /* FIXME: notification is different with Resume than Play */
763 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
766 /** This function will be called again by a thread when async is used.
767 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
768 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
770 if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
771 !((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
772 /* FIXME: Check FROM/TO parameters first. */
773 /* FIXME: Play; Play [notify|wait] must hook into the running player. */
774 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL);
775 if (dwRet) return dwRet;
778 if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
779 if (wmw->lpWaveFormat->nBlockAlign !=
780 wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
781 WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
782 wmw->lpWaveFormat->nBlockAlign,
783 wmw->lpWaveFormat->nChannels *
784 wmw->lpWaveFormat->wBitsPerSample/8);
785 wmw->lpWaveFormat->nBlockAlign =
786 wmw->lpWaveFormat->nChannels *
787 wmw->lpWaveFormat->wBitsPerSample/8;
789 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
790 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
791 WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
792 wmw->lpWaveFormat->nAvgBytesPerSec,
793 wmw->lpWaveFormat->nSamplesPerSec *
794 wmw->lpWaveFormat->nBlockAlign);
795 wmw->lpWaveFormat->nAvgBytesPerSec =
796 wmw->lpWaveFormat->nSamplesPerSec *
797 wmw->lpWaveFormat->nBlockAlign;
801 end = wmw->ckWaveData.cksize;
802 if (dwFlags & MCI_TO) {
803 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
804 if (position > end) return MCIERR_OUTOFRANGE;
805 end = position;
807 if (dwFlags & MCI_FROM) {
808 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
809 if (position > end) return MCIERR_OUTOFRANGE;
810 /* Seek rounds down, so do we. */
811 position /= wmw->lpWaveFormat->nBlockAlign;
812 position *= wmw->lpWaveFormat->nBlockAlign;
813 wmw->dwPosition = position;
815 if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
816 left = end - wmw->dwPosition;
817 if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
819 wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
820 wmw->dwStatus = MCI_MODE_PLAY;
822 if (!(dwFlags & MCI_WAIT)) {
823 return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
824 (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
827 TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
829 oldcb = InterlockedExchangePointer(&wmw->hCallback,
830 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
831 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
832 oldcb = NULL;
834 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
835 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
837 /* go back to beginning of chunk plus the requested position */
838 /* FIXME: I'm not sure this is correct, notably because some data linked to
839 * the decompression state machine will not be correctly initialized.
840 * try it this way (other way would be to decompress from 0 up to dwPosition
841 * and to start sending to hWave when dwPosition is reached)
843 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
845 dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat,
846 (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
848 if (dwRet != 0) {
849 TRACE("Can't open low level audio device %d\n", dwRet);
850 dwRet = MCIERR_DEVICE_OPEN;
851 wmw->hWave = 0;
852 goto cleanUp;
855 /* make it so that 3 buffers per second are needed */
856 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
858 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
859 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
860 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
861 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
862 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
863 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
864 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
865 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
866 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
867 dwRet = MCIERR_INTERNAL;
868 goto cleanUp;
871 whidx = 0;
872 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
873 if (!wmw->hEvent) {
874 dwRet = MCIERR_OUT_OF_MEMORY;
875 goto cleanUp;
877 wmw->dwEventCount = 1L; /* for first buffer */
879 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
880 if (hEvent) SetEvent(hEvent);
882 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
883 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
884 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
885 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
886 if (count < 1)
887 break;
888 /* count is always <= bufsize, so this is correct regarding the
889 * waveOutPrepareHeader function
891 waveHdr[whidx].dwBufferLength = count;
892 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
893 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
894 &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
895 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
896 if (dwRet) {
897 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
898 dwRet = MCIERR_HARDWARE;
899 break;
901 left -= count;
902 wmw->dwPosition += count;
903 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
904 /* InterlockedDecrement if and only if waveOutWrite is successful */
905 WAVE_mciPlayWaitDone(wmw);
906 whidx ^= 1;
909 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
911 /* just to get rid of some race conditions between play, stop and pause */
912 waveOutReset(wmw->hWave);
914 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
915 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
917 cleanUp:
918 if (dwFlags & MCI_NOTIFY)
919 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
921 HeapFree(GetProcessHeap(), 0, waveHdr);
923 if (wmw->hWave) {
924 waveOutClose(wmw->hWave);
925 wmw->hWave = 0;
927 CloseHandle(wmw->hEvent);
928 wmw->hEvent = NULL;
930 wmw->dwStatus = MCI_MODE_STOP;
932 /* Let the potentially asynchronous commands support FAILURE notification. */
933 if (oldcb) mciDriverNotify(oldcb, wDevID,
934 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
936 return dwRet;
939 /**************************************************************************
940 * WAVE_mciRecordCallback [internal]
942 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
943 DWORD_PTR dwInstance,
944 LPARAM dwParam1, LPARAM dwParam2)
946 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
947 LPWAVEHDR lpWaveHdr;
948 LONG count = 0;
950 switch (uMsg) {
951 case WIM_OPEN:
952 case WIM_CLOSE:
953 break;
954 case WIM_DATA:
955 lpWaveHdr = (LPWAVEHDR) dwParam1;
957 InterlockedIncrement(&wmw->dwEventCount);
959 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
961 lpWaveHdr->dwFlags &= ~WHDR_DONE;
962 if (count > 0)
963 wmw->dwPosition += count;
964 /* else error reporting ?? */
965 if (wmw->dwStatus == MCI_MODE_RECORD)
967 /* Only queue up another buffer if we are recording. We could receive this
968 message also when waveInReset() is called, since it notifies on all wave
969 buffers that are outstanding. Queueing up more sometimes causes waveInClose
970 to fail. */
971 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
972 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
975 SetEvent(wmw->hEvent);
976 break;
977 default:
978 ERR("Unknown uMsg=%d\n", uMsg);
982 /******************************************************************
983 * WAVE_mciRecordWaitDone [internal]
985 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
987 for (;;) {
988 ResetEvent(wmw->hEvent);
989 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
990 break;
992 InterlockedIncrement(&wmw->dwEventCount);
994 WaitForSingleObject(wmw->hEvent, INFINITE);
998 /**************************************************************************
999 * WAVE_mciRecord [internal]
1001 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
1003 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
1004 DWORD end;
1005 DWORD dwRet = MMSYSERR_NOERROR;
1006 LONG bufsize;
1007 LPWAVEHDR waveHdr = NULL;
1008 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1009 HANDLE oldcb;
1011 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1013 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1014 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1016 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1017 /* FIXME: parameters (start/end) in lpParams may not be used */
1018 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1021 /** This function will be called again by a thread when async is used.
1022 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1023 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1025 if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
1026 !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
1027 return MCIERR_INTERNAL;
1030 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1031 wmw->dwStatus = MCI_MODE_RECORD;
1033 if (!(dwFlags & MCI_WAIT)) {
1034 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1035 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1038 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1039 * we don't modify the wave part of an existing file (ie. we always erase an
1040 * existing content, we don't overwrite)
1042 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
1043 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1044 if (dwRet != 0) return dwRet;
1046 /* new RIFF file, lpWaveFormat now valid */
1047 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1048 if (dwRet != 0) return dwRet;
1050 if (dwFlags & MCI_TO) {
1051 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1052 } else end = 0xFFFFFFFF;
1053 if (dwFlags & MCI_FROM) {
1054 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1055 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1056 /* Seek rounds down, so do we. */
1057 position /= wmw->lpWaveFormat->nBlockAlign;
1058 position *= wmw->lpWaveFormat->nBlockAlign;
1059 wmw->dwPosition = position;
1061 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1063 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1065 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1066 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1067 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1068 oldcb = NULL;
1070 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1071 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1073 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1075 /* Go back to the beginning of the chunk plus the requested position */
1076 /* FIXME: I'm not sure this is correct, notably because some data linked to
1077 * the decompression state machine will not be correctly initialized.
1078 * Try it this way (other way would be to decompress from 0 up to dwPosition
1079 * and to start sending to hWave when dwPosition is reached).
1081 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1083 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
1084 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1086 if (dwRet != MMSYSERR_NOERROR) {
1087 TRACE("Can't open low level audio device %d\n", dwRet);
1088 dwRet = MCIERR_DEVICE_OPEN;
1089 wmw->hWave = 0;
1090 goto cleanUp;
1093 /* make it so that 3 buffers per second are needed */
1094 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1096 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1097 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1098 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1099 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1100 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1101 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1102 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1104 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1105 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1106 dwRet = MCIERR_INTERNAL;
1107 goto cleanUp;
1110 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1111 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1112 dwRet = MCIERR_INTERNAL;
1113 goto cleanUp;
1116 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1117 wmw->dwEventCount = 1L; /* for first buffer */
1119 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1121 dwRet = waveInStart(wmw->hWave);
1123 if (hEvent) SetEvent(hEvent);
1125 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1126 WAVE_mciRecordWaitDone(wmw);
1128 /* Grab callback before another thread kicks in after we change dwStatus. */
1129 if (dwFlags & MCI_NOTIFY) {
1130 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1131 dwFlags &= ~MCI_NOTIFY;
1133 /* needed so that the callback above won't add again the buffers returned by the reset */
1134 wmw->dwStatus = MCI_MODE_STOP;
1136 waveInReset(wmw->hWave);
1138 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1139 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1141 dwRet = 0;
1143 cleanUp:
1144 if (dwFlags & MCI_NOTIFY)
1145 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1147 HeapFree(GetProcessHeap(), 0, waveHdr);
1149 if (wmw->hWave) {
1150 waveInClose(wmw->hWave);
1151 wmw->hWave = 0;
1153 CloseHandle(wmw->hEvent);
1155 wmw->dwStatus = MCI_MODE_STOP;
1157 if (oldcb) mciDriverNotify(oldcb, wDevID,
1158 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1160 return dwRet;
1164 /**************************************************************************
1165 * WAVE_mciPause [internal]
1167 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1169 DWORD dwRet;
1170 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1172 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1174 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1176 switch (wmw->dwStatus) {
1177 case MCI_MODE_PLAY:
1178 dwRet = waveOutPause(wmw->hWave);
1179 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1180 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1181 ERR("waveOutPause error %d\n",dwRet);
1182 dwRet = MCIERR_INTERNAL;
1184 break;
1185 case MCI_MODE_RECORD:
1186 dwRet = waveInStop(wmw->hWave);
1187 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1188 else {
1189 ERR("waveInStop error %d\n",dwRet);
1190 dwRet = MCIERR_INTERNAL;
1192 break;
1193 case MCI_MODE_PAUSE:
1194 dwRet = MMSYSERR_NOERROR;
1195 break;
1196 default:
1197 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1199 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1200 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1201 return dwRet;
1204 /**************************************************************************
1205 * WAVE_mciResume [internal]
1207 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1209 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1210 DWORD dwRet;
1212 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1214 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1216 switch (wmw->dwStatus) {
1217 case MCI_MODE_PAUSE:
1218 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1219 if (wmw->fInput) {
1220 dwRet = waveInStart(wmw->hWave);
1221 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1222 else {
1223 ERR("waveInStart error %d\n",dwRet);
1224 dwRet = MCIERR_INTERNAL;
1226 } else {
1227 dwRet = waveOutRestart(wmw->hWave);
1228 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1229 else {
1230 ERR("waveOutRestart error %d\n",dwRet);
1231 dwRet = MCIERR_INTERNAL;
1234 break;
1235 case MCI_MODE_PLAY:
1236 case MCI_MODE_RECORD:
1237 dwRet = MMSYSERR_NOERROR;
1238 break;
1239 default:
1240 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1242 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1243 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1244 return dwRet;
1247 /**************************************************************************
1248 * WAVE_mciSeek [internal]
1250 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1252 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1253 DWORD position, dwRet;
1255 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1257 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1258 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1260 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1261 if (!position) return MCIERR_MISSING_PARAMETER;
1262 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
1264 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1265 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1266 if (dwRet != MMSYSERR_NOERROR) return dwRet;
1268 if (dwFlags & MCI_TO) {
1269 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1270 if (position > wmw->ckWaveData.cksize)
1271 return MCIERR_OUTOFRANGE;
1272 } else if (dwFlags & MCI_SEEK_TO_START) {
1273 position = 0;
1274 } else {
1275 position = wmw->ckWaveData.cksize;
1277 /* Seek rounds down, unless at end */
1278 if (position != wmw->ckWaveData.cksize) {
1279 position /= wmw->lpWaveFormat->nBlockAlign;
1280 position *= wmw->lpWaveFormat->nBlockAlign;
1282 wmw->dwPosition = position;
1283 TRACE("Seeking to position=%u bytes\n", position);
1285 if (dwFlags & MCI_NOTIFY)
1286 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1288 return MMSYSERR_NOERROR;
1291 /**************************************************************************
1292 * WAVE_mciSet [internal]
1294 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
1296 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1298 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1300 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1301 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1303 if (dwFlags & MCI_SET_TIME_FORMAT) {
1304 switch (lpParms->dwTimeFormat) {
1305 case MCI_FORMAT_MILLISECONDS:
1306 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1307 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1308 break;
1309 case MCI_FORMAT_BYTES:
1310 TRACE("MCI_FORMAT_BYTES !\n");
1311 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1312 break;
1313 case MCI_FORMAT_SAMPLES:
1314 TRACE("MCI_FORMAT_SAMPLES !\n");
1315 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1316 break;
1317 default:
1318 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1319 return MCIERR_BAD_TIME_FORMAT;
1322 if (dwFlags & MCI_SET_VIDEO) {
1323 TRACE("No support for video !\n");
1324 return MCIERR_UNSUPPORTED_FUNCTION;
1326 if (dwFlags & MCI_SET_DOOR_OPEN) {
1327 TRACE("No support for door open !\n");
1328 return MCIERR_UNSUPPORTED_FUNCTION;
1330 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1331 TRACE("No support for door close !\n");
1332 return MCIERR_UNSUPPORTED_FUNCTION;
1334 if (dwFlags & MCI_SET_AUDIO) {
1335 if (dwFlags & MCI_SET_ON) {
1336 TRACE("MCI_SET_ON audio !\n");
1337 } else if (dwFlags & MCI_SET_OFF) {
1338 TRACE("MCI_SET_OFF audio !\n");
1339 } else {
1340 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1341 return MCIERR_BAD_INTEGER;
1344 switch (lpParms->dwAudio)
1346 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1347 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1348 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1349 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1352 if (dwFlags & MCI_WAVE_INPUT) {
1353 TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
1354 if (lpParms->wInput >= waveInGetNumDevs())
1355 return MCIERR_OUTOFRANGE;
1356 if (wmw->wInput != (WORD)lpParms->wInput)
1357 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1358 wmw->wInput = lpParms->wInput;
1360 if (dwFlags & MCI_WAVE_OUTPUT) {
1361 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
1362 if (lpParms->wOutput >= waveOutGetNumDevs())
1363 return MCIERR_OUTOFRANGE;
1364 if (wmw->wOutput != (WORD)lpParms->wOutput)
1365 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1366 wmw->wOutput = lpParms->wOutput;
1368 if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
1369 TRACE("MCI_WAVE_SET_ANYINPUT\n");
1370 if (wmw->wInput != (WORD)lpParms->wInput)
1371 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1372 wmw->wInput = WAVE_MAPPER;
1374 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
1375 TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1376 if (wmw->wOutput != (WORD)lpParms->wOutput)
1377 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1378 wmw->wOutput = WAVE_MAPPER;
1380 /* Set wave format parameters is refused after Open or Record.*/
1381 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1382 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
1383 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1384 if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
1385 return MCIERR_OUTOFRANGE;
1387 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1388 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1389 wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
1390 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1392 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1393 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1394 wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
1395 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1397 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1398 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1399 wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
1400 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1402 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1403 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1404 wmw->wfxRef.nChannels = lpParms->nChannels;
1405 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1407 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1408 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1409 wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
1410 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1412 if (dwFlags & MCI_NOTIFY)
1413 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1414 return 0;
1417 /**************************************************************************
1418 * WAVE_mciSave [internal]
1420 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1422 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1423 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1425 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1426 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1427 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1429 if (dwFlags & MCI_WAIT)
1431 FIXME("MCI_WAIT not implemented\n");
1433 WAVE_mciStop(wDevID, 0, NULL);
1435 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1436 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1438 ret = mmioClose(wmw->hFile, 0);
1439 wmw->hFile = 0;
1442 If the destination file already exists, it has to be overwritten. (Behaviour
1443 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1444 my applications. We are making use of mmioRename, which WILL NOT overwrite
1445 the destination file (which is what Windows does, also verified in Win2K)
1446 So, lets delete the destination file before calling mmioRename. If the
1447 destination file DOESN'T exist, the delete will fail silently. Let's also be
1448 careful not to lose our previous error code.
1450 tmpRet = GetLastError();
1451 DeleteFileW (lpParms->lpfilename);
1452 SetLastError(tmpRet);
1454 /* FIXME: Open file.wav; Save; must not rename the original file.
1455 * Nor must Save a.wav; Save b.wav rename a. */
1456 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1457 ret = MMSYSERR_NOERROR;
1460 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1461 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1463 if (ret == MMSYSERR_NOERROR)
1464 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1466 return ret;
1469 /**************************************************************************
1470 * WAVE_mciStatus [internal]
1472 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1474 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1475 DWORD ret = 0;
1477 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1478 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1479 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1480 if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER;
1482 if (dwFlags & MCI_STATUS_ITEM) {
1483 switch (lpParms->dwItem) {
1484 case MCI_STATUS_CURRENT_TRACK:
1485 lpParms->dwReturn = 1;
1486 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1487 break;
1488 case MCI_STATUS_LENGTH:
1489 if (!wmw->hFile) {
1490 lpParms->dwReturn = 0;
1491 return MCIERR_UNSUPPORTED_FUNCTION;
1493 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1494 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize);
1495 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1496 break;
1497 case MCI_STATUS_MODE:
1498 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1499 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1500 ret = MCI_RESOURCE_RETURNED;
1501 break;
1502 case MCI_STATUS_MEDIA_PRESENT:
1503 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1504 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1505 ret = MCI_RESOURCE_RETURNED;
1506 break;
1507 case MCI_STATUS_NUMBER_OF_TRACKS:
1508 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1509 lpParms->dwReturn = 1;
1510 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1511 break;
1512 case MCI_STATUS_POSITION:
1513 if (!wmw->hFile) {
1514 lpParms->dwReturn = 0;
1515 return MCIERR_UNSUPPORTED_FUNCTION;
1517 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1518 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1519 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
1520 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1521 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1522 break;
1523 case MCI_STATUS_READY:
1524 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1525 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1526 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1527 ret = MCI_RESOURCE_RETURNED;
1528 break;
1529 case MCI_STATUS_TIME_FORMAT:
1530 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1531 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1532 ret = MCI_RESOURCE_RETURNED;
1533 break;
1534 case MCI_WAVE_INPUT:
1535 if (wmw->wInput != (WORD)WAVE_MAPPER)
1536 lpParms->dwReturn = wmw->wInput;
1537 else {
1538 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1539 ret = MCI_RESOURCE_RETURNED;
1541 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
1542 break;
1543 case MCI_WAVE_OUTPUT:
1544 if (wmw->wOutput != (WORD)WAVE_MAPPER)
1545 lpParms->dwReturn = wmw->wOutput;
1546 else {
1547 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1548 ret = MCI_RESOURCE_RETURNED;
1550 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
1551 break;
1552 /* It is always ok to query wave format parameters,
1553 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1554 case MCI_WAVE_STATUS_FORMATTAG:
1555 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
1556 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1557 else {
1558 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
1559 ret = MCI_RESOURCE_RETURNED;
1561 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1562 break;
1563 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1564 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1565 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1566 break;
1567 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1568 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1569 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1570 break;
1571 case MCI_WAVE_STATUS_BLOCKALIGN:
1572 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1573 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1574 break;
1575 case MCI_WAVE_STATUS_CHANNELS:
1576 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1577 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1578 break;
1579 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1580 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1581 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1582 break;
1583 case MCI_WAVE_STATUS_LEVEL:
1584 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1585 lpParms->dwReturn = 0xAAAA5555;
1586 break;
1587 default:
1588 WARN("unknown command %08X !\n", lpParms->dwItem);
1589 return MCIERR_UNSUPPORTED_FUNCTION;
1592 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1593 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1594 return ret;
1597 /**************************************************************************
1598 * WAVE_mciGetDevCaps [internal]
1600 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1601 LPMCI_GETDEVCAPS_PARMS lpParms)
1603 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1604 DWORD ret = 0;
1606 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1608 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1609 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1611 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1612 switch(lpParms->dwItem) {
1613 case MCI_GETDEVCAPS_DEVICE_TYPE:
1614 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1615 ret = MCI_RESOURCE_RETURNED;
1616 break;
1617 case MCI_GETDEVCAPS_HAS_AUDIO:
1618 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1619 ret = MCI_RESOURCE_RETURNED;
1620 break;
1621 case MCI_GETDEVCAPS_HAS_VIDEO:
1622 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1623 ret = MCI_RESOURCE_RETURNED;
1624 break;
1625 case MCI_GETDEVCAPS_USES_FILES:
1626 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1627 ret = MCI_RESOURCE_RETURNED;
1628 break;
1629 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1630 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1631 ret = MCI_RESOURCE_RETURNED;
1632 break;
1633 case MCI_GETDEVCAPS_CAN_RECORD:
1634 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1635 ret = MCI_RESOURCE_RETURNED;
1636 break;
1637 case MCI_GETDEVCAPS_CAN_EJECT:
1638 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1639 ret = MCI_RESOURCE_RETURNED;
1640 break;
1641 case MCI_GETDEVCAPS_CAN_PLAY:
1642 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1643 ret = MCI_RESOURCE_RETURNED;
1644 break;
1645 case MCI_GETDEVCAPS_CAN_SAVE:
1646 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1647 ret = MCI_RESOURCE_RETURNED;
1648 break;
1649 case MCI_WAVE_GETDEVCAPS_INPUTS:
1650 lpParms->dwReturn = waveInGetNumDevs();
1651 break;
1652 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1653 lpParms->dwReturn = waveOutGetNumDevs();
1654 break;
1655 default:
1656 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1657 return MCIERR_UNRECOGNIZED_COMMAND;
1659 } else {
1660 WARN("No GetDevCaps-Item !\n");
1661 return MCIERR_UNRECOGNIZED_COMMAND;
1663 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1664 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1665 return ret;
1668 /**************************************************************************
1669 * WAVE_mciInfo [internal]
1671 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1673 DWORD ret = 0;
1674 LPCWSTR str = 0;
1675 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1677 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1679 if (!lpParms || !lpParms->lpstrReturn)
1680 return MCIERR_NULL_PARAMETER_BLOCK;
1682 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1684 if (wmw == NULL) {
1685 ret = MCIERR_INVALID_DEVICE_ID;
1686 } else {
1687 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1688 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1689 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1691 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1692 case MCI_INFO_PRODUCT: str = wszAudio; break;
1693 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1694 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1695 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1696 default:
1697 WARN("Don't know this info command (%u)\n", dwFlags);
1698 ret = MCIERR_UNRECOGNIZED_KEYWORD;
1701 if (!ret) {
1702 if (lpParms->dwRetSize) {
1703 WCHAR zero = 0;
1704 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
1705 * to the number of characters written, excluding \0. */
1706 lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
1707 } else ret = MCIERR_PARAM_OVERFLOW;
1709 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1710 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1711 return ret;
1714 /**************************************************************************
1715 * DriverProc (MCIWAVE.@)
1717 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1718 LPARAM dwParam1, LPARAM dwParam2)
1720 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1721 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1723 switch (wMsg) {
1724 case DRV_LOAD: return 1;
1725 case DRV_FREE: return 1;
1726 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1727 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1728 case DRV_ENABLE: return 1;
1729 case DRV_DISABLE: return 1;
1730 case DRV_QUERYCONFIGURE: return 1;
1731 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1732 case DRV_INSTALL: return DRVCNF_RESTART;
1733 case DRV_REMOVE: return DRVCNF_RESTART;
1736 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1738 switch (wMsg) {
1739 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1740 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1741 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1742 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1743 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1744 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1745 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS) dwParam2);
1746 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1747 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1748 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1749 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1750 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1751 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1752 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1753 /* commands that should be supported */
1754 case MCI_LOAD:
1755 case MCI_FREEZE:
1756 case MCI_PUT:
1757 case MCI_REALIZE:
1758 case MCI_UNFREEZE:
1759 case MCI_UPDATE:
1760 case MCI_WHERE:
1761 case MCI_STEP:
1762 case MCI_SPIN:
1763 case MCI_ESCAPE:
1764 case MCI_COPY:
1765 case MCI_CUT:
1766 case MCI_DELETE:
1767 case MCI_PASTE:
1768 FIXME("Unsupported command [%u]\n", wMsg);
1769 break;
1770 case MCI_WINDOW:
1771 TRACE("Unsupported command [%u]\n", wMsg);
1772 break;
1773 /* option which can be silenced */
1774 case MCI_CONFIGURE:
1775 return 0;
1776 case MCI_OPEN:
1777 case MCI_CLOSE:
1778 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1779 break;
1780 default:
1781 FIXME("is probably wrong msg [%u]\n", wMsg);
1782 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1784 return MCIERR_UNRECOGNIZED_COMMAND;