Fixed buggy playback for non PCM files
[wine/dcerpc.git] / dlls / winmm / mciwave / mciwave.c
blob751dd1388a792ef9b4b44b5050f69eadbcc78195
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Sample Wine Driver for MCI wave forms
5 * Copyright 1994 Martin Ayotte
6 * 1999 Eric Pouech
7 */
8 /*
9 * FIXME:
10 * - record/play should and must be done asynchronous
11 * - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
14 #include "winuser.h"
15 #include "driver.h"
16 #include "mmddk.h"
17 #include "heap.h"
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(mciwave)
22 typedef struct {
23 UINT wDevID;
24 HANDLE hWave;
25 int nUseCount; /* Incremented for each shared open */
26 BOOL fShareable; /* TRUE if first open was shareable */
27 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
28 HANDLE hCallback; /* Callback handle for pending notification */
29 HMMIO hFile; /* mmio file handle open as Element */
30 MCI_WAVE_OPEN_PARMSA openParms;
31 LPWAVEFORMATEX lpWaveFormat;
32 BOOL fInput; /* FALSE = Output, TRUE = Input */
33 WORD dwStatus; /* one from MCI_MODE_xxxx */
34 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
35 DWORD dwFileOffset; /* Offset of chunk in mmio file */
36 DWORD dwLength; /* number of bytes in chunk for playing */
37 DWORD dwPosition; /* position in bytes in chunk for playing */
38 } WINE_MCIWAVE;
40 /* ===================================================================
41 * ===================================================================
42 * FIXME: should be using the new mmThreadXXXX functions from WINMM
43 * instead of those
44 * it would require to add a wine internal flag to mmThreadCreate
45 * in order to pass a 32 bit function instead of a 16 bit one
46 * ===================================================================
47 * =================================================================== */
49 struct SCA {
50 UINT wDevID;
51 UINT wMsg;
52 DWORD dwParam1;
53 DWORD dwParam2;
54 BOOL allocatedCopy;
57 /**************************************************************************
58 * MCI_SCAStarter [internal]
60 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
62 struct SCA* sca = (struct SCA*)arg;
63 DWORD ret;
65 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
66 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
67 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
68 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
69 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
70 if (sca->allocatedCopy)
71 HeapFree(GetProcessHeap(), 0, (LPVOID)sca->dwParam2);
72 HeapFree(GetProcessHeap(), 0, sca);
73 ExitThread(ret);
74 WARN("Should not happen ? what's wrong \n");
75 /* should not go after this point */
76 return ret;
79 /**************************************************************************
80 * MCI_SendCommandAsync [internal]
82 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
83 DWORD dwParam2, UINT size)
85 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA));
87 if (sca == 0)
88 return MCIERR_OUT_OF_MEMORY;
90 sca->wDevID = wDevID;
91 sca->wMsg = wMsg;
92 sca->dwParam1 = dwParam1;
94 if (size) {
95 sca->dwParam2 = (DWORD)HeapAlloc(GetProcessHeap(), 0, size);
96 if (sca->dwParam2 == 0) {
97 HeapFree(GetProcessHeap(), 0, sca);
98 return MCIERR_OUT_OF_MEMORY;
100 sca->allocatedCopy = TRUE;
101 /* copy structure passed by program in dwParam2 to be sure
102 * we can still use it whatever the program does
104 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
105 } else {
106 sca->dwParam2 = dwParam2;
107 sca->allocatedCopy = FALSE;
110 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
111 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
112 return MCI_SCAStarter(&sca);
114 return 0;
117 /*======================================================================*
118 * MCI WAVE implemantation *
119 *======================================================================*/
121 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
123 /**************************************************************************
124 * MCIWAVE_drvOpen [internal]
126 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
128 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
130 if (!wmw)
131 return 0;
133 wmw->wDevID = modp->wDeviceID;
134 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
135 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
136 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
137 return modp->wDeviceID;
140 /**************************************************************************
141 * MCIWAVE_drvClose [internal]
143 static DWORD WAVE_drvClose(DWORD dwDevID)
145 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
147 if (wmw) {
148 HeapFree(GetProcessHeap(), 0, wmw);
149 mciSetDriverData(dwDevID, 0);
150 return 1;
152 return 0;
155 /**************************************************************************
156 * WAVE_mciGetOpenDev [internal]
158 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
160 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
162 if (wmw == NULL || wmw->nUseCount == 0) {
163 WARN("Invalid wDevID=%u\n", wDevID);
164 return 0;
166 return wmw;
169 /**************************************************************************
170 * WAVE_ConvertByteToTimeFormat [internal]
172 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
174 DWORD ret = 0;
176 switch (wmw->dwMciTimeFormat) {
177 case MCI_FORMAT_MILLISECONDS:
178 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
179 break;
180 case MCI_FORMAT_BYTES:
181 ret = val;
182 break;
183 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
184 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
185 break;
186 default:
187 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
189 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
190 *lpRet = 0;
191 return ret;
194 /**************************************************************************
195 * WAVE_ConvertTimeFormatToByte [internal]
197 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
199 DWORD ret = 0;
201 switch (wmw->dwMciTimeFormat) {
202 case MCI_FORMAT_MILLISECONDS:
203 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
204 break;
205 case MCI_FORMAT_BYTES:
206 ret = val;
207 break;
208 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
209 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
210 break;
211 default:
212 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
214 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
215 return ret;
218 /**************************************************************************
219 * WAVE_mciReadFmt [internal]
221 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
223 MMCKINFO mmckInfo;
224 long r;
226 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
227 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
228 return MCIERR_INVALID_FILE;
229 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
230 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
232 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
233 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
234 if (r < sizeof(WAVEFORMAT))
235 return MCIERR_INVALID_FILE;
237 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
238 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
239 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
240 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
241 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
242 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
243 if (r >= (long)sizeof(WAVEFORMATEX))
244 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
246 mmioAscend(wmw->hFile, &mmckInfo, 0);
247 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
248 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
249 TRACE("can't find data chunk\n");
250 return MCIERR_INVALID_FILE;
252 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
253 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
254 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
255 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
256 wmw->dwLength = mmckInfo.cksize;
257 wmw->dwFileOffset = mmckInfo.dwDataOffset;
258 return 0;
261 /**************************************************************************
262 * WAVE_mciOpen [internal]
264 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
266 DWORD dwRet = 0;
267 DWORD dwDeviceID;
268 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
270 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
271 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
272 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
274 if (dwFlags & MCI_OPEN_SHAREABLE)
275 return MCIERR_HARDWARE;
277 if (wmw->nUseCount > 0) {
278 /* The driver is already opened on this channel
279 * Wave driver cannot be shared
281 return MCIERR_DEVICE_OPEN;
283 wmw->nUseCount++;
285 dwDeviceID = lpOpenParms->wDeviceID;
287 wmw->fInput = FALSE;
288 wmw->hWave = 0;
290 TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
292 if (dwFlags & MCI_OPEN_ELEMENT) {
293 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
294 /* could it be that (DWORD)lpOpenParms->lpstrElementName
295 * contains the hFile value ?
297 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
298 } else {
299 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
301 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
302 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
303 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
304 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL,
305 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
306 if (wmw->hFile == 0) {
307 WARN("can't find file='%s' !\n", lpstrElementName);
308 dwRet = MCIERR_FILE_NOT_FOUND;
310 } else {
311 wmw->hFile = 0;
315 TRACE("hFile=%u\n", wmw->hFile);
317 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
318 wmw->wNotifyDeviceID = dwDeviceID;
319 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
321 if (dwRet == 0 && wmw->hFile != 0) {
322 MMCKINFO ckMainRIFF;
324 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
325 dwRet = MCIERR_INVALID_FILE;
326 } else {
327 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
328 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
329 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
330 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
331 dwRet = MCIERR_INVALID_FILE;
332 } else {
333 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
336 } else {
337 wmw->dwLength = 0;
339 if (dwRet == 0) {
340 if (wmw->lpWaveFormat) {
341 switch (wmw->lpWaveFormat->wFormatTag) {
342 case WAVE_FORMAT_PCM:
343 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
344 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
345 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
346 wmw->lpWaveFormat->nAvgBytesPerSec,
347 wmw->lpWaveFormat->nSamplesPerSec *
348 wmw->lpWaveFormat->nBlockAlign);
349 wmw->lpWaveFormat->nAvgBytesPerSec =
350 wmw->lpWaveFormat->nSamplesPerSec *
351 wmw->lpWaveFormat->nBlockAlign;
353 break;
356 wmw->dwPosition = 0;
358 wmw->dwStatus = MCI_MODE_STOP;
359 } else {
360 wmw->nUseCount--;
361 if (wmw->hFile != 0)
362 mmioClose(wmw->hFile, 0);
363 wmw->hFile = 0;
365 return dwRet;
368 /**************************************************************************
369 * WAVE_mciCue [internal]
371 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
374 FIXME
376 This routine is far from complete. At the moment only a check is done on the
377 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
378 is the default.
380 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
381 are ignored
384 DWORD dwRet;
385 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
387 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
389 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
391 /* always close elements ? */
392 if (wmw->hFile != 0) {
393 mmioClose(wmw->hFile, 0);
394 wmw->hFile = 0;
397 dwRet = MMSYSERR_NOERROR; /* assume success */
399 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
400 dwRet = waveOutClose(wmw->hWave);
401 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
402 wmw->fInput = TRUE;
403 } else if (wmw->fInput) {
404 dwRet = waveInClose(wmw->hWave);
405 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
406 wmw->fInput = FALSE;
408 wmw->hWave = 0;
409 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
412 /**************************************************************************
413 * WAVE_mciStop [internal]
415 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
417 DWORD dwRet;
418 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
420 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
422 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
423 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
425 wmw->dwStatus = MCI_MODE_STOP;
426 wmw->dwPosition = 0;
427 TRACE("wmw->dwStatus=%d\n", wmw->dwStatus);
429 if (wmw->fInput)
430 dwRet = waveInReset(wmw->hWave);
431 else
432 dwRet = waveOutReset(wmw->hWave);
434 if (dwFlags & MCI_NOTIFY) {
435 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
436 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
437 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
440 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
443 /**************************************************************************
444 * WAVE_mciClose [internal]
446 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
448 DWORD dwRet = 0;
449 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
451 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
453 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
455 if (wmw->dwStatus != MCI_MODE_STOP) {
456 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
459 wmw->nUseCount--;
461 if (wmw->nUseCount == 0) {
462 DWORD mmRet;
463 if (wmw->hFile != 0) {
464 mmioClose(wmw->hFile, 0);
465 wmw->hFile = 0;
467 mmRet = (wmw->fInput) ? waveInClose(wmw->hWave) : waveOutClose(wmw->hWave);
469 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
472 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
473 wmw->lpWaveFormat = NULL;
475 if ((dwFlags & MCI_NOTIFY) && lpParms) {
476 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
477 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
478 wmw->wNotifyDeviceID,
479 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
481 return 0;
484 /**************************************************************************
485 * WAVE_mciPlay [internal]
487 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
489 DWORD end;
490 LONG bufsize, count, left;
491 DWORD dwRet;
492 LPWAVEHDR waveHdr;
493 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
494 int whidx;
496 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
498 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
499 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
501 if (wmw->fInput) {
502 WARN("cannot play on input device\n");
503 return MCIERR_NONAPPLICABLE_FUNCTION;
506 if (wmw->hFile == 0) {
507 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
508 return MCIERR_FILE_NOT_FOUND;
511 if (!(dwFlags & MCI_WAIT)) {
512 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
513 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
516 if (wmw->dwStatus != MCI_MODE_STOP) {
517 if (wmw->dwStatus == MCI_MODE_PAUSE) {
518 /* FIXME: parameters (start/end) in lpParams may not be used */
519 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
521 return MCIERR_INTERNAL;
524 end = 0xFFFFFFFF;
525 if (lpParms && (dwFlags & MCI_FROM)) {
526 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
528 if (lpParms && (dwFlags & MCI_TO)) {
529 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
532 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
534 if (end <= wmw->dwPosition)
535 return TRUE;
537 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
538 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
540 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
541 wmw->dwLength = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwLength);
542 /* go back to begining of chunk plus the requested position */
543 /* FIXME: I'm not sure this is correct, notably because some data linked to
544 * the decompression state machine will not be correcly initialized.
545 * try it this way (other way would be to decompress from 0 up to dwPosition
546 * and to start sending to hWave when dwPosition is reached)
548 mmioSeek(wmw->hFile, wmw->dwFileOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
550 /* By default the device will be opened for output, the MCI_CUE function is there to
551 * change from output to input and back
553 /* FIXME: how to choose between several output channels ? here mapper is forced */
554 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat, 0L, 0L, CALLBACK_NULL);
555 if (dwRet != 0) {
556 TRACE("Can't open low level audio device %ld\n", dwRet);
557 return MCIERR_DEVICE_OPEN;
560 /* make it so that 3 buffers per second are needed */
561 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
563 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
564 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
565 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
566 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
567 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
569 wmw->dwStatus = MCI_MODE_PLAY;
571 whidx = 0;
572 left = MIN(wmw->dwLength, end - wmw->dwPosition);
574 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
576 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
577 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP) {
578 waveHdr[whidx].dwFlags = 0L;
579 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, MIN(bufsize, left));
580 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
581 if (count < 1)
582 break;
583 left -= count;
584 waveHdr[whidx].dwBufferLength = count;
585 dwRet = waveOutPrepareHeader(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
586 /* EPP waveHdr[whidx].dwBytesRecorded = 0; */
587 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
588 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
589 waveHdr[whidx].dwBytesRecorded);
590 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
591 wmw->dwPosition += count;
592 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
594 whidx ^= 1;
595 if (waveHdr[whidx].dwFlags & WHDR_PREPARED) {
596 /* FIXME: should use callback mechanisms from audio driver */
597 while (!(waveHdr[whidx].dwFlags & WHDR_DONE))
598 Sleep(100);
599 dwRet = waveOutUnprepareHeader(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
602 whidx ^= 1;
603 if (waveHdr[whidx].dwFlags & WHDR_PREPARED) {
604 /* test if waveHdr[whidx] has been prepared, if so it has been queued for playing */
605 while (!(waveHdr[whidx].dwFlags & WHDR_DONE))
606 Sleep(100);
607 waveOutUnprepareHeader(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
610 HeapFree(GetProcessHeap(), 0, waveHdr);
612 waveOutReset(wmw->hWave);
613 waveOutClose(wmw->hWave);
615 wmw->dwStatus = MCI_MODE_STOP;
616 if (lpParms && (dwFlags & MCI_NOTIFY)) {
617 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
618 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
619 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
621 return 0;
624 /**************************************************************************
625 * WAVE_mciRecord [internal]
627 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
629 int start, end;
630 LONG bufsize;
631 WAVEHDR waveHdr;
632 DWORD dwRet;
633 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
635 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
637 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
638 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
640 if (!wmw->fInput) {
641 WARN("cannot record on output device\n");
642 return MCIERR_NONAPPLICABLE_FUNCTION;
645 if (wmw->hFile == 0) {
646 WARN("can't find file='%s' !\n",
647 wmw->openParms.lpstrElementName);
648 return MCIERR_FILE_NOT_FOUND;
650 start = 1; end = 99999;
651 if (dwFlags & MCI_FROM) {
652 start = lpParms->dwFrom;
653 TRACE("MCI_FROM=%d \n", start);
655 if (dwFlags & MCI_TO) {
656 end = lpParms->dwTo;
657 TRACE("MCI_TO=%d \n", end);
659 bufsize = 64000;
660 waveHdr.lpData = HeapAlloc(GetProcessHeap(), 0, bufsize);
661 waveHdr.dwBufferLength = bufsize;
662 waveHdr.dwUser = 0L;
663 waveHdr.dwFlags = 0L;
664 waveHdr.dwLoops = 0L;
665 dwRet = waveInPrepareHeader(wmw->hWave, &waveHdr, sizeof(WAVEHDR));
667 for (;;) { /* FIXME: I don't see any waveInAddBuffer ? */
668 waveHdr.dwBytesRecorded = 0;
669 dwRet = waveInStart(wmw->hWave);
670 TRACE("waveInStart => lpWaveHdr=%p dwBytesRecorded=%lu\n",
671 &waveHdr, waveHdr.dwBytesRecorded);
672 if (waveHdr.dwBytesRecorded == 0) break;
674 dwRet = waveInUnprepareHeader(wmw->hWave, &waveHdr, sizeof(WAVEHDR));
675 HeapFree(GetProcessHeap(), 0, waveHdr.lpData);
677 if (dwFlags & MCI_NOTIFY) {
678 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
679 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
680 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
682 return 0;
685 /**************************************************************************
686 * WAVE_mciPause [internal]
688 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
690 DWORD dwRet;
691 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
693 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
695 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
696 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
698 if (wmw->dwStatus == MCI_MODE_PLAY) {
699 wmw->dwStatus = MCI_MODE_PAUSE;
702 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
703 else dwRet = waveOutPause(wmw->hWave);
705 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
708 /**************************************************************************
709 * WAVE_mciResume [internal]
711 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
713 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
714 DWORD dwRet = 0;
716 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
718 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
719 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
721 if (wmw->dwStatus == MCI_MODE_PAUSE) {
722 wmw->dwStatus = MCI_MODE_PLAY;
725 /* FIXME: I doubt WIDM_START is correct */
726 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
727 else dwRet = waveOutRestart(wmw->hWave);
728 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
731 /**************************************************************************
732 * WAVE_mciSeek [internal]
734 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
736 DWORD ret = 0;
737 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
739 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
741 if (lpParms == NULL) {
742 ret = MCIERR_NULL_PARAMETER_BLOCK;
743 } else if (wmw == NULL) {
744 ret = MCIERR_INVALID_DEVICE_ID;
745 } else {
746 WAVE_mciStop(wDevID, MCI_WAIT, 0);
748 if (dwFlags & MCI_SEEK_TO_START) {
749 wmw->dwPosition = 0;
750 } else if (dwFlags & MCI_SEEK_TO_END) {
751 wmw->dwPosition = wmw->dwLength;
752 } else if (dwFlags & MCI_TO) {
753 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
754 } else {
755 WARN("dwFlag doesn't tell where to seek to...\n");
756 return MCIERR_MISSING_PARAMETER;
759 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
761 if (dwFlags & MCI_NOTIFY) {
762 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
763 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
764 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
767 return ret;
770 /**************************************************************************
771 * WAVE_mciSet [internal]
773 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
775 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
777 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
779 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
780 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
782 if (dwFlags & MCI_SET_TIME_FORMAT) {
783 switch (lpParms->dwTimeFormat) {
784 case MCI_FORMAT_MILLISECONDS:
785 TRACE("MCI_FORMAT_MILLISECONDS !\n");
786 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
787 break;
788 case MCI_FORMAT_BYTES:
789 TRACE("MCI_FORMAT_BYTES !\n");
790 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
791 break;
792 case MCI_FORMAT_SAMPLES:
793 TRACE("MCI_FORMAT_SAMPLES !\n");
794 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
795 break;
796 default:
797 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
798 return MCIERR_BAD_TIME_FORMAT;
801 if (dwFlags & MCI_SET_VIDEO) {
802 TRACE("No support for video !\n");
803 return MCIERR_UNSUPPORTED_FUNCTION;
805 if (dwFlags & MCI_SET_DOOR_OPEN) {
806 TRACE("No support for door open !\n");
807 return MCIERR_UNSUPPORTED_FUNCTION;
809 if (dwFlags & MCI_SET_DOOR_CLOSED) {
810 TRACE("No support for door close !\n");
811 return MCIERR_UNSUPPORTED_FUNCTION;
813 if (dwFlags & MCI_SET_AUDIO) {
814 if (dwFlags & MCI_SET_ON) {
815 TRACE("MCI_SET_ON audio !\n");
816 } else if (dwFlags & MCI_SET_OFF) {
817 TRACE("MCI_SET_OFF audio !\n");
818 } else {
819 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
820 return MCIERR_BAD_INTEGER;
823 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
824 TRACE("MCI_SET_AUDIO_ALL !\n");
825 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
826 TRACE("MCI_SET_AUDIO_LEFT !\n");
827 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
828 TRACE("MCI_SET_AUDIO_RIGHT !\n");
830 if (dwFlags & MCI_WAVE_INPUT)
831 TRACE("MCI_WAVE_INPUT !\n");
832 if (dwFlags & MCI_WAVE_OUTPUT)
833 TRACE("MCI_WAVE_OUTPUT !\n");
834 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
835 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
836 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
837 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
838 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
839 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
840 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
841 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
842 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
843 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
844 if (dwFlags & MCI_WAVE_SET_CHANNELS)
845 TRACE("MCI_WAVE_SET_CHANNELS !\n");
846 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
847 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
848 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
849 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
850 return 0;
853 /**************************************************************************
854 * WAVE_mciStatus [internal]
856 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
858 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
859 DWORD ret = 0;
861 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
862 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
863 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
865 if (dwFlags & MCI_STATUS_ITEM) {
866 switch (lpParms->dwItem) {
867 case MCI_STATUS_CURRENT_TRACK:
868 lpParms->dwReturn = 1;
869 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
870 break;
871 case MCI_STATUS_LENGTH:
872 if (!wmw->hFile) {
873 lpParms->dwReturn = 0;
874 return MCIERR_UNSUPPORTED_FUNCTION;
876 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
877 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength, &ret);
878 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
879 break;
880 case MCI_STATUS_MODE:
881 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
882 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
883 ret = MCI_RESOURCE_RETURNED;
884 break;
885 case MCI_STATUS_MEDIA_PRESENT:
886 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
887 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
888 ret = MCI_RESOURCE_RETURNED;
889 break;
890 case MCI_STATUS_NUMBER_OF_TRACKS:
891 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
892 lpParms->dwReturn = 1;
893 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
894 break;
895 case MCI_STATUS_POSITION:
896 if (!wmw->hFile) {
897 lpParms->dwReturn = 0;
898 return MCIERR_UNSUPPORTED_FUNCTION;
900 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
901 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
902 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
903 &ret);
904 TRACE("MCI_STATUS_POSITION %s => %lu\n",
905 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
906 break;
907 case MCI_STATUS_READY:
908 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
909 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
910 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
911 ret = MCI_RESOURCE_RETURNED;
912 break;
913 case MCI_STATUS_TIME_FORMAT:
914 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
915 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
916 ret = MCI_RESOURCE_RETURNED;
917 break;
918 case MCI_WAVE_INPUT:
919 TRACE("MCI_WAVE_INPUT !\n");
920 lpParms->dwReturn = 0;
921 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
922 break;
923 case MCI_WAVE_OUTPUT:
924 TRACE("MCI_WAVE_OUTPUT !\n");
926 UINT id;
927 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
928 lpParms->dwReturn = id;
929 } else {
930 lpParms->dwReturn = 0;
931 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
934 break;
935 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
936 if (!wmw->hFile) {
937 lpParms->dwReturn = 0;
938 return MCIERR_UNSUPPORTED_FUNCTION;
940 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
941 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
942 break;
943 case MCI_WAVE_STATUS_BITSPERSAMPLE:
944 if (!wmw->hFile) {
945 lpParms->dwReturn = 0;
946 return MCIERR_UNSUPPORTED_FUNCTION;
948 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
949 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
950 break;
951 case MCI_WAVE_STATUS_BLOCKALIGN:
952 if (!wmw->hFile) {
953 lpParms->dwReturn = 0;
954 return MCIERR_UNSUPPORTED_FUNCTION;
956 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
957 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
958 break;
959 case MCI_WAVE_STATUS_CHANNELS:
960 if (!wmw->hFile) {
961 lpParms->dwReturn = 0;
962 return MCIERR_UNSUPPORTED_FUNCTION;
964 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
965 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
966 break;
967 case MCI_WAVE_STATUS_FORMATTAG:
968 if (!wmw->hFile) {
969 lpParms->dwReturn = 0;
970 return MCIERR_UNSUPPORTED_FUNCTION;
972 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
973 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
974 break;
975 case MCI_WAVE_STATUS_LEVEL:
976 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
977 lpParms->dwReturn = 0xAAAA5555;
978 break;
979 case MCI_WAVE_STATUS_SAMPLESPERSEC:
980 if (!wmw->hFile) {
981 lpParms->dwReturn = 0;
982 return MCIERR_UNSUPPORTED_FUNCTION;
984 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
985 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
986 break;
987 default:
988 WARN("unknown command %08lX !\n", lpParms->dwItem);
989 return MCIERR_UNRECOGNIZED_COMMAND;
992 if (dwFlags & MCI_NOTIFY) {
993 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
994 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
995 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
997 return ret;
1000 /**************************************************************************
1001 * WAVE_mciGetDevCaps [internal]
1003 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1004 LPMCI_GETDEVCAPS_PARMS lpParms)
1006 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1007 DWORD ret = 0;
1009 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1011 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1012 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1014 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1015 switch(lpParms->dwItem) {
1016 case MCI_GETDEVCAPS_DEVICE_TYPE:
1017 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1018 ret = MCI_RESOURCE_RETURNED;
1019 break;
1020 case MCI_GETDEVCAPS_HAS_AUDIO:
1021 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1022 ret = MCI_RESOURCE_RETURNED;
1023 break;
1024 case MCI_GETDEVCAPS_HAS_VIDEO:
1025 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1026 ret = MCI_RESOURCE_RETURNED;
1027 break;
1028 case MCI_GETDEVCAPS_USES_FILES:
1029 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1030 ret = MCI_RESOURCE_RETURNED;
1031 break;
1032 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1033 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1034 ret = MCI_RESOURCE_RETURNED;
1035 break;
1036 case MCI_GETDEVCAPS_CAN_RECORD:
1037 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1038 ret = MCI_RESOURCE_RETURNED;
1039 break;
1040 case MCI_GETDEVCAPS_CAN_EJECT:
1041 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1042 ret = MCI_RESOURCE_RETURNED;
1043 break;
1044 case MCI_GETDEVCAPS_CAN_PLAY:
1045 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1046 ret = MCI_RESOURCE_RETURNED;
1047 break;
1048 case MCI_GETDEVCAPS_CAN_SAVE:
1049 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1050 ret = MCI_RESOURCE_RETURNED;
1051 break;
1052 case MCI_WAVE_GETDEVCAPS_INPUTS:
1053 lpParms->dwReturn = 1;
1054 break;
1055 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1056 lpParms->dwReturn = 1;
1057 break;
1058 default:
1059 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1060 return MCIERR_UNRECOGNIZED_COMMAND;
1062 } else {
1063 WARN("No GetDevCaps-Item !\n");
1064 return MCIERR_UNRECOGNIZED_COMMAND;
1066 return ret;
1069 /**************************************************************************
1070 * WAVE_mciInfo [internal]
1072 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1074 DWORD ret = 0;
1075 LPCSTR str = 0;
1076 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1078 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1080 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1081 ret = MCIERR_NULL_PARAMETER_BLOCK;
1082 } else if (wmw == NULL) {
1083 ret = MCIERR_INVALID_DEVICE_ID;
1084 } else {
1085 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1087 switch(dwFlags) {
1088 case MCI_INFO_PRODUCT:
1089 str = "Wine's audio player";
1090 break;
1091 case MCI_INFO_FILE:
1092 str = wmw->openParms.lpstrElementName;
1093 break;
1094 case MCI_WAVE_INPUT:
1095 str = "Wine Wave In";
1096 break;
1097 case MCI_WAVE_OUTPUT:
1098 str = "Wine Wave Out";
1099 break;
1100 default:
1101 WARN("Don't know this info command (%lu)\n", dwFlags);
1102 ret = MCIERR_UNRECOGNIZED_COMMAND;
1105 if (str) {
1106 if (strlen(str) + 1 > lpParms->dwRetSize) {
1107 ret = MCIERR_PARAM_OVERFLOW;
1108 } else {
1109 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1111 } else {
1112 lpParms->lpstrReturn[0] = 0;
1115 return ret;
1118 /**************************************************************************
1119 * MCIWAVE_DriverProc [sample driver]
1121 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1122 DWORD dwParam1, DWORD dwParam2)
1124 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1125 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1127 switch(wMsg) {
1128 case DRV_LOAD: return 1;
1129 case DRV_FREE: return 1;
1130 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1131 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1132 case DRV_ENABLE: return 1;
1133 case DRV_DISABLE: return 1;
1134 case DRV_QUERYCONFIGURE: return 1;
1135 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1136 case DRV_INSTALL: return DRVCNF_RESTART;
1137 case DRV_REMOVE: return DRVCNF_RESTART;
1138 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1139 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1140 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1141 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1142 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1143 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1144 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1145 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1146 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1147 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1148 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1149 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1150 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1151 /* commands that should be supported */
1152 case MCI_LOAD:
1153 case MCI_SAVE:
1154 case MCI_FREEZE:
1155 case MCI_PUT:
1156 case MCI_REALIZE:
1157 case MCI_UNFREEZE:
1158 case MCI_UPDATE:
1159 case MCI_WHERE:
1160 case MCI_STEP:
1161 case MCI_SPIN:
1162 case MCI_ESCAPE:
1163 case MCI_COPY:
1164 case MCI_CUT:
1165 case MCI_DELETE:
1166 case MCI_PASTE:
1167 FIXME("Unsupported yet command [%lu]\n", wMsg);
1168 break;
1169 case MCI_WINDOW:
1170 TRACE("Unsupported command [%lu]\n", wMsg);
1171 break;
1172 case MCI_OPEN:
1173 case MCI_CLOSE:
1174 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1175 break;
1176 default:
1177 FIXME("is probably wrong msg [%lu]\n", wMsg);
1178 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1180 return MCIERR_UNRECOGNIZED_COMMAND;