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
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
38 int nUseCount; /* Incremented for each shared open */
39 BOOL fShareable; /* TRUE if first open was shareable */
40 HMMIO hFile; /* mmio file handle open as Element */
41 MCI_WAVE_OPEN_PARMSA openParms;
43 LPWAVEFORMATEX lpWaveFormat;
44 BOOL fInput; /* FALSE = Output, TRUE = Input */
45 volatile WORD dwStatus; /* one from MCI_MODE_xxxx */
46 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
47 DWORD dwRemaining; /* remaining bytes to play or record */
48 DWORD dwPosition; /* position in bytes in chunk */
49 HANDLE hEvent; /* for synchronization */
50 DWORD dwEventCount; /* for synchronization */
51 BOOL bTemporaryFile; /* temporary file (MCI_RECORD) */
52 MMCKINFO ckMainRIFF; /* main RIFF chunk */
53 MMCKINFO ckWaveData; /* data chunk */
56 /* ===================================================================
57 * ===================================================================
58 * FIXME: should be using the new mmThreadXXXX functions from WINMM
60 * it would require to add a wine internal flag to mmThreadCreate
61 * in order to pass a 32 bit function instead of a 16 bit one
62 * ===================================================================
63 * =================================================================== */
72 /**************************************************************************
73 * MCI_SCAStarter [internal]
75 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
77 struct SCA* sca = (struct SCA*)arg;
80 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
81 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
82 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
83 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
84 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
85 HeapFree(GetProcessHeap(), 0, sca);
87 WARN("Should not happen ? what's wrong \n");
88 /* should not go after this point */
92 /**************************************************************************
93 * MCI_SendCommandAsync [internal]
95 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
96 DWORD dwParam2, UINT size)
98 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
101 return MCIERR_OUT_OF_MEMORY;
103 sca->wDevID = wDevID;
105 sca->dwParam1 = dwParam1;
107 if (size && dwParam2) {
108 sca->dwParam2 = (DWORD)sca + sizeof(struct SCA);
109 /* copy structure passed by program in dwParam2 to be sure
110 * we can still use it whatever the program does
112 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
114 sca->dwParam2 = dwParam2;
117 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
118 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
119 return MCI_SCAStarter(&sca);
124 /*======================================================================*
125 * MCI WAVE implemantation *
126 *======================================================================*/
128 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
130 /**************************************************************************
131 * MCIWAVE_drvOpen [internal]
133 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
135 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
140 wmw->wDevID = modp->wDeviceID;
141 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
142 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
143 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
145 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
146 wmw->wfxRef.nChannels = 1; /* MONO */
147 wmw->wfxRef.nSamplesPerSec = 11025;
148 wmw->wfxRef.nAvgBytesPerSec = 11025;
149 wmw->wfxRef.nBlockAlign = 1;
150 wmw->wfxRef.wBitsPerSample = 8;
151 wmw->wfxRef.cbSize = 0; /* don't care */
153 return modp->wDeviceID;
156 /**************************************************************************
157 * MCIWAVE_drvClose [internal]
159 static DWORD WAVE_drvClose(DWORD dwDevID)
161 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
164 HeapFree(GetProcessHeap(), 0, wmw);
165 mciSetDriverData(dwDevID, 0);
171 /**************************************************************************
172 * WAVE_mciGetOpenDev [internal]
174 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
176 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
178 if (wmw == NULL || wmw->nUseCount == 0) {
179 WARN("Invalid wDevID=%u\n", wDevID);
185 /**************************************************************************
186 * WAVE_ConvertByteToTimeFormat [internal]
188 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
192 switch (wmw->dwMciTimeFormat) {
193 case MCI_FORMAT_MILLISECONDS:
194 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
196 case MCI_FORMAT_BYTES:
199 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
200 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
203 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
205 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
210 /**************************************************************************
211 * WAVE_ConvertTimeFormatToByte [internal]
213 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
217 switch (wmw->dwMciTimeFormat) {
218 case MCI_FORMAT_MILLISECONDS:
219 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
221 case MCI_FORMAT_BYTES:
224 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
225 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
228 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
230 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
234 /**************************************************************************
235 * WAVE_mciReadFmt [internal]
237 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
242 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
243 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
244 return MCIERR_INVALID_FILE;
245 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
246 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
248 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
249 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
250 if (r < sizeof(WAVEFORMAT))
251 return MCIERR_INVALID_FILE;
253 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
254 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
255 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
256 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
257 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
258 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
259 if (r >= (long)sizeof(WAVEFORMATEX))
260 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
262 mmioAscend(wmw->hFile, &mmckInfo, 0);
263 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
264 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
265 TRACE("can't find data chunk\n");
266 return MCIERR_INVALID_FILE;
268 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
269 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
270 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
271 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
276 /**************************************************************************
277 * WAVE_mciCreateRIFFSkeleton [internal]
279 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
281 MMCKINFO ckWaveFormat;
283 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
284 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
285 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
288 HMMIO hmmio = wmw->hFile;
290 lpckRIFF->ckid = FOURCC_RIFF;
291 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
292 lpckRIFF->cksize = 0;
294 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
297 ckWaveFormat.fccType = 0;
298 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
299 ckWaveFormat.cksize = 16;
303 /* FIXME: for non PCM formats, the size of the waveFormat has to be
306 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
308 memcpy(lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
311 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
314 /* only the first 16 bytes are serialized */
315 /* wrong... for non PCM, the whole waveFormat is stored
317 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
320 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
323 lpckWaveData->cksize = 0;
324 lpckWaveData->fccType = 0;
325 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
327 /* create data chunk */
328 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
334 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
335 return MCIERR_INVALID_FILE;
338 /**************************************************************************
339 * WAVE_mciOpen [internal]
341 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
344 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
345 CHAR* pszTmpFileName = 0;
347 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
348 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
349 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
351 if (dwFlags & MCI_OPEN_SHAREABLE)
352 return MCIERR_HARDWARE;
354 if (wmw->nUseCount > 0) {
355 /* The driver is already opened on this channel
356 * Wave driver cannot be shared
358 return MCIERR_DEVICE_OPEN;
365 wmw->dwStatus = MCI_MODE_NOT_READY;
367 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
369 if (dwFlags & MCI_OPEN_ELEMENT) {
370 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
371 /* could it be that (DWORD)lpOpenParms->lpstrElementName
372 * contains the hFile value ?
374 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
376 if (strlen(lpOpenParms->lpstrElementName) > 0) {
377 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
379 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
380 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
382 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
383 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
384 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
386 if (wmw->hFile == 0) {
387 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
388 dwRet = MCIERR_FILE_NOT_FOUND;
392 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
394 /* make sure we're are the beginning of the file */
395 mmioSeek(wmw->hFile, 0, SEEK_SET);
397 /* first reading of this file. read the waveformat chunk */
398 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
399 dwRet = MCIERR_INVALID_FILE;
401 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
402 (LPSTR)&(lpckMainRIFF->ckid),
403 (LPSTR) &(lpckMainRIFF->fccType),
404 (lpckMainRIFF->cksize));
406 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
407 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
408 dwRet = MCIERR_INVALID_FILE;
410 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
420 CHAR szTmpPath[MAX_PATH];
421 CHAR szPrefix[4] = "TMP\0";
423 pszTmpFileName = HeapAlloc(GetProcessHeap(),
425 MAX_PATH * sizeof(*pszTmpFileName));
427 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
428 WARN("can't retrieve temp path!\n");
429 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
430 return MCIERR_FILE_NOT_FOUND;
433 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
434 WARN("can't retrieve temp file name!\n");
435 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
436 return MCIERR_FILE_NOT_FOUND;
439 wmw->bTemporaryFile = TRUE;
441 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
443 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
445 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
446 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
448 if (wmw->hFile == 0) {
449 /* temporary file could not be created. clean filename. */
450 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
451 WARN("can't create file='%s' !\n", pszTmpFileName);
452 dwRet = MCIERR_FILE_NOT_FOUND;
459 TRACE("hFile=%u\n", wmw->hFile);
461 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
463 if (wmw->bTemporaryFile == TRUE)
465 /* Additional openParms is temporary file's name */
466 wmw->openParms.lpstrElementName = pszTmpFileName;
470 if (wmw->lpWaveFormat) {
471 switch (wmw->lpWaveFormat->wFormatTag) {
472 case WAVE_FORMAT_PCM:
473 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
474 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
475 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
476 wmw->lpWaveFormat->nAvgBytesPerSec,
477 wmw->lpWaveFormat->nSamplesPerSec *
478 wmw->lpWaveFormat->nBlockAlign);
479 wmw->lpWaveFormat->nAvgBytesPerSec =
480 wmw->lpWaveFormat->nSamplesPerSec *
481 wmw->lpWaveFormat->nBlockAlign;
488 wmw->dwStatus = MCI_MODE_STOP;
492 mmioClose(wmw->hFile, 0);
498 /**************************************************************************
499 * WAVE_mciCue [internal]
501 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
506 This routine is far from complete. At the moment only a check is done on the
507 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
510 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
515 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
517 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
519 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
521 /* always close elements ? */
522 if (wmw->hFile != 0) {
523 mmioClose(wmw->hFile, 0);
527 dwRet = MMSYSERR_NOERROR; /* assume success */
529 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
530 dwRet = waveOutClose(wmw->hWave);
531 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
533 } else if (wmw->fInput) {
534 dwRet = waveInClose(wmw->hWave);
535 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
539 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
542 /**************************************************************************
543 * WAVE_mciStop [internal]
545 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
548 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
550 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
552 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
554 /* wait for playback thread (if any) to exit before processing further */
555 switch (wmw->dwStatus) {
558 case MCI_MODE_RECORD:
560 int oldStat = wmw->dwStatus;
561 wmw->dwStatus = MCI_MODE_NOT_READY;
562 if (oldStat == MCI_MODE_PAUSE)
563 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
565 while (wmw->dwStatus != MCI_MODE_STOP)
573 wmw->dwStatus = MCI_MODE_STOP;
575 if ((dwFlags & MCI_NOTIFY) && lpParms) {
576 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
577 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
583 /**************************************************************************
584 * WAVE_mciClose [internal]
586 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
589 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
591 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
593 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
595 if (wmw->dwStatus != MCI_MODE_STOP) {
596 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
601 if (wmw->nUseCount == 0) {
602 if (wmw->hFile != 0) {
603 mmioClose(wmw->hFile, 0);
608 /* That string got allocated in mciOpen because no filename was specified
609 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
610 * allocated by mciOpen, *NOT* the application.
612 if (wmw->bTemporaryFile)
614 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
615 wmw->openParms.lpstrElementName = NULL;
618 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
619 wmw->lpWaveFormat = NULL;
621 if ((dwFlags & MCI_NOTIFY) && lpParms) {
622 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
623 wmw->openParms.wDeviceID,
624 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
630 /**************************************************************************
631 * WAVE_mciPlayCallback [internal]
633 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
635 DWORD dwParam1, DWORD dwParam2)
637 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
644 InterlockedIncrement(&wmw->dwEventCount);
645 TRACE("Returning waveHdr=%lx\n", dwParam1);
646 SetEvent(wmw->hEvent);
649 ERR("Unknown uMsg=%d\n", uMsg);
653 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
656 ResetEvent(wmw->hEvent);
657 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
660 InterlockedIncrement(&wmw->dwEventCount);
662 WaitForSingleObject(wmw->hEvent, INFINITE);
666 /**************************************************************************
667 * WAVE_mciPlay [internal]
669 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
672 LONG bufsize, count, left;
674 LPWAVEHDR waveHdr = NULL;
675 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
678 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
680 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
681 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
683 /* FIXME : since there is no way to determine in which mode the device is
684 * open (recording/playback) automatically switch from a mode to another
689 WARN("cannot play on input device\n");
690 return MCIERR_NONAPPLICABLE_FUNCTION;
693 if (wmw->hFile == 0) {
694 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
695 return MCIERR_FILE_NOT_FOUND;
698 if (wmw->dwStatus == MCI_MODE_PAUSE) {
699 /* FIXME: parameters (start/end) in lpParams may not be used */
700 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
703 /** This function will be called again by a thread when async is used.
704 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
705 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
707 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
708 return MCIERR_INTERNAL;
711 wmw->dwStatus = MCI_MODE_PLAY;
713 if (!(dwFlags & MCI_WAIT)) {
714 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
715 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
719 if (lpParms && (dwFlags & MCI_FROM)) {
720 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
722 if (lpParms && (dwFlags & MCI_TO)) {
723 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
726 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
728 if (end <= wmw->dwPosition)
732 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
733 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
735 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
736 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
739 if (wmw->lpWaveFormat) {
740 switch (wmw->lpWaveFormat->wFormatTag) {
741 case WAVE_FORMAT_PCM:
742 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
743 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
744 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
745 wmw->lpWaveFormat->nAvgBytesPerSec,
746 wmw->lpWaveFormat->nSamplesPerSec *
747 wmw->lpWaveFormat->nBlockAlign);
748 wmw->lpWaveFormat->nAvgBytesPerSec =
749 wmw->lpWaveFormat->nSamplesPerSec *
750 wmw->lpWaveFormat->nBlockAlign;
756 TRACE("can't retrieve wave format %ld\n", dwRet);
761 /* go back to beginning of chunk plus the requested position */
762 /* FIXME: I'm not sure this is correct, notably because some data linked to
763 * the decompression state machine will not be correcly initialized.
764 * try it this way (other way would be to decompress from 0 up to dwPosition
765 * and to start sending to hWave when dwPosition is reached)
767 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
769 /* By default the device will be opened for output, the MCI_CUE function is there to
770 * change from output to input and back
772 /* FIXME: how to choose between several output channels ? here mapper is forced */
773 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
774 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
777 TRACE("Can't open low level audio device %ld\n", dwRet);
778 dwRet = MCIERR_DEVICE_OPEN;
783 /* make it so that 3 buffers per second are needed */
784 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
786 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
787 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
788 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
789 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
790 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
791 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
792 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
793 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
794 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
795 dwRet = MCIERR_INTERNAL;
800 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
801 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
802 wmw->dwEventCount = 1L; /* for first buffer */
804 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
806 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
807 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
808 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
809 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
812 /* count is always <= bufsize, so this is correct regarding the
813 * waveOutPrepareHeader function
815 waveHdr[whidx].dwBufferLength = count;
816 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
817 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
818 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
819 waveHdr[whidx].dwBytesRecorded);
820 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
822 wmw->dwPosition += count;
823 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
825 WAVE_mciPlayWaitDone(wmw);
829 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
831 /* just to get rid of some race conditions between play, stop and pause */
832 waveOutReset(wmw->hWave);
834 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
835 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
840 HeapFree(GetProcessHeap(), 0, waveHdr);
843 waveOutClose(wmw->hWave);
846 CloseHandle(wmw->hEvent);
848 if (lpParms && (dwFlags & MCI_NOTIFY)) {
849 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
850 wmw->openParms.wDeviceID,
851 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
854 wmw->dwStatus = MCI_MODE_STOP;
859 /**************************************************************************
860 * WAVE_mciPlayCallback [internal]
862 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
864 DWORD dwParam1, DWORD dwParam2)
866 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
867 LPWAVEHDR lpWaveHdr = NULL;
874 lpWaveHdr = (LPWAVEHDR) dwParam1;
876 InterlockedIncrement(&wmw->dwEventCount);
878 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
880 lpWaveHdr->dwFlags &= ~WHDR_DONE;
881 wmw->dwPosition += count;
882 wmw->dwRemaining -= count;
884 if (wmw->dwStatus == MCI_MODE_RECORD)
886 /* Only queue up another buffer if we are recording. We could receive this
887 message also when waveInReset() is called, since it notifies on all wave
888 buffers that are outstanding. Queueing up more sometimes causes waveInClose
890 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
891 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
894 SetEvent(wmw->hEvent);
897 ERR("Unknown uMsg=%d\n", uMsg);
901 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
904 ResetEvent(wmw->hEvent);
905 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
908 InterlockedIncrement(&wmw->dwEventCount);
910 WaitForSingleObject(wmw->hEvent, INFINITE);
914 /**************************************************************************
915 * WAVE_mciRecord [internal]
917 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
922 LPWAVEHDR waveHdr = NULL;
923 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
926 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
928 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
929 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
931 /* FIXME : since there is no way to determine in which mode the device is
932 * open (recording/playback) automatically switch from a mode to another
937 WARN("cannot record on output device\n");
938 return MCIERR_NONAPPLICABLE_FUNCTION;
941 if (wmw->dwStatus == MCI_MODE_PAUSE) {
942 /* FIXME: parameters (start/end) in lpParams may not be used */
943 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
946 /** This function will be called again by a thread when async is used.
947 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
948 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
950 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
951 return MCIERR_INTERNAL;
954 wmw->dwStatus = MCI_MODE_RECORD;
956 if (!(dwFlags & MCI_WAIT)) {
957 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
958 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
961 if (!wmw->lpWaveFormat)
964 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
967 FIXME("Should descend into data chunk. Please report.\n");
971 if (lpParms && (dwFlags & MCI_FROM)) {
972 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
975 if (lpParms && (dwFlags & MCI_TO)) {
976 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
979 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
981 if (end <= wmw->dwPosition)
986 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
987 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
989 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
990 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
992 /* go back to beginning of chunk plus the requested position */
993 /* FIXME: I'm not sure this is correct, notably because some data linked to
994 * the decompression state machine will not be correcly initialized.
995 * try it this way (other way would be to decompress from 0 up to dwPosition
996 * and to start sending to hWave when dwPosition is reached)
998 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1000 /* By default the device will be opened for output, the MCI_CUE function is there to
1001 * change from output to input and back
1003 /* FIXME: how to choose between several output channels ? here mapper is forced */
1004 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1005 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1008 TRACE("Can't open low level audio device %ld\n", dwRet);
1009 dwRet = MCIERR_DEVICE_OPEN;
1014 /* make it so that 3 buffers per second are needed */
1015 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1017 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1018 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1019 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1020 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1021 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1022 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1023 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1025 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1026 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1027 dwRet = MCIERR_INTERNAL;
1031 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1032 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1033 dwRet = MCIERR_INTERNAL;
1037 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1038 wmw->dwEventCount = 1L; /* for first buffer */
1040 wmw->dwRemaining = end - wmw->dwPosition;
1042 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1044 dwRet = waveInStart(wmw->hWave);
1046 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1047 WAVE_mciRecordWaitDone(wmw);
1050 /* needed so that the callback above won't add again the buffers returned by the reset */
1051 wmw->dwStatus = MCI_MODE_STOP;
1053 waveInReset(wmw->hWave);
1055 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1056 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1061 HeapFree(GetProcessHeap(), 0, waveHdr);
1064 waveInClose(wmw->hWave);
1067 CloseHandle(wmw->hEvent);
1069 /* need to update the size of the data chunk */
1070 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1071 TRACE("failed on ascend\n");
1074 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1075 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1076 wmw->openParms.wDeviceID,
1077 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1080 wmw->dwStatus = MCI_MODE_STOP;
1086 /**************************************************************************
1087 * WAVE_mciPause [internal]
1089 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1092 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1094 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1096 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1097 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1099 if (wmw->dwStatus == MCI_MODE_PLAY) {
1100 wmw->dwStatus = MCI_MODE_PAUSE;
1103 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1104 else dwRet = waveOutPause(wmw->hWave);
1106 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1109 /**************************************************************************
1110 * WAVE_mciResume [internal]
1112 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1114 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1117 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1119 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1121 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1122 wmw->dwStatus = MCI_MODE_PLAY;
1125 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1126 else dwRet = waveOutRestart(wmw->hWave);
1127 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1130 /**************************************************************************
1131 * WAVE_mciSeek [internal]
1133 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1136 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1138 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1140 if (lpParms == NULL) {
1141 ret = MCIERR_NULL_PARAMETER_BLOCK;
1142 } else if (wmw == NULL) {
1143 ret = MCIERR_INVALID_DEVICE_ID;
1145 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1147 if (dwFlags & MCI_SEEK_TO_START) {
1148 wmw->dwPosition = 0;
1149 } else if (dwFlags & MCI_SEEK_TO_END) {
1150 wmw->dwPosition = wmw->ckWaveData.cksize;
1151 } else if (dwFlags & MCI_TO) {
1152 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1154 WARN("dwFlag doesn't tell where to seek to...\n");
1155 return MCIERR_MISSING_PARAMETER;
1158 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1160 if (dwFlags & MCI_NOTIFY) {
1161 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1162 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1168 /**************************************************************************
1169 * WAVE_mciSet [internal]
1171 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1173 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1175 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1177 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1178 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1180 if (dwFlags & MCI_SET_TIME_FORMAT) {
1181 switch (lpParms->dwTimeFormat) {
1182 case MCI_FORMAT_MILLISECONDS:
1183 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1184 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1186 case MCI_FORMAT_BYTES:
1187 TRACE("MCI_FORMAT_BYTES !\n");
1188 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1190 case MCI_FORMAT_SAMPLES:
1191 TRACE("MCI_FORMAT_SAMPLES !\n");
1192 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1195 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1196 return MCIERR_BAD_TIME_FORMAT;
1199 if (dwFlags & MCI_SET_VIDEO) {
1200 TRACE("No support for video !\n");
1201 return MCIERR_UNSUPPORTED_FUNCTION;
1203 if (dwFlags & MCI_SET_DOOR_OPEN) {
1204 TRACE("No support for door open !\n");
1205 return MCIERR_UNSUPPORTED_FUNCTION;
1207 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1208 TRACE("No support for door close !\n");
1209 return MCIERR_UNSUPPORTED_FUNCTION;
1211 if (dwFlags & MCI_SET_AUDIO) {
1212 if (dwFlags & MCI_SET_ON) {
1213 TRACE("MCI_SET_ON audio !\n");
1214 } else if (dwFlags & MCI_SET_OFF) {
1215 TRACE("MCI_SET_OFF audio !\n");
1217 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1218 return MCIERR_BAD_INTEGER;
1221 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1222 TRACE("MCI_SET_AUDIO_ALL !\n");
1223 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1224 TRACE("MCI_SET_AUDIO_LEFT !\n");
1225 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1226 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1228 if (dwFlags & MCI_WAVE_INPUT)
1229 TRACE("MCI_WAVE_INPUT !\n");
1230 if (dwFlags & MCI_WAVE_OUTPUT)
1231 TRACE("MCI_WAVE_OUTPUT !\n");
1232 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1233 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1234 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1235 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1236 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1237 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1238 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1240 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1241 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1242 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1244 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1245 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1246 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1248 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1249 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1250 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1252 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1253 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1254 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1256 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1257 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1258 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1263 /**************************************************************************
1264 * WAVE_mciSave [internal]
1266 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1268 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1269 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1270 WPARAM wparam = MCI_NOTIFY_FAILURE;
1272 TRACE("%d, %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_WAIT)
1278 FIXME("MCI_WAIT not implemented\n");
1281 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1282 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1285 ret = mmioClose(wmw->hFile, 0);
1288 If the destination file already exists, it has to be overwritten. (Behaviour
1289 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1290 my applications. We are making use of mmioRename, which WILL NOT overwrite
1291 the destination file (which is what Windows does, also verified in Win2K)
1292 So, lets delete the destination file before calling mmioRename. If the
1293 destination file DOESN'T exist, the delete will fail silently. Let's also be
1294 careful not to lose our previous error code.
1296 tmpRet = GetLastError();
1297 DeleteFileA (lpParms->lpfilename);
1298 SetLastError(tmpRet);
1300 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1301 ret = ERROR_SUCCESS;
1304 if (dwFlags & MCI_NOTIFY) {
1305 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1307 mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback),
1308 wmw->openParms.wDeviceID, wparam);
1314 /**************************************************************************
1315 * WAVE_mciStatus [internal]
1317 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1319 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1322 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1323 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1324 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1326 if (dwFlags & MCI_STATUS_ITEM) {
1327 switch (lpParms->dwItem) {
1328 case MCI_STATUS_CURRENT_TRACK:
1329 lpParms->dwReturn = 1;
1330 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1332 case MCI_STATUS_LENGTH:
1334 lpParms->dwReturn = 0;
1335 return MCIERR_UNSUPPORTED_FUNCTION;
1337 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1338 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1339 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1341 case MCI_STATUS_MODE:
1342 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1343 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1344 ret = MCI_RESOURCE_RETURNED;
1346 case MCI_STATUS_MEDIA_PRESENT:
1347 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1348 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1349 ret = MCI_RESOURCE_RETURNED;
1351 case MCI_STATUS_NUMBER_OF_TRACKS:
1352 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1353 lpParms->dwReturn = 1;
1354 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1356 case MCI_STATUS_POSITION:
1358 lpParms->dwReturn = 0;
1359 return MCIERR_UNSUPPORTED_FUNCTION;
1361 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1362 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1363 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1365 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1366 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1368 case MCI_STATUS_READY:
1369 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1370 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1371 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1372 ret = MCI_RESOURCE_RETURNED;
1374 case MCI_STATUS_TIME_FORMAT:
1375 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1376 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1377 ret = MCI_RESOURCE_RETURNED;
1379 case MCI_WAVE_INPUT:
1380 TRACE("MCI_WAVE_INPUT !\n");
1381 lpParms->dwReturn = 0;
1382 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1384 case MCI_WAVE_OUTPUT:
1385 TRACE("MCI_WAVE_OUTPUT !\n");
1388 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1389 lpParms->dwReturn = id;
1391 lpParms->dwReturn = 0;
1392 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1396 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1398 lpParms->dwReturn = 0;
1399 return MCIERR_UNSUPPORTED_FUNCTION;
1401 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1402 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1404 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1406 lpParms->dwReturn = 0;
1407 return MCIERR_UNSUPPORTED_FUNCTION;
1409 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1410 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1412 case MCI_WAVE_STATUS_BLOCKALIGN:
1414 lpParms->dwReturn = 0;
1415 return MCIERR_UNSUPPORTED_FUNCTION;
1417 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1418 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1420 case MCI_WAVE_STATUS_CHANNELS:
1422 lpParms->dwReturn = 0;
1423 return MCIERR_UNSUPPORTED_FUNCTION;
1425 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1426 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1428 case MCI_WAVE_STATUS_FORMATTAG:
1430 lpParms->dwReturn = 0;
1431 return MCIERR_UNSUPPORTED_FUNCTION;
1433 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1434 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1436 case MCI_WAVE_STATUS_LEVEL:
1437 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1438 lpParms->dwReturn = 0xAAAA5555;
1440 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1442 lpParms->dwReturn = 0;
1443 return MCIERR_UNSUPPORTED_FUNCTION;
1445 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1446 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1449 WARN("unknown command %08lX !\n", lpParms->dwItem);
1450 return MCIERR_UNRECOGNIZED_COMMAND;
1453 if (dwFlags & MCI_NOTIFY) {
1454 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1455 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1460 /**************************************************************************
1461 * WAVE_mciGetDevCaps [internal]
1463 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1464 LPMCI_GETDEVCAPS_PARMS lpParms)
1466 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1469 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1471 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1472 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1474 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1475 switch(lpParms->dwItem) {
1476 case MCI_GETDEVCAPS_DEVICE_TYPE:
1477 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1478 ret = MCI_RESOURCE_RETURNED;
1480 case MCI_GETDEVCAPS_HAS_AUDIO:
1481 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1482 ret = MCI_RESOURCE_RETURNED;
1484 case MCI_GETDEVCAPS_HAS_VIDEO:
1485 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1486 ret = MCI_RESOURCE_RETURNED;
1488 case MCI_GETDEVCAPS_USES_FILES:
1489 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1490 ret = MCI_RESOURCE_RETURNED;
1492 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1493 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1494 ret = MCI_RESOURCE_RETURNED;
1496 case MCI_GETDEVCAPS_CAN_RECORD:
1497 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1498 ret = MCI_RESOURCE_RETURNED;
1500 case MCI_GETDEVCAPS_CAN_EJECT:
1501 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1502 ret = MCI_RESOURCE_RETURNED;
1504 case MCI_GETDEVCAPS_CAN_PLAY:
1505 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1506 ret = MCI_RESOURCE_RETURNED;
1508 case MCI_GETDEVCAPS_CAN_SAVE:
1509 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1510 ret = MCI_RESOURCE_RETURNED;
1512 case MCI_WAVE_GETDEVCAPS_INPUTS:
1513 lpParms->dwReturn = 1;
1515 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1516 lpParms->dwReturn = 1;
1519 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1520 return MCIERR_UNRECOGNIZED_COMMAND;
1523 WARN("No GetDevCaps-Item !\n");
1524 return MCIERR_UNRECOGNIZED_COMMAND;
1529 /**************************************************************************
1530 * WAVE_mciInfo [internal]
1532 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1536 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1538 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1540 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1541 ret = MCIERR_NULL_PARAMETER_BLOCK;
1542 } else if (wmw == NULL) {
1543 ret = MCIERR_INVALID_DEVICE_ID;
1545 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1547 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1548 case MCI_INFO_PRODUCT:
1549 str = "Wine's audio player";
1552 str = wmw->openParms.lpstrElementName;
1554 case MCI_WAVE_INPUT:
1555 str = "Wine Wave In";
1557 case MCI_WAVE_OUTPUT:
1558 str = "Wine Wave Out";
1561 WARN("Don't know this info command (%lu)\n", dwFlags);
1562 ret = MCIERR_UNRECOGNIZED_COMMAND;
1566 if (strlen(str) + 1 > lpParms->dwRetSize) {
1567 ret = MCIERR_PARAM_OVERFLOW;
1569 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1572 lpParms->lpstrReturn[0] = 0;
1578 /**************************************************************************
1579 * DriverProc (MCIWAVE.@)
1581 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1582 DWORD dwParam1, DWORD dwParam2)
1584 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1585 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1588 case DRV_LOAD: return 1;
1589 case DRV_FREE: return 1;
1590 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1591 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1592 case DRV_ENABLE: return 1;
1593 case DRV_DISABLE: return 1;
1594 case DRV_QUERYCONFIGURE: return 1;
1595 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1596 case DRV_INSTALL: return DRVCNF_RESTART;
1597 case DRV_REMOVE: return DRVCNF_RESTART;
1598 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1599 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1600 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1601 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1602 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1603 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1604 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1605 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1606 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1607 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1608 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1609 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1610 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1611 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1612 /* commands that should be supported */
1627 FIXME("Unsupported yet command [%lu]\n", wMsg);
1630 TRACE("Unsupported command [%lu]\n", wMsg);
1632 /* option which can be silenced */
1637 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1640 FIXME("is probably wrong msg [%lu]\n", wMsg);
1641 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1643 return MCIERR_UNRECOGNIZED_COMMAND;