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