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>
22 #include "wine/winuser16.h"
25 #include "multimedia.h"
32 #define SOUND_DEV "/dev/dsp"
33 #define MIXER_DEV "/dev/mixer"
36 #define IOCTL(a,b,c) ((-1==ioctl(a,b,&c))&&(perror("ioctl:"#b":"#c),0))
38 #define IOCTL(a,b,c) (c = ioctl(a,b,c) )
41 #define MAX_WAVEOUTDRV (1)
42 #define MAX_WAVEINDRV (1)
48 WAVEOPENDESC waveDesc;
58 DWORD bufsize; /* OpenSound '/dev/dsp' give us that size */
59 WAVEOPENDESC waveDesc;
63 DWORD dwTotalRecorded;
66 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
67 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
69 /*======================================================================*
70 * Low level WAVE implemantation *
71 *======================================================================*/
73 /**************************************************************************
74 * WAVE_NotifyClient [internal]
76 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg,
77 DWORD dwParam1, DWORD dwParam2)
79 TRACE(wave,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
85 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
87 if (WOutDev[wDevID].wFlags != DCB_NULL &&
89 WOutDev[wDevID].waveDesc.dwCallBack,
90 WOutDev[wDevID].wFlags,
91 WOutDev[wDevID].waveDesc.hWave,
93 WOutDev[wDevID].waveDesc.dwInstance,
96 WARN(wave, "can't notify client !\n");
97 return MMSYSERR_NOERROR;
104 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
106 if (WInDev[wDevID].wFlags != DCB_NULL &&
108 WInDev[wDevID].waveDesc.dwCallBack,
109 WInDev[wDevID].wFlags,
110 WInDev[wDevID].waveDesc.hWave,
112 WInDev[wDevID].waveDesc.dwInstance,
115 WARN(wave, "can't notify client !\n");
116 return MMSYSERR_NOERROR;
123 /**************************************************************************
124 * wodGetDevCaps [internal]
126 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
134 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
135 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
136 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
137 audio = open (SOUND_DEV, O_WRONLY, 0);
138 if (audio == -1) return MMSYSERR_ALLOCATED ;
140 lpCaps->wMid = 0x0002;
141 lpCaps->wPid = 0x0104;
142 strcpy(lpCaps->szPname, "SB16 Wave Out");
144 lpCaps->wMid = 0x00FF; /* Manufac ID */
145 lpCaps->wPid = 0x0001; /* Product ID */
146 strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");
148 lpCaps->vDriverVersion = 0x0100;
149 lpCaps->dwFormats = 0x00000000;
150 lpCaps->dwSupport = WAVECAPS_VOLUME;
152 /* First bytespersampl, then stereo */
153 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
155 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
156 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
159 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
160 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
161 if (lpCaps->wChannels > 1)
162 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
163 if (bytespersmpl > 1) {
164 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
165 if (lpCaps->wChannels > 1)
166 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
170 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
171 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
172 if (lpCaps->wChannels > 1)
173 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
174 if (bytespersmpl > 1) {
175 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
176 if (lpCaps->wChannels > 1)
177 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
181 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
182 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
183 if (lpCaps->wChannels > 1)
184 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
185 if (bytespersmpl > 1) {
186 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
187 if (lpCaps->wChannels > 1)
188 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
192 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
193 return MMSYSERR_NOERROR;
197 /**************************************************************************
200 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
202 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
203 LPWAVEFORMAT lpFormat;
205 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
206 if (lpDesc == NULL) {
207 WARN(wave, "Invalid Parameter !\n");
208 return MMSYSERR_INVALPARAM;
210 if (wDevID >= MAX_WAVEOUTDRV) {
211 TRACE(wave,"MAX_WAVOUTDRV reached !\n");
212 return MMSYSERR_ALLOCATED;
214 WOutDev[wDevID].unixdev = 0;
215 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
216 audio = open (SOUND_DEV, O_WRONLY, 0);
218 WARN(wave, "can't open !\n");
219 return MMSYSERR_ALLOCATED ;
221 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
222 if (abuf_size < 1024 || abuf_size > 65536) {
224 WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
226 WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
227 return MMSYSERR_NOTENABLED;
229 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
230 switch(WOutDev[wDevID].wFlags) {
232 TRACE(wave, "CALLBACK_NULL !\n");
235 TRACE(wave, "CALLBACK_WINDOW !\n");
238 TRACE(wave, "CALLBACK_TASK !\n");
241 TRACE(wave, "CALLBACK_FUNCTION !\n");
244 WOutDev[wDevID].lpQueueHdr = NULL;
245 WOutDev[wDevID].unixdev = audio;
246 WOutDev[wDevID].dwTotalPlayed = 0;
247 WOutDev[wDevID].bufsize = abuf_size;
248 /* FIXME: copy lpFormat too? */
249 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
250 TRACE(wave,"lpDesc->lpFormat = %p\n",lpDesc->lpFormat);
251 lpFormat = lpDesc->lpFormat;
252 TRACE(wave,"lpFormat = %p\n",lpFormat);
253 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
254 WARN(wave,"Bad format %04X !\n", lpFormat->wFormatTag);
255 WARN(wave,"Bad nChannels %d !\n", lpFormat->nChannels);
256 WARN(wave,"Bad nSamplesPerSec %ld !\n", lpFormat->nSamplesPerSec);
257 return WAVERR_BADFORMAT;
259 memcpy(&WOutDev[wDevID].Format, lpFormat, sizeof(PCMWAVEFORMAT));
260 if (WOutDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT;
261 if (WOutDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
262 TRACE(wave,"wBitsPerSample=%u !\n", WOutDev[wDevID].Format.wBitsPerSample);
263 if (WOutDev[wDevID].Format.wBitsPerSample == 0) {
264 WOutDev[wDevID].Format.wBitsPerSample = 8 *
265 (WOutDev[wDevID].Format.wf.nAvgBytesPerSec /
266 WOutDev[wDevID].Format.wf.nSamplesPerSec) /
267 WOutDev[wDevID].Format.wf.nChannels;
269 samplesize = WOutDev[wDevID].Format.wBitsPerSample;
270 smplrate = WOutDev[wDevID].Format.wf.nSamplesPerSec;
271 dsp_stereo = (WOutDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
273 /* First size and stereo then samplerate */
274 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
275 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
276 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
278 TRACE(wave,"wBitsPerSample=%u !\n", WOutDev[wDevID].Format.wBitsPerSample);
279 TRACE(wave,"nAvgBytesPerSec=%lu !\n", WOutDev[wDevID].Format.wf.nAvgBytesPerSec);
280 TRACE(wave,"nSamplesPerSec=%lu !\n", WOutDev[wDevID].Format.wf.nSamplesPerSec);
281 TRACE(wave,"nChannels=%u !\n", WOutDev[wDevID].Format.wf.nChannels);
282 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
283 WARN(wave, "can't notify client !\n");
284 return MMSYSERR_INVALPARAM;
286 return MMSYSERR_NOERROR;
289 /**************************************************************************
290 * wodClose [internal]
292 static DWORD wodClose(WORD wDevID)
294 TRACE(wave,"(%u);\n", wDevID);
296 if (wDevID > MAX_WAVEOUTDRV) return MMSYSERR_INVALPARAM;
297 if (WOutDev[wDevID].unixdev == 0) {
298 WARN(wave, "can't close !\n");
299 return MMSYSERR_NOTENABLED;
301 if (WOutDev[wDevID].lpQueueHdr != NULL) {
302 WARN(wave, "still buffers open !\n");
303 /* Don't care. Who needs those buffers anyway */
304 /*return WAVERR_STILLPLAYING; */
306 close(WOutDev[wDevID].unixdev);
307 WOutDev[wDevID].unixdev = 0;
308 WOutDev[wDevID].bufsize = 0;
309 WOutDev[wDevID].lpQueueHdr = NULL;
310 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
311 WARN(wave, "can't notify client !\n");
312 return MMSYSERR_INVALPARAM;
314 return MMSYSERR_NOERROR;
317 /**************************************************************************
318 * wodWrite [internal]
319 * FIXME: this should _APPEND_ the lpWaveHdr to the output queue of the
320 * device, and initiate async playing.
322 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
328 TRACE(wave,"(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
329 if (WOutDev[wDevID].unixdev == 0) {
330 WARN(wave, "can't play !\n");
331 return MMSYSERR_NOTENABLED;
333 if (lpWaveHdr->lpData == NULL) return WAVERR_UNPREPARED;
334 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) return WAVERR_UNPREPARED;
335 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) return WAVERR_STILLPLAYING;
336 lpWaveHdr->dwFlags &= ~WHDR_DONE;
337 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
338 TRACE(wave, "dwBufferLength %lu !\n", lpWaveHdr->dwBufferLength);
339 TRACE(wave, "WOutDev[%u].unixdev %u !\n", wDevID, WOutDev[wDevID].unixdev);
340 lpData = lpWaveHdr->lpData;
341 count = write (WOutDev[wDevID].unixdev, lpData, lpWaveHdr->dwBufferLength);
342 TRACE(wave,"write returned count %u !\n",count);
343 if (count != lpWaveHdr->dwBufferLength) {
344 WARN(wave, " error writing !\n");
345 return MMSYSERR_NOTENABLED;
347 WOutDev[wDevID].dwTotalPlayed += count;
348 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
349 lpWaveHdr->dwFlags |= WHDR_DONE;
350 if ((DWORD)lpWaveHdr->lpData!=lpWaveHdr->reserved) {
351 /* FIXME: what if it expects it's OWN lpwavehdr back? */
352 xwavehdr = SEGPTR_NEW(WAVEHDR);
353 memcpy(xwavehdr,lpWaveHdr,sizeof(WAVEHDR));
354 xwavehdr->lpData = (LPBYTE)xwavehdr->reserved;
355 if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)SEGPTR_GET(xwavehdr), count) != MMSYSERR_NOERROR) {
356 WARN(wave, "can't notify client !\n");
357 SEGPTR_FREE(xwavehdr);
358 return MMSYSERR_INVALPARAM;
360 SEGPTR_FREE(xwavehdr);
362 if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)lpWaveHdr, count) != MMSYSERR_NOERROR) {
363 WARN(wave, "can't notify client !\n");
364 return MMSYSERR_INVALPARAM;
367 return MMSYSERR_NOERROR;
370 /**************************************************************************
371 * wodPrepare [internal]
373 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
375 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
376 if (WOutDev[wDevID].unixdev == 0) {
377 WARN(wave, "can't prepare !\n");
378 return MMSYSERR_NOTENABLED;
380 /* don't append to queue, wodWrite does that */
381 WOutDev[wDevID].dwTotalPlayed = 0;
382 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
383 return WAVERR_STILLPLAYING;
384 lpWaveHdr->dwFlags |= WHDR_PREPARED;
385 lpWaveHdr->dwFlags &= ~WHDR_DONE;
386 return MMSYSERR_NOERROR;
389 /**************************************************************************
390 * wodUnprepare [internal]
392 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
394 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
395 if (WOutDev[wDevID].unixdev == 0) {
396 WARN(wave, "can't unprepare !\n");
397 return MMSYSERR_NOTENABLED;
399 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
400 return WAVERR_STILLPLAYING;
402 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
403 lpWaveHdr->dwFlags |= WHDR_DONE;
404 TRACE(wave, "all headers unprepared !\n");
405 return MMSYSERR_NOERROR;
408 /**************************************************************************
409 * wodRestart [internal]
411 static DWORD wodRestart(WORD wDevID)
413 TRACE(wave,"(%u);\n", wDevID);
414 if (WOutDev[wDevID].unixdev == 0) {
415 WARN(wave, "can't restart !\n");
416 return MMSYSERR_NOTENABLED;
418 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
419 /* FIXME: Myst crashes with this ... hmm -MM
420 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
421 WARN(wave, "can't notify client !\n");
422 return MMSYSERR_INVALPARAM;
426 return MMSYSERR_NOERROR;
429 /**************************************************************************
430 * wodReset [internal]
432 static DWORD wodReset(WORD wDevID)
434 TRACE(wave,"(%u);\n", wDevID);
435 if (WOutDev[wDevID].unixdev == 0) {
436 WARN(wave, "can't reset !\n");
437 return MMSYSERR_NOTENABLED;
439 return MMSYSERR_NOERROR;
443 /**************************************************************************
444 * wodGetPosition [internal]
446 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
449 TRACE(wave,"(%u, %p, %lu);\n", wDevID, lpTime, uSize);
450 if (WOutDev[wDevID].unixdev == 0) {
451 WARN(wave, "can't get pos !\n");
452 return MMSYSERR_NOTENABLED;
454 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
455 TRACE(wave,"wType=%04X !\n", lpTime->wType);
456 TRACE(wave,"wBitsPerSample=%u\n", WOutDev[wDevID].Format.wBitsPerSample);
457 TRACE(wave,"nSamplesPerSec=%lu\n", WOutDev[wDevID].Format.wf.nSamplesPerSec);
458 TRACE(wave,"nChannels=%u\n", WOutDev[wDevID].Format.wf.nChannels);
459 TRACE(wave,"nAvgBytesPerSec=%lu\n", WOutDev[wDevID].Format.wf.nAvgBytesPerSec);
460 switch(lpTime->wType) {
462 lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
463 TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
466 TRACE(wave,"dwTotalPlayed=%lu\n", WOutDev[wDevID].dwTotalPlayed);
467 TRACE(wave,"wBitsPerSample=%u\n", WOutDev[wDevID].Format.wBitsPerSample);
468 lpTime->u.sample = WOutDev[wDevID].dwTotalPlayed * 8 /
469 WOutDev[wDevID].Format.wBitsPerSample;
470 TRACE(wave,"TIME_SAMPLES=%lu\n", lpTime->u.sample);
473 time = WOutDev[wDevID].dwTotalPlayed /
474 (WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
475 lpTime->u.smpte.hour = time / 108000;
476 time -= lpTime->u.smpte.hour * 108000;
477 lpTime->u.smpte.min = time / 1800;
478 time -= lpTime->u.smpte.min * 1800;
479 lpTime->u.smpte.sec = time / 30;
480 time -= lpTime->u.smpte.sec * 30;
481 lpTime->u.smpte.frame = time;
482 lpTime->u.smpte.fps = 30;
483 TRACE(wave, "wodGetPosition , TIME_SMPTE=%02u:%02u:%02u:%02u\n",
484 lpTime->u.smpte.hour, lpTime->u.smpte.min,
485 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
488 FIXME(wave, "wodGetPosition() format %d not supported ! use TIME_MS !\n",lpTime->wType);
489 lpTime->wType = TIME_MS;
491 lpTime->u.ms = WOutDev[wDevID].dwTotalPlayed /
492 (WOutDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
493 TRACE(wave,"wodGetPosition , TIME_MS=%lu\n", lpTime->u.ms);
496 return MMSYSERR_NOERROR;
499 /**************************************************************************
500 * wodGetVolume [internal]
502 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
505 int volume, left, right;
506 TRACE(wave,"(%u, %p);\n", wDevID, lpdwVol);
507 if (lpdwVol == NULL) return MMSYSERR_NOTENABLED;
508 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
509 WARN(wave, "mixer device not available !\n");
510 return MMSYSERR_NOTENABLED;
512 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
513 WARN(wave, "unable read mixer !\n");
514 return MMSYSERR_NOTENABLED;
517 left = volume & 0x7F;
518 right = (volume >> 8) & 0x7F;
519 TRACE(wave,"left=%d right=%d !\n", left, right);
520 *lpdwVol = MAKELONG(left << 9, right << 9);
521 return MMSYSERR_NOERROR;
525 /**************************************************************************
526 * wodSetVolume [internal]
528 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
532 TRACE(wave,"(%u, %08lX);\n", wDevID, dwParam);
533 volume = (LOWORD(dwParam) >> 9 & 0x7F) +
534 ((HIWORD(dwParam) >> 9 & 0x7F) << 8);
535 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
536 WARN(wave, "mixer device not available !\n");
537 return MMSYSERR_NOTENABLED;
539 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
540 WARN(wave, "unable set mixer !\n");
541 return MMSYSERR_NOTENABLED;
544 return MMSYSERR_NOERROR;
547 /**************************************************************************
548 * wodMessage [sample driver]
550 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
551 DWORD dwParam1, DWORD dwParam2)
554 TRACE(wave,"wodMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
555 wDevID, wMsg, dwUser, dwParam1, dwParam2);
558 return wodOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
560 return wodClose(wDevID);
562 return wodWrite(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
564 return MMSYSERR_NOTSUPPORTED;
566 return MMSYSERR_NOTSUPPORTED;
568 return wodGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
570 return MMSYSERR_NOTSUPPORTED;
572 return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
574 return wodUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
575 case WODM_GETDEVCAPS:
576 return wodGetDevCaps(wDevID,(LPWAVEOUTCAPS16)dwParam1,dwParam2);
577 case WODM_GETNUMDEVS:
578 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
579 audio = open (SOUND_DEV, O_WRONLY, 0);
590 return MMSYSERR_NOTSUPPORTED;
592 return MMSYSERR_NOTSUPPORTED;
593 case WODM_GETPLAYBACKRATE:
594 return MMSYSERR_NOTSUPPORTED;
595 case WODM_SETPLAYBACKRATE:
596 return MMSYSERR_NOTSUPPORTED;
598 return wodGetVolume(wDevID, (LPDWORD)dwParam1);
600 return wodSetVolume(wDevID, dwParam1);
602 return wodRestart(wDevID);
604 return wodReset(wDevID);
606 WARN(wave,"unknown message !\n");
608 return MMSYSERR_NOTSUPPORTED;
612 /*-----------------------------------------------------------------------*/
614 /**************************************************************************
615 * widGetDevCaps [internal]
617 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
619 int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
621 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
622 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
623 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
624 audio = open (SOUND_DEV, O_RDONLY, 0);
625 if (audio == -1) return MMSYSERR_ALLOCATED ;
627 lpCaps->wMid = 0x0002;
628 lpCaps->wPid = 0x0004;
629 strcpy(lpCaps->szPname, "SB16 Wave In");
631 lpCaps->wMid = 0x00FF; /* Manufac ID */
632 lpCaps->wPid = 0x0001; /* Product ID */
633 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
635 lpCaps->dwFormats = 0x00000000;
636 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
637 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
639 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
640 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
641 if (lpCaps->wChannels > 1)
642 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
643 if (bytespersmpl > 1) {
644 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
645 if (lpCaps->wChannels > 1)
646 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
650 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
651 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
652 if (lpCaps->wChannels > 1)
653 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
654 if (bytespersmpl > 1) {
655 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
656 if (lpCaps->wChannels > 1)
657 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
661 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
662 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
663 if (lpCaps->wChannels > 1)
664 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
665 if (bytespersmpl > 1) {
666 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
667 if (lpCaps->wChannels > 1)
668 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
672 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
673 return MMSYSERR_NOERROR;
677 /**************************************************************************
680 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
682 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
683 LPWAVEFORMAT lpFormat;
685 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
686 if (lpDesc == NULL) {
687 WARN(wave, "Invalid Parameter !\n");
688 return MMSYSERR_INVALPARAM;
690 if (wDevID >= MAX_WAVEINDRV) {
691 TRACE(wave,"MAX_WAVINDRV reached !\n");
692 return MMSYSERR_ALLOCATED;
694 WInDev[wDevID].unixdev = 0;
695 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
696 audio = open (SOUND_DEV, O_RDONLY, 0);
698 WARN(wave,"can't open !\n");
699 return MMSYSERR_ALLOCATED;
701 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
702 if (abuf_size < 1024 || abuf_size > 65536) {
704 WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
706 WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
707 return MMSYSERR_NOTENABLED;
709 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
710 switch(WInDev[wDevID].wFlags) {
712 TRACE(wave,"CALLBACK_NULL!\n");
715 TRACE(wave,"CALLBACK_WINDOW!\n");
718 TRACE(wave,"CALLBACK_TASK!\n");
721 TRACE(wave,"CALLBACK_FUNCTION!\n");
724 if (WInDev[wDevID].lpQueueHdr) {
725 HeapFree(GetProcessHeap(),0,WInDev[wDevID].lpQueueHdr);
726 WInDev[wDevID].lpQueueHdr = NULL;
728 WInDev[wDevID].unixdev = audio;
729 WInDev[wDevID].bufsize = abuf_size;
730 WInDev[wDevID].dwTotalRecorded = 0;
731 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
732 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
733 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
734 WARN(wave, "Bad format %04X !\n",
735 lpFormat->wFormatTag);
736 return WAVERR_BADFORMAT;
738 memcpy(&WInDev[wDevID].Format, lpFormat, sizeof(PCMWAVEFORMAT));
739 WInDev[wDevID].Format.wBitsPerSample = 8; /* <-------------- */
740 if (WInDev[wDevID].Format.wf.nChannels == 0) return WAVERR_BADFORMAT;
741 if (WInDev[wDevID].Format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
742 if (WInDev[wDevID].Format.wBitsPerSample == 0) {
743 WInDev[wDevID].Format.wBitsPerSample = 8 *
744 (WInDev[wDevID].Format.wf.nAvgBytesPerSec /
745 WInDev[wDevID].Format.wf.nSamplesPerSec) /
746 WInDev[wDevID].Format.wf.nChannels;
748 samplesize = WInDev[wDevID].Format.wBitsPerSample;
749 smplrate = WInDev[wDevID].Format.wf.nSamplesPerSec;
750 dsp_stereo = (WInDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
751 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
752 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
753 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
754 TRACE(wave,"wBitsPerSample=%u !\n", WInDev[wDevID].Format.wBitsPerSample);
755 TRACE(wave,"nSamplesPerSec=%lu !\n", WInDev[wDevID].Format.wf.nSamplesPerSec);
756 TRACE(wave,"nChannels=%u !\n", WInDev[wDevID].Format.wf.nChannels);
757 TRACE(wave,"nAvgBytesPerSec=%lu\n", WInDev[wDevID].Format.wf.nAvgBytesPerSec);
758 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
759 WARN(wave,"can't notify client !\n");
760 return MMSYSERR_INVALPARAM;
762 return MMSYSERR_NOERROR;
765 /**************************************************************************
766 * widClose [internal]
768 static DWORD widClose(WORD wDevID)
770 TRACE(wave,"(%u);\n", wDevID);
771 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
772 if (WInDev[wDevID].unixdev == 0) {
773 WARN(wave,"can't close !\n");
774 return MMSYSERR_NOTENABLED;
776 if (WInDev[wDevID].lpQueueHdr != NULL) {
777 WARN(wave, "still buffers open !\n");
778 return WAVERR_STILLPLAYING;
780 close(WInDev[wDevID].unixdev);
781 WInDev[wDevID].unixdev = 0;
782 WInDev[wDevID].bufsize = 0;
783 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
784 WARN(wave,"can't notify client !\n");
785 return MMSYSERR_INVALPARAM;
787 return MMSYSERR_NOERROR;
790 /**************************************************************************
791 * widAddBuffer [internal]
793 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
798 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
799 if (WInDev[wDevID].unixdev == 0) {
800 WARN(wave,"can't do it !\n");
801 return MMSYSERR_NOTENABLED;
803 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
804 TRACE(wave, "never been prepared !\n");
805 return WAVERR_UNPREPARED;
807 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
808 TRACE(wave, "header already in use !\n");
809 return WAVERR_STILLPLAYING;
811 lpWaveHdr->dwFlags |= WHDR_PREPARED;
812 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
813 lpWaveHdr->dwFlags &= ~WHDR_DONE;
814 lpWaveHdr->dwBytesRecorded = 0;
815 if (WInDev[wDevID].lpQueueHdr == NULL) {
816 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
818 lpWIHdr = WInDev[wDevID].lpQueueHdr;
819 while (lpWIHdr->lpNext != NULL) {
820 lpWIHdr = lpWIHdr->lpNext;
823 lpWIHdr->lpNext = lpWaveHdr;
824 lpWaveHdr->lpNext = NULL;
827 TRACE(wave, "buffer added ! (now %u in queue)\n", count);
828 return MMSYSERR_NOERROR;
831 /**************************************************************************
832 * widPrepare [internal]
834 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
836 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
837 if (WInDev[wDevID].unixdev == 0) {
838 WARN(wave,"can't prepare !\n");
839 return MMSYSERR_NOTENABLED;
841 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
842 return WAVERR_STILLPLAYING;
843 lpWaveHdr->dwFlags |= WHDR_PREPARED;
844 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
845 lpWaveHdr->dwFlags &= ~WHDR_DONE;
846 lpWaveHdr->dwBytesRecorded = 0;
847 TRACE(wave,"header prepared !\n");
848 return MMSYSERR_NOERROR;
851 /**************************************************************************
852 * widUnprepare [internal]
854 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
856 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
857 if (WInDev[wDevID].unixdev == 0) {
858 WARN(wave,"can't unprepare !\n");
859 return MMSYSERR_NOTENABLED;
861 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
862 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
863 lpWaveHdr->dwFlags |= WHDR_DONE;
865 TRACE(wave, "all headers unprepared !\n");
866 return MMSYSERR_NOERROR;
869 /**************************************************************************
870 * widStart [internal]
872 static DWORD widStart(WORD wDevID)
877 LPWAVEHDR *lpWaveHdr;
879 TRACE(wave,"(%u);\n", wDevID);
880 if (WInDev[wDevID].unixdev == 0) {
881 WARN(wave, "can't start recording !\n");
882 return MMSYSERR_NOTENABLED;
885 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
886 TRACE(wave,"lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
887 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
888 TRACE(wave,"never been prepared !\n");
889 return WAVERR_UNPREPARED;
892 while(*lpWaveHdr != NULL) {
893 lpWIHdr = *lpWaveHdr;
894 TRACE(wave, "recording buf#%u=%p size=%lu \n",
895 count, lpWIHdr->lpData, lpWIHdr->dwBufferLength);
897 bytesRead = read (WInDev[wDevID].unixdev,
899 lpWIHdr->dwBufferLength);
901 perror("read from audio device");
902 TRACE(wave,"bytesread=%d (%ld)\n", bytesRead, lpWIHdr->dwBufferLength);
903 lpWIHdr->dwBytesRecorded = bytesRead;
904 WInDev[wDevID].dwTotalRecorded += lpWIHdr->dwBytesRecorded;
905 lpWIHdr->dwFlags &= ~WHDR_INQUEUE;
906 lpWIHdr->dwFlags |= WHDR_DONE;
908 /* FIXME: should pass segmented pointer here, do we need that?*/
909 if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)lpWaveHdr, lpWIHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
910 WARN(wave, "can't notify client !\n");
911 return MMSYSERR_INVALPARAM;
913 /* removes the current block from the queue */
914 *lpWaveHdr = lpWIHdr->lpNext;
917 TRACE(wave,"end of recording !\n");
919 return MMSYSERR_NOERROR;
922 /**************************************************************************
925 static DWORD widStop(WORD wDevID)
927 TRACE(wave,"(%u);\n", wDevID);
928 if (WInDev[wDevID].unixdev == 0) {
929 WARN(wave,"can't stop !\n");
930 return MMSYSERR_NOTENABLED;
932 return MMSYSERR_NOERROR;
935 /**************************************************************************
936 * widReset [internal]
938 static DWORD widReset(WORD wDevID)
940 TRACE(wave,"(%u);\n", wDevID);
941 if (WInDev[wDevID].unixdev == 0) {
942 WARN(wave,"can't reset !\n");
943 return MMSYSERR_NOTENABLED;
945 return MMSYSERR_NOERROR;
948 /**************************************************************************
949 * widGetPosition [internal]
951 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
955 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize);
956 if (WInDev[wDevID].unixdev == 0) {
957 WARN(wave,"can't get pos !\n");
958 return MMSYSERR_NOTENABLED;
960 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
961 TRACE(wave,"wType=%04X !\n", lpTime->wType);
962 TRACE(wave,"wBitsPerSample=%u\n", WInDev[wDevID].Format.wBitsPerSample);
963 TRACE(wave,"nSamplesPerSec=%lu\n", WInDev[wDevID].Format.wf.nSamplesPerSec);
964 TRACE(wave,"nChannels=%u\n", WInDev[wDevID].Format.wf.nChannels);
965 TRACE(wave,"nAvgBytesPerSec=%lu\n", WInDev[wDevID].Format.wf.nAvgBytesPerSec);
967 switch(lpTime->wType) {
969 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
970 TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
973 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
974 WInDev[wDevID].Format.wBitsPerSample;
975 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
978 time = WInDev[wDevID].dwTotalRecorded /
979 (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
980 lpTime->u.smpte.hour = time / 108000;
981 time -= lpTime->u.smpte.hour * 108000;
982 lpTime->u.smpte.min = time / 1800;
983 time -= lpTime->u.smpte.min * 1800;
984 lpTime->u.smpte.sec = time / 30;
985 time -= lpTime->u.smpte.sec * 30;
986 lpTime->u.smpte.frame = time;
987 lpTime->u.smpte.fps = 30;
988 TRACE(wave,"TIME_SMPTE=%02u:%02u:%02u:%02u\n",
989 lpTime->u.smpte.hour, lpTime->u.smpte.min,
990 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
993 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
994 (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
995 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
998 FIXME(wave, "format not supported ! use TIME_MS !\n");
999 lpTime->wType = TIME_MS;
1001 return MMSYSERR_NOERROR;
1004 /**************************************************************************
1005 * widMessage [sample driver]
1007 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1008 DWORD dwParam1, DWORD dwParam2)
1011 TRACE(wave,"widMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
1012 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1015 return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1017 return widClose(wDevID);
1018 case WIDM_ADDBUFFER:
1019 return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1021 return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1022 case WIDM_UNPREPARE:
1023 return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1024 case WIDM_GETDEVCAPS:
1025 return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1026 case WIDM_GETNUMDEVS:
1027 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
1028 audio = open (SOUND_DEV, O_RDONLY, 0);
1039 return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1041 return widReset(wDevID);
1043 return widStart(wDevID);
1045 return widStop(wDevID);
1047 return widStop(wDevID);
1049 WARN(wave,"unknown message !\n");
1051 return MMSYSERR_NOTSUPPORTED;
1055 /**************************************************************************
1056 * WAVE_DriverProc16 [sample driver]
1058 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg,
1059 DWORD dwParam1, DWORD dwParam2)
1061 TRACE(wave,"(%08lX, %04X, %04X, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1064 case DRV_LOAD: return 1;
1065 case DRV_FREE: return 1;
1066 case DRV_OPEN: return 1;
1067 case DRV_CLOSE: return 1;
1068 case DRV_ENABLE: return 1;
1069 case DRV_DISABLE: return 1;
1070 case DRV_QUERYCONFIGURE: return 1;
1071 case DRV_CONFIGURE: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1072 case DRV_INSTALL: return DRVCNF_RESTART;
1073 case DRV_REMOVE: return DRVCNF_RESTART;
1075 FIXME(wave, "is probably wrong msg=0x%04x\n", wMsg);
1076 return DefDriverProc16(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1078 return MMSYSERR_NOTENABLED;
1081 /**************************************************************************
1082 * WAVE_DriverProc32 [sample driver]
1084 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1085 DWORD dwParam1, DWORD dwParam2)
1087 TRACE(wave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1089 case DRV_LOAD: return 1;
1090 case DRV_FREE: return 1;
1091 case DRV_OPEN: return 1;
1092 case DRV_CLOSE: return 1;
1093 case DRV_ENABLE: return 1;
1094 case DRV_DISABLE: return 1;
1095 case DRV_QUERYCONFIGURE: return 1;
1096 case DRV_CONFIGURE: MessageBox16(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1097 case DRV_INSTALL: return DRVCNF_RESTART;
1098 case DRV_REMOVE: return DRVCNF_RESTART;
1100 FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg);
1101 return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1103 return MMSYSERR_NOTENABLED;
1106 #else /* !HAVE_OSS */
1108 /**************************************************************************
1109 * wodMessage [sample driver]
1111 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1112 DWORD dwParam1, DWORD dwParam2)
1114 FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1115 return MMSYSERR_NOTENABLED;
1118 /**************************************************************************
1119 * widMessage [sample driver]
1121 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1122 DWORD dwParam1, DWORD dwParam2)
1124 FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1125 return MMSYSERR_NOTENABLED;
1128 /**************************************************************************
1129 * WAVE_DriverProc16 [sample driver]
1131 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg,
1132 DWORD dwParam1, DWORD dwParam2)
1134 return MMSYSERR_NOTENABLED;
1137 /**************************************************************************
1138 * WAVE_DriverProc32 [sample driver]
1140 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1141 DWORD dwParam1, DWORD dwParam2)
1143 return MMSYSERR_NOTENABLED;
1145 #endif /* HAVE_OSS */