1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
6 * 1999,2000 Eric Pouech
7 * 2000 Francois Jacques
17 #include "debugtools.h"
19 DEFAULT_DEBUG_CHANNEL(mciwave);
24 int nUseCount; /* Incremented for each shared open */
25 BOOL fShareable; /* TRUE if first open was shareable */
26 HMMIO hFile; /* mmio file handle open as Element */
27 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 */
42 /* ===================================================================
43 * ===================================================================
44 * FIXME: should be using the new mmThreadXXXX functions from WINMM
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 * =================================================================== */
58 /**************************************************************************
59 * MCI_SCAStarter [internal]
61 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
63 struct SCA* sca = (struct SCA*)arg;
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);
73 WARN("Should not happen ? what's wrong \n");
74 /* should not go after this point */
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);
87 return MCIERR_OUT_OF_MEMORY;
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);
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);
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));
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;
131 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
132 wmw->wfxRef.nChannels = 1; /* MONO */
133 wmw->wfxRef.nSamplesPerSec = 11025;
134 wmw->wfxRef.nAvgBytesPerSec = 11025;
135 wmw->wfxRef.nBlockAlign = 1;
136 wmw->wfxRef.wBitsPerSample = 8;
137 wmw->wfxRef.cbSize = 0; /* don't care */
139 return modp->wDeviceID;
142 /**************************************************************************
143 * MCIWAVE_drvClose [internal]
145 static DWORD WAVE_drvClose(DWORD dwDevID)
147 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
150 HeapFree(GetProcessHeap(), 0, wmw);
151 mciSetDriverData(dwDevID, 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);
171 /**************************************************************************
172 * WAVE_ConvertByteToTimeFormat [internal]
174 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
178 switch (wmw->dwMciTimeFormat) {
179 case MCI_FORMAT_MILLISECONDS:
180 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
182 case MCI_FORMAT_BYTES:
185 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
186 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
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);
196 /**************************************************************************
197 * WAVE_ConvertTimeFormatToByte [internal]
199 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
203 switch (wmw->dwMciTimeFormat) {
204 case MCI_FORMAT_MILLISECONDS:
205 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
207 case MCI_FORMAT_BYTES:
210 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
211 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
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);
220 /**************************************************************************
221 * WAVE_mciReadFmt [internal]
223 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
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);
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))
283 ckWaveFormat.fccType = 0;
284 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
285 ckWaveFormat.cksize = 16;
289 /* FIXME: for non PCM formats, the size of the waveFormat has to be
292 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
294 memcpy(lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
297 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
300 /* only the first 16 bytes are serialized */
301 /* wrong... for non PCM, the whole waveFormat is stored
303 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
306 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
309 lpckWaveData->cksize = 0;
310 lpckWaveData->fccType = 0;
311 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
313 /* create data chunk */
314 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
320 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
321 return MCIERR_INVALID_FILE;
324 /**************************************************************************
325 * WAVE_mciOpen [internal]
327 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
330 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
331 CHAR* pszTmpFileName = 0;
333 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
334 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
335 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
337 if (dwFlags & MCI_OPEN_SHAREABLE)
338 return MCIERR_HARDWARE;
340 if (wmw->nUseCount > 0) {
341 /* The driver is already opened on this channel
342 * Wave driver cannot be shared
344 return MCIERR_DEVICE_OPEN;
351 wmw->dwStatus = MCI_MODE_NOT_READY;
353 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
355 if (dwFlags & MCI_OPEN_ELEMENT) {
356 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
357 /* could it be that (DWORD)lpOpenParms->lpstrElementName
358 * contains the hFile value ?
360 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
362 if (strlen(lpOpenParms->lpstrElementName) > 0) {
363 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
365 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
366 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
368 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
369 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
370 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
372 if (wmw->hFile == 0) {
373 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
374 dwRet = MCIERR_FILE_NOT_FOUND;
378 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
380 /* make sure we're are the beginning of the file */
381 mmioSeek(wmw->hFile, 0, SEEK_SET);
383 /* first reading of this file. read the waveformat chunk */
384 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
385 dwRet = MCIERR_INVALID_FILE;
387 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
388 (LPSTR)&(lpckMainRIFF->ckid),
389 (LPSTR) &(lpckMainRIFF->fccType),
390 (lpckMainRIFF->cksize));
392 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
393 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
394 dwRet = MCIERR_INVALID_FILE;
396 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
406 CHAR szTmpPath[MAX_PATH];
407 CHAR szPrefix[4] = "TMP\0";
409 pszTmpFileName = HeapAlloc(GetProcessHeap(),
411 MAX_PATH * sizeof(*pszTmpFileName));
413 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
414 WARN("can't retrieve temp path!\n");
415 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
416 return MCIERR_FILE_NOT_FOUND;
419 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
420 WARN("can't retrieve temp file name!\n");
421 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
422 return MCIERR_FILE_NOT_FOUND;
425 wmw->bTemporaryFile = TRUE;
427 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
429 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
431 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
432 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
434 if (wmw->hFile == 0) {
435 /* temporary file could not be created. clean filename. */
436 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
437 WARN("can't create file='%s' !\n", pszTmpFileName);
438 dwRet = MCIERR_FILE_NOT_FOUND;
445 TRACE("hFile=%u\n", wmw->hFile);
447 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
449 if (wmw->bTemporaryFile == TRUE)
451 /* Additional openParms is temporary file's name */
452 wmw->openParms.lpstrElementName = pszTmpFileName;
456 if (wmw->lpWaveFormat) {
457 switch (wmw->lpWaveFormat->wFormatTag) {
458 case WAVE_FORMAT_PCM:
459 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
460 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
461 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
462 wmw->lpWaveFormat->nAvgBytesPerSec,
463 wmw->lpWaveFormat->nSamplesPerSec *
464 wmw->lpWaveFormat->nBlockAlign);
465 wmw->lpWaveFormat->nAvgBytesPerSec =
466 wmw->lpWaveFormat->nSamplesPerSec *
467 wmw->lpWaveFormat->nBlockAlign;
474 wmw->dwStatus = MCI_MODE_STOP;
478 mmioClose(wmw->hFile, 0);
484 /**************************************************************************
485 * WAVE_mciCue [internal]
487 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
492 This routine is far from complete. At the moment only a check is done on the
493 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
496 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
501 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
503 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
505 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
507 /* always close elements ? */
508 if (wmw->hFile != 0) {
509 mmioClose(wmw->hFile, 0);
513 dwRet = MMSYSERR_NOERROR; /* assume success */
515 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
516 dwRet = waveOutClose(wmw->hWave);
517 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
519 } else if (wmw->fInput) {
520 dwRet = waveInClose(wmw->hWave);
521 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
525 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
528 /**************************************************************************
529 * WAVE_mciStop [internal]
531 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
534 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
536 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
538 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
540 /* wait for playback thread (if any) to exit before processing further */
541 switch (wmw->dwStatus) {
544 case MCI_MODE_RECORD:
546 int oldStat = wmw->dwStatus;
547 wmw->dwStatus = MCI_MODE_NOT_READY;
548 if (oldStat == MCI_MODE_PAUSE)
549 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
551 while (wmw->dwStatus != MCI_MODE_STOP)
559 wmw->dwStatus = MCI_MODE_STOP;
561 if ((dwFlags & MCI_NOTIFY) && lpParms) {
562 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
563 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
569 /**************************************************************************
570 * WAVE_mciClose [internal]
572 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
575 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
577 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
579 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
581 if (wmw->dwStatus != MCI_MODE_STOP) {
582 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
587 if (wmw->nUseCount == 0) {
588 if (wmw->hFile != 0) {
589 mmioClose(wmw->hFile, 0);
594 /* That string got allocated in mciOpen because no filename was specified
595 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
596 * allocated by mciOpen, *NOT* the application.
598 if (wmw->bTemporaryFile)
600 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
601 wmw->openParms.lpstrElementName = NULL;
604 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
605 wmw->lpWaveFormat = NULL;
607 if ((dwFlags & MCI_NOTIFY) && lpParms) {
608 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
609 wmw->openParms.wDeviceID,
610 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
616 /**************************************************************************
617 * WAVE_mciPlayCallback [internal]
619 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
621 DWORD dwParam1, DWORD dwParam2)
623 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
630 InterlockedIncrement(&wmw->dwEventCount);
631 TRACE("Returning waveHdr=%lx\n", dwParam1);
632 SetEvent(wmw->hEvent);
635 ERR("Unknown uMsg=%d\n", uMsg);
639 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
642 ResetEvent(wmw->hEvent);
643 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
646 InterlockedIncrement(&wmw->dwEventCount);
648 WaitForSingleObject(wmw->hEvent, INFINITE);
652 /**************************************************************************
653 * WAVE_mciPlay [internal]
655 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
658 LONG bufsize, count, left;
660 LPWAVEHDR waveHdr = NULL;
661 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
664 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
666 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
667 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
669 /* FIXME : since there is no way to determine in which mode the device is
670 * open (recording/playback) automatically switch from a mode to another
675 WARN("cannot play on input device\n");
676 return MCIERR_NONAPPLICABLE_FUNCTION;
679 if (wmw->hFile == 0) {
680 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
681 return MCIERR_FILE_NOT_FOUND;
684 if (wmw->dwStatus == MCI_MODE_PAUSE) {
685 /* FIXME: parameters (start/end) in lpParams may not be used */
686 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
689 /** This function will be called again by a thread when async is used.
690 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
691 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
693 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
694 return MCIERR_INTERNAL;
697 wmw->dwStatus = MCI_MODE_PLAY;
699 if (!(dwFlags & MCI_WAIT)) {
700 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
701 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
705 if (lpParms && (dwFlags & MCI_FROM)) {
706 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
708 if (lpParms && (dwFlags & MCI_TO)) {
709 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
712 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
714 if (end <= wmw->dwPosition)
718 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
719 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
721 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
722 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
725 if (wmw->lpWaveFormat) {
726 switch (wmw->lpWaveFormat->wFormatTag) {
727 case WAVE_FORMAT_PCM:
728 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
729 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
730 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
731 wmw->lpWaveFormat->nAvgBytesPerSec,
732 wmw->lpWaveFormat->nSamplesPerSec *
733 wmw->lpWaveFormat->nBlockAlign);
734 wmw->lpWaveFormat->nAvgBytesPerSec =
735 wmw->lpWaveFormat->nSamplesPerSec *
736 wmw->lpWaveFormat->nBlockAlign;
742 TRACE("can't retrieve wave format %ld\n", dwRet);
747 /* go back to begining of chunk plus the requested position */
748 /* FIXME: I'm not sure this is correct, notably because some data linked to
749 * the decompression state machine will not be correcly initialized.
750 * try it this way (other way would be to decompress from 0 up to dwPosition
751 * and to start sending to hWave when dwPosition is reached)
753 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
755 /* By default the device will be opened for output, the MCI_CUE function is there to
756 * change from output to input and back
758 /* FIXME: how to choose between several output channels ? here mapper is forced */
759 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
760 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
763 TRACE("Can't open low level audio device %ld\n", dwRet);
764 dwRet = MCIERR_DEVICE_OPEN;
769 /* make it so that 3 buffers per second are needed */
770 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
772 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
773 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
774 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
775 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
776 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
777 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
778 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
779 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
780 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
781 dwRet = MCIERR_INTERNAL;
786 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
787 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
788 wmw->dwEventCount = 1L; /* for first buffer */
790 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
792 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
793 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
794 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
795 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
798 /* count is always <= bufsize, so this is correct regarding the
799 * waveOutPrepareHeader function
801 waveHdr[whidx].dwBufferLength = count;
802 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
803 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
804 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
805 waveHdr[whidx].dwBytesRecorded);
806 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
808 wmw->dwPosition += count;
809 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
811 WAVE_mciPlayWaitDone(wmw);
815 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
817 /* just to get rid of some race conditions between play, stop and pause */
818 waveOutReset(wmw->hWave);
820 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
821 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
826 HeapFree(GetProcessHeap(), 0, waveHdr);
829 waveOutClose(wmw->hWave);
832 CloseHandle(wmw->hEvent);
834 if (lpParms && (dwFlags & MCI_NOTIFY)) {
835 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
836 wmw->openParms.wDeviceID,
837 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
840 wmw->dwStatus = MCI_MODE_STOP;
845 /**************************************************************************
846 * WAVE_mciPlayCallback [internal]
848 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
850 DWORD dwParam1, DWORD dwParam2)
852 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
853 LPWAVEHDR lpWaveHdr = NULL;
860 lpWaveHdr = (LPWAVEHDR) dwParam1;
862 InterlockedIncrement(&wmw->dwEventCount);
864 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
866 lpWaveHdr->dwFlags &= ~WHDR_DONE;
867 wmw->dwPosition += count;
868 wmw->dwRemaining -= count;
870 if (wmw->dwStatus == MCI_MODE_RECORD)
872 /* Only queue up another buffer if we are recording. We could receive this
873 message also when waveInReset() is called, since it notifies on all wave
874 buffers that are outstanding. Queueing up more sometimes causes waveInClose
876 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
877 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
880 SetEvent(wmw->hEvent);
883 ERR("Unknown uMsg=%d\n", uMsg);
887 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
890 ResetEvent(wmw->hEvent);
891 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
894 InterlockedIncrement(&wmw->dwEventCount);
896 WaitForSingleObject(wmw->hEvent, INFINITE);
900 /**************************************************************************
901 * WAVE_mciRecord [internal]
903 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
908 LPWAVEHDR waveHdr = NULL;
909 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
912 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
914 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
915 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
917 /* FIXME : since there is no way to determine in which mode the device is
918 * open (recording/playback) automatically switch from a mode to another
923 WARN("cannot record on output device\n");
924 return MCIERR_NONAPPLICABLE_FUNCTION;
927 if (wmw->dwStatus == MCI_MODE_PAUSE) {
928 /* FIXME: parameters (start/end) in lpParams may not be used */
929 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
932 /** This function will be called again by a thread when async is used.
933 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
934 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
936 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
937 return MCIERR_INTERNAL;
940 wmw->dwStatus = MCI_MODE_RECORD;
942 if (!(dwFlags & MCI_WAIT)) {
943 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
944 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
947 if (!wmw->lpWaveFormat)
950 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
953 FIXME("Should descend into data chunk. Please report.\n");
957 if (lpParms && (dwFlags & MCI_FROM)) {
958 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
961 if (lpParms && (dwFlags & MCI_TO)) {
962 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
965 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
967 if (end <= wmw->dwPosition)
972 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
973 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
975 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
976 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
978 /* go back to begining of chunk plus the requested position */
979 /* FIXME: I'm not sure this is correct, notably because some data linked to
980 * the decompression state machine will not be correcly initialized.
981 * try it this way (other way would be to decompress from 0 up to dwPosition
982 * and to start sending to hWave when dwPosition is reached)
984 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
986 /* By default the device will be opened for output, the MCI_CUE function is there to
987 * change from output to input and back
989 /* FIXME: how to choose between several output channels ? here mapper is forced */
990 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
991 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
994 TRACE("Can't open low level audio device %ld\n", dwRet);
995 dwRet = MCIERR_DEVICE_OPEN;
1000 /* make it so that 3 buffers per second are needed */
1001 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1003 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1004 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1005 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1006 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1007 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1008 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1009 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1011 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1012 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1013 dwRet = MCIERR_INTERNAL;
1017 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1018 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1019 dwRet = MCIERR_INTERNAL;
1023 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1024 wmw->dwEventCount = 1L; /* for first buffer */
1026 wmw->dwRemaining = end - wmw->dwPosition;
1028 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1030 dwRet = waveInStart(wmw->hWave);
1032 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1033 WAVE_mciRecordWaitDone(wmw);
1036 /* needed so that the callback above won't add again the buffers returned by the reset */
1037 wmw->dwStatus = MCI_MODE_STOP;
1039 waveInReset(wmw->hWave);
1041 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1042 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1047 HeapFree(GetProcessHeap(), 0, waveHdr);
1050 waveInClose(wmw->hWave);
1053 CloseHandle(wmw->hEvent);
1055 /* need to update the size of the data chunk */
1056 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1057 TRACE("failed on ascend\n");
1060 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1061 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1062 wmw->openParms.wDeviceID,
1063 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1066 wmw->dwStatus = MCI_MODE_STOP;
1072 /**************************************************************************
1073 * WAVE_mciPause [internal]
1075 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1078 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1080 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1082 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1083 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1085 if (wmw->dwStatus == MCI_MODE_PLAY) {
1086 wmw->dwStatus = MCI_MODE_PAUSE;
1089 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1090 else dwRet = waveOutPause(wmw->hWave);
1092 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1095 /**************************************************************************
1096 * WAVE_mciResume [internal]
1098 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1100 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1103 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1105 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1107 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1108 wmw->dwStatus = MCI_MODE_PLAY;
1111 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1112 else dwRet = waveOutRestart(wmw->hWave);
1113 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1116 /**************************************************************************
1117 * WAVE_mciSeek [internal]
1119 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1122 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1124 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1126 if (lpParms == NULL) {
1127 ret = MCIERR_NULL_PARAMETER_BLOCK;
1128 } else if (wmw == NULL) {
1129 ret = MCIERR_INVALID_DEVICE_ID;
1131 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1133 if (dwFlags & MCI_SEEK_TO_START) {
1134 wmw->dwPosition = 0;
1135 } else if (dwFlags & MCI_SEEK_TO_END) {
1136 wmw->dwPosition = wmw->ckWaveData.cksize;
1137 } else if (dwFlags & MCI_TO) {
1138 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1140 WARN("dwFlag doesn't tell where to seek to...\n");
1141 return MCIERR_MISSING_PARAMETER;
1144 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1146 if (dwFlags & MCI_NOTIFY) {
1147 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1148 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1154 /**************************************************************************
1155 * WAVE_mciSet [internal]
1157 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1159 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1161 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1163 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1164 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1166 if (dwFlags & MCI_SET_TIME_FORMAT) {
1167 switch (lpParms->dwTimeFormat) {
1168 case MCI_FORMAT_MILLISECONDS:
1169 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1170 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1172 case MCI_FORMAT_BYTES:
1173 TRACE("MCI_FORMAT_BYTES !\n");
1174 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1176 case MCI_FORMAT_SAMPLES:
1177 TRACE("MCI_FORMAT_SAMPLES !\n");
1178 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1181 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1182 return MCIERR_BAD_TIME_FORMAT;
1185 if (dwFlags & MCI_SET_VIDEO) {
1186 TRACE("No support for video !\n");
1187 return MCIERR_UNSUPPORTED_FUNCTION;
1189 if (dwFlags & MCI_SET_DOOR_OPEN) {
1190 TRACE("No support for door open !\n");
1191 return MCIERR_UNSUPPORTED_FUNCTION;
1193 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1194 TRACE("No support for door close !\n");
1195 return MCIERR_UNSUPPORTED_FUNCTION;
1197 if (dwFlags & MCI_SET_AUDIO) {
1198 if (dwFlags & MCI_SET_ON) {
1199 TRACE("MCI_SET_ON audio !\n");
1200 } else if (dwFlags & MCI_SET_OFF) {
1201 TRACE("MCI_SET_OFF audio !\n");
1203 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1204 return MCIERR_BAD_INTEGER;
1207 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1208 TRACE("MCI_SET_AUDIO_ALL !\n");
1209 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1210 TRACE("MCI_SET_AUDIO_LEFT !\n");
1211 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1212 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1214 if (dwFlags & MCI_WAVE_INPUT)
1215 TRACE("MCI_WAVE_INPUT !\n");
1216 if (dwFlags & MCI_WAVE_OUTPUT)
1217 TRACE("MCI_WAVE_OUTPUT !\n");
1218 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1219 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1220 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1221 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1222 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1223 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1224 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1226 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1227 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1228 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1230 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1231 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1232 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1234 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1235 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1236 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1238 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1239 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1240 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1242 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1243 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1244 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1249 /**************************************************************************
1250 * WAVE_mciSave [internal]
1252 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1254 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1255 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1256 WPARAM wparam = MCI_NOTIFY_FAILURE;
1258 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1259 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1260 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1262 if (dwFlags & MCI_WAIT)
1264 FIXME("MCI_WAIT not implemented\n");
1267 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1268 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1271 ret = mmioClose(wmw->hFile, 0);
1274 If the destination file already exists, it has to be overwritten. (Behaviour
1275 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1276 my applications. We are making use of mmioRename, which WILL NOT overwrite
1277 the destination file (which is what Windows does, also verified in Win2K)
1278 So, lets delete the destination file before calling mmioRename. If the
1279 destination file DOESN'T exist, the delete will fail silently. Let's also be
1280 careful not to lose our previous error code.
1282 tmpRet = GetLastError();
1283 DeleteFileA (lpParms->lpfilename);
1284 SetLastError(tmpRet);
1286 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1287 ret = ERROR_SUCCESS;
1290 if (dwFlags & MCI_NOTIFY) {
1291 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1293 mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback),
1294 wmw->openParms.wDeviceID, wparam);
1300 /**************************************************************************
1301 * WAVE_mciStatus [internal]
1303 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1305 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1308 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1309 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1310 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1312 if (dwFlags & MCI_STATUS_ITEM) {
1313 switch (lpParms->dwItem) {
1314 case MCI_STATUS_CURRENT_TRACK:
1315 lpParms->dwReturn = 1;
1316 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1318 case MCI_STATUS_LENGTH:
1320 lpParms->dwReturn = 0;
1321 return MCIERR_UNSUPPORTED_FUNCTION;
1323 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1324 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1325 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1327 case MCI_STATUS_MODE:
1328 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1329 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1330 ret = MCI_RESOURCE_RETURNED;
1332 case MCI_STATUS_MEDIA_PRESENT:
1333 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1334 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1335 ret = MCI_RESOURCE_RETURNED;
1337 case MCI_STATUS_NUMBER_OF_TRACKS:
1338 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1339 lpParms->dwReturn = 1;
1340 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1342 case MCI_STATUS_POSITION:
1344 lpParms->dwReturn = 0;
1345 return MCIERR_UNSUPPORTED_FUNCTION;
1347 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1348 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1349 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1351 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1352 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1354 case MCI_STATUS_READY:
1355 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1356 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1357 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1358 ret = MCI_RESOURCE_RETURNED;
1360 case MCI_STATUS_TIME_FORMAT:
1361 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1362 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1363 ret = MCI_RESOURCE_RETURNED;
1365 case MCI_WAVE_INPUT:
1366 TRACE("MCI_WAVE_INPUT !\n");
1367 lpParms->dwReturn = 0;
1368 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1370 case MCI_WAVE_OUTPUT:
1371 TRACE("MCI_WAVE_OUTPUT !\n");
1374 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1375 lpParms->dwReturn = id;
1377 lpParms->dwReturn = 0;
1378 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1382 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1384 lpParms->dwReturn = 0;
1385 return MCIERR_UNSUPPORTED_FUNCTION;
1387 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1388 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1390 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1392 lpParms->dwReturn = 0;
1393 return MCIERR_UNSUPPORTED_FUNCTION;
1395 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1396 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1398 case MCI_WAVE_STATUS_BLOCKALIGN:
1400 lpParms->dwReturn = 0;
1401 return MCIERR_UNSUPPORTED_FUNCTION;
1403 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1404 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1406 case MCI_WAVE_STATUS_CHANNELS:
1408 lpParms->dwReturn = 0;
1409 return MCIERR_UNSUPPORTED_FUNCTION;
1411 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1412 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1414 case MCI_WAVE_STATUS_FORMATTAG:
1416 lpParms->dwReturn = 0;
1417 return MCIERR_UNSUPPORTED_FUNCTION;
1419 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1420 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1422 case MCI_WAVE_STATUS_LEVEL:
1423 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1424 lpParms->dwReturn = 0xAAAA5555;
1426 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1428 lpParms->dwReturn = 0;
1429 return MCIERR_UNSUPPORTED_FUNCTION;
1431 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1432 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1435 WARN("unknown command %08lX !\n", lpParms->dwItem);
1436 return MCIERR_UNRECOGNIZED_COMMAND;
1439 if (dwFlags & MCI_NOTIFY) {
1440 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1441 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1446 /**************************************************************************
1447 * WAVE_mciGetDevCaps [internal]
1449 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1450 LPMCI_GETDEVCAPS_PARMS lpParms)
1452 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1455 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1457 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1458 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1460 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1461 switch(lpParms->dwItem) {
1462 case MCI_GETDEVCAPS_DEVICE_TYPE:
1463 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1464 ret = MCI_RESOURCE_RETURNED;
1466 case MCI_GETDEVCAPS_HAS_AUDIO:
1467 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1468 ret = MCI_RESOURCE_RETURNED;
1470 case MCI_GETDEVCAPS_HAS_VIDEO:
1471 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1472 ret = MCI_RESOURCE_RETURNED;
1474 case MCI_GETDEVCAPS_USES_FILES:
1475 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1476 ret = MCI_RESOURCE_RETURNED;
1478 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1479 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1480 ret = MCI_RESOURCE_RETURNED;
1482 case MCI_GETDEVCAPS_CAN_RECORD:
1483 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1484 ret = MCI_RESOURCE_RETURNED;
1486 case MCI_GETDEVCAPS_CAN_EJECT:
1487 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1488 ret = MCI_RESOURCE_RETURNED;
1490 case MCI_GETDEVCAPS_CAN_PLAY:
1491 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1492 ret = MCI_RESOURCE_RETURNED;
1494 case MCI_GETDEVCAPS_CAN_SAVE:
1495 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1496 ret = MCI_RESOURCE_RETURNED;
1498 case MCI_WAVE_GETDEVCAPS_INPUTS:
1499 lpParms->dwReturn = 1;
1501 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1502 lpParms->dwReturn = 1;
1505 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1506 return MCIERR_UNRECOGNIZED_COMMAND;
1509 WARN("No GetDevCaps-Item !\n");
1510 return MCIERR_UNRECOGNIZED_COMMAND;
1515 /**************************************************************************
1516 * WAVE_mciInfo [internal]
1518 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1522 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1524 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1526 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1527 ret = MCIERR_NULL_PARAMETER_BLOCK;
1528 } else if (wmw == NULL) {
1529 ret = MCIERR_INVALID_DEVICE_ID;
1531 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1533 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1534 case MCI_INFO_PRODUCT:
1535 str = "Wine's audio player";
1538 str = wmw->openParms.lpstrElementName;
1540 case MCI_WAVE_INPUT:
1541 str = "Wine Wave In";
1543 case MCI_WAVE_OUTPUT:
1544 str = "Wine Wave Out";
1547 WARN("Don't know this info command (%lu)\n", dwFlags);
1548 ret = MCIERR_UNRECOGNIZED_COMMAND;
1552 if (strlen(str) + 1 > lpParms->dwRetSize) {
1553 ret = MCIERR_PARAM_OVERFLOW;
1555 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1558 lpParms->lpstrReturn[0] = 0;
1564 /**************************************************************************
1565 * DriverProc (MCIWAVE.@)
1567 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1568 DWORD dwParam1, DWORD dwParam2)
1570 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1571 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1574 case DRV_LOAD: return 1;
1575 case DRV_FREE: return 1;
1576 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1577 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1578 case DRV_ENABLE: return 1;
1579 case DRV_DISABLE: return 1;
1580 case DRV_QUERYCONFIGURE: return 1;
1581 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1582 case DRV_INSTALL: return DRVCNF_RESTART;
1583 case DRV_REMOVE: return DRVCNF_RESTART;
1584 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1585 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1586 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1587 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1588 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1589 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1590 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1591 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1592 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1593 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1594 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1595 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1596 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1597 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1598 /* commands that should be supported */
1613 FIXME("Unsupported yet command [%lu]\n", wMsg);
1616 TRACE("Unsupported command [%lu]\n", wMsg);
1618 /* option which can be silenced */
1623 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1626 FIXME("is probably wrong msg [%lu]\n", wMsg);
1627 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1629 return MCIERR_UNRECOGNIZED_COMMAND;