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