1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
5 * Copyright 1994 Martin Ayotte
9 * - record/play should and must be done asynchronous
10 * - segmented/linear pointer problems (lpData in waveheaders,W*_DONE cbs)
21 #include <sys/ioctl.h>
32 #ifdef HAVE_MACHINE_SOUNDCARD_H
33 # include <machine/soundcard.h>
35 #ifdef HAVE_SYS_SOUNDCARD_H
36 # include <sys/soundcard.h>
39 #define SOUND_DEV "/dev/dsp"
40 #define MIXER_DEV "/dev/mixer"
43 #define IOCTL(a,b,c) ((-1==ioctl(a,b,&c))&&(perror("ioctl:"#b":"#c),0))
45 #define IOCTL(a,b,c) (c = ioctl(a,b,c) )
48 #define MAX_WAVEOUTDRV (1)
49 #define MAX_WAVEINDRV (1)
55 WAVEOPENDESC waveDesc;
65 DWORD bufsize; /* OpenSound '/dev/dsp' give us that size */
66 WAVEOPENDESC waveDesc;
70 DWORD dwTotalRecorded;
73 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
74 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
76 /*======================================================================*
77 * Low level WAVE implemantation *
78 *======================================================================*/
80 /**************************************************************************
81 * WAVE_NotifyClient [internal]
83 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg,
84 DWORD dwParam1, DWORD dwParam2)
86 TRACE(wave,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
92 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
94 if (WOutDev[wDevID].wFlags != DCB_NULL &&
96 WOutDev[wDevID].waveDesc.dwCallBack,
97 WOutDev[wDevID].wFlags,
98 WOutDev[wDevID].waveDesc.hWave,
100 WOutDev[wDevID].waveDesc.dwInstance,
103 WARN(wave, "can't notify client !\n");
104 return MMSYSERR_NOERROR;
111 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
113 if (WInDev[wDevID].wFlags != DCB_NULL &&
115 WInDev[wDevID].waveDesc.dwCallBack,
116 WInDev[wDevID].wFlags,
117 WInDev[wDevID].waveDesc.hWave,
119 WInDev[wDevID].waveDesc.dwInstance,
122 WARN(wave, "can't notify client !\n");
123 return MMSYSERR_NOERROR;
130 /**************************************************************************
131 * wodGetDevCaps [internal]
133 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
141 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
142 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
143 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
144 audio = open (SOUND_DEV, O_WRONLY, 0);
145 if (audio == -1) return MMSYSERR_ALLOCATED ;
147 lpCaps->wMid = 0x0002;
148 lpCaps->wPid = 0x0104;
149 strcpy(lpCaps->szPname, "SB16 Wave Out");
151 lpCaps->wMid = 0x00FF; /* Manufac ID */
152 lpCaps->wPid = 0x0001; /* Product ID */
153 strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");
155 lpCaps->vDriverVersion = 0x0100;
156 lpCaps->dwFormats = 0x00000000;
157 lpCaps->dwSupport = WAVECAPS_VOLUME;
159 /* First bytespersampl, then stereo */
160 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
162 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
163 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
166 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
167 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
168 if (lpCaps->wChannels > 1)
169 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
170 if (bytespersmpl > 1) {
171 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
172 if (lpCaps->wChannels > 1)
173 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
177 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
178 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
179 if (lpCaps->wChannels > 1)
180 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
181 if (bytespersmpl > 1) {
182 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
183 if (lpCaps->wChannels > 1)
184 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
188 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
189 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
190 if (lpCaps->wChannels > 1)
191 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
192 if (bytespersmpl > 1) {
193 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
194 if (lpCaps->wChannels > 1)
195 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
199 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
200 return MMSYSERR_NOERROR;
204 /**************************************************************************
207 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
209 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
210 LPWAVEFORMAT lpFormat;
212 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
213 if (lpDesc == NULL) {
214 WARN(wave, "Invalid Parameter !\n");
215 return MMSYSERR_INVALPARAM;
217 if (wDevID >= MAX_WAVEOUTDRV) {
218 TRACE(wave,"MAX_WAVOUTDRV reached !\n");
219 return MMSYSERR_ALLOCATED;
221 WOutDev[wDevID].unixdev = 0;
222 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
223 audio = open (SOUND_DEV, O_WRONLY, 0);
225 WARN(wave, "can't open !\n");
226 return MMSYSERR_ALLOCATED ;
228 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
229 if (abuf_size < 1024 || abuf_size > 65536) {
231 WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
233 WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
234 return MMSYSERR_NOTENABLED;
236 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
237 switch(WOutDev[wDevID].wFlags) {
239 TRACE(wave, "CALLBACK_NULL !\n");
242 TRACE(wave, "CALLBACK_WINDOW !\n");
245 TRACE(wave, "CALLBACK_TASK !\n");
248 TRACE(wave, "CALLBACK_FUNCTION !\n");
251 WOutDev[wDevID].lpQueueHdr = NULL;
252 WOutDev[wDevID].unixdev = audio;
253 WOutDev[wDevID].dwTotalPlayed = 0;
254 WOutDev[wDevID].bufsize = abuf_size;
255 /* FIXME: copy lpFormat too? */
256 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
257 TRACE(wave,"lpDesc->lpFormat = %p\n",lpDesc->lpFormat);
258 lpFormat = lpDesc->lpFormat;
259 TRACE(wave,"lpFormat = %p\n",lpFormat);
260 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
261 WARN(wave,"Bad format %04X !\n", lpFormat->wFormatTag);
262 WARN(wave,"Bad nChannels %d !\n", lpFormat->nChannels);
263 WARN(wave,"Bad nSamplesPerSec %ld !\n", lpFormat->nSamplesPerSec);
264 return WAVERR_BADFORMAT;
266 memcpy(&WOutDev[wDevID].Format, lpFormat, sizeof(PCMWAVEFORMAT));
267 if (WOutDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT;
268 if (WOutDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
269 TRACE(wave,"wBitsPerSample=%u !\n", WOutDev[wDevID].Format.wBitsPerSample);
270 if (WOutDev[wDevID].Format.wBitsPerSample == 0) {
271 WOutDev[wDevID].Format.wBitsPerSample = 8 *
272 (WOutDev[wDevID].Format.wf.nAvgBytesPerSec /
273 WOutDev[wDevID].Format.wf.nSamplesPerSec) /
274 WOutDev[wDevID].Format.wf.nChannels;
276 samplesize = WOutDev[wDevID].Format.wBitsPerSample;
277 smplrate = WOutDev[wDevID].Format.wf.nSamplesPerSec;
278 dsp_stereo = (WOutDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
280 /* First size and stereo then samplerate */
281 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
282 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
283 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
285 TRACE(wave,"wBitsPerSample=%u !\n", WOutDev[wDevID].Format.wBitsPerSample);
286 TRACE(wave,"nAvgBytesPerSec=%lu !\n", WOutDev[wDevID].Format.wf.nAvgBytesPerSec);
287 TRACE(wave,"nSamplesPerSec=%lu !\n", WOutDev[wDevID].Format.wf.nSamplesPerSec);
288 TRACE(wave,"nChannels=%u !\n", WOutDev[wDevID].Format.wf.nChannels);
289 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
290 WARN(wave, "can't notify client !\n");
291 return MMSYSERR_INVALPARAM;
293 return MMSYSERR_NOERROR;
296 /**************************************************************************
297 * wodClose [internal]
299 static DWORD wodClose(WORD wDevID)
301 TRACE(wave,"(%u);\n", wDevID);
303 if (wDevID > MAX_WAVEOUTDRV) return MMSYSERR_INVALPARAM;
304 if (WOutDev[wDevID].unixdev == 0) {
305 WARN(wave, "can't close !\n");
306 return MMSYSERR_NOTENABLED;
308 if (WOutDev[wDevID].lpQueueHdr != NULL) {
309 WARN(wave, "still buffers open !\n");
310 /* Don't care. Who needs those buffers anyway */
311 /*return WAVERR_STILLPLAYING; */
313 close(WOutDev[wDevID].unixdev);
314 WOutDev[wDevID].unixdev = 0;
315 WOutDev[wDevID].bufsize = 0;
316 WOutDev[wDevID].lpQueueHdr = NULL;
317 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
318 WARN(wave, "can't notify client !\n");
319 return MMSYSERR_INVALPARAM;
321 return MMSYSERR_NOERROR;
324 /**************************************************************************
325 * wodWrite [internal]
326 * FIXME: this should _APPEND_ the lpWaveHdr to the output queue of the
327 * device, and initiate async playing.
329 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
335 TRACE(wave,"(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
336 if (WOutDev[wDevID].unixdev == 0) {
337 WARN(wave, "can't play !\n");
338 return MMSYSERR_NOTENABLED;
340 if (lpWaveHdr->lpData == NULL) return WAVERR_UNPREPARED;
341 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) return WAVERR_UNPREPARED;
342 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) return WAVERR_STILLPLAYING;
343 lpWaveHdr->dwFlags &= ~WHDR_DONE;
344 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
345 TRACE(wave, "dwBufferLength %lu !\n", lpWaveHdr->dwBufferLength);
346 TRACE(wave, "WOutDev[%u].unixdev %u !\n", wDevID, WOutDev[wDevID].unixdev);
347 lpData = lpWaveHdr->lpData;
348 count = write (WOutDev[wDevID].unixdev, lpData, lpWaveHdr->dwBufferLength);
349 TRACE(wave,"write returned count %u !\n",count);
350 if (count != lpWaveHdr->dwBufferLength) {
351 WARN(wave, " error writting !\n");
352 return MMSYSERR_NOTENABLED;
354 WOutDev[wDevID].dwTotalPlayed += count;
355 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
356 lpWaveHdr->dwFlags |= WHDR_DONE;
357 if ((DWORD)lpWaveHdr->lpData!=lpWaveHdr->reserved) {
358 /* FIXME: what if it expects it's OWN lpwavehdr back? */
359 xwavehdr = SEGPTR_NEW(WAVEHDR);
360 memcpy(xwavehdr,lpWaveHdr,sizeof(WAVEHDR));
361 xwavehdr->lpData = (LPBYTE)xwavehdr->reserved;
362 if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)SEGPTR_GET(xwavehdr), count) != MMSYSERR_NOERROR) {
363 WARN(wave, "can't notify client !\n");
364 SEGPTR_FREE(xwavehdr);
365 return MMSYSERR_INVALPARAM;
367 SEGPTR_FREE(xwavehdr);
369 if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)lpWaveHdr, count) != MMSYSERR_NOERROR) {
370 WARN(wave, "can't notify client !\n");
371 return MMSYSERR_INVALPARAM;
374 return MMSYSERR_NOERROR;
377 /**************************************************************************
378 * wodPrepare [internal]
380 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
382 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
383 if (WOutDev[wDevID].unixdev == 0) {
384 WARN(wave, "can't prepare !\n");
385 return MMSYSERR_NOTENABLED;
387 /* don't append to queue, wodWrite does that */
388 WOutDev[wDevID].dwTotalPlayed = 0;
389 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
390 return WAVERR_STILLPLAYING;
391 lpWaveHdr->dwFlags |= WHDR_PREPARED;
392 lpWaveHdr->dwFlags &= ~WHDR_DONE;
393 return MMSYSERR_NOERROR;
396 /**************************************************************************
397 * wodUnprepare [internal]
399 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
401 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
402 if (WOutDev[wDevID].unixdev == 0) {
403 WARN(wave, "can't unprepare !\n");
404 return MMSYSERR_NOTENABLED;
406 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
407 return WAVERR_STILLPLAYING;
409 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
410 lpWaveHdr->dwFlags |= WHDR_DONE;
411 TRACE(wave, "all headers unprepared !\n");
412 return MMSYSERR_NOERROR;
415 /**************************************************************************
416 * wodRestart [internal]
418 static DWORD wodRestart(WORD wDevID)
420 TRACE(wave,"(%u);\n", wDevID);
421 if (WOutDev[wDevID].unixdev == 0) {
422 WARN(wave, "can't restart !\n");
423 return MMSYSERR_NOTENABLED;
425 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
426 /* FIXME: Myst crashes with this ... hmm -MM
427 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
428 WARN(wave, "can't notify client !\n");
429 return MMSYSERR_INVALPARAM;
433 return MMSYSERR_NOERROR;
436 /**************************************************************************
437 * wodReset [internal]
439 static DWORD wodReset(WORD wDevID)
441 TRACE(wave,"(%u);\n", wDevID);
442 if (WOutDev[wDevID].unixdev == 0) {
443 WARN(wave, "can't reset !\n");
444 return MMSYSERR_NOTENABLED;
446 return MMSYSERR_NOERROR;
450 /**************************************************************************
451 * wodGetPosition [internal]
453 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
456 TRACE(wave,"(%u, %p, %lu);\n", wDevID, lpTime, uSize);
457 if (WOutDev[wDevID].unixdev == 0) {
458 WARN(wave, "can't get pos !\n");
459 return MMSYSERR_NOTENABLED;
461 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
462 TRACE(wave,"wType=%04X !\n", lpTime->wType);
463 TRACE(wave,"wBitsPerSample=%u\n", WOutDev[wDevID].Format.wBitsPerSample);
464 TRACE(wave,"nSamplesPerSec=%lu\n", WOutDev[wDevID].Format.wf.nSamplesPerSec);
465 TRACE(wave,"nChannels=%u\n", WOutDev[wDevID].Format.wf.nChannels);
466 TRACE(wave,"nAvgBytesPerSec=%lu\n", WOutDev[wDevID].Format.wf.nAvgBytesPerSec);
467 switch(lpTime->wType) {
469 lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
470 TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
473 TRACE(wave,"dwTotalPlayed=%lu\n", WOutDev[wDevID].dwTotalPlayed);
474 TRACE(wave,"wBitsPerSample=%u\n", WOutDev[wDevID].Format.wBitsPerSample);
475 lpTime->u.sample = WOutDev[wDevID].dwTotalPlayed * 8 /
476 WOutDev[wDevID].Format.wBitsPerSample;
477 TRACE(wave,"TIME_SAMPLES=%lu\n", lpTime->u.sample);
480 time = WOutDev[wDevID].dwTotalPlayed /
481 (WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
482 lpTime->u.smpte.hour = time / 108000;
483 time -= lpTime->u.smpte.hour * 108000;
484 lpTime->u.smpte.min = time / 1800;
485 time -= lpTime->u.smpte.min * 1800;
486 lpTime->u.smpte.sec = time / 30;
487 time -= lpTime->u.smpte.sec * 30;
488 lpTime->u.smpte.frame = time;
489 lpTime->u.smpte.fps = 30;
490 TRACE(wave, "wodGetPosition // TIME_SMPTE=%02u:%02u:%02u:%02u\n",
491 lpTime->u.smpte.hour, lpTime->u.smpte.min,
492 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
495 FIXME(wave, "wodGetPosition() format %d not supported ! use TIME_MS !\n",lpTime->wType);
496 lpTime->wType = TIME_MS;
498 lpTime->u.ms = WOutDev[wDevID].dwTotalPlayed /
499 (WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
500 TRACE(wave,"wodGetPosition // TIME_MS=%lu\n", lpTime->u.ms);
503 return MMSYSERR_NOERROR;
506 /**************************************************************************
507 * wodGetVolume [internal]
509 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
512 int volume, left, right;
513 TRACE(wave,"(%u, %p);\n", wDevID, lpdwVol);
514 if (lpdwVol == NULL) return MMSYSERR_NOTENABLED;
515 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
516 WARN(wave, "mixer device not available !\n");
517 return MMSYSERR_NOTENABLED;
519 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
520 WARN(wave, "unable read mixer !\n");
521 return MMSYSERR_NOTENABLED;
524 left = volume & 0x7F;
525 right = (volume >> 8) & 0x7F;
526 TRACE(wave,"left=%d right=%d !\n", left, right);
527 *lpdwVol = MAKELONG(left << 9, right << 9);
528 return MMSYSERR_NOERROR;
532 /**************************************************************************
533 * wodSetVolume [internal]
535 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
539 TRACE(wave,"(%u, %08lX);\n", wDevID, dwParam);
540 volume = (LOWORD(dwParam) >> 9 & 0x7F) +
541 ((HIWORD(dwParam) >> 9 & 0x7F) << 8);
542 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
543 WARN(wave, "mixer device not available !\n");
544 return MMSYSERR_NOTENABLED;
546 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
547 WARN(wave, "unable set mixer !\n");
548 return MMSYSERR_NOTENABLED;
551 return MMSYSERR_NOERROR;
554 /**************************************************************************
555 * wodMessage [sample driver]
557 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
558 DWORD dwParam1, DWORD dwParam2)
561 TRACE(wave,"wodMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
562 wDevID, wMsg, dwUser, dwParam1, dwParam2);
565 return wodOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
567 return wodClose(wDevID);
569 return wodWrite(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
571 return MMSYSERR_NOTSUPPORTED;
573 return MMSYSERR_NOTSUPPORTED;
575 return wodGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
577 return MMSYSERR_NOTSUPPORTED;
579 return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
581 return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
582 case WODM_GETDEVCAPS:
583 return wodGetDevCaps(wDevID,(LPWAVEOUTCAPS16)dwParam1,dwParam2);
584 case WODM_GETNUMDEVS:
585 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
586 audio = open (SOUND_DEV, O_WRONLY, 0);
597 return MMSYSERR_NOTSUPPORTED;
599 return MMSYSERR_NOTSUPPORTED;
600 case WODM_GETPLAYBACKRATE:
601 return MMSYSERR_NOTSUPPORTED;
602 case WODM_SETPLAYBACKRATE:
603 return MMSYSERR_NOTSUPPORTED;
605 return wodGetVolume(wDevID, (LPDWORD)dwParam1);
607 return wodSetVolume(wDevID, dwParam1);
609 return wodRestart(wDevID);
611 return wodReset(wDevID);
613 WARN(wave,"unknown message !\n");
615 return MMSYSERR_NOTSUPPORTED;
619 /*-----------------------------------------------------------------------*/
621 /**************************************************************************
622 * widGetDevCaps [internal]
624 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
626 int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
628 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
629 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
630 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
631 audio = open (SOUND_DEV, O_RDONLY, 0);
632 if (audio == -1) return MMSYSERR_ALLOCATED ;
634 lpCaps->wMid = 0x0002;
635 lpCaps->wPid = 0x0004;
636 strcpy(lpCaps->szPname, "SB16 Wave In");
638 lpCaps->wMid = 0x00FF; /* Manufac ID */
639 lpCaps->wPid = 0x0001; /* Product ID */
640 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
642 lpCaps->dwFormats = 0x00000000;
643 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
644 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
646 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
647 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
648 if (lpCaps->wChannels > 1)
649 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
650 if (bytespersmpl > 1) {
651 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
652 if (lpCaps->wChannels > 1)
653 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
657 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
658 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
659 if (lpCaps->wChannels > 1)
660 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
661 if (bytespersmpl > 1) {
662 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
663 if (lpCaps->wChannels > 1)
664 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
668 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
669 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
670 if (lpCaps->wChannels > 1)
671 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
672 if (bytespersmpl > 1) {
673 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
674 if (lpCaps->wChannels > 1)
675 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
679 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
680 return MMSYSERR_NOERROR;
684 /**************************************************************************
687 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
689 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
690 LPWAVEFORMAT lpFormat;
692 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
693 if (lpDesc == NULL) {
694 WARN(wave, "Invalid Parameter !\n");
695 return MMSYSERR_INVALPARAM;
697 if (wDevID >= MAX_WAVEINDRV) {
698 TRACE(wave,"MAX_WAVINDRV reached !\n");
699 return MMSYSERR_ALLOCATED;
701 WInDev[wDevID].unixdev = 0;
702 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
703 audio = open (SOUND_DEV, O_RDONLY, 0);
705 WARN(wave,"can't open !\n");
706 return MMSYSERR_ALLOCATED;
708 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
709 if (abuf_size < 1024 || abuf_size > 65536) {
711 WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
713 WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
714 return MMSYSERR_NOTENABLED;
716 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
717 switch(WInDev[wDevID].wFlags) {
719 TRACE(wave,"CALLBACK_NULL!\n");
722 TRACE(wave,"CALLBACK_WINDOW!\n");
725 TRACE(wave,"CALLBACK_TASK!\n");
728 TRACE(wave,"CALLBACK_FUNCTION!\n");
731 if (WInDev[wDevID].lpQueueHdr) {
732 HeapFree(GetProcessHeap(),0,WInDev[wDevID].lpQueueHdr);
733 WInDev[wDevID].lpQueueHdr = NULL;
735 WInDev[wDevID].unixdev = audio;
736 WInDev[wDevID].bufsize = abuf_size;
737 WInDev[wDevID].dwTotalRecorded = 0;
738 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
739 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
740 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
741 WARN(wave, "Bad format %04X !\n",
742 lpFormat->wFormatTag);
743 return WAVERR_BADFORMAT;
745 memcpy(&WInDev[wDevID].Format, lpFormat, sizeof(PCMWAVEFORMAT));
746 WInDev[wDevID].Format.wBitsPerSample = 8; /* <-------------- */
747 if (WInDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT;
748 if (WInDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
749 if (WInDev[wDevID].Format.wBitsPerSample == 0) {
750 WInDev[wDevID].Format.wBitsPerSample = 8 *
751 (WInDev[wDevID].Format.wf.nAvgBytesPerSec /
752 WInDev[wDevID].Format.wf.nSamplesPerSec) /
753 WInDev[wDevID].Format.wf.nChannels;
755 samplesize = WInDev[wDevID].Format.wBitsPerSample;
756 smplrate = WInDev[wDevID].Format.wf.nSamplesPerSec;
757 dsp_stereo = (WInDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
758 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
759 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
760 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
761 TRACE(wave,"wBitsPerSample=%u !\n", WInDev[wDevID].Format.wBitsPerSample);
762 TRACE(wave,"nSamplesPerSec=%lu !\n", WInDev[wDevID].Format.wf.nSamplesPerSec);
763 TRACE(wave,"nChannels=%u !\n", WInDev[wDevID].Format.wf.nChannels);
764 TRACE(wave,"nAvgBytesPerSec=%lu\n", WInDev[wDevID].Format.wf.nAvgBytesPerSec);
765 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
766 WARN(wave,"can't notify client !\n");
767 return MMSYSERR_INVALPARAM;
769 return MMSYSERR_NOERROR;
772 /**************************************************************************
773 * widClose [internal]
775 static DWORD widClose(WORD wDevID)
777 TRACE(wave,"(%u);\n", wDevID);
778 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
779 if (WInDev[wDevID].unixdev == 0) {
780 WARN(wave,"can't close !\n");
781 return MMSYSERR_NOTENABLED;
783 if (WInDev[wDevID].lpQueueHdr != NULL) {
784 WARN(wave, "still buffers open !\n");
785 return WAVERR_STILLPLAYING;
787 close(WInDev[wDevID].unixdev);
788 WInDev[wDevID].unixdev = 0;
789 WInDev[wDevID].bufsize = 0;
790 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
791 WARN(wave,"can't notify client !\n");
792 return MMSYSERR_INVALPARAM;
794 return MMSYSERR_NOERROR;
797 /**************************************************************************
798 * widAddBuffer [internal]
800 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
805 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
806 if (WInDev[wDevID].unixdev == 0) {
807 WARN(wave,"can't do it !\n");
808 return MMSYSERR_NOTENABLED;
810 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
811 TRACE(wave, "never been prepared !\n");
812 return WAVERR_UNPREPARED;
814 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
815 TRACE(wave, "header already in use !\n");
816 return WAVERR_STILLPLAYING;
818 lpWaveHdr->dwFlags |= WHDR_PREPARED;
819 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
820 lpWaveHdr->dwFlags &= ~WHDR_DONE;
821 lpWaveHdr->dwBytesRecorded = 0;
822 if (WInDev[wDevID].lpQueueHdr == NULL) {
823 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
825 lpWIHdr = WInDev[wDevID].lpQueueHdr;
826 while (lpWIHdr->lpNext != NULL) {
827 lpWIHdr = lpWIHdr->lpNext;
830 lpWIHdr->lpNext = lpWaveHdr;
831 lpWaveHdr->lpNext = NULL;
834 TRACE(wave, "buffer added ! (now %u in queue)\n", count);
835 return MMSYSERR_NOERROR;
838 /**************************************************************************
839 * widPrepare [internal]
841 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
843 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
844 if (WInDev[wDevID].unixdev == 0) {
845 WARN(wave,"can't prepare !\n");
846 return MMSYSERR_NOTENABLED;
848 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
849 return WAVERR_STILLPLAYING;
850 lpWaveHdr->dwFlags |= WHDR_PREPARED;
851 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
852 lpWaveHdr->dwFlags &= ~WHDR_DONE;
853 lpWaveHdr->dwBytesRecorded = 0;
854 TRACE(wave,"header prepared !\n");
855 return MMSYSERR_NOERROR;
858 /**************************************************************************
859 * widUnprepare [internal]
861 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
863 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
864 if (WInDev[wDevID].unixdev == 0) {
865 WARN(wave,"can't unprepare !\n");
866 return MMSYSERR_NOTENABLED;
868 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
869 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
870 lpWaveHdr->dwFlags |= WHDR_DONE;
872 TRACE(wave, "all headers unprepared !\n");
873 return MMSYSERR_NOERROR;
876 /**************************************************************************
877 * widStart [internal]
879 static DWORD widStart(WORD wDevID)
884 LPWAVEHDR *lpWaveHdr;
886 TRACE(wave,"(%u);\n", wDevID);
887 if (WInDev[wDevID].unixdev == 0) {
888 WARN(wave, "can't start recording !\n");
889 return MMSYSERR_NOTENABLED;
892 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
893 TRACE(wave,"lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
894 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
895 TRACE(wave,"never been prepared !\n");
896 return WAVERR_UNPREPARED;
899 while(*lpWaveHdr != NULL) {
900 lpWIHdr = *lpWaveHdr;
901 TRACE(wave, "recording buf#%u=%p size=%lu \n",
902 count, lpWIHdr->lpData, lpWIHdr->dwBufferLength);
904 bytesRead = read (WInDev[wDevID].unixdev,
906 lpWIHdr->dwBufferLength);
908 perror("read from audio device");
909 TRACE(wave,"bytesread=%d (%ld)\n", bytesRead, lpWIHdr->dwBufferLength);
910 lpWIHdr->dwBytesRecorded = bytesRead;
911 WInDev[wDevID].dwTotalRecorded += lpWIHdr->dwBytesRecorded;
912 lpWIHdr->dwFlags &= ~WHDR_INQUEUE;
913 lpWIHdr->dwFlags |= WHDR_DONE;
915 /* FIXME: should pass segmented pointer here, do we need that?*/
916 if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)lpWaveHdr, lpWIHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
917 WARN(wave, "can't notify client !\n");
918 return MMSYSERR_INVALPARAM;
920 /* removes the current block from the queue */
921 *lpWaveHdr = lpWIHdr->lpNext;
924 TRACE(wave,"end of recording !\n");
926 return MMSYSERR_NOERROR;
929 /**************************************************************************
932 static DWORD widStop(WORD wDevID)
934 TRACE(wave,"(%u);\n", wDevID);
935 if (WInDev[wDevID].unixdev == 0) {
936 WARN(wave,"can't stop !\n");
937 return MMSYSERR_NOTENABLED;
939 return MMSYSERR_NOERROR;
942 /**************************************************************************
943 * widReset [internal]
945 static DWORD widReset(WORD wDevID)
947 TRACE(wave,"(%u);\n", wDevID);
948 if (WInDev[wDevID].unixdev == 0) {
949 WARN(wave,"can't reset !\n");
950 return MMSYSERR_NOTENABLED;
952 return MMSYSERR_NOERROR;
955 /**************************************************************************
956 * widGetPosition [internal]
958 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
962 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize);
963 if (WInDev[wDevID].unixdev == 0) {
964 WARN(wave,"can't get pos !\n");
965 return MMSYSERR_NOTENABLED;
967 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
968 TRACE(wave,"wType=%04X !\n", lpTime->wType);
969 TRACE(wave,"wBitsPerSample=%u\n", WInDev[wDevID].Format.wBitsPerSample);
970 TRACE(wave,"nSamplesPerSec=%lu\n", WInDev[wDevID].Format.wf.nSamplesPerSec);
971 TRACE(wave,"nChannels=%u\n", WInDev[wDevID].Format.wf.nChannels);
972 TRACE(wave,"nAvgBytesPerSec=%lu\n", WInDev[wDevID].Format.wf.nAvgBytesPerSec);
974 switch(lpTime->wType) {
976 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
977 TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
980 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
981 WInDev[wDevID].Format.wBitsPerSample;
982 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
985 time = WInDev[wDevID].dwTotalRecorded /
986 (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
987 lpTime->u.smpte.hour = time / 108000;
988 time -= lpTime->u.smpte.hour * 108000;
989 lpTime->u.smpte.min = time / 1800;
990 time -= lpTime->u.smpte.min * 1800;
991 lpTime->u.smpte.sec = time / 30;
992 time -= lpTime->u.smpte.sec * 30;
993 lpTime->u.smpte.frame = time;
994 lpTime->u.smpte.fps = 30;
995 TRACE(wave,"TIME_SMPTE=%02u:%02u:%02u:%02u\n",
996 lpTime->u.smpte.hour, lpTime->u.smpte.min,
997 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1000 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1001 (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
1002 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
1005 FIXME(wave, "format not supported ! use TIME_MS !\n");
1006 lpTime->wType = TIME_MS;
1008 return MMSYSERR_NOERROR;
1011 /**************************************************************************
1012 * widMessage [sample driver]
1014 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1015 DWORD dwParam1, DWORD dwParam2)
1018 TRACE(wave,"widMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
1019 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1022 return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1024 return widClose(wDevID);
1025 case WIDM_ADDBUFFER:
1026 return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1028 return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1029 case WIDM_UNPREPARE:
1030 return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1031 case WIDM_GETDEVCAPS:
1032 return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1033 case WIDM_GETNUMDEVS:
1034 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
1035 audio = open (SOUND_DEV, O_RDONLY, 0);
1046 return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1048 return widReset(wDevID);
1050 return widStart(wDevID);
1052 return widStop(wDevID);
1054 return widStop(wDevID);
1056 WARN(wave,"unknown message !\n");
1058 return MMSYSERR_NOTSUPPORTED;
1062 /**************************************************************************
1063 * WAVE_DriverProc16 [sample driver]
1065 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg,
1066 DWORD dwParam1, DWORD dwParam2)
1068 TRACE(wave,"(%08lX, %04X, %04X, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1071 case DRV_LOAD: return 1;
1072 case DRV_FREE: return 1;
1073 case DRV_OPEN: return 1;
1074 case DRV_CLOSE: return 1;
1075 case DRV_ENABLE: return 1;
1076 case DRV_DISABLE: return 1;
1077 case DRV_QUERYCONFIGURE: return 1;
1078 case DRV_CONFIGURE: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1079 case DRV_INSTALL: return DRVCNF_RESTART;
1080 case DRV_REMOVE: return DRVCNF_RESTART;
1082 FIXME(wave, "is probably wrong msg=0x%04x\n", wMsg);
1083 return DefDriverProc16(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1085 return MMSYSERR_NOTENABLED;
1088 /**************************************************************************
1089 * WAVE_DriverProc32 [sample driver]
1091 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1092 DWORD dwParam1, DWORD dwParam2)
1094 TRACE(wave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1096 case DRV_LOAD: return 1;
1097 case DRV_FREE: return 1;
1098 case DRV_OPEN: return 1;
1099 case DRV_CLOSE: return 1;
1100 case DRV_ENABLE: return 1;
1101 case DRV_DISABLE: return 1;
1102 case DRV_QUERYCONFIGURE: return 1;
1103 case DRV_CONFIGURE: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1104 case DRV_INSTALL: return DRVCNF_RESTART;
1105 case DRV_REMOVE: return DRVCNF_RESTART;
1107 FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg);
1108 return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1110 return MMSYSERR_NOTENABLED;
1113 #else /* !HAVE_OSS */
1115 /**************************************************************************
1116 * wodMessage [sample driver]
1118 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1119 DWORD dwParam1, DWORD dwParam2)
1121 FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1122 return MMSYSERR_NOTENABLED;
1125 /**************************************************************************
1126 * widMessage [sample driver]
1128 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1129 DWORD dwParam1, DWORD dwParam2)
1131 FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1132 return MMSYSERR_NOTENABLED;
1135 /**************************************************************************
1136 * WAVE_DriverProc16 [sample driver]
1138 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg,
1139 DWORD dwParam1, DWORD dwParam2)
1141 return MMSYSERR_NOTENABLED;
1144 /**************************************************************************
1145 * WAVE_DriverProc32 [sample driver]
1147 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1148 DWORD dwParam1, DWORD dwParam2)
1150 return MMSYSERR_NOTENABLED;
1152 #endif /* HAVE_OSS */