Cast the unsigned int to signed, or NEAR_MATCH will not work.
[wine] / dlls / winmm / wineoss / 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  *           1999 Eric Pouech (async playing in waveOut)
7  */
8 /*
9  * FIXME:
10  *      pause in waveOut does not work correctly
11  *      implement async handling in waveIn
12  */
13
14 /*#define EMULATE_SB16*/
15
16 #include "config.h"
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <sys/ioctl.h>
24 #include "wine/winuser16.h"
25 #include "driver.h"
26 #include "mmddk.h"
27 #include "oss.h"
28 #include "heap.h"
29 #include "ldt.h"
30 #include "debugtools.h"
31
32 DEFAULT_DEBUG_CHANNEL(wave)
33
34 /* Allow 1% deviation for sample rates (some ES137x cards) */
35 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
36
37 #ifdef HAVE_OSS
38
39 #define SOUND_DEV "/dev/dsp"
40 #define MIXER_DEV "/dev/mixer"
41
42 #define MAX_WAVEOUTDRV  (1)
43 #define MAX_WAVEINDRV   (1)
44
45 /* state diagram for waveOut writing:
46  *
47  * +---------+-------------+---------------+---------------------------------+
48  * |  state  |  function   |     event     |            new state            |
49  * +---------+-------------+---------------+---------------------------------+
50  * |         | open()      |               | STOPPED                         |
51  * | PAUSED  | write()     |               | PAUSED                          |
52  * | STOPPED | write()     | <thrd create> | PLAYING                         |
53  * | PLAYING | write()     | HEADER        | PLAYING                         |
54  * | (other) | write()     | <error>       |                                 |
55  * | (any)   | pause()     | PAUSING       | PAUSED                          |
56  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
57  * | (any)   | reset()     | RESETTING     | STOPPED                         |
58  * | (any)   | close()     | CLOSING       | CLOSED                          |
59  * +---------+-------------+---------------+---------------------------------+
60  */
61
62 /* states of the playing device */
63 #define WINE_WS_PLAYING         0
64 #define WINE_WS_PAUSED          1
65 #define WINE_WS_STOPPED         2
66 #define WINE_WS_CLOSED          3
67
68 /* events to be send to device */
69 #define WINE_WM_PAUSING         (WM_USER + 1)
70 #define WINE_WM_RESTARTING      (WM_USER + 2)
71 #define WINE_WM_RESETTING       (WM_USER + 3)
72 #define WINE_WM_CLOSING         (WM_USER + 4)
73 #define WINE_WM_HEADER          (WM_USER + 5)
74
75 typedef struct {
76     int                         unixdev;
77     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
78     DWORD                       dwFragmentSize;         /* size of OSS buffer fragment */
79     WAVEOPENDESC                waveDesc;
80     WORD                        wFlags;
81     PCMWAVEFORMAT               format;
82     LPWAVEHDR                   lpQueueHdr;             /* pending buffers for playing */
83     LPWAVEHDR                   lpNotifyHdr;            /* list of wavehdr for which write() has been called, pending for notification */
84
85     DWORD                       dwPlayedTotal;          /* number of bytes played since opening */
86     DWORD                       dwPlayed;               /* number of bytes played since last DSP_RESET */
87     DWORD                       dwNotifiedBytes;        /* number of bytes for which wavehdr notification has been done */
88
89     /* info on current lpQueueHdr->lpWaveHdr */
90     DWORD                       dwOffCurrHdr;           /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
91     DWORD                       dwRemain;               /* number of bytes to write to end the current fragment  */
92
93     /* synchronization stuff */
94     HANDLE                      hThread;
95     DWORD                       dwThreadID;
96     HANDLE                      hEvent;
97
98     WORD                        wMaxFragments;          /* max number of fragments that can be written to dsp */
99     WORD                        wFragsUsedInQueue;      /* current number of used fragments inside dsp queue */
100 } WINE_WAVEOUT;
101
102 typedef struct {
103     int                         unixdev;
104     volatile int                state;
105     DWORD                       dwFragmentSize;         /* OpenSound '/dev/dsp' give us that size */
106     WAVEOPENDESC                waveDesc;
107     WORD                        wFlags;
108     PCMWAVEFORMAT               format;
109     LPWAVEHDR                   lpQueueHdr;
110     DWORD                       dwTotalRecorded;
111 } WINE_WAVEIN;
112
113 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
114 static WINE_WAVEIN      WInDev    [MAX_WAVEOUTDRV];
115
116 /*======================================================================*
117  *                  Low level WAVE implemantation                       *
118  *======================================================================*/
119
120 /**************************************************************************
121  *                      WAVE_NotifyClient                       [internal]
122  */
123 static DWORD WAVE_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, 
124                                DWORD dwParam2)
125 {
126     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
127     
128     switch (wMsg) {
129     case WOM_OPEN:
130     case WOM_CLOSE:
131     case WOM_DONE:
132         if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
133         
134         if (WOutDev[wDevID].wFlags != DCB_NULL && 
135             !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback, 
136                             WOutDev[wDevID].wFlags, 
137                             WOutDev[wDevID].waveDesc.hWave, 
138                             wMsg, 
139                             WOutDev[wDevID].waveDesc.dwInstance, 
140                             dwParam1, 
141                             dwParam2)) {
142             WARN("can't notify client !\n");
143             return MMSYSERR_NOERROR;
144         }
145         break;
146         
147     case WIM_OPEN:
148     case WIM_CLOSE:
149     case WIM_DATA:
150         if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
151         
152         if (WInDev[wDevID].wFlags != DCB_NULL && 
153             !DriverCallback(WInDev[wDevID].waveDesc.dwCallback, 
154                             WInDev[wDevID].wFlags, 
155                             WInDev[wDevID].waveDesc.hWave, 
156                             wMsg, 
157                             WInDev[wDevID].waveDesc.dwInstance, 
158                             dwParam1, 
159                             dwParam2)) {
160             WARN("can't notify client !\n");
161             return MMSYSERR_NOERROR;
162         }
163         break;
164     default:
165         FIXME("Unknown CB message %u\n", wMsg);
166         break;
167     }
168     return 0;
169 }
170
171 /*======================================================================*
172  *                  Low level WAVE OUT implemantation                   *
173  *======================================================================*/
174
175 /**************************************************************************
176  *                              wodPlayer_WriteFragments        [internal]
177  *
178  * wodPlayer helper. Writes as many fragments it can to unixdev.
179  * Returns TRUE in case of buffer underrun.
180  */
181 static  BOOL    wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
182 {
183     LPWAVEHDR           lpWaveHdr;
184     LPBYTE              lpData;
185     int                 count;
186
187     for (;;) {
188         TRACE("Fragments: %d used on fd %d\n", wwo->wFragsUsedInQueue, wwo->unixdev);
189         if (wwo->wFragsUsedInQueue == wwo->wMaxFragments)       /* output queue is full, wait a bit */
190             return FALSE;
191
192         lpWaveHdr = wwo->lpQueueHdr;
193         if (!lpWaveHdr) {
194             if (wwo->dwRemain > 0 &&                            /* still data to send to complete current fragment */
195                 wwo->dwNotifiedBytes >= wwo->dwFragmentSize &&  /* first fragment has been played */
196                 wwo->wFragsUsedInQueue < 2) {                   /* done with all waveOutWrite()' fragments */
197                 /* FIXME: should do better handling here */
198                 TRACE("Oooch, buffer underrun !\n");
199                 return TRUE; /* force resetting of waveOut device */
200             }
201             return FALSE;       /* wait a bit */
202         }
203         
204         if (wwo->dwOffCurrHdr == 0) {
205             TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
206             if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
207                 FIXME("NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
208         }
209         
210         lpData = lpWaveHdr->lpData;
211
212         /* finish current wave hdr ? */
213         if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) { 
214             DWORD       toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
215             
216             /* write end of current wave hdr */
217             count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
218             TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
219             
220             if (count > 0 || toWrite == 0) {
221                 LPWAVEHDR*      wh;
222                 
223                 /* move lpWaveHdr to the end of notify list */
224                 for (wh = &(wwo->lpNotifyHdr); *wh; wh = &((*wh)->lpNext));
225                     *wh = lpWaveHdr;
226
227                 wwo->lpQueueHdr = lpWaveHdr->lpNext;
228                 lpWaveHdr->lpNext = 0;
229                 
230                 wwo->dwOffCurrHdr = 0;
231                 if ((wwo->dwRemain -= count) == 0) {
232                     wwo->dwRemain = wwo->dwFragmentSize;
233                     wwo->wFragsUsedInQueue++;
234                 }
235             }
236             continue; /* try to go to use next wavehdr */
237         }  else {
238             count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
239             TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
240             if (count > 0) {
241                 wwo->dwOffCurrHdr += count;
242                 wwo->dwRemain = wwo->dwFragmentSize;
243                 wwo->wFragsUsedInQueue++;
244             }
245         }
246     }
247 }
248
249 /**************************************************************************
250  *                              wodPlayer_WriteFragments        [internal]
251  *
252  * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
253  * have been played (actually to speaker, not to unixdev fd).
254  */
255 static  void    wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
256 {
257     LPWAVEHDR           lpWaveHdr;
258     count_info          cinfo;
259     
260     /* get effective number of written bytes */
261     if (!force) {
262         int     c;
263
264         if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
265             perror("ioctl SNDCTL_DSP_GETOPTR");
266             wwo->hThread = 0;
267             wwo->state = WINE_WS_STOPPED;
268             ExitThread(-1);
269         }
270         TRACE("Played %d bytes (played=%ld) on fd %d\n", cinfo.bytes, wwo->dwPlayed, wwo->unixdev);
271         c = cinfo.bytes / wwo->dwFragmentSize - wwo->dwPlayed / wwo->dwFragmentSize;
272         if (wwo->wFragsUsedInQueue > c)
273             wwo->wFragsUsedInQueue -= c;
274         else
275             wwo->wFragsUsedInQueue = 0;
276         wwo->dwPlayed = cinfo.bytes;
277     }
278     if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
279         /* remove all wavehdr which can be notified */
280         while (wwo->lpNotifyHdr && 
281                (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
282             lpWaveHdr = wwo->lpNotifyHdr;
283             
284             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
285             lpWaveHdr->dwFlags |= WHDR_DONE;
286             if (!force)
287                 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
288             wwo->lpNotifyHdr = lpWaveHdr->lpNext;
289             
290             TRACE("Notifying client with %p\n", lpWaveHdr);
291             if (WAVE_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
292                 WARN("can't notify client !\n");
293             }
294         }
295     }
296 }
297
298 /**************************************************************************
299  *                              wodPlayer_Reset                 [internal]
300  *
301  * wodPlayer helper. Resets current output stream.
302  */
303 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
304 {
305     LPWAVEHDR           lpWaveHdr;
306
307     /* updates current notify list */
308     wodPlayer_Notify(wwo, uDevID, FALSE);
309
310     /* flush all possible output */
311     if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
312         perror("ioctl SNDCTL_DSP_RESET");
313         wwo->hThread = 0;
314         wwo->state = WINE_WS_STOPPED;
315         ExitThread(-1);
316     }
317
318     wwo->dwOffCurrHdr = 0;
319     if (reset) {
320         /* empty notify list */
321         wodPlayer_Notify(wwo, uDevID, TRUE);
322         if (wwo->lpNotifyHdr) {
323             ERR("out of sync\n");
324         }
325         /* get rid also of all the current queue */
326         for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
327             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
328             lpWaveHdr->dwFlags |= WHDR_DONE;
329             
330             if (WAVE_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
331                 WARN("can't notify client !\n");
332             }
333         }
334         wwo->lpQueueHdr = 0;
335         wwo->state = WINE_WS_STOPPED;
336         wwo->dwPlayedTotal = 0;
337     } else {
338         /* move notify list to begining of lpQueueHdr list */
339         while (wwo->lpNotifyHdr) {
340             lpWaveHdr = wwo->lpNotifyHdr;
341             wwo->lpNotifyHdr = lpWaveHdr->lpNext;
342             lpWaveHdr->lpNext = wwo->lpQueueHdr;
343             wwo->lpQueueHdr = lpWaveHdr;
344         }
345         wwo->state = WINE_WS_PAUSED;
346         wwo->dwPlayedTotal += wwo->dwPlayed;
347     }
348     wwo->dwNotifiedBytes = wwo->dwPlayed = 0;
349     wwo->wFragsUsedInQueue = 0;
350 }
351
352 /**************************************************************************
353  *                              wodPlayer                       [internal]
354  */
355 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
356 {
357     WORD                uDevID = (DWORD)pmt;
358     WINE_WAVEOUT*       wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
359     WAVEHDR*            lpWaveHdr;
360     DWORD               dwSleepTime;
361     MSG                 msg;
362
363     PeekMessageA(&msg, 0, 0, 0, 0);
364     wwo->state = WINE_WS_STOPPED;
365
366     wwo->dwNotifiedBytes = 0;
367     wwo->dwOffCurrHdr = 0;
368     wwo->dwRemain = wwo->dwFragmentSize;
369     wwo->lpQueueHdr = NULL;
370     wwo->lpNotifyHdr = NULL;
371     wwo->wFragsUsedInQueue = 0;
372     wwo->dwPlayedTotal = 0;
373     wwo->dwPlayed = 0;
374
375     TRACE("imhere[0]\n");
376     SetEvent(wwo->hEvent);
377
378     /* make sleep time to be # of ms to output a fragment */
379     dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
380
381     for (;;) {
382         /* wait for dwSleepTime or an event in thread's queue */
383         /* FIXME: could improve wait time depending on queue state,
384          * ie, number of queued fragments
385          */
386         TRACE("imhere[1]\n");
387         MsgWaitForMultipleObjects(0, NULL, FALSE, 
388                                   (wwo->state == WINE_WS_PLAYING) ? 
389                                      (MAX(wwo->wFragsUsedInQueue, 4) - 2) * dwSleepTime : 
390                                      /*INFINITE*/100, 
391                                   QS_POSTMESSAGE);
392         TRACE("imhere[2]\n");
393         wodPlayer_Notify(wwo, uDevID, FALSE);
394         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
395             switch (msg.message) {
396             case WINE_WM_PAUSING:
397                 wodPlayer_Reset(wwo, uDevID, FALSE);
398                 wwo->state = WINE_WS_PAUSED;
399                 SetEvent(wwo->hEvent);
400                 break;
401             case WINE_WM_RESTARTING:
402                 wwo->state = WINE_WS_PLAYING;
403                 SetEvent(wwo->hEvent);
404                 break;
405             case WINE_WM_HEADER:
406                 lpWaveHdr = (LPWAVEHDR)msg.lParam;
407                 
408                 lpWaveHdr->dwFlags &= ~WHDR_DONE;
409                 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
410                 lpWaveHdr->lpNext = 0;
411                 
412                 /* insert buffer at the end of queue */
413                 {
414                     LPWAVEHDR*  wh;
415                     for (wh = &(wwo->lpQueueHdr); *wh; wh = &((*wh)->lpNext));
416                     *wh = lpWaveHdr;
417                 }
418                 if (wwo->state == WINE_WS_STOPPED)
419                     wwo->state = WINE_WS_PLAYING;
420                 break;
421             case WINE_WM_RESETTING:
422                 wodPlayer_Reset(wwo, uDevID, TRUE);
423                 SetEvent(wwo->hEvent);
424                 break;
425             case WINE_WM_CLOSING:
426                 /* sanity check: this should not happen since the device must have been reset before */
427                 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
428                     ERR("out of sync\n");
429                 }
430                 wwo->hThread = 0;
431                 wwo->state = WINE_WS_CLOSED;
432                 SetEvent(wwo->hEvent);
433                 ExitThread(0);
434                 /* shouldn't go here */
435             default:
436                 FIXME("unknown message %d\n", msg.message);
437                 break;
438             }
439         }
440         if (wwo->state == WINE_WS_PLAYING) {
441             wodPlayer_WriteFragments(wwo);
442         }
443         wodPlayer_Notify(wwo, uDevID, FALSE);
444     }
445     ExitThread(0);
446     /* just for not generating compilation warnings... should never be executed */
447     return 0; 
448 }
449
450 /**************************************************************************
451  *                      wodGetDevCaps                           [internal]
452  */
453 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
454 {
455     int         audio;
456     int         smplrate;
457     int         samplesize = 16;
458     int         dsp_stereo = 1;
459     int         bytespersmpl;
460     int         caps;
461     int         mask;
462     
463     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
464     
465     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
466     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
467     
468     if (wDevID >= MAX_WAVEOUTDRV) {
469         TRACE("MAX_WAVOUTDRV reached !\n");
470         return MMSYSERR_BADDEVICEID;
471     }
472     
473     if (WOutDev[wDevID].unixdev == 0) {
474         audio = open(SOUND_DEV, O_WRONLY, 0);
475         if (audio == -1) return MMSYSERR_ALLOCATED;
476     } else {
477         audio = WOutDev[wDevID].unixdev;
478     }
479
480     /* FIXME: some programs compare this string against the content of the registry
481      * for MM drivers. The name have to match in order the program to work 
482      * (e.g. MS win9x mplayer.exe)
483      */
484 #ifdef EMULATE_SB16
485     lpCaps->wMid = 0x0002;
486     lpCaps->wPid = 0x0104;
487     strcpy(lpCaps->szPname, "SB16 Wave Out");
488 #else
489     lpCaps->wMid = 0x00FF;      /* Manufac ID */
490     lpCaps->wPid = 0x0001;      /* Product ID */
491     /*    strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
492     strcpy(lpCaps->szPname, "CS4236/37/38");
493 #endif
494     lpCaps->vDriverVersion = 0x0100;
495     lpCaps->dwFormats = 0x00000000;
496     lpCaps->dwSupport = WAVECAPS_VOLUME;
497     
498     IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
499     TRACE("OSS dsp mask=%08x\n", mask);
500     mask = AFMT_QUERY;
501     IOCTL(audio, SNDCTL_DSP_SETFMT, mask);
502     TRACE("OSS dsp current=%08x\n", mask);
503
504     /* First bytespersampl, then stereo */
505     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
506     
507     lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
508     if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
509     
510     smplrate = 44100;
511     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
512         lpCaps->dwFormats |= WAVE_FORMAT_4M08;
513         if (lpCaps->wChannels > 1)
514             lpCaps->dwFormats |= WAVE_FORMAT_4S08;
515         if (bytespersmpl > 1) {
516             lpCaps->dwFormats |= WAVE_FORMAT_4M16;
517             if (lpCaps->wChannels > 1)
518                 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
519         }
520     }
521     smplrate = 22050;
522     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
523         lpCaps->dwFormats |= WAVE_FORMAT_2M08;
524         if (lpCaps->wChannels > 1)
525             lpCaps->dwFormats |= WAVE_FORMAT_2S08;
526         if (bytespersmpl > 1) {
527             lpCaps->dwFormats |= WAVE_FORMAT_2M16;
528             if (lpCaps->wChannels > 1)
529                 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
530         }
531     }
532     smplrate = 11025;
533     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
534         lpCaps->dwFormats |= WAVE_FORMAT_1M08;
535         if (lpCaps->wChannels > 1)
536             lpCaps->dwFormats |= WAVE_FORMAT_1S08;
537         if (bytespersmpl > 1) {
538             lpCaps->dwFormats |= WAVE_FORMAT_1M16;
539             if (lpCaps->wChannels > 1)
540                 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
541         }
542     }
543     if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
544         if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
545             lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
546     }
547     if (WOutDev[wDevID].unixdev == 0) {
548         close(audio);
549     }
550     TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
551     return MMSYSERR_NOERROR;
552 }
553
554 /**************************************************************************
555  *                              wodOpen                         [internal]
556  */
557 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
558 {
559     int                 audio;
560     int                 sample_rate;
561     int                 sample_size;
562     int                 dsp_stereo;
563     int                 audio_fragment;
564     int                 fragment_size;
565     WAVEOUTCAPSA        woc;
566
567     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
568     if (lpDesc == NULL) {
569         WARN("Invalid Parameter !\n");
570         return MMSYSERR_INVALPARAM;
571     }
572     if (wDevID >= MAX_WAVEOUTDRV) {
573         TRACE("MAX_WAVOUTDRV reached !\n");
574         return MMSYSERR_BADDEVICEID;
575     }
576     wodGetDevCaps(wDevID, &woc, sizeof(woc));
577
578     /* only PCM format is supported so far... */
579     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
580         lpDesc->lpFormat->nChannels == 0 ||
581         lpDesc->lpFormat->nSamplesPerSec == 0) {
582         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
583              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
584              lpDesc->lpFormat->nSamplesPerSec);
585         return WAVERR_BADFORMAT;
586     }
587
588     if (dwFlags & WAVE_FORMAT_QUERY) {
589         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
590              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
591              lpDesc->lpFormat->nSamplesPerSec);
592         return MMSYSERR_NOERROR;
593     }
594
595     WOutDev[wDevID].unixdev = 0;
596     if (access(SOUND_DEV, 0) != 0) 
597         return MMSYSERR_NOTENABLED;
598     audio = open(SOUND_DEV, O_WRONLY, 0);
599     if (audio == -1) {
600         WARN("can't open !\n");
601         return MMSYSERR_ALLOCATED ;
602     }
603
604     WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
605     
606     memcpy(&WOutDev[wDevID].waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
607     memcpy(&WOutDev[wDevID].format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
608     
609     if (WOutDev[wDevID].format.wBitsPerSample == 0) {
610         WOutDev[wDevID].format.wBitsPerSample = 8 *
611             (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
612              WOutDev[wDevID].format.wf.nSamplesPerSec) /
613             WOutDev[wDevID].format.wf.nChannels;
614     }
615     
616     /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
617      * thus leading to 46ms per fragment, and a turnaround time of 185ms
618      */
619     /* 2^10=1024 bytes per fragment, 16 fragments max */
620     audio_fragment = 0x000F000A;
621     sample_size = WOutDev[wDevID].format.wBitsPerSample;
622     sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
623     dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0;
624
625     IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
626     /* First size and stereo then samplerate */
627     IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
628     IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
629     IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
630
631     /* paranoid checks */
632     if (sample_size != WOutDev[wDevID].format.wBitsPerSample)
633         ERR("Can't set sample_size to %u (%d)\n", 
634             WOutDev[wDevID].format.wBitsPerSample, sample_size);
635     if (dsp_stereo != (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0) 
636         ERR("Can't set stereo to %u (%d)\n", 
637             (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
638     if (!NEAR_MATCH(sample_rate,WOutDev[wDevID].format.wf.nSamplesPerSec))
639         ERR("Can't set sample_rate to %lu (%d)\n", 
640             WOutDev[wDevID].format.wf.nSamplesPerSec, sample_rate);
641
642     /* even if we set fragment size above, read it again, just in case */
643     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
644
645     WOutDev[wDevID].unixdev = audio;
646     WOutDev[wDevID].dwFragmentSize = fragment_size;
647     WOutDev[wDevID].wMaxFragments = HIWORD(audio_fragment) + 1;
648
649     WOutDev[wDevID].hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
650     WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(WOutDev[wDevID].dwThreadID));
651     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
652
653     TRACE("fd=%d fragmentSize=%ld\n", 
654           WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
655
656     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n", 
657           WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec, 
658           WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
659           WOutDev[wDevID].format.wf.nBlockAlign);
660     
661     if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
662         WARN("can't notify client !\n");
663         return MMSYSERR_INVALPARAM;
664     }
665     return MMSYSERR_NOERROR;
666 }
667
668 /**************************************************************************
669  *                              wodClose                        [internal]
670  */
671 static DWORD wodClose(WORD wDevID)
672 {
673     DWORD       ret = MMSYSERR_NOERROR;
674     
675     TRACE("(%u);\n", wDevID);
676     
677     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
678         WARN("bad device ID !\n");
679         return MMSYSERR_BADDEVICEID;
680     }
681     
682     if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
683         WARN("buffers still playing !\n");
684         ret = WAVERR_STILLPLAYING;
685     } else {
686         TRACE("imhere[3-close]\n");
687         PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_CLOSING, 0, 0);
688         WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
689         CloseHandle(WOutDev[wDevID].hEvent);
690
691         close(WOutDev[wDevID].unixdev);
692         WOutDev[wDevID].unixdev = 0;
693         WOutDev[wDevID].dwFragmentSize = 0;
694         if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
695             WARN("can't notify client !\n");
696             ret = MMSYSERR_INVALPARAM;
697         }
698     }
699     return ret;
700 }
701
702 /**************************************************************************
703  *                              wodWrite                        [internal]
704  * 
705  */
706 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
707 {
708     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
709     
710     /* first, do the sanity checks... */
711     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
712         WARN("bad dev ID !\n");
713         return MMSYSERR_BADDEVICEID;
714     }
715     
716     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) 
717         return WAVERR_UNPREPARED;
718     
719     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) 
720         return WAVERR_STILLPLAYING;
721
722     TRACE("imhere[3-HEADER]\n");
723     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
724
725     return MMSYSERR_NOERROR;
726 }
727
728 /**************************************************************************
729  *                              wodPrepare                      [internal]
730  */
731 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
732 {
733     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
734     
735     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
736         WARN("bad device ID !\n");
737         return MMSYSERR_BADDEVICEID;
738     }
739     
740     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
741         return WAVERR_STILLPLAYING;
742     
743     lpWaveHdr->dwFlags |= WHDR_PREPARED;
744     lpWaveHdr->dwFlags &= ~WHDR_DONE;
745     return MMSYSERR_NOERROR;
746 }
747
748 /**************************************************************************
749  *                              wodUnprepare                    [internal]
750  */
751 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
752 {
753     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
754     
755     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
756         WARN("bad device ID !\n");
757         return MMSYSERR_BADDEVICEID;
758     }
759     
760     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
761         return WAVERR_STILLPLAYING;
762     
763     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
764     lpWaveHdr->dwFlags |= WHDR_DONE;
765     
766     return MMSYSERR_NOERROR;
767 }
768
769 /**************************************************************************
770  *                      wodPause                                [internal]
771  */
772 static DWORD wodPause(WORD wDevID)
773 {
774     TRACE("(%u);!\n", wDevID);
775     
776     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
777         WARN("bad device ID !\n");
778         return MMSYSERR_BADDEVICEID;
779     }
780     
781     TRACE("imhere[3-PAUSING]\n");
782     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
783     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
784     
785     return MMSYSERR_NOERROR;
786 }
787
788 /**************************************************************************
789  *                      wodRestart                              [internal]
790  */
791 static DWORD wodRestart(WORD wDevID)
792 {
793     TRACE("(%u);\n", wDevID);
794     
795     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
796         WARN("bad device ID !\n");
797         return MMSYSERR_BADDEVICEID;
798     }
799     
800     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
801         TRACE("imhere[3-RESTARTING]\n");
802         PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
803         WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
804     }
805     
806     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
807     /* FIXME: Myst crashes with this ... hmm -MM
808        if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
809        WARN("can't notify client !\n");
810        return MMSYSERR_INVALPARAM;
811        }
812     */
813     
814     return MMSYSERR_NOERROR;
815 }
816
817 /**************************************************************************
818  *                      wodReset                                [internal]
819  */
820 static DWORD wodReset(WORD wDevID)
821 {
822     TRACE("(%u);\n", wDevID);
823     
824     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
825         WARN("bad device ID !\n");
826         return MMSYSERR_BADDEVICEID;
827     }
828     
829     TRACE("imhere[3-RESET]\n");
830     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
831     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
832     
833     return MMSYSERR_NOERROR;
834 }
835
836
837 /**************************************************************************
838  *                              wodGetPosition                  [internal]
839  */
840 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
841 {
842     int         time;
843     DWORD       val;
844
845     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
846     
847     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
848         WARN("bad device ID !\n");
849         return MMSYSERR_BADDEVICEID;
850     }
851     
852     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
853
854     val = WOutDev[wDevID].dwPlayedTotal + WOutDev[wDevID].dwPlayed;
855
856     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n", 
857           lpTime->wType, WOutDev[wDevID].format.wBitsPerSample, 
858           WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels, 
859           WOutDev[wDevID].format.wf.nAvgBytesPerSec); 
860     TRACE("dwTotalPlayed=%lu\n", val);
861     
862     switch (lpTime->wType) {
863     case TIME_BYTES:
864         lpTime->u.cb = val;
865         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
866         break;
867     case TIME_SAMPLES:
868         lpTime->u.sample = val * 8 / WOutDev[wDevID].format.wBitsPerSample;
869         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
870         break;
871     case TIME_SMPTE:
872         time = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
873         lpTime->u.smpte.hour = time / 108000;
874         time -= lpTime->u.smpte.hour * 108000;
875         lpTime->u.smpte.min = time / 1800;
876         time -= lpTime->u.smpte.min * 1800;
877         lpTime->u.smpte.sec = time / 30;
878         time -= lpTime->u.smpte.sec * 30;
879         lpTime->u.smpte.frame = time;
880         lpTime->u.smpte.fps = 30;
881         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
882               lpTime->u.smpte.hour, lpTime->u.smpte.min,
883               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
884         break;
885     default:
886         FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
887         lpTime->wType = TIME_MS;
888     case TIME_MS:
889         lpTime->u.ms = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
890         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
891         break;
892     }
893     return MMSYSERR_NOERROR;
894 }
895
896 /**************************************************************************
897  *                              wodGetVolume                    [internal]
898  */
899 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
900 {
901     int         mixer;
902     int         volume;
903     DWORD       left, right;
904     
905     TRACE("(%u, %p);\n", wDevID, lpdwVol);
906     
907     if (lpdwVol == NULL) 
908         return MMSYSERR_NOTENABLED;
909     if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
910         WARN("mixer device not available !\n");
911         return MMSYSERR_NOTENABLED;
912     }
913     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
914         WARN("unable read mixer !\n");
915         return MMSYSERR_NOTENABLED;
916     }
917     close(mixer);
918     left = LOBYTE(volume);
919     right = HIBYTE(volume);
920     TRACE("left=%ld right=%ld !\n", left, right);
921     *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
922     return MMSYSERR_NOERROR;
923 }
924
925
926 /**************************************************************************
927  *                              wodSetVolume                    [internal]
928  */
929 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
930 {
931     int         mixer;
932     int         volume;
933     DWORD       left, right;
934
935     TRACE("(%u, %08lX);\n", wDevID, dwParam);
936
937     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
938     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
939     volume = left + (right << 8);
940     
941     if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
942         WARN("mixer device not available !\n");
943         return MMSYSERR_NOTENABLED;
944     }
945     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
946         WARN("unable set mixer !\n");
947         return MMSYSERR_NOTENABLED;
948     }
949     close(mixer);
950     return MMSYSERR_NOERROR;
951 }
952
953 /**************************************************************************
954  *                              wodGetNumDevs                   [internal]
955  */
956 static  DWORD   wodGetNumDevs(void)
957 {
958     DWORD       ret = 1;
959     
960     /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
961     int audio = open(SOUND_DEV, O_WRONLY, 0);
962     
963     if (audio == -1) {
964         if (errno != EBUSY)
965             ret = 0;
966     } else {
967         close(audio);
968     }
969     return ret;
970 }
971
972 /**************************************************************************
973  *                              OSS_wodMessage          [sample driver]
974  */
975 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
976                             DWORD dwParam1, DWORD dwParam2)
977 {
978     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
979           wDevID, wMsg, dwUser, dwParam1, dwParam2);
980     
981     switch (wMsg) {
982     case DRVM_INIT:
983     case DRVM_EXIT:
984     case DRVM_ENABLE:
985     case DRVM_DISABLE:
986         /* FIXME: Pretend this is supported */
987         return 0;
988     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
989     case WODM_CLOSE:            return wodClose         (wDevID);
990     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
991     case WODM_PAUSE:            return wodPause         (wDevID);
992     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
993     case WODM_BREAKLOOP:        return MMSYSERR_NOTSUPPORTED;
994     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
995     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
996     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSA)dwParam1,      dwParam2);
997     case WODM_GETNUMDEVS:       return wodGetNumDevs    ();
998     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
999     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
1000     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1001     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1002     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
1003     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
1004     case WODM_RESTART:          return wodRestart       (wDevID);
1005     case WODM_RESET:            return wodReset         (wDevID);
1006     default:
1007         FIXME("unknown message %d!\n", wMsg);
1008     }
1009     return MMSYSERR_NOTSUPPORTED;
1010 }
1011
1012 /*======================================================================*
1013  *                  Low level WAVE IN implemantation                    *
1014  *======================================================================*/
1015
1016 /**************************************************************************
1017  *                      widGetDevCaps                           [internal]
1018  */
1019 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1020 {
1021     int         audio, smplrate, samplesize=16, dsp_stereo=1, bytespersmpl;
1022     
1023     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1024     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1025     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1026     audio = open(SOUND_DEV, O_RDONLY, 0);
1027     if (audio == -1) return MMSYSERR_ALLOCATED ;
1028 #ifdef EMULATE_SB16
1029     lpCaps->wMid = 0x0002;
1030     lpCaps->wPid = 0x0004;
1031     strcpy(lpCaps->szPname, "SB16 Wave In");
1032 #else
1033     lpCaps->wMid = 0x00FF;      /* Manufac ID */
1034     lpCaps->wPid = 0x0001;      /* Product ID */
1035     strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
1036 #endif
1037     lpCaps->dwFormats = 0x00000000;
1038     lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
1039     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
1040     smplrate = 44100;
1041     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1042         lpCaps->dwFormats |= WAVE_FORMAT_4M08;
1043         if (lpCaps->wChannels > 1)
1044             lpCaps->dwFormats |= WAVE_FORMAT_4S08;
1045         if (bytespersmpl > 1) {
1046             lpCaps->dwFormats |= WAVE_FORMAT_4M16;
1047             if (lpCaps->wChannels > 1)
1048                 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1049         }
1050     }
1051     smplrate = 22050;
1052     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1053         lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1054         if (lpCaps->wChannels > 1)
1055             lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1056         if (bytespersmpl > 1) {
1057             lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1058             if (lpCaps->wChannels > 1)
1059                 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1060         }
1061     }
1062     smplrate = 11025;
1063     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1064         lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1065         if (lpCaps->wChannels > 1)
1066             lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1067         if (bytespersmpl > 1) {
1068             lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1069             if (lpCaps->wChannels > 1)
1070                 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1071         }
1072     }
1073     close(audio);
1074     TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
1075     return MMSYSERR_NOERROR;
1076 }
1077
1078 /**************************************************************************
1079  *                              widOpen                         [internal]
1080  */
1081 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1082 {
1083     int                 audio, abuf_size, smplrate, samplesize, dsp_stereo;
1084     LPWAVEFORMAT        lpFormat;
1085     
1086     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1087     if (lpDesc == NULL) {
1088         WARN("Invalid Parameter !\n");
1089         return MMSYSERR_INVALPARAM;
1090     }
1091     if (wDevID >= MAX_WAVEINDRV) {
1092         TRACE("MAX_WAVINDRV reached !\n");
1093         return MMSYSERR_ALLOCATED;
1094     }
1095
1096     /* only PCM format is supported so far... */
1097     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1098         lpDesc->lpFormat->nChannels == 0 ||
1099         lpDesc->lpFormat->nSamplesPerSec == 0) {
1100         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
1101              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1102              lpDesc->lpFormat->nSamplesPerSec);
1103         return WAVERR_BADFORMAT;
1104     }
1105
1106     if (dwFlags & WAVE_FORMAT_QUERY) {
1107         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
1108              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1109              lpDesc->lpFormat->nSamplesPerSec);
1110         return MMSYSERR_NOERROR;
1111     }
1112
1113     WInDev[wDevID].unixdev = 0;
1114     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1115     audio = open(SOUND_DEV, O_RDONLY, 0);
1116     if (audio == -1) {
1117         WARN("can't open !\n");
1118         return MMSYSERR_ALLOCATED;
1119     }
1120     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1121     if (abuf_size < 1024 || abuf_size > 65536) {
1122         if (abuf_size == -1)
1123             WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1124         else
1125             WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1126         return MMSYSERR_NOTENABLED;
1127     }
1128     WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1129
1130     if (WInDev[wDevID].lpQueueHdr) {
1131         HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1132         WInDev[wDevID].lpQueueHdr = NULL;
1133     }
1134     WInDev[wDevID].unixdev = audio;
1135     WInDev[wDevID].dwFragmentSize = abuf_size;
1136     WInDev[wDevID].dwTotalRecorded = 0;
1137     memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1138     lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat; 
1139
1140     memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1141     WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1142     if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1143     if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1144     if (WInDev[wDevID].format.wBitsPerSample == 0) {
1145         WInDev[wDevID].format.wBitsPerSample = 8 *
1146             (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1147              WInDev[wDevID].format.wf.nSamplesPerSec) /
1148             WInDev[wDevID].format.wf.nChannels;
1149     }
1150     samplesize = WInDev[wDevID].format.wBitsPerSample;
1151     smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1152     dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1153     IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1154     IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1155     IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1156     TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1157     TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1158     TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1159     TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec); 
1160     if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1161         WARN("can't notify client !\n");
1162         return MMSYSERR_INVALPARAM;
1163     }
1164     return MMSYSERR_NOERROR;
1165 }
1166
1167 /**************************************************************************
1168  *                              widClose                        [internal]
1169  */
1170 static DWORD widClose(WORD wDevID)
1171 {
1172     TRACE("(%u);\n", wDevID);
1173     if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1174     if (WInDev[wDevID].unixdev == 0) {
1175         WARN("can't close !\n");
1176         return MMSYSERR_NOTENABLED;
1177     }
1178     if (WInDev[wDevID].lpQueueHdr != NULL) {
1179         WARN("still buffers open !\n");
1180         return WAVERR_STILLPLAYING;
1181     }
1182     close(WInDev[wDevID].unixdev);
1183     WInDev[wDevID].unixdev = 0;
1184     WInDev[wDevID].dwFragmentSize = 0;
1185     if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1186         WARN("can't notify client !\n");
1187         return MMSYSERR_INVALPARAM;
1188     }
1189     return MMSYSERR_NOERROR;
1190 }
1191
1192 /**************************************************************************
1193  *                              widAddBuffer            [internal]
1194  */
1195 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1196 {
1197     int         count   = 1;
1198     LPWAVEHDR   lpWIHdr;
1199     
1200     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1201     if (WInDev[wDevID].unixdev == 0) {
1202         WARN("can't do it !\n");
1203         return MMSYSERR_NOTENABLED;
1204     }
1205     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1206         TRACE("never been prepared !\n");
1207         return WAVERR_UNPREPARED;
1208     }
1209     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1210         TRACE("header already in use !\n");
1211         return WAVERR_STILLPLAYING;
1212     }
1213     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1214     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1215     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1216     lpWaveHdr->dwBytesRecorded = 0;
1217     if (WInDev[wDevID].lpQueueHdr == NULL) {
1218         WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1219     } else {
1220         lpWIHdr = WInDev[wDevID].lpQueueHdr;
1221         while (lpWIHdr->lpNext != NULL) {
1222             lpWIHdr = lpWIHdr->lpNext;
1223             count++;
1224         }
1225         lpWIHdr->lpNext = lpWaveHdr;
1226         lpWaveHdr->lpNext = NULL;
1227         count++;
1228     }
1229     TRACE("buffer added ! (now %u in queue)\n", count);
1230     return MMSYSERR_NOERROR;
1231 }
1232
1233 /**************************************************************************
1234  *                              widPrepare                      [internal]
1235  */
1236 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1237 {
1238     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1239     if (WInDev[wDevID].unixdev == 0) {
1240         WARN("can't prepare !\n");
1241         return MMSYSERR_NOTENABLED;
1242     }
1243     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1244         return WAVERR_STILLPLAYING;
1245     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1246     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1247     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1248     lpWaveHdr->dwBytesRecorded = 0;
1249     TRACE("header prepared !\n");
1250     return MMSYSERR_NOERROR;
1251 }
1252
1253 /**************************************************************************
1254  *                              widUnprepare                    [internal]
1255  */
1256 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1257 {
1258     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1259     if (WInDev[wDevID].unixdev == 0) {
1260         WARN("can't unprepare !\n");
1261         return MMSYSERR_NOTENABLED;
1262     }
1263     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1264     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1265     lpWaveHdr->dwFlags |= WHDR_DONE;
1266     
1267     return MMSYSERR_NOERROR;
1268 }
1269
1270 /**************************************************************************
1271  *                      widStart                                [internal]
1272  */
1273 static DWORD widStart(WORD wDevID)
1274 {
1275     int         count   = 1;
1276     int         bytesRead;
1277     LPWAVEHDR   *lpWaveHdr;
1278     LPBYTE      lpData;
1279
1280     TRACE("(%u);\n", wDevID);
1281     if (WInDev[wDevID].unixdev == 0) {
1282         WARN("can't start recording !\n");
1283         return MMSYSERR_NOTENABLED;
1284     }
1285     
1286     lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1287     TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1288
1289     if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1290         TRACE("never been prepared !\n");
1291         return WAVERR_UNPREPARED;
1292     }
1293     
1294     while (*lpWaveHdr != NULL) {
1295         lpData = (*lpWaveHdr)->lpData;
1296         TRACE("recording buf#%u=%p size=%lu \n",
1297               count, lpData, (*lpWaveHdr)->dwBufferLength);
1298
1299         bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1300
1301         if (bytesRead == -1)
1302             perror("read from audio device");
1303
1304         TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1305         (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1306         WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1307         (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1308         (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1309         
1310         if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)*lpWaveHdr, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1311             WARN("can't notify client !\n");
1312             return MMSYSERR_INVALPARAM;
1313         }
1314
1315         /* removes the current block from the queue */
1316         *lpWaveHdr = (*lpWaveHdr)->lpNext;
1317         count++;
1318     }
1319     TRACE("end of recording !\n");
1320     return MMSYSERR_NOERROR;
1321 }
1322
1323 /**************************************************************************
1324  *                      widStop                                 [internal]
1325  */
1326 static DWORD widStop(WORD wDevID)
1327 {
1328     TRACE("(%u);\n", wDevID);
1329     if (WInDev[wDevID].unixdev == 0) {
1330         WARN("can't stop !\n");
1331         return MMSYSERR_NOTENABLED;
1332     }
1333     return MMSYSERR_NOERROR;
1334 }
1335
1336 /**************************************************************************
1337  *                      widReset                                [internal]
1338  */
1339 static DWORD widReset(WORD wDevID)
1340 {
1341     TRACE("(%u);\n", wDevID);
1342     if (WInDev[wDevID].unixdev == 0) {
1343         WARN("can't reset !\n");
1344         return MMSYSERR_NOTENABLED;
1345     }
1346     return MMSYSERR_NOERROR;
1347 }
1348
1349 /**************************************************************************
1350  *                              widGetPosition                  [internal]
1351  */
1352 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1353 {
1354     int         time;
1355     
1356     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1357     if (WInDev[wDevID].unixdev == 0) {
1358         WARN("can't get pos !\n");
1359         return MMSYSERR_NOTENABLED;
1360     }
1361     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1362     TRACE("wType=%04X !\n", lpTime->wType);
1363     TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample); 
1364     TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec); 
1365     TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels); 
1366     TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec); 
1367     switch (lpTime->wType) {
1368     case TIME_BYTES:
1369         lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1370         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1371         break;
1372     case TIME_SAMPLES:
1373         lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1374             WInDev[wDevID].format.wBitsPerSample;
1375         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1376         break;
1377     case TIME_SMPTE:
1378         time = WInDev[wDevID].dwTotalRecorded /
1379             (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1380         lpTime->u.smpte.hour = time / 108000;
1381         time -= lpTime->u.smpte.hour * 108000;
1382         lpTime->u.smpte.min = time / 1800;
1383         time -= lpTime->u.smpte.min * 1800;
1384         lpTime->u.smpte.sec = time / 30;
1385         time -= lpTime->u.smpte.sec * 30;
1386         lpTime->u.smpte.frame = time;
1387         lpTime->u.smpte.fps = 30;
1388         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1389               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1390               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1391         break;
1392     case TIME_MS:
1393         lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1394             (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1395         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1396         break;
1397     default:
1398         FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1399         lpTime->wType = TIME_MS;
1400     }
1401     return MMSYSERR_NOERROR;
1402 }
1403
1404 /**************************************************************************
1405  *                              OSS_widMessage                  [sample driver]
1406  */
1407 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1408                             DWORD dwParam1, DWORD dwParam2)
1409 {
1410     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1411           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1412
1413     switch (wMsg) {
1414     case DRVM_INIT:
1415     case DRVM_EXIT:
1416     case DRVM_ENABLE:
1417     case DRVM_DISABLE:
1418         /* FIXME: Pretend this is supported */
1419         return 0;
1420     case WIDM_OPEN:             return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1421     case WIDM_CLOSE:            return widClose(wDevID);
1422     case WIDM_ADDBUFFER:        return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1423     case WIDM_PREPARE:          return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1424     case WIDM_UNPREPARE:        return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1425     case WIDM_GETDEVCAPS:       return widGetDevCaps(wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
1426     case WIDM_GETNUMDEVS:       return wodGetNumDevs(); /* same number of devices in output as in input */
1427     case WIDM_GETPOS:           return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
1428     case WIDM_RESET:            return widReset(wDevID);
1429     case WIDM_START:            return widStart(wDevID);
1430     case WIDM_STOP:             return widStop(wDevID);
1431     default:
1432         FIXME("unknown message %u!\n", wMsg);
1433     }
1434     return MMSYSERR_NOTSUPPORTED;
1435 }
1436
1437 #else /* !HAVE_OSS */
1438
1439 /**************************************************************************
1440  *                              wodMessage                      [sample driver]
1441  */
1442 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1443                             DWORD dwParam1, DWORD dwParam2)
1444 {
1445     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1446     return MMSYSERR_NOTENABLED;
1447 }
1448
1449 /**************************************************************************
1450  *                              widMessage                      [sample driver]
1451  */
1452 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1453                             DWORD dwParam1, DWORD dwParam2)
1454 {
1455     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1456     return MMSYSERR_NOTENABLED;
1457 }
1458
1459 #endif /* HAVE_OSS */