DOSFS_ToDosFCBFormat: fail if extension longer than 3 characters.
[wine/gsoc-2012-control.git] / dlls / winmm / mciwave / mciwave.c
blobc777786c335c65ca9f1fa499c22cc83262abd9c0
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
13 #include "windef.h"
14 #include "wingdi.h"
15 #include "winuser.h"
16 #include "driver.h"
17 #include "mmddk.h"
18 #include "heap.h"
19 #include "debugtools.h"
21 DEFAULT_DEBUG_CHANNEL(mciwave)
23 typedef struct {
24 UINT wDevID;
25 HANDLE hWave;
26 int nUseCount; /* Incremented for each shared open */
27 BOOL fShareable; /* TRUE if first open was shareable */
28 WORD wNotifyDeviceID;/* MCI device ID with a 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 volatile 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 HANDLE hEvent; /* for synchronization */
39 DWORD dwEventCount; /* for synchronization */
40 } WINE_MCIWAVE;
42 /* ===================================================================
43 * ===================================================================
44 * FIXME: should be using the new mmThreadXXXX functions from WINMM
45 * instead of those
46 * it would require to add a wine internal flag to mmThreadCreate
47 * in order to pass a 32 bit function instead of a 16 bit one
48 * ===================================================================
49 * =================================================================== */
51 struct SCA {
52 UINT wDevID;
53 UINT wMsg;
54 DWORD dwParam1;
55 DWORD dwParam2;
56 BOOL allocatedCopy;
59 /**************************************************************************
60 * MCI_SCAStarter [internal]
62 static DWORD CALLBACK MCI_SCAStarter(LPVOID arg)
64 struct SCA* sca = (struct SCA*)arg;
65 DWORD ret;
67 TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
68 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
69 ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
70 TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
71 sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
72 if (sca->allocatedCopy)
73 HeapFree(GetProcessHeap(), 0, (LPVOID)sca->dwParam2);
74 HeapFree(GetProcessHeap(), 0, sca);
75 ExitThread(ret);
76 WARN("Should not happen ? what's wrong \n");
77 /* should not go after this point */
78 return ret;
81 /**************************************************************************
82 * MCI_SendCommandAsync [internal]
84 static DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD dwParam1,
85 DWORD dwParam2, UINT size)
87 struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA));
89 if (sca == 0)
90 return MCIERR_OUT_OF_MEMORY;
92 sca->wDevID = wDevID;
93 sca->wMsg = wMsg;
94 sca->dwParam1 = dwParam1;
96 if (size) {
97 sca->dwParam2 = (DWORD)HeapAlloc(GetProcessHeap(), 0, size);
98 if (sca->dwParam2 == 0) {
99 HeapFree(GetProcessHeap(), 0, sca);
100 return MCIERR_OUT_OF_MEMORY;
102 sca->allocatedCopy = TRUE;
103 /* copy structure passed by program in dwParam2 to be sure
104 * we can still use it whatever the program does
106 memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
107 } else {
108 sca->dwParam2 = dwParam2;
109 sca->allocatedCopy = FALSE;
112 if (CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL) == 0) {
113 WARN("Couldn't allocate thread for async command handling, sending synchonously\n");
114 return MCI_SCAStarter(&sca);
116 return 0;
119 /*======================================================================*
120 * MCI WAVE implemantation *
121 *======================================================================*/
123 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
125 /**************************************************************************
126 * MCIWAVE_drvOpen [internal]
128 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
130 WINE_MCIWAVE* wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
132 if (!wmw)
133 return 0;
135 wmw->wDevID = modp->wDeviceID;
136 mciSetDriverData(wmw->wDevID, (DWORD)wmw);
137 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
138 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
139 return modp->wDeviceID;
142 /**************************************************************************
143 * MCIWAVE_drvClose [internal]
145 static DWORD WAVE_drvClose(DWORD dwDevID)
147 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
149 if (wmw) {
150 HeapFree(GetProcessHeap(), 0, wmw);
151 mciSetDriverData(dwDevID, 0);
152 return 1;
154 return 0;
157 /**************************************************************************
158 * WAVE_mciGetOpenDev [internal]
160 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT wDevID)
162 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
164 if (wmw == NULL || wmw->nUseCount == 0) {
165 WARN("Invalid wDevID=%u\n", wDevID);
166 return 0;
168 return wmw;
171 /**************************************************************************
172 * WAVE_ConvertByteToTimeFormat [internal]
174 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
176 DWORD ret = 0;
178 switch (wmw->dwMciTimeFormat) {
179 case MCI_FORMAT_MILLISECONDS:
180 ret = (val * 1000) / wmw->lpWaveFormat->nAvgBytesPerSec;
181 break;
182 case MCI_FORMAT_BYTES:
183 ret = val;
184 break;
185 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
186 ret = (val * 8) / wmw->lpWaveFormat->wBitsPerSample;
187 break;
188 default:
189 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
191 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
192 *lpRet = 0;
193 return ret;
196 /**************************************************************************
197 * WAVE_ConvertTimeFormatToByte [internal]
199 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
201 DWORD ret = 0;
203 switch (wmw->dwMciTimeFormat) {
204 case MCI_FORMAT_MILLISECONDS:
205 ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
206 break;
207 case MCI_FORMAT_BYTES:
208 ret = val;
209 break;
210 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
211 ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
212 break;
213 default:
214 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
216 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
217 return ret;
220 /**************************************************************************
221 * WAVE_mciReadFmt [internal]
223 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
225 MMCKINFO mmckInfo;
226 long r;
228 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
229 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
230 return MCIERR_INVALID_FILE;
231 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
232 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
234 wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
235 r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
236 if (r < sizeof(WAVEFORMAT))
237 return MCIERR_INVALID_FILE;
239 TRACE("wFormatTag=%04X !\n", wmw->lpWaveFormat->wFormatTag);
240 TRACE("nChannels=%d \n", wmw->lpWaveFormat->nChannels);
241 TRACE("nSamplesPerSec=%ld\n", wmw->lpWaveFormat->nSamplesPerSec);
242 TRACE("nAvgBytesPerSec=%ld\n", wmw->lpWaveFormat->nAvgBytesPerSec);
243 TRACE("nBlockAlign=%d \n", wmw->lpWaveFormat->nBlockAlign);
244 TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
245 if (r >= (long)sizeof(WAVEFORMATEX))
246 TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
248 mmioAscend(wmw->hFile, &mmckInfo, 0);
249 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
250 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
251 TRACE("can't find data chunk\n");
252 return MCIERR_INVALID_FILE;
254 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
255 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
256 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
257 wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
258 wmw->dwLength = mmckInfo.cksize;
259 wmw->dwFileOffset = mmckInfo.dwDataOffset;
260 return 0;
263 /**************************************************************************
264 * WAVE_mciOpen [internal]
266 static DWORD WAVE_mciOpen(UINT wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
268 DWORD dwRet = 0;
269 DWORD dwDeviceID;
270 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
272 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
273 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
274 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
276 if (dwFlags & MCI_OPEN_SHAREABLE)
277 return MCIERR_HARDWARE;
279 if (wmw->nUseCount > 0) {
280 /* The driver is already opened on this channel
281 * Wave driver cannot be shared
283 return MCIERR_DEVICE_OPEN;
285 wmw->nUseCount++;
287 dwDeviceID = lpOpenParms->wDeviceID;
289 wmw->fInput = FALSE;
290 wmw->hWave = 0;
292 TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
294 if (dwFlags & MCI_OPEN_ELEMENT) {
295 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
296 /* could it be that (DWORD)lpOpenParms->lpstrElementName
297 * contains the hFile value ?
299 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
300 } else {
301 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
303 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
304 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
305 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
306 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL,
307 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
308 if (wmw->hFile == 0) {
309 WARN("can't find file='%s' !\n", lpstrElementName);
310 dwRet = MCIERR_FILE_NOT_FOUND;
312 } else {
313 wmw->hFile = 0;
317 TRACE("hFile=%u\n", wmw->hFile);
319 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
320 wmw->wNotifyDeviceID = dwDeviceID;
321 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
323 if (dwRet == 0 && wmw->hFile != 0) {
324 MMCKINFO ckMainRIFF;
326 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
327 dwRet = MCIERR_INVALID_FILE;
328 } else {
329 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
330 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
331 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
332 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
333 dwRet = MCIERR_INVALID_FILE;
334 } else {
335 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
338 } else {
339 wmw->dwLength = 0;
341 if (dwRet == 0) {
342 if (wmw->lpWaveFormat) {
343 switch (wmw->lpWaveFormat->wFormatTag) {
344 case WAVE_FORMAT_PCM:
345 if (wmw->lpWaveFormat->nAvgBytesPerSec !=
346 wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
347 WARN("Incorrect nAvgBytesPerSec (%ld), setting it to %ld\n",
348 wmw->lpWaveFormat->nAvgBytesPerSec,
349 wmw->lpWaveFormat->nSamplesPerSec *
350 wmw->lpWaveFormat->nBlockAlign);
351 wmw->lpWaveFormat->nAvgBytesPerSec =
352 wmw->lpWaveFormat->nSamplesPerSec *
353 wmw->lpWaveFormat->nBlockAlign;
355 break;
358 wmw->dwPosition = 0;
360 wmw->dwStatus = MCI_MODE_STOP;
361 } else {
362 wmw->nUseCount--;
363 if (wmw->hFile != 0)
364 mmioClose(wmw->hFile, 0);
365 wmw->hFile = 0;
367 return dwRet;
370 /**************************************************************************
371 * WAVE_mciCue [internal]
373 static DWORD WAVE_mciCue(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
376 FIXME
378 This routine is far from complete. At the moment only a check is done on the
379 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
380 is the default.
382 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
383 are ignored
386 DWORD dwRet;
387 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
389 FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
391 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
393 /* always close elements ? */
394 if (wmw->hFile != 0) {
395 mmioClose(wmw->hFile, 0);
396 wmw->hFile = 0;
399 dwRet = MMSYSERR_NOERROR; /* assume success */
401 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
402 dwRet = waveOutClose(wmw->hWave);
403 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
404 wmw->fInput = TRUE;
405 } else if (wmw->fInput) {
406 dwRet = waveInClose(wmw->hWave);
407 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
408 wmw->fInput = FALSE;
410 wmw->hWave = 0;
411 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
414 /**************************************************************************
415 * WAVE_mciStop [internal]
417 static DWORD WAVE_mciStop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
419 DWORD dwRet = 0;
420 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
422 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
424 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
426 /* wait for playback thread (if any) to exit before processing further */
427 switch (wmw->dwStatus) {
428 case MCI_MODE_PAUSE:
429 case MCI_MODE_PLAY:
430 case MCI_MODE_RECORD:
432 int oldStat = wmw->dwStatus;
433 wmw->dwStatus = MCI_MODE_NOT_READY;
434 if (oldStat == MCI_MODE_PAUSE)
435 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
437 while (wmw->dwStatus != MCI_MODE_STOP)
438 Sleep(10);
439 break;
442 wmw->dwPosition = 0;
444 /* sanity resets */
445 wmw->dwStatus = MCI_MODE_STOP;
447 if ((dwFlags & MCI_NOTIFY) && lpParms) {
448 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
449 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
452 return dwRet;
455 /**************************************************************************
456 * WAVE_mciClose [internal]
458 static DWORD WAVE_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
460 DWORD dwRet = 0;
461 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
463 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
465 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
467 if (wmw->dwStatus != MCI_MODE_STOP) {
468 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
471 wmw->nUseCount--;
473 if (wmw->nUseCount == 0) {
474 if (wmw->hFile != 0) {
475 mmioClose(wmw->hFile, 0);
476 wmw->hFile = 0;
480 HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
481 wmw->lpWaveFormat = NULL;
483 if ((dwFlags & MCI_NOTIFY) && lpParms) {
484 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
485 wmw->wNotifyDeviceID,
486 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
489 return 0;
492 /**************************************************************************
493 * WAVE_mciPlayCallback [internal]
495 static void CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
496 DWORD dwInstance,
497 DWORD dwParam1, DWORD dwParam2)
499 WINE_MCIWAVE* wmw = (WINE_MCIWAVE*)dwInstance;
501 switch (uMsg) {
502 case WOM_OPEN:
503 case WOM_CLOSE:
504 break;
505 case WOM_DONE:
506 InterlockedIncrement(&wmw->dwEventCount);
507 TRACE("Returning waveHdr=%lx\n", dwParam1);
508 SetEvent(wmw->hEvent);
509 break;
510 default:
511 ERR("Unknown uMsg=%d\n", uMsg);
515 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
517 for (;;) {
518 ResetEvent(wmw->hEvent);
519 if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
520 break;
522 InterlockedIncrement(&wmw->dwEventCount);
524 WaitForSingleObject(wmw->hEvent, INFINITE);
528 /**************************************************************************
529 * WAVE_mciPlay [internal]
531 static DWORD WAVE_mciPlay(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
533 DWORD end;
534 LONG bufsize, count, left;
535 DWORD dwRet = 0;
536 LPWAVEHDR waveHdr = NULL;
537 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
538 int whidx;
540 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
542 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
543 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
545 if (wmw->fInput) {
546 WARN("cannot play on input device\n");
547 return MCIERR_NONAPPLICABLE_FUNCTION;
550 if (wmw->hFile == 0) {
551 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
552 return MCIERR_FILE_NOT_FOUND;
555 if (wmw->dwStatus == MCI_MODE_PAUSE) {
556 /* FIXME: parameters (start/end) in lpParams may not be used */
557 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
560 /** This function will be called again by a thread when async is used.
561 * We have to set MCI_MODE_PLAY before we do this so that the app can spin
562 * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
564 if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
565 return MCIERR_INTERNAL;
568 wmw->dwStatus = MCI_MODE_PLAY;
570 if (!(dwFlags & MCI_WAIT)) {
571 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
572 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
575 end = 0xFFFFFFFF;
576 if (lpParms && (dwFlags & MCI_FROM)) {
577 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
579 if (lpParms && (dwFlags & MCI_TO)) {
580 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
583 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
585 if (end <= wmw->dwPosition)
586 return TRUE;
588 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
589 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
591 wmw->dwPosition = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
592 wmw->dwLength = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwLength);
593 /* go back to begining of chunk plus the requested position */
594 /* FIXME: I'm not sure this is correct, notably because some data linked to
595 * the decompression state machine will not be correcly initialized.
596 * try it this way (other way would be to decompress from 0 up to dwPosition
597 * and to start sending to hWave when dwPosition is reached)
599 mmioSeek(wmw->hFile, wmw->dwFileOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
601 /* By default the device will be opened for output, the MCI_CUE function is there to
602 * change from output to input and back
604 /* FIXME: how to choose between several output channels ? here mapper is forced */
605 dwRet = waveOutOpen(&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
606 (DWORD)WAVE_mciPlayCallback, (DWORD)wmw, CALLBACK_FUNCTION);
608 if (dwRet != 0) {
609 TRACE("Can't open low level audio device %ld\n", dwRet);
610 dwRet = MCIERR_DEVICE_OPEN;
611 wmw->hWave = 0;
612 goto cleanUp;
615 /* make it so that 3 buffers per second are needed */
616 bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
618 waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
619 waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
620 waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
621 waveHdr[0].dwUser = waveHdr[1].dwUser = 0L;
622 waveHdr[0].dwLoops = waveHdr[1].dwLoops = 0L;
623 waveHdr[0].dwFlags = waveHdr[1].dwFlags = 0L;
624 waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
625 if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
626 waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
627 dwRet = MCIERR_INTERNAL;
628 goto cleanUp;
631 whidx = 0;
632 left = min(wmw->dwLength, end - wmw->dwPosition);
633 wmw->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
634 wmw->dwEventCount = 1L; /* for first buffer */
636 TRACE("Playing (normalized) from byte=%lu for %lu bytes\n", wmw->dwPosition, left);
638 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
639 while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
640 count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
641 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
642 if (count < 1)
643 break;
644 /* count is always <= bufsize, so this is correct regarding the
645 * waveOutPrepareHeader function
647 waveHdr[whidx].dwBufferLength = count;
648 waveHdr[whidx].dwFlags &= ~WHDR_DONE;
649 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
650 &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
651 waveHdr[whidx].dwBytesRecorded);
652 dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
653 left -= count;
654 wmw->dwPosition += count;
655 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
657 WAVE_mciPlayWaitDone(wmw);
658 whidx ^= 1;
661 WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
663 /* just to get rid of some race conditions between play, stop and pause */
664 waveOutReset(wmw->hWave);
666 waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
667 waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
669 dwRet = 0;
671 cleanUp:
672 HeapFree(GetProcessHeap(), 0, waveHdr);
674 if (wmw->hWave) {
675 waveOutClose(wmw->hWave);
676 wmw->hWave = 0;
678 CloseHandle(wmw->hEvent);
680 if (lpParms && (dwFlags & MCI_NOTIFY)) {
681 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
682 wmw->wNotifyDeviceID,
683 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
686 wmw->dwStatus = MCI_MODE_STOP;
688 return dwRet;
691 /**************************************************************************
692 * WAVE_mciRecord [internal]
694 static DWORD WAVE_mciRecord(UINT wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
696 int start, end;
697 LONG bufsize;
698 WAVEHDR waveHdr;
699 DWORD dwRet;
700 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
702 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
704 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
705 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
707 if (!wmw->fInput) {
708 WARN("cannot record on output device\n");
709 return MCIERR_NONAPPLICABLE_FUNCTION;
712 if (wmw->hFile == 0) {
713 WARN("can't find file='%s' !\n",
714 wmw->openParms.lpstrElementName);
715 return MCIERR_FILE_NOT_FOUND;
717 start = 1; end = 99999;
718 if (dwFlags & MCI_FROM) {
719 start = lpParms->dwFrom;
720 TRACE("MCI_FROM=%d \n", start);
722 if (dwFlags & MCI_TO) {
723 end = lpParms->dwTo;
724 TRACE("MCI_TO=%d \n", end);
726 bufsize = 64000;
727 waveHdr.lpData = HeapAlloc(GetProcessHeap(), 0, bufsize);
728 waveHdr.dwBufferLength = bufsize;
729 waveHdr.dwUser = 0L;
730 waveHdr.dwFlags = 0L;
731 waveHdr.dwLoops = 0L;
732 dwRet = waveInPrepareHeader(wmw->hWave, &waveHdr, sizeof(WAVEHDR));
734 for (;;) { /* FIXME: I don't see any waveInAddBuffer ? */
735 waveHdr.dwBytesRecorded = 0;
736 dwRet = waveInStart(wmw->hWave);
737 TRACE("waveInStart => lpWaveHdr=%p dwBytesRecorded=%lu\n",
738 &waveHdr, waveHdr.dwBytesRecorded);
739 if (waveHdr.dwBytesRecorded == 0) break;
741 dwRet = waveInUnprepareHeader(wmw->hWave, &waveHdr, sizeof(WAVEHDR));
742 HeapFree(GetProcessHeap(), 0, waveHdr.lpData);
744 if (dwFlags & MCI_NOTIFY) {
745 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
746 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
748 return 0;
751 /**************************************************************************
752 * WAVE_mciPause [internal]
754 static DWORD WAVE_mciPause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
756 DWORD dwRet;
757 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
759 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
761 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
762 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
764 if (wmw->dwStatus == MCI_MODE_PLAY) {
765 wmw->dwStatus = MCI_MODE_PAUSE;
768 if (wmw->fInput) dwRet = waveInStop(wmw->hWave);
769 else dwRet = waveOutPause(wmw->hWave);
771 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
774 /**************************************************************************
775 * WAVE_mciResume [internal]
777 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
779 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
780 DWORD dwRet = 0;
782 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
784 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
786 if (wmw->dwStatus == MCI_MODE_PAUSE) {
787 wmw->dwStatus = MCI_MODE_PLAY;
790 if (wmw->fInput) dwRet = waveInStart(wmw->hWave);
791 else dwRet = waveOutRestart(wmw->hWave);
792 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
795 /**************************************************************************
796 * WAVE_mciSeek [internal]
798 static DWORD WAVE_mciSeek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
800 DWORD ret = 0;
801 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
803 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
805 if (lpParms == NULL) {
806 ret = MCIERR_NULL_PARAMETER_BLOCK;
807 } else if (wmw == NULL) {
808 ret = MCIERR_INVALID_DEVICE_ID;
809 } else {
810 WAVE_mciStop(wDevID, MCI_WAIT, 0);
812 if (dwFlags & MCI_SEEK_TO_START) {
813 wmw->dwPosition = 0;
814 } else if (dwFlags & MCI_SEEK_TO_END) {
815 wmw->dwPosition = wmw->dwLength;
816 } else if (dwFlags & MCI_TO) {
817 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
818 } else {
819 WARN("dwFlag doesn't tell where to seek to...\n");
820 return MCIERR_MISSING_PARAMETER;
823 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
825 if (dwFlags & MCI_NOTIFY) {
826 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
827 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
830 return ret;
833 /**************************************************************************
834 * WAVE_mciSet [internal]
836 static DWORD WAVE_mciSet(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
838 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
840 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
842 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
843 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
845 if (dwFlags & MCI_SET_TIME_FORMAT) {
846 switch (lpParms->dwTimeFormat) {
847 case MCI_FORMAT_MILLISECONDS:
848 TRACE("MCI_FORMAT_MILLISECONDS !\n");
849 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
850 break;
851 case MCI_FORMAT_BYTES:
852 TRACE("MCI_FORMAT_BYTES !\n");
853 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
854 break;
855 case MCI_FORMAT_SAMPLES:
856 TRACE("MCI_FORMAT_SAMPLES !\n");
857 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
858 break;
859 default:
860 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
861 return MCIERR_BAD_TIME_FORMAT;
864 if (dwFlags & MCI_SET_VIDEO) {
865 TRACE("No support for video !\n");
866 return MCIERR_UNSUPPORTED_FUNCTION;
868 if (dwFlags & MCI_SET_DOOR_OPEN) {
869 TRACE("No support for door open !\n");
870 return MCIERR_UNSUPPORTED_FUNCTION;
872 if (dwFlags & MCI_SET_DOOR_CLOSED) {
873 TRACE("No support for door close !\n");
874 return MCIERR_UNSUPPORTED_FUNCTION;
876 if (dwFlags & MCI_SET_AUDIO) {
877 if (dwFlags & MCI_SET_ON) {
878 TRACE("MCI_SET_ON audio !\n");
879 } else if (dwFlags & MCI_SET_OFF) {
880 TRACE("MCI_SET_OFF audio !\n");
881 } else {
882 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
883 return MCIERR_BAD_INTEGER;
886 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
887 TRACE("MCI_SET_AUDIO_ALL !\n");
888 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
889 TRACE("MCI_SET_AUDIO_LEFT !\n");
890 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
891 TRACE("MCI_SET_AUDIO_RIGHT !\n");
893 if (dwFlags & MCI_WAVE_INPUT)
894 TRACE("MCI_WAVE_INPUT !\n");
895 if (dwFlags & MCI_WAVE_OUTPUT)
896 TRACE("MCI_WAVE_OUTPUT !\n");
897 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
898 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
899 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
900 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
901 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
902 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
903 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
904 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
905 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
906 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
907 if (dwFlags & MCI_WAVE_SET_CHANNELS)
908 TRACE("MCI_WAVE_SET_CHANNELS !\n");
909 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
910 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
911 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
912 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
913 return 0;
916 /**************************************************************************
917 * WAVE_mciStatus [internal]
919 static DWORD WAVE_mciStatus(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
921 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
922 DWORD ret = 0;
924 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
925 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
926 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
928 if (dwFlags & MCI_STATUS_ITEM) {
929 switch (lpParms->dwItem) {
930 case MCI_STATUS_CURRENT_TRACK:
931 lpParms->dwReturn = 1;
932 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
933 break;
934 case MCI_STATUS_LENGTH:
935 if (!wmw->hFile) {
936 lpParms->dwReturn = 0;
937 return MCIERR_UNSUPPORTED_FUNCTION;
939 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
940 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength, &ret);
941 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
942 break;
943 case MCI_STATUS_MODE:
944 TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
945 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
946 ret = MCI_RESOURCE_RETURNED;
947 break;
948 case MCI_STATUS_MEDIA_PRESENT:
949 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
950 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
951 ret = MCI_RESOURCE_RETURNED;
952 break;
953 case MCI_STATUS_NUMBER_OF_TRACKS:
954 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
955 lpParms->dwReturn = 1;
956 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
957 break;
958 case MCI_STATUS_POSITION:
959 if (!wmw->hFile) {
960 lpParms->dwReturn = 0;
961 return MCIERR_UNSUPPORTED_FUNCTION;
963 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
964 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
965 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
966 &ret);
967 TRACE("MCI_STATUS_POSITION %s => %lu\n",
968 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
969 break;
970 case MCI_STATUS_READY:
971 lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
972 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
973 TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
974 ret = MCI_RESOURCE_RETURNED;
975 break;
976 case MCI_STATUS_TIME_FORMAT:
977 lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, wmw->dwMciTimeFormat);
978 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
979 ret = MCI_RESOURCE_RETURNED;
980 break;
981 case MCI_WAVE_INPUT:
982 TRACE("MCI_WAVE_INPUT !\n");
983 lpParms->dwReturn = 0;
984 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
985 break;
986 case MCI_WAVE_OUTPUT:
987 TRACE("MCI_WAVE_OUTPUT !\n");
989 UINT id;
990 if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
991 lpParms->dwReturn = id;
992 } else {
993 lpParms->dwReturn = 0;
994 ret = MCIERR_WAVE_INPUTUNSPECIFIED;
997 break;
998 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
999 if (!wmw->hFile) {
1000 lpParms->dwReturn = 0;
1001 return MCIERR_UNSUPPORTED_FUNCTION;
1003 lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1004 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
1005 break;
1006 case MCI_WAVE_STATUS_BITSPERSAMPLE:
1007 if (!wmw->hFile) {
1008 lpParms->dwReturn = 0;
1009 return MCIERR_UNSUPPORTED_FUNCTION;
1011 lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1012 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
1013 break;
1014 case MCI_WAVE_STATUS_BLOCKALIGN:
1015 if (!wmw->hFile) {
1016 lpParms->dwReturn = 0;
1017 return MCIERR_UNSUPPORTED_FUNCTION;
1019 lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1020 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
1021 break;
1022 case MCI_WAVE_STATUS_CHANNELS:
1023 if (!wmw->hFile) {
1024 lpParms->dwReturn = 0;
1025 return MCIERR_UNSUPPORTED_FUNCTION;
1027 lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1028 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
1029 break;
1030 case MCI_WAVE_STATUS_FORMATTAG:
1031 if (!wmw->hFile) {
1032 lpParms->dwReturn = 0;
1033 return MCIERR_UNSUPPORTED_FUNCTION;
1035 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1036 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
1037 break;
1038 case MCI_WAVE_STATUS_LEVEL:
1039 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1040 lpParms->dwReturn = 0xAAAA5555;
1041 break;
1042 case MCI_WAVE_STATUS_SAMPLESPERSEC:
1043 if (!wmw->hFile) {
1044 lpParms->dwReturn = 0;
1045 return MCIERR_UNSUPPORTED_FUNCTION;
1047 lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1048 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
1049 break;
1050 default:
1051 WARN("unknown command %08lX !\n", lpParms->dwItem);
1052 return MCIERR_UNRECOGNIZED_COMMAND;
1055 if (dwFlags & MCI_NOTIFY) {
1056 mciDriverNotify((HWND)LOWORD(lpParms->dwCallback),
1057 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1059 return ret;
1062 /**************************************************************************
1063 * WAVE_mciGetDevCaps [internal]
1065 static DWORD WAVE_mciGetDevCaps(UINT wDevID, DWORD dwFlags,
1066 LPMCI_GETDEVCAPS_PARMS lpParms)
1068 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1069 DWORD ret = 0;
1071 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1073 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1074 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
1076 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1077 switch(lpParms->dwItem) {
1078 case MCI_GETDEVCAPS_DEVICE_TYPE:
1079 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1080 ret = MCI_RESOURCE_RETURNED;
1081 break;
1082 case MCI_GETDEVCAPS_HAS_AUDIO:
1083 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1084 ret = MCI_RESOURCE_RETURNED;
1085 break;
1086 case MCI_GETDEVCAPS_HAS_VIDEO:
1087 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1088 ret = MCI_RESOURCE_RETURNED;
1089 break;
1090 case MCI_GETDEVCAPS_USES_FILES:
1091 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1092 ret = MCI_RESOURCE_RETURNED;
1093 break;
1094 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1095 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1096 ret = MCI_RESOURCE_RETURNED;
1097 break;
1098 case MCI_GETDEVCAPS_CAN_RECORD:
1099 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1100 ret = MCI_RESOURCE_RETURNED;
1101 break;
1102 case MCI_GETDEVCAPS_CAN_EJECT:
1103 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1104 ret = MCI_RESOURCE_RETURNED;
1105 break;
1106 case MCI_GETDEVCAPS_CAN_PLAY:
1107 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1108 ret = MCI_RESOURCE_RETURNED;
1109 break;
1110 case MCI_GETDEVCAPS_CAN_SAVE:
1111 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1112 ret = MCI_RESOURCE_RETURNED;
1113 break;
1114 case MCI_WAVE_GETDEVCAPS_INPUTS:
1115 lpParms->dwReturn = 1;
1116 break;
1117 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1118 lpParms->dwReturn = 1;
1119 break;
1120 default:
1121 FIXME("Unknown capability (%08lx) !\n", lpParms->dwItem);
1122 return MCIERR_UNRECOGNIZED_COMMAND;
1124 } else {
1125 WARN("No GetDevCaps-Item !\n");
1126 return MCIERR_UNRECOGNIZED_COMMAND;
1128 return ret;
1131 /**************************************************************************
1132 * WAVE_mciInfo [internal]
1134 static DWORD WAVE_mciInfo(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSA lpParms)
1136 DWORD ret = 0;
1137 LPCSTR str = 0;
1138 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
1140 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1142 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
1143 ret = MCIERR_NULL_PARAMETER_BLOCK;
1144 } else if (wmw == NULL) {
1145 ret = MCIERR_INVALID_DEVICE_ID;
1146 } else {
1147 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1149 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1150 case MCI_INFO_PRODUCT:
1151 str = "Wine's audio player";
1152 break;
1153 case MCI_INFO_FILE:
1154 str = wmw->openParms.lpstrElementName;
1155 break;
1156 case MCI_WAVE_INPUT:
1157 str = "Wine Wave In";
1158 break;
1159 case MCI_WAVE_OUTPUT:
1160 str = "Wine Wave Out";
1161 break;
1162 default:
1163 WARN("Don't know this info command (%lu)\n", dwFlags);
1164 ret = MCIERR_UNRECOGNIZED_COMMAND;
1167 if (str) {
1168 if (strlen(str) + 1 > lpParms->dwRetSize) {
1169 ret = MCIERR_PARAM_OVERFLOW;
1170 } else {
1171 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1173 } else {
1174 lpParms->lpstrReturn[0] = 0;
1177 return ret;
1180 /**************************************************************************
1181 * MCIWAVE_DriverProc [sample driver]
1183 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1184 DWORD dwParam1, DWORD dwParam2)
1186 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
1187 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1189 switch(wMsg) {
1190 case DRV_LOAD: return 1;
1191 case DRV_FREE: return 1;
1192 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
1193 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
1194 case DRV_ENABLE: return 1;
1195 case DRV_DISABLE: return 1;
1196 case DRV_QUERYCONFIGURE: return 1;
1197 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
1198 case DRV_INSTALL: return DRVCNF_RESTART;
1199 case DRV_REMOVE: return DRVCNF_RESTART;
1200 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
1201 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1202 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1203 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
1204 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
1205 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1206 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
1207 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1208 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
1209 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
1210 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
1211 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMSA) dwParam2);
1212 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
1213 /* commands that should be supported */
1214 case MCI_LOAD:
1215 case MCI_SAVE:
1216 case MCI_FREEZE:
1217 case MCI_PUT:
1218 case MCI_REALIZE:
1219 case MCI_UNFREEZE:
1220 case MCI_UPDATE:
1221 case MCI_WHERE:
1222 case MCI_STEP:
1223 case MCI_SPIN:
1224 case MCI_ESCAPE:
1225 case MCI_COPY:
1226 case MCI_CUT:
1227 case MCI_DELETE:
1228 case MCI_PASTE:
1229 FIXME("Unsupported yet command [%lu]\n", wMsg);
1230 break;
1231 case MCI_WINDOW:
1232 TRACE("Unsupported command [%lu]\n", wMsg);
1233 break;
1234 case MCI_OPEN:
1235 case MCI_CLOSE:
1236 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1237 break;
1238 default:
1239 FIXME("is probably wrong msg [%lu]\n", wMsg);
1240 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1242 return MCIERR_UNRECOGNIZED_COMMAND;