Cosmetics.
[wine.git] / multimedia / mciwave.c
blobdd07848bcf6ec473058fa674f1198e8947f92f4b
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 <string.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include "wine/winuser16.h"
24 #include "driver.h"
25 #include "multimedia.h"
26 #include "mmsystem.h"
27 #include "heap.h"
28 #include "debug.h"
30 #define MAX_MCIWAVEDRV (1)
32 typedef struct {
33 int nUseCount; /* Incremented for each shared open */
34 BOOL16 fShareable; /* TRUE if first open was shareable */
35 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
36 HANDLE16 hCallback; /* Callback handle for pending notification */
37 HMMIO hFile; /* mmio file handle open as Element */
38 MCI_WAVE_OPEN_PARMSA openParms;
39 WAVEOPENDESC waveDesc;
40 PCMWAVEFORMAT WaveFormat;
41 WAVEHDR WaveHdr;
42 BOOL16 fInput; /* FALSE = Output, TRUE = Input */
43 WORD dwStatus; /* one from MCI_MODE_xxxx */
44 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
45 DWORD dwFileOffset; /* Offset of chunk in mmio file */
46 DWORD dwLength; /* number of bytes in chunk for playing */
47 DWORD dwPosition; /* position in bytes in chunk for playing */
48 } WINE_MCIWAVE;
50 static WINE_MCIWAVE MCIWaveDev[MAX_MCIWAVEDRV];
52 /*======================================================================*
53 * MCI WAVE implemantation *
54 *======================================================================*/
56 /**************************************************************************
57 * WAVE_mciGetOpenDev [internal]
59 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT16 wDevID)
61 if (wDevID >= MAX_MCIWAVEDRV || MCIWaveDev[wDevID].nUseCount == 0) {
62 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
63 return 0;
65 return &MCIWaveDev[wDevID];
68 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
70 DWORD ret = 0;
72 switch (wmw->dwMciTimeFormat) {
73 case MCI_FORMAT_MILLISECONDS:
74 ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
75 break;
76 case MCI_FORMAT_BYTES:
77 ret = val;
78 break;
79 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
80 ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
81 break;
82 default:
83 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
85 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
86 return ret;
89 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
91 DWORD ret = 0;
93 switch (wmw->dwMciTimeFormat) {
94 case MCI_FORMAT_MILLISECONDS:
95 ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
96 break;
97 case MCI_FORMAT_BYTES:
98 ret = val;
99 break;
100 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
101 ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
102 break;
103 default:
104 WARN(mciwave, "Bad time format %lu!\n", wmw->dwMciTimeFormat);
106 TRACE(mciwave, "val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
107 return ret;
110 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
112 MMCKINFO mmckInfo;
114 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
115 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
116 return MCIERR_INVALID_FILE;
117 TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
118 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
119 if (mmioRead(wmw->hFile, (HPSTR)&wmw->WaveFormat,
120 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
121 return MCIERR_INVALID_FILE;
123 TRACE(mciwave, "wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
124 TRACE(mciwave, "nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
125 TRACE(mciwave, "nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
126 TRACE(mciwave, "nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
127 TRACE(mciwave, "nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
128 TRACE(mciwave, "wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
129 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
130 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
131 return MCIERR_INVALID_FILE;
132 TRACE(mciwave, "Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
133 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
134 TRACE(mciwave, "nChannels=%d nSamplesPerSec=%ld\n",
135 wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
136 wmw->dwLength = mmckInfo.cksize;
137 wmw->dwFileOffset = mmioSeek(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
138 return 0;
141 /**************************************************************************
142 * WAVE_mciOpen [internal]
144 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
146 DWORD dwRet = 0;
147 DWORD dwDeviceID;
148 WINE_MCIWAVE* wmw;
150 TRACE(mciwave, "(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
151 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
153 if (wDevID >= MAX_MCIWAVEDRV) {
154 WARN(mciwave, "Invalid wDevID=%u\n", wDevID);
155 return MCIERR_INVALID_DEVICE_ID;
157 if (dwFlags & MCI_OPEN_SHAREABLE)
158 return MCIERR_HARDWARE;
160 wmw = &MCIWaveDev[wDevID];
162 if (wmw->nUseCount > 0) {
163 /* The driver is already opened on this channel
164 * Wave driver cannot be shared
166 return MCIERR_DEVICE_OPEN;
168 wmw->nUseCount++;
170 dwDeviceID = lpOpenParms->wDeviceID;
172 wmw->fInput = FALSE;
174 TRACE(mciwave, "wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
176 if (dwFlags & MCI_OPEN_ELEMENT) {
177 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
178 /* could it be that (DWORD)lpOpenParms->lpstrElementName
179 * contains the hFile value ?
181 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
182 } else {
183 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
185 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
186 TRACE(mciwave, "MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
187 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
188 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL,
189 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_EXCLUSIVE);
190 if (wmw->hFile == 0) {
191 WARN(mciwave, "can't find file='%s' !\n", lpstrElementName);
192 dwRet = MCIERR_FILE_NOT_FOUND;
194 } else {
195 wmw->hFile = 0;
199 TRACE(mciwave, "hFile=%u\n", wmw->hFile);
201 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
202 wmw->wNotifyDeviceID = dwDeviceID;
203 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
205 wmw->waveDesc.hWave = 0;
207 if (dwRet == 0 && wmw->hFile != 0) {
208 MMCKINFO ckMainRIFF;
210 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
211 dwRet = MCIERR_INVALID_FILE;
212 } else {
213 TRACE(mciwave, "ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
214 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
215 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
216 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
217 dwRet = MCIERR_INVALID_FILE;
218 } else {
219 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
222 } else {
223 wmw->dwLength = 0;
225 if (dwRet == 0) {
226 wmw->WaveFormat.wf.nAvgBytesPerSec =
227 wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
228 wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
229 wmw->dwPosition = 0;
231 wmw->dwStatus = MCI_MODE_STOP;
232 } else {
233 wmw->nUseCount--;
234 if (wmw->hFile != 0)
235 mmioClose(wmw->hFile, 0);
236 wmw->hFile = 0;
238 return dwRet;
241 /**************************************************************************
242 * WAVE_mciCue [internal]
244 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
247 FIXME
249 This routine is far from complete. At the moment only a check is done on the
250 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
251 is the default.
253 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
254 are ignored
257 DWORD dwRet;
258 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
260 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
262 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
264 /* always close elements ? */
265 if (wmw->hFile != 0) {
266 mmioClose(wmw->hFile, 0);
267 wmw->hFile = 0;
270 dwRet = MMSYSERR_NOERROR; /* assume success */
272 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
273 dwRet = wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
274 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
275 dwRet = widMessage(wDevID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
276 wmw->fInput = TRUE;
277 } else if (wmw->fInput) {
278 dwRet = widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L);
279 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
280 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
281 wmw->fInput = FALSE;
283 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
286 /**************************************************************************
287 * WAVE_mciStop [internal]
289 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
291 DWORD dwRet;
292 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
294 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
296 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
297 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
299 wmw->dwStatus = MCI_MODE_STOP;
300 wmw->dwPosition = 0;
301 TRACE(mciwave, "wmw->dwStatus=%d\n", wmw->dwStatus);
303 if (wmw->fInput)
304 dwRet = widMessage(wDevID, WIDM_STOP, 0, dwFlags, (DWORD)lpParms);
305 else
306 dwRet = wodMessage(wDevID, WODM_STOP, 0, dwFlags, (DWORD)lpParms);
308 if (dwFlags & MCI_NOTIFY) {
309 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
310 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
311 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
314 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
317 /**************************************************************************
318 * WAVE_mciClose [internal]
320 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
322 DWORD dwRet = 0;
323 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
325 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
327 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
329 if (wmw->dwStatus != MCI_MODE_STOP) {
330 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
333 wmw->nUseCount--;
335 if (wmw->nUseCount == 0) {
336 DWORD mmRet;
337 if (wmw->hFile != 0) {
338 mmioClose(wmw->hFile, 0);
339 wmw->hFile = 0;
341 mmRet = (wmw->fInput) ? widMessage(wDevID, WIDM_CLOSE, 0, 0L, 0L) :
342 wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
344 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
347 if ((dwFlags & MCI_NOTIFY) && lpParms) {
348 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
349 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
350 wmw->wNotifyDeviceID,
351 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
353 return 0;
356 /**************************************************************************
357 * WAVE_mciPlay [internal]
359 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
361 DWORD end;
362 LONG bufsize, count;
363 HGLOBAL16 hData;
364 DWORD dwRet;
365 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
367 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
369 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
370 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
372 if (wmw->fInput) {
373 WARN(mciwave, "cannot play on input device\n");
374 return MCIERR_NONAPPLICABLE_FUNCTION;
377 if (wmw->hFile == 0) {
378 WARN(mciwave, "Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
379 return MCIERR_FILE_NOT_FOUND;
382 if (!(dwFlags & MCI_WAIT)) {
383 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
384 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
387 end = 0xFFFFFFFF;
388 if (lpParms && (dwFlags & MCI_FROM)) {
389 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
391 if (lpParms && (dwFlags & MCI_TO)) {
392 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
395 TRACE(mciwave, "Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
397 /* go back to begining of chunk */
398 mmioSeek(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
400 /* By default the device will be opened for output, the MCI_CUE function is there to
401 * change from output to input and back
403 dwRet = wodMessage(wDevID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
404 if (dwRet != 0) {
405 TRACE(mciwave, "Can't open low level audio device %ld\n", dwRet);
406 return MCIERR_DEVICE_OPEN;
409 /* at 22050 bytes per sec => 30 ms by block */
410 bufsize = 10240;
411 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
412 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
414 wmw->dwStatus = MCI_MODE_PLAY;
416 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
417 while (wmw->dwStatus != MCI_MODE_STOP) {
418 wmw->WaveHdr.dwUser = 0L;
419 wmw->WaveHdr.dwFlags = 0L;
420 wmw->WaveHdr.dwLoops = 0L;
421 count = mmioRead(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
422 TRACE(mciwave, "mmioRead bufsize=%ld count=%ld\n", bufsize, count);
423 if (count < 1)
424 break;
425 dwRet = wodMessage(wDevID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
426 wmw->WaveHdr.dwBufferLength = count;
427 wmw->WaveHdr.dwBytesRecorded = 0;
428 TRACE(mciwave, "before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
429 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
430 dwRet = wodMessage(wDevID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
431 /* FIXME: should use callback mechanisms from audio driver */
432 #if 1
433 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
434 Sleep(1);
435 #endif
436 wmw->dwPosition += count;
437 TRACE(mciwave, "after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
438 dwRet = wodMessage(wDevID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
441 if (wmw->WaveHdr.lpData != NULL) {
442 GlobalUnlock16(hData);
443 GlobalFree16(hData);
444 wmw->WaveHdr.lpData = NULL;
447 wodMessage(wDevID, WODM_STOP, 0, 0L, 0L);
448 wodMessage(wDevID, WODM_CLOSE, 0, 0L, 0L);
450 wmw->dwStatus = MCI_MODE_STOP;
451 if (lpParms && (dwFlags & MCI_NOTIFY)) {
452 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
453 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
454 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
456 return 0;
459 /**************************************************************************
460 * WAVE_mciRecord [internal]
462 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
464 int start, end;
465 LONG bufsize;
466 HGLOBAL16 hData;
467 LPWAVEHDR lpWaveHdr;
468 DWORD dwRet;
469 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
471 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
473 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
474 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
476 if (!wmw->fInput) {
477 WARN(mciwave, "cannot record on output device\n");
478 return MCIERR_NONAPPLICABLE_FUNCTION;
481 if (wmw->hFile == 0) {
482 WARN(mciwave, "can't find file='%s' !\n",
483 wmw->openParms.lpstrElementName);
484 return MCIERR_FILE_NOT_FOUND;
486 start = 1; end = 99999;
487 if (dwFlags & MCI_FROM) {
488 start = lpParms->dwFrom;
489 TRACE(mciwave, "MCI_FROM=%d \n", start);
491 if (dwFlags & MCI_TO) {
492 end = lpParms->dwTo;
493 TRACE(mciwave, "MCI_TO=%d \n", end);
495 bufsize = 64000;
496 lpWaveHdr = &wmw->WaveHdr;
497 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
498 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
499 lpWaveHdr->dwBufferLength = bufsize;
500 lpWaveHdr->dwUser = 0L;
501 lpWaveHdr->dwFlags = 0L;
502 lpWaveHdr->dwLoops = 0L;
503 dwRet = widMessage(wDevID,WIDM_PREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
504 TRACE(mciwave, "after WIDM_PREPARE \n");
505 while (TRUE) {
506 lpWaveHdr->dwBytesRecorded = 0;
507 dwRet = widMessage(wDevID, WIDM_START, 0, 0L, 0L);
508 TRACE(mciwave, "after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
509 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
510 if (lpWaveHdr->dwBytesRecorded == 0) break;
512 TRACE(mciwave, "before WIDM_UNPREPARE \n");
513 dwRet = widMessage(wDevID,WIDM_UNPREPARE,0,(DWORD)lpWaveHdr,sizeof(WAVEHDR));
514 TRACE(mciwave, "after WIDM_UNPREPARE \n");
515 if (lpWaveHdr->lpData != NULL) {
516 GlobalUnlock16(hData);
517 GlobalFree16(hData);
518 lpWaveHdr->lpData = NULL;
520 if (dwFlags & MCI_NOTIFY) {
521 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
522 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
523 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
525 return 0;
528 /**************************************************************************
529 * WAVE_mciPause [internal]
531 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
533 DWORD dwRet;
534 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
536 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
538 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
539 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
541 if (wmw->dwStatus == MCI_MODE_PLAY) {
542 wmw->dwStatus = MCI_MODE_PAUSE;
545 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PAUSE, 0, dwFlags, (DWORD)lpParms);
546 else dwRet = wodMessage(wDevID, WODM_PAUSE, 0, dwFlags, (DWORD)lpParms);
548 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
551 /**************************************************************************
552 * WAVE_mciResume [internal]
554 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
556 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
557 DWORD dwRet = 0;
559 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
561 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
562 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
564 if (wmw->dwStatus == MCI_MODE_PAUSE) {
565 wmw->dwStatus = MCI_MODE_PLAY;
568 #if 0
569 if (wmw->fInput) dwRet = widMessage(wDevID, WIDM_PLAY, 0, dwFlags, (DWORD)lpParms);
570 else dwRet = wodMessage(wDevID, WODM_PLAY, 0, dwFlags, (DWORD)lpParms);
571 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
572 #else
573 return dwRet;
574 #endif
578 /**************************************************************************
579 * WAVE_mciSeek [internal]
581 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
583 DWORD ret = 0;
584 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
586 TRACE(mciwave, "(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
588 if (lpParms == NULL) {
589 ret = MCIERR_NULL_PARAMETER_BLOCK;
590 } else if (wmw == NULL) {
591 ret = MCIERR_INVALID_DEVICE_ID;
592 } else {
593 WAVE_mciStop(wDevID, MCI_WAIT, 0);
595 if (dwFlags & MCI_SEEK_TO_START) {
596 wmw->dwPosition = 0;
597 } else if (dwFlags & MCI_SEEK_TO_END) {
598 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
599 } else if (dwFlags & MCI_TO) {
600 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
601 } else {
602 WARN(mciwave, "dwFlag doesn't tell where to seek to...\n");
603 return MCIERR_MISSING_PARAMETER;
606 TRACE(mciwave, "Seeking to position=%lu bytes\n", wmw->dwPosition);
608 if (dwFlags & MCI_NOTIFY) {
609 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
610 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
611 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
614 return ret;
617 /**************************************************************************
618 * WAVE_mciSet [internal]
620 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
622 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
624 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
626 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
627 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
629 if (dwFlags & MCI_SET_TIME_FORMAT) {
630 switch (lpParms->dwTimeFormat) {
631 case MCI_FORMAT_MILLISECONDS:
632 TRACE(mciwave, "MCI_FORMAT_MILLISECONDS !\n");
633 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
634 break;
635 case MCI_FORMAT_BYTES:
636 TRACE(mciwave, "MCI_FORMAT_BYTES !\n");
637 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
638 break;
639 case MCI_FORMAT_SAMPLES:
640 TRACE(mciwave, "MCI_FORMAT_SAMPLES !\n");
641 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
642 break;
643 default:
644 WARN(mciwave, "Bad time format %lu!\n", lpParms->dwTimeFormat);
645 return MCIERR_BAD_TIME_FORMAT;
648 if (dwFlags & MCI_SET_VIDEO) {
649 TRACE(mciwave, "No support for video !\n");
650 return MCIERR_UNSUPPORTED_FUNCTION;
652 if (dwFlags & MCI_SET_DOOR_OPEN) {
653 TRACE(mciwave, "No support for door open !\n");
654 return MCIERR_UNSUPPORTED_FUNCTION;
656 if (dwFlags & MCI_SET_DOOR_CLOSED) {
657 TRACE(mciwave, "No support for door close !\n");
658 return MCIERR_UNSUPPORTED_FUNCTION;
660 if (dwFlags & MCI_SET_AUDIO) {
661 if (dwFlags & MCI_SET_ON) {
662 TRACE(mciwave, "MCI_SET_ON audio !\n");
663 } else if (dwFlags & MCI_SET_OFF) {
664 TRACE(mciwave, "MCI_SET_OFF audio !\n");
665 } else {
666 WARN(mciwave, "MCI_SET_AUDIO without SET_ON or SET_OFF\n");
667 return MCIERR_BAD_INTEGER;
670 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
671 TRACE(mciwave, "MCI_SET_AUDIO_ALL !\n");
672 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
673 TRACE(mciwave, "MCI_SET_AUDIO_LEFT !\n");
674 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
675 TRACE(mciwave, "MCI_SET_AUDIO_RIGHT !\n");
677 if (dwFlags & MCI_WAVE_INPUT)
678 TRACE(mciwave, "MCI_WAVE_INPUT !\n");
679 if (dwFlags & MCI_WAVE_OUTPUT)
680 TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
681 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
682 TRACE(mciwave, "MCI_WAVE_SET_ANYINPUT !\n");
683 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
684 TRACE(mciwave, "MCI_WAVE_SET_ANYOUTPUT !\n");
685 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
686 TRACE(mciwave, "MCI_WAVE_SET_AVGBYTESPERSEC !\n");
687 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
688 TRACE(mciwave, "MCI_WAVE_SET_BITSPERSAMPLE !\n");
689 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
690 TRACE(mciwave, "MCI_WAVE_SET_BLOCKALIGN !\n");
691 if (dwFlags & MCI_WAVE_SET_CHANNELS)
692 TRACE(mciwave, "MCI_WAVE_SET_CHANNELS !\n");
693 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
694 TRACE(mciwave, "MCI_WAVE_SET_FORMATTAG !\n");
695 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
696 TRACE(mciwave, "MCI_WAVE_SET_SAMPLESPERSEC !\n");
697 return 0;
700 /**************************************************************************
701 * WAVE_mciStatus [internal]
703 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
705 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
707 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
708 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
709 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
711 if (dwFlags & MCI_STATUS_ITEM) {
712 switch(lpParms->dwItem) {
713 case MCI_STATUS_CURRENT_TRACK:
714 lpParms->dwReturn = 1;
715 TRACE(mciwave, "MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
716 break;
717 case MCI_STATUS_LENGTH:
718 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
719 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
720 TRACE(mciwave, "MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
721 break;
722 case MCI_STATUS_MODE:
723 lpParms->dwReturn = wmw->dwStatus;
724 TRACE(mciwave, "MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
725 break;
726 case MCI_STATUS_MEDIA_PRESENT:
727 TRACE(mciwave, "MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
728 lpParms->dwReturn = TRUE;
729 break;
730 case MCI_STATUS_NUMBER_OF_TRACKS:
731 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
732 lpParms->dwReturn = 1;
733 TRACE(mciwave, "MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
734 break;
735 case MCI_STATUS_POSITION:
736 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
737 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
738 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
739 TRACE(mciwave, "MCI_STATUS_POSITION %s => %lu\n",
740 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
741 break;
742 case MCI_STATUS_READY:
743 lpParms->dwReturn = (wmw->dwStatus != MCI_MODE_NOT_READY);
744 TRACE(mciwave, "MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
745 break;
746 case MCI_STATUS_TIME_FORMAT:
747 lpParms->dwReturn = wmw->dwMciTimeFormat;
748 TRACE(mciwave, "MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
749 break;
750 case MCI_WAVE_INPUT:
751 TRACE(mciwave, "MCI_WAVE_INPUT !\n");
752 lpParms->dwReturn = 0;
753 break;
754 case MCI_WAVE_OUTPUT:
755 TRACE(mciwave, "MCI_WAVE_OUTPUT !\n");
756 lpParms->dwReturn = 0;
757 break;
758 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
759 lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
760 TRACE(mciwave, "MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
761 break;
762 case MCI_WAVE_STATUS_BITSPERSAMPLE:
763 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
764 TRACE(mciwave, "MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
765 break;
766 case MCI_WAVE_STATUS_BLOCKALIGN:
767 lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
768 TRACE(mciwave, "MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
769 break;
770 case MCI_WAVE_STATUS_CHANNELS:
771 lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
772 TRACE(mciwave, "MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
773 break;
774 case MCI_WAVE_STATUS_FORMATTAG:
775 lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
776 TRACE(mciwave, "MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
777 break;
778 case MCI_WAVE_STATUS_LEVEL:
779 TRACE(mciwave, "MCI_WAVE_STATUS_LEVEL !\n");
780 lpParms->dwReturn = 0xAAAA5555;
781 break;
782 case MCI_WAVE_STATUS_SAMPLESPERSEC:
783 lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
784 TRACE(mciwave, "MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
785 break;
786 default:
787 WARN(mciwave, "unknown command %08lX !\n", lpParms->dwItem);
788 return MCIERR_UNRECOGNIZED_COMMAND;
791 if (dwFlags & MCI_NOTIFY) {
792 TRACE(mciwave, "MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
793 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
794 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
796 return 0;
799 /**************************************************************************
800 * WAVE_mciGetDevCaps [internal]
802 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
803 LPMCI_GETDEVCAPS_PARMS lpParms)
805 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
807 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
809 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
810 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
812 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
813 switch(lpParms->dwItem) {
814 case MCI_GETDEVCAPS_DEVICE_TYPE:
815 lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
816 break;
817 case MCI_GETDEVCAPS_HAS_AUDIO:
818 lpParms->dwReturn = TRUE;
819 break;
820 case MCI_GETDEVCAPS_HAS_VIDEO:
821 lpParms->dwReturn = FALSE;
822 break;
823 case MCI_GETDEVCAPS_USES_FILES:
824 lpParms->dwReturn = TRUE;
825 break;
826 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
827 lpParms->dwReturn = TRUE;
828 break;
829 case MCI_GETDEVCAPS_CAN_RECORD:
830 lpParms->dwReturn = TRUE;
831 break;
832 case MCI_GETDEVCAPS_CAN_EJECT:
833 lpParms->dwReturn = FALSE;
834 break;
835 case MCI_GETDEVCAPS_CAN_PLAY:
836 lpParms->dwReturn = TRUE;
837 break;
838 case MCI_GETDEVCAPS_CAN_SAVE:
839 lpParms->dwReturn = TRUE;
840 break;
841 case MCI_WAVE_GETDEVCAPS_INPUTS:
842 lpParms->dwReturn = 1;
843 break;
844 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
845 lpParms->dwReturn = 1;
846 break;
847 default:
848 TRACE(mciwave, "Unknown capability (%08lx) !\n", lpParms->dwItem);
849 return MCIERR_UNRECOGNIZED_COMMAND;
852 return 0;
855 /**************************************************************************
856 * WAVE_mciInfo [internal]
858 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
860 DWORD ret = 0;
861 LPCSTR str = 0;
862 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
864 TRACE(mciwave, "(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
866 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
867 ret = MCIERR_NULL_PARAMETER_BLOCK;
868 } else if (wmw == NULL) {
869 ret = MCIERR_INVALID_DEVICE_ID;
870 } else {
871 TRACE(mciwave, "buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
873 switch(dwFlags) {
874 case MCI_INFO_PRODUCT:
875 str = "Wine's audio player";
876 break;
877 case MCI_INFO_FILE:
878 str = wmw->openParms.lpstrElementName;
879 break;
880 case MCI_WAVE_INPUT:
881 str = "Wine Wave In";
882 break;
883 case MCI_WAVE_OUTPUT:
884 str = "Wine Wave Out";
885 break;
886 default:
887 WARN(mciwave, "Don't know this info command (%lu)\n", dwFlags);
888 ret = MCIERR_UNRECOGNIZED_COMMAND;
891 if (str) {
892 if (strlen(str) + 1 > lpParms->dwRetSize) {
893 ret = MCIERR_PARAM_OVERFLOW;
894 } else {
895 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
897 } else {
898 lpParms->lpstrReturn[0] = 0;
901 return ret;
904 /**************************************************************************
905 * MCIWAVE_DriverProc [sample driver]
907 LONG MCIWAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
908 DWORD dwParam1, DWORD dwParam2)
910 TRACE(mciwave, "(%08lX, %04X, %08lX, %08lX, %08lX)\n",
911 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
913 switch(wMsg) {
914 case DRV_LOAD: return 1;
915 case DRV_FREE: return 1;
916 case DRV_OPEN: return 1;
917 case DRV_CLOSE: return 1;
918 case DRV_ENABLE: return 1;
919 case DRV_DISABLE: return 1;
920 case DRV_QUERYCONFIGURE: return 1;
921 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
922 case DRV_INSTALL: return DRVCNF_RESTART;
923 case DRV_REMOVE: return DRVCNF_RESTART;
924 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
925 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
926 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
927 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
928 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
929 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
930 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
931 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
932 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
933 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
934 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
935 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMS16) dwParam2);
936 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
937 case MCI_LOAD:
938 case MCI_SAVE:
939 case MCI_FREEZE:
940 case MCI_PUT:
941 case MCI_REALIZE:
942 case MCI_UNFREEZE:
943 case MCI_UPDATE:
944 case MCI_WHERE:
945 case MCI_WINDOW:
946 case MCI_STEP:
947 case MCI_SPIN:
948 case MCI_ESCAPE:
949 case MCI_COPY:
950 case MCI_CUT:
951 case MCI_DELETE:
952 case MCI_PASTE:
953 WARN(mciwave, "Unsupported command=%s\n", MCI_CommandToString(wMsg));
954 break;
955 case MCI_OPEN:
956 case MCI_CLOSE:
957 FIXME(mciwave, "Shouldn't receive a MCI_OPEN or CLOSE message\n");
958 break;
959 default:
960 FIXME(mciwave, "is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
961 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
963 return MCIERR_UNRECOGNIZED_COMMAND;