Removed duplicate field in internal structure.
[wine/multimedia.git] / dlls / winmm / mciwave / mciwave.c
blobb4f90c094fe30662eec1d02551ff852b9182d2ff
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;
56 BOOL allocatedCopy;
59 /**************************************************************************
60 * MCI_SCAStarter [internal]
62 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
64 struct SCA* sca = (struct SCA*)arg;
65 DWORD ret;
67 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
68 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
69 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
70 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
71 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
72 if (sca->allocatedCopy)
73 HeapFree(GetProcessHeap(), 0, (LPVOID)sca->dwParam2);
74 HeapFree(GetProcessHeap(), 0, sca);
75 ExitThread(ret);
76 WARN("Should not happen ? what's wrong \n");
77 /* should not go after this point */
78 return ret;
81 /**************************************************************************
82 * MCI_SendCommandAsync [internal]
84 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
85 DWORD dwParam2, UINT size)
87 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA));
89 if (sca == 0)
90 return MCIERR_OUT_OF_MEMORY;
92 sca->wDevID = wDevID;
93 sca->wMsg = wMsg;
94 sca->dwParam1 = dwParam1;
96 if (size) {
97 sca->dwParam2 = (DWORD)HeapAlloc(GetProcessHeap(), 0, size);
98 if (sca->dwParam2 == 0) {
99 HeapFree(GetProcessHeap(), 0, sca);
100 return MCIERR_OUT_OF_MEMORY;
102 sca->allocatedCopy = TRUE;
103 /* copy structure passed by program in dwParam2 to be sure
104 * we can still use it whatever the program does
106 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
107 } else {
108 sca->dwParam2 = dwParam2;
109 sca->allocatedCopy = FALSE;
112 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
113 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
114 return MCI_SCAStarter(&sca);
116 return 0;
119 /*======================================================================*
120 * MCI WAVE implemantation *
121 *======================================================================*/
123 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
125 /**************************************************************************
126 * MCIWAVE_drvOpen [internal]
128 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
130 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
132 if (!wmw)
133 return 0;
135 wmw->wDevID = modp->wDeviceID;
136 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
137 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
138 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
139 return modp->wDeviceID;
142 /**************************************************************************
143 * MCIWAVE_drvClose [internal]
145 static DWORD WAVE_drvClose(DWORD dwDevID)
147 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
149 if (wmw) {
150 HeapFree(GetProcessHeap(), 0, wmw);
151 mciSetDriverData(dwDevID, 0);
152 return 1;
154 return 0;
157 /**************************************************************************
158 * WAVE_mciGetOpenDev [internal]
160 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
162 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
164 if (wmw == NULL || wmw->nUseCount == 0) {
165 WARN("Invalid wDevID=%u\n", wDevID);
166 return 0;
168 return wmw;
171 /**************************************************************************
172 * WAVE_ConvertByteToTimeFormat [internal]
174 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
176 DWORD ret = 0;
178 switch (wmw->dwMciTimeFormat) {
179 case MCI_FORMAT_MILLISECONDS:
180 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
181 break;
182 case MCI_FORMAT_BYTES:
183 ret = val;
184 break;
185 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
186 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
187 break;
188 default:
189 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
191 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
192 *lpRet = 0;
193 return ret;
196 /**************************************************************************
197 * WAVE_ConvertTimeFormatToByte [internal]
199 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
201 DWORD ret = 0;
203 switch (wmw->dwMciTimeFormat) {
204 case MCI_FORMAT_MILLISECONDS:
205 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
206 break;
207 case MCI_FORMAT_BYTES:
208 ret = val;
209 break;
210 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
211 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
212 break;
213 default:
214 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
216 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
217 return ret;
220 /**************************************************************************
221 * WAVE_mciReadFmt [internal]
223 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
225 MMCKINFO mmckInfo;
226 long r;
228 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
229 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
230 return MCIERR_INVALID_FILE;
231 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
232 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
234 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
235 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
236 if (r < sizeof(WAVEFORMAT))
237 return MCIERR_INVALID_FILE;
239 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
240 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
241 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
242 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
243 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
244 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
245 if (r >= (long)sizeof(WAVEFORMATEX))
246 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
248 mmioAscend(wmw->hFile, &mmckInfo, 0);
249 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
250 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
251 TRACE("can't find data chunk\n");
252 return MCIERR_INVALID_FILE;
254 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
255 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
256 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
257 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
259 return 0;
262 /**************************************************************************
263 * WAVE_mciCreateRIFFSkeleton [internal]
265 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
267 MMCKINFO ckWaveFormat;
269 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
270 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
271 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
274 HMMIO hmmio = wmw->hFile;
276 lpckRIFF->ckid = FOURCC_RIFF;
277 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
278 lpckRIFF->cksize = 0;
280 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
281 goto err;
283 ckWaveFormat.fccType = 0;
284 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
285 ckWaveFormat.cksize = 16;
287 if (!lpWaveFormat)
289 TRACE("allocating waveformat with default waveformat 11khz/8bit/mono \n");
291 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
293 lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
294 lpWaveFormat->nChannels = 1; /* MONO */
295 lpWaveFormat->nSamplesPerSec = 11025;
296 lpWaveFormat->nAvgBytesPerSec = 11025;
297 lpWaveFormat->nBlockAlign = 1;
298 lpWaveFormat->wBitsPerSample = 8;
299 lpWaveFormat->cbSize = 0; /* don't care */
302 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
303 goto err;
305 /* only the first 16 bytes are serialized */
306 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
307 goto err;
309 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
310 goto err;
312 lpckWaveData->cksize = 0;
313 lpckWaveData->fccType = 0;
314 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
316 /* create data chunk */
317 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
318 goto err;
320 return 0;
322 err:
323 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
324 return MCIERR_INVALID_FILE;
327 /**************************************************************************
328 * WAVE_mciOpen [internal]
330 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
332 DWORD dwRet = 0;
333 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
334 CHAR* pszTmpFileName = 0;
336 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
337 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
338 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
340 if (dwFlags & MCI_OPEN_SHAREABLE)
341 return MCIERR_HARDWARE;
343 if (wmw->nUseCount > 0) {
344 /* The driver is already opened on this channel
345 * Wave driver cannot be shared
347 return MCIERR_DEVICE_OPEN;
350 wmw->nUseCount++;
352 wmw->fInput = FALSE;
353 wmw->hWave = 0;
354 wmw->dwStatus = MCI_MODE_NOT_READY;
356 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
358 if (dwFlags & MCI_OPEN_ELEMENT) {
359 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
360 /* could it be that (DWORD)lpOpenParms->lpstrElementName
361 * contains the hFile value ?
363 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
364 } else {
365 if (strlen(lpOpenParms->lpstrElementName) > 0) {
366 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
368 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
369 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
371 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
372 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
373 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
375 if (wmw->hFile == 0) {
376 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
377 dwRet = MCIERR_FILE_NOT_FOUND;
379 else
381 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
383 /* make sure we're are the beginning of the file */
384 mmioSeek(wmw->hFile, 0, SEEK_SET);
386 /* first reading of this file. read the waveformat chunk */
387 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
388 dwRet = MCIERR_INVALID_FILE;
389 } else {
390 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
391 (LPSTR)&(lpckMainRIFF->ckid),
392 (LPSTR) &(lpckMainRIFF->fccType),
393 (lpckMainRIFF->cksize));
395 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
396 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
397 dwRet = MCIERR_INVALID_FILE;
398 } else {
399 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
404 else {
405 wmw->hFile = 0;
408 else {
409 CHAR szTmpPath[MAX_PATH];
410 CHAR szPrefix[4] = "TMP\0";
412 pszTmpFileName = HeapAlloc(GetProcessHeap(),
413 HEAP_ZERO_MEMORY,
414 MAX_PATH * sizeof(*pszTmpFileName));
416 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
417 WARN("can't retrieve temp path!\n");
418 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
419 return MCIERR_FILE_NOT_FOUND;
422 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
423 WARN("can't retrieve temp file name!\n");
424 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
425 return MCIERR_FILE_NOT_FOUND;
428 wmw->bTemporaryFile = TRUE;
430 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
432 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
434 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
435 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
437 if (wmw->hFile == 0) {
438 /* temporary file could not be created. clean filename. */
439 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
440 WARN("can't create file='%s' !\n", pszTmpFileName);
441 dwRet = MCIERR_FILE_NOT_FOUND;
448 TRACE("hFile=%u\n", wmw->hFile);
450 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
452 if (wmw->bTemporaryFile == TRUE)
454 /* Additional openParms is temporary file's name */
455 wmw->openParms.lpstrElementName = pszTmpFileName;
458 if (dwRet == 0) {
459 if (wmw->lpWaveFormat) {
460 switch (wmw->lpWaveFormat->wFormatTag) {
461 case WAVE_FORMAT_PCM:
462 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
463 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
464 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
465 wmw->lpWaveFormat->nAvgBytesPerSec,
466 wmw->lpWaveFormat->nSamplesPerSec *
467 wmw->lpWaveFormat->nBlockAlign);
468 wmw->lpWaveFormat->nAvgBytesPerSec =
469 wmw->lpWaveFormat->nSamplesPerSec *
470 wmw->lpWaveFormat->nBlockAlign;
472 break;
475 wmw->dwPosition = 0;
477 wmw->dwStatus = MCI_MODE_STOP;
478 } else {
479 wmw->nUseCount--;
480 if (wmw->hFile != 0)
481 mmioClose(wmw->hFile, 0);
482 wmw->hFile = 0;
484 return dwRet;
487 /**************************************************************************
488 * WAVE_mciCue [internal]
490 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
493 FIXME
495 This routine is far from complete. At the moment only a check is done on the
496 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
497 is the default.
499 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
500 are ignored
503 DWORD dwRet;
504 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
506 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
508 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
510 /* always close elements ? */
511 if (wmw->hFile != 0) {
512 mmioClose(wmw->hFile, 0);
513 wmw->hFile = 0;
516 dwRet = MMSYSERR_NOERROR; /* assume success */
518 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
519 dwRet = waveOutClose(wmw->hWave);
520 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
521 wmw->fInput = TRUE;
522 } else if (wmw->fInput) {
523 dwRet = waveInClose(wmw->hWave);
524 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
525 wmw->fInput = FALSE;
527 wmw->hWave = 0;
528 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
531 /**************************************************************************
532 * WAVE_mciStop [internal]
534 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
536 DWORD dwRet = 0;
537 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
539 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
541 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
543 /* wait for playback thread (if any) to exit before processing further */
544 switch (wmw->dwStatus) {
545 case MCI_MODE_PAUSE:
546 case MCI_MODE_PLAY:
547 case MCI_MODE_RECORD:
549 int oldStat = wmw->dwStatus;
550 wmw->dwStatus = MCI_MODE_NOT_READY;
551 if (oldStat == MCI_MODE_PAUSE)
552 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
554 while (wmw->dwStatus != MCI_MODE_STOP)
555 Sleep(10);
556 break;
559 wmw->dwPosition = 0;
561 /* sanity resets */
562 wmw->dwStatus = MCI_MODE_STOP;
564 if ((dwFlags & MCI_NOTIFY) && lpParms) {
565 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
566 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
569 return dwRet;
572 /**************************************************************************
573 * WAVE_mciClose [internal]
575 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
577 DWORD dwRet = 0;
578 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
580 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
582 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
584 if (wmw->dwStatus != MCI_MODE_STOP) {
585 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
588 wmw->nUseCount--;
590 if (wmw->nUseCount == 0) {
591 if (wmw->hFile != 0) {
592 mmioClose(wmw->hFile, 0);
593 wmw->hFile = 0;
597 /* That string got allocated in mciOpen because no filename was specified
598 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
599 * allocated by mciOpen, *NOT* the application.
601 if (wmw->bTemporaryFile)
603 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
604 wmw->openParms.lpstrElementName = NULL;
607 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
608 wmw->lpWaveFormat = NULL;
610 if ((dwFlags & MCI_NOTIFY) && lpParms) {
611 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
612 wmw->openParms.wDeviceID,
613 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
616 return 0;
619 /**************************************************************************
620 * WAVE_mciPlayCallback [internal]
622 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
623 DWORD dwInstance,
624 DWORD dwParam1, DWORD dwParam2)
626 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
628 switch (uMsg) {
629 case WOM_OPEN:
630 case WOM_CLOSE:
631 break;
632 case WOM_DONE:
633 InterlockedIncrement(&wmw->dwEventCount);
634 TRACE("Returning waveHdr=%lx\n", dwParam1);
635 SetEvent(wmw->hEvent);
636 break;
637 default:
638 ERR("Unknown uMsg=%d\n", uMsg);
642 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
644 for (;;) {
645 ResetEvent(wmw->hEvent);
646 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
647 break;
649 InterlockedIncrement(&wmw->dwEventCount);
651 WaitForSingleObject(wmw->hEvent, INFINITE);
655 /**************************************************************************
656 * WAVE_mciPlay [internal]
658 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
660 DWORD end;
661 LONG bufsize, count, left;
662 DWORD dwRet = 0;
663 LPWAVEHDR waveHdr = NULL;
664 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
665 int whidx;
667 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
669 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
670 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
672 /* FIXME : since there is no way to determine in which mode the device is
673 * open (recording/playback) automatically switch from a mode to another
675 wmw->fInput = FALSE;
677 if (wmw->fInput) {
678 WARN("cannot play on input device\n");
679 return MCIERR_NONAPPLICABLE_FUNCTION;
682 if (wmw->hFile == 0) {
683 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
684 return MCIERR_FILE_NOT_FOUND;
687 if (wmw->dwStatus == MCI_MODE_PAUSE) {
688 /* FIXME: parameters (start/end) in lpParams may not be used */
689 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
692 /** This function will be called again by a thread when async is used.
693 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
694 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
696 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
697 return MCIERR_INTERNAL;
700 wmw->dwStatus = MCI_MODE_PLAY;
702 if (!(dwFlags & MCI_WAIT)) {
703 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
704 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
707 end = 0xFFFFFFFF;
708 if (lpParms && (dwFlags & MCI_FROM)) {
709 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
711 if (lpParms && (dwFlags & MCI_TO)) {
712 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
715 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
717 if (end <= wmw->dwPosition)
718 return TRUE;
721 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
722 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
724 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
725 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
727 if (dwRet == 0) {
728 if (wmw->lpWaveFormat) {
729 switch (wmw->lpWaveFormat->wFormatTag) {
730 case WAVE_FORMAT_PCM:
731 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
732 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
733 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
734 wmw->lpWaveFormat->nAvgBytesPerSec,
735 wmw->lpWaveFormat->nSamplesPerSec *
736 wmw->lpWaveFormat->nBlockAlign);
737 wmw->lpWaveFormat->nAvgBytesPerSec =
738 wmw->lpWaveFormat->nSamplesPerSec *
739 wmw->lpWaveFormat->nBlockAlign;
741 break;
744 } else {
745 TRACE("can't retrieve wave format %ld\n", dwRet);
746 goto cleanUp;
750 /* go back to begining of chunk plus the requested position */
751 /* FIXME: I'm not sure this is correct, notably because some data linked to
752 * the decompression state machine will not be correcly initialized.
753 * try it this way (other way would be to decompress from 0 up to dwPosition
754 * and to start sending to hWave when dwPosition is reached)
756 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
758 /* By default the device will be opened for output, the MCI_CUE function is there to
759 * change from output to input and back
761 /* FIXME: how to choose between several output channels ? here mapper is forced */
762 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
763 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
765 if (dwRet != 0) {
766 TRACE("Can't open low level audio device %ld\n", dwRet);
767 dwRet = MCIERR_DEVICE_OPEN;
768 wmw->hWave = 0;
769 goto cleanUp;
772 /* make it so that 3 buffers per second are needed */
773 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
775 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
776 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
777 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
778 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
779 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
780 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
781 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
782 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
783 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
784 dwRet = MCIERR_INTERNAL;
785 goto cleanUp;
788 whidx = 0;
789 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
790 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
791 wmw->dwEventCount = 1L; /* for first buffer */
793 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
795 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
796 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
797 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
798 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
799 if (count < 1)
800 break;
801 /* count is always <= bufsize, so this is correct regarding the
802 * waveOutPrepareHeader function
804 waveHdr[whidx].dwBufferLength = count;
805 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
806 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
807 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
808 waveHdr[whidx].dwBytesRecorded);
809 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
810 left -= count;
811 wmw->dwPosition += count;
812 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
814 WAVE_mciPlayWaitDone(wmw);
815 whidx ^= 1;
818 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
820 /* just to get rid of some race conditions between play, stop and pause */
821 waveOutReset(wmw->hWave);
823 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
824 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
826 dwRet = 0;
828 cleanUp:
829 HeapFree(GetProcessHeap(), 0, waveHdr);
831 if (wmw->hWave) {
832 waveOutClose(wmw->hWave);
833 wmw->hWave = 0;
835 CloseHandle(wmw->hEvent);
837 if (lpParms && (dwFlags & MCI_NOTIFY)) {
838 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
839 wmw->openParms.wDeviceID,
840 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
843 wmw->dwStatus = MCI_MODE_STOP;
845 return dwRet;
848 /**************************************************************************
849 * WAVE_mciPlayCallback [internal]
851 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
852 DWORD dwInstance,
853 DWORD dwParam1, DWORD dwParam2)
855 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
856 LPWAVEHDR lpWaveHdr = NULL;
857 LONG count = 0;
858 switch (uMsg) {
859 case WIM_OPEN:
860 case WIM_CLOSE:
861 break;
862 case WIM_DATA:
863 lpWaveHdr = (LPWAVEHDR) dwParam1;
865 InterlockedIncrement(&wmw->dwEventCount);
867 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
869 lpWaveHdr->dwFlags &= ~WHDR_DONE;
870 wmw->dwPosition += count;
871 wmw->dwRemaining -= count;
873 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
874 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
876 SetEvent(wmw->hEvent);
877 break;
878 default:
879 ERR("Unknown uMsg=%d\n", uMsg);
883 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
885 for (;;) {
886 ResetEvent(wmw->hEvent);
887 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
888 break;
890 InterlockedIncrement(&wmw->dwEventCount);
892 WaitForSingleObject(wmw->hEvent, INFINITE);
896 /**************************************************************************
897 * WAVE_mciRecord [internal]
899 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
901 DWORD end;
902 DWORD dwRet = 0;
903 LONG bufsize;
904 LPWAVEHDR waveHdr = NULL;
905 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
908 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
910 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
911 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
913 /* FIXME : since there is no way to determine in which mode the device is
914 * open (recording/playback) automatically switch from a mode to another
916 wmw->fInput = TRUE;
918 if (!wmw->fInput) {
919 WARN("cannot record on output device\n");
920 return MCIERR_NONAPPLICABLE_FUNCTION;
923 if (wmw->dwStatus == MCI_MODE_PAUSE) {
924 /* FIXME: parameters (start/end) in lpParams may not be used */
925 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
928 /** This function will be called again by a thread when async is used.
929 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
930 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
932 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
933 return MCIERR_INTERNAL;
936 wmw->dwStatus = MCI_MODE_RECORD;
938 if (!(dwFlags & MCI_WAIT)) {
939 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
940 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
943 if (!wmw->lpWaveFormat)
945 /* new RIFF file */
946 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
949 end = 0xFFFFFFFF;
950 if (lpParms && (dwFlags & MCI_FROM)) {
951 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
954 if (lpParms && (dwFlags & MCI_TO)) {
955 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
958 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
960 if (end <= wmw->dwPosition)
962 return TRUE;
965 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
966 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
968 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
969 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
971 /* go back to begining of chunk plus the requested position */
972 /* FIXME: I'm not sure this is correct, notably because some data linked to
973 * the decompression state machine will not be correcly initialized.
974 * try it this way (other way would be to decompress from 0 up to dwPosition
975 * and to start sending to hWave when dwPosition is reached)
977 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
979 /* By default the device will be opened for output, the MCI_CUE function is there to
980 * change from output to input and back
982 /* FIXME: how to choose between several output channels ? here mapper is forced */
983 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
984 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
986 if (dwRet != 0) {
987 TRACE("Can't open low level audio device %ld\n", dwRet);
988 dwRet = MCIERR_DEVICE_OPEN;
989 wmw->hWave = 0;
990 goto cleanUp;
993 /* make it so that 3 buffers per second are needed */
994 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
996 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
997 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
998 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
999 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1000 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1001 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1002 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1004 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1005 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1006 dwRet = MCIERR_INTERNAL;
1007 goto cleanUp;
1010 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1011 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1012 dwRet = MCIERR_INTERNAL;
1013 goto cleanUp;
1016 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1017 wmw->dwEventCount = 1L; /* for first buffer */
1019 wmw->dwRemaining = end - wmw->dwPosition;
1021 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1023 dwRet = waveInStart(wmw->hWave);
1025 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1026 WAVE_mciRecordWaitDone(wmw);
1029 waveInReset(wmw->hWave);
1030 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1031 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1033 dwRet = 0;
1035 cleanUp:
1036 HeapFree(GetProcessHeap(), 0, waveHdr);
1038 if (wmw->hWave) {
1039 waveInClose(wmw->hWave);
1040 wmw->hWave = 0;
1042 CloseHandle(wmw->hEvent);
1044 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1045 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1046 wmw->openParms.wDeviceID,
1047 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1050 wmw->dwStatus = MCI_MODE_STOP;
1052 return dwRet;
1056 /**************************************************************************
1057 * WAVE_mciPause [internal]
1059 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1061 DWORD dwRet;
1062 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1064 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1066 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1067 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1069 if (wmw->dwStatus == MCI_MODE_PLAY) {
1070 wmw->dwStatus = MCI_MODE_PAUSE;
1073 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1074 else dwRet = waveOutPause(wmw->hWave);
1076 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1079 /**************************************************************************
1080 * WAVE_mciResume [internal]
1082 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1084 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1085 DWORD dwRet = 0;
1087 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1089 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1091 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1092 wmw->dwStatus = MCI_MODE_PLAY;
1095 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1096 else dwRet = waveOutRestart(wmw->hWave);
1097 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1100 /**************************************************************************
1101 * WAVE_mciSeek [internal]
1103 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1105 DWORD ret = 0;
1106 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1108 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1110 if (lpParms == NULL) {
1111 ret = MCIERR_NULL_PARAMETER_BLOCK;
1112 } else if (wmw == NULL) {
1113 ret = MCIERR_INVALID_DEVICE_ID;
1114 } else {
1115 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1117 if (dwFlags & MCI_SEEK_TO_START) {
1118 wmw->dwPosition = 0;
1119 } else if (dwFlags & MCI_SEEK_TO_END) {
1120 wmw->dwPosition = wmw->ckWaveData.cksize;
1121 } else if (dwFlags & MCI_TO) {
1122 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1123 } else {
1124 WARN("dwFlag doesn't tell where to seek to...\n");
1125 return MCIERR_MISSING_PARAMETER;
1128 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1130 if (dwFlags & MCI_NOTIFY) {
1131 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1132 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1135 return ret;
1138 /**************************************************************************
1139 * WAVE_mciSet [internal]
1141 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1143 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1145 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1147 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1148 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1150 if (dwFlags & MCI_SET_TIME_FORMAT) {
1151 switch (lpParms->dwTimeFormat) {
1152 case MCI_FORMAT_MILLISECONDS:
1153 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1154 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1155 break;
1156 case MCI_FORMAT_BYTES:
1157 TRACE("MCI_FORMAT_BYTES !\n");
1158 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1159 break;
1160 case MCI_FORMAT_SAMPLES:
1161 TRACE("MCI_FORMAT_SAMPLES !\n");
1162 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1163 break;
1164 default:
1165 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1166 return MCIERR_BAD_TIME_FORMAT;
1169 if (dwFlags & MCI_SET_VIDEO) {
1170 TRACE("No support for video !\n");
1171 return MCIERR_UNSUPPORTED_FUNCTION;
1173 if (dwFlags & MCI_SET_DOOR_OPEN) {
1174 TRACE("No support for door open !\n");
1175 return MCIERR_UNSUPPORTED_FUNCTION;
1177 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1178 TRACE("No support for door close !\n");
1179 return MCIERR_UNSUPPORTED_FUNCTION;
1181 if (dwFlags & MCI_SET_AUDIO) {
1182 if (dwFlags & MCI_SET_ON) {
1183 TRACE("MCI_SET_ON audio !\n");
1184 } else if (dwFlags & MCI_SET_OFF) {
1185 TRACE("MCI_SET_OFF audio !\n");
1186 } else {
1187 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1188 return MCIERR_BAD_INTEGER;
1191 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1192 TRACE("MCI_SET_AUDIO_ALL !\n");
1193 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1194 TRACE("MCI_SET_AUDIO_LEFT !\n");
1195 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1196 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1198 if (dwFlags & MCI_WAVE_INPUT)
1199 TRACE("MCI_WAVE_INPUT !\n");
1200 if (dwFlags & MCI_WAVE_OUTPUT)
1201 TRACE("MCI_WAVE_OUTPUT !\n");
1202 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1203 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1204 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1205 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1206 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
1207 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
1208 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
1209 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
1210 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
1211 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
1212 if (dwFlags & MCI_WAVE_SET_CHANNELS)
1213 TRACE("MCI_WAVE_SET_CHANNELS !\n");
1214 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
1215 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
1216 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
1217 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
1218 return 0;
1221 /**************************************************************************
1222 * WAVE_mciSave [internal]
1224 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1226 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1227 DWORD ret = MCIERR_FILE_NOT_SAVED;
1228 WPARAM wparam = MCI_NOTIFY_FAILURE;
1230 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1231 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1232 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1234 if (dwFlags & MCI_WAIT)
1236 FIXME("MCI_WAIT not implemented\n");
1239 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1240 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1243 ret = mmioClose(wmw->hFile, 0);
1245 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1246 ret = ERROR_SUCCESS;
1249 if (dwFlags & MCI_NOTIFY) {
1250 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1252 mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback),
1253 wmw->openParms.wDeviceID, wparam);
1256 return ret;
1259 /**************************************************************************
1260 * WAVE_mciStatus [internal]
1262 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1264 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1265 DWORD ret = 0;
1267 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1268 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1269 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1271 if (dwFlags & MCI_STATUS_ITEM) {
1272 switch (lpParms->dwItem) {
1273 case MCI_STATUS_CURRENT_TRACK:
1274 lpParms->dwReturn = 1;
1275 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1276 break;
1277 case MCI_STATUS_LENGTH:
1278 if (!wmw->hFile) {
1279 lpParms->dwReturn = 0;
1280 return MCIERR_UNSUPPORTED_FUNCTION;
1282 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1283 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1284 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1285 break;
1286 case MCI_STATUS_MODE:
1287 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1288 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1289 ret = MCI_RESOURCE_RETURNED;
1290 break;
1291 case MCI_STATUS_MEDIA_PRESENT:
1292 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1293 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1294 ret = MCI_RESOURCE_RETURNED;
1295 break;
1296 case MCI_STATUS_NUMBER_OF_TRACKS:
1297 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1298 lpParms->dwReturn = 1;
1299 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1300 break;
1301 case MCI_STATUS_POSITION:
1302 if (!wmw->hFile) {
1303 lpParms->dwReturn = 0;
1304 return MCIERR_UNSUPPORTED_FUNCTION;
1306 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1307 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1308 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1309 &ret);
1310 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1311 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1312 break;
1313 case MCI_STATUS_READY:
1314 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1315 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1316 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1317 ret = MCI_RESOURCE_RETURNED;
1318 break;
1319 case MCI_STATUS_TIME_FORMAT:
1320 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1321 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1322 ret = MCI_RESOURCE_RETURNED;
1323 break;
1324 case MCI_WAVE_INPUT:
1325 TRACE("MCI_WAVE_INPUT !\n");
1326 lpParms->dwReturn = 0;
1327 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1328 break;
1329 case MCI_WAVE_OUTPUT:
1330 TRACE("MCI_WAVE_OUTPUT !\n");
1332 UINT id;
1333 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1334 lpParms->dwReturn = id;
1335 } else {
1336 lpParms->dwReturn = 0;
1337 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1340 break;
1341 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1342 if (!wmw->hFile) {
1343 lpParms->dwReturn = 0;
1344 return MCIERR_UNSUPPORTED_FUNCTION;
1346 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1347 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1348 break;
1349 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1350 if (!wmw->hFile) {
1351 lpParms->dwReturn = 0;
1352 return MCIERR_UNSUPPORTED_FUNCTION;
1354 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1355 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1356 break;
1357 case MCI_WAVE_STATUS_BLOCKALIGN:
1358 if (!wmw->hFile) {
1359 lpParms->dwReturn = 0;
1360 return MCIERR_UNSUPPORTED_FUNCTION;
1362 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1363 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1364 break;
1365 case MCI_WAVE_STATUS_CHANNELS:
1366 if (!wmw->hFile) {
1367 lpParms->dwReturn = 0;
1368 return MCIERR_UNSUPPORTED_FUNCTION;
1370 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1371 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1372 break;
1373 case MCI_WAVE_STATUS_FORMATTAG:
1374 if (!wmw->hFile) {
1375 lpParms->dwReturn = 0;
1376 return MCIERR_UNSUPPORTED_FUNCTION;
1378 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1379 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1380 break;
1381 case MCI_WAVE_STATUS_LEVEL:
1382 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1383 lpParms->dwReturn = 0xAAAA5555;
1384 break;
1385 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1386 if (!wmw->hFile) {
1387 lpParms->dwReturn = 0;
1388 return MCIERR_UNSUPPORTED_FUNCTION;
1390 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1391 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1392 break;
1393 default:
1394 WARN("unknown command %08lX !\n", lpParms->dwItem);
1395 return MCIERR_UNRECOGNIZED_COMMAND;
1398 if (dwFlags & MCI_NOTIFY) {
1399 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1400 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1402 return ret;
1405 /**************************************************************************
1406 * WAVE_mciGetDevCaps [internal]
1408 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1409 LPMCI_GETDEVCAPS_PARMS lpParms)
1411 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1412 DWORD ret = 0;
1414 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1416 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1417 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1419 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1420 switch(lpParms->dwItem) {
1421 case MCI_GETDEVCAPS_DEVICE_TYPE:
1422 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1423 ret = MCI_RESOURCE_RETURNED;
1424 break;
1425 case MCI_GETDEVCAPS_HAS_AUDIO:
1426 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1427 ret = MCI_RESOURCE_RETURNED;
1428 break;
1429 case MCI_GETDEVCAPS_HAS_VIDEO:
1430 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1431 ret = MCI_RESOURCE_RETURNED;
1432 break;
1433 case MCI_GETDEVCAPS_USES_FILES:
1434 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1435 ret = MCI_RESOURCE_RETURNED;
1436 break;
1437 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1438 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1439 ret = MCI_RESOURCE_RETURNED;
1440 break;
1441 case MCI_GETDEVCAPS_CAN_RECORD:
1442 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1443 ret = MCI_RESOURCE_RETURNED;
1444 break;
1445 case MCI_GETDEVCAPS_CAN_EJECT:
1446 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1447 ret = MCI_RESOURCE_RETURNED;
1448 break;
1449 case MCI_GETDEVCAPS_CAN_PLAY:
1450 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1451 ret = MCI_RESOURCE_RETURNED;
1452 break;
1453 case MCI_GETDEVCAPS_CAN_SAVE:
1454 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1455 ret = MCI_RESOURCE_RETURNED;
1456 break;
1457 case MCI_WAVE_GETDEVCAPS_INPUTS:
1458 lpParms->dwReturn = 1;
1459 break;
1460 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1461 lpParms->dwReturn = 1;
1462 break;
1463 default:
1464 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1465 return MCIERR_UNRECOGNIZED_COMMAND;
1467 } else {
1468 WARN("No GetDevCaps-Item !\n");
1469 return MCIERR_UNRECOGNIZED_COMMAND;
1471 return ret;
1474 /**************************************************************************
1475 * WAVE_mciInfo [internal]
1477 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1479 DWORD ret = 0;
1480 LPCSTR str = 0;
1481 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1483 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1485 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1486 ret = MCIERR_NULL_PARAMETER_BLOCK;
1487 } else if (wmw == NULL) {
1488 ret = MCIERR_INVALID_DEVICE_ID;
1489 } else {
1490 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1492 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1493 case MCI_INFO_PRODUCT:
1494 str = "Wine's audio player";
1495 break;
1496 case MCI_INFO_FILE:
1497 str = wmw->openParms.lpstrElementName;
1498 break;
1499 case MCI_WAVE_INPUT:
1500 str = "Wine Wave In";
1501 break;
1502 case MCI_WAVE_OUTPUT:
1503 str = "Wine Wave Out";
1504 break;
1505 default:
1506 WARN("Don't know this info command (%lu)\n", dwFlags);
1507 ret = MCIERR_UNRECOGNIZED_COMMAND;
1510 if (str) {
1511 if (strlen(str) + 1 > lpParms->dwRetSize) {
1512 ret = MCIERR_PARAM_OVERFLOW;
1513 } else {
1514 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1516 } else {
1517 lpParms->lpstrReturn[0] = 0;
1520 return ret;
1523 /**************************************************************************
1524 * MCIWAVE_DriverProc [sample driver]
1526 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1527 DWORD dwParam1, DWORD dwParam2)
1529 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1530 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1532 switch(wMsg) {
1533 case DRV_LOAD: return 1;
1534 case DRV_FREE: return 1;
1535 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1536 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1537 case DRV_ENABLE: return 1;
1538 case DRV_DISABLE: return 1;
1539 case DRV_QUERYCONFIGURE: return 1;
1540 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1541 case DRV_INSTALL: return DRVCNF_RESTART;
1542 case DRV_REMOVE: return DRVCNF_RESTART;
1543 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1544 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1545 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1546 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1547 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1548 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1549 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1550 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1551 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1552 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1553 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1554 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1555 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1556 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1557 /* commands that should be supported */
1558 case MCI_LOAD:
1559 case MCI_FREEZE:
1560 case MCI_PUT:
1561 case MCI_REALIZE:
1562 case MCI_UNFREEZE:
1563 case MCI_UPDATE:
1564 case MCI_WHERE:
1565 case MCI_STEP:
1566 case MCI_SPIN:
1567 case MCI_ESCAPE:
1568 case MCI_COPY:
1569 case MCI_CUT:
1570 case MCI_DELETE:
1571 case MCI_PASTE:
1572 FIXME("Unsupported yet command [%lu]\n", wMsg);
1573 break;
1574 case MCI_WINDOW:
1575 TRACE("Unsupported command [%lu]\n", wMsg);
1576 break;
1577 /* option which can be silenced */
1578 case MCI_CONFIGURE:
1579 return 0;
1580 case MCI_OPEN:
1581 case MCI_CLOSE:
1582 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1583 break;
1584 default:
1585 FIXME("is probably wrong msg [%lu]\n", wMsg);
1586 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1588 return MCIERR_UNRECOGNIZED_COMMAND;