moved to the new debug interface
[wine.git] / multimedia / mciwave.c
blobda5ba674d5c031c0318a264fb9d4a21eff63fe74
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 "winuser.h"
24 #include "driver.h"
25 #include "multimedia.h"
26 #include "mmsystem.h"
27 #include "heap.h"
28 #include "debugtools.h"
30 DEFAULT_DEBUG_CHANNEL(mciwave)
32 #define MAX_MCIWAVEDRV (1)
34 typedef struct {
35 UINT16 wDevID;
36 UINT16 wWavID;
37 int nUseCount; /* Incremented for each shared open */
38 BOOL16 fShareable; /* TRUE if first open was shareable */
39 WORD wNotifyDeviceID;/* MCI device ID with a pending notification */
40 HANDLE16 hCallback; /* Callback handle for pending notification */
41 HMMIO hFile; /* mmio file handle open as Element */
42 MCI_WAVE_OPEN_PARMSA openParms;
43 WAVEOPENDESC waveDesc;
44 PCMWAVEFORMAT WaveFormat;
45 WAVEHDR WaveHdr;
46 BOOL16 fInput; /* FALSE = Output, TRUE = Input */
47 WORD dwStatus; /* one from MCI_MODE_xxxx */
48 DWORD dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
49 DWORD dwFileOffset; /* Offset of chunk in mmio file */
50 DWORD dwLength; /* number of bytes in chunk for playing */
51 DWORD dwPosition; /* position in bytes in chunk for playing */
52 } WINE_MCIWAVE;
54 static WINE_MCIWAVE MCIWaveDev[MAX_MCIWAVEDRV];
56 /*======================================================================*
57 * MCI WAVE implemantation *
58 *======================================================================*/
60 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
62 /**************************************************************************
63 * MCIWAVE_drvGetDrv [internal]
65 static WINE_MCIWAVE* WAVE_drvGetDrv(UINT16 wDevID)
67 int i;
69 for (i = 0; i < MAX_MCIWAVEDRV; i++) {
70 if (MCIWaveDev[i].wDevID == wDevID) {
71 return &MCIWaveDev[i];
74 return 0;
77 /**************************************************************************
78 * MCIWAVE_drvOpen [internal]
80 static DWORD WAVE_drvOpen(LPSTR str, LPMCI_OPEN_DRIVER_PARMSA modp)
82 int i;
84 for (i = 0; i < MAX_MCIWAVEDRV; i++) {
85 if (MCIWaveDev[i].wDevID == 0) {
86 MCIWaveDev[i].wDevID = modp->wDeviceID;
87 modp->wCustomCommandTable = -1;
88 modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
89 return modp->wDeviceID;
92 return 0;
95 /**************************************************************************
96 * MCIWAVE_drvClose [internal]
98 static DWORD WAVE_drvClose(DWORD dwDevID)
100 WINE_MCIWAVE* wmcda = WAVE_drvGetDrv(dwDevID);
102 if (wmcda) {
103 wmcda->wDevID = 0;
104 return 1;
106 return 0;
109 /**************************************************************************
110 * WAVE_mciGetOpenDev [internal]
112 static WINE_MCIWAVE* WAVE_mciGetOpenDev(UINT16 wDevID)
114 WINE_MCIWAVE* wmw = WAVE_drvGetDrv(wDevID);
116 if (wmw == NULL || wmw->nUseCount == 0) {
117 WARN("Invalid wDevID=%u\n", wDevID);
118 return 0;
120 return wmw;
123 static DWORD WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
125 DWORD ret = 0;
127 switch (wmw->dwMciTimeFormat) {
128 case MCI_FORMAT_MILLISECONDS:
129 ret = (val * 1000) / wmw->WaveFormat.wf.nAvgBytesPerSec;
130 break;
131 case MCI_FORMAT_BYTES:
132 ret = val;
133 break;
134 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
135 ret = (val * 8) / wmw->WaveFormat.wBitsPerSample;
136 break;
137 default:
138 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
140 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
141 return ret;
144 static DWORD WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
146 DWORD ret = 0;
148 switch (wmw->dwMciTimeFormat) {
149 case MCI_FORMAT_MILLISECONDS:
150 ret = (val * wmw->WaveFormat.wf.nAvgBytesPerSec) / 1000;
151 break;
152 case MCI_FORMAT_BYTES:
153 ret = val;
154 break;
155 case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
156 ret = (val * wmw->WaveFormat.wBitsPerSample) / 8;
157 break;
158 default:
159 WARN("Bad time format %lu!\n", wmw->dwMciTimeFormat);
161 TRACE("val=%lu=0x%08lx [tf=%lu] => ret=%lu\n", val, val, wmw->dwMciTimeFormat, ret);
162 return ret;
165 static DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, MMCKINFO* pckMainRIFF)
167 MMCKINFO mmckInfo;
169 mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
170 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
171 return MCIERR_INVALID_FILE;
172 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
173 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
174 if (mmioRead(wmw->hFile, (HPSTR)&wmw->WaveFormat,
175 (long)sizeof(PCMWAVEFORMAT)) != (long)sizeof(PCMWAVEFORMAT))
176 return MCIERR_INVALID_FILE;
178 TRACE("wFormatTag=%04X !\n", wmw->WaveFormat.wf.wFormatTag);
179 TRACE("nChannels=%d \n", wmw->WaveFormat.wf.nChannels);
180 TRACE("nSamplesPerSec=%ld\n", wmw->WaveFormat.wf.nSamplesPerSec);
181 TRACE("nAvgBytesPerSec=%ld\n", wmw->WaveFormat.wf.nAvgBytesPerSec);
182 TRACE("nBlockAlign=%d \n", wmw->WaveFormat.wf.nBlockAlign);
183 TRACE("wBitsPerSample=%u !\n", wmw->WaveFormat.wBitsPerSample);
184 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
185 if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
186 return MCIERR_INVALID_FILE;
187 TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08lX \n",
188 (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
189 TRACE("nChannels=%d nSamplesPerSec=%ld\n",
190 wmw->WaveFormat.wf.nChannels, wmw->WaveFormat.wf.nSamplesPerSec);
191 wmw->dwLength = mmckInfo.cksize;
192 wmw->dwFileOffset = mmioSeek(wmw->hFile, 0, SEEK_CUR); /* >= 0 */
193 return 0;
196 /**************************************************************************
197 * WAVE_mciOpen [internal]
199 static DWORD WAVE_mciOpen(UINT16 wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSA lpOpenParms)
201 DWORD dwRet = 0;
202 DWORD dwDeviceID;
203 WINE_MCIWAVE* wmw = WAVE_drvGetDrv(wDevID);
205 TRACE("(%04X, %08lX, %p)\n", wDevID, dwFlags, lpOpenParms);
206 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
207 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
209 if (dwFlags & MCI_OPEN_SHAREABLE)
210 return MCIERR_HARDWARE;
212 if (wmw->nUseCount > 0) {
213 /* The driver is already opened on this channel
214 * Wave driver cannot be shared
216 return MCIERR_DEVICE_OPEN;
218 wmw->nUseCount++;
220 dwDeviceID = lpOpenParms->wDeviceID;
222 wmw->fInput = FALSE;
223 wmw->wWavID = 0;
225 TRACE("wDevID=%04X (lpParams->wDeviceID=%08lX)\n", wDevID, dwDeviceID);
227 if (dwFlags & MCI_OPEN_ELEMENT) {
228 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
229 /* could it be that (DWORD)lpOpenParms->lpstrElementName
230 * contains the hFile value ?
232 dwRet = MCIERR_UNRECOGNIZED_COMMAND;
233 } else {
234 LPCSTR lpstrElementName = lpOpenParms->lpstrElementName;
236 /*FIXME : what should be done id wmw->hFile is already != 0, or the driver is playin' */
237 TRACE("MCI_OPEN_ELEMENT '%s' !\n", lpstrElementName);
238 if (lpstrElementName && (strlen(lpstrElementName) > 0)) {
239 wmw->hFile = mmioOpenA((LPSTR)lpstrElementName, NULL,
240 MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_DENYWRITE);
241 if (wmw->hFile == 0) {
242 WARN("can't find file='%s' !\n", lpstrElementName);
243 dwRet = MCIERR_FILE_NOT_FOUND;
245 } else {
246 wmw->hFile = 0;
250 TRACE("hFile=%u\n", wmw->hFile);
252 memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
253 wmw->wNotifyDeviceID = dwDeviceID;
254 wmw->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */
256 wmw->waveDesc.hWave = 0;
258 if (dwRet == 0 && wmw->hFile != 0) {
259 MMCKINFO ckMainRIFF;
261 if (mmioDescend(wmw->hFile, &ckMainRIFF, NULL, 0) != 0) {
262 dwRet = MCIERR_INVALID_FILE;
263 } else {
264 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08lX \n",
265 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize);
266 if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
267 (ckMainRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E'))) {
268 dwRet = MCIERR_INVALID_FILE;
269 } else {
270 dwRet = WAVE_mciReadFmt(wmw, &ckMainRIFF);
273 } else {
274 wmw->dwLength = 0;
276 if (dwRet == 0) {
277 wmw->WaveFormat.wf.nAvgBytesPerSec =
278 wmw->WaveFormat.wf.nSamplesPerSec * wmw->WaveFormat.wf.nBlockAlign;
279 wmw->waveDesc.lpFormat = (LPWAVEFORMAT)&wmw->WaveFormat;
280 wmw->dwPosition = 0;
282 wmw->dwStatus = MCI_MODE_STOP;
283 } else {
284 wmw->nUseCount--;
285 if (wmw->hFile != 0)
286 mmioClose(wmw->hFile, 0);
287 wmw->hFile = 0;
289 return dwRet;
292 /**************************************************************************
293 * WAVE_mciCue [internal]
295 static DWORD WAVE_mciCue(UINT16 wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
298 FIXME
300 This routine is far from complete. At the moment only a check is done on the
301 MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
302 is the default.
304 The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
305 are ignored
308 DWORD dwRet;
309 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
311 TRACE("(%u, %08lX, %p);\n", wDevID, dwParam, lpParms);
313 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
315 /* always close elements ? */
316 if (wmw->hFile != 0) {
317 mmioClose(wmw->hFile, 0);
318 wmw->hFile = 0;
321 dwRet = MMSYSERR_NOERROR; /* assume success */
323 if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
324 dwRet = wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
325 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
326 dwRet = widMessage(wmw->wWavID, WIDM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
327 wmw->fInput = TRUE;
328 } else if (wmw->fInput) {
329 dwRet = widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L);
330 if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
331 dwRet = wodMessage(wmw->wWavID, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
332 wmw->fInput = FALSE;
334 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
337 /**************************************************************************
338 * WAVE_mciStop [internal]
340 static DWORD WAVE_mciStop(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
342 DWORD dwRet;
343 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
345 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
347 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
348 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
350 wmw->dwStatus = MCI_MODE_STOP;
351 wmw->dwPosition = 0;
352 TRACE("wmw->dwStatus=%d\n", wmw->dwStatus);
354 if (wmw->fInput)
355 dwRet = widMessage(wmw->wWavID, WIDM_RESET, 0, dwFlags, (DWORD)lpParms);
356 else
357 dwRet = wodMessage(wmw->wWavID, WODM_RESET, 0, dwFlags, (DWORD)lpParms);
359 if (dwFlags & MCI_NOTIFY) {
360 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
361 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
362 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
365 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
368 /**************************************************************************
369 * WAVE_mciClose [internal]
371 static DWORD WAVE_mciClose(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
373 DWORD dwRet = 0;
374 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
376 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
378 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
380 if (wmw->dwStatus != MCI_MODE_STOP) {
381 dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
384 wmw->nUseCount--;
386 if (wmw->nUseCount == 0) {
387 DWORD mmRet;
388 if (wmw->hFile != 0) {
389 mmioClose(wmw->hFile, 0);
390 wmw->hFile = 0;
392 mmRet = (wmw->fInput) ? widMessage(wmw->wWavID, WIDM_CLOSE, 0, 0L, 0L) :
393 wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
395 if (mmRet != MMSYSERR_NOERROR) dwRet = MCIERR_INTERNAL;
398 if ((dwFlags & MCI_NOTIFY) && lpParms) {
399 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
400 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
401 wmw->wNotifyDeviceID,
402 (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
404 return 0;
407 /**************************************************************************
408 * WAVE_mciPlay [internal]
410 static DWORD WAVE_mciPlay(UINT16 wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
412 DWORD end;
413 LONG bufsize, count;
414 HGLOBAL16 hData;
415 DWORD dwRet;
416 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
418 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
420 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
421 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
423 if (wmw->fInput) {
424 WARN("cannot play on input device\n");
425 return MCIERR_NONAPPLICABLE_FUNCTION;
428 if (wmw->hFile == 0) {
429 WARN("Can't play: no file='%s' !\n", wmw->openParms.lpstrElementName);
430 return MCIERR_FILE_NOT_FOUND;
433 if (!(dwFlags & MCI_WAIT)) {
434 return MCI_SendCommandAsync(wmw->wNotifyDeviceID, MCI_PLAY, dwFlags,
435 (DWORD)lpParms, sizeof(MCI_PLAY_PARMS));
438 if (wmw->dwStatus != MCI_MODE_STOP) {
439 if (wmw->dwStatus == MCI_MODE_PAUSE) {
440 /* FIXME: parameters (start/end) in lpParams may not be used */
441 return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
443 return MCIERR_INTERNAL;
446 end = 0xFFFFFFFF;
447 if (lpParms && (dwFlags & MCI_FROM)) {
448 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
450 if (lpParms && (dwFlags & MCI_TO)) {
451 end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
454 TRACE("Playing from byte=%lu to byte=%lu\n", wmw->dwPosition, end);
456 /* go back to begining of chunk */
457 mmioSeek(wmw->hFile, wmw->dwFileOffset, SEEK_SET); /* >= 0 */
459 /* By default the device will be opened for output, the MCI_CUE function is there to
460 * change from output to input and back
462 /* FIXME: how to choose between several output channels ? here 0 is forced */
463 dwRet = wodMessage(0, WODM_OPEN, 0, (DWORD)&wmw->waveDesc, CALLBACK_NULL);
464 if (dwRet != 0) {
465 TRACE("Can't open low level audio device %ld\n", dwRet);
466 return MCIERR_DEVICE_OPEN;
469 /* at 22050 bytes per sec => 30 ms by block */
470 bufsize = 10240;
471 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
472 wmw->WaveHdr.lpData = (LPSTR)GlobalLock16(hData);
474 wmw->dwStatus = MCI_MODE_PLAY;
476 /* FIXME: this doesn't work if wmw->dwPosition != 0 */
477 /* FIXME: use several WaveHdr for smoother playback */
478 /* FIXME: use only regular MMSYS functions, not calling directly the driver */
479 while (wmw->dwStatus != MCI_MODE_STOP) {
480 wmw->WaveHdr.dwUser = 0L;
481 wmw->WaveHdr.dwFlags = 0L;
482 wmw->WaveHdr.dwLoops = 0L;
483 count = mmioRead(wmw->hFile, wmw->WaveHdr.lpData, bufsize);
484 TRACE("mmioRead bufsize=%ld count=%ld\n", bufsize, count);
485 if (count < 1)
486 break;
487 dwRet = wodMessage(wmw->wWavID, WODM_PREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
488 wmw->WaveHdr.dwBufferLength = count;
489 wmw->WaveHdr.dwBytesRecorded = 0;
490 /* FIXME */
491 wmw->WaveHdr.reserved = (DWORD)&wmw->WaveHdr;
492 TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%lu dwBytesRecorded=%lu\n",
493 &wmw->WaveHdr, wmw->WaveHdr.dwBufferLength, wmw->WaveHdr.dwBytesRecorded);
494 dwRet = wodMessage(wmw->wWavID, WODM_WRITE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
495 /* FIXME: should use callback mechanisms from audio driver */
496 #if 1
497 while (!(wmw->WaveHdr.dwFlags & WHDR_DONE))
498 Sleep(1);
499 #endif
500 wmw->dwPosition += count;
501 TRACE("after WODM_WRITE dwPosition=%lu\n", wmw->dwPosition);
502 dwRet = wodMessage(wmw->wWavID, WODM_UNPREPARE, 0, (DWORD)&wmw->WaveHdr, sizeof(WAVEHDR));
505 if (wmw->WaveHdr.lpData != NULL) {
506 GlobalUnlock16(hData);
507 GlobalFree16(hData);
508 wmw->WaveHdr.lpData = NULL;
511 wodMessage(wmw->wWavID, WODM_RESET, 0, 0L, 0L);
512 wodMessage(wmw->wWavID, WODM_CLOSE, 0, 0L, 0L);
514 wmw->dwStatus = MCI_MODE_STOP;
515 if (lpParms && (dwFlags & MCI_NOTIFY)) {
516 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
517 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
518 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
520 return 0;
523 /**************************************************************************
524 * WAVE_mciRecord [internal]
526 static DWORD WAVE_mciRecord(UINT16 wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
528 int start, end;
529 LONG bufsize;
530 HGLOBAL16 hData;
531 LPWAVEHDR lpWaveHdr;
532 DWORD dwRet;
533 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
535 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
537 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
538 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
540 if (!wmw->fInput) {
541 WARN("cannot record on output device\n");
542 return MCIERR_NONAPPLICABLE_FUNCTION;
545 if (wmw->hFile == 0) {
546 WARN("can't find file='%s' !\n",
547 wmw->openParms.lpstrElementName);
548 return MCIERR_FILE_NOT_FOUND;
550 start = 1; end = 99999;
551 if (dwFlags & MCI_FROM) {
552 start = lpParms->dwFrom;
553 TRACE("MCI_FROM=%d \n", start);
555 if (dwFlags & MCI_TO) {
556 end = lpParms->dwTo;
557 TRACE("MCI_TO=%d \n", end);
559 bufsize = 64000;
560 lpWaveHdr = &wmw->WaveHdr;
561 hData = GlobalAlloc16(GMEM_MOVEABLE, bufsize);
562 lpWaveHdr->lpData = (LPSTR)GlobalLock16(hData);
563 lpWaveHdr->dwBufferLength = bufsize;
564 lpWaveHdr->dwUser = 0L;
565 lpWaveHdr->dwFlags = 0L;
566 lpWaveHdr->dwLoops = 0L;
567 dwRet = widMessage(wmw->wWavID, WIDM_PREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
568 TRACE("after WIDM_PREPARE \n");
569 while (TRUE) {
570 lpWaveHdr->dwBytesRecorded = 0;
571 dwRet = widMessage(wmw->wWavID, WIDM_START, 0, 0L, 0L);
572 TRACE("after WIDM_START lpWaveHdr=%p dwBytesRecorded=%lu\n",
573 lpWaveHdr, lpWaveHdr->dwBytesRecorded);
574 if (lpWaveHdr->dwBytesRecorded == 0) break;
576 TRACE("before WIDM_UNPREPARE \n");
577 dwRet = widMessage(wmw->wWavID, WIDM_UNPREPARE, 0, (DWORD)lpWaveHdr, sizeof(WAVEHDR));
578 TRACE("after WIDM_UNPREPARE \n");
579 if (lpWaveHdr->lpData != NULL) {
580 GlobalUnlock16(hData);
581 GlobalFree16(hData);
582 lpWaveHdr->lpData = NULL;
584 if (dwFlags & MCI_NOTIFY) {
585 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
586 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
587 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
589 return 0;
592 /**************************************************************************
593 * WAVE_mciPause [internal]
595 static DWORD WAVE_mciPause(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
597 DWORD dwRet;
598 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
600 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
602 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
603 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
605 if (wmw->dwStatus == MCI_MODE_PLAY) {
606 wmw->dwStatus = MCI_MODE_PAUSE;
609 if (wmw->fInput) dwRet = widMessage(wmw->wWavID, WIDM_PAUSE, 0, 0L, 0L);
610 else dwRet = wodMessage(wmw->wWavID, WODM_PAUSE, 0, 0L, 0L);
612 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
615 /**************************************************************************
616 * WAVE_mciResume [internal]
618 static DWORD WAVE_mciResume(UINT16 wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
620 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
621 DWORD dwRet = 0;
623 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
625 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
626 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
628 if (wmw->dwStatus == MCI_MODE_PAUSE) {
629 wmw->dwStatus = MCI_MODE_PLAY;
632 /* FIXME: I doubt WIDM_START is correct */
633 if (wmw->fInput) dwRet = widMessage(wmw->wWavID, WIDM_START, 0, 0L, 0L);
634 else dwRet = wodMessage(wmw->wWavID, WODM_RESTART, 0, 0L, 0L);
635 return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
638 /**************************************************************************
639 * WAVE_mciSeek [internal]
641 static DWORD WAVE_mciSeek(UINT16 wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
643 DWORD ret = 0;
644 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
646 TRACE("(%04X, %08lX, %p);\n", wDevID, dwFlags, lpParms);
648 if (lpParms == NULL) {
649 ret = MCIERR_NULL_PARAMETER_BLOCK;
650 } else if (wmw == NULL) {
651 ret = MCIERR_INVALID_DEVICE_ID;
652 } else {
653 WAVE_mciStop(wDevID, MCI_WAIT, 0);
655 if (dwFlags & MCI_SEEK_TO_START) {
656 wmw->dwPosition = 0;
657 } else if (dwFlags & MCI_SEEK_TO_END) {
658 wmw->dwPosition = 0xFFFFFFFF; /* fixme */
659 } else if (dwFlags & MCI_TO) {
660 wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
661 } else {
662 WARN("dwFlag doesn't tell where to seek to...\n");
663 return MCIERR_MISSING_PARAMETER;
666 TRACE("Seeking to position=%lu bytes\n", wmw->dwPosition);
668 if (dwFlags & MCI_NOTIFY) {
669 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
670 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
671 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
674 return ret;
677 /**************************************************************************
678 * WAVE_mciSet [internal]
680 static DWORD WAVE_mciSet(UINT16 wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
682 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
684 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
686 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
687 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
689 if (dwFlags & MCI_SET_TIME_FORMAT) {
690 switch (lpParms->dwTimeFormat) {
691 case MCI_FORMAT_MILLISECONDS:
692 TRACE("MCI_FORMAT_MILLISECONDS !\n");
693 wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
694 break;
695 case MCI_FORMAT_BYTES:
696 TRACE("MCI_FORMAT_BYTES !\n");
697 wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
698 break;
699 case MCI_FORMAT_SAMPLES:
700 TRACE("MCI_FORMAT_SAMPLES !\n");
701 wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
702 break;
703 default:
704 WARN("Bad time format %lu!\n", lpParms->dwTimeFormat);
705 return MCIERR_BAD_TIME_FORMAT;
708 if (dwFlags & MCI_SET_VIDEO) {
709 TRACE("No support for video !\n");
710 return MCIERR_UNSUPPORTED_FUNCTION;
712 if (dwFlags & MCI_SET_DOOR_OPEN) {
713 TRACE("No support for door open !\n");
714 return MCIERR_UNSUPPORTED_FUNCTION;
716 if (dwFlags & MCI_SET_DOOR_CLOSED) {
717 TRACE("No support for door close !\n");
718 return MCIERR_UNSUPPORTED_FUNCTION;
720 if (dwFlags & MCI_SET_AUDIO) {
721 if (dwFlags & MCI_SET_ON) {
722 TRACE("MCI_SET_ON audio !\n");
723 } else if (dwFlags & MCI_SET_OFF) {
724 TRACE("MCI_SET_OFF audio !\n");
725 } else {
726 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
727 return MCIERR_BAD_INTEGER;
730 if (lpParms->dwAudio & MCI_SET_AUDIO_ALL)
731 TRACE("MCI_SET_AUDIO_ALL !\n");
732 if (lpParms->dwAudio & MCI_SET_AUDIO_LEFT)
733 TRACE("MCI_SET_AUDIO_LEFT !\n");
734 if (lpParms->dwAudio & MCI_SET_AUDIO_RIGHT)
735 TRACE("MCI_SET_AUDIO_RIGHT !\n");
737 if (dwFlags & MCI_WAVE_INPUT)
738 TRACE("MCI_WAVE_INPUT !\n");
739 if (dwFlags & MCI_WAVE_OUTPUT)
740 TRACE("MCI_WAVE_OUTPUT !\n");
741 if (dwFlags & MCI_WAVE_SET_ANYINPUT)
742 TRACE("MCI_WAVE_SET_ANYINPUT !\n");
743 if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
744 TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
745 if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC)
746 TRACE("MCI_WAVE_SET_AVGBYTESPERSEC !\n");
747 if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE)
748 TRACE("MCI_WAVE_SET_BITSPERSAMPLE !\n");
749 if (dwFlags & MCI_WAVE_SET_BLOCKALIGN)
750 TRACE("MCI_WAVE_SET_BLOCKALIGN !\n");
751 if (dwFlags & MCI_WAVE_SET_CHANNELS)
752 TRACE("MCI_WAVE_SET_CHANNELS !\n");
753 if (dwFlags & MCI_WAVE_SET_FORMATTAG)
754 TRACE("MCI_WAVE_SET_FORMATTAG !\n");
755 if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC)
756 TRACE("MCI_WAVE_SET_SAMPLESPERSEC !\n");
757 return 0;
760 /**************************************************************************
761 * WAVE_mciStatus [internal]
763 static DWORD WAVE_mciStatus(UINT16 wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
765 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
767 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
768 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
769 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
771 if (dwFlags & MCI_STATUS_ITEM) {
772 switch(lpParms->dwItem) {
773 case MCI_STATUS_CURRENT_TRACK:
774 lpParms->dwReturn = 1;
775 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
776 break;
777 case MCI_STATUS_LENGTH:
778 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
779 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->dwLength);
780 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
781 break;
782 case MCI_STATUS_MODE:
783 lpParms->dwReturn = wmw->dwStatus;
784 TRACE("MCI_STATUS_MODE => %lu\n", lpParms->dwReturn);
785 break;
786 case MCI_STATUS_MEDIA_PRESENT:
787 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
788 lpParms->dwReturn = TRUE;
789 break;
790 case MCI_STATUS_NUMBER_OF_TRACKS:
791 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
792 lpParms->dwReturn = 1;
793 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu!\n", lpParms->dwReturn);
794 break;
795 case MCI_STATUS_POSITION:
796 /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
797 lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
798 (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
799 TRACE("MCI_STATUS_POSITION %s => %lu\n",
800 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
801 break;
802 case MCI_STATUS_READY:
803 lpParms->dwReturn = (wmw->dwStatus != MCI_MODE_NOT_READY);
804 TRACE("MCI_STATUS_READY => %lu!\n", lpParms->dwReturn);
805 break;
806 case MCI_STATUS_TIME_FORMAT:
807 lpParms->dwReturn = wmw->dwMciTimeFormat;
808 TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
809 break;
810 case MCI_WAVE_INPUT:
811 TRACE("MCI_WAVE_INPUT !\n");
812 lpParms->dwReturn = 0;
813 break;
814 case MCI_WAVE_OUTPUT:
815 TRACE("MCI_WAVE_OUTPUT !\n");
816 lpParms->dwReturn = 0;
817 break;
818 case MCI_WAVE_STATUS_AVGBYTESPERSEC:
819 lpParms->dwReturn = wmw->WaveFormat.wf.nAvgBytesPerSec;
820 TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu!\n", lpParms->dwReturn);
821 break;
822 case MCI_WAVE_STATUS_BITSPERSAMPLE:
823 lpParms->dwReturn = wmw->WaveFormat.wBitsPerSample;
824 TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu!\n", lpParms->dwReturn);
825 break;
826 case MCI_WAVE_STATUS_BLOCKALIGN:
827 lpParms->dwReturn = wmw->WaveFormat.wf.nBlockAlign;
828 TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu!\n", lpParms->dwReturn);
829 break;
830 case MCI_WAVE_STATUS_CHANNELS:
831 lpParms->dwReturn = wmw->WaveFormat.wf.nChannels;
832 TRACE("MCI_WAVE_STATUS_CHANNELS => %lu!\n", lpParms->dwReturn);
833 break;
834 case MCI_WAVE_STATUS_FORMATTAG:
835 lpParms->dwReturn = wmw->WaveFormat.wf.wFormatTag;
836 TRACE("MCI_WAVE_FORMATTAG => %lu!\n", lpParms->dwReturn);
837 break;
838 case MCI_WAVE_STATUS_LEVEL:
839 TRACE("MCI_WAVE_STATUS_LEVEL !\n");
840 lpParms->dwReturn = 0xAAAA5555;
841 break;
842 case MCI_WAVE_STATUS_SAMPLESPERSEC:
843 lpParms->dwReturn = wmw->WaveFormat.wf.nSamplesPerSec;
844 TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu!\n", lpParms->dwReturn);
845 break;
846 default:
847 WARN("unknown command %08lX !\n", lpParms->dwItem);
848 return MCIERR_UNRECOGNIZED_COMMAND;
851 if (dwFlags & MCI_NOTIFY) {
852 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
853 mciDriverNotify16((HWND16)LOWORD(lpParms->dwCallback),
854 wmw->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
856 return 0;
859 /**************************************************************************
860 * WAVE_mciGetDevCaps [internal]
862 static DWORD WAVE_mciGetDevCaps(UINT16 wDevID, DWORD dwFlags,
863 LPMCI_GETDEVCAPS_PARMS lpParms)
865 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
867 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
869 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
870 if (wmw == NULL) return MCIERR_INVALID_DEVICE_ID;
872 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
873 switch(lpParms->dwItem) {
874 case MCI_GETDEVCAPS_DEVICE_TYPE:
875 lpParms->dwReturn = MCI_DEVTYPE_WAVEFORM_AUDIO;
876 break;
877 case MCI_GETDEVCAPS_HAS_AUDIO:
878 lpParms->dwReturn = TRUE;
879 break;
880 case MCI_GETDEVCAPS_HAS_VIDEO:
881 lpParms->dwReturn = FALSE;
882 break;
883 case MCI_GETDEVCAPS_USES_FILES:
884 lpParms->dwReturn = TRUE;
885 break;
886 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
887 lpParms->dwReturn = TRUE;
888 break;
889 case MCI_GETDEVCAPS_CAN_RECORD:
890 lpParms->dwReturn = TRUE;
891 break;
892 case MCI_GETDEVCAPS_CAN_EJECT:
893 lpParms->dwReturn = FALSE;
894 break;
895 case MCI_GETDEVCAPS_CAN_PLAY:
896 lpParms->dwReturn = TRUE;
897 break;
898 case MCI_GETDEVCAPS_CAN_SAVE:
899 lpParms->dwReturn = TRUE;
900 break;
901 case MCI_WAVE_GETDEVCAPS_INPUTS:
902 lpParms->dwReturn = 1;
903 break;
904 case MCI_WAVE_GETDEVCAPS_OUTPUTS:
905 lpParms->dwReturn = 1;
906 break;
907 default:
908 TRACE("Unknown capability (%08lx) !\n", lpParms->dwItem);
909 return MCIERR_UNRECOGNIZED_COMMAND;
912 return 0;
915 /**************************************************************************
916 * WAVE_mciInfo [internal]
918 static DWORD WAVE_mciInfo(UINT16 wDevID, DWORD dwFlags, LPMCI_INFO_PARMS16 lpParms)
920 DWORD ret = 0;
921 LPCSTR str = 0;
922 WINE_MCIWAVE* wmw = WAVE_mciGetOpenDev(wDevID);
924 TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
926 if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
927 ret = MCIERR_NULL_PARAMETER_BLOCK;
928 } else if (wmw == NULL) {
929 ret = MCIERR_INVALID_DEVICE_ID;
930 } else {
931 TRACE("buf=%p, len=%lu\n", lpParms->lpstrReturn, lpParms->dwRetSize);
933 switch(dwFlags) {
934 case MCI_INFO_PRODUCT:
935 str = "Wine's audio player";
936 break;
937 case MCI_INFO_FILE:
938 str = wmw->openParms.lpstrElementName;
939 break;
940 case MCI_WAVE_INPUT:
941 str = "Wine Wave In";
942 break;
943 case MCI_WAVE_OUTPUT:
944 str = "Wine Wave Out";
945 break;
946 default:
947 WARN("Don't know this info command (%lu)\n", dwFlags);
948 ret = MCIERR_UNRECOGNIZED_COMMAND;
951 if (str) {
952 if (strlen(str) + 1 > lpParms->dwRetSize) {
953 ret = MCIERR_PARAM_OVERFLOW;
954 } else {
955 lstrcpynA(lpParms->lpstrReturn, str, lpParms->dwRetSize);
957 } else {
958 lpParms->lpstrReturn[0] = 0;
961 return ret;
964 /**************************************************************************
965 * MCIWAVE_DriverProc [sample driver]
967 LONG CALLBACK MCIWAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
968 DWORD dwParam1, DWORD dwParam2)
970 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",
971 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
973 switch(wMsg) {
974 case DRV_LOAD: return 1;
975 case DRV_FREE: return 1;
976 case DRV_OPEN: return WAVE_drvOpen((LPSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSA)dwParam2);
977 case DRV_CLOSE: return WAVE_drvClose(dwDevID);
978 case DRV_ENABLE: return 1;
979 case DRV_DISABLE: return 1;
980 case DRV_QUERYCONFIGURE: return 1;
981 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK); return 1;
982 case DRV_INSTALL: return DRVCNF_RESTART;
983 case DRV_REMOVE: return DRVCNF_RESTART;
984 case MCI_OPEN_DRIVER: return WAVE_mciOpen (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSA) dwParam2);
985 case MCI_CLOSE_DRIVER: return WAVE_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
986 case MCI_CUE: return WAVE_mciCue (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
987 case MCI_PLAY: return WAVE_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2);
988 case MCI_RECORD: return WAVE_mciRecord (dwDevID, dwParam1, (LPMCI_RECORD_PARMS) dwParam2);
989 case MCI_STOP: return WAVE_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
990 case MCI_SET: return WAVE_mciSet (dwDevID, dwParam1, (LPMCI_SET_PARMS) dwParam2);
991 case MCI_PAUSE: return WAVE_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
992 case MCI_RESUME: return WAVE_mciResume (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2);
993 case MCI_STATUS: return WAVE_mciStatus (dwDevID, dwParam1, (LPMCI_STATUS_PARMS) dwParam2);
994 case MCI_GETDEVCAPS: return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2);
995 case MCI_INFO: return WAVE_mciInfo (dwDevID, dwParam1, (LPMCI_INFO_PARMS16) dwParam2);
996 case MCI_SEEK: return WAVE_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2);
997 case MCI_LOAD:
998 case MCI_SAVE:
999 case MCI_FREEZE:
1000 case MCI_PUT:
1001 case MCI_REALIZE:
1002 case MCI_UNFREEZE:
1003 case MCI_UPDATE:
1004 case MCI_WHERE:
1005 case MCI_WINDOW:
1006 case MCI_STEP:
1007 case MCI_SPIN:
1008 case MCI_ESCAPE:
1009 case MCI_COPY:
1010 case MCI_CUT:
1011 case MCI_DELETE:
1012 case MCI_PASTE:
1013 WARN("Unsupported command=%s\n", MCI_CommandToString(wMsg));
1014 break;
1015 case MCI_OPEN:
1016 case MCI_CLOSE:
1017 FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1018 break;
1019 default:
1020 FIXME("is probably wrong msg=%s\n", MCI_CommandToString(wMsg));
1021 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1023 return MCIERR_UNRECOGNIZED_COMMAND;