1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
7 * 2000 Francois Jacques
16 #include "debugtools.h"
18 DEFAULT_DEBUG_CHANNEL(mciwave);
23 int nUseCount; /* Incremented for each shared open */
24 BOOL fShareable; /* TRUE if first open was shareable */
25 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
26 HMMIO hFile; /* mmio file handle open as Element */
27 MCI_WAVE_OPEN_PARMSA openParms;
28 LPWAVEFORMATEX lpWaveFormat;
29 BOOL fInput; /* FALSE = Output, TRUE = Input */
30 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
31 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
32 DWORD dwRemaining; /* remaining bytes to play or record */
33 DWORD dwPosition; /* position in bytes in chunk */
34 HANDLE hEvent; /* for synchronization */
35 DWORD dwEventCount; /* for synchronization */
36 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
37 MMCKINFO ckMainRIFF; /* main RIFF chunk */
38 MMCKINFO ckWaveData; /* data chunk */
41 /* ===================================================================
42 * ===================================================================
43 * FIXME: should be using the new mmThreadXXXX functions from WINMM
45 * it would require to add a wine internal flag to mmThreadCreate
46 * in order to pass a 32 bit function instead of a 16 bit one
47 * ===================================================================
48 * =================================================================== */
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 if (sca->allocatedCopy)
72 HeapFree(GetProcessHeap(), 0, (LPVOID)sca->dwParam2);
73 HeapFree(GetProcessHeap(), 0, sca);
75 WARN("Should not happen ? what's wrong \n");
76 /* should not go after this point */
80 /**************************************************************************
81 * MCI_SendCommandAsync [internal]
83 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
84 DWORD dwParam2, UINT size)
86 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA));
89 return MCIERR_OUT_OF_MEMORY;
93 sca->dwParam1 = dwParam1;
96 sca->dwParam2 = (DWORD)HeapAlloc(GetProcessHeap(), 0, size);
97 if (sca->dwParam2 == 0) {
98 HeapFree(GetProcessHeap(), 0, sca);
99 return MCIERR_OUT_OF_MEMORY;
101 sca->allocatedCopy = TRUE;
102 /* copy structure passed by program in dwParam2 to be sure
103 * we can still use it whatever the program does
105 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
107 sca->dwParam2 = dwParam2;
108 sca->allocatedCopy = FALSE;
111 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
112 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
113 return MCI_SCAStarter(&sca);
118 /*======================================================================*
119 * MCI WAVE implemantation *
120 *======================================================================*/
122 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
124 /**************************************************************************
125 * MCIWAVE_drvOpen [internal]
127 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
129 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
134 wmw->wDevID = modp->wDeviceID;
135 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
136 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
137 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
138 return modp->wDeviceID;
141 /**************************************************************************
142 * MCIWAVE_drvClose [internal]
144 static DWORD WAVE_drvClose(DWORD dwDevID)
146 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
149 HeapFree(GetProcessHeap(), 0, wmw);
150 mciSetDriverData(dwDevID, 0);
156 /**************************************************************************
157 * WAVE_mciGetOpenDev [internal]
159 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
161 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
163 if (wmw == NULL || wmw->nUseCount == 0) {
164 WARN("Invalid wDevID=%u\n", wDevID);
170 /**************************************************************************
171 * WAVE_ConvertByteToTimeFormat [internal]
173 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
177 switch (wmw->dwMciTimeFormat) {
178 case MCI_FORMAT_MILLISECONDS:
179 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
181 case MCI_FORMAT_BYTES:
184 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
185 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
188 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
190 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
195 /**************************************************************************
196 * WAVE_ConvertTimeFormatToByte [internal]
198 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
202 switch (wmw->dwMciTimeFormat) {
203 case MCI_FORMAT_MILLISECONDS:
204 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
206 case MCI_FORMAT_BYTES:
209 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
210 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
213 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
215 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
219 /**************************************************************************
220 * WAVE_mciReadFmt [internal]
222 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
227 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
228 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
229 return MCIERR_INVALID_FILE;
230 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
231 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
233 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
234 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
235 if (r < sizeof(WAVEFORMAT))
236 return MCIERR_INVALID_FILE;
238 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
239 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
240 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
241 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
242 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
243 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
244 if (r >= (long)sizeof(WAVEFORMATEX))
245 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
247 mmioAscend(wmw->hFile, &mmckInfo, 0);
248 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
249 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
250 TRACE("can't find data chunk\n");
251 return MCIERR_INVALID_FILE;
253 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
254 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
255 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
256 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
261 /**************************************************************************
262 * WAVE_mciCreateRIFFSkeleton [internal]
264 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
266 MMCKINFO ckWaveFormat;
268 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
269 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
270 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
273 HMMIO hmmio = wmw->hFile;
275 lpckRIFF->ckid = FOURCC_RIFF;
276 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
277 lpckRIFF->cksize = 0;
279 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
282 ckWaveFormat.fccType = 0;
283 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
284 ckWaveFormat.cksize = 16;
288 TRACE("allocating waveformat with default waveformat 11khz/8bit/mono \n");
290 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
292 lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
293 lpWaveFormat->nChannels = 1; /* MONO */
294 lpWaveFormat->nSamplesPerSec = 11025;
295 lpWaveFormat->nAvgBytesPerSec = 11025;
296 lpWaveFormat->nBlockAlign = 1;
297 lpWaveFormat->wBitsPerSample = 8;
298 lpWaveFormat->cbSize = 0; /* don't care */
301 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
304 /* only the first 16 bytes are serialized */
305 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
308 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
311 lpckWaveData->cksize = 0;
312 lpckWaveData->fccType = 0;
313 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
315 /* create data chunk */
316 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
322 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
323 return MCIERR_INVALID_FILE;
326 /**************************************************************************
327 * WAVE_mciOpen [internal]
329 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
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;
352 dwDeviceID = lpOpenParms->wDeviceID;
356 wmw->dwStatus = MCI_MODE_NOT_READY;
358 TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
360 if (dwFlags & MCI_OPEN_ELEMENT) {
361 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
362 /* could it be that (DWORD)lpOpenParms->lpstrElementName
363 * contains the hFile value ?
365 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
367 if (strlen(lpOpenParms->lpstrElementName) > 0) {
368 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
370 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
371 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
373 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
374 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
375 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
377 if (wmw->hFile == 0) {
378 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
379 dwRet = MCIERR_FILE_NOT_FOUND;
383 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
385 /* make sure we're are the beginning of the file */
386 mmioSeek(wmw->hFile, 0, SEEK_SET);
388 /* first reading of this file. read the waveformat chunk */
389 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
390 dwRet = MCIERR_INVALID_FILE;
392 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
393 (LPSTR)&(lpckMainRIFF->ckid),
394 (LPSTR) &(lpckMainRIFF->fccType),
395 (lpckMainRIFF->cksize));
397 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
398 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
399 dwRet = MCIERR_INVALID_FILE;
401 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
411 CHAR szTmpPath[MAX_PATH];
412 CHAR szPrefix[4] = "TMP\0";
414 pszTmpFileName = HeapAlloc(GetProcessHeap(),
416 MAX_PATH * sizeof(*pszTmpFileName));
418 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
419 WARN("can't retrieve temp path!\n");
420 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
421 return MCIERR_FILE_NOT_FOUND;
424 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
425 WARN("can't retrieve temp file name!\n");
426 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
427 return MCIERR_FILE_NOT_FOUND;
430 wmw->bTemporaryFile = TRUE;
432 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
434 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
436 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
437 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
439 if (wmw->hFile == 0) {
440 /* temporary file could not be created. clean filename. */
441 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
442 WARN("can't create file='%s' !\n", pszTmpFileName);
443 dwRet = MCIERR_FILE_NOT_FOUND;
450 TRACE("hFile=%u\n", wmw->hFile);
452 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
454 if (wmw->bTemporaryFile == TRUE)
456 /* Additional openParms is temporary file's name */
457 wmw->openParms.lpstrElementName = pszTmpFileName;
460 wmw->wNotifyDeviceID = dwDeviceID;
463 if (wmw->lpWaveFormat) {
464 switch (wmw->lpWaveFormat->wFormatTag) {
465 case WAVE_FORMAT_PCM:
466 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
467 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
468 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
469 wmw->lpWaveFormat->nAvgBytesPerSec,
470 wmw->lpWaveFormat->nSamplesPerSec *
471 wmw->lpWaveFormat->nBlockAlign);
472 wmw->lpWaveFormat->nAvgBytesPerSec =
473 wmw->lpWaveFormat->nSamplesPerSec *
474 wmw->lpWaveFormat->nBlockAlign;
481 wmw->dwStatus = MCI_MODE_STOP;
485 mmioClose(wmw->hFile, 0);
491 /**************************************************************************
492 * WAVE_mciCue [internal]
494 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
499 This routine is far from complete. At the moment only a check is done on the
500 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
503 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
508 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
510 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
512 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
514 /* always close elements ? */
515 if (wmw->hFile != 0) {
516 mmioClose(wmw->hFile, 0);
520 dwRet = MMSYSERR_NOERROR; /* assume success */
522 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
523 dwRet = waveOutClose(wmw->hWave);
524 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
526 } else if (wmw->fInput) {
527 dwRet = waveInClose(wmw->hWave);
528 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
532 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
535 /**************************************************************************
536 * WAVE_mciStop [internal]
538 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
541 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
543 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
545 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
547 /* wait for playback thread (if any) to exit before processing further */
548 switch (wmw->dwStatus) {
551 case MCI_MODE_RECORD:
553 int oldStat = wmw->dwStatus;
554 wmw->dwStatus = MCI_MODE_NOT_READY;
555 if (oldStat == MCI_MODE_PAUSE)
556 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
558 while (wmw->dwStatus != MCI_MODE_STOP)
566 wmw->dwStatus = MCI_MODE_STOP;
568 if ((dwFlags & MCI_NOTIFY) && lpParms) {
569 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
570 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
576 /**************************************************************************
577 * WAVE_mciClose [internal]
579 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
582 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
584 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
586 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
588 if (wmw->dwStatus != MCI_MODE_STOP) {
589 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
594 if (wmw->nUseCount == 0) {
595 if (wmw->hFile != 0) {
596 mmioClose(wmw->hFile, 0);
601 /* That string got allocated in mciOpen because no filename was specified
602 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
603 * allocated by mciOpen, *NOT* the application.
605 if (wmw->bTemporaryFile)
607 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
608 wmw->openParms.lpstrElementName = NULL;
611 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
612 wmw->lpWaveFormat = NULL;
614 if ((dwFlags & MCI_NOTIFY) && lpParms) {
615 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
616 wmw->wNotifyDeviceID,
617 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
623 /**************************************************************************
624 * WAVE_mciPlayCallback [internal]
626 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
628 DWORD dwParam1, DWORD dwParam2)
630 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
637 InterlockedIncrement(&wmw->dwEventCount);
638 TRACE("Returning waveHdr=%lx\n", dwParam1);
639 SetEvent(wmw->hEvent);
642 ERR("Unknown uMsg=%d\n", uMsg);
646 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
649 ResetEvent(wmw->hEvent);
650 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
653 InterlockedIncrement(&wmw->dwEventCount);
655 WaitForSingleObject(wmw->hEvent, INFINITE);
659 /**************************************************************************
660 * WAVE_mciPlay [internal]
662 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
665 LONG bufsize, count, left;
667 LPWAVEHDR waveHdr = NULL;
668 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
671 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
673 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
674 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
676 /* FIXME : since there is no way to determine in which mode the device is
677 * open (recording/playback) automatically switch from a mode to another
682 WARN("cannot play on input device\n");
683 return MCIERR_NONAPPLICABLE_FUNCTION;
686 if (wmw->hFile == 0) {
687 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
688 return MCIERR_FILE_NOT_FOUND;
691 if (wmw->dwStatus == MCI_MODE_PAUSE) {
692 /* FIXME: parameters (start/end) in lpParams may not be used */
693 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
696 /** This function will be called again by a thread when async is used.
697 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
698 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
700 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
701 return MCIERR_INTERNAL;
704 wmw->dwStatus = MCI_MODE_PLAY;
706 if (!(dwFlags & MCI_WAIT)) {
707 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
708 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
712 if (lpParms && (dwFlags & MCI_FROM)) {
713 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
715 if (lpParms && (dwFlags & MCI_TO)) {
716 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
719 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
721 if (end <= wmw->dwPosition)
725 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
726 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
728 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
729 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
732 if (wmw->lpWaveFormat) {
733 switch (wmw->lpWaveFormat->wFormatTag) {
734 case WAVE_FORMAT_PCM:
735 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
736 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
737 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
738 wmw->lpWaveFormat->nAvgBytesPerSec,
739 wmw->lpWaveFormat->nSamplesPerSec *
740 wmw->lpWaveFormat->nBlockAlign);
741 wmw->lpWaveFormat->nAvgBytesPerSec =
742 wmw->lpWaveFormat->nSamplesPerSec *
743 wmw->lpWaveFormat->nBlockAlign;
749 TRACE("can't retrieve wave format %ld\n", dwRet);
754 /* go back to begining of chunk plus the requested position */
755 /* FIXME: I'm not sure this is correct, notably because some data linked to
756 * the decompression state machine will not be correcly initialized.
757 * try it this way (other way would be to decompress from 0 up to dwPosition
758 * and to start sending to hWave when dwPosition is reached)
760 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
762 /* By default the device will be opened for output, the MCI_CUE function is there to
763 * change from output to input and back
765 /* FIXME: how to choose between several output channels ? here mapper is forced */
766 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
767 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
770 TRACE("Can't open low level audio device %ld\n", dwRet);
771 dwRet = MCIERR_DEVICE_OPEN;
776 /* make it so that 3 buffers per second are needed */
777 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
779 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
780 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
781 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
782 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
783 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
784 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
785 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
786 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
787 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
788 dwRet = MCIERR_INTERNAL;
793 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
794 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
795 wmw->dwEventCount = 1L; /* for first buffer */
797 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
799 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
800 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
801 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
802 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
805 /* count is always <= bufsize, so this is correct regarding the
806 * waveOutPrepareHeader function
808 waveHdr[whidx].dwBufferLength = count;
809 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
810 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
811 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
812 waveHdr[whidx].dwBytesRecorded);
813 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
815 wmw->dwPosition += count;
816 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
818 WAVE_mciPlayWaitDone(wmw);
822 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
824 /* just to get rid of some race conditions between play, stop and pause */
825 waveOutReset(wmw->hWave);
827 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
828 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
833 HeapFree(GetProcessHeap(), 0, waveHdr);
836 waveOutClose(wmw->hWave);
839 CloseHandle(wmw->hEvent);
841 if (lpParms && (dwFlags & MCI_NOTIFY)) {
842 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
843 wmw->wNotifyDeviceID,
844 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
847 wmw->dwStatus = MCI_MODE_STOP;
852 /**************************************************************************
853 * WAVE_mciPlayCallback [internal]
855 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
857 DWORD dwParam1, DWORD dwParam2)
859 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
860 LPWAVEHDR lpWaveHdr = NULL;
867 lpWaveHdr = (LPWAVEHDR) dwParam1;
869 InterlockedIncrement(&wmw->dwEventCount);
871 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
873 lpWaveHdr->dwFlags &= ~WHDR_DONE;
874 wmw->dwPosition += count;
875 wmw->dwRemaining -= count;
877 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
878 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->wNotifyDeviceID, MCI_RECORD, dwFlags,
944 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
947 if (!wmw->lpWaveFormat)
950 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
954 if (lpParms && (dwFlags & MCI_FROM)) {
955 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
958 if (lpParms && (dwFlags & MCI_TO)) {
959 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
962 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
964 if (end <= wmw->dwPosition)
969 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
970 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
972 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
973 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
975 /* go back to begining of chunk plus the requested position */
976 /* FIXME: I'm not sure this is correct, notably because some data linked to
977 * the decompression state machine will not be correcly initialized.
978 * try it this way (other way would be to decompress from 0 up to dwPosition
979 * and to start sending to hWave when dwPosition is reached)
981 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
983 /* By default the device will be opened for output, the MCI_CUE function is there to
984 * change from output to input and back
986 /* FIXME: how to choose between several output channels ? here mapper is forced */
987 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
988 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
991 TRACE("Can't open low level audio device %ld\n", dwRet);
992 dwRet = MCIERR_DEVICE_OPEN;
997 /* make it so that 3 buffers per second are needed */
998 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1000 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1001 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1002 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1003 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1004 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1005 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1006 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1008 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1009 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1010 dwRet = MCIERR_INTERNAL;
1014 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1015 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1016 dwRet = MCIERR_INTERNAL;
1020 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1021 wmw->dwEventCount = 1L; /* for first buffer */
1023 wmw->dwRemaining = end - wmw->dwPosition;
1025 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1027 dwRet = waveInStart(wmw->hWave);
1029 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1030 WAVE_mciRecordWaitDone(wmw);
1033 waveInReset(wmw->hWave);
1034 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1035 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1040 HeapFree(GetProcessHeap(), 0, waveHdr);
1043 waveInClose(wmw->hWave);
1046 CloseHandle(wmw->hEvent);
1048 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1049 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1050 wmw->wNotifyDeviceID,
1051 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1054 wmw->dwStatus = MCI_MODE_STOP;
1060 /**************************************************************************
1061 * WAVE_mciPause [internal]
1063 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1066 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1068 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1070 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1071 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1073 if (wmw->dwStatus == MCI_MODE_PLAY) {
1074 wmw->dwStatus = MCI_MODE_PAUSE;
1077 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1078 else dwRet = waveOutPause(wmw->hWave);
1080 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1083 /**************************************************************************
1084 * WAVE_mciResume [internal]
1086 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1088 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1091 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1093 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1095 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1096 wmw->dwStatus = MCI_MODE_PLAY;
1099 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1100 else dwRet = waveOutRestart(wmw->hWave);
1101 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1104 /**************************************************************************
1105 * WAVE_mciSeek [internal]
1107 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1110 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1112 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1114 if (lpParms == NULL) {
1115 ret = MCIERR_NULL_PARAMETER_BLOCK;
1116 } else if (wmw == NULL) {
1117 ret = MCIERR_INVALID_DEVICE_ID;
1119 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1121 if (dwFlags & MCI_SEEK_TO_START) {
1122 wmw->dwPosition = 0;
1123 } else if (dwFlags & MCI_SEEK_TO_END) {
1124 wmw->dwPosition = wmw->ckWaveData.cksize;
1125 } else if (dwFlags & MCI_TO) {
1126 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1128 WARN("dwFlag doesn't tell where to seek to...\n");
1129 return MCIERR_MISSING_PARAMETER;
1132 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1134 if (dwFlags & MCI_NOTIFY) {
1135 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1136 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1142 /**************************************************************************
1143 * WAVE_mciSet [internal]
1145 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1147 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1149 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1151 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1152 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1154 if (dwFlags & MCI_SET_TIME_FORMAT) {
1155 switch (lpParms->dwTimeFormat) {
1156 case MCI_FORMAT_MILLISECONDS:
1157 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1158 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1160 case MCI_FORMAT_BYTES:
1161 TRACE("MCI_FORMAT_BYTES !\n");
1162 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1164 case MCI_FORMAT_SAMPLES:
1165 TRACE("MCI_FORMAT_SAMPLES !\n");
1166 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1169 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1170 return MCIERR_BAD_TIME_FORMAT;
1173 if (dwFlags & MCI_SET_VIDEO) {
1174 TRACE("No support for video !\n");
1175 return MCIERR_UNSUPPORTED_FUNCTION;
1177 if (dwFlags & MCI_SET_DOOR_OPEN) {
1178 TRACE("No support for door open !\n");
1179 return MCIERR_UNSUPPORTED_FUNCTION;
1181 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1182 TRACE("No support for door close !\n");
1183 return MCIERR_UNSUPPORTED_FUNCTION;
1185 if (dwFlags & MCI_SET_AUDIO) {
1186 if (dwFlags & MCI_SET_ON) {
1187 TRACE("MCI_SET_ON audio !\n");
1188 } else if (dwFlags & MCI_SET_OFF) {
1189 TRACE("MCI_SET_OFF audio !\n");
1191 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1192 return MCIERR_BAD_INTEGER;
1195 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1196 TRACE("MCI_SET_AUDIO_ALL !\n");
1197 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1198 TRACE("MCI_SET_AUDIO_LEFT !\n");
1199 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1200 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1202 if (dwFlags & MCI_WAVE_INPUT)
1203 TRACE("MCI_WAVE_INPUT !\n");
1204 if (dwFlags & MCI_WAVE_OUTPUT)
1205 TRACE("MCI_WAVE_OUTPUT !\n");
1206 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1207 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1208 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1209 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1210 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
1211 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
1212 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
1213 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
1214 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
1215 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
1216 if (dwFlags & MCI_WAVE_SET_CHANNELS)
1217 TRACE("MCI_WAVE_SET_CHANNELS !\n");
1218 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
1219 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
1220 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
1221 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
1225 /**************************************************************************
1226 * WAVE_mciSave [internal]
1228 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1230 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1231 DWORD ret = MCIERR_FILE_NOT_SAVED;
1232 WPARAM wparam = MCI_NOTIFY_FAILURE;
1234 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1235 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1236 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1238 if (dwFlags & MCI_WAIT)
1240 FIXME("MCI_WAIT not implemented\n");
1243 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1244 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1247 ret = mmioClose(wmw->hFile, 0);
1249 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1250 ret = ERROR_SUCCESS;
1253 if (dwFlags & MCI_NOTIFY) {
1254 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1256 mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback),
1257 wmw->wNotifyDeviceID,
1264 /**************************************************************************
1265 * WAVE_mciStatus [internal]
1267 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1269 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1272 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1273 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1274 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1276 if (dwFlags & MCI_STATUS_ITEM) {
1277 switch (lpParms->dwItem) {
1278 case MCI_STATUS_CURRENT_TRACK:
1279 lpParms->dwReturn = 1;
1280 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1282 case MCI_STATUS_LENGTH:
1284 lpParms->dwReturn = 0;
1285 return MCIERR_UNSUPPORTED_FUNCTION;
1287 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1288 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1289 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1291 case MCI_STATUS_MODE:
1292 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1293 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1294 ret = MCI_RESOURCE_RETURNED;
1296 case MCI_STATUS_MEDIA_PRESENT:
1297 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1298 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1299 ret = MCI_RESOURCE_RETURNED;
1301 case MCI_STATUS_NUMBER_OF_TRACKS:
1302 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1303 lpParms->dwReturn = 1;
1304 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1306 case MCI_STATUS_POSITION:
1308 lpParms->dwReturn = 0;
1309 return MCIERR_UNSUPPORTED_FUNCTION;
1311 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1312 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1313 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1315 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1316 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1318 case MCI_STATUS_READY:
1319 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1320 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1321 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1322 ret = MCI_RESOURCE_RETURNED;
1324 case MCI_STATUS_TIME_FORMAT:
1325 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1326 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1327 ret = MCI_RESOURCE_RETURNED;
1329 case MCI_WAVE_INPUT:
1330 TRACE("MCI_WAVE_INPUT !\n");
1331 lpParms->dwReturn = 0;
1332 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1334 case MCI_WAVE_OUTPUT:
1335 TRACE("MCI_WAVE_OUTPUT !\n");
1338 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1339 lpParms->dwReturn = id;
1341 lpParms->dwReturn = 0;
1342 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1346 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1348 lpParms->dwReturn = 0;
1349 return MCIERR_UNSUPPORTED_FUNCTION;
1351 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1352 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1354 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1356 lpParms->dwReturn = 0;
1357 return MCIERR_UNSUPPORTED_FUNCTION;
1359 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1360 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1362 case MCI_WAVE_STATUS_BLOCKALIGN:
1364 lpParms->dwReturn = 0;
1365 return MCIERR_UNSUPPORTED_FUNCTION;
1367 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1368 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1370 case MCI_WAVE_STATUS_CHANNELS:
1372 lpParms->dwReturn = 0;
1373 return MCIERR_UNSUPPORTED_FUNCTION;
1375 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1376 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1378 case MCI_WAVE_STATUS_FORMATTAG:
1380 lpParms->dwReturn = 0;
1381 return MCIERR_UNSUPPORTED_FUNCTION;
1383 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1384 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1386 case MCI_WAVE_STATUS_LEVEL:
1387 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1388 lpParms->dwReturn = 0xAAAA5555;
1390 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1392 lpParms->dwReturn = 0;
1393 return MCIERR_UNSUPPORTED_FUNCTION;
1395 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1396 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1399 WARN("unknown command %08lX !\n", lpParms->dwItem);
1400 return MCIERR_UNRECOGNIZED_COMMAND;
1403 if (dwFlags & MCI_NOTIFY) {
1404 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1405 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1410 /**************************************************************************
1411 * WAVE_mciGetDevCaps [internal]
1413 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1414 LPMCI_GETDEVCAPS_PARMS lpParms)
1416 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1419 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1421 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1422 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1424 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1425 switch(lpParms->dwItem) {
1426 case MCI_GETDEVCAPS_DEVICE_TYPE:
1427 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1428 ret = MCI_RESOURCE_RETURNED;
1430 case MCI_GETDEVCAPS_HAS_AUDIO:
1431 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1432 ret = MCI_RESOURCE_RETURNED;
1434 case MCI_GETDEVCAPS_HAS_VIDEO:
1435 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1436 ret = MCI_RESOURCE_RETURNED;
1438 case MCI_GETDEVCAPS_USES_FILES:
1439 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1440 ret = MCI_RESOURCE_RETURNED;
1442 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1443 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1444 ret = MCI_RESOURCE_RETURNED;
1446 case MCI_GETDEVCAPS_CAN_RECORD:
1447 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1448 ret = MCI_RESOURCE_RETURNED;
1450 case MCI_GETDEVCAPS_CAN_EJECT:
1451 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1452 ret = MCI_RESOURCE_RETURNED;
1454 case MCI_GETDEVCAPS_CAN_PLAY:
1455 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1456 ret = MCI_RESOURCE_RETURNED;
1458 case MCI_GETDEVCAPS_CAN_SAVE:
1459 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1460 ret = MCI_RESOURCE_RETURNED;
1462 case MCI_WAVE_GETDEVCAPS_INPUTS:
1463 lpParms->dwReturn = 1;
1465 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1466 lpParms->dwReturn = 1;
1469 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1470 return MCIERR_UNRECOGNIZED_COMMAND;
1473 WARN("No GetDevCaps-Item !\n");
1474 return MCIERR_UNRECOGNIZED_COMMAND;
1479 /**************************************************************************
1480 * WAVE_mciInfo [internal]
1482 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1486 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1488 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1490 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1491 ret = MCIERR_NULL_PARAMETER_BLOCK;
1492 } else if (wmw == NULL) {
1493 ret = MCIERR_INVALID_DEVICE_ID;
1495 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1497 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1498 case MCI_INFO_PRODUCT:
1499 str = "Wine's audio player";
1502 str = wmw->openParms.lpstrElementName;
1504 case MCI_WAVE_INPUT:
1505 str = "Wine Wave In";
1507 case MCI_WAVE_OUTPUT:
1508 str = "Wine Wave Out";
1511 WARN("Don't know this info command (%lu)\n", dwFlags);
1512 ret = MCIERR_UNRECOGNIZED_COMMAND;
1516 if (strlen(str) + 1 > lpParms->dwRetSize) {
1517 ret = MCIERR_PARAM_OVERFLOW;
1519 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1522 lpParms->lpstrReturn[0] = 0;
1528 /**************************************************************************
1529 * MCIWAVE_DriverProc [sample driver]
1531 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1532 DWORD dwParam1, DWORD dwParam2)
1534 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1535 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1538 case DRV_LOAD: return 1;
1539 case DRV_FREE: return 1;
1540 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1541 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1542 case DRV_ENABLE: return 1;
1543 case DRV_DISABLE: return 1;
1544 case DRV_QUERYCONFIGURE: return 1;
1545 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1546 case DRV_INSTALL: return DRVCNF_RESTART;
1547 case DRV_REMOVE: return DRVCNF_RESTART;
1548 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1549 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1550 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1551 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1552 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1553 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1554 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1555 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1556 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1557 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1558 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1559 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1560 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1561 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1562 /* commands that should be supported */
1577 FIXME("Unsupported yet command [%lu]\n", wMsg);
1580 TRACE("Unsupported command [%lu]\n", wMsg);
1584 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1587 FIXME("is probably wrong msg [%lu]\n", wMsg);
1588 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1590 return MCIERR_UNRECOGNIZED_COMMAND;