Stubs for EnumServiceStatus32, small changes.
[wine/multimedia.git] / multimedia / mciwave.c
blobe102a79c8025ad0087fc55586f5d56e8d60605f8
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 "windows.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 /**************************************************************************
112 * WAVE_mciOpen [internal]
114 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMS32A lpOpenParms)
116 DWORD dwRet;
117 DWORD dwDeviceID;
118 WINE_MCIWAVE* wmw;
120 TRACE(mciwave,"(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
121 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
123 if (wDevID >= MAX_MCIWAVEDRV) {
124 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
125 return MCIERR_INVALID_DEVICE_ID;
128 wmw = &MCIWaveDev[wDevID];
130 if (wmw->nUseCount > 0) {
131 /* The driver already open on this channel */
132 /* If the driver was opened shareable before and this open specifies */
133 /* shareable then increment the use count */
134 if (wmw->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
135 ++wmw->nUseCount;
136 else
137 return MCIERR_MUST_USE_SHAREABLE;
138 } else {
139 wmw->nUseCount = 1;
140 wmw->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
143 dwDeviceID = lpOpenParms->wDeviceID;
145 wmw->fInput = FALSE;
147 TRACE(mciwave, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
149 if (dwFlags & MCI_OPEN_ELEMENT) {
150 LPCSTR lpstrElementName;
152 lpstrElementName = lpOpenParms->lpstrElementName;
154 TRACE(mciwave,"MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
155 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
156 wmw->hFile = mmioOpen32A(lpstrElementName, NULL,
157 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
158 if (wmw->hFile == 0) {
159 WARN(mciwave, "can't find file='%s' !\n", lpstrElementName);
160 return MCIERR_FILE_NOT_FOUND;
162 } else {
163 wmw->hFile = 0;
166 TRACE(mciwave,"hFile=%u\n", wmw->hFile);
168 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMS32A));
169 wmw->wNotifyDeviceID = dwDeviceID;
170 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
172 wmw->waveDesc.hWave = 0;
174 if (wmw->hFile != 0) {
175 MMCKINFO mmckInfo;
176 MMCKINFO ckMainRIFF;
178 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0)
179 return MCIERR_INTERNAL;
180 TRACE(mciwave, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
181 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
182 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
183 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
184 return MCIERR_INTERNAL;
185 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
186 if (mmioDescend(wmw->hFile, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) != 0)
187 return MCIERR_INTERNAL;
188 TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
189 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
190 if (mmioRead32(wmw->hFile, (HPSTR)&wmw->WaveFormat,
191 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
192 return MCIERR_INTERNAL;
193 TRACE(mciwave, "wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
194 TRACE(mciwave, "nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
195 TRACE(mciwave, "nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
196 TRACE(mciwave, "nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
197 TRACE(mciwave, "nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
198 TRACE(mciwave, "wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
199 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
200 if (mmioDescend(wmw->hFile, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) != 0)
201 return MCIERR_INTERNAL;
202 TRACE(mciwave,"Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
203 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
204 TRACE(mciwave, "nChannels=%d nSamplesPerSec=%ld\n",
205 wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
206 wmw->dwLength = mmckInfo.cksize;
207 wmw->dwFileOffset = mmioSeek32(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
208 } else {
209 wmw->dwLength = 0;
211 wmw->WaveFormat.wf.nAvgBytesPerSec =
212 wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
213 wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
214 wmw->dwPosition = 0;
216 /* By default the device will be opened for output, the MCI_CUE function is there to
217 * change from output to input and back
219 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
220 wmw->dwStatus = MCI_MODE_STOP;
221 return 0;
224 /**************************************************************************
225 * WAVE_mciCue [internal]
227 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
230 FIXME
232 This routine is far from complete. At the moment only a check is done on the
233 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
234 is the default.
236 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
237 are ignored
240 DWORD dwRet;
241 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
243 TRACE(mciwave,"(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
245 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
247 /* always close elements ? */
248 if (wmw->hFile != 0) {
249 mmioClose32(wmw->hFile, 0);
250 wmw->hFile = 0;
253 dwRet = MMSYSERR_NOERROR; /* assume success */
255 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
256 dwRet = wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
257 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
258 dwRet = widMessage(wDevID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
259 wmw->fInput = TRUE;
260 } else if (wmw->fInput) {
261 dwRet = widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L);
262 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
263 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
264 wmw->fInput = FALSE;
266 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
269 /**************************************************************************
270 * WAVE_mciStop [internal]
272 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
274 DWORD dwRet;
275 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
277 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
279 if (lpParms == NULL) return MCIERR_INTERNAL;
280 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
282 wmw->dwStatus = MCI_MODE_STOP;
283 wmw->dwPosition = 0;
284 TRACE(mciwave, "wmw->dwStatus=%d\n", wmw->dwStatus);
286 if (wmw->fInput)
287 dwRet = widMessage(wDevID, WIDM_STOP, 0, dwFlags, (DWORD)lpParms);
288 else
289 dwRet = wodMessage(wDevID, WODM_STOP, 0, dwFlags, (DWORD)lpParms);
291 if (dwFlags & MCI_NOTIFY) {
292 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
293 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
294 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
297 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
300 /**************************************************************************
301 * WAVE_mciClose [internal]
303 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
305 DWORD dwRet;
306 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
308 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
310 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
312 if (wmw->dwStatus != MCI_MODE_STOP) {
313 WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
316 wmw->dwStatus = MCI_MODE_STOP;
317 wmw->nUseCount--;
319 if (wmw->nUseCount == 0) {
320 if (wmw->hFile != 0) {
321 mmioClose32(wmw->hFile, 0);
322 wmw->hFile = 0;
324 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L);
325 else dwRet = wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
327 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
330 if (lpParms && (dwFlags & MCI_NOTIFY)) {
331 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
332 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
333 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
335 return 0;
338 /**************************************************************************
339 * WAVE_mciPlay [internal]
341 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
343 DWORD end;
344 LONG bufsize, count;
345 HGLOBAL16 hData;
346 DWORD dwRet;
347 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
349 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
351 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
353 if (wmw->fInput) {
354 WARN(mciwave, "cannot play on input device\n");
355 return MCIERR_NONAPPLICABLE_FUNCTION;
358 if (wmw->hFile == 0) {
359 WARN(mciwave, "Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
360 return MCIERR_FILE_NOT_FOUND;
363 if (!(dwFlags & MCI_WAIT)) {
364 /** FIXME: I'm not 100% sure that wNotifyDeviceID is the right value in all cases ??? */
365 return MCI_SendCommandAsync32(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags, (DWORD)lpParms);
368 end = 0xFFFFFFFF;
369 if (lpParms && (dwFlags & MCI_FROM)) {
370 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
372 if (lpParms && (dwFlags & MCI_TO)) {
373 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
376 TRACE(mciwave, "Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
378 /* go back to begining of chunk */
379 mmioSeek32(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
381 /* at 22050 bytes per sec => 30 ms by block */
382 bufsize = 1024;
383 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
384 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
386 wmw->dwStatus = MCI_MODE_PLAY;
388 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
389 while (wmw->dwStatus != MCI_MODE_STOP) {
390 wmw->WaveHdr.dwUser = 0L;
391 wmw->WaveHdr.dwFlags = 0L;
392 wmw->WaveHdr.dwLoops = 0L;
393 dwRet = wodMessage(wDevID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
394 count = mmioRead32(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
395 TRACE(mciwave,"mmioRead bufsize=%ld count=%ld\n", bufsize, count);
396 if (count < 1) break;
397 wmw->WaveHdr.dwBufferLength = count;
398 wmw->WaveHdr.dwBytesRecorded = 0;
399 TRACE(mciwave,"before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
400 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
401 dwRet = wodMessage(wDevID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
402 /* FIXME: should use callback mechanisms from audio driver */
403 #if 0
404 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
405 Sleep(1);
406 #endif
407 wmw->dwPosition += count;
408 TRACE(mciwave,"after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
409 dwRet = wodMessage(wDevID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
412 if (wmw->WaveHdr.lpData != NULL) {
413 GlobalUnlock16(hData);
414 GlobalFree16(hData);
415 wmw->WaveHdr.lpData = NULL;
417 wmw->dwStatus = MCI_MODE_STOP;
418 if (lpParms && (dwFlags & MCI_NOTIFY)) {
419 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
420 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
421 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
423 return 0;
427 /**************************************************************************
428 * WAVE_mciRecord [internal]
430 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
432 int start, end;
433 LONG bufsize;
434 HGLOBAL16 hData;
435 LPWAVEHDR lpWaveHdr;
436 DWORD dwRet;
437 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
439 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
441 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
443 if (!wmw->fInput) {
444 WARN(mciwave, "cannot record on output device\n");
445 return MCIERR_NONAPPLICABLE_FUNCTION;
448 if (wmw->hFile == 0) {
449 WARN(mciwave, "can't find file='%s' !\n",
450 wmw->openParms.lpstrElementName);
451 return MCIERR_FILE_NOT_FOUND;
453 start = 1; end = 99999;
454 if (dwFlags & MCI_FROM) {
455 start = lpParms->dwFrom;
456 TRACE(mciwave, "MCI_FROM=%d \n", start);
458 if (dwFlags & MCI_TO) {
459 end = lpParms->dwTo;
460 TRACE(mciwave,"MCI_TO=%d \n", end);
462 bufsize = 64000;
463 lpWaveHdr = &wmw->WaveHdr;
464 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
465 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
466 lpWaveHdr->dwBufferLength = bufsize;
467 lpWaveHdr->dwUser = 0L;
468 lpWaveHdr->dwFlags = 0L;
469 lpWaveHdr->dwLoops = 0L;
470 dwRet=widMessage(wDevID,WIDM_PREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
471 TRACE(mciwave,"after WIDM_PREPARE \n");
472 while (TRUE) {
473 lpWaveHdr->dwBytesRecorded = 0;
474 dwRet = widMessage(wDevID, WIDM_START, 0, 0L, 0L);
475 TRACE(mciwave, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
476 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
477 if (lpWaveHdr->dwBytesRecorded == 0) break;
479 TRACE(mciwave,"before WIDM_UNPREPARE \n");
480 dwRet = widMessage(wDevID,WIDM_UNPREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
481 TRACE(mciwave,"after WIDM_UNPREPARE \n");
482 if (lpWaveHdr->lpData != NULL) {
483 GlobalUnlock16(hData);
484 GlobalFree16(hData);
485 lpWaveHdr->lpData = NULL;
487 if (dwFlags & MCI_NOTIFY) {
488 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
489 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
490 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
492 return 0;
495 /**************************************************************************
496 * WAVE_mciPause [internal]
498 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
500 DWORD dwRet;
501 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
503 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
505 if (lpParms == NULL) return MCIERR_INTERNAL;
506 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
508 if (wmw->dwStatus == MCI_MODE_PLAY) {
509 wmw->dwStatus = MCI_MODE_PAUSE;
512 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PAUSE, 0, dwFlags, (DWORD)lpParms);
513 else dwRet = wodMessage(wDevID, WODM_PAUSE, 0, dwFlags, (DWORD)lpParms);
515 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
519 /**************************************************************************
520 * WAVE_mciResume [internal]
522 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
524 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
525 DWORD dwRet = 0;
527 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
529 if (lpParms == NULL) return MCIERR_INTERNAL;
530 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
532 if (wmw->dwStatus == MCI_MODE_PAUSE) {
533 wmw->dwStatus = MCI_MODE_PLAY;
536 #if 0
537 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PLAY, 0, dwFlags, (DWORD)lpParms);
538 else dwRet = wodMessage(wDevID, WODM_PLAY, 0, dwFlags, (DWORD)lpParms);
539 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
540 #else
541 return 0;
542 #endif
546 /**************************************************************************
547 * WAVE_mciSeek [internal]
549 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
551 DWORD ret = 0;
552 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
554 TRACE(mciwave, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
556 if (lpParms == NULL) {
557 ret = MCIERR_NULL_PARAMETER_BLOCK;
558 } else if (wmw == NULL) {
559 ret = MCIERR_INVALID_DEVICE_ID;
560 } else {
561 WAVE_mciStop(wDevID, MCI_WAIT, 0);
563 if (dwFlags & MCI_SEEK_TO_START) {
564 wmw->dwPosition = 0;
565 } else if (dwFlags & MCI_SEEK_TO_END) {
566 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
567 } else if (dwFlags & MCI_TO) {
568 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
569 } else {
570 WARN(mciwave, "dwFlag doesn't tell where to seek to...\n");
571 return MCIERR_MISSING_PARAMETER;
574 TRACE(mciwave, "Seeking to position=%lu bytes\n", wmw->dwPosition);
576 if (dwFlags & MCI_NOTIFY) {
577 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
578 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
579 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
582 return ret;
585 /**************************************************************************
586 * WAVE_mciSet [internal]
588 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
590 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
592 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
593 if (lpParms == NULL) return MCIERR_INTERNAL;
594 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
596 if (dwFlags & MCI_SET_TIME_FORMAT) {
597 switch (lpParms->dwTimeFormat) {
598 case MCI_FORMAT_MILLISECONDS:
599 TRACE(mciwave, "MCI_FORMAT_MILLISECONDS !\n");
600 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
601 break;
602 case MCI_FORMAT_BYTES:
603 TRACE(mciwave, "MCI_FORMAT_BYTES !\n");
604 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
605 break;
606 case MCI_FORMAT_SAMPLES:
607 TRACE(mciwave, "MCI_FORMAT_SAMPLES !\n");
608 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
609 break;
610 default:
611 WARN(mciwave,"Bad time format %lu!\n", lpParms->dwTimeFormat);
612 return MCIERR_BAD_TIME_FORMAT;
615 if (dwFlags & MCI_SET_VIDEO) {
616 TRACE(mciwave, "No support for video !\n");
617 return MCIERR_UNSUPPORTED_FUNCTION;
619 if (dwFlags & MCI_SET_DOOR_OPEN) {
620 TRACE(mciwave, "No support for door open !\n");
621 return MCIERR_UNSUPPORTED_FUNCTION;
623 if (dwFlags & MCI_SET_DOOR_CLOSED) {
624 TRACE(mciwave, "No support for door close !\n");
625 return MCIERR_UNSUPPORTED_FUNCTION;
627 if (dwFlags & MCI_SET_AUDIO) {
628 if (dwFlags & MCI_SET_ON) {
629 TRACE(mciwave, "MCI_SET_ON audio !\n");
630 } else if (dwFlags & MCI_SET_OFF) {
631 TRACE(mciwave, "MCI_SET_OFF audio !\n");
632 } else {
633 WARN(mciwave, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
634 return MCIERR_BAD_INTEGER;
637 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
638 TRACE(mciwave, "MCI_SET_AUDIO_ALL !\n");
639 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
640 TRACE(mciwave, "MCI_SET_AUDIO_LEFT !\n");
641 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
642 TRACE(mciwave, "MCI_SET_AUDIO_RIGHT !\n");
644 if (dwFlags & MCI_WAVE_INPUT)
645 TRACE(mciwave, "MCI_WAVE_INPUT !\n");
646 if (dwFlags & MCI_WAVE_OUTPUT)
647 TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
648 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
649 TRACE(mciwave, "MCI_WAVE_SET_ANYINPUT !\n");
650 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
651 TRACE(mciwave, "MCI_WAVE_SET_ANYOUTPUT !\n");
652 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
653 TRACE(mciwave, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
654 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
655 TRACE(mciwave, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
656 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
657 TRACE(mciwave, "MCI_WAVE_SET_BLOCKALIGN !\n");
658 if (dwFlags & MCI_WAVE_SET_CHANNELS)
659 TRACE(mciwave, "MCI_WAVE_SET_CHANNELS !\n");
660 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
661 TRACE(mciwave, "MCI_WAVE_SET_FORMATTAG !\n");
662 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
663 TRACE(mciwave, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
664 return 0;
668 /**************************************************************************
669 * WAVE_mciStatus [internal]
671 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
673 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
675 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
676 if (lpParms == NULL) return MCIERR_INTERNAL;
677 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
679 if (dwFlags & MCI_STATUS_ITEM) {
680 switch(lpParms->dwItem) {
681 case MCI_STATUS_CURRENT_TRACK:
682 lpParms->dwReturn = 1;
683 TRACE(mciwave, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
684 break;
685 case MCI_STATUS_LENGTH:
686 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
687 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
688 TRACE(mciwave, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
689 break;
690 case MCI_STATUS_MODE:
691 lpParms->dwReturn = wmw->dwStatus;
692 TRACE(mciwave, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
693 break;
694 case MCI_STATUS_MEDIA_PRESENT:
695 TRACE(mciwave, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
696 lpParms->dwReturn = TRUE;
697 break;
698 case MCI_STATUS_NUMBER_OF_TRACKS:
699 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
700 lpParms->dwReturn = 1;
701 TRACE(mciwave, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
702 break;
703 case MCI_STATUS_POSITION:
704 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
705 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
706 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
707 TRACE(mciwave, "MCI_STATUS_POSITION %s => %lu\n",
708 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
709 break;
710 case MCI_STATUS_READY:
711 lpParms->dwReturn = (wmw->dwStatus != MCI_MODE_NOT_READY);
712 TRACE(mciwave,"MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
713 break;
714 case MCI_STATUS_TIME_FORMAT:
715 lpParms->dwReturn = wmw->dwMciTimeFormat;
716 TRACE(mciwave, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
717 break;
718 case MCI_WAVE_INPUT:
719 TRACE(mciwave,"MCI_WAVE_INPUT !\n");
720 lpParms->dwReturn = 0;
721 break;
722 case MCI_WAVE_OUTPUT:
723 TRACE(mciwave,"MCI_WAVE_OUTPUT !\n");
724 lpParms->dwReturn = 0;
725 break;
726 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
727 lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
728 TRACE(mciwave,"MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
729 break;
730 case MCI_WAVE_STATUS_BITSPERSAMPLE:
731 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
732 TRACE(mciwave,"MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
733 break;
734 case MCI_WAVE_STATUS_BLOCKALIGN:
735 lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
736 TRACE(mciwave,"MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
737 break;
738 case MCI_WAVE_STATUS_CHANNELS:
739 lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
740 TRACE(mciwave,"MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
741 break;
742 case MCI_WAVE_STATUS_FORMATTAG:
743 lpParms->dwReturn = wmw->WaveFormat.wf.
744 wFormatTag;
745 TRACE(mciwave,"MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
746 break;
747 case MCI_WAVE_STATUS_LEVEL:
748 TRACE(mciwave,"MCI_WAVE_STATUS_LEVEL !\n");
749 lpParms->dwReturn = 0xAAAA5555;
750 break;
751 case MCI_WAVE_STATUS_SAMPLESPERSEC:
752 lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
753 TRACE(mciwave,"MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
754 break;
755 default:
756 WARN(mciwave,"unknown command %08lX !\n", lpParms->dwItem);
757 return MCIERR_UNRECOGNIZED_COMMAND;
760 if (dwFlags & MCI_NOTIFY) {
761 TRACE(mciwave,"MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
762 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
763 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
765 return 0;
768 /**************************************************************************
769 * WAVE_mciGetDevCaps [internal]
771 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
772 LPMCI_GETDEVCAPS_PARMS lpParms)
774 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
776 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
778 if (lpParms == NULL) return MCIERR_INTERNAL;
779 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
781 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
782 switch(lpParms->dwItem) {
783 case MCI_GETDEVCAPS_DEVICE_TYPE:
784 lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
785 break;
786 case MCI_GETDEVCAPS_HAS_AUDIO:
787 lpParms->dwReturn = TRUE;
788 break;
789 case MCI_GETDEVCAPS_HAS_VIDEO:
790 lpParms->dwReturn = FALSE;
791 break;
792 case MCI_GETDEVCAPS_USES_FILES:
793 lpParms->dwReturn = TRUE;
794 break;
795 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
796 lpParms->dwReturn = TRUE;
797 break;
798 case MCI_GETDEVCAPS_CAN_RECORD:
799 lpParms->dwReturn = TRUE;
800 break;
801 case MCI_GETDEVCAPS_CAN_EJECT:
802 lpParms->dwReturn = FALSE;
803 break;
804 case MCI_GETDEVCAPS_CAN_PLAY:
805 lpParms->dwReturn = TRUE;
806 break;
807 case MCI_GETDEVCAPS_CAN_SAVE:
808 lpParms->dwReturn = TRUE;
809 break;
810 case MCI_WAVE_GETDEVCAPS_INPUTS:
811 lpParms->dwReturn = 1;
812 break;
813 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
814 lpParms->dwReturn = 1;
815 break;
816 default:
817 TRACE(mciwave, "Unknown capability (%08lx) !\n", lpParms->dwItem);
818 return MCIERR_UNRECOGNIZED_COMMAND;
821 return 0;
824 /**************************************************************************
825 * WAVE_mciInfo [internal]
827 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
829 DWORD ret = 0;
830 LPCSTR str = 0;
831 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
833 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
835 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
836 ret = MCIERR_NULL_PARAMETER_BLOCK;
837 } else if (wmw == NULL) {
838 ret = MCIERR_INVALID_DEVICE_ID;
839 } else {
840 TRACE(mciwave, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
842 switch(dwFlags) {
843 case MCI_INFO_PRODUCT:
844 str = "Wine's audio player";
845 break;
846 case MCI_INFO_FILE:
847 str = wmw->openParms.lpstrElementName;
848 break;
849 case MCI_WAVE_INPUT:
850 str = "Wine Wave In";
851 break;
852 case MCI_WAVE_OUTPUT:
853 str = "Wine Wave Out";
854 break;
855 default:
856 WARN(mciwave, "Don't know this info command (%lu)\n", dwFlags);
857 return MCIERR_UNRECOGNIZED_COMMAND;
860 if (str) {
861 if (strlen(str) + 1 > lpParms->dwRetSize) {
862 ret = MCIERR_PARAM_OVERFLOW;
863 } else {
864 lstrcpyn32A(lpParms->lpstrReturn, str, lpParms->dwRetSize);
866 } else {
867 lpParms->lpstrReturn[0] = 0;
870 return ret;
873 /**************************************************************************
874 * WAVE_DriverProc32 [sample driver]
876 LONG MCIWAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
877 DWORD dwParam1, DWORD dwParam2)
879 TRACE(mciwave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n",
880 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
882 switch(wMsg) {
883 case DRV_LOAD: return 1;
884 case DRV_FREE: return 1;
885 case DRV_OPEN: return 1;
886 case DRV_CLOSE: return 1;
887 case DRV_ENABLE: return 1;
888 case DRV_DISABLE: return 1;
889 case DRV_QUERYCONFIGURE: return 1;
890 case DRV_CONFIGURE: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
891 case DRV_INSTALL: return DRVCNF_RESTART;
892 case DRV_REMOVE: return DRVCNF_RESTART;
893 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMS32A)dwParam2);
894 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
895 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
896 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
897 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
898 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
899 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
900 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
901 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
902 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
903 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
904 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMS16) dwParam2);
905 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
906 case MCI_LOAD:
907 case MCI_SAVE:
908 case MCI_FREEZE:
909 case MCI_PUT:
910 case MCI_REALIZE:
911 case MCI_UNFREEZE:
912 case MCI_UPDATE:
913 case MCI_WHERE:
914 case MCI_WINDOW:
915 case MCI_STEP:
916 case MCI_SPIN:
917 case MCI_ESCAPE:
918 case MCI_COPY:
919 case MCI_CUT:
920 case MCI_DELETE:
921 case MCI_PASTE:
922 WARN(mciwave, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
923 break;
924 case MCI_OPEN:
925 case MCI_CLOSE:
926 FIXME(mciwave, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
927 break;
928 default:
929 FIXME(mciwave, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
930 return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
932 return MCIERR_UNRECOGNIZED_COMMAND;