- implementation of RtlReg* (read access), RtlEvent*, RtlSemaphore*,
[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         {
1124             WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1125             return MMSYSERR_NOTENABLED;
1126         }
1127         WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize %d!\n",abuf_size);
1128     }
1129     WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1130
1131     if (WInDev[wDevID].lpQueueHdr) {
1132         HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1133         WInDev[wDevID].lpQueueHdr = NULL;
1134     }
1135     WInDev[wDevID].unixdev = audio;
1136     WInDev[wDevID].dwFragmentSize = abuf_size;
1137     WInDev[wDevID].dwTotalRecorded = 0;
1138     memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1139     lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat; 
1140
1141     memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1142     WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1143     if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1144     if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1145     if (WInDev[wDevID].format.wBitsPerSample == 0) {
1146         WInDev[wDevID].format.wBitsPerSample = 8 *
1147             (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1148              WInDev[wDevID].format.wf.nSamplesPerSec) /
1149             WInDev[wDevID].format.wf.nChannels;
1150     }
1151     samplesize = WInDev[wDevID].format.wBitsPerSample;
1152     smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1153     dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1154     IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1155     IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1156     IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1157     TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1158     TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1159     TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1160     TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec); 
1161     if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1162         WARN("can't notify client !\n");
1163         return MMSYSERR_INVALPARAM;
1164     }
1165     return MMSYSERR_NOERROR;
1166 }
1167
1168 /**************************************************************************
1169  *                              widClose                        [internal]
1170  */
1171 static DWORD widClose(WORD wDevID)
1172 {
1173     TRACE("(%u);\n", wDevID);
1174     if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1175     if (WInDev[wDevID].unixdev == 0) {
1176         WARN("can't close !\n");
1177         return MMSYSERR_NOTENABLED;
1178     }
1179     if (WInDev[wDevID].lpQueueHdr != NULL) {
1180         WARN("still buffers open !\n");
1181         return WAVERR_STILLPLAYING;
1182     }
1183     close(WInDev[wDevID].unixdev);
1184     WInDev[wDevID].unixdev = 0;
1185     WInDev[wDevID].dwFragmentSize = 0;
1186     if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1187         WARN("can't notify client !\n");
1188         return MMSYSERR_INVALPARAM;
1189     }
1190     return MMSYSERR_NOERROR;
1191 }
1192
1193 /**************************************************************************
1194  *                              widAddBuffer            [internal]
1195  */
1196 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1197 {
1198     int         count   = 1;
1199     LPWAVEHDR   lpWIHdr;
1200     
1201     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1202     if (WInDev[wDevID].unixdev == 0) {
1203         WARN("can't do it !\n");
1204         return MMSYSERR_NOTENABLED;
1205     }
1206     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1207         TRACE("never been prepared !\n");
1208         return WAVERR_UNPREPARED;
1209     }
1210     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1211         TRACE("header already in use !\n");
1212         return WAVERR_STILLPLAYING;
1213     }
1214     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1215     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1216     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1217     lpWaveHdr->dwBytesRecorded = 0;
1218     lpWaveHdr->lpNext = NULL;
1219     if (WInDev[wDevID].lpQueueHdr == NULL) {
1220         WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1221     } else {
1222         lpWIHdr = WInDev[wDevID].lpQueueHdr;
1223         while (lpWIHdr->lpNext != NULL) {
1224             lpWIHdr = lpWIHdr->lpNext;
1225             count++;
1226         }
1227         lpWIHdr->lpNext = lpWaveHdr;
1228         count++;
1229     }
1230     TRACE("buffer added ! (now %u in queue)\n", count);
1231     return MMSYSERR_NOERROR;
1232 }
1233
1234 /**************************************************************************
1235  *                              widPrepare                      [internal]
1236  */
1237 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1238 {
1239     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1240     if (WInDev[wDevID].unixdev == 0) {
1241         WARN("can't prepare !\n");
1242         return MMSYSERR_NOTENABLED;
1243     }
1244     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1245         return WAVERR_STILLPLAYING;
1246     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1247     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1248     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1249     lpWaveHdr->dwBytesRecorded = 0;
1250     TRACE("header prepared !\n");
1251     return MMSYSERR_NOERROR;
1252 }
1253
1254 /**************************************************************************
1255  *                              widUnprepare                    [internal]
1256  */
1257 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1258 {
1259     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1260     if (WInDev[wDevID].unixdev == 0) {
1261         WARN("can't unprepare !\n");
1262         return MMSYSERR_NOTENABLED;
1263     }
1264     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1265     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1266     lpWaveHdr->dwFlags |= WHDR_DONE;
1267     
1268     return MMSYSERR_NOERROR;
1269 }
1270
1271 /**************************************************************************
1272  *                      widStart                                [internal]
1273  */
1274 static DWORD widStart(WORD wDevID)
1275 {
1276     int         count   = 1;
1277     int         bytesRead;
1278     LPWAVEHDR   *lpWaveHdr;
1279     LPBYTE      lpData;
1280
1281     TRACE("(%u);\n", wDevID);
1282     if (WInDev[wDevID].unixdev == 0) {
1283         WARN("can't start recording !\n");
1284         return MMSYSERR_NOTENABLED;
1285     }
1286     
1287     lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1288     TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1289
1290     if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1291         TRACE("never been prepared !\n");
1292         return WAVERR_UNPREPARED;
1293     }
1294     
1295     while (*lpWaveHdr != NULL) {
1296         lpData = (*lpWaveHdr)->lpData;
1297         TRACE("recording buf#%u=%p size=%lu \n",
1298               count, lpData, (*lpWaveHdr)->dwBufferLength);
1299
1300         bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1301
1302         if (bytesRead == -1)
1303             perror("read from audio device");
1304
1305         TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1306         (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1307         WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1308         (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1309         (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1310         
1311         if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)*lpWaveHdr, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1312             WARN("can't notify client !\n");
1313             return MMSYSERR_INVALPARAM;
1314         }
1315
1316         /* removes the current block from the queue */
1317         *lpWaveHdr = (*lpWaveHdr)->lpNext;
1318         count++;
1319     }
1320     TRACE("end of recording !\n");
1321     return MMSYSERR_NOERROR;
1322 }
1323
1324 /**************************************************************************
1325  *                      widStop                                 [internal]
1326  */
1327 static DWORD widStop(WORD wDevID)
1328 {
1329     TRACE("(%u);\n", wDevID);
1330     if (WInDev[wDevID].unixdev == 0) {
1331         WARN("can't stop !\n");
1332         return MMSYSERR_NOTENABLED;
1333     }
1334     return MMSYSERR_NOERROR;
1335 }
1336
1337 /**************************************************************************
1338  *                      widReset                                [internal]
1339  */
1340 static DWORD widReset(WORD wDevID)
1341 {
1342     TRACE("(%u);\n", wDevID);
1343     if (WInDev[wDevID].unixdev == 0) {
1344         WARN("can't reset !\n");
1345         return MMSYSERR_NOTENABLED;
1346     }
1347     return MMSYSERR_NOERROR;
1348 }
1349
1350 /**************************************************************************
1351  *                              widGetPosition                  [internal]
1352  */
1353 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1354 {
1355     int         time;
1356     
1357     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1358     if (WInDev[wDevID].unixdev == 0) {
1359         WARN("can't get pos !\n");
1360         return MMSYSERR_NOTENABLED;
1361     }
1362     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1363     TRACE("wType=%04X !\n", lpTime->wType);
1364     TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample); 
1365     TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec); 
1366     TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels); 
1367     TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec); 
1368     switch (lpTime->wType) {
1369     case TIME_BYTES:
1370         lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1371         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1372         break;
1373     case TIME_SAMPLES:
1374         lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1375             WInDev[wDevID].format.wBitsPerSample;
1376         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1377         break;
1378     case TIME_SMPTE:
1379         time = WInDev[wDevID].dwTotalRecorded /
1380             (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1381         lpTime->u.smpte.hour = time / 108000;
1382         time -= lpTime->u.smpte.hour * 108000;
1383         lpTime->u.smpte.min = time / 1800;
1384         time -= lpTime->u.smpte.min * 1800;
1385         lpTime->u.smpte.sec = time / 30;
1386         time -= lpTime->u.smpte.sec * 30;
1387         lpTime->u.smpte.frame = time;
1388         lpTime->u.smpte.fps = 30;
1389         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1390               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1391               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1392         break;
1393     case TIME_MS:
1394         lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1395             (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1396         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1397         break;
1398     default:
1399         FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1400         lpTime->wType = TIME_MS;
1401     }
1402     return MMSYSERR_NOERROR;
1403 }
1404
1405 /**************************************************************************
1406  *                              OSS_widMessage                  [sample driver]
1407  */
1408 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1409                             DWORD dwParam1, DWORD dwParam2)
1410 {
1411     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1412           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1413
1414     switch (wMsg) {
1415     case DRVM_INIT:
1416     case DRVM_EXIT:
1417     case DRVM_ENABLE:
1418     case DRVM_DISABLE:
1419         /* FIXME: Pretend this is supported */
1420         return 0;
1421     case WIDM_OPEN:             return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1422     case WIDM_CLOSE:            return widClose(wDevID);
1423     case WIDM_ADDBUFFER:        return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1424     case WIDM_PREPARE:          return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1425     case WIDM_UNPREPARE:        return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1426     case WIDM_GETDEVCAPS:       return widGetDevCaps(wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
1427     case WIDM_GETNUMDEVS:       return wodGetNumDevs(); /* same number of devices in output as in input */
1428     case WIDM_GETPOS:           return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
1429     case WIDM_RESET:            return widReset(wDevID);
1430     case WIDM_START:            return widStart(wDevID);
1431     case WIDM_STOP:             return widStop(wDevID);
1432     default:
1433         FIXME("unknown message %u!\n", wMsg);
1434     }
1435     return MMSYSERR_NOTSUPPORTED;
1436 }
1437
1438 #else /* !HAVE_OSS */
1439
1440 /**************************************************************************
1441  *                              wodMessage                      [sample driver]
1442  */
1443 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1444                             DWORD dwParam1, DWORD dwParam2)
1445 {
1446     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1447     return MMSYSERR_NOTENABLED;
1448 }
1449
1450 /**************************************************************************
1451  *                              widMessage                      [sample driver]
1452  */
1453 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1454                             DWORD dwParam1, DWORD dwParam2)
1455 {
1456     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1457     return MMSYSERR_NOTENABLED;
1458 }
1459
1460 #endif /* HAVE_OSS */