Fixes buffer overrun problems with GetDIBits.
[wine.git] / multimedia / mciwave.c
blob2e282ce16a53dacd95561db6ab30711e95f21d28
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 "driver.h"
24 #include "multimedia.h"
25 #include "mmsystem.h"
26 #include "heap.h"
27 #include "debug.h"
29 #define MAX_MCIWAVEDRV (1)
31 typedef struct {
32 int nUseCount; /* Incremented for each shared open */
33 BOOL16 fShareable; /* TRUE if first open was shareable */
34 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
35 HANDLE16 hCallback; /* Callback handle for pending notification */
36 HMMIO32 hFile; /* mmio file handle open as Element */
37 MCI_WAVE_OPEN_PARMS32A openParms;
38 WAVEOPENDESC waveDesc;
39 PCMWAVEFORMAT WaveFormat;
40 WAVEHDR WaveHdr;
41 BOOL16 fInput; /* FALSE = Output, TRUE = Input */
42 WORD dwStatus; /* one from MCI_MODE_xxxx */
43 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
44 DWORD dwFileOffset; /* Offset of chunk in mmio file */
45 DWORD dwLength; /* number of bytes in chunk for playing */
46 DWORD dwPosition; /* position in bytes in chunk for playing */
47 } WINE_MCIWAVE;
49 static WINE_MCIWAVE MCIWaveDev[MAX_MCIWAVEDRV];
51 /*======================================================================*
52 * MCI WAVE implemantation *
53 *======================================================================*/
55 /**************************************************************************
56 * WAVE_mciGetOpenDev [internal]
58 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT16 wDevID)
60 if (wDevID >= MAX_MCIWAVEDRV || MCIWaveDev[wDevID].nUseCount == 0) {
61 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
62 return 0;
64 return &MCIWaveDev[wDevID];
67 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
69 DWORD ret = 0;
71 switch (wmw->dwMciTimeFormat) {
72 case MCI_FORMAT_MILLISECONDS:
73 ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
74 break;
75 case MCI_FORMAT_BYTES:
76 ret = val;
77 break;
78 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
79 ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
80 break;
81 default:
82 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
84 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
85 return ret;
88 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
90 DWORD ret = 0;
92 switch (wmw->dwMciTimeFormat) {
93 case MCI_FORMAT_MILLISECONDS:
94 ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
95 break;
96 case MCI_FORMAT_BYTES:
97 ret = val;
98 break;
99 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
100 ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
101 break;
102 default:
103 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
105 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
106 return ret;
109 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
111 MMCKINFO mmckInfo;
113 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
114 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
115 return MCIERR_INVALID_FILE;
116 TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
117 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
118 if (mmioRead32(wmw->hFile, (HPSTR)&wmw->WaveFormat,
119 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
120 return MCIERR_INVALID_FILE;
122 TRACE(mciwave, "wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
123 TRACE(mciwave, "nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
124 TRACE(mciwave, "nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
125 TRACE(mciwave, "nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
126 TRACE(mciwave, "nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
127 TRACE(mciwave, "wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
128 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
129 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
130 return MCIERR_INVALID_FILE;
131 TRACE(mciwave,"Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
132 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
133 TRACE(mciwave, "nChannels=%d nSamplesPerSec=%ld\n",
134 wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
135 wmw->dwLength = mmckInfo.cksize;
136 wmw->dwFileOffset = mmioSeek32(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
137 return 0;
140 /**************************************************************************
141 * WAVE_mciOpen [internal]
143 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMS32A lpOpenParms)
145 DWORD dwRet = 0;
146 DWORD dwDeviceID;
147 WINE_MCIWAVE* wmw;
149 TRACE(mciwave,"(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
150 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
152 if (wDevID >= MAX_MCIWAVEDRV) {
153 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
154 return MCIERR_INVALID_DEVICE_ID;
156 if (dwFlags & MCI_OPEN_SHAREABLE)
157 return MCIERR_HARDWARE;
159 wmw = &MCIWaveDev[wDevID];
161 if (wmw->nUseCount > 0) {
162 /* The driver is already opened on this channel
163 * Wave driver cannot be shared
165 return MCIERR_DEVICE_OPEN;
167 wmw->nUseCount++;
169 dwDeviceID = lpOpenParms->wDeviceID;
171 wmw->fInput = FALSE;
173 TRACE(mciwave, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
175 if (dwFlags & MCI_OPEN_ELEMENT) {
176 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
177 /* could it be that (DWORD)lpOpenParms->lpstrElementName
178 * contains the hFile value ?
180 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
181 } else {
182 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
184 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
185 TRACE(mciwave,"MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
186 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
187 wmw->hFile = mmioOpen32A((LPSTR)lpstrElementName, NULL,
188 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
189 if (wmw->hFile == 0) {
190 WARN(mciwave, "can't find file='%s' !\n", lpstrElementName);
191 dwRet = MCIERR_FILE_NOT_FOUND;
193 } else {
194 wmw->hFile = 0;
198 TRACE(mciwave,"hFile=%u\n", wmw->hFile);
200 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMS32A));
201 wmw->wNotifyDeviceID = dwDeviceID;
202 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
204 wmw->waveDesc.hWave = 0;
206 if (dwRet == 0 && wmw->hFile != 0) {
207 MMCKINFO ckMainRIFF;
209 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
210 dwRet = MCIERR_INVALID_FILE;
211 } else {
212 TRACE(mciwave, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
213 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
214 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
215 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
216 dwRet = MCIERR_INVALID_FILE;
217 } else {
218 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
221 } else {
222 wmw->dwLength = 0;
224 if (dwRet == 0) {
225 wmw->WaveFormat.wf.nAvgBytesPerSec =
226 wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
227 wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
228 wmw->dwPosition = 0;
230 /* By default the device will be opened for output, the MCI_CUE function is there to
231 * change from output to input and back
233 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
234 wmw->dwStatus = MCI_MODE_STOP;
235 } else {
236 wmw->nUseCount--;
237 if (wmw->hFile != 0)
238 mmioClose32(wmw->hFile, 0);
239 wmw->hFile = 0;
241 return 0;
244 /**************************************************************************
245 * WAVE_mciCue [internal]
247 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
250 FIXME
252 This routine is far from complete. At the moment only a check is done on the
253 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
254 is the default.
256 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
257 are ignored
260 DWORD dwRet;
261 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
263 TRACE(mciwave,"(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
265 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
267 /* always close elements ? */
268 if (wmw->hFile != 0) {
269 mmioClose32(wmw->hFile, 0);
270 wmw->hFile = 0;
273 dwRet = MMSYSERR_NOERROR; /* assume success */
275 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
276 dwRet = wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
277 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
278 dwRet = widMessage(wDevID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
279 wmw->fInput = TRUE;
280 } else if (wmw->fInput) {
281 dwRet = widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L);
282 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
283 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
284 wmw->fInput = FALSE;
286 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
289 /**************************************************************************
290 * WAVE_mciStop [internal]
292 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
294 DWORD dwRet;
295 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
297 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
299 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
300 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
302 wmw->dwStatus = MCI_MODE_STOP;
303 wmw->dwPosition = 0;
304 TRACE(mciwave, "wmw->dwStatus=%d\n", wmw->dwStatus);
306 if (wmw->fInput)
307 dwRet = widMessage(wDevID, WIDM_STOP, 0, dwFlags, (DWORD)lpParms);
308 else
309 dwRet = wodMessage(wDevID, WODM_STOP, 0, dwFlags, (DWORD)lpParms);
311 if (dwFlags & MCI_NOTIFY) {
312 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
313 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
314 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
317 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
320 /**************************************************************************
321 * WAVE_mciClose [internal]
323 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
325 DWORD dwRet = 0;
326 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
328 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
330 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
331 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
333 if (wmw->dwStatus != MCI_MODE_STOP) {
334 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
337 wmw->dwStatus = MCI_MODE_STOP;
338 wmw->nUseCount--;
340 if (wmw->nUseCount == 0) {
341 DWORD mmRet;
342 if (wmw->hFile != 0) {
343 mmioClose32(wmw->hFile, 0);
344 wmw->hFile = 0;
346 mmRet = (wmw->fInput) ? widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L) :
347 wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
349 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
352 if (dwFlags & MCI_NOTIFY) {
353 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
354 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
355 wmw->wNotifyDeviceID,
356 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
358 return 0;
361 /**************************************************************************
362 * WAVE_mciPlay [internal]
364 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
366 DWORD end;
367 LONG bufsize, count;
368 HGLOBAL16 hData;
369 DWORD dwRet;
370 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
372 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
374 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
375 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
377 if (wmw->fInput) {
378 WARN(mciwave, "cannot play on input device\n");
379 return MCIERR_NONAPPLICABLE_FUNCTION;
382 if (wmw->hFile == 0) {
383 WARN(mciwave, "Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
384 return MCIERR_FILE_NOT_FOUND;
387 if (!(dwFlags & MCI_WAIT)) {
388 return MCI_SendCommandAsync32(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
389 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
392 end = 0xFFFFFFFF;
393 if (lpParms && (dwFlags & MCI_FROM)) {
394 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
396 if (lpParms && (dwFlags & MCI_TO)) {
397 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
400 TRACE(mciwave, "Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
402 /* go back to begining of chunk */
403 mmioSeek32(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
405 /* at 22050 bytes per sec => 30 ms by block */
406 bufsize = 10240;
407 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
408 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
410 wmw->dwStatus = MCI_MODE_PLAY;
412 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
413 while (wmw->dwStatus != MCI_MODE_STOP) {
414 wmw->WaveHdr.dwUser = 0L;
415 wmw->WaveHdr.dwFlags = 0L;
416 wmw->WaveHdr.dwLoops = 0L;
417 dwRet = wodMessage(wDevID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
418 count = mmioRead32(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
419 TRACE(mciwave,"mmioRead bufsize=%ld count=%ld\n", bufsize, count);
420 if (count < 1) break;
421 wmw->WaveHdr.dwBufferLength = count;
422 wmw->WaveHdr.dwBytesRecorded = 0;
423 TRACE(mciwave,"before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
424 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
425 dwRet = wodMessage(wDevID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
426 /* FIXME: should use callback mechanisms from audio driver */
427 #if 1
428 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
429 Sleep(1);
430 #endif
431 wmw->dwPosition += count;
432 TRACE(mciwave,"after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
433 dwRet = wodMessage(wDevID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
436 if (wmw->WaveHdr.lpData != NULL) {
437 GlobalUnlock16(hData);
438 GlobalFree16(hData);
439 wmw->WaveHdr.lpData = NULL;
441 wmw->dwStatus = MCI_MODE_STOP;
442 if (lpParms && (dwFlags & MCI_NOTIFY)) {
443 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
444 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
445 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
447 return 0;
450 /**************************************************************************
451 * WAVE_mciRecord [internal]
453 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
455 int start, end;
456 LONG bufsize;
457 HGLOBAL16 hData;
458 LPWAVEHDR lpWaveHdr;
459 DWORD dwRet;
460 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
462 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
464 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
465 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
467 if (!wmw->fInput) {
468 WARN(mciwave, "cannot record on output device\n");
469 return MCIERR_NONAPPLICABLE_FUNCTION;
472 if (wmw->hFile == 0) {
473 WARN(mciwave, "can't find file='%s' !\n",
474 wmw->openParms.lpstrElementName);
475 return MCIERR_FILE_NOT_FOUND;
477 start = 1; end = 99999;
478 if (dwFlags & MCI_FROM) {
479 start = lpParms->dwFrom;
480 TRACE(mciwave, "MCI_FROM=%d \n", start);
482 if (dwFlags & MCI_TO) {
483 end = lpParms->dwTo;
484 TRACE(mciwave,"MCI_TO=%d \n", end);
486 bufsize = 64000;
487 lpWaveHdr = &wmw->WaveHdr;
488 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
489 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
490 lpWaveHdr->dwBufferLength = bufsize;
491 lpWaveHdr->dwUser = 0L;
492 lpWaveHdr->dwFlags = 0L;
493 lpWaveHdr->dwLoops = 0L;
494 dwRet=widMessage(wDevID,WIDM_PREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
495 TRACE(mciwave,"after WIDM_PREPARE \n");
496 while (TRUE) {
497 lpWaveHdr->dwBytesRecorded = 0;
498 dwRet = widMessage(wDevID, WIDM_START, 0, 0L, 0L);
499 TRACE(mciwave, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
500 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
501 if (lpWaveHdr->dwBytesRecorded == 0) break;
503 TRACE(mciwave,"before WIDM_UNPREPARE \n");
504 dwRet = widMessage(wDevID,WIDM_UNPREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
505 TRACE(mciwave,"after WIDM_UNPREPARE \n");
506 if (lpWaveHdr->lpData != NULL) {
507 GlobalUnlock16(hData);
508 GlobalFree16(hData);
509 lpWaveHdr->lpData = NULL;
511 if (dwFlags & MCI_NOTIFY) {
512 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
513 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
514 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
516 return 0;
519 /**************************************************************************
520 * WAVE_mciPause [internal]
522 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
524 DWORD dwRet;
525 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
527 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
529 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
530 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
532 if (wmw->dwStatus == MCI_MODE_PLAY) {
533 wmw->dwStatus = MCI_MODE_PAUSE;
536 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PAUSE, 0, dwFlags, (DWORD)lpParms);
537 else dwRet = wodMessage(wDevID, WODM_PAUSE, 0, dwFlags, (DWORD)lpParms);
539 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
542 /**************************************************************************
543 * WAVE_mciResume [internal]
545 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
547 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
548 DWORD dwRet = 0;
550 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
552 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
553 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
555 if (wmw->dwStatus == MCI_MODE_PAUSE) {
556 wmw->dwStatus = MCI_MODE_PLAY;
559 #if 0
560 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PLAY, 0, dwFlags, (DWORD)lpParms);
561 else dwRet = wodMessage(wDevID, WODM_PLAY, 0, dwFlags, (DWORD)lpParms);
562 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
563 #else
564 return dwRet;
565 #endif
569 /**************************************************************************
570 * WAVE_mciSeek [internal]
572 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
574 DWORD ret = 0;
575 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
577 TRACE(mciwave, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
579 if (lpParms == NULL) {
580 ret = MCIERR_NULL_PARAMETER_BLOCK;
581 } else if (wmw == NULL) {
582 ret = MCIERR_INVALID_DEVICE_ID;
583 } else {
584 WAVE_mciStop(wDevID, MCI_WAIT, 0);
586 if (dwFlags & MCI_SEEK_TO_START) {
587 wmw->dwPosition = 0;
588 } else if (dwFlags & MCI_SEEK_TO_END) {
589 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
590 } else if (dwFlags & MCI_TO) {
591 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
592 } else {
593 WARN(mciwave, "dwFlag doesn't tell where to seek to...\n");
594 return MCIERR_MISSING_PARAMETER;
597 TRACE(mciwave, "Seeking to position=%lu bytes\n", wmw->dwPosition);
599 if (dwFlags & MCI_NOTIFY) {
600 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
601 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
602 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
605 return ret;
608 /**************************************************************************
609 * WAVE_mciSet [internal]
611 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
613 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
615 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
617 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
618 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
620 if (dwFlags & MCI_SET_TIME_FORMAT) {
621 switch (lpParms->dwTimeFormat) {
622 case MCI_FORMAT_MILLISECONDS:
623 TRACE(mciwave, "MCI_FORMAT_MILLISECONDS !\n");
624 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
625 break;
626 case MCI_FORMAT_BYTES:
627 TRACE(mciwave, "MCI_FORMAT_BYTES !\n");
628 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
629 break;
630 case MCI_FORMAT_SAMPLES:
631 TRACE(mciwave, "MCI_FORMAT_SAMPLES !\n");
632 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
633 break;
634 default:
635 WARN(mciwave,"Bad time format %lu!\n", lpParms->dwTimeFormat);
636 return MCIERR_BAD_TIME_FORMAT;
639 if (dwFlags & MCI_SET_VIDEO) {
640 TRACE(mciwave, "No support for video !\n");
641 return MCIERR_UNSUPPORTED_FUNCTION;
643 if (dwFlags & MCI_SET_DOOR_OPEN) {
644 TRACE(mciwave, "No support for door open !\n");
645 return MCIERR_UNSUPPORTED_FUNCTION;
647 if (dwFlags & MCI_SET_DOOR_CLOSED) {
648 TRACE(mciwave, "No support for door close !\n");
649 return MCIERR_UNSUPPORTED_FUNCTION;
651 if (dwFlags & MCI_SET_AUDIO) {
652 if (dwFlags & MCI_SET_ON) {
653 TRACE(mciwave, "MCI_SET_ON audio !\n");
654 } else if (dwFlags & MCI_SET_OFF) {
655 TRACE(mciwave, "MCI_SET_OFF audio !\n");
656 } else {
657 WARN(mciwave, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
658 return MCIERR_BAD_INTEGER;
661 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
662 TRACE(mciwave, "MCI_SET_AUDIO_ALL !\n");
663 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
664 TRACE(mciwave, "MCI_SET_AUDIO_LEFT !\n");
665 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
666 TRACE(mciwave, "MCI_SET_AUDIO_RIGHT !\n");
668 if (dwFlags & MCI_WAVE_INPUT)
669 TRACE(mciwave, "MCI_WAVE_INPUT !\n");
670 if (dwFlags & MCI_WAVE_OUTPUT)
671 TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
672 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
673 TRACE(mciwave, "MCI_WAVE_SET_ANYINPUT !\n");
674 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
675 TRACE(mciwave, "MCI_WAVE_SET_ANYOUTPUT !\n");
676 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
677 TRACE(mciwave, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
678 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
679 TRACE(mciwave, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
680 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
681 TRACE(mciwave, "MCI_WAVE_SET_BLOCKALIGN !\n");
682 if (dwFlags & MCI_WAVE_SET_CHANNELS)
683 TRACE(mciwave, "MCI_WAVE_SET_CHANNELS !\n");
684 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
685 TRACE(mciwave, "MCI_WAVE_SET_FORMATTAG !\n");
686 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
687 TRACE(mciwave, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
688 return 0;
691 /**************************************************************************
692 * WAVE_mciStatus [internal]
694 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
696 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
698 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
699 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
700 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
702 if (dwFlags & MCI_STATUS_ITEM) {
703 switch(lpParms->dwItem) {
704 case MCI_STATUS_CURRENT_TRACK:
705 lpParms->dwReturn = 1;
706 TRACE(mciwave, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
707 break;
708 case MCI_STATUS_LENGTH:
709 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
710 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
711 TRACE(mciwave, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
712 break;
713 case MCI_STATUS_MODE:
714 lpParms->dwReturn = wmw->dwStatus;
715 TRACE(mciwave, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
716 break;
717 case MCI_STATUS_MEDIA_PRESENT:
718 TRACE(mciwave, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
719 lpParms->dwReturn = TRUE;
720 break;
721 case MCI_STATUS_NUMBER_OF_TRACKS:
722 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
723 lpParms->dwReturn = 1;
724 TRACE(mciwave, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
725 break;
726 case MCI_STATUS_POSITION:
727 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
728 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
729 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
730 TRACE(mciwave, "MCI_STATUS_POSITION %s => %lu\n",
731 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
732 break;
733 case MCI_STATUS_READY:
734 lpParms->dwReturn = (wmw->dwStatus != MCI_MODE_NOT_READY);
735 TRACE(mciwave,"MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
736 break;
737 case MCI_STATUS_TIME_FORMAT:
738 lpParms->dwReturn = wmw->dwMciTimeFormat;
739 TRACE(mciwave, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
740 break;
741 case MCI_WAVE_INPUT:
742 TRACE(mciwave,"MCI_WAVE_INPUT !\n");
743 lpParms->dwReturn = 0;
744 break;
745 case MCI_WAVE_OUTPUT:
746 TRACE(mciwave,"MCI_WAVE_OUTPUT !\n");
747 lpParms->dwReturn = 0;
748 break;
749 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
750 lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
751 TRACE(mciwave,"MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
752 break;
753 case MCI_WAVE_STATUS_BITSPERSAMPLE:
754 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
755 TRACE(mciwave,"MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
756 break;
757 case MCI_WAVE_STATUS_BLOCKALIGN:
758 lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
759 TRACE(mciwave,"MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
760 break;
761 case MCI_WAVE_STATUS_CHANNELS:
762 lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
763 TRACE(mciwave,"MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
764 break;
765 case MCI_WAVE_STATUS_FORMATTAG:
766 lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
767 TRACE(mciwave,"MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
768 break;
769 case MCI_WAVE_STATUS_LEVEL:
770 TRACE(mciwave,"MCI_WAVE_STATUS_LEVEL !\n");
771 lpParms->dwReturn = 0xAAAA5555;
772 break;
773 case MCI_WAVE_STATUS_SAMPLESPERSEC:
774 lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
775 TRACE(mciwave,"MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
776 break;
777 default:
778 WARN(mciwave,"unknown command %08lX !\n", lpParms->dwItem);
779 return MCIERR_UNRECOGNIZED_COMMAND;
782 if (dwFlags & MCI_NOTIFY) {
783 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
784 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
785 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
787 return 0;
790 /**************************************************************************
791 * WAVE_mciGetDevCaps [internal]
793 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
794 LPMCI_GETDEVCAPS_PARMS lpParms)
796 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
798 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
800 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
801 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
803 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
804 switch(lpParms->dwItem) {
805 case MCI_GETDEVCAPS_DEVICE_TYPE:
806 lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
807 break;
808 case MCI_GETDEVCAPS_HAS_AUDIO:
809 lpParms->dwReturn = TRUE;
810 break;
811 case MCI_GETDEVCAPS_HAS_VIDEO:
812 lpParms->dwReturn = FALSE;
813 break;
814 case MCI_GETDEVCAPS_USES_FILES:
815 lpParms->dwReturn = TRUE;
816 break;
817 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
818 lpParms->dwReturn = TRUE;
819 break;
820 case MCI_GETDEVCAPS_CAN_RECORD:
821 lpParms->dwReturn = TRUE;
822 break;
823 case MCI_GETDEVCAPS_CAN_EJECT:
824 lpParms->dwReturn = FALSE;
825 break;
826 case MCI_GETDEVCAPS_CAN_PLAY:
827 lpParms->dwReturn = TRUE;
828 break;
829 case MCI_GETDEVCAPS_CAN_SAVE:
830 lpParms->dwReturn = TRUE;
831 break;
832 case MCI_WAVE_GETDEVCAPS_INPUTS:
833 lpParms->dwReturn = 1;
834 break;
835 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
836 lpParms->dwReturn = 1;
837 break;
838 default:
839 TRACE(mciwave, "Unknown capability (%08lx) !\n", lpParms->dwItem);
840 return MCIERR_UNRECOGNIZED_COMMAND;
843 return 0;
846 /**************************************************************************
847 * WAVE_mciInfo [internal]
849 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
851 DWORD ret = 0;
852 LPCSTR str = 0;
853 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
855 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
857 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
858 ret = MCIERR_NULL_PARAMETER_BLOCK;
859 } else if (wmw == NULL) {
860 ret = MCIERR_INVALID_DEVICE_ID;
861 } else {
862 TRACE(mciwave, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
864 switch(dwFlags) {
865 case MCI_INFO_PRODUCT:
866 str = "Wine's audio player";
867 break;
868 case MCI_INFO_FILE:
869 str = wmw->openParms.lpstrElementName;
870 break;
871 case MCI_WAVE_INPUT:
872 str = "Wine Wave In";
873 break;
874 case MCI_WAVE_OUTPUT:
875 str = "Wine Wave Out";
876 break;
877 default:
878 WARN(mciwave, "Don't know this info command (%lu)\n", dwFlags);
879 ret = MCIERR_UNRECOGNIZED_COMMAND;
882 if (str) {
883 if (strlen(str) + 1 > lpParms->dwRetSize) {
884 ret = MCIERR_PARAM_OVERFLOW;
885 } else {
886 lstrcpyn32A(lpParms->lpstrReturn, str, lpParms->dwRetSize);
888 } else {
889 lpParms->lpstrReturn[0] = 0;
892 return ret;
895 /**************************************************************************
896 * WAVE_DriverProc32 [sample driver]
898 LONG MCIWAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
899 DWORD dwParam1, DWORD dwParam2)
901 TRACE(mciwave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n",
902 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
904 switch(wMsg) {
905 case DRV_LOAD: return 1;
906 case DRV_FREE: return 1;
907 case DRV_OPEN: return 1;
908 case DRV_CLOSE: return 1;
909 case DRV_ENABLE: return 1;
910 case DRV_DISABLE: return 1;
911 case DRV_QUERYCONFIGURE: return 1;
912 case DRV_CONFIGURE: MessageBox32A(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
913 case DRV_INSTALL: return DRVCNF_RESTART;
914 case DRV_REMOVE: return DRVCNF_RESTART;
915 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMS32A)dwParam2);
916 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
917 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
918 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
919 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
920 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
921 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
922 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
923 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
924 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
925 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
926 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMS16) dwParam2);
927 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
928 case MCI_LOAD:
929 case MCI_SAVE:
930 case MCI_FREEZE:
931 case MCI_PUT:
932 case MCI_REALIZE:
933 case MCI_UNFREEZE:
934 case MCI_UPDATE:
935 case MCI_WHERE:
936 case MCI_WINDOW:
937 case MCI_STEP:
938 case MCI_SPIN:
939 case MCI_ESCAPE:
940 case MCI_COPY:
941 case MCI_CUT:
942 case MCI_DELETE:
943 case MCI_PASTE:
944 WARN(mciwave, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
945 break;
946 case MCI_OPEN:
947 case MCI_CLOSE:
948 FIXME(mciwave, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
949 break;
950 default:
951 FIXME(mciwave, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
952 return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
954 return MCIERR_UNRECOGNIZED_COMMAND;