gdi32: Consider whether the logical font face is vertical when selecting.
[wine/multimedia.git] / dlls / mciwave / mciwave.c
blob723b5fe1e0f0e43ccebce6dda96a856720c54f43
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 wmw->dwEventCount = 1L; /* for first buffer */
875 TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
876 if (hEvent) SetEvent(hEvent);
878 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
879 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
880 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
881 TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
882 if (count < 1)
883 break;
884 /* count is always <= bufsize, so this is correct regarding the
885 * waveOutPrepareHeader function
887 waveHdr[whidx].dwBufferLength = count;
888 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
889 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
890 &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
891 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
892 if (dwRet) {
893 ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
894 dwRet = MCIERR_HARDWARE;
895 break;
897 left -= count;
898 wmw->dwPosition += count;
899 TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
900 /* InterlockedDecrement if and only if waveOutWrite is successful */
901 WAVE_mciPlayWaitDone(wmw);
902 whidx ^= 1;
905 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
907 /* just to get rid of some race conditions between play, stop and pause */
908 waveOutReset(wmw->hWave);
910 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
911 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
913 cleanUp:
914 if (dwFlags & MCI_NOTIFY)
915 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
917 HeapFree(GetProcessHeap(), 0, waveHdr);
919 if (wmw->hWave) {
920 waveOutClose(wmw->hWave);
921 wmw->hWave = 0;
923 CloseHandle(wmw->hEvent);
925 wmw->dwStatus = MCI_MODE_STOP;
927 /* Let the potentially asynchronous commands support FAILURE notification. */
928 if (oldcb) mciDriverNotify(oldcb, wDevID,
929 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
931 return dwRet;
934 /**************************************************************************
935 * WAVE_mciRecordCallback [internal]
937 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
938 DWORD_PTR dwInstance,
939 LPARAM dwParam1, LPARAM dwParam2)
941 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
942 LPWAVEHDR lpWaveHdr;
943 LONG count = 0;
945 switch (uMsg) {
946 case WIM_OPEN:
947 case WIM_CLOSE:
948 break;
949 case WIM_DATA:
950 lpWaveHdr = (LPWAVEHDR) dwParam1;
952 InterlockedIncrement(&wmw->dwEventCount);
954 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
956 lpWaveHdr->dwFlags &= ~WHDR_DONE;
957 if (count > 0)
958 wmw->dwPosition += count;
959 /* else error reporting ?? */
960 if (wmw->dwStatus == MCI_MODE_RECORD)
962 /* Only queue up another buffer if we are recording. We could receive this
963 message also when waveInReset() is called, since it notifies on all wave
964 buffers that are outstanding. Queueing up more sometimes causes waveInClose
965 to fail. */
966 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
967 TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
970 SetEvent(wmw->hEvent);
971 break;
972 default:
973 ERR("Unknown uMsg=%d\n", uMsg);
977 /******************************************************************
978 * WAVE_mciRecordWaitDone [internal]
980 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
982 for (;;) {
983 ResetEvent(wmw->hEvent);
984 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
985 break;
987 InterlockedIncrement(&wmw->dwEventCount);
989 WaitForSingleObject(wmw->hEvent, INFINITE);
993 /**************************************************************************
994 * WAVE_mciRecord [internal]
996 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
998 LPMCI_RECORD_PARMS lpParms = (void*)pmt;
999 DWORD end;
1000 DWORD dwRet = MMSYSERR_NOERROR;
1001 LONG bufsize;
1002 LPWAVEHDR waveHdr = NULL;
1003 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1004 HANDLE oldcb;
1006 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1008 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1009 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1011 if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1012 /* FIXME: parameters (start/end) in lpParams may not be used */
1013 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1016 /** This function will be called again by a thread when async is used.
1017 * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1018 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1020 if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
1021 !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
1022 return MCIERR_INTERNAL;
1025 wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1026 wmw->dwStatus = MCI_MODE_RECORD;
1028 if (!(dwFlags & MCI_WAIT)) {
1029 return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1030 (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1033 /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1034 * we don't modify the wave part of an existing file (ie. we always erase an
1035 * existing content, we don't overwrite)
1037 HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
1038 dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1039 if (dwRet != 0) return dwRet;
1041 /* new RIFF file, lpWaveFormat now valid */
1042 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1043 if (dwRet != 0) return dwRet;
1045 if (dwFlags & MCI_TO) {
1046 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1047 } else end = 0xFFFFFFFF;
1048 if (dwFlags & MCI_FROM) {
1049 DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1050 if (wmw->ckWaveData.cksize < position) return MCIERR_OUTOFRANGE;
1051 /* Seek rounds down, so do we. */
1052 position /= wmw->lpWaveFormat->nBlockAlign;
1053 position *= wmw->lpWaveFormat->nBlockAlign;
1054 wmw->dwPosition = position;
1056 if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1058 TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1060 oldcb = InterlockedExchangePointer(&wmw->hCallback,
1061 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1062 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1063 oldcb = NULL;
1065 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1066 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1068 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1070 /* Go back to the beginning of the chunk plus the requested position */
1071 /* FIXME: I'm not sure this is correct, notably because some data linked to
1072 * the decompression state machine will not be correctly initialized.
1073 * Try it this way (other way would be to decompress from 0 up to dwPosition
1074 * and to start sending to hWave when dwPosition is reached).
1076 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1078 dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
1079 (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1081 if (dwRet != MMSYSERR_NOERROR) {
1082 TRACE("Can't open low level audio device %d\n", dwRet);
1083 dwRet = MCIERR_DEVICE_OPEN;
1084 wmw->hWave = 0;
1085 goto cleanUp;
1088 /* make it so that 3 buffers per second are needed */
1089 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1091 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1092 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1093 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1094 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1095 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1096 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1097 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1099 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1100 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1101 dwRet = MCIERR_INTERNAL;
1102 goto cleanUp;
1105 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1106 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1107 dwRet = MCIERR_INTERNAL;
1108 goto cleanUp;
1111 wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1112 wmw->dwEventCount = 1L; /* for first buffer */
1114 TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1116 dwRet = waveInStart(wmw->hWave);
1118 if (hEvent) SetEvent(hEvent);
1120 while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1121 WAVE_mciRecordWaitDone(wmw);
1123 /* Grab callback before another thread kicks in after we change dwStatus. */
1124 if (dwFlags & MCI_NOTIFY) {
1125 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1126 dwFlags &= ~MCI_NOTIFY;
1128 /* needed so that the callback above won't add again the buffers returned by the reset */
1129 wmw->dwStatus = MCI_MODE_STOP;
1131 waveInReset(wmw->hWave);
1133 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1134 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1136 dwRet = 0;
1138 cleanUp:
1139 if (dwFlags & MCI_NOTIFY)
1140 oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1142 HeapFree(GetProcessHeap(), 0, waveHdr);
1144 if (wmw->hWave) {
1145 waveInClose(wmw->hWave);
1146 wmw->hWave = 0;
1148 CloseHandle(wmw->hEvent);
1150 wmw->dwStatus = MCI_MODE_STOP;
1152 if (oldcb) mciDriverNotify(oldcb, wDevID,
1153 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1155 return dwRet;
1159 /**************************************************************************
1160 * WAVE_mciPause [internal]
1162 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1164 DWORD dwRet;
1165 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1167 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1169 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1171 switch (wmw->dwStatus) {
1172 case MCI_MODE_PLAY:
1173 dwRet = waveOutPause(wmw->hWave);
1174 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1175 else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1176 ERR("waveOutPause error %d\n",dwRet);
1177 dwRet = MCIERR_INTERNAL;
1179 break;
1180 case MCI_MODE_RECORD:
1181 dwRet = waveInStop(wmw->hWave);
1182 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1183 else {
1184 ERR("waveInStop error %d\n",dwRet);
1185 dwRet = MCIERR_INTERNAL;
1187 break;
1188 case MCI_MODE_PAUSE:
1189 dwRet = MMSYSERR_NOERROR;
1190 break;
1191 default:
1192 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1194 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1195 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1196 return dwRet;
1199 /**************************************************************************
1200 * WAVE_mciResume [internal]
1202 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1204 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1205 DWORD dwRet;
1207 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1209 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1211 switch (wmw->dwStatus) {
1212 case MCI_MODE_PAUSE:
1213 /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1214 if (wmw->fInput) {
1215 dwRet = waveInStart(wmw->hWave);
1216 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1217 else {
1218 ERR("waveInStart error %d\n",dwRet);
1219 dwRet = MCIERR_INTERNAL;
1221 } else {
1222 dwRet = waveOutRestart(wmw->hWave);
1223 if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1224 else {
1225 ERR("waveOutRestart error %d\n",dwRet);
1226 dwRet = MCIERR_INTERNAL;
1229 break;
1230 case MCI_MODE_PLAY:
1231 case MCI_MODE_RECORD:
1232 dwRet = MMSYSERR_NOERROR;
1233 break;
1234 default:
1235 dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1237 if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1238 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1239 return dwRet;
1242 /**************************************************************************
1243 * WAVE_mciSeek [internal]
1245 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1247 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1248 DWORD position, dwRet;
1250 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1252 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1253 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1255 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1256 if (!position) return MCIERR_MISSING_PARAMETER;
1257 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE;
1259 /* Stop sends MCI_NOTIFY_ABORTED when needed */
1260 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1261 if (dwRet != MMSYSERR_NOERROR) return dwRet;
1263 if (dwFlags & MCI_TO) {
1264 position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1265 if (position > wmw->ckWaveData.cksize)
1266 return MCIERR_OUTOFRANGE;
1267 } else if (dwFlags & MCI_SEEK_TO_START) {
1268 position = 0;
1269 } else {
1270 position = wmw->ckWaveData.cksize;
1272 /* Seek rounds down, unless at end */
1273 if (position != wmw->ckWaveData.cksize) {
1274 position /= wmw->lpWaveFormat->nBlockAlign;
1275 position *= wmw->lpWaveFormat->nBlockAlign;
1277 wmw->dwPosition = position;
1278 TRACE("Seeking to position=%u bytes\n", position);
1280 if (dwFlags & MCI_NOTIFY)
1281 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1283 return MMSYSERR_NOERROR;
1286 /**************************************************************************
1287 * WAVE_mciSet [internal]
1289 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
1291 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1293 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1295 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1296 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1298 if (dwFlags & MCI_SET_TIME_FORMAT) {
1299 switch (lpParms->dwTimeFormat) {
1300 case MCI_FORMAT_MILLISECONDS:
1301 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1302 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1303 break;
1304 case MCI_FORMAT_BYTES:
1305 TRACE("MCI_FORMAT_BYTES !\n");
1306 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1307 break;
1308 case MCI_FORMAT_SAMPLES:
1309 TRACE("MCI_FORMAT_SAMPLES !\n");
1310 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1311 break;
1312 default:
1313 WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1314 return MCIERR_BAD_TIME_FORMAT;
1317 if (dwFlags & MCI_SET_VIDEO) {
1318 TRACE("No support for video !\n");
1319 return MCIERR_UNSUPPORTED_FUNCTION;
1321 if (dwFlags & MCI_SET_DOOR_OPEN) {
1322 TRACE("No support for door open !\n");
1323 return MCIERR_UNSUPPORTED_FUNCTION;
1325 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1326 TRACE("No support for door close !\n");
1327 return MCIERR_UNSUPPORTED_FUNCTION;
1329 if (dwFlags & MCI_SET_AUDIO) {
1330 if (dwFlags & MCI_SET_ON) {
1331 TRACE("MCI_SET_ON audio !\n");
1332 } else if (dwFlags & MCI_SET_OFF) {
1333 TRACE("MCI_SET_OFF audio !\n");
1334 } else {
1335 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1336 return MCIERR_BAD_INTEGER;
1339 switch (lpParms->dwAudio)
1341 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1342 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1343 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1344 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1347 if (dwFlags & MCI_WAVE_INPUT) {
1348 TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
1349 if (lpParms->wInput >= waveInGetNumDevs())
1350 return MCIERR_OUTOFRANGE;
1351 if (wmw->wInput != (WORD)lpParms->wInput)
1352 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1353 wmw->wInput = lpParms->wInput;
1355 if (dwFlags & MCI_WAVE_OUTPUT) {
1356 TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
1357 if (lpParms->wOutput >= waveOutGetNumDevs())
1358 return MCIERR_OUTOFRANGE;
1359 if (wmw->wOutput != (WORD)lpParms->wOutput)
1360 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1361 wmw->wOutput = lpParms->wOutput;
1363 if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
1364 TRACE("MCI_WAVE_SET_ANYINPUT\n");
1365 if (wmw->wInput != (WORD)lpParms->wInput)
1366 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1367 wmw->wInput = WAVE_MAPPER;
1369 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
1370 TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1371 if (wmw->wOutput != (WORD)lpParms->wOutput)
1372 WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1373 wmw->wOutput = WAVE_MAPPER;
1375 /* Set wave format parameters is refused after Open or Record.*/
1376 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1377 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
1378 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1379 if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
1380 return MCIERR_OUTOFRANGE;
1382 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1383 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1384 wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
1385 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1387 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1388 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1389 wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
1390 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1392 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1393 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1394 wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
1395 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1397 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1398 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1399 wmw->wfxRef.nChannels = lpParms->nChannels;
1400 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1402 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1403 if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1404 wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
1405 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1407 if (dwFlags & MCI_NOTIFY)
1408 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1409 return 0;
1412 /**************************************************************************
1413 * WAVE_mciSave [internal]
1415 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1417 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1418 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1420 TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1421 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1422 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1424 if (dwFlags & MCI_WAIT)
1426 FIXME("MCI_WAIT not implemented\n");
1428 WAVE_mciStop(wDevID, 0, NULL);
1430 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1431 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1433 ret = mmioClose(wmw->hFile, 0);
1434 wmw->hFile = 0;
1437 If the destination file already exists, it has to be overwritten. (Behaviour
1438 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1439 my applications. We are making use of mmioRename, which WILL NOT overwrite
1440 the destination file (which is what Windows does, also verified in Win2K)
1441 So, lets delete the destination file before calling mmioRename. If the
1442 destination file DOESN'T exist, the delete will fail silently. Let's also be
1443 careful not to lose our previous error code.
1445 tmpRet = GetLastError();
1446 DeleteFileW (lpParms->lpfilename);
1447 SetLastError(tmpRet);
1449 /* FIXME: Open file.wav; Save; must not rename the original file.
1450 * Nor must Save a.wav; Save b.wav rename a. */
1451 if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1452 ret = MMSYSERR_NOERROR;
1455 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1456 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1458 if (ret == MMSYSERR_NOERROR)
1459 ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1461 return ret;
1464 /**************************************************************************
1465 * WAVE_mciStatus [internal]
1467 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1469 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1470 DWORD ret = 0;
1472 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1473 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1474 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1475 if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER;
1477 if (dwFlags & MCI_STATUS_ITEM) {
1478 switch (lpParms->dwItem) {
1479 case MCI_STATUS_CURRENT_TRACK:
1480 lpParms->dwReturn = 1;
1481 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1482 break;
1483 case MCI_STATUS_LENGTH:
1484 if (!wmw->hFile) {
1485 lpParms->dwReturn = 0;
1486 return MCIERR_UNSUPPORTED_FUNCTION;
1488 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1489 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize);
1490 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1491 break;
1492 case MCI_STATUS_MODE:
1493 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1494 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1495 ret = MCI_RESOURCE_RETURNED;
1496 break;
1497 case MCI_STATUS_MEDIA_PRESENT:
1498 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1499 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1500 ret = MCI_RESOURCE_RETURNED;
1501 break;
1502 case MCI_STATUS_NUMBER_OF_TRACKS:
1503 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1504 lpParms->dwReturn = 1;
1505 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1506 break;
1507 case MCI_STATUS_POSITION:
1508 if (!wmw->hFile) {
1509 lpParms->dwReturn = 0;
1510 return MCIERR_UNSUPPORTED_FUNCTION;
1512 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1513 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1514 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
1515 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1516 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1517 break;
1518 case MCI_STATUS_READY:
1519 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1520 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1521 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1522 ret = MCI_RESOURCE_RETURNED;
1523 break;
1524 case MCI_STATUS_TIME_FORMAT:
1525 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1526 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1527 ret = MCI_RESOURCE_RETURNED;
1528 break;
1529 case MCI_WAVE_INPUT:
1530 if (wmw->wInput != (WORD)WAVE_MAPPER)
1531 lpParms->dwReturn = wmw->wInput;
1532 else {
1533 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1534 ret = MCI_RESOURCE_RETURNED;
1536 TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
1537 break;
1538 case MCI_WAVE_OUTPUT:
1539 if (wmw->wOutput != (WORD)WAVE_MAPPER)
1540 lpParms->dwReturn = wmw->wOutput;
1541 else {
1542 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1543 ret = MCI_RESOURCE_RETURNED;
1545 TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
1546 break;
1547 /* It is always ok to query wave format parameters,
1548 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1549 case MCI_WAVE_STATUS_FORMATTAG:
1550 if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
1551 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1552 else {
1553 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
1554 ret = MCI_RESOURCE_RETURNED;
1556 TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1557 break;
1558 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1559 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1560 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1561 break;
1562 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1563 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1564 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1565 break;
1566 case MCI_WAVE_STATUS_BLOCKALIGN:
1567 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1568 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1569 break;
1570 case MCI_WAVE_STATUS_CHANNELS:
1571 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1572 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1573 break;
1574 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1575 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1576 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1577 break;
1578 case MCI_WAVE_STATUS_LEVEL:
1579 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1580 lpParms->dwReturn = 0xAAAA5555;
1581 break;
1582 default:
1583 WARN("unknown command %08X !\n", lpParms->dwItem);
1584 return MCIERR_UNSUPPORTED_FUNCTION;
1587 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1588 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1589 return ret;
1592 /**************************************************************************
1593 * WAVE_mciGetDevCaps [internal]
1595 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1596 LPMCI_GETDEVCAPS_PARMS lpParms)
1598 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1599 DWORD ret = 0;
1601 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1603 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1604 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1606 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1607 switch(lpParms->dwItem) {
1608 case MCI_GETDEVCAPS_DEVICE_TYPE:
1609 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1610 ret = MCI_RESOURCE_RETURNED;
1611 break;
1612 case MCI_GETDEVCAPS_HAS_AUDIO:
1613 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1614 ret = MCI_RESOURCE_RETURNED;
1615 break;
1616 case MCI_GETDEVCAPS_HAS_VIDEO:
1617 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1618 ret = MCI_RESOURCE_RETURNED;
1619 break;
1620 case MCI_GETDEVCAPS_USES_FILES:
1621 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1622 ret = MCI_RESOURCE_RETURNED;
1623 break;
1624 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1625 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1626 ret = MCI_RESOURCE_RETURNED;
1627 break;
1628 case MCI_GETDEVCAPS_CAN_RECORD:
1629 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1630 ret = MCI_RESOURCE_RETURNED;
1631 break;
1632 case MCI_GETDEVCAPS_CAN_EJECT:
1633 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1634 ret = MCI_RESOURCE_RETURNED;
1635 break;
1636 case MCI_GETDEVCAPS_CAN_PLAY:
1637 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1638 ret = MCI_RESOURCE_RETURNED;
1639 break;
1640 case MCI_GETDEVCAPS_CAN_SAVE:
1641 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1642 ret = MCI_RESOURCE_RETURNED;
1643 break;
1644 case MCI_WAVE_GETDEVCAPS_INPUTS:
1645 lpParms->dwReturn = waveInGetNumDevs();
1646 break;
1647 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1648 lpParms->dwReturn = waveOutGetNumDevs();
1649 break;
1650 default:
1651 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1652 return MCIERR_UNRECOGNIZED_COMMAND;
1654 } else {
1655 WARN("No GetDevCaps-Item !\n");
1656 return MCIERR_UNRECOGNIZED_COMMAND;
1658 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1659 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1660 return ret;
1663 /**************************************************************************
1664 * WAVE_mciInfo [internal]
1666 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1668 DWORD ret = 0;
1669 LPCWSTR str = 0;
1670 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1672 TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1674 if (!lpParms || !lpParms->lpstrReturn)
1675 return MCIERR_NULL_PARAMETER_BLOCK;
1677 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1679 if (wmw == NULL) {
1680 ret = MCIERR_INVALID_DEVICE_ID;
1681 } else {
1682 static const WCHAR wszAudio [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1683 static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1684 static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1686 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1687 case MCI_INFO_PRODUCT: str = wszAudio; break;
1688 case MCI_INFO_FILE: str = wmw->lpFileName; break;
1689 case MCI_WAVE_INPUT: str = wszWaveIn; break;
1690 case MCI_WAVE_OUTPUT: str = wszWaveOut; break;
1691 default:
1692 WARN("Don't know this info command (%u)\n", dwFlags);
1693 ret = MCIERR_UNRECOGNIZED_KEYWORD;
1696 if (!ret) {
1697 if (lpParms->dwRetSize) {
1698 WCHAR zero = 0;
1699 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
1700 * to the number of characters written, excluding \0. */
1701 lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
1702 } else ret = MCIERR_PARAM_OVERFLOW;
1704 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1705 WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1706 return ret;
1709 /**************************************************************************
1710 * DriverProc (MCIWAVE.@)
1712 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1713 LPARAM dwParam1, LPARAM dwParam2)
1715 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1716 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1718 switch (wMsg) {
1719 case DRV_LOAD: return 1;
1720 case DRV_FREE: return 1;
1721 case DRV_OPEN: return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1722 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1723 case DRV_ENABLE: return 1;
1724 case DRV_DISABLE: return 1;
1725 case DRV_QUERYCONFIGURE: return 1;
1726 case DRV_CONFIGURE: MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1727 case DRV_INSTALL: return DRVCNF_RESTART;
1728 case DRV_REMOVE: return DRVCNF_RESTART;
1731 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1733 switch (wMsg) {
1734 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW) dwParam2);
1735 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1736 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1737 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, dwParam2, NULL);
1738 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, dwParam2, NULL);
1739 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1740 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS) dwParam2);
1741 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1742 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1743 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1744 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1745 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSW) dwParam2);
1746 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1747 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW) dwParam2);
1748 /* commands that should be supported */
1749 case MCI_LOAD:
1750 case MCI_FREEZE:
1751 case MCI_PUT:
1752 case MCI_REALIZE:
1753 case MCI_UNFREEZE:
1754 case MCI_UPDATE:
1755 case MCI_WHERE:
1756 case MCI_STEP:
1757 case MCI_SPIN:
1758 case MCI_ESCAPE:
1759 case MCI_COPY:
1760 case MCI_CUT:
1761 case MCI_DELETE:
1762 case MCI_PASTE:
1763 FIXME("Unsupported command [%u]\n", wMsg);
1764 break;
1765 case MCI_WINDOW:
1766 TRACE("Unsupported command [%u]\n", wMsg);
1767 break;
1768 /* option which can be silenced */
1769 case MCI_CONFIGURE:
1770 return 0;
1771 case MCI_OPEN:
1772 case MCI_CLOSE:
1773 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1774 break;
1775 default:
1776 FIXME("is probably wrong msg [%u]\n", wMsg);
1777 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1779 return MCIERR_UNRECOGNIZED_COMMAND;