Fixed bugs on MCI notification (callback info are copied before
[wine/testsucceed.git] / multimedia / mciwave.c
blobf48fe12549c153e92305c003125bba3d1a7c7d67
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
5 * Copyright 1994 Martin Ayotte
6 */
7 /*
8 * FIXME:
9 * - record/play should and must be done asynchronous
10 * - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
13 #define EMULATE_SB16
15 #define DEBUG_MCIWAVE
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/ioctl.h>
22 #include "wine/winuser16.h"
23 #include "user.h"
24 #include "driver.h"
25 #include "mmsystem.h"
26 #include "heap.h"
27 #include "ldt.h"
28 #include "debug.h"
29 #include "multimedia.h"
31 #define MAX_MCIWAVEDRV (1)
33 typedef struct {
34 int nUseCount; /* Incremented for each shared open */
35 BOOL16 fShareable; /* TRUE if first open was shareable */
36 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
37 HANDLE16 hCallback; /* Callback handle for pending notification */
38 HMMIO32 hFile; /* mmio file handle open as Element */
39 MCI_WAVE_OPEN_PARMS32A openParms;
40 WAVEOPENDESC waveDesc;
41 PCMWAVEFORMAT WaveFormat;
42 WAVEHDR WaveHdr;
43 BOOL16 fInput; /* FALSE = Output, TRUE = Input */
44 WORD dwStatus; /* one from MCI_MODE_xxxx */
45 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
46 DWORD dwFileOffset; /* Offset of chunk in mmio file */
47 DWORD dwLength; /* number of bytes in chunk for playing */
48 DWORD dwPosition; /* position in bytes in chunk for playing */
49 } WINE_MCIWAVE;
51 static WINE_MCIWAVE MCIWaveDev[MAX_MCIWAVEDRV];
53 /*======================================================================*
54 * MCI WAVE implemantation *
55 *======================================================================*/
57 /**************************************************************************
58 * WAVE_mciGetOpenDev [internal]
60 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT16 wDevID)
62 if (wDevID >= MAX_MCIWAVEDRV || MCIWaveDev[wDevID].nUseCount == 0) {
63 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
64 return 0;
66 return &MCIWaveDev[wDevID];
69 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
71 DWORD ret = 0;
73 switch (wmw->dwMciTimeFormat) {
74 case MCI_FORMAT_MILLISECONDS:
75 ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
76 break;
77 case MCI_FORMAT_BYTES:
78 ret = val;
79 break;
80 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
81 ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
82 break;
83 default:
84 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
86 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
87 return ret;
90 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
92 DWORD ret = 0;
94 switch (wmw->dwMciTimeFormat) {
95 case MCI_FORMAT_MILLISECONDS:
96 ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
97 break;
98 case MCI_FORMAT_BYTES:
99 ret = val;
100 break;
101 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
102 ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
103 break;
104 default:
105 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
107 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
108 return ret;
111 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
113 MMCKINFO mmckInfo;
115 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
116 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
117 return MCIERR_INVALID_FILE;
118 TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
119 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
120 if (mmioRead32(wmw->hFile, (HPSTR)&wmw->WaveFormat,
121 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
122 return MCIERR_INVALID_FILE;
124 TRACE(mciwave, "wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
125 TRACE(mciwave, "nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
126 TRACE(mciwave, "nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
127 TRACE(mciwave, "nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
128 TRACE(mciwave, "nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
129 TRACE(mciwave, "wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
130 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
131 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
132 return MCIERR_INVALID_FILE;
133 TRACE(mciwave,"Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
134 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
135 TRACE(mciwave, "nChannels=%d nSamplesPerSec=%ld\n",
136 wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
137 wmw->dwLength = mmckInfo.cksize;
138 wmw->dwFileOffset = mmioSeek32(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
139 return 0;
142 /**************************************************************************
143 * WAVE_mciOpen [internal]
145 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMS32A lpOpenParms)
147 DWORD dwRet = 0;
148 DWORD dwDeviceID;
149 WINE_MCIWAVE* wmw;
151 TRACE(mciwave,"(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
152 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
154 if (wDevID >= MAX_MCIWAVEDRV) {
155 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
156 return MCIERR_INVALID_DEVICE_ID;
158 if (dwFlags & MCI_OPEN_SHAREABLE)
159 return MCIERR_HARDWARE;
161 wmw = &MCIWaveDev[wDevID];
163 if (wmw->nUseCount > 0) {
164 /* The driver is already opened on this channel
165 * Wave driver cannot be shared
167 return MCIERR_DEVICE_OPEN;
169 wmw->nUseCount++;
171 dwDeviceID = lpOpenParms->wDeviceID;
173 wmw->fInput = FALSE;
175 TRACE(mciwave, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
177 if (dwFlags & MCI_OPEN_ELEMENT) {
178 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
179 /* could it be that (DWORD)lpOpenParms->lpstrElementName
180 * contains the hFile value ?
182 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
183 } else {
184 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
186 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
187 TRACE(mciwave,"MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
188 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
189 wmw->hFile = mmioOpen32A((LPSTR)lpstrElementName, NULL,
190 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
191 if (wmw->hFile == 0) {
192 WARN(mciwave, "can't find file='%s' !\n", lpstrElementName);
193 dwRet = MCIERR_FILE_NOT_FOUND;
195 } else {
196 wmw->hFile = 0;
200 TRACE(mciwave,"hFile=%u\n", wmw->hFile);
202 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMS32A));
203 wmw->wNotifyDeviceID = dwDeviceID;
204 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
206 wmw->waveDesc.hWave = 0;
208 if (dwRet == 0 && wmw->hFile != 0) {
209 MMCKINFO ckMainRIFF;
211 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
212 dwRet = MCIERR_INVALID_FILE;
213 } else {
214 TRACE(mciwave, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
215 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
216 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
217 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
218 dwRet = MCIERR_INVALID_FILE;
219 } else {
220 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
223 } else {
224 wmw->dwLength = 0;
226 if (dwRet == 0) {
227 wmw->WaveFormat.wf.nAvgBytesPerSec =
228 wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
229 wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
230 wmw->dwPosition = 0;
232 /* By default the device will be opened for output, the MCI_CUE function is there to
233 * change from output to input and back
235 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
236 wmw->dwStatus = MCI_MODE_STOP;
237 } else {
238 wmw->nUseCount--;
239 if (wmw->hFile != 0)
240 mmioClose32(wmw->hFile, 0);
241 wmw->hFile = 0;
243 return 0;
246 /**************************************************************************
247 * WAVE_mciCue [internal]
249 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
252 FIXME
254 This routine is far from complete. At the moment only a check is done on the
255 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
256 is the default.
258 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
259 are ignored
262 DWORD dwRet;
263 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
265 TRACE(mciwave,"(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
267 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
269 /* always close elements ? */
270 if (wmw->hFile != 0) {
271 mmioClose32(wmw->hFile, 0);
272 wmw->hFile = 0;
275 dwRet = MMSYSERR_NOERROR; /* assume success */
277 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
278 dwRet = wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
279 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
280 dwRet = widMessage(wDevID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
281 wmw->fInput = TRUE;
282 } else if (wmw->fInput) {
283 dwRet = widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L);
284 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
285 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
286 wmw->fInput = FALSE;
288 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
291 /**************************************************************************
292 * WAVE_mciStop [internal]
294 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
296 DWORD dwRet;
297 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
299 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
301 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
302 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
304 wmw->dwStatus = MCI_MODE_STOP;
305 wmw->dwPosition = 0;
306 TRACE(mciwave, "wmw->dwStatus=%d\n", wmw->dwStatus);
308 if (wmw->fInput)
309 dwRet = widMessage(wDevID, WIDM_STOP, 0, dwFlags, (DWORD)lpParms);
310 else
311 dwRet = wodMessage(wDevID, WODM_STOP, 0, dwFlags, (DWORD)lpParms);
313 if (dwFlags & MCI_NOTIFY) {
314 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
315 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
316 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
319 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
322 /**************************************************************************
323 * WAVE_mciClose [internal]
325 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
327 DWORD dwRet = 0;
328 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
330 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
332 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
333 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
335 if (wmw->dwStatus != MCI_MODE_STOP) {
336 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
339 wmw->dwStatus = MCI_MODE_STOP;
340 wmw->nUseCount--;
342 if (wmw->nUseCount == 0) {
343 DWORD mmRet;
344 if (wmw->hFile != 0) {
345 mmioClose32(wmw->hFile, 0);
346 wmw->hFile = 0;
348 mmRet = (wmw->fInput) ? widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L) :
349 wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
351 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
354 if (dwFlags & MCI_NOTIFY) {
355 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
356 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
357 wmw->wNotifyDeviceID,
358 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
360 return 0;
363 /**************************************************************************
364 * WAVE_mciPlay [internal]
366 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
368 DWORD end;
369 LONG bufsize, count;
370 HGLOBAL16 hData;
371 DWORD dwRet;
372 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
374 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
376 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
377 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
379 if (wmw->fInput) {
380 WARN(mciwave, "cannot play on input device\n");
381 return MCIERR_NONAPPLICABLE_FUNCTION;
384 if (wmw->hFile == 0) {
385 WARN(mciwave, "Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
386 return MCIERR_FILE_NOT_FOUND;
389 if (!(dwFlags & MCI_WAIT)) {
390 return MCI_SendCommandAsync32(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
391 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
394 end = 0xFFFFFFFF;
395 if (lpParms && (dwFlags & MCI_FROM)) {
396 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
398 if (lpParms && (dwFlags & MCI_TO)) {
399 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
402 TRACE(mciwave, "Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
404 /* go back to begining of chunk */
405 mmioSeek32(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
407 /* at 22050 bytes per sec => 30 ms by block */
408 bufsize = 10240;
409 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
410 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
412 wmw->dwStatus = MCI_MODE_PLAY;
414 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
415 while (wmw->dwStatus != MCI_MODE_STOP) {
416 wmw->WaveHdr.dwUser = 0L;
417 wmw->WaveHdr.dwFlags = 0L;
418 wmw->WaveHdr.dwLoops = 0L;
419 dwRet = wodMessage(wDevID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
420 count = mmioRead32(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
421 TRACE(mciwave,"mmioRead bufsize=%ld count=%ld\n", bufsize, count);
422 if (count < 1) break;
423 wmw->WaveHdr.dwBufferLength = count;
424 wmw->WaveHdr.dwBytesRecorded = 0;
425 TRACE(mciwave,"before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
426 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
427 dwRet = wodMessage(wDevID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
428 /* FIXME: should use callback mechanisms from audio driver */
429 #if 1
430 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
431 Sleep(1);
432 #endif
433 wmw->dwPosition += count;
434 TRACE(mciwave,"after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
435 dwRet = wodMessage(wDevID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
438 if (wmw->WaveHdr.lpData != NULL) {
439 GlobalUnlock16(hData);
440 GlobalFree16(hData);
441 wmw->WaveHdr.lpData = NULL;
443 wmw->dwStatus = MCI_MODE_STOP;
444 if (lpParms && (dwFlags & MCI_NOTIFY)) {
445 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
446 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
447 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
449 return 0;
452 /**************************************************************************
453 * WAVE_mciRecord [internal]
455 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
457 int start, end;
458 LONG bufsize;
459 HGLOBAL16 hData;
460 LPWAVEHDR lpWaveHdr;
461 DWORD dwRet;
462 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
464 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
466 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
467 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
469 if (!wmw->fInput) {
470 WARN(mciwave, "cannot record on output device\n");
471 return MCIERR_NONAPPLICABLE_FUNCTION;
474 if (wmw->hFile == 0) {
475 WARN(mciwave, "can't find file='%s' !\n",
476 wmw->openParms.lpstrElementName);
477 return MCIERR_FILE_NOT_FOUND;
479 start = 1; end = 99999;
480 if (dwFlags & MCI_FROM) {
481 start = lpParms->dwFrom;
482 TRACE(mciwave, "MCI_FROM=%d \n", start);
484 if (dwFlags & MCI_TO) {
485 end = lpParms->dwTo;
486 TRACE(mciwave,"MCI_TO=%d \n", end);
488 bufsize = 64000;
489 lpWaveHdr = &wmw->WaveHdr;
490 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
491 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
492 lpWaveHdr->dwBufferLength = bufsize;
493 lpWaveHdr->dwUser = 0L;
494 lpWaveHdr->dwFlags = 0L;
495 lpWaveHdr->dwLoops = 0L;
496 dwRet=widMessage(wDevID,WIDM_PREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
497 TRACE(mciwave,"after WIDM_PREPARE \n");
498 while (TRUE) {
499 lpWaveHdr->dwBytesRecorded = 0;
500 dwRet = widMessage(wDevID, WIDM_START, 0, 0L, 0L);
501 TRACE(mciwave, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
502 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
503 if (lpWaveHdr->dwBytesRecorded == 0) break;
505 TRACE(mciwave,"before WIDM_UNPREPARE \n");
506 dwRet = widMessage(wDevID,WIDM_UNPREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
507 TRACE(mciwave,"after WIDM_UNPREPARE \n");
508 if (lpWaveHdr->lpData != NULL) {
509 GlobalUnlock16(hData);
510 GlobalFree16(hData);
511 lpWaveHdr->lpData = NULL;
513 if (dwFlags & MCI_NOTIFY) {
514 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
515 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
516 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
518 return 0;
521 /**************************************************************************
522 * WAVE_mciPause [internal]
524 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
526 DWORD dwRet;
527 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
529 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
531 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
532 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
534 if (wmw->dwStatus == MCI_MODE_PLAY) {
535 wmw->dwStatus = MCI_MODE_PAUSE;
538 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PAUSE, 0, dwFlags, (DWORD)lpParms);
539 else dwRet = wodMessage(wDevID, WODM_PAUSE, 0, dwFlags, (DWORD)lpParms);
541 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
544 /**************************************************************************
545 * WAVE_mciResume [internal]
547 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
549 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
550 DWORD dwRet = 0;
552 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
554 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
555 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
557 if (wmw->dwStatus == MCI_MODE_PAUSE) {
558 wmw->dwStatus = MCI_MODE_PLAY;
561 #if 0
562 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PLAY, 0, dwFlags, (DWORD)lpParms);
563 else dwRet = wodMessage(wDevID, WODM_PLAY, 0, dwFlags, (DWORD)lpParms);
564 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
565 #else
566 return dwRet;
567 #endif
571 /**************************************************************************
572 * WAVE_mciSeek [internal]
574 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
576 DWORD ret = 0;
577 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
579 TRACE(mciwave, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
581 if (lpParms == NULL) {
582 ret = MCIERR_NULL_PARAMETER_BLOCK;
583 } else if (wmw == NULL) {
584 ret = MCIERR_INVALID_DEVICE_ID;
585 } else {
586 WAVE_mciStop(wDevID, MCI_WAIT, 0);
588 if (dwFlags & MCI_SEEK_TO_START) {
589 wmw->dwPosition = 0;
590 } else if (dwFlags & MCI_SEEK_TO_END) {
591 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
592 } else if (dwFlags & MCI_TO) {
593 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
594 } else {
595 WARN(mciwave, "dwFlag doesn't tell where to seek to...\n");
596 return MCIERR_MISSING_PARAMETER;
599 TRACE(mciwave, "Seeking to position=%lu bytes\n", wmw->dwPosition);
601 if (dwFlags & MCI_NOTIFY) {
602 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
603 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
604 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
607 return ret;
610 /**************************************************************************
611 * WAVE_mciSet [internal]
613 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
615 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
617 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
619 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
620 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
622 if (dwFlags & MCI_SET_TIME_FORMAT) {
623 switch (lpParms->dwTimeFormat) {
624 case MCI_FORMAT_MILLISECONDS:
625 TRACE(mciwave, "MCI_FORMAT_MILLISECONDS !\n");
626 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
627 break;
628 case MCI_FORMAT_BYTES:
629 TRACE(mciwave, "MCI_FORMAT_BYTES !\n");
630 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
631 break;
632 case MCI_FORMAT_SAMPLES:
633 TRACE(mciwave, "MCI_FORMAT_SAMPLES !\n");
634 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
635 break;
636 default:
637 WARN(mciwave,"Bad time format %lu!\n", lpParms->dwTimeFormat);
638 return MCIERR_BAD_TIME_FORMAT;
641 if (dwFlags & MCI_SET_VIDEO) {
642 TRACE(mciwave, "No support for video !\n");
643 return MCIERR_UNSUPPORTED_FUNCTION;
645 if (dwFlags & MCI_SET_DOOR_OPEN) {
646 TRACE(mciwave, "No support for door open !\n");
647 return MCIERR_UNSUPPORTED_FUNCTION;
649 if (dwFlags & MCI_SET_DOOR_CLOSED) {
650 TRACE(mciwave, "No support for door close !\n");
651 return MCIERR_UNSUPPORTED_FUNCTION;
653 if (dwFlags & MCI_SET_AUDIO) {
654 if (dwFlags & MCI_SET_ON) {
655 TRACE(mciwave, "MCI_SET_ON audio !\n");
656 } else if (dwFlags & MCI_SET_OFF) {
657 TRACE(mciwave, "MCI_SET_OFF audio !\n");
658 } else {
659 WARN(mciwave, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
660 return MCIERR_BAD_INTEGER;
663 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
664 TRACE(mciwave, "MCI_SET_AUDIO_ALL !\n");
665 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
666 TRACE(mciwave, "MCI_SET_AUDIO_LEFT !\n");
667 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
668 TRACE(mciwave, "MCI_SET_AUDIO_RIGHT !\n");
670 if (dwFlags & MCI_WAVE_INPUT)
671 TRACE(mciwave, "MCI_WAVE_INPUT !\n");
672 if (dwFlags & MCI_WAVE_OUTPUT)
673 TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
674 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
675 TRACE(mciwave, "MCI_WAVE_SET_ANYINPUT !\n");
676 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
677 TRACE(mciwave, "MCI_WAVE_SET_ANYOUTPUT !\n");
678 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
679 TRACE(mciwave, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
680 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
681 TRACE(mciwave, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
682 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
683 TRACE(mciwave, "MCI_WAVE_SET_BLOCKALIGN !\n");
684 if (dwFlags & MCI_WAVE_SET_CHANNELS)
685 TRACE(mciwave, "MCI_WAVE_SET_CHANNELS !\n");
686 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
687 TRACE(mciwave, "MCI_WAVE_SET_FORMATTAG !\n");
688 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
689 TRACE(mciwave, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
690 return 0;
693 /**************************************************************************
694 * WAVE_mciStatus [internal]
696 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
698 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
700 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
701 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
702 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
704 if (dwFlags & MCI_STATUS_ITEM) {
705 switch(lpParms->dwItem) {
706 case MCI_STATUS_CURRENT_TRACK:
707 lpParms->dwReturn = 1;
708 TRACE(mciwave, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
709 break;
710 case MCI_STATUS_LENGTH:
711 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
712 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
713 TRACE(mciwave, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
714 break;
715 case MCI_STATUS_MODE:
716 lpParms->dwReturn = wmw->dwStatus;
717 TRACE(mciwave, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
718 break;
719 case MCI_STATUS_MEDIA_PRESENT:
720 TRACE(mciwave, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
721 lpParms->dwReturn = TRUE;
722 break;
723 case MCI_STATUS_NUMBER_OF_TRACKS:
724 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
725 lpParms->dwReturn = 1;
726 TRACE(mciwave, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
727 break;
728 case MCI_STATUS_POSITION:
729 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
730 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
731 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
732 TRACE(mciwave, "MCI_STATUS_POSITION %s => %lu\n",
733 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
734 break;
735 case MCI_STATUS_READY:
736 lpParms->dwReturn = (wmw->dwStatus != MCI_MODE_NOT_READY);
737 TRACE(mciwave,"MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
738 break;
739 case MCI_STATUS_TIME_FORMAT:
740 lpParms->dwReturn = wmw->dwMciTimeFormat;
741 TRACE(mciwave, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
742 break;
743 case MCI_WAVE_INPUT:
744 TRACE(mciwave,"MCI_WAVE_INPUT !\n");
745 lpParms->dwReturn = 0;
746 break;
747 case MCI_WAVE_OUTPUT:
748 TRACE(mciwave,"MCI_WAVE_OUTPUT !\n");
749 lpParms->dwReturn = 0;
750 break;
751 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
752 lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
753 TRACE(mciwave,"MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
754 break;
755 case MCI_WAVE_STATUS_BITSPERSAMPLE:
756 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
757 TRACE(mciwave,"MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
758 break;
759 case MCI_WAVE_STATUS_BLOCKALIGN:
760 lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
761 TRACE(mciwave,"MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
762 break;
763 case MCI_WAVE_STATUS_CHANNELS:
764 lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
765 TRACE(mciwave,"MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
766 break;
767 case MCI_WAVE_STATUS_FORMATTAG:
768 lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
769 TRACE(mciwave,"MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
770 break;
771 case MCI_WAVE_STATUS_LEVEL:
772 TRACE(mciwave,"MCI_WAVE_STATUS_LEVEL !\n");
773 lpParms->dwReturn = 0xAAAA5555;
774 break;
775 case MCI_WAVE_STATUS_SAMPLESPERSEC:
776 lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
777 TRACE(mciwave,"MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
778 break;
779 default:
780 WARN(mciwave,"unknown command %08lX !\n", lpParms->dwItem);
781 return MCIERR_UNRECOGNIZED_COMMAND;
784 if (dwFlags & MCI_NOTIFY) {
785 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
786 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
787 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
789 return 0;
792 /**************************************************************************
793 * WAVE_mciGetDevCaps [internal]
795 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
796 LPMCI_GETDEVCAPS_PARMS lpParms)
798 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
800 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
802 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
803 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
805 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
806 switch(lpParms->dwItem) {
807 case MCI_GETDEVCAPS_DEVICE_TYPE:
808 lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
809 break;
810 case MCI_GETDEVCAPS_HAS_AUDIO:
811 lpParms->dwReturn = TRUE;
812 break;
813 case MCI_GETDEVCAPS_HAS_VIDEO:
814 lpParms->dwReturn = FALSE;
815 break;
816 case MCI_GETDEVCAPS_USES_FILES:
817 lpParms->dwReturn = TRUE;
818 break;
819 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
820 lpParms->dwReturn = TRUE;
821 break;
822 case MCI_GETDEVCAPS_CAN_RECORD:
823 lpParms->dwReturn = TRUE;
824 break;
825 case MCI_GETDEVCAPS_CAN_EJECT:
826 lpParms->dwReturn = FALSE;
827 break;
828 case MCI_GETDEVCAPS_CAN_PLAY:
829 lpParms->dwReturn = TRUE;
830 break;
831 case MCI_GETDEVCAPS_CAN_SAVE:
832 lpParms->dwReturn = TRUE;
833 break;
834 case MCI_WAVE_GETDEVCAPS_INPUTS:
835 lpParms->dwReturn = 1;
836 break;
837 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
838 lpParms->dwReturn = 1;
839 break;
840 default:
841 TRACE(mciwave, "Unknown capability (%08lx) !\n", lpParms->dwItem);
842 return MCIERR_UNRECOGNIZED_COMMAND;
845 return 0;
848 /**************************************************************************
849 * WAVE_mciInfo [internal]
851 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
853 DWORD ret = 0;
854 LPCSTR str = 0;
855 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
857 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
859 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
860 ret = MCIERR_NULL_PARAMETER_BLOCK;
861 } else if (wmw == NULL) {
862 ret = MCIERR_INVALID_DEVICE_ID;
863 } else {
864 TRACE(mciwave, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
866 switch(dwFlags) {
867 case MCI_INFO_PRODUCT:
868 str = "Wine's audio player";
869 break;
870 case MCI_INFO_FILE:
871 str = wmw->openParms.lpstrElementName;
872 break;
873 case MCI_WAVE_INPUT:
874 str = "Wine Wave In";
875 break;
876 case MCI_WAVE_OUTPUT:
877 str = "Wine Wave Out";
878 break;
879 default:
880 WARN(mciwave, "Don't know this info command (%lu)\n", dwFlags);
881 ret = MCIERR_UNRECOGNIZED_COMMAND;
884 if (str) {
885 if (strlen(str) + 1 > lpParms->dwRetSize) {
886 ret = MCIERR_PARAM_OVERFLOW;
887 } else {
888 lstrcpyn32A(lpParms->lpstrReturn, str, lpParms->dwRetSize);
890 } else {
891 lpParms->lpstrReturn[0] = 0;
894 return ret;
897 /**************************************************************************
898 * WAVE_DriverProc32 [sample driver]
900 LONG MCIWAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
901 DWORD dwParam1, DWORD dwParam2)
903 TRACE(mciwave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n",
904 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
906 switch(wMsg) {
907 case DRV_LOAD: return 1;
908 case DRV_FREE: return 1;
909 case DRV_OPEN: return 1;
910 case DRV_CLOSE: return 1;
911 case DRV_ENABLE: return 1;
912 case DRV_DISABLE: return 1;
913 case DRV_QUERYCONFIGURE: return 1;
914 case DRV_CONFIGURE: MessageBox32A(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
915 case DRV_INSTALL: return DRVCNF_RESTART;
916 case DRV_REMOVE: return DRVCNF_RESTART;
917 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMS32A)dwParam2);
918 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
919 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
920 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
921 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
922 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
923 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
924 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
925 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
926 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
927 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
928 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMS16) dwParam2);
929 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
930 case MCI_LOAD:
931 case MCI_SAVE:
932 case MCI_FREEZE:
933 case MCI_PUT:
934 case MCI_REALIZE:
935 case MCI_UNFREEZE:
936 case MCI_UPDATE:
937 case MCI_WHERE:
938 case MCI_WINDOW:
939 case MCI_STEP:
940 case MCI_SPIN:
941 case MCI_ESCAPE:
942 case MCI_COPY:
943 case MCI_CUT:
944 case MCI_DELETE:
945 case MCI_PASTE:
946 WARN(mciwave, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
947 break;
948 case MCI_OPEN:
949 case MCI_CLOSE:
950 FIXME(mciwave, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
951 break;
952 default:
953 FIXME(mciwave, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
954 return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
956 return MCIERR_UNRECOGNIZED_COMMAND;