Moved MCI part to mciwave.c ; added some error checking ; added
[wine] / multimedia / audio.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*                                 
3  * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
4  *
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)
11  */
12
13 #define EMULATE_SB16
14
15 #define DEBUG_MCIWAVE
16
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/ioctl.h>
22 #include "windows.h"
23 #include "user.h"
24 #include "driver.h"
25 #include "mmsystem.h"
26 #include "heap.h"
27 #include "ldt.h"
28 #include "debug.h"
29
30 #ifdef HAVE_OSS
31
32 #ifdef HAVE_MACHINE_SOUNDCARD_H
33 # include <machine/soundcard.h>
34 #endif
35 #ifdef HAVE_SYS_SOUNDCARD_H
36 # include <sys/soundcard.h>
37 #endif
38
39 #define SOUND_DEV "/dev/dsp"
40 #define MIXER_DEV "/dev/mixer"
41
42 #ifdef SOUND_VERSION
43 #define IOCTL(a,b,c)            ((-1==ioctl(a,b,&c))&&(perror("ioctl:"#b":"#c),0))
44 #else
45 #define IOCTL(a,b,c)            (c = ioctl(a,b,c) )
46 #endif
47
48 #define MAX_WAVEOUTDRV  (1)
49 #define MAX_WAVEINDRV   (1)
50
51 typedef struct {
52     int                         unixdev;
53     int                         state;
54     DWORD                       bufsize;
55     WAVEOPENDESC                waveDesc;
56     WORD                        wFlags;
57     PCMWAVEFORMAT               Format;
58     LPWAVEHDR                   lpQueueHdr;
59     DWORD                       dwTotalPlayed;
60 } WINE_WAVEOUT;
61
62 typedef struct {
63     int                         unixdev;
64     int                         state;
65     DWORD                       bufsize;        /* OpenSound '/dev/dsp' give us that size */
66     WAVEOPENDESC                waveDesc;
67     WORD                        wFlags;
68     PCMWAVEFORMAT               Format;
69     LPWAVEHDR                   lpQueueHdr;
70     DWORD                       dwTotalRecorded;
71 } WINE_WAVEIN;
72
73 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
74 static WINE_WAVEIN      WInDev    [MAX_WAVEOUTDRV];
75
76 /*======================================================================*
77  *                  Low level WAVE implemantation                       *
78  *======================================================================*/
79
80 /**************************************************************************
81  *                      WAVE_NotifyClient                       [internal]
82  */
83 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg, 
84                                DWORD dwParam1, DWORD dwParam2)
85 {
86     TRACE(wave,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
87     
88     switch (wMsg) {
89     case WOM_OPEN:
90     case WOM_CLOSE:
91     case WOM_DONE:
92         if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
93         
94         if (WOutDev[wDevID].wFlags != DCB_NULL && 
95             !DriverCallback(
96                                                                   WOutDev[wDevID].waveDesc.dwCallBack, 
97                                                                   WOutDev[wDevID].wFlags, 
98                                                                   WOutDev[wDevID].waveDesc.hWave, 
99                                                                   wMsg, 
100                                                                   WOutDev[wDevID].waveDesc.dwInstance, 
101                                                                   dwParam1, 
102                                                                   dwParam2)) {
103             WARN(wave, "can't notify client !\n");
104             return MMSYSERR_NOERROR;
105         }
106         break;
107         
108     case WIM_OPEN:
109     case WIM_CLOSE:
110     case WIM_DATA:
111         if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
112         
113         if (WInDev[wDevID].wFlags != DCB_NULL && 
114             !DriverCallback(
115                             WInDev[wDevID].waveDesc.dwCallBack, 
116                             WInDev[wDevID].wFlags, 
117                             WInDev[wDevID].waveDesc.hWave, 
118                             wMsg, 
119                             WInDev[wDevID].waveDesc.dwInstance, 
120                             dwParam1, 
121                             dwParam2)) {
122             WARN(wave, "can't notify client !\n");
123             return MMSYSERR_NOERROR;
124         }
125         break;
126     }
127     return 0;
128 }
129
130 /**************************************************************************
131  *                      wodGetDevCaps                           [internal]
132  */
133 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
134 {
135     int         audio;
136     int         smplrate;
137     int         samplesize = 16;
138     int         dsp_stereo = 1;
139     int         bytespersmpl;
140     
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 ;
146 #ifdef EMULATE_SB16
147     lpCaps->wMid = 0x0002;
148     lpCaps->wPid = 0x0104;
149     strcpy(lpCaps->szPname, "SB16 Wave Out");
150 #else
151     lpCaps->wMid = 0x00FF;      /* Manufac ID */
152     lpCaps->wPid = 0x0001;      /* Product ID */
153     strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");
154 #endif
155     lpCaps->vDriverVersion = 0x0100;
156     lpCaps->dwFormats = 0x00000000;
157     lpCaps->dwSupport = WAVECAPS_VOLUME;
158     
159     /* First bytespersampl, then stereo */
160     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
161     
162     lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
163     if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
164     
165     smplrate = 44100;
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;
174         }
175     }
176     smplrate = 22050;
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;
185         }
186     }
187     smplrate = 11025;
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;
196         }
197     }
198     close(audio);
199     TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
200     return MMSYSERR_NOERROR;
201 }
202
203
204 /**************************************************************************
205  *                              wodOpen                         [internal]
206  */
207 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
208 {
209     int                 audio,abuf_size,smplrate,samplesize,dsp_stereo;
210     LPWAVEFORMAT        lpFormat;
211     
212     TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
213     if (lpDesc == NULL) {
214         WARN(wave, "Invalid Parameter !\n");
215         return MMSYSERR_INVALPARAM;
216     }
217     if (wDevID >= MAX_WAVEOUTDRV) {
218         TRACE(wave,"MAX_WAVOUTDRV reached !\n");
219         return MMSYSERR_ALLOCATED;
220     }
221     WOutDev[wDevID].unixdev = 0;
222     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
223     audio = open (SOUND_DEV, O_WRONLY, 0);
224     if (audio == -1) {
225         WARN(wave, "can't open !\n");
226         return MMSYSERR_ALLOCATED ;
227     }
228     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
229     if (abuf_size < 1024 || abuf_size > 65536) {
230         if (abuf_size == -1)
231             WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
232         else
233             WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
234         return MMSYSERR_NOTENABLED;
235     }
236     WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
237     switch(WOutDev[wDevID].wFlags) {
238     case DCB_NULL:
239         TRACE(wave, "CALLBACK_NULL !\n");
240         break;
241     case DCB_WINDOW:
242         TRACE(wave, "CALLBACK_WINDOW !\n");
243         break;
244     case DCB_TASK:
245         TRACE(wave, "CALLBACK_TASK !\n");
246         break;
247     case DCB_FUNCTION:
248         TRACE(wave, "CALLBACK_FUNCTION !\n");
249         break;
250     }
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;
265     }
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;
275     }
276     samplesize = WOutDev[wDevID].Format.wBitsPerSample;
277     smplrate = WOutDev[wDevID].Format.wf.nSamplesPerSec;
278     dsp_stereo = (WOutDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
279     
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);
284     
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;
292     }
293     return MMSYSERR_NOERROR;
294 }
295
296 /**************************************************************************
297  *                              wodClose                        [internal]
298  */
299 static DWORD wodClose(WORD wDevID)
300 {
301     TRACE(wave,"(%u);\n", wDevID);
302
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;
307     }
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; */
312     }
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;
320     }
321     return MMSYSERR_NOERROR;
322 }
323
324 /**************************************************************************
325  *                              wodWrite                        [internal]
326  * FIXME: this should _APPEND_ the lpWaveHdr to the output queue of the
327  * device, and initiate async playing.
328  */
329 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
330 {
331     int         count;
332     LPSTR               lpData;
333     LPWAVEHDR   xwavehdr;
334     
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;
339     }
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;
353     }
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;
366         }
367         SEGPTR_FREE(xwavehdr);
368     } else {
369         if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)lpWaveHdr, count) != MMSYSERR_NOERROR) {
370             WARN(wave, "can't notify client !\n");
371             return MMSYSERR_INVALPARAM;
372         }
373     }
374     return MMSYSERR_NOERROR;
375 }
376
377 /**************************************************************************
378  *                              wodPrepare                      [internal]
379  */
380 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
381 {
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;
386     }
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;
394 }
395
396 /**************************************************************************
397  *                              wodUnprepare                    [internal]
398  */
399 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
400 {
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;
405     }
406     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
407         return WAVERR_STILLPLAYING;
408     
409     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
410     lpWaveHdr->dwFlags |= WHDR_DONE;
411     TRACE(wave, "all headers unprepared !\n");
412     return MMSYSERR_NOERROR;
413 }
414
415 /**************************************************************************
416  *                      wodRestart                              [internal]
417  */
418 static DWORD wodRestart(WORD wDevID)
419 {
420     TRACE(wave,"(%u);\n", wDevID);
421     if (WOutDev[wDevID].unixdev == 0) {
422         WARN(wave, "can't restart !\n");
423         return MMSYSERR_NOTENABLED;
424     }
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;
430        }
431     */
432     
433     return MMSYSERR_NOERROR;
434 }
435
436 /**************************************************************************
437  *                      wodReset                                [internal]
438  */
439 static DWORD wodReset(WORD wDevID)
440 {
441     TRACE(wave,"(%u);\n", wDevID);
442     if (WOutDev[wDevID].unixdev == 0) {
443         WARN(wave, "can't reset !\n");
444         return MMSYSERR_NOTENABLED;
445     }
446     return MMSYSERR_NOERROR;
447 }
448
449
450 /**************************************************************************
451  *                              wodGetPosition                  [internal]
452  */
453 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
454 {
455     int         time;
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;
460     }
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) {
468     case TIME_BYTES:
469         lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
470         TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
471         break;
472     case TIME_SAMPLES:
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);
478         break;
479     case TIME_SMPTE:
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);
493         break;
494     default:
495         FIXME(wave, "wodGetPosition() format %d not supported ! use TIME_MS !\n",lpTime->wType);
496         lpTime->wType = TIME_MS;
497     case 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);
501         break;
502     }
503     return MMSYSERR_NOERROR;
504 }
505
506 /**************************************************************************
507  *                              wodGetVolume                    [internal]
508  */
509 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
510 {
511     int         mixer;
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;
518     }
519     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
520         WARN(wave, "unable read mixer !\n");
521         return MMSYSERR_NOTENABLED;
522     }
523     close(mixer);
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;
529 }
530
531
532 /**************************************************************************
533  *                              wodSetVolume                    [internal]
534  */
535 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
536 {
537     int         mixer;
538     int         volume;
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;
545     }
546     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
547         WARN(wave, "unable set mixer !\n");
548         return MMSYSERR_NOTENABLED;
549     }
550     close(mixer);
551     return MMSYSERR_NOERROR;
552 }
553
554 /**************************************************************************
555  *                              wodMessage                      [sample driver]
556  */
557 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
558                         DWORD dwParam1, DWORD dwParam2)
559 {
560     int audio;
561     TRACE(wave,"wodMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
562           wDevID, wMsg, dwUser, dwParam1, dwParam2);
563     switch(wMsg) {
564     case WODM_OPEN:
565         return wodOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
566     case WODM_CLOSE:
567         return wodClose(wDevID);
568     case WODM_WRITE:
569         return wodWrite(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
570     case WODM_PAUSE:
571         return MMSYSERR_NOTSUPPORTED;
572     case WODM_STOP:
573         return MMSYSERR_NOTSUPPORTED;
574     case WODM_GETPOS:
575         return wodGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
576     case WODM_BREAKLOOP:
577         return MMSYSERR_NOTSUPPORTED;
578     case WODM_PREPARE:
579         return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
580     case WODM_UNPREPARE:
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);
587         if (audio == -1)
588         {
589             if (errno == EBUSY)
590                 return 1;
591             else
592                 return 0;
593         }
594         close (audio);
595         return 1;
596     case WODM_GETPITCH:
597         return MMSYSERR_NOTSUPPORTED;
598     case WODM_SETPITCH:
599         return MMSYSERR_NOTSUPPORTED;
600     case WODM_GETPLAYBACKRATE:
601         return MMSYSERR_NOTSUPPORTED;
602     case WODM_SETPLAYBACKRATE:
603         return MMSYSERR_NOTSUPPORTED;
604     case WODM_GETVOLUME:
605         return wodGetVolume(wDevID, (LPDWORD)dwParam1);
606     case WODM_SETVOLUME:
607         return wodSetVolume(wDevID, dwParam1);
608     case WODM_RESTART:
609         return wodRestart(wDevID);
610     case WODM_RESET:
611         return wodReset(wDevID);
612     default:
613         WARN(wave,"unknown message !\n");
614     }
615     return MMSYSERR_NOTSUPPORTED;
616 }
617
618
619 /*-----------------------------------------------------------------------*/
620
621 /**************************************************************************
622  *                      widGetDevCaps                           [internal]
623  */
624 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
625 {
626     int         audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
627     
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 ;
633 #ifdef EMULATE_SB16
634     lpCaps->wMid = 0x0002;
635     lpCaps->wPid = 0x0004;
636     strcpy(lpCaps->szPname, "SB16 Wave In");
637 #else
638     lpCaps->wMid = 0x00FF;      /* Manufac ID */
639     lpCaps->wPid = 0x0001;      /* Product ID */
640     strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
641 #endif
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;
645     smplrate = 44100;
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;
654         }
655     }
656     smplrate = 22050;
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;
665         }
666     }
667     smplrate = 11025;
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;
676         }
677     }
678     close(audio);
679     TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
680     return MMSYSERR_NOERROR;
681 }
682
683
684 /**************************************************************************
685  *                              widOpen                         [internal]
686  */
687 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
688 {
689     int                 audio,abuf_size,smplrate,samplesize,dsp_stereo;
690     LPWAVEFORMAT        lpFormat;
691     
692     TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
693     if (lpDesc == NULL) {
694         WARN(wave, "Invalid Parameter !\n");
695         return MMSYSERR_INVALPARAM;
696     }
697     if (wDevID >= MAX_WAVEINDRV) {
698         TRACE(wave,"MAX_WAVINDRV reached !\n");
699         return MMSYSERR_ALLOCATED;
700     }
701     WInDev[wDevID].unixdev = 0;
702     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
703     audio = open (SOUND_DEV, O_RDONLY, 0);
704     if (audio == -1) {
705         WARN(wave,"can't open !\n");
706         return MMSYSERR_ALLOCATED;
707     }
708     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
709     if (abuf_size < 1024 || abuf_size > 65536) {
710         if (abuf_size == -1)
711             WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
712         else
713             WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
714         return MMSYSERR_NOTENABLED;
715     }
716     WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
717     switch(WInDev[wDevID].wFlags) {
718     case DCB_NULL:
719         TRACE(wave,"CALLBACK_NULL!\n");
720         break;
721     case DCB_WINDOW:
722         TRACE(wave,"CALLBACK_WINDOW!\n");
723         break;
724     case DCB_TASK:
725         TRACE(wave,"CALLBACK_TASK!\n");
726         break;
727     case DCB_FUNCTION:
728         TRACE(wave,"CALLBACK_FUNCTION!\n");
729         break;
730     }
731     if (WInDev[wDevID].lpQueueHdr) {
732         HeapFree(GetProcessHeap(),0,WInDev[wDevID].lpQueueHdr);
733         WInDev[wDevID].lpQueueHdr = NULL;
734     }
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;
744     }
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;
754     }
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;
768     }
769     return MMSYSERR_NOERROR;
770 }
771
772 /**************************************************************************
773  *                              widClose                        [internal]
774  */
775 static DWORD widClose(WORD wDevID)
776 {
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;
782     }
783     if (WInDev[wDevID].lpQueueHdr != NULL) {
784         WARN(wave, "still buffers open !\n");
785         return WAVERR_STILLPLAYING;
786     }
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;
793     }
794     return MMSYSERR_NOERROR;
795 }
796
797 /**************************************************************************
798  *                              widAddBuffer            [internal]
799  */
800 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
801 {
802     int         count   = 1;
803     LPWAVEHDR   lpWIHdr;
804     
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;
809     }
810     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
811         TRACE(wave, "never been prepared !\n");
812         return WAVERR_UNPREPARED;
813     }
814     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
815         TRACE(wave,     "header already in use !\n");
816         return WAVERR_STILLPLAYING;
817     }
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;
824     } else {
825         lpWIHdr = WInDev[wDevID].lpQueueHdr;
826         while (lpWIHdr->lpNext != NULL) {
827             lpWIHdr = lpWIHdr->lpNext;
828             count++;
829         }
830         lpWIHdr->lpNext = lpWaveHdr;
831         lpWaveHdr->lpNext = NULL;
832         count++;
833     }
834     TRACE(wave, "buffer added ! (now %u in queue)\n", count);
835     return MMSYSERR_NOERROR;
836 }
837
838 /**************************************************************************
839  *                              widPrepare                      [internal]
840  */
841 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
842 {
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;
847     }
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;
856 }
857
858 /**************************************************************************
859  *                              widUnprepare                    [internal]
860  */
861 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
862 {
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;
867     }
868     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
869     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
870     lpWaveHdr->dwFlags |= WHDR_DONE;
871     
872     TRACE(wave, "all headers unprepared !\n");
873     return MMSYSERR_NOERROR;
874 }
875
876 /**************************************************************************
877  *                      widStart                                [internal]
878  */
879 static DWORD widStart(WORD wDevID)
880 {
881     int         count   = 1;
882     int            bytesRead;
883     LPWAVEHDR   lpWIHdr;
884     LPWAVEHDR   *lpWaveHdr;
885     
886     TRACE(wave,"(%u);\n", wDevID);
887     if (WInDev[wDevID].unixdev == 0) {
888         WARN(wave, "can't start recording !\n");
889         return MMSYSERR_NOTENABLED;
890     }
891     
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;
897     }
898     
899     while(*lpWaveHdr != NULL) {
900         lpWIHdr = *lpWaveHdr;
901         TRACE(wave, "recording buf#%u=%p size=%lu \n",
902               count, lpWIHdr->lpData, lpWIHdr->dwBufferLength);
903         fflush(stddeb);
904         bytesRead = read (WInDev[wDevID].unixdev, 
905                           lpWIHdr->lpData,
906                           lpWIHdr->dwBufferLength);
907         if (bytesRead==-1)
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;
914         
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;
919         }
920         /* removes the current block from the queue */
921         *lpWaveHdr = lpWIHdr->lpNext;
922         count++;
923     }
924     TRACE(wave,"end of recording !\n");
925     fflush(stddeb);
926     return MMSYSERR_NOERROR;
927 }
928
929 /**************************************************************************
930  *                      widStop                                 [internal]
931  */
932 static DWORD widStop(WORD wDevID)
933 {
934     TRACE(wave,"(%u);\n", wDevID);
935     if (WInDev[wDevID].unixdev == 0) {
936         WARN(wave,"can't stop !\n");
937         return MMSYSERR_NOTENABLED;
938     }
939     return MMSYSERR_NOERROR;
940 }
941
942 /**************************************************************************
943  *                      widReset                                [internal]
944  */
945 static DWORD widReset(WORD wDevID)
946 {
947     TRACE(wave,"(%u);\n", wDevID);
948     if (WInDev[wDevID].unixdev == 0) {
949         WARN(wave,"can't reset !\n");
950         return MMSYSERR_NOTENABLED;
951     }
952     return MMSYSERR_NOERROR;
953 }
954
955 /**************************************************************************
956  *                              widGetPosition                  [internal]
957  */
958 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
959 {
960     int         time;
961     
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;
966     }
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); 
973     fflush(stddeb);
974     switch(lpTime->wType) {
975     case TIME_BYTES:
976         lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
977         TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
978         break;
979     case TIME_SAMPLES:
980         lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
981             WInDev[wDevID].Format.wBitsPerSample;
982         TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
983         break;
984     case TIME_SMPTE:
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);
998         break;
999     case TIME_MS:
1000         lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1001             (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
1002         TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
1003         break;
1004     default:
1005         FIXME(wave, "format not supported ! use TIME_MS !\n");
1006         lpTime->wType = TIME_MS;
1007     }
1008     return MMSYSERR_NOERROR;
1009 }
1010
1011 /**************************************************************************
1012  *                              widMessage                      [sample driver]
1013  */
1014 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1015                         DWORD dwParam1, DWORD dwParam2)
1016 {
1017     int audio;
1018     TRACE(wave,"widMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
1019           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1020     switch(wMsg) {
1021     case WIDM_OPEN:
1022         return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1023     case WIDM_CLOSE:
1024         return widClose(wDevID);
1025     case WIDM_ADDBUFFER:
1026         return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1027     case WIDM_PREPARE:
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);
1036         if (audio == -1)
1037         {
1038             if (errno == EBUSY)
1039                 return 1;
1040             else
1041                 return 0;
1042         }
1043         close (audio);
1044         return 1;
1045     case WIDM_GETPOS:
1046         return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1047     case WIDM_RESET:
1048         return widReset(wDevID);
1049     case WIDM_START:
1050         return widStart(wDevID);
1051     case WIDM_PAUSE:
1052         return widStop(wDevID);
1053     case WIDM_STOP:
1054         return widStop(wDevID);
1055     default:
1056         WARN(wave,"unknown message !\n");
1057     }
1058     return MMSYSERR_NOTSUPPORTED;
1059 }
1060
1061
1062 /**************************************************************************
1063  *                              WAVE_DriverProc16               [sample driver]
1064  */
1065 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
1066                        DWORD dwParam1, DWORD dwParam2)
1067 {
1068     TRACE(wave,"(%08lX, %04X, %04X, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1069
1070     switch(wMsg) {
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;
1081     default:
1082         FIXME(wave, "is probably wrong msg=0x%04x\n", wMsg);
1083         return DefDriverProc16(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1084     }
1085     return MMSYSERR_NOTENABLED;
1086 }
1087
1088 /**************************************************************************
1089  *                              WAVE_DriverProc32               [sample driver]
1090  */
1091 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, 
1092                        DWORD dwParam1, DWORD dwParam2)
1093 {
1094     TRACE(wave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1095     switch(wMsg) {
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;
1106     default:
1107         FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg);
1108         return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1109     }
1110     return MMSYSERR_NOTENABLED;
1111 }
1112
1113 #else /* !HAVE_OSS */
1114
1115 /**************************************************************************
1116  *                              wodMessage                      [sample driver]
1117  */
1118 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1119                         DWORD dwParam1, DWORD dwParam2)
1120 {
1121     FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1122     return MMSYSERR_NOTENABLED;
1123 }
1124
1125 /**************************************************************************
1126  *                              widMessage                      [sample driver]
1127  */
1128 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1129                         DWORD dwParam1, DWORD dwParam2)
1130 {
1131     FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1132     return MMSYSERR_NOTENABLED;
1133 }
1134
1135 /**************************************************************************
1136  *                              WAVE_DriverProc16               [sample driver]
1137  */
1138 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
1139                        DWORD dwParam1, DWORD dwParam2)
1140 {
1141     return MMSYSERR_NOTENABLED;
1142 }
1143
1144 /**************************************************************************
1145  *                              WAVE_DriverProc32               [sample driver]
1146  */
1147 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, 
1148                        DWORD dwParam1, DWORD dwParam2)
1149 {
1150     return MMSYSERR_NOTENABLED;
1151 }
1152 #endif /* HAVE_OSS */