Fixed error messages.
[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 "wine/winuser16.h"
23 #include "user.h"
24 #include "driver.h"
25 #include "multimedia.h"
26 #include "heap.h"
27 #include "ldt.h"
28 #include "debug.h"
29
30 #ifdef HAVE_OSS
31
32 #define SOUND_DEV "/dev/dsp"
33 #define MIXER_DEV "/dev/mixer"
34
35 #ifdef SOUND_VERSION
36 #define IOCTL(a,b,c)            ((-1==ioctl(a,b,&c))&&(perror("ioctl:"#b":"#c),0))
37 #else
38 #define IOCTL(a,b,c)            (c = ioctl(a,b,c) )
39 #endif
40
41 #define MAX_WAVEOUTDRV  (1)
42 #define MAX_WAVEINDRV   (1)
43
44 typedef struct {
45     int                         unixdev;
46     int                         state;
47     DWORD                       bufsize;
48     WAVEOPENDESC                waveDesc;
49     WORD                        wFlags;
50     PCMWAVEFORMAT               Format;
51     LPWAVEHDR                   lpQueueHdr;
52     DWORD                       dwTotalPlayed;
53 } WINE_WAVEOUT;
54
55 typedef struct {
56     int                         unixdev;
57     int                         state;
58     DWORD                       bufsize;        /* OpenSound '/dev/dsp' give us that size */
59     WAVEOPENDESC                waveDesc;
60     WORD                        wFlags;
61     PCMWAVEFORMAT               Format;
62     LPWAVEHDR                   lpQueueHdr;
63     DWORD                       dwTotalRecorded;
64 } WINE_WAVEIN;
65
66 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
67 static WINE_WAVEIN      WInDev    [MAX_WAVEOUTDRV];
68
69 /*======================================================================*
70  *                  Low level WAVE implemantation                       *
71  *======================================================================*/
72
73 /**************************************************************************
74  *                      WAVE_NotifyClient                       [internal]
75  */
76 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg, 
77                                DWORD dwParam1, DWORD dwParam2)
78 {
79     TRACE(wave,"wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
80     
81     switch (wMsg) {
82     case WOM_OPEN:
83     case WOM_CLOSE:
84     case WOM_DONE:
85         if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
86         
87         if (WOutDev[wDevID].wFlags != DCB_NULL && 
88             !DriverCallback(
89                                                                   WOutDev[wDevID].waveDesc.dwCallBack, 
90                                                                   WOutDev[wDevID].wFlags, 
91                                                                   WOutDev[wDevID].waveDesc.hWave, 
92                                                                   wMsg, 
93                                                                   WOutDev[wDevID].waveDesc.dwInstance, 
94                                                                   dwParam1, 
95                                                                   dwParam2)) {
96             WARN(wave, "can't notify client !\n");
97             return MMSYSERR_NOERROR;
98         }
99         break;
100         
101     case WIM_OPEN:
102     case WIM_CLOSE:
103     case WIM_DATA:
104         if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
105         
106         if (WInDev[wDevID].wFlags != DCB_NULL && 
107             !DriverCallback(
108                             WInDev[wDevID].waveDesc.dwCallBack, 
109                             WInDev[wDevID].wFlags, 
110                             WInDev[wDevID].waveDesc.hWave, 
111                             wMsg, 
112                             WInDev[wDevID].waveDesc.dwInstance, 
113                             dwParam1, 
114                             dwParam2)) {
115             WARN(wave, "can't notify client !\n");
116             return MMSYSERR_NOERROR;
117         }
118         break;
119     }
120     return 0;
121 }
122
123 /**************************************************************************
124  *                      wodGetDevCaps                           [internal]
125  */
126 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
127 {
128     int         audio;
129     int         smplrate;
130     int         samplesize = 16;
131     int         dsp_stereo = 1;
132     int         bytespersmpl;
133     
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 ;
139 #ifdef EMULATE_SB16
140     lpCaps->wMid = 0x0002;
141     lpCaps->wPid = 0x0104;
142     strcpy(lpCaps->szPname, "SB16 Wave Out");
143 #else
144     lpCaps->wMid = 0x00FF;      /* Manufac ID */
145     lpCaps->wPid = 0x0001;      /* Product ID */
146     strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");
147 #endif
148     lpCaps->vDriverVersion = 0x0100;
149     lpCaps->dwFormats = 0x00000000;
150     lpCaps->dwSupport = WAVECAPS_VOLUME;
151     
152     /* First bytespersampl, then stereo */
153     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
154     
155     lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
156     if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
157     
158     smplrate = 44100;
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;
167         }
168     }
169     smplrate = 22050;
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;
178         }
179     }
180     smplrate = 11025;
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;
189         }
190     }
191     close(audio);
192     TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
193     return MMSYSERR_NOERROR;
194 }
195
196
197 /**************************************************************************
198  *                              wodOpen                         [internal]
199  */
200 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
201 {
202     int                 audio,abuf_size,smplrate,samplesize,dsp_stereo;
203     LPWAVEFORMAT        lpFormat;
204     
205     TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
206     if (lpDesc == NULL) {
207         WARN(wave, "Invalid Parameter !\n");
208         return MMSYSERR_INVALPARAM;
209     }
210     if (wDevID >= MAX_WAVEOUTDRV) {
211         TRACE(wave,"MAX_WAVOUTDRV reached !\n");
212         return MMSYSERR_ALLOCATED;
213     }
214     WOutDev[wDevID].unixdev = 0;
215     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
216     audio = open (SOUND_DEV, O_WRONLY, 0);
217     if (audio == -1) {
218         WARN(wave, "can't open !\n");
219         return MMSYSERR_ALLOCATED ;
220     }
221     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
222     if (abuf_size < 1024 || abuf_size > 65536) {
223         if (abuf_size == -1)
224             WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
225         else
226             WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
227         return MMSYSERR_NOTENABLED;
228     }
229     WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
230     switch(WOutDev[wDevID].wFlags) {
231     case DCB_NULL:
232         TRACE(wave, "CALLBACK_NULL !\n");
233         break;
234     case DCB_WINDOW:
235         TRACE(wave, "CALLBACK_WINDOW !\n");
236         break;
237     case DCB_TASK:
238         TRACE(wave, "CALLBACK_TASK !\n");
239         break;
240     case DCB_FUNCTION:
241         TRACE(wave, "CALLBACK_FUNCTION !\n");
242         break;
243     }
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;
258     }
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;
268     }
269     samplesize = WOutDev[wDevID].Format.wBitsPerSample;
270     smplrate = WOutDev[wDevID].Format.wf.nSamplesPerSec;
271     dsp_stereo = (WOutDev[wDevID].Format.wf.nChannels > 1) ? TRUE : FALSE;
272     
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);
277     
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;
285     }
286     return MMSYSERR_NOERROR;
287 }
288
289 /**************************************************************************
290  *                              wodClose                        [internal]
291  */
292 static DWORD wodClose(WORD wDevID)
293 {
294     TRACE(wave,"(%u);\n", wDevID);
295
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;
300     }
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; */
305     }
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;
313     }
314     return MMSYSERR_NOERROR;
315 }
316
317 /**************************************************************************
318  *                              wodWrite                        [internal]
319  * FIXME: this should _APPEND_ the lpWaveHdr to the output queue of the
320  * device, and initiate async playing.
321  */
322 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
323 {
324     int         count;
325     LPSTR               lpData;
326     LPWAVEHDR   xwavehdr;
327     
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;
332     }
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;
346     }
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;
359         }
360         SEGPTR_FREE(xwavehdr);
361     } else {
362         if (WAVE_NotifyClient(wDevID, WOM_DONE, (DWORD)lpWaveHdr, count) != MMSYSERR_NOERROR) {
363             WARN(wave, "can't notify client !\n");
364             return MMSYSERR_INVALPARAM;
365         }
366     }
367     return MMSYSERR_NOERROR;
368 }
369
370 /**************************************************************************
371  *                              wodPrepare                      [internal]
372  */
373 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
374 {
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;
379     }
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;
387 }
388
389 /**************************************************************************
390  *                              wodUnprepare                    [internal]
391  */
392 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
393 {
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;
398     }
399     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
400         return WAVERR_STILLPLAYING;
401     
402     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
403     lpWaveHdr->dwFlags |= WHDR_DONE;
404     TRACE(wave, "all headers unprepared !\n");
405     return MMSYSERR_NOERROR;
406 }
407
408 /**************************************************************************
409  *                      wodRestart                              [internal]
410  */
411 static DWORD wodRestart(WORD wDevID)
412 {
413     TRACE(wave,"(%u);\n", wDevID);
414     if (WOutDev[wDevID].unixdev == 0) {
415         WARN(wave, "can't restart !\n");
416         return MMSYSERR_NOTENABLED;
417     }
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;
423        }
424     */
425     
426     return MMSYSERR_NOERROR;
427 }
428
429 /**************************************************************************
430  *                      wodReset                                [internal]
431  */
432 static DWORD wodReset(WORD wDevID)
433 {
434     TRACE(wave,"(%u);\n", wDevID);
435     if (WOutDev[wDevID].unixdev == 0) {
436         WARN(wave, "can't reset !\n");
437         return MMSYSERR_NOTENABLED;
438     }
439     return MMSYSERR_NOERROR;
440 }
441
442
443 /**************************************************************************
444  *                              wodGetPosition                  [internal]
445  */
446 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
447 {
448     int         time;
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;
453     }
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) {
461     case TIME_BYTES:
462         lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
463         TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
464         break;
465     case TIME_SAMPLES:
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);
471         break;
472     case TIME_SMPTE:
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);
486         break;
487     default:
488         FIXME(wave, "wodGetPosition() format %d not supported ! use TIME_MS !\n",lpTime->wType);
489         lpTime->wType = TIME_MS;
490     case 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);
494         break;
495     }
496     return MMSYSERR_NOERROR;
497 }
498
499 /**************************************************************************
500  *                              wodGetVolume                    [internal]
501  */
502 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
503 {
504     int         mixer;
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;
511     }
512     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
513         WARN(wave, "unable read mixer !\n");
514         return MMSYSERR_NOTENABLED;
515     }
516     close(mixer);
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;
522 }
523
524
525 /**************************************************************************
526  *                              wodSetVolume                    [internal]
527  */
528 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
529 {
530     int         mixer;
531     int         volume;
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;
538     }
539     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
540         WARN(wave, "unable set mixer !\n");
541         return MMSYSERR_NOTENABLED;
542     }
543     close(mixer);
544     return MMSYSERR_NOERROR;
545 }
546
547 /**************************************************************************
548  *                              wodMessage                      [sample driver]
549  */
550 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
551                         DWORD dwParam1, DWORD dwParam2)
552 {
553     int audio;
554     TRACE(wave,"wodMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
555           wDevID, wMsg, dwUser, dwParam1, dwParam2);
556     switch(wMsg) {
557     case WODM_OPEN:
558         return wodOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
559     case WODM_CLOSE:
560         return wodClose(wDevID);
561     case WODM_WRITE:
562         return wodWrite(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
563     case WODM_PAUSE:
564         return MMSYSERR_NOTSUPPORTED;
565     case WODM_STOP:
566         return MMSYSERR_NOTSUPPORTED;
567     case WODM_GETPOS:
568         return wodGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
569     case WODM_BREAKLOOP:
570         return MMSYSERR_NOTSUPPORTED;
571     case WODM_PREPARE:
572         return wodPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
573     case WODM_UNPREPARE:
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);
580         if (audio == -1)
581         {
582             if (errno == EBUSY)
583                 return 1;
584             else
585                 return 0;
586         }
587         close (audio);
588         return 1;
589     case WODM_GETPITCH:
590         return MMSYSERR_NOTSUPPORTED;
591     case WODM_SETPITCH:
592         return MMSYSERR_NOTSUPPORTED;
593     case WODM_GETPLAYBACKRATE:
594         return MMSYSERR_NOTSUPPORTED;
595     case WODM_SETPLAYBACKRATE:
596         return MMSYSERR_NOTSUPPORTED;
597     case WODM_GETVOLUME:
598         return wodGetVolume(wDevID, (LPDWORD)dwParam1);
599     case WODM_SETVOLUME:
600         return wodSetVolume(wDevID, dwParam1);
601     case WODM_RESTART:
602         return wodRestart(wDevID);
603     case WODM_RESET:
604         return wodReset(wDevID);
605     default:
606         WARN(wave,"unknown message !\n");
607     }
608     return MMSYSERR_NOTSUPPORTED;
609 }
610
611
612 /*-----------------------------------------------------------------------*/
613
614 /**************************************************************************
615  *                      widGetDevCaps                           [internal]
616  */
617 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
618 {
619     int         audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
620     
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 ;
626 #ifdef EMULATE_SB16
627     lpCaps->wMid = 0x0002;
628     lpCaps->wPid = 0x0004;
629     strcpy(lpCaps->szPname, "SB16 Wave In");
630 #else
631     lpCaps->wMid = 0x00FF;      /* Manufac ID */
632     lpCaps->wPid = 0x0001;      /* Product ID */
633     strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
634 #endif
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;
638     smplrate = 44100;
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;
647         }
648     }
649     smplrate = 22050;
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;
658         }
659     }
660     smplrate = 11025;
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;
669         }
670     }
671     close(audio);
672     TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
673     return MMSYSERR_NOERROR;
674 }
675
676
677 /**************************************************************************
678  *                              widOpen                         [internal]
679  */
680 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
681 {
682     int                 audio,abuf_size,smplrate,samplesize,dsp_stereo;
683     LPWAVEFORMAT        lpFormat;
684     
685     TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
686     if (lpDesc == NULL) {
687         WARN(wave, "Invalid Parameter !\n");
688         return MMSYSERR_INVALPARAM;
689     }
690     if (wDevID >= MAX_WAVEINDRV) {
691         TRACE(wave,"MAX_WAVINDRV reached !\n");
692         return MMSYSERR_ALLOCATED;
693     }
694     WInDev[wDevID].unixdev = 0;
695     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
696     audio = open (SOUND_DEV, O_RDONLY, 0);
697     if (audio == -1) {
698         WARN(wave,"can't open !\n");
699         return MMSYSERR_ALLOCATED;
700     }
701     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
702     if (abuf_size < 1024 || abuf_size > 65536) {
703         if (abuf_size == -1)
704             WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
705         else
706             WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid bufsize !\n");
707         return MMSYSERR_NOTENABLED;
708     }
709     WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
710     switch(WInDev[wDevID].wFlags) {
711     case DCB_NULL:
712         TRACE(wave,"CALLBACK_NULL!\n");
713         break;
714     case DCB_WINDOW:
715         TRACE(wave,"CALLBACK_WINDOW!\n");
716         break;
717     case DCB_TASK:
718         TRACE(wave,"CALLBACK_TASK!\n");
719         break;
720     case DCB_FUNCTION:
721         TRACE(wave,"CALLBACK_FUNCTION!\n");
722         break;
723     }
724     if (WInDev[wDevID].lpQueueHdr) {
725         HeapFree(GetProcessHeap(),0,WInDev[wDevID].lpQueueHdr);
726         WInDev[wDevID].lpQueueHdr = NULL;
727     }
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;
737     }
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;
747     }
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;
761     }
762     return MMSYSERR_NOERROR;
763 }
764
765 /**************************************************************************
766  *                              widClose                        [internal]
767  */
768 static DWORD widClose(WORD wDevID)
769 {
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;
775     }
776     if (WInDev[wDevID].lpQueueHdr != NULL) {
777         WARN(wave, "still buffers open !\n");
778         return WAVERR_STILLPLAYING;
779     }
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;
786     }
787     return MMSYSERR_NOERROR;
788 }
789
790 /**************************************************************************
791  *                              widAddBuffer            [internal]
792  */
793 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
794 {
795     int         count   = 1;
796     LPWAVEHDR   lpWIHdr;
797     
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;
802     }
803     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
804         TRACE(wave, "never been prepared !\n");
805         return WAVERR_UNPREPARED;
806     }
807     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
808         TRACE(wave,     "header already in use !\n");
809         return WAVERR_STILLPLAYING;
810     }
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;
817     } else {
818         lpWIHdr = WInDev[wDevID].lpQueueHdr;
819         while (lpWIHdr->lpNext != NULL) {
820             lpWIHdr = lpWIHdr->lpNext;
821             count++;
822         }
823         lpWIHdr->lpNext = lpWaveHdr;
824         lpWaveHdr->lpNext = NULL;
825         count++;
826     }
827     TRACE(wave, "buffer added ! (now %u in queue)\n", count);
828     return MMSYSERR_NOERROR;
829 }
830
831 /**************************************************************************
832  *                              widPrepare                      [internal]
833  */
834 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
835 {
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;
840     }
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;
849 }
850
851 /**************************************************************************
852  *                              widUnprepare                    [internal]
853  */
854 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
855 {
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;
860     }
861     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
862     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
863     lpWaveHdr->dwFlags |= WHDR_DONE;
864     
865     TRACE(wave, "all headers unprepared !\n");
866     return MMSYSERR_NOERROR;
867 }
868
869 /**************************************************************************
870  *                      widStart                                [internal]
871  */
872 static DWORD widStart(WORD wDevID)
873 {
874     int         count   = 1;
875     int            bytesRead;
876     LPWAVEHDR   lpWIHdr;
877     LPWAVEHDR   *lpWaveHdr;
878     
879     TRACE(wave,"(%u);\n", wDevID);
880     if (WInDev[wDevID].unixdev == 0) {
881         WARN(wave, "can't start recording !\n");
882         return MMSYSERR_NOTENABLED;
883     }
884     
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;
890     }
891     
892     while(*lpWaveHdr != NULL) {
893         lpWIHdr = *lpWaveHdr;
894         TRACE(wave, "recording buf#%u=%p size=%lu \n",
895               count, lpWIHdr->lpData, lpWIHdr->dwBufferLength);
896         fflush(stddeb);
897         bytesRead = read (WInDev[wDevID].unixdev, 
898                           lpWIHdr->lpData,
899                           lpWIHdr->dwBufferLength);
900         if (bytesRead==-1)
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;
907         
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;
912         }
913         /* removes the current block from the queue */
914         *lpWaveHdr = lpWIHdr->lpNext;
915         count++;
916     }
917     TRACE(wave,"end of recording !\n");
918     fflush(stddeb);
919     return MMSYSERR_NOERROR;
920 }
921
922 /**************************************************************************
923  *                      widStop                                 [internal]
924  */
925 static DWORD widStop(WORD wDevID)
926 {
927     TRACE(wave,"(%u);\n", wDevID);
928     if (WInDev[wDevID].unixdev == 0) {
929         WARN(wave,"can't stop !\n");
930         return MMSYSERR_NOTENABLED;
931     }
932     return MMSYSERR_NOERROR;
933 }
934
935 /**************************************************************************
936  *                      widReset                                [internal]
937  */
938 static DWORD widReset(WORD wDevID)
939 {
940     TRACE(wave,"(%u);\n", wDevID);
941     if (WInDev[wDevID].unixdev == 0) {
942         WARN(wave,"can't reset !\n");
943         return MMSYSERR_NOTENABLED;
944     }
945     return MMSYSERR_NOERROR;
946 }
947
948 /**************************************************************************
949  *                              widGetPosition                  [internal]
950  */
951 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
952 {
953     int         time;
954     
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;
959     }
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); 
966     fflush(stddeb);
967     switch(lpTime->wType) {
968     case TIME_BYTES:
969         lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
970         TRACE(wave,"TIME_BYTES=%lu\n", lpTime->u.cb);
971         break;
972     case TIME_SAMPLES:
973         lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
974             WInDev[wDevID].Format.wBitsPerSample;
975         TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
976         break;
977     case TIME_SMPTE:
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);
991         break;
992     case TIME_MS:
993         lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
994             (WInDev[wDevID].Format.wf.nAvgBytesPerSec / 1000);
995         TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
996         break;
997     default:
998         FIXME(wave, "format not supported ! use TIME_MS !\n");
999         lpTime->wType = TIME_MS;
1000     }
1001     return MMSYSERR_NOERROR;
1002 }
1003
1004 /**************************************************************************
1005  *                              widMessage                      [sample driver]
1006  */
1007 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1008                         DWORD dwParam1, DWORD dwParam2)
1009 {
1010     int audio;
1011     TRACE(wave,"widMessage(%u, %04X, %08lX, %08lX, %08lX);\n",
1012           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1013     switch(wMsg) {
1014     case WIDM_OPEN:
1015         return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1016     case WIDM_CLOSE:
1017         return widClose(wDevID);
1018     case WIDM_ADDBUFFER:
1019         return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1020     case WIDM_PREPARE:
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);
1029         if (audio == -1)
1030         {
1031             if (errno == EBUSY)
1032                 return 1;
1033             else
1034                 return 0;
1035         }
1036         close (audio);
1037         return 1;
1038     case WIDM_GETPOS:
1039         return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1040     case WIDM_RESET:
1041         return widReset(wDevID);
1042     case WIDM_START:
1043         return widStart(wDevID);
1044     case WIDM_PAUSE:
1045         return widStop(wDevID);
1046     case WIDM_STOP:
1047         return widStop(wDevID);
1048     default:
1049         WARN(wave,"unknown message !\n");
1050     }
1051     return MMSYSERR_NOTSUPPORTED;
1052 }
1053
1054
1055 /**************************************************************************
1056  *                              WAVE_DriverProc16               [sample driver]
1057  */
1058 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
1059                        DWORD dwParam1, DWORD dwParam2)
1060 {
1061     TRACE(wave,"(%08lX, %04X, %04X, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1062
1063     switch(wMsg) {
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;
1074     default:
1075         FIXME(wave, "is probably wrong msg=0x%04x\n", wMsg);
1076         return DefDriverProc16(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1077     }
1078     return MMSYSERR_NOTENABLED;
1079 }
1080
1081 /**************************************************************************
1082  *                              WAVE_DriverProc32               [sample driver]
1083  */
1084 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, 
1085                        DWORD dwParam1, DWORD dwParam2)
1086 {
1087     TRACE(wave,"(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1088     switch(wMsg) {
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;
1099     default:
1100         FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg);
1101         return DefDriverProc32(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1102     }
1103     return MMSYSERR_NOTENABLED;
1104 }
1105
1106 #else /* !HAVE_OSS */
1107
1108 /**************************************************************************
1109  *                              wodMessage                      [sample driver]
1110  */
1111 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1112                         DWORD dwParam1, DWORD dwParam2)
1113 {
1114     FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1115     return MMSYSERR_NOTENABLED;
1116 }
1117
1118 /**************************************************************************
1119  *                              widMessage                      [sample driver]
1120  */
1121 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1122                         DWORD dwParam1, DWORD dwParam2)
1123 {
1124     FIXME(wave,"(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1125     return MMSYSERR_NOTENABLED;
1126 }
1127
1128 /**************************************************************************
1129  *                              WAVE_DriverProc16               [sample driver]
1130  */
1131 LONG WAVE_DriverProc16(DWORD dwDevID, HDRVR16 hDriv, WORD wMsg, 
1132                        DWORD dwParam1, DWORD dwParam2)
1133 {
1134     return MMSYSERR_NOTENABLED;
1135 }
1136
1137 /**************************************************************************
1138  *                              WAVE_DriverProc32               [sample driver]
1139  */
1140 LONG WAVE_DriverProc32(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, 
1141                        DWORD dwParam1, DWORD dwParam2)
1142 {
1143     return MMSYSERR_NOTENABLED;
1144 }
1145 #endif /* HAVE_OSS */