- MCI_SAVE should overwrite its destination file.
[wine.git] / dlls / winmm / mciwave / mciwave.c
blobdfb8e8d37a10e90f141e22a016dbc73824a1658b
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
6 * 1999 Eric Pouech
7 * 2000 Francois Jacques
8 */
10 #include "winerror.h"
11 #include "windef.h"
12 #include "winbase.h"
13 #include "wingdi.h"
14 #include "winuser.h"
15 #include "mmddk.h"
16 #include "digitalv.h"
17 #include "heap.h"
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(mciwave);
22 typedef struct {
23 UINT wDevID;
24 HANDLE hWave;
25 int nUseCount; /* Incremented for each shared open */
26 BOOL fShareable; /* TRUE if first open was shareable */
27 HMMIO hFile; /* mmio file handle open as Element */
28 MCI_WAVE_OPEN_PARMSA openParms;
29 LPWAVEFORMATEX lpWaveFormat;
30 BOOL fInput; /* FALSE = Output, TRUE = Input */
31 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
32 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
33 DWORD dwRemaining; /* remaining bytes to play or record */
34 DWORD dwPosition; /* position in bytes in chunk */
35 HANDLE hEvent; /* for synchronization */
36 DWORD dwEventCount; /* for synchronization */
37 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
38 MMCKINFO ckMainRIFF; /* main RIFF chunk */
39 MMCKINFO ckWaveData; /* data chunk */
40 } WINE_MCIWAVE;
42 /* ===================================================================
43 * ===================================================================
44 * FIXME: should be using the new mmThreadXXXX functions from WINMM
45 * instead of those
46 * it would require to add a wine internal flag to mmThreadCreate
47 * in order to pass a 32 bit function instead of a 16 bit one
48 * ===================================================================
49 * =================================================================== */
51 struct SCA {
52 UINT wDevID;
53 UINT wMsg;
54 DWORD dwParam1;
55 DWORD dwParam2;
58 /**************************************************************************
59 * MCI_SCAStarter [internal]
61 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
63 struct SCA* sca = (struct SCA*)arg;
64 DWORD ret;
66 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
67 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
68 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
69 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
70 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
71 HeapFree(GetProcessHeap(), 0, sca);
72 ExitThread(ret);
73 WARN("Should not happen ? what's wrong \n");
74 /* should not go after this point */
75 return ret;
78 /**************************************************************************
79 * MCI_SendCommandAsync [internal]
81 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
82 DWORD dwParam2, UINT size)
84 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
86 if (sca == 0)
87 return MCIERR_OUT_OF_MEMORY;
89 sca->wDevID = wDevID;
90 sca->wMsg = wMsg;
91 sca->dwParam1 = dwParam1;
93 if (size && dwParam2) {
94 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
95 /* copy structure passed by program in dwParam2 to be sure
96 * we can still use it whatever the program does
98 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
99 } else {
100 sca->dwParam2 = dwParam2;
103 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
104 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
105 return MCI_SCAStarter(&sca);
107 return 0;
110 /*======================================================================*
111 * MCI WAVE implemantation *
112 *======================================================================*/
114 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
116 /**************************************************************************
117 * MCIWAVE_drvOpen [internal]
119 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
121 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
123 if (!wmw)
124 return 0;
126 wmw->wDevID = modp->wDeviceID;
127 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
128 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
129 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
130 return modp->wDeviceID;
133 /**************************************************************************
134 * MCIWAVE_drvClose [internal]
136 static DWORD WAVE_drvClose(DWORD dwDevID)
138 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
140 if (wmw) {
141 HeapFree(GetProcessHeap(), 0, wmw);
142 mciSetDriverData(dwDevID, 0);
143 return 1;
145 return 0;
148 /**************************************************************************
149 * WAVE_mciGetOpenDev [internal]
151 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
153 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
155 if (wmw == NULL || wmw->nUseCount == 0) {
156 WARN("Invalid wDevID=%u\n", wDevID);
157 return 0;
159 return wmw;
162 /**************************************************************************
163 * WAVE_ConvertByteToTimeFormat [internal]
165 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
167 DWORD ret = 0;
169 switch (wmw->dwMciTimeFormat) {
170 case MCI_FORMAT_MILLISECONDS:
171 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
172 break;
173 case MCI_FORMAT_BYTES:
174 ret = val;
175 break;
176 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
177 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
178 break;
179 default:
180 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
182 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
183 *lpRet = 0;
184 return ret;
187 /**************************************************************************
188 * WAVE_ConvertTimeFormatToByte [internal]
190 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
192 DWORD ret = 0;
194 switch (wmw->dwMciTimeFormat) {
195 case MCI_FORMAT_MILLISECONDS:
196 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
197 break;
198 case MCI_FORMAT_BYTES:
199 ret = val;
200 break;
201 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
202 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
203 break;
204 default:
205 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
207 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
208 return ret;
211 /**************************************************************************
212 * WAVE_mciReadFmt [internal]
214 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
216 MMCKINFO mmckInfo;
217 long r;
219 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
220 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
221 return MCIERR_INVALID_FILE;
222 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
223 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
225 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
226 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
227 if (r < sizeof(WAVEFORMAT))
228 return MCIERR_INVALID_FILE;
230 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
231 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
232 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
233 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
234 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
235 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
236 if (r >= (long)sizeof(WAVEFORMATEX))
237 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
239 mmioAscend(wmw->hFile, &mmckInfo, 0);
240 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
241 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
242 TRACE("can't find data chunk\n");
243 return MCIERR_INVALID_FILE;
245 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
246 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
247 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
248 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
250 return 0;
253 /**************************************************************************
254 * WAVE_mciCreateRIFFSkeleton [internal]
256 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
258 MMCKINFO ckWaveFormat;
260 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
261 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
262 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
265 HMMIO hmmio = wmw->hFile;
267 lpckRIFF->ckid = FOURCC_RIFF;
268 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
269 lpckRIFF->cksize = 0;
271 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
272 goto err;
274 ckWaveFormat.fccType = 0;
275 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
276 ckWaveFormat.cksize = 16;
278 if (!lpWaveFormat)
280 TRACE("allocating waveformat with default waveformat 11khz/8bit/mono \n");
282 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
284 lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
285 lpWaveFormat->nChannels = 1; /* MONO */
286 lpWaveFormat->nSamplesPerSec = 11025;
287 lpWaveFormat->nAvgBytesPerSec = 11025;
288 lpWaveFormat->nBlockAlign = 1;
289 lpWaveFormat->wBitsPerSample = 8;
290 lpWaveFormat->cbSize = 0; /* don't care */
293 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
294 goto err;
296 /* only the first 16 bytes are serialized */
297 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
298 goto err;
300 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
301 goto err;
303 lpckWaveData->cksize = 0;
304 lpckWaveData->fccType = 0;
305 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
307 /* create data chunk */
308 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
309 goto err;
311 return 0;
313 err:
314 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
315 return MCIERR_INVALID_FILE;
318 /**************************************************************************
319 * WAVE_mciOpen [internal]
321 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
323 DWORD dwRet = 0;
324 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
325 CHAR* pszTmpFileName = 0;
327 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
328 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
329 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
331 if (dwFlags & MCI_OPEN_SHAREABLE)
332 return MCIERR_HARDWARE;
334 if (wmw->nUseCount > 0) {
335 /* The driver is already opened on this channel
336 * Wave driver cannot be shared
338 return MCIERR_DEVICE_OPEN;
341 wmw->nUseCount++;
343 wmw->fInput = FALSE;
344 wmw->hWave = 0;
345 wmw->dwStatus = MCI_MODE_NOT_READY;
347 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
349 if (dwFlags & MCI_OPEN_ELEMENT) {
350 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
351 /* could it be that (DWORD)lpOpenParms->lpstrElementName
352 * contains the hFile value ?
354 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
355 } else {
356 if (strlen(lpOpenParms->lpstrElementName) > 0) {
357 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
359 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
360 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
362 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
363 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
364 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
366 if (wmw->hFile == 0) {
367 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
368 dwRet = MCIERR_FILE_NOT_FOUND;
370 else
372 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
374 /* make sure we're are the beginning of the file */
375 mmioSeek(wmw->hFile, 0, SEEK_SET);
377 /* first reading of this file. read the waveformat chunk */
378 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
379 dwRet = MCIERR_INVALID_FILE;
380 } else {
381 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
382 (LPSTR)&(lpckMainRIFF->ckid),
383 (LPSTR) &(lpckMainRIFF->fccType),
384 (lpckMainRIFF->cksize));
386 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
387 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
388 dwRet = MCIERR_INVALID_FILE;
389 } else {
390 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
395 else {
396 wmw->hFile = 0;
399 else {
400 CHAR szTmpPath[MAX_PATH];
401 CHAR szPrefix[4] = "TMP\0";
403 pszTmpFileName = HeapAlloc(GetProcessHeap(),
404 HEAP_ZERO_MEMORY,
405 MAX_PATH * sizeof(*pszTmpFileName));
407 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
408 WARN("can't retrieve temp path!\n");
409 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
410 return MCIERR_FILE_NOT_FOUND;
413 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
414 WARN("can't retrieve temp file name!\n");
415 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
416 return MCIERR_FILE_NOT_FOUND;
419 wmw->bTemporaryFile = TRUE;
421 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
423 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
425 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
426 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
428 if (wmw->hFile == 0) {
429 /* temporary file could not be created. clean filename. */
430 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
431 WARN("can't create file='%s' !\n", pszTmpFileName);
432 dwRet = MCIERR_FILE_NOT_FOUND;
439 TRACE("hFile=%u\n", wmw->hFile);
441 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
443 if (wmw->bTemporaryFile == TRUE)
445 /* Additional openParms is temporary file's name */
446 wmw->openParms.lpstrElementName = pszTmpFileName;
449 if (dwRet == 0) {
450 if (wmw->lpWaveFormat) {
451 switch (wmw->lpWaveFormat->wFormatTag) {
452 case WAVE_FORMAT_PCM:
453 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
454 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
455 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
456 wmw->lpWaveFormat->nAvgBytesPerSec,
457 wmw->lpWaveFormat->nSamplesPerSec *
458 wmw->lpWaveFormat->nBlockAlign);
459 wmw->lpWaveFormat->nAvgBytesPerSec =
460 wmw->lpWaveFormat->nSamplesPerSec *
461 wmw->lpWaveFormat->nBlockAlign;
463 break;
466 wmw->dwPosition = 0;
468 wmw->dwStatus = MCI_MODE_STOP;
469 } else {
470 wmw->nUseCount--;
471 if (wmw->hFile != 0)
472 mmioClose(wmw->hFile, 0);
473 wmw->hFile = 0;
475 return dwRet;
478 /**************************************************************************
479 * WAVE_mciCue [internal]
481 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
484 FIXME
486 This routine is far from complete. At the moment only a check is done on the
487 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
488 is the default.
490 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
491 are ignored
494 DWORD dwRet;
495 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
497 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
499 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
501 /* always close elements ? */
502 if (wmw->hFile != 0) {
503 mmioClose(wmw->hFile, 0);
504 wmw->hFile = 0;
507 dwRet = MMSYSERR_NOERROR; /* assume success */
509 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
510 dwRet = waveOutClose(wmw->hWave);
511 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
512 wmw->fInput = TRUE;
513 } else if (wmw->fInput) {
514 dwRet = waveInClose(wmw->hWave);
515 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
516 wmw->fInput = FALSE;
518 wmw->hWave = 0;
519 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
522 /**************************************************************************
523 * WAVE_mciStop [internal]
525 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
527 DWORD dwRet = 0;
528 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
530 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
532 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
534 /* wait for playback thread (if any) to exit before processing further */
535 switch (wmw->dwStatus) {
536 case MCI_MODE_PAUSE:
537 case MCI_MODE_PLAY:
538 case MCI_MODE_RECORD:
540 int oldStat = wmw->dwStatus;
541 wmw->dwStatus = MCI_MODE_NOT_READY;
542 if (oldStat == MCI_MODE_PAUSE)
543 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
545 while (wmw->dwStatus != MCI_MODE_STOP)
546 Sleep(10);
547 break;
550 wmw->dwPosition = 0;
552 /* sanity resets */
553 wmw->dwStatus = MCI_MODE_STOP;
555 if ((dwFlags & MCI_NOTIFY) && lpParms) {
556 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
557 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
560 return dwRet;
563 /**************************************************************************
564 * WAVE_mciClose [internal]
566 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
568 DWORD dwRet = 0;
569 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
571 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
573 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
575 if (wmw->dwStatus != MCI_MODE_STOP) {
576 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
579 wmw->nUseCount--;
581 if (wmw->nUseCount == 0) {
582 if (wmw->hFile != 0) {
583 mmioClose(wmw->hFile, 0);
584 wmw->hFile = 0;
588 /* That string got allocated in mciOpen because no filename was specified
589 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
590 * allocated by mciOpen, *NOT* the application.
592 if (wmw->bTemporaryFile)
594 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
595 wmw->openParms.lpstrElementName = NULL;
598 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
599 wmw->lpWaveFormat = NULL;
601 if ((dwFlags & MCI_NOTIFY) && lpParms) {
602 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
603 wmw->openParms.wDeviceID,
604 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
607 return 0;
610 /**************************************************************************
611 * WAVE_mciPlayCallback [internal]
613 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
614 DWORD dwInstance,
615 DWORD dwParam1, DWORD dwParam2)
617 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
619 switch (uMsg) {
620 case WOM_OPEN:
621 case WOM_CLOSE:
622 break;
623 case WOM_DONE:
624 InterlockedIncrement(&wmw->dwEventCount);
625 TRACE("Returning waveHdr=%lx\n", dwParam1);
626 SetEvent(wmw->hEvent);
627 break;
628 default:
629 ERR("Unknown uMsg=%d\n", uMsg);
633 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
635 for (;;) {
636 ResetEvent(wmw->hEvent);
637 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
638 break;
640 InterlockedIncrement(&wmw->dwEventCount);
642 WaitForSingleObject(wmw->hEvent, INFINITE);
646 /**************************************************************************
647 * WAVE_mciPlay [internal]
649 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
651 DWORD end;
652 LONG bufsize, count, left;
653 DWORD dwRet = 0;
654 LPWAVEHDR waveHdr = NULL;
655 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
656 int whidx;
658 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
660 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
661 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
663 /* FIXME : since there is no way to determine in which mode the device is
664 * open (recording/playback) automatically switch from a mode to another
666 wmw->fInput = FALSE;
668 if (wmw->fInput) {
669 WARN("cannot play on input device\n");
670 return MCIERR_NONAPPLICABLE_FUNCTION;
673 if (wmw->hFile == 0) {
674 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
675 return MCIERR_FILE_NOT_FOUND;
678 if (wmw->dwStatus == MCI_MODE_PAUSE) {
679 /* FIXME: parameters (start/end) in lpParams may not be used */
680 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
683 /** This function will be called again by a thread when async is used.
684 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
685 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
687 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
688 return MCIERR_INTERNAL;
691 wmw->dwStatus = MCI_MODE_PLAY;
693 if (!(dwFlags & MCI_WAIT)) {
694 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
695 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
698 end = 0xFFFFFFFF;
699 if (lpParms && (dwFlags & MCI_FROM)) {
700 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
702 if (lpParms && (dwFlags & MCI_TO)) {
703 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
706 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
708 if (end <= wmw->dwPosition)
709 return TRUE;
712 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
713 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
715 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
716 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
718 if (dwRet == 0) {
719 if (wmw->lpWaveFormat) {
720 switch (wmw->lpWaveFormat->wFormatTag) {
721 case WAVE_FORMAT_PCM:
722 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
723 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
724 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
725 wmw->lpWaveFormat->nAvgBytesPerSec,
726 wmw->lpWaveFormat->nSamplesPerSec *
727 wmw->lpWaveFormat->nBlockAlign);
728 wmw->lpWaveFormat->nAvgBytesPerSec =
729 wmw->lpWaveFormat->nSamplesPerSec *
730 wmw->lpWaveFormat->nBlockAlign;
732 break;
735 } else {
736 TRACE("can't retrieve wave format %ld\n", dwRet);
737 goto cleanUp;
741 /* go back to begining of chunk plus the requested position */
742 /* FIXME: I'm not sure this is correct, notably because some data linked to
743 * the decompression state machine will not be correcly initialized.
744 * try it this way (other way would be to decompress from 0 up to dwPosition
745 * and to start sending to hWave when dwPosition is reached)
747 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
749 /* By default the device will be opened for output, the MCI_CUE function is there to
750 * change from output to input and back
752 /* FIXME: how to choose between several output channels ? here mapper is forced */
753 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
754 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
756 if (dwRet != 0) {
757 TRACE("Can't open low level audio device %ld\n", dwRet);
758 dwRet = MCIERR_DEVICE_OPEN;
759 wmw->hWave = 0;
760 goto cleanUp;
763 /* make it so that 3 buffers per second are needed */
764 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
766 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
767 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
768 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
769 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
770 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
771 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
772 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
773 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
774 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
775 dwRet = MCIERR_INTERNAL;
776 goto cleanUp;
779 whidx = 0;
780 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
781 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
782 wmw->dwEventCount = 1L; /* for first buffer */
784 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
786 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
787 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
788 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
789 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
790 if (count < 1)
791 break;
792 /* count is always <= bufsize, so this is correct regarding the
793 * waveOutPrepareHeader function
795 waveHdr[whidx].dwBufferLength = count;
796 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
797 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
798 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
799 waveHdr[whidx].dwBytesRecorded);
800 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
801 left -= count;
802 wmw->dwPosition += count;
803 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
805 WAVE_mciPlayWaitDone(wmw);
806 whidx ^= 1;
809 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
811 /* just to get rid of some race conditions between play, stop and pause */
812 waveOutReset(wmw->hWave);
814 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
815 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
817 dwRet = 0;
819 cleanUp:
820 HeapFree(GetProcessHeap(), 0, waveHdr);
822 if (wmw->hWave) {
823 waveOutClose(wmw->hWave);
824 wmw->hWave = 0;
826 CloseHandle(wmw->hEvent);
828 if (lpParms && (dwFlags & MCI_NOTIFY)) {
829 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
830 wmw->openParms.wDeviceID,
831 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
834 wmw->dwStatus = MCI_MODE_STOP;
836 return dwRet;
839 /**************************************************************************
840 * WAVE_mciPlayCallback [internal]
842 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
843 DWORD dwInstance,
844 DWORD dwParam1, DWORD dwParam2)
846 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
847 LPWAVEHDR lpWaveHdr = NULL;
848 LONG count = 0;
849 switch (uMsg) {
850 case WIM_OPEN:
851 case WIM_CLOSE:
852 break;
853 case WIM_DATA:
854 lpWaveHdr = (LPWAVEHDR) dwParam1;
856 InterlockedIncrement(&wmw->dwEventCount);
858 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
860 lpWaveHdr->dwFlags &= ~WHDR_DONE;
861 wmw->dwPosition += count;
862 wmw->dwRemaining -= count;
864 if (wmw->dwStatus == MCI_MODE_RECORD)
866 /* Only queue up another buffer if we are recording. We could receive this
867 message also when waveInReset() is called, since it notifies on all wave
868 buffers that are outstanding. Queueing up more sometimes causes waveInClose
869 to fail. */
870 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
871 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
874 SetEvent(wmw->hEvent);
875 break;
876 default:
877 ERR("Unknown uMsg=%d\n", uMsg);
881 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
883 for (;;) {
884 ResetEvent(wmw->hEvent);
885 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
886 break;
888 InterlockedIncrement(&wmw->dwEventCount);
890 WaitForSingleObject(wmw->hEvent, INFINITE);
894 /**************************************************************************
895 * WAVE_mciRecord [internal]
897 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
899 DWORD end;
900 DWORD dwRet = 0;
901 LONG bufsize;
902 LPWAVEHDR waveHdr = NULL;
903 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
906 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
908 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
909 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
911 /* FIXME : since there is no way to determine in which mode the device is
912 * open (recording/playback) automatically switch from a mode to another
914 wmw->fInput = TRUE;
916 if (!wmw->fInput) {
917 WARN("cannot record on output device\n");
918 return MCIERR_NONAPPLICABLE_FUNCTION;
921 if (wmw->dwStatus == MCI_MODE_PAUSE) {
922 /* FIXME: parameters (start/end) in lpParams may not be used */
923 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
926 /** This function will be called again by a thread when async is used.
927 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
928 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
930 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
931 return MCIERR_INTERNAL;
934 wmw->dwStatus = MCI_MODE_RECORD;
936 if (!(dwFlags & MCI_WAIT)) {
937 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
938 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
941 if (!wmw->lpWaveFormat)
943 /* new RIFF file */
944 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
947 end = 0xFFFFFFFF;
948 if (lpParms && (dwFlags & MCI_FROM)) {
949 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
952 if (lpParms && (dwFlags & MCI_TO)) {
953 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
956 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
958 if (end <= wmw->dwPosition)
960 return TRUE;
963 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
964 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
966 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
967 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
969 /* go back to begining of chunk plus the requested position */
970 /* FIXME: I'm not sure this is correct, notably because some data linked to
971 * the decompression state machine will not be correcly initialized.
972 * try it this way (other way would be to decompress from 0 up to dwPosition
973 * and to start sending to hWave when dwPosition is reached)
975 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
977 /* By default the device will be opened for output, the MCI_CUE function is there to
978 * change from output to input and back
980 /* FIXME: how to choose between several output channels ? here mapper is forced */
981 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
982 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
984 if (dwRet != 0) {
985 TRACE("Can't open low level audio device %ld\n", dwRet);
986 dwRet = MCIERR_DEVICE_OPEN;
987 wmw->hWave = 0;
988 goto cleanUp;
991 /* make it so that 3 buffers per second are needed */
992 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
994 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
995 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
996 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
997 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
998 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
999 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1000 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1002 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1003 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1004 dwRet = MCIERR_INTERNAL;
1005 goto cleanUp;
1008 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1009 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1010 dwRet = MCIERR_INTERNAL;
1011 goto cleanUp;
1014 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1015 wmw->dwEventCount = 1L; /* for first buffer */
1017 wmw->dwRemaining = end - wmw->dwPosition;
1019 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1021 dwRet = waveInStart(wmw->hWave);
1023 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1024 WAVE_mciRecordWaitDone(wmw);
1027 waveInReset(wmw->hWave);
1028 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1029 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1031 dwRet = 0;
1033 cleanUp:
1034 HeapFree(GetProcessHeap(), 0, waveHdr);
1036 if (wmw->hWave) {
1037 waveInClose(wmw->hWave);
1038 wmw->hWave = 0;
1040 CloseHandle(wmw->hEvent);
1042 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1043 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1044 wmw->openParms.wDeviceID,
1045 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1048 wmw->dwStatus = MCI_MODE_STOP;
1050 return dwRet;
1054 /**************************************************************************
1055 * WAVE_mciPause [internal]
1057 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1059 DWORD dwRet;
1060 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1062 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1064 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1065 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1067 if (wmw->dwStatus == MCI_MODE_PLAY) {
1068 wmw->dwStatus = MCI_MODE_PAUSE;
1071 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1072 else dwRet = waveOutPause(wmw->hWave);
1074 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1077 /**************************************************************************
1078 * WAVE_mciResume [internal]
1080 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1082 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1083 DWORD dwRet = 0;
1085 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1087 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1089 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1090 wmw->dwStatus = MCI_MODE_PLAY;
1093 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1094 else dwRet = waveOutRestart(wmw->hWave);
1095 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1098 /**************************************************************************
1099 * WAVE_mciSeek [internal]
1101 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1103 DWORD ret = 0;
1104 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1106 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1108 if (lpParms == NULL) {
1109 ret = MCIERR_NULL_PARAMETER_BLOCK;
1110 } else if (wmw == NULL) {
1111 ret = MCIERR_INVALID_DEVICE_ID;
1112 } else {
1113 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1115 if (dwFlags & MCI_SEEK_TO_START) {
1116 wmw->dwPosition = 0;
1117 } else if (dwFlags & MCI_SEEK_TO_END) {
1118 wmw->dwPosition = wmw->ckWaveData.cksize;
1119 } else if (dwFlags & MCI_TO) {
1120 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1121 } else {
1122 WARN("dwFlag doesn't tell where to seek to...\n");
1123 return MCIERR_MISSING_PARAMETER;
1126 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1128 if (dwFlags & MCI_NOTIFY) {
1129 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1130 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1133 return ret;
1136 /**************************************************************************
1137 * WAVE_mciSet [internal]
1139 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1141 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1143 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1145 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1146 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1148 if (dwFlags & MCI_SET_TIME_FORMAT) {
1149 switch (lpParms->dwTimeFormat) {
1150 case MCI_FORMAT_MILLISECONDS:
1151 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1152 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1153 break;
1154 case MCI_FORMAT_BYTES:
1155 TRACE("MCI_FORMAT_BYTES !\n");
1156 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1157 break;
1158 case MCI_FORMAT_SAMPLES:
1159 TRACE("MCI_FORMAT_SAMPLES !\n");
1160 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1161 break;
1162 default:
1163 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1164 return MCIERR_BAD_TIME_FORMAT;
1167 if (dwFlags & MCI_SET_VIDEO) {
1168 TRACE("No support for video !\n");
1169 return MCIERR_UNSUPPORTED_FUNCTION;
1171 if (dwFlags & MCI_SET_DOOR_OPEN) {
1172 TRACE("No support for door open !\n");
1173 return MCIERR_UNSUPPORTED_FUNCTION;
1175 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1176 TRACE("No support for door close !\n");
1177 return MCIERR_UNSUPPORTED_FUNCTION;
1179 if (dwFlags & MCI_SET_AUDIO) {
1180 if (dwFlags & MCI_SET_ON) {
1181 TRACE("MCI_SET_ON audio !\n");
1182 } else if (dwFlags & MCI_SET_OFF) {
1183 TRACE("MCI_SET_OFF audio !\n");
1184 } else {
1185 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1186 return MCIERR_BAD_INTEGER;
1189 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1190 TRACE("MCI_SET_AUDIO_ALL !\n");
1191 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1192 TRACE("MCI_SET_AUDIO_LEFT !\n");
1193 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1194 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1196 if (dwFlags & MCI_WAVE_INPUT)
1197 TRACE("MCI_WAVE_INPUT !\n");
1198 if (dwFlags & MCI_WAVE_OUTPUT)
1199 TRACE("MCI_WAVE_OUTPUT !\n");
1200 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1201 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1202 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1203 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1204 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
1205 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
1206 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
1207 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
1208 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
1209 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
1210 if (dwFlags & MCI_WAVE_SET_CHANNELS)
1211 TRACE("MCI_WAVE_SET_CHANNELS !\n");
1212 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
1213 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
1214 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
1215 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
1216 return 0;
1219 /**************************************************************************
1220 * WAVE_mciSave [internal]
1222 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1224 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1225 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1226 WPARAM wparam = MCI_NOTIFY_FAILURE;
1228 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1229 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1230 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1232 if (dwFlags & MCI_WAIT)
1234 FIXME("MCI_WAIT not implemented\n");
1237 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1238 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1241 ret = mmioClose(wmw->hFile, 0);
1244 If the destination file already exists, it has to be overwritten. (Behaviour
1245 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1246 my applications. We are making use of mmioRename, which WILL NOT overwrite
1247 the destination file (which is what Windows does, also verified in Win2K)
1248 So, lets delete the destination file before calling mmioRename. If the
1249 destination file DOESN'T exist, the delete will fail silently. Let's also be
1250 careful not to lose our previous error code.
1252 tmpRet = GetLastError();
1253 DeleteFileA (lpParms->lpfilename);
1254 SetLastError(tmpRet);
1256 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1257 ret = ERROR_SUCCESS;
1260 if (dwFlags & MCI_NOTIFY) {
1261 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1263 mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback),
1264 wmw->openParms.wDeviceID, wparam);
1267 return ret;
1270 /**************************************************************************
1271 * WAVE_mciStatus [internal]
1273 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1275 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1276 DWORD ret = 0;
1278 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1279 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1280 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1282 if (dwFlags & MCI_STATUS_ITEM) {
1283 switch (lpParms->dwItem) {
1284 case MCI_STATUS_CURRENT_TRACK:
1285 lpParms->dwReturn = 1;
1286 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1287 break;
1288 case MCI_STATUS_LENGTH:
1289 if (!wmw->hFile) {
1290 lpParms->dwReturn = 0;
1291 return MCIERR_UNSUPPORTED_FUNCTION;
1293 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1294 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1295 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1296 break;
1297 case MCI_STATUS_MODE:
1298 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1299 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1300 ret = MCI_RESOURCE_RETURNED;
1301 break;
1302 case MCI_STATUS_MEDIA_PRESENT:
1303 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1304 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1305 ret = MCI_RESOURCE_RETURNED;
1306 break;
1307 case MCI_STATUS_NUMBER_OF_TRACKS:
1308 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1309 lpParms->dwReturn = 1;
1310 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1311 break;
1312 case MCI_STATUS_POSITION:
1313 if (!wmw->hFile) {
1314 lpParms->dwReturn = 0;
1315 return MCIERR_UNSUPPORTED_FUNCTION;
1317 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1318 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1319 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1320 &ret);
1321 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1322 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1323 break;
1324 case MCI_STATUS_READY:
1325 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1326 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1327 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1328 ret = MCI_RESOURCE_RETURNED;
1329 break;
1330 case MCI_STATUS_TIME_FORMAT:
1331 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1332 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1333 ret = MCI_RESOURCE_RETURNED;
1334 break;
1335 case MCI_WAVE_INPUT:
1336 TRACE("MCI_WAVE_INPUT !\n");
1337 lpParms->dwReturn = 0;
1338 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1339 break;
1340 case MCI_WAVE_OUTPUT:
1341 TRACE("MCI_WAVE_OUTPUT !\n");
1343 UINT id;
1344 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1345 lpParms->dwReturn = id;
1346 } else {
1347 lpParms->dwReturn = 0;
1348 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1351 break;
1352 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1353 if (!wmw->hFile) {
1354 lpParms->dwReturn = 0;
1355 return MCIERR_UNSUPPORTED_FUNCTION;
1357 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1358 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1359 break;
1360 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1361 if (!wmw->hFile) {
1362 lpParms->dwReturn = 0;
1363 return MCIERR_UNSUPPORTED_FUNCTION;
1365 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1366 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1367 break;
1368 case MCI_WAVE_STATUS_BLOCKALIGN:
1369 if (!wmw->hFile) {
1370 lpParms->dwReturn = 0;
1371 return MCIERR_UNSUPPORTED_FUNCTION;
1373 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1374 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1375 break;
1376 case MCI_WAVE_STATUS_CHANNELS:
1377 if (!wmw->hFile) {
1378 lpParms->dwReturn = 0;
1379 return MCIERR_UNSUPPORTED_FUNCTION;
1381 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1382 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1383 break;
1384 case MCI_WAVE_STATUS_FORMATTAG:
1385 if (!wmw->hFile) {
1386 lpParms->dwReturn = 0;
1387 return MCIERR_UNSUPPORTED_FUNCTION;
1389 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1390 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1391 break;
1392 case MCI_WAVE_STATUS_LEVEL:
1393 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1394 lpParms->dwReturn = 0xAAAA5555;
1395 break;
1396 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1397 if (!wmw->hFile) {
1398 lpParms->dwReturn = 0;
1399 return MCIERR_UNSUPPORTED_FUNCTION;
1401 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1402 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1403 break;
1404 default:
1405 WARN("unknown command %08lX !\n", lpParms->dwItem);
1406 return MCIERR_UNRECOGNIZED_COMMAND;
1409 if (dwFlags & MCI_NOTIFY) {
1410 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1411 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1413 return ret;
1416 /**************************************************************************
1417 * WAVE_mciGetDevCaps [internal]
1419 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1420 LPMCI_GETDEVCAPS_PARMS lpParms)
1422 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1423 DWORD ret = 0;
1425 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1427 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1428 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1430 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1431 switch(lpParms->dwItem) {
1432 case MCI_GETDEVCAPS_DEVICE_TYPE:
1433 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1434 ret = MCI_RESOURCE_RETURNED;
1435 break;
1436 case MCI_GETDEVCAPS_HAS_AUDIO:
1437 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1438 ret = MCI_RESOURCE_RETURNED;
1439 break;
1440 case MCI_GETDEVCAPS_HAS_VIDEO:
1441 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1442 ret = MCI_RESOURCE_RETURNED;
1443 break;
1444 case MCI_GETDEVCAPS_USES_FILES:
1445 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1446 ret = MCI_RESOURCE_RETURNED;
1447 break;
1448 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1449 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1450 ret = MCI_RESOURCE_RETURNED;
1451 break;
1452 case MCI_GETDEVCAPS_CAN_RECORD:
1453 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1454 ret = MCI_RESOURCE_RETURNED;
1455 break;
1456 case MCI_GETDEVCAPS_CAN_EJECT:
1457 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1458 ret = MCI_RESOURCE_RETURNED;
1459 break;
1460 case MCI_GETDEVCAPS_CAN_PLAY:
1461 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1462 ret = MCI_RESOURCE_RETURNED;
1463 break;
1464 case MCI_GETDEVCAPS_CAN_SAVE:
1465 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1466 ret = MCI_RESOURCE_RETURNED;
1467 break;
1468 case MCI_WAVE_GETDEVCAPS_INPUTS:
1469 lpParms->dwReturn = 1;
1470 break;
1471 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1472 lpParms->dwReturn = 1;
1473 break;
1474 default:
1475 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1476 return MCIERR_UNRECOGNIZED_COMMAND;
1478 } else {
1479 WARN("No GetDevCaps-Item !\n");
1480 return MCIERR_UNRECOGNIZED_COMMAND;
1482 return ret;
1485 /**************************************************************************
1486 * WAVE_mciInfo [internal]
1488 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1490 DWORD ret = 0;
1491 LPCSTR str = 0;
1492 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1494 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1496 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1497 ret = MCIERR_NULL_PARAMETER_BLOCK;
1498 } else if (wmw == NULL) {
1499 ret = MCIERR_INVALID_DEVICE_ID;
1500 } else {
1501 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1503 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1504 case MCI_INFO_PRODUCT:
1505 str = "Wine's audio player";
1506 break;
1507 case MCI_INFO_FILE:
1508 str = wmw->openParms.lpstrElementName;
1509 break;
1510 case MCI_WAVE_INPUT:
1511 str = "Wine Wave In";
1512 break;
1513 case MCI_WAVE_OUTPUT:
1514 str = "Wine Wave Out";
1515 break;
1516 default:
1517 WARN("Don't know this info command (%lu)\n", dwFlags);
1518 ret = MCIERR_UNRECOGNIZED_COMMAND;
1521 if (str) {
1522 if (strlen(str) + 1 > lpParms->dwRetSize) {
1523 ret = MCIERR_PARAM_OVERFLOW;
1524 } else {
1525 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1527 } else {
1528 lpParms->lpstrReturn[0] = 0;
1531 return ret;
1534 /**************************************************************************
1535 * MCIWAVE_DriverProc [sample driver]
1537 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1538 DWORD dwParam1, DWORD dwParam2)
1540 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1541 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1543 switch(wMsg) {
1544 case DRV_LOAD: return 1;
1545 case DRV_FREE: return 1;
1546 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1547 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1548 case DRV_ENABLE: return 1;
1549 case DRV_DISABLE: return 1;
1550 case DRV_QUERYCONFIGURE: return 1;
1551 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1552 case DRV_INSTALL: return DRVCNF_RESTART;
1553 case DRV_REMOVE: return DRVCNF_RESTART;
1554 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1555 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1556 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1557 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1558 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1559 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1560 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1561 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1562 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1563 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1564 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1565 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1566 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1567 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1568 /* commands that should be supported */
1569 case MCI_LOAD:
1570 case MCI_FREEZE:
1571 case MCI_PUT:
1572 case MCI_REALIZE:
1573 case MCI_UNFREEZE:
1574 case MCI_UPDATE:
1575 case MCI_WHERE:
1576 case MCI_STEP:
1577 case MCI_SPIN:
1578 case MCI_ESCAPE:
1579 case MCI_COPY:
1580 case MCI_CUT:
1581 case MCI_DELETE:
1582 case MCI_PASTE:
1583 FIXME("Unsupported yet command [%lu]\n", wMsg);
1584 break;
1585 case MCI_WINDOW:
1586 TRACE("Unsupported command [%lu]\n", wMsg);
1587 break;
1588 /* option which can be silenced */
1589 case MCI_CONFIGURE:
1590 return 0;
1591 case MCI_OPEN:
1592 case MCI_CLOSE:
1593 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1594 break;
1595 default:
1596 FIXME("is probably wrong msg [%lu]\n", wMsg);
1597 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1599 return MCIERR_UNRECOGNIZED_COMMAND;