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)
137 if (modp == NULL) return 0xFFFFFFFF;
139 wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
144 wmw->wDevID = modp->wDeviceID;
145 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
146 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
147 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
149 wmw->wfxRef.wFormatTag = WAVE_FORMAT_PCM;
150 wmw->wfxRef.nChannels = 1; /* MONO */
151 wmw->wfxRef.nSamplesPerSec = 11025;
152 wmw->wfxRef.nAvgBytesPerSec = 11025;
153 wmw->wfxRef.nBlockAlign = 1;
154 wmw->wfxRef.wBitsPerSample = 8;
155 wmw->wfxRef.cbSize = 0; /* don't care */
157 return modp->wDeviceID;
160 /**************************************************************************
161 * MCIWAVE_drvClose [internal]
163 static DWORD WAVE_drvClose(DWORD dwDevID)
165 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
168 HeapFree(GetProcessHeap(), 0, wmw);
169 mciSetDriverData(dwDevID, 0);
172 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
175 /**************************************************************************
176 * WAVE_mciGetOpenDev [internal]
178 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
180 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
182 if (wmw == NULL || wmw->nUseCount == 0) {
183 WARN("Invalid wDevID=%u\n", wDevID);
189 /**************************************************************************
190 * WAVE_ConvertByteToTimeFormat [internal]
192 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
196 switch (wmw->dwMciTimeFormat) {
197 case MCI_FORMAT_MILLISECONDS:
198 ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
200 case MCI_FORMAT_BYTES:
203 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
204 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
207 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
209 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
214 /**************************************************************************
215 * WAVE_ConvertTimeFormatToByte [internal]
217 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
221 switch (wmw->dwMciTimeFormat) {
222 case MCI_FORMAT_MILLISECONDS:
223 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
225 case MCI_FORMAT_BYTES:
228 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
229 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
232 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
234 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
238 /**************************************************************************
239 * WAVE_mciReadFmt [internal]
241 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
246 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
247 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
248 return MCIERR_INVALID_FILE;
249 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
250 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
252 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
253 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
254 if (r < sizeof(WAVEFORMAT))
255 return MCIERR_INVALID_FILE;
257 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
258 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
259 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
260 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
261 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
262 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
263 if (r >= (long)sizeof(WAVEFORMATEX))
264 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
266 mmioAscend(wmw->hFile, &mmckInfo, 0);
267 wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
268 if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
269 TRACE("can't find data chunk\n");
270 return MCIERR_INVALID_FILE;
272 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
273 (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
274 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
275 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
280 /**************************************************************************
281 * WAVE_mciCreateRIFFSkeleton [internal]
283 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
285 MMCKINFO ckWaveFormat;
287 LPMMCKINFO lpckRIFF = &(wmw->ckMainRIFF);
288 LPMMCKINFO lpckWaveData = &(wmw->ckWaveData);
289 LPWAVEFORMATEX lpWaveFormat = wmw->lpWaveFormat;
292 HMMIO hmmio = wmw->hFile;
294 lpckRIFF->ckid = FOURCC_RIFF;
295 lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
296 lpckRIFF->cksize = 0;
298 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckRIFF, MMIO_CREATERIFF))
301 ckWaveFormat.fccType = 0;
302 ckWaveFormat.ckid = mmioFOURCC('f', 'm', 't', ' ');
303 ckWaveFormat.cksize = 16;
307 /* FIXME: for non PCM formats, the size of the waveFormat has to be
310 lpWaveFormat = wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpWaveFormat));
312 memcpy(lpWaveFormat, &wmw->wfxRef, sizeof(wmw->wfxRef));
315 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, &ckWaveFormat, 0))
318 /* only the first 16 bytes are serialized */
319 /* wrong... for non PCM, the whole waveFormat is stored
321 if (-1 == mmioWrite(hmmio, (HPCSTR) lpWaveFormat, 16))
324 if (MMSYSERR_NOERROR != mmioAscend(hmmio, &ckWaveFormat, 0))
327 lpckWaveData->cksize = 0;
328 lpckWaveData->fccType = 0;
329 lpckWaveData->ckid = mmioFOURCC('d', 'a', 't', 'a');
331 /* create data chunk */
332 if (MMSYSERR_NOERROR != mmioCreateChunk(hmmio, lpckWaveData, 0))
338 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
339 return MCIERR_INVALID_FILE;
342 /**************************************************************************
343 * WAVE_mciOpen [internal]
345 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
348 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
349 CHAR* pszTmpFileName = 0;
351 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
352 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
353 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
355 if (dwFlags & MCI_OPEN_SHAREABLE)
356 return MCIERR_HARDWARE;
358 if (wmw->nUseCount > 0) {
359 /* The driver is already opened on this channel
360 * Wave driver cannot be shared
362 return MCIERR_DEVICE_OPEN;
369 wmw->dwStatus = MCI_MODE_NOT_READY;
371 TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
373 if (dwFlags & MCI_OPEN_ELEMENT) {
374 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
375 /* could it be that (DWORD)lpOpenParms->lpstrElementName
376 * contains the hFile value ?
378 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
380 if (strlen(lpOpenParms->lpstrElementName) > 0) {
381 lpOpenParms->lpstrElementName = lpOpenParms->lpstrElementName;
383 /* FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
384 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpOpenParms->lpstrElementName);
386 if (lpOpenParms->lpstrElementName && (strlen(lpOpenParms->lpstrElementName) > 0)) {
387 wmw->hFile = mmioOpenA((LPSTR)lpOpenParms->lpstrElementName, NULL,
388 MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READWRITE);
390 if (wmw->hFile == 0) {
391 WARN("can't find file='%s' !\n", lpOpenParms->lpstrElementName);
392 dwRet = MCIERR_FILE_NOT_FOUND;
396 LPMMCKINFO lpckMainRIFF = &wmw->ckMainRIFF;
398 /* make sure we're are the beginning of the file */
399 mmioSeek(wmw->hFile, 0, SEEK_SET);
401 /* first reading of this file. read the waveformat chunk */
402 if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
403 dwRet = MCIERR_INVALID_FILE;
405 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
406 (LPSTR)&(lpckMainRIFF->ckid),
407 (LPSTR) &(lpckMainRIFF->fccType),
408 (lpckMainRIFF->cksize));
410 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
411 lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
412 dwRet = MCIERR_INVALID_FILE;
414 dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
424 CHAR szTmpPath[MAX_PATH];
425 CHAR szPrefix[4] = "TMP\0";
427 pszTmpFileName = HeapAlloc(GetProcessHeap(),
429 MAX_PATH * sizeof(*pszTmpFileName));
431 if (!GetTempPathA(sizeof(szTmpPath), szTmpPath)) {
432 WARN("can't retrieve temp path!\n");
433 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
434 return MCIERR_FILE_NOT_FOUND;
437 if (!GetTempFileNameA(szTmpPath, szPrefix, 0, pszTmpFileName)) {
438 WARN("can't retrieve temp file name!\n");
439 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
440 return MCIERR_FILE_NOT_FOUND;
443 wmw->bTemporaryFile = TRUE;
445 TRACE("MCI_OPEN_ELEMENT '%s' !\n", pszTmpFileName);
447 if (pszTmpFileName && (strlen(pszTmpFileName) > 0)) {
449 wmw->hFile = mmioOpenA(pszTmpFileName, NULL,
450 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
452 if (wmw->hFile == 0) {
453 /* temporary file could not be created. clean filename. */
454 HeapFree(GetProcessHeap(), 0, pszTmpFileName);
455 WARN("can't create file='%s' !\n", pszTmpFileName);
456 dwRet = MCIERR_FILE_NOT_FOUND;
463 TRACE("hFile=%u\n", wmw->hFile);
465 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
467 if (wmw->bTemporaryFile == TRUE)
469 /* Additional openParms is temporary file's name */
470 wmw->openParms.lpstrElementName = pszTmpFileName;
474 if (wmw->lpWaveFormat) {
475 switch (wmw->lpWaveFormat->wFormatTag) {
476 case WAVE_FORMAT_PCM:
477 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
478 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
479 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
480 wmw->lpWaveFormat->nAvgBytesPerSec,
481 wmw->lpWaveFormat->nSamplesPerSec *
482 wmw->lpWaveFormat->nBlockAlign);
483 wmw->lpWaveFormat->nAvgBytesPerSec =
484 wmw->lpWaveFormat->nSamplesPerSec *
485 wmw->lpWaveFormat->nBlockAlign;
492 wmw->dwStatus = MCI_MODE_STOP;
496 mmioClose(wmw->hFile, 0);
502 /**************************************************************************
503 * WAVE_mciCue [internal]
505 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
510 This routine is far from complete. At the moment only a check is done on the
511 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
514 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
519 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
521 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
523 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
525 /* always close elements ? */
526 if (wmw->hFile != 0) {
527 mmioClose(wmw->hFile, 0);
531 dwRet = MMSYSERR_NOERROR; /* assume success */
533 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
534 dwRet = waveOutClose(wmw->hWave);
535 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
537 } else if (wmw->fInput) {
538 dwRet = waveInClose(wmw->hWave);
539 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
543 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
546 /**************************************************************************
547 * WAVE_mciStop [internal]
549 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
552 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
554 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
556 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
558 /* wait for playback thread (if any) to exit before processing further */
559 switch (wmw->dwStatus) {
562 case MCI_MODE_RECORD:
564 int oldStat = wmw->dwStatus;
565 wmw->dwStatus = MCI_MODE_NOT_READY;
566 if (oldStat == MCI_MODE_PAUSE)
567 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
569 while (wmw->dwStatus != MCI_MODE_STOP)
577 wmw->dwStatus = MCI_MODE_STOP;
579 if ((dwFlags & MCI_NOTIFY) && lpParms) {
580 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
581 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
587 /**************************************************************************
588 * WAVE_mciClose [internal]
590 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
593 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
595 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
597 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
599 if (wmw->dwStatus != MCI_MODE_STOP) {
600 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
605 if (wmw->nUseCount == 0) {
606 if (wmw->hFile != 0) {
607 mmioClose(wmw->hFile, 0);
612 /* That string got allocated in mciOpen because no filename was specified
613 * in MCI_OPEN_PARMS stucture. Cast-away const from string since it was
614 * allocated by mciOpen, *NOT* the application.
616 if (wmw->bTemporaryFile)
618 HeapFree(GetProcessHeap(), 0, (CHAR*) wmw->openParms.lpstrElementName);
619 wmw->openParms.lpstrElementName = NULL;
622 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
623 wmw->lpWaveFormat = NULL;
625 if ((dwFlags & MCI_NOTIFY) && lpParms) {
626 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
627 wmw->openParms.wDeviceID,
628 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
634 /**************************************************************************
635 * WAVE_mciPlayCallback [internal]
637 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
639 DWORD dwParam1, DWORD dwParam2)
641 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
648 InterlockedIncrement(&wmw->dwEventCount);
649 TRACE("Returning waveHdr=%lx\n", dwParam1);
650 SetEvent(wmw->hEvent);
653 ERR("Unknown uMsg=%d\n", uMsg);
657 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
660 ResetEvent(wmw->hEvent);
661 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
664 InterlockedIncrement(&wmw->dwEventCount);
666 WaitForSingleObject(wmw->hEvent, INFINITE);
670 /**************************************************************************
671 * WAVE_mciPlay [internal]
673 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
676 LONG bufsize, count, left;
678 LPWAVEHDR waveHdr = NULL;
679 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
682 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
684 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
685 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
687 /* FIXME : since there is no way to determine in which mode the device is
688 * open (recording/playback) automatically switch from a mode to another
693 WARN("cannot play on input device\n");
694 return MCIERR_NONAPPLICABLE_FUNCTION;
697 if (wmw->hFile == 0) {
698 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
699 return MCIERR_FILE_NOT_FOUND;
702 if (wmw->dwStatus == MCI_MODE_PAUSE) {
703 /* FIXME: parameters (start/end) in lpParams may not be used */
704 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
707 /** This function will be called again by a thread when async is used.
708 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
709 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
711 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
712 return MCIERR_INTERNAL;
715 wmw->dwStatus = MCI_MODE_PLAY;
717 if (!(dwFlags & MCI_WAIT)) {
718 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
719 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
723 if (lpParms && (dwFlags & MCI_FROM)) {
724 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
726 if (lpParms && (dwFlags & MCI_TO)) {
727 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
730 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
732 if (end <= wmw->dwPosition)
736 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
737 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
739 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
740 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
743 if (wmw->lpWaveFormat) {
744 switch (wmw->lpWaveFormat->wFormatTag) {
745 case WAVE_FORMAT_PCM:
746 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
747 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
748 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
749 wmw->lpWaveFormat->nAvgBytesPerSec,
750 wmw->lpWaveFormat->nSamplesPerSec *
751 wmw->lpWaveFormat->nBlockAlign);
752 wmw->lpWaveFormat->nAvgBytesPerSec =
753 wmw->lpWaveFormat->nSamplesPerSec *
754 wmw->lpWaveFormat->nBlockAlign;
760 TRACE("can't retrieve wave format %ld\n", dwRet);
765 /* go back to beginning of chunk plus the requested position */
766 /* FIXME: I'm not sure this is correct, notably because some data linked to
767 * the decompression state machine will not be correcly initialized.
768 * try it this way (other way would be to decompress from 0 up to dwPosition
769 * and to start sending to hWave when dwPosition is reached)
771 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
773 /* By default the device will be opened for output, the MCI_CUE function is there to
774 * change from output to input and back
776 /* FIXME: how to choose between several output channels ? here mapper is forced */
777 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
778 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
781 TRACE("Can't open low level audio device %ld\n", dwRet);
782 dwRet = MCIERR_DEVICE_OPEN;
787 /* make it so that 3 buffers per second are needed */
788 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
790 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
791 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
792 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
793 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
794 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
795 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
796 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
797 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
798 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
799 dwRet = MCIERR_INTERNAL;
804 left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
805 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
806 wmw->dwEventCount = 1L; /* for first buffer */
808 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
810 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
811 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
812 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
813 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
816 /* count is always <= bufsize, so this is correct regarding the
817 * waveOutPrepareHeader function
819 waveHdr[whidx].dwBufferLength = count;
820 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
821 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
822 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
823 waveHdr[whidx].dwBytesRecorded);
824 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
826 wmw->dwPosition += count;
827 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
829 WAVE_mciPlayWaitDone(wmw);
833 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
835 /* just to get rid of some race conditions between play, stop and pause */
836 waveOutReset(wmw->hWave);
838 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
839 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
844 HeapFree(GetProcessHeap(), 0, waveHdr);
847 waveOutClose(wmw->hWave);
850 CloseHandle(wmw->hEvent);
852 if (lpParms && (dwFlags & MCI_NOTIFY)) {
853 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
854 wmw->openParms.wDeviceID,
855 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
858 wmw->dwStatus = MCI_MODE_STOP;
863 /**************************************************************************
864 * WAVE_mciPlayCallback [internal]
866 static void CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
868 DWORD dwParam1, DWORD dwParam2)
870 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
871 LPWAVEHDR lpWaveHdr = NULL;
878 lpWaveHdr = (LPWAVEHDR) dwParam1;
880 InterlockedIncrement(&wmw->dwEventCount);
882 count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
884 lpWaveHdr->dwFlags &= ~WHDR_DONE;
885 wmw->dwPosition += count;
886 wmw->dwRemaining -= count;
888 if (wmw->dwStatus == MCI_MODE_RECORD)
890 /* Only queue up another buffer if we are recording. We could receive this
891 message also when waveInReset() is called, since it notifies on all wave
892 buffers that are outstanding. Queueing up more sometimes causes waveInClose
894 waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
895 TRACE("after mmioWrite dwPosition=%lu\n", wmw->dwPosition);
898 SetEvent(wmw->hEvent);
901 ERR("Unknown uMsg=%d\n", uMsg);
905 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
908 ResetEvent(wmw->hEvent);
909 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
912 InterlockedIncrement(&wmw->dwEventCount);
914 WaitForSingleObject(wmw->hEvent, INFINITE);
918 /**************************************************************************
919 * WAVE_mciRecord [internal]
921 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
926 LPWAVEHDR waveHdr = NULL;
927 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
930 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
932 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
933 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
935 /* FIXME : since there is no way to determine in which mode the device is
936 * open (recording/playback) automatically switch from a mode to another
941 WARN("cannot record on output device\n");
942 return MCIERR_NONAPPLICABLE_FUNCTION;
945 if (wmw->dwStatus == MCI_MODE_PAUSE) {
946 /* FIXME: parameters (start/end) in lpParams may not be used */
947 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
950 /** This function will be called again by a thread when async is used.
951 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
952 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
954 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
955 return MCIERR_INTERNAL;
958 wmw->dwStatus = MCI_MODE_RECORD;
960 if (!(dwFlags & MCI_WAIT)) {
961 return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
962 (DWORD)lpParms, sizeof(MCI_RECORD_PARMS));
965 if (!wmw->lpWaveFormat)
968 dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
971 FIXME("Should descend into data chunk. Please report.\n");
975 if (lpParms && (dwFlags & MCI_FROM)) {
976 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
979 if (lpParms && (dwFlags & MCI_TO)) {
980 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
983 TRACE("Recording from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
985 if (end <= wmw->dwPosition)
990 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
991 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
993 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
994 wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
996 /* go back to beginning of chunk plus the requested position */
997 /* FIXME: I'm not sure this is correct, notably because some data linked to
998 * the decompression state machine will not be correcly initialized.
999 * try it this way (other way would be to decompress from 0 up to dwPosition
1000 * and to start sending to hWave when dwPosition is reached)
1002 mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1004 /* By default the device will be opened for output, the MCI_CUE function is there to
1005 * change from output to input and back
1007 /* FIXME: how to choose between several output channels ? here mapper is forced */
1008 dwRet = waveInOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1009 (DWORD)WAVE_mciRecordCallback, (DWORD)wmw, CALLBACK_FUNCTION);
1012 TRACE("Can't open low level audio device %ld\n", dwRet);
1013 dwRet = MCIERR_DEVICE_OPEN;
1018 /* make it so that 3 buffers per second are needed */
1019 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1021 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1022 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1023 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1024 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
1025 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
1026 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
1027 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1029 if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1030 waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1031 dwRet = MCIERR_INTERNAL;
1035 if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1036 waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1037 dwRet = MCIERR_INTERNAL;
1041 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1042 wmw->dwEventCount = 1L; /* for first buffer */
1044 wmw->dwRemaining = end - wmw->dwPosition;
1046 TRACE("Recording (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, wmw->dwRemaining);
1048 dwRet = waveInStart(wmw->hWave);
1050 while ( wmw->dwRemaining > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1051 WAVE_mciRecordWaitDone(wmw);
1054 /* needed so that the callback above won't add again the buffers returned by the reset */
1055 wmw->dwStatus = MCI_MODE_STOP;
1057 waveInReset(wmw->hWave);
1059 waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1060 waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1065 HeapFree(GetProcessHeap(), 0, waveHdr);
1068 waveInClose(wmw->hWave);
1071 CloseHandle(wmw->hEvent);
1073 /* need to update the size of the data chunk */
1074 if (mmioAscend(wmw->hFile, &wmw->ckWaveData, 0) != MMSYSERR_NOERROR) {
1075 TRACE("failed on ascend\n");
1078 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1079 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1080 wmw->openParms.wDeviceID,
1081 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1084 wmw->dwStatus = MCI_MODE_STOP;
1090 /**************************************************************************
1091 * WAVE_mciPause [internal]
1093 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1096 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1098 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1100 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1101 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1103 if (wmw->dwStatus == MCI_MODE_PLAY) {
1104 wmw->dwStatus = MCI_MODE_PAUSE;
1107 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
1108 else dwRet = waveOutPause(wmw->hWave);
1110 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1113 /**************************************************************************
1114 * WAVE_mciResume [internal]
1116 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1118 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1121 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1123 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1125 if (wmw->dwStatus == MCI_MODE_PAUSE) {
1126 wmw->dwStatus = MCI_MODE_PLAY;
1129 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
1130 else dwRet = waveOutRestart(wmw->hWave);
1131 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
1134 /**************************************************************************
1135 * WAVE_mciSeek [internal]
1137 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1140 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1142 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1144 if (lpParms == NULL) {
1145 ret = MCIERR_NULL_PARAMETER_BLOCK;
1146 } else if (wmw == NULL) {
1147 ret = MCIERR_INVALID_DEVICE_ID;
1149 WAVE_mciStop(wDevID, MCI_WAIT, 0);
1151 if (dwFlags & MCI_SEEK_TO_START) {
1152 wmw->dwPosition = 0;
1153 } else if (dwFlags & MCI_SEEK_TO_END) {
1154 wmw->dwPosition = wmw->ckWaveData.cksize;
1155 } else if (dwFlags & MCI_TO) {
1156 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1158 WARN("dwFlag doesn't tell where to seek to...\n");
1159 return MCIERR_MISSING_PARAMETER;
1162 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
1164 if (dwFlags & MCI_NOTIFY) {
1165 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1166 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1172 /**************************************************************************
1173 * WAVE_mciSet [internal]
1175 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1177 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1179 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1181 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1182 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1184 if (dwFlags & MCI_SET_TIME_FORMAT) {
1185 switch (lpParms->dwTimeFormat) {
1186 case MCI_FORMAT_MILLISECONDS:
1187 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1188 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1190 case MCI_FORMAT_BYTES:
1191 TRACE("MCI_FORMAT_BYTES !\n");
1192 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1194 case MCI_FORMAT_SAMPLES:
1195 TRACE("MCI_FORMAT_SAMPLES !\n");
1196 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1199 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
1200 return MCIERR_BAD_TIME_FORMAT;
1203 if (dwFlags & MCI_SET_VIDEO) {
1204 TRACE("No support for video !\n");
1205 return MCIERR_UNSUPPORTED_FUNCTION;
1207 if (dwFlags & MCI_SET_DOOR_OPEN) {
1208 TRACE("No support for door open !\n");
1209 return MCIERR_UNSUPPORTED_FUNCTION;
1211 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1212 TRACE("No support for door close !\n");
1213 return MCIERR_UNSUPPORTED_FUNCTION;
1215 if (dwFlags & MCI_SET_AUDIO) {
1216 if (dwFlags & MCI_SET_ON) {
1217 TRACE("MCI_SET_ON audio !\n");
1218 } else if (dwFlags & MCI_SET_OFF) {
1219 TRACE("MCI_SET_OFF audio !\n");
1221 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1222 return MCIERR_BAD_INTEGER;
1225 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
1226 TRACE("MCI_SET_AUDIO_ALL !\n");
1227 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
1228 TRACE("MCI_SET_AUDIO_LEFT !\n");
1229 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
1230 TRACE("MCI_SET_AUDIO_RIGHT !\n");
1232 if (dwFlags & MCI_WAVE_INPUT)
1233 TRACE("MCI_WAVE_INPUT !\n");
1234 if (dwFlags & MCI_WAVE_OUTPUT)
1235 TRACE("MCI_WAVE_OUTPUT !\n");
1236 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1237 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1238 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1239 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1240 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1241 wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1242 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %ld\n", wmw->wfxRef.nAvgBytesPerSec);
1244 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1245 wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
1246 TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1248 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1249 wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
1250 TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1252 if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1253 wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
1254 TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1256 if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1257 wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
1258 TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
1260 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1261 wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1262 TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %ld\n", wmw->wfxRef.nSamplesPerSec);
1267 /**************************************************************************
1268 * WAVE_mciSave [internal]
1270 static DWORD WAVE_mciSave(UINT wDevID, DWORD dwFlags, LPMCI_SAVE_PARMS lpParms)
1272 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1273 DWORD ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1274 WPARAM wparam = MCI_NOTIFY_FAILURE;
1276 TRACE("%d, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1277 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1278 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1280 if (dwFlags & MCI_WAIT)
1282 FIXME("MCI_WAIT not implemented\n");
1285 ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1286 ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1289 ret = mmioClose(wmw->hFile, 0);
1292 If the destination file already exists, it has to be overwritten. (Behaviour
1293 verified in Windows (2000)). If it doesn't overwrite, it is breaking one of
1294 my applications. We are making use of mmioRename, which WILL NOT overwrite
1295 the destination file (which is what Windows does, also verified in Win2K)
1296 So, lets delete the destination file before calling mmioRename. If the
1297 destination file DOESN'T exist, the delete will fail silently. Let's also be
1298 careful not to lose our previous error code.
1300 tmpRet = GetLastError();
1301 DeleteFileA (lpParms->lpfilename);
1302 SetLastError(tmpRet);
1304 if (0 == mmioRenameA(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
1305 ret = ERROR_SUCCESS;
1308 if (dwFlags & MCI_NOTIFY) {
1309 if (ret == ERROR_SUCCESS) wparam = MCI_NOTIFY_SUCCESSFUL;
1311 mciDriverNotify( (HWND) LOWORD(lpParms->dwCallback),
1312 wmw->openParms.wDeviceID, wparam);
1318 /**************************************************************************
1319 * WAVE_mciStatus [internal]
1321 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1323 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1326 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1327 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1328 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1330 if (dwFlags & MCI_STATUS_ITEM) {
1331 switch (lpParms->dwItem) {
1332 case MCI_STATUS_CURRENT_TRACK:
1333 lpParms->dwReturn = 1;
1334 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1336 case MCI_STATUS_LENGTH:
1338 lpParms->dwReturn = 0;
1339 return MCIERR_UNSUPPORTED_FUNCTION;
1341 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1342 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1343 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1345 case MCI_STATUS_MODE:
1346 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1347 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1348 ret = MCI_RESOURCE_RETURNED;
1350 case MCI_STATUS_MEDIA_PRESENT:
1351 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1352 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1353 ret = MCI_RESOURCE_RETURNED;
1355 case MCI_STATUS_NUMBER_OF_TRACKS:
1356 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1357 lpParms->dwReturn = 1;
1358 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
1360 case MCI_STATUS_POSITION:
1362 lpParms->dwReturn = 0;
1363 return MCIERR_UNSUPPORTED_FUNCTION;
1365 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1366 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1367 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
1369 TRACE("MCI_STATUS_POSITION %s => %lu\n",
1370 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1372 case MCI_STATUS_READY:
1373 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1374 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1375 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1376 ret = MCI_RESOURCE_RETURNED;
1378 case MCI_STATUS_TIME_FORMAT:
1379 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
1380 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1381 ret = MCI_RESOURCE_RETURNED;
1383 case MCI_WAVE_INPUT:
1384 TRACE("MCI_WAVE_INPUT !\n");
1385 lpParms->dwReturn = 0;
1386 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1388 case MCI_WAVE_OUTPUT:
1389 TRACE("MCI_WAVE_OUTPUT !\n");
1392 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
1393 lpParms->dwReturn = id;
1395 lpParms->dwReturn = 0;
1396 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1400 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1402 lpParms->dwReturn = 0;
1403 return MCIERR_UNSUPPORTED_FUNCTION;
1405 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1406 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1408 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1410 lpParms->dwReturn = 0;
1411 return MCIERR_UNSUPPORTED_FUNCTION;
1413 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1414 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1416 case MCI_WAVE_STATUS_BLOCKALIGN:
1418 lpParms->dwReturn = 0;
1419 return MCIERR_UNSUPPORTED_FUNCTION;
1421 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1422 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1424 case MCI_WAVE_STATUS_CHANNELS:
1426 lpParms->dwReturn = 0;
1427 return MCIERR_UNSUPPORTED_FUNCTION;
1429 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1430 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1432 case MCI_WAVE_STATUS_FORMATTAG:
1434 lpParms->dwReturn = 0;
1435 return MCIERR_UNSUPPORTED_FUNCTION;
1437 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1438 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1440 case MCI_WAVE_STATUS_LEVEL:
1441 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1442 lpParms->dwReturn = 0xAAAA5555;
1444 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1446 lpParms->dwReturn = 0;
1447 return MCIERR_UNSUPPORTED_FUNCTION;
1449 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1450 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1453 WARN("unknown command %08lX !\n", lpParms->dwItem);
1454 return MCIERR_UNRECOGNIZED_COMMAND;
1457 if (dwFlags & MCI_NOTIFY) {
1458 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1459 wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1464 /**************************************************************************
1465 * WAVE_mciGetDevCaps [internal]
1467 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1468 LPMCI_GETDEVCAPS_PARMS lpParms)
1470 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1473 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1475 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1476 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1478 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1479 switch(lpParms->dwItem) {
1480 case MCI_GETDEVCAPS_DEVICE_TYPE:
1481 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1482 ret = MCI_RESOURCE_RETURNED;
1484 case MCI_GETDEVCAPS_HAS_AUDIO:
1485 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1486 ret = MCI_RESOURCE_RETURNED;
1488 case MCI_GETDEVCAPS_HAS_VIDEO:
1489 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1490 ret = MCI_RESOURCE_RETURNED;
1492 case MCI_GETDEVCAPS_USES_FILES:
1493 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1494 ret = MCI_RESOURCE_RETURNED;
1496 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1497 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1498 ret = MCI_RESOURCE_RETURNED;
1500 case MCI_GETDEVCAPS_CAN_RECORD:
1501 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1502 ret = MCI_RESOURCE_RETURNED;
1504 case MCI_GETDEVCAPS_CAN_EJECT:
1505 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1506 ret = MCI_RESOURCE_RETURNED;
1508 case MCI_GETDEVCAPS_CAN_PLAY:
1509 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1510 ret = MCI_RESOURCE_RETURNED;
1512 case MCI_GETDEVCAPS_CAN_SAVE:
1513 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1514 ret = MCI_RESOURCE_RETURNED;
1516 case MCI_WAVE_GETDEVCAPS_INPUTS:
1517 lpParms->dwReturn = 1;
1519 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1520 lpParms->dwReturn = 1;
1523 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1524 return MCIERR_UNRECOGNIZED_COMMAND;
1527 WARN("No GetDevCaps-Item !\n");
1528 return MCIERR_UNRECOGNIZED_COMMAND;
1533 /**************************************************************************
1534 * WAVE_mciInfo [internal]
1536 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1540 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1542 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1544 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1545 ret = MCIERR_NULL_PARAMETER_BLOCK;
1546 } else if (wmw == NULL) {
1547 ret = MCIERR_INVALID_DEVICE_ID;
1549 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1551 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1552 case MCI_INFO_PRODUCT:
1553 str = "Wine's audio player";
1556 str = wmw->openParms.lpstrElementName;
1558 case MCI_WAVE_INPUT:
1559 str = "Wine Wave In";
1561 case MCI_WAVE_OUTPUT:
1562 str = "Wine Wave Out";
1565 WARN("Don't know this info command (%lu)\n", dwFlags);
1566 ret = MCIERR_UNRECOGNIZED_COMMAND;
1570 if (strlen(str) + 1 > lpParms->dwRetSize) {
1571 ret = MCIERR_PARAM_OVERFLOW;
1573 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1576 lpParms->lpstrReturn[0] = 0;
1582 /**************************************************************************
1583 * DriverProc (MCIWAVE.@)
1585 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1586 DWORD dwParam1, DWORD dwParam2)
1588 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1589 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1592 case DRV_LOAD: return 1;
1593 case DRV_FREE: return 1;
1594 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1595 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1596 case DRV_ENABLE: return 1;
1597 case DRV_DISABLE: return 1;
1598 case DRV_QUERYCONFIGURE: return 1;
1599 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1600 case DRV_INSTALL: return DRVCNF_RESTART;
1601 case DRV_REMOVE: return DRVCNF_RESTART;
1604 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1607 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1608 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1609 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1610 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1611 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1612 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1613 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1614 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1615 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1616 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1617 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1618 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1619 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1620 case MCI_SAVE: return WAVE_mciSave (dwDevID, dwParam1, (LPMCI_SAVE_PARMS) dwParam2);
1621 /* commands that should be supported */
1636 FIXME("Unsupported yet command [%lu]\n", wMsg);
1639 TRACE("Unsupported command [%lu]\n", wMsg);
1641 /* option which can be silenced */
1646 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1649 FIXME("is probably wrong msg [%lu]\n", wMsg);
1650 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1652 return MCIERR_UNRECOGNIZED_COMMAND;