Merged msacm and msacm32 dlls.
[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/waveIn)
7  *           2000 Eric Pouech (loops in waveOut)
8  */
9 /*
10  * FIXME:
11  *      pause in waveOut does not work correctly
12  *      full duplex (in/out) is not working (device is opened twice for Out 
13  *      and In) (OSS is known for its poor duplex capabilities, alsa is
14  *      better)
15  */
16
17 /*#define EMULATE_SB16*/
18
19 #include "config.h"
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28 #ifdef HAVE_SYS_MMAN_H
29 # include <sys/mman.h>
30 #endif
31 #include "windef.h"
32 #include "wingdi.h"
33 #include "winerror.h"
34 #include "wine/winuser16.h"
35 #include "driver.h"
36 #include "mmddk.h"
37 #include "dsound.h"
38 #include "dsdriver.h"
39 #include "oss.h"
40 #include "heap.h"
41 #include "ldt.h"
42 #include "debugtools.h"
43
44 DEFAULT_DEBUG_CHANNEL(wave);
45
46 /* Allow 1% deviation for sample rates (some ES137x cards) */
47 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
48
49 #ifdef HAVE_OSS
50
51 #define SOUND_DEV "/dev/dsp"
52 #define MIXER_DEV "/dev/mixer"
53
54 #define MAX_WAVEOUTDRV  (1)
55 #define MAX_WAVEINDRV   (1)
56
57 /* state diagram for waveOut writing:
58  *
59  * +---------+-------------+---------------+---------------------------------+
60  * |  state  |  function   |     event     |            new state            |
61  * +---------+-------------+---------------+---------------------------------+
62  * |         | open()      |               | STOPPED                         |
63  * | PAUSED  | write()     |               | PAUSED                          |
64  * | STOPPED | write()     | <thrd create> | PLAYING                         |
65  * | PLAYING | write()     | HEADER        | PLAYING                         |
66  * | (other) | write()     | <error>       |                                 |
67  * | (any)   | pause()     | PAUSING       | PAUSED                          |
68  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
69  * | (any)   | reset()     | RESETTING     | STOPPED                         |
70  * | (any)   | close()     | CLOSING       | CLOSED                          |
71  * +---------+-------------+---------------+---------------------------------+
72  */
73
74 /* states of the playing device */
75 #define WINE_WS_PLAYING         0
76 #define WINE_WS_PAUSED          1
77 #define WINE_WS_STOPPED         2
78 #define WINE_WS_CLOSED          3
79
80 /* events to be send to device */
81 #define WINE_WM_PAUSING         (WM_USER + 1)
82 #define WINE_WM_RESTARTING      (WM_USER + 2)
83 #define WINE_WM_RESETTING       (WM_USER + 3)
84 #define WINE_WM_CLOSING         (WM_USER + 4)
85 #define WINE_WM_HEADER          (WM_USER + 5)
86
87 typedef struct {
88     int                         unixdev;
89     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
90     DWORD                       dwFragmentSize;         /* size of OSS buffer fragment */
91     WAVEOPENDESC                waveDesc;
92     WORD                        wFlags;
93     PCMWAVEFORMAT               format;
94     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
95     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
96     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
97     
98     DWORD                       dwLastFragDone;         /* time in ms, when last played fragment will be actually played */
99     DWORD                       dwPlayedTotal;          /* number of bytes played since opening */
100
101     /* info on current lpQueueHdr->lpWaveHdr */
102     DWORD                       dwOffCurrHdr;           /* offset in lpPlayPtr->lpData for fragments */
103     DWORD                       dwRemain;               /* number of bytes to write to end the current fragment  */
104
105     /* synchronization stuff */
106     HANDLE                      hThread;
107     DWORD                       dwThreadID;
108     HANDLE                      hEvent;
109     WAVEOUTCAPSA                caps;
110
111     /* DirectSound stuff */
112     LPBYTE                      mapping;
113     DWORD                       maplen;
114 } WINE_WAVEOUT;
115
116 typedef struct {
117     int                         unixdev;
118     volatile int                state;
119     DWORD                       dwFragmentSize;         /* OpenSound '/dev/dsp' give us that size */
120     WAVEOPENDESC                waveDesc;
121     WORD                        wFlags;
122     PCMWAVEFORMAT               format;
123     LPWAVEHDR                   lpQueuePtr;
124     DWORD                       dwTotalRecorded;
125     WAVEINCAPSA                 caps;
126
127     /* synchronization stuff */
128     HANDLE                      hThread;
129     DWORD                       dwThreadID;
130     HANDLE                      hEvent;
131 } WINE_WAVEIN;
132
133 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
134 static WINE_WAVEIN      WInDev    [MAX_WAVEINDRV ];
135
136 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
137
138 /*======================================================================*
139  *                  Low level WAVE implemantation                       *
140  *======================================================================*/
141
142 LONG OSS_WaveInit(void)
143 {
144     int         audio;
145     int         smplrate;
146     int         samplesize = 16;
147     int         dsp_stereo = 1;
148     int         bytespersmpl;
149     int         caps;
150     int         mask;
151
152     /* start with output device */
153
154     /* FIXME: only one device is supported */
155     memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps));
156
157     if (access(SOUND_DEV,0) != 0 ||
158         (audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0)) == -1) {
159         TRACE("Couldn't open out %s (%s)\n", SOUND_DEV, strerror(errno));
160         return -1;
161     }
162
163     ioctl(audio, SNDCTL_DSP_RESET, 0);
164
165     /* FIXME: some programs compare this string against the content of the registry
166      * for MM drivers. The name have to match in order the program to work 
167      * (e.g. MS win9x mplayer.exe)
168      */
169 #ifdef EMULATE_SB16
170     WOutDev[0].caps.wMid = 0x0002;
171     WOutDev[0].caps.wPid = 0x0104;
172     strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");
173 #else
174     WOutDev[0].caps.wMid = 0x00FF;      /* Manufac ID */
175     WOutDev[0].caps.wPid = 0x0001;      /* Product ID */
176     /*    strcpy(WOutDev[0].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
177     strcpy(WOutDev[0].caps.szPname, "CS4236/37/38");
178 #endif
179     WOutDev[0].caps.vDriverVersion = 0x0100;
180     WOutDev[0].caps.dwFormats = 0x00000000;
181     WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;
182     
183     IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
184     TRACE("OSS dsp out mask=%08x\n", mask);
185
186     /* First bytespersampl, then stereo */
187     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
188     
189     WOutDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
190     if (WOutDev[0].caps.wChannels > 1) WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;
191     
192     smplrate = 44100;
193     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
194         if (mask & AFMT_U8) {
195             WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
196             if (WOutDev[0].caps.wChannels > 1)
197                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
198         }
199         if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
200             WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
201             if (WOutDev[0].caps.wChannels > 1)
202                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
203         }
204     }
205     smplrate = 22050;
206     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
207         if (mask & AFMT_U8) {
208             WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
209             if (WOutDev[0].caps.wChannels > 1)
210                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
211         }
212         if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
213             WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
214             if (WOutDev[0].caps.wChannels > 1)
215                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
216         }
217     }
218     smplrate = 11025;
219     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
220         if (mask & AFMT_U8) {
221             WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
222             if (WOutDev[0].caps.wChannels > 1)
223                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
224         }
225         if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
226             WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
227             if (WOutDev[0].caps.wChannels > 1)
228                 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
229         }
230     }
231     if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
232         TRACE("OSS dsp out caps=%08X\n", caps);
233         if ((caps & DSP_CAP_REALTIME) && !(caps & DSP_CAP_BATCH)) {
234             WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
235
236             /* well, might as well use the DirectSound cap flag for something */
237             if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP))
238                 WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND;
239         }
240     }
241     close(audio);
242     TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
243           WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport);
244
245     /* then do input device */
246     samplesize = 16;
247     dsp_stereo = 1;
248     
249     if (access(SOUND_DEV,0) != 0 ||
250         (audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0)) == -1) {
251         TRACE("Couldn't open in %s (%s)\n", SOUND_DEV, strerror(errno));
252         return -1;
253     }
254
255     ioctl(audio, SNDCTL_DSP_RESET, 0);
256
257 #ifdef EMULATE_SB16
258     WInDev[0].caps.wMid = 0x0002;
259     WInDev[0].caps.wPid = 0x0004;
260     strcpy(WInDev[0].caps.szPname, "SB16 Wave In");
261 #else
262     WInDev[0].caps.wMid = 0x00FF;       /* Manufac ID */
263     WInDev[0].caps.wPid = 0x0001;       /* Product ID */
264     strcpy(WInDev[0].caps.szPname, "OpenSoundSystem WAVIN Driver");
265 #endif
266     WInDev[0].caps.dwFormats = 0x00000000;
267     WInDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
268
269     IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
270     TRACE("OSS in dsp mask=%08x\n", mask);
271
272     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
273     smplrate = 44100;
274     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
275         if (mask & AFMT_U8) {
276             WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
277             if (WInDev[0].caps.wChannels > 1)
278                 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
279         }
280         if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
281             WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
282             if (WInDev[0].caps.wChannels > 1)
283                 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
284         }
285     }
286     smplrate = 22050;
287     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
288         if (mask & AFMT_U8) {
289             WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
290             if (WInDev[0].caps.wChannels > 1)
291                 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
292         }
293         if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
294             WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
295             if (WInDev[0].caps.wChannels > 1)
296                 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
297         }
298     }
299     smplrate = 11025;
300     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
301         if (mask & AFMT_U8) {
302             WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
303             if (WInDev[0].caps.wChannels > 1)
304                 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
305         }
306         if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
307             WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
308             if (WInDev[0].caps.wChannels > 1)
309                 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
310         }
311     }
312     close(audio);
313     TRACE("in dwFormats = %08lX\n", WInDev[0].caps.dwFormats);
314
315     return 0;
316 }
317
318 /**************************************************************************
319  *                      OSS_NotifyClient                        [internal]
320  */
321 static DWORD OSS_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, 
322                               DWORD dwParam2)
323 {
324     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
325     
326     switch (wMsg) {
327     case WOM_OPEN:
328     case WOM_CLOSE:
329     case WOM_DONE:
330         if (wDevID >= MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
331         
332         if (WOutDev[wDevID].wFlags != DCB_NULL && 
333             !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback, 
334                             WOutDev[wDevID].wFlags, 
335                             WOutDev[wDevID].waveDesc.hWave, 
336                             wMsg, 
337                             WOutDev[wDevID].waveDesc.dwInstance, 
338                             dwParam1, 
339                             dwParam2)) {
340             WARN("can't notify client !\n");
341             return MMSYSERR_NOERROR;
342         }
343         break;
344         
345     case WIM_OPEN:
346     case WIM_CLOSE:
347     case WIM_DATA:
348         if (wDevID >= MAX_WAVEINDRV) return MCIERR_INTERNAL;
349         
350         if (WInDev[wDevID].wFlags != DCB_NULL && 
351             !DriverCallback(WInDev[wDevID].waveDesc.dwCallback, 
352                             WInDev[wDevID].wFlags, 
353                             WInDev[wDevID].waveDesc.hWave, 
354                             wMsg, 
355                             WInDev[wDevID].waveDesc.dwInstance, 
356                             dwParam1, 
357                             dwParam2)) {
358             WARN("can't notify client !\n");
359             return MMSYSERR_NOERROR;
360         }
361         break;
362     default:
363         FIXME("Unknown CB message %u\n", wMsg);
364         break;
365     }
366     return 0;
367 }
368
369 /*======================================================================*
370  *                  Low level WAVE OUT implemantation                   *
371  *======================================================================*/
372
373 /**************************************************************************
374  *                              wodPlayer_WriteFragments        [internal]
375  *
376  * wodPlayer helper. Writes as many fragments it can to unixdev.
377  * Returns TRUE in case of buffer underrun.
378  */
379 static  BOOL    wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
380 {
381     LPWAVEHDR           lpWaveHdr;
382     LPBYTE              lpData;
383     int                 count;
384     audio_buf_info      info;
385
386     for (;;) {
387         if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
388             ERR("ioctl failed (%s)\n", strerror(errno));
389             return FALSE;
390         }
391         
392         TRACE("Fragments %d/%d\n", info.fragments, info.fragstotal);
393
394         if (!info.fragments)    /* output queue is full, wait a bit */
395             return FALSE;
396
397         lpWaveHdr = wwo->lpPlayPtr;
398         if (!lpWaveHdr) {
399             if (wwo->dwRemain > 0 &&            /* still data to send to complete current fragment */
400                 wwo->dwLastFragDone &&          /* first fragment has been played */
401                 info.fragments + 2 > info.fragstotal) {   /* done with all waveOutWrite()' fragments */
402                 /* FIXME: should do better handling here */
403                 TRACE("Oooch, buffer underrun !\n");
404                 return TRUE; /* force resetting of waveOut device */
405             }
406             return FALSE;       /* wait a bit */
407         }
408         
409         if (wwo->dwOffCurrHdr == 0) {
410             TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
411             if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
412                 if (wwo->lpLoopPtr) {
413                     WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
414                 } else {
415                     wwo->lpLoopPtr = lpWaveHdr;
416                 }
417             }
418         }
419         
420         lpData = lpWaveHdr->lpData;
421
422         /* finish current wave hdr ? */
423         if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) { 
424             DWORD       toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
425             
426             /* write end of current wave hdr */
427             count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
428             TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
429             
430             if (count > 0 || toWrite == 0) {
431                 DWORD   tc = GetTickCount();
432
433                 if (wwo->dwLastFragDone /* + guard time ?? */ < tc) 
434                     wwo->dwLastFragDone = tc;
435                 wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec;
436
437                 lpWaveHdr->reserved = wwo->dwLastFragDone;
438                 TRACE("Tagging hdr %p with %08lx\n", lpWaveHdr, wwo->dwLastFragDone);
439
440                 /* WAVEHDR written, go to next one */
441                 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
442                     if (--wwo->lpLoopPtr->dwLoops > 0) {
443                         wwo->lpPlayPtr = wwo->lpLoopPtr;
444                     } else {
445                         /* last one played */
446                         if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
447                             FIXME("Correctly handled case ? (ending loop buffer also starts a new loop\n");
448                             /* shall we consider the END flag for the closing loop or for
449                              * the opening one or for both ???
450                              * code assumes for closing loop only
451                              */
452                             wwo->lpLoopPtr = lpWaveHdr;
453                         } else {
454                             wwo->lpLoopPtr = NULL;
455                         }
456                         wwo->lpPlayPtr = lpWaveHdr->lpNext;
457                     }
458                 } else {
459                     wwo->lpPlayPtr = lpWaveHdr->lpNext;
460                 }
461                 wwo->dwOffCurrHdr = 0;
462                 if ((wwo->dwRemain -= count) == 0) {
463                     wwo->dwRemain = wwo->dwFragmentSize;
464                 }
465             }
466             continue; /* try to go to use next wavehdr */
467         }  else {
468             count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
469             TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
470             if (count > 0) {
471                 DWORD   tc = GetTickCount();
472
473                 if (wwo->dwLastFragDone /* + guard time ?? */ < tc) 
474                     wwo->dwLastFragDone = tc;
475                 wwo->dwLastFragDone += (wwo->dwRemain * 1000) / wwo->format.wf.nAvgBytesPerSec;
476
477                 TRACE("Tagging frag with %08lx\n", wwo->dwLastFragDone);
478
479                 wwo->dwOffCurrHdr += count;
480                 wwo->dwRemain = wwo->dwFragmentSize;
481             }
482         }
483     }
484 }
485
486 /**************************************************************************
487  *                              wodPlayer_Notify                [internal]
488  *
489  * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
490  * have been played (actually to speaker, not to unixdev fd).
491  */
492 static  void    wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
493 {
494     LPWAVEHDR           lpWaveHdr;
495     DWORD               tc = GetTickCount();
496
497     while (wwo->lpQueuePtr && 
498            (force || 
499             (wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) {
500         lpWaveHdr = wwo->lpQueuePtr;
501             
502         if (lpWaveHdr->reserved > tc && !force) break;
503
504         wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength;
505         wwo->lpQueuePtr = lpWaveHdr->lpNext;
506
507         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
508         lpWaveHdr->dwFlags |= WHDR_DONE;
509
510         TRACE("Notifying client with %p\n", lpWaveHdr);
511         if (OSS_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
512             WARN("can't notify client !\n");
513         }
514     }
515 }
516
517 /**************************************************************************
518  *                              wodPlayer_Reset                 [internal]
519  *
520  * wodPlayer helper. Resets current output stream.
521  */
522 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
523 {
524     /* updates current notify list */
525     wodPlayer_Notify(wwo, uDevID, FALSE);
526
527     /* flush all possible output */
528     if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
529         perror("ioctl SNDCTL_DSP_RESET");
530         wwo->hThread = 0;
531         wwo->state = WINE_WS_STOPPED;
532         ExitThread(-1);
533     }
534
535     wwo->dwOffCurrHdr = 0;
536     wwo->dwRemain = wwo->dwFragmentSize;
537     if (reset) {
538         /* empty notify list */
539         wodPlayer_Notify(wwo, uDevID, TRUE);
540
541         wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
542         wwo->state = WINE_WS_STOPPED;
543         wwo->dwPlayedTotal = 0;
544     } else {
545         /* FIXME: this is not accurate when looping, but can be do better ? */
546         wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr;
547         wwo->state = WINE_WS_PAUSED;
548     }
549 }
550
551 /**************************************************************************
552  *                              wodPlayer                       [internal]
553  */
554 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
555 {
556     WORD                uDevID = (DWORD)pmt;
557     WINE_WAVEOUT*       wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
558     WAVEHDR*            lpWaveHdr;
559     DWORD               dwSleepTime;
560     MSG                 msg;
561
562     PeekMessageA(&msg, 0, 0, 0, 0);
563     wwo->state = WINE_WS_STOPPED;
564
565     wwo->dwLastFragDone = 0;
566     wwo->dwOffCurrHdr = 0;
567     wwo->dwRemain = wwo->dwFragmentSize;
568     wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
569     wwo->dwPlayedTotal = 0;
570
571     TRACE("imhere[0]\n");
572     SetEvent(wwo->hEvent);
573
574     /* make sleep time to be # of ms to output a fragment */
575     dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
576
577     for (;;) {
578         /* wait for dwSleepTime or an event in thread's queue */
579         /* FIXME: could improve wait time depending on queue state,
580          * ie, number of queued fragments
581          */
582         TRACE("imhere[1]\n");
583         MsgWaitForMultipleObjects(0, NULL, FALSE, 
584                                   (wwo->state == WINE_WS_PLAYING) ? 
585                                      2 * dwSleepTime : /*INFINITE*/100, 
586                                   QS_POSTMESSAGE);
587         TRACE("imhere[2] (q=%p p=%p)\n", wwo->lpQueuePtr, wwo->lpPlayPtr);
588         wodPlayer_Notify(wwo, uDevID, FALSE);
589         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
590             switch (msg.message) {
591             case WINE_WM_PAUSING:
592                 wodPlayer_Reset(wwo, uDevID, FALSE);
593                 wwo->state = WINE_WS_PAUSED;
594                 SetEvent(wwo->hEvent);
595                 break;
596             case WINE_WM_RESTARTING:
597                 wwo->state = WINE_WS_PLAYING;
598                 SetEvent(wwo->hEvent);
599                 break;
600             case WINE_WM_HEADER:
601                 lpWaveHdr = (LPWAVEHDR)msg.lParam;
602                 
603                 /* insert buffer at the end of queue */
604                 {
605                     LPWAVEHDR*  wh;
606                     for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
607                     *wh = lpWaveHdr;
608                 }
609                 if (!wwo->lpPlayPtr) wwo->lpPlayPtr = lpWaveHdr;
610                 if (wwo->state == WINE_WS_STOPPED)
611                     wwo->state = WINE_WS_PLAYING;
612                 break;
613             case WINE_WM_RESETTING:
614                 wodPlayer_Reset(wwo, uDevID, TRUE);
615                 SetEvent(wwo->hEvent);
616                 break;
617             case WINE_WM_CLOSING:
618                 /* sanity check: this should not happen since the device must have been reset before */
619                 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
620                 wwo->hThread = 0;
621                 wwo->state = WINE_WS_CLOSED;
622                 SetEvent(wwo->hEvent);
623                 ExitThread(0);
624                 /* shouldn't go here */
625             default:
626                 FIXME("unknown message %d\n", msg.message);
627                 break;
628             }
629         }
630         if (wwo->state == WINE_WS_PLAYING) {
631             wodPlayer_WriteFragments(wwo);
632         }
633         wodPlayer_Notify(wwo, uDevID, FALSE);
634     }
635     ExitThread(0);
636     /* just for not generating compilation warnings... should never be executed */
637     return 0; 
638 }
639
640 /**************************************************************************
641  *                      wodGetDevCaps                           [internal]
642  */
643 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
644 {
645     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
646     
647     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
648     
649     if (wDevID >= MAX_WAVEOUTDRV) {
650         TRACE("MAX_WAVOUTDRV reached !\n");
651         return MMSYSERR_BADDEVICEID;
652     }
653
654     memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
655     return MMSYSERR_NOERROR;
656 }
657
658 /**************************************************************************
659  *                              wodOpen                         [internal]
660  */
661 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
662 {
663     int                 audio;
664     int                 format;
665     int                 sample_rate;
666     int                 dsp_stereo;
667     int                 audio_fragment;
668     int                 fragment_size;
669     WINE_WAVEOUT*       wwo;
670
671     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
672     if (lpDesc == NULL) {
673         WARN("Invalid Parameter !\n");
674         return MMSYSERR_INVALPARAM;
675     }
676     if (wDevID >= MAX_WAVEOUTDRV) {
677         TRACE("MAX_WAVOUTDRV reached !\n");
678         return MMSYSERR_BADDEVICEID;
679     }
680
681     /* only PCM format is supported so far... */
682     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
683         lpDesc->lpFormat->nChannels == 0 ||
684         lpDesc->lpFormat->nSamplesPerSec == 0) {
685         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
686              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
687              lpDesc->lpFormat->nSamplesPerSec);
688         return WAVERR_BADFORMAT;
689     }
690
691     if (dwFlags & WAVE_FORMAT_QUERY) {
692         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
693              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
694              lpDesc->lpFormat->nSamplesPerSec);
695         return MMSYSERR_NOERROR;
696     }
697
698     wwo = &WOutDev[wDevID];
699
700     if (dwFlags & WAVE_DIRECTSOUND) {
701         if (!(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
702             /* not supported, ignore it */
703             dwFlags &= ~WAVE_DIRECTSOUND;
704     }
705
706     
707     wwo->unixdev = 0;
708     if (access(SOUND_DEV, 0) != 0) 
709         return MMSYSERR_NOTENABLED;
710     if (dwFlags & WAVE_DIRECTSOUND)
711         /* we want to be able to mmap() the device, which means it must be opened readable,
712          * otherwise mmap() will fail (at least under Linux) */
713         audio = open(SOUND_DEV, O_RDWR|O_NDELAY, 0);
714     else
715         audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
716     if (audio == -1) {
717         WARN("can't open (%s)!\n", strerror(errno));
718         return MMSYSERR_ALLOCATED;
719     }
720
721     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
722     
723     memcpy(&wwo->waveDesc, lpDesc,              sizeof(WAVEOPENDESC));
724     memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
725     
726     if (WOutDev[wDevID].format.wBitsPerSample == 0) {
727         WOutDev[wDevID].format.wBitsPerSample = 8 *
728             (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
729              WOutDev[wDevID].format.wf.nSamplesPerSec) /
730             WOutDev[wDevID].format.wf.nChannels;
731     }
732     
733     if (dwFlags & WAVE_DIRECTSOUND) {
734         /* with DirectSound, fragments are irrelevant, but a large buffer isn't...
735          * so let's choose a full 64KB for DirectSound */
736         audio_fragment = 0x0020000B;
737     } else {
738         /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
739          * thus leading to 46ms per fragment, and a turnaround time of 185ms
740          */
741         /* 2^10=1024 bytes per fragment, 16 fragments max */
742         audio_fragment = 0x000F000A;
743     }
744     sample_rate = wwo->format.wf.nSamplesPerSec;
745     dsp_stereo = (wwo->format.wf.nChannels > 1) ? 1 : 0;
746     format = (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8;
747
748     IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
749     /* First size and stereo then samplerate */
750     IOCTL(audio, SNDCTL_DSP_SETFMT, format);
751     IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
752     IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
753
754     /* paranoid checks */
755     if (format != ((wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
756         ERR("Can't set format to %d (%d)\n", 
757             (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
758     if (dsp_stereo != (wwo->format.wf.nChannels > 1) ? 1 : 0) 
759         ERR("Can't set stereo to %u (%d)\n", 
760             (wwo->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
761     if (!NEAR_MATCH(sample_rate,wwo->format.wf.nSamplesPerSec))
762         ERR("Can't set sample_rate to %lu (%d)\n", 
763             wwo->format.wf.nSamplesPerSec, sample_rate);
764
765     /* even if we set fragment size above, read it again, just in case */
766     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
767
768     wwo->unixdev = audio;
769     wwo->dwFragmentSize = fragment_size;
770
771     if (!(dwFlags & WAVE_DIRECTSOUND)) {
772         wwo->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
773         wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
774         WaitForSingleObject(wwo->hEvent, INFINITE);
775     } else {
776         wwo->hEvent = INVALID_HANDLE_VALUE;
777         wwo->hThread = INVALID_HANDLE_VALUE;
778         wwo->dwThreadID = 0;
779     }
780
781     TRACE("fd=%d fragmentSize=%ld\n", 
782           wwo->unixdev, wwo->dwFragmentSize);
783     if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
784         ERR("Fragment doesn't contain an integral number of data blocks\n");
785
786     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n", 
787           wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec, 
788           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
789           wwo->format.wf.nBlockAlign);
790     
791     if (OSS_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
792         WARN("can't notify client !\n");
793         return MMSYSERR_INVALPARAM;
794     }
795     return MMSYSERR_NOERROR;
796 }
797
798 /**************************************************************************
799  *                              wodClose                        [internal]
800  */
801 static DWORD wodClose(WORD wDevID)
802 {
803     DWORD               ret = MMSYSERR_NOERROR;
804     WINE_WAVEOUT*       wwo;
805
806     TRACE("(%u);\n", wDevID);
807     
808     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
809         WARN("bad device ID !\n");
810         return MMSYSERR_BADDEVICEID;
811     }
812     
813     wwo = &WOutDev[wDevID];
814     if (wwo->lpQueuePtr) {
815         WARN("buffers still playing !\n");
816         ret = WAVERR_STILLPLAYING;
817     } else {
818         TRACE("imhere[3-close]\n");
819         if (wwo->hEvent != INVALID_HANDLE_VALUE) {
820             PostThreadMessageA(wwo->dwThreadID, WINE_WM_CLOSING, 0, 0);
821             WaitForSingleObject(wwo->hEvent, INFINITE);
822             CloseHandle(wwo->hEvent);
823         }
824         if (wwo->mapping) {
825             munmap(wwo->mapping, wwo->maplen);
826             wwo->mapping = NULL;
827         }
828
829         close(wwo->unixdev);
830         wwo->unixdev = 0;
831         wwo->dwFragmentSize = 0;
832         if (OSS_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
833             WARN("can't notify client !\n");
834             ret = MMSYSERR_INVALPARAM;
835         }
836     }
837     return ret;
838 }
839
840 /**************************************************************************
841  *                              wodWrite                        [internal]
842  * 
843  */
844 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
845 {
846     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
847     
848     /* first, do the sanity checks... */
849     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
850         WARN("bad dev ID !\n");
851         return MMSYSERR_BADDEVICEID;
852     }
853     
854     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) 
855         return WAVERR_UNPREPARED;
856     
857     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) 
858         return WAVERR_STILLPLAYING;
859
860     lpWaveHdr->dwFlags &= ~WHDR_DONE;
861     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
862     lpWaveHdr->lpNext = 0;
863
864     TRACE("imhere[3-HEADER]\n");
865     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
866
867     return MMSYSERR_NOERROR;
868 }
869
870 /**************************************************************************
871  *                              wodPrepare                      [internal]
872  */
873 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
874 {
875     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
876     
877     if (wDevID >= MAX_WAVEOUTDRV) {
878         WARN("bad device ID !\n");
879         return MMSYSERR_BADDEVICEID;
880     }
881     
882     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
883         return WAVERR_STILLPLAYING;
884     
885     lpWaveHdr->dwFlags |= WHDR_PREPARED;
886     lpWaveHdr->dwFlags &= ~WHDR_DONE;
887     return MMSYSERR_NOERROR;
888 }
889
890 /**************************************************************************
891  *                              wodUnprepare                    [internal]
892  */
893 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
894 {
895     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
896     
897     if (wDevID >= MAX_WAVEOUTDRV) {
898         WARN("bad device ID !\n");
899         return MMSYSERR_BADDEVICEID;
900     }
901     
902     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
903         return WAVERR_STILLPLAYING;
904     
905     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
906     lpWaveHdr->dwFlags |= WHDR_DONE;
907     
908     return MMSYSERR_NOERROR;
909 }
910
911 /**************************************************************************
912  *                      wodPause                                [internal]
913  */
914 static DWORD wodPause(WORD wDevID)
915 {
916     TRACE("(%u);!\n", wDevID);
917     
918     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
919         WARN("bad device ID !\n");
920         return MMSYSERR_BADDEVICEID;
921     }
922     
923     TRACE("imhere[3-PAUSING]\n");
924     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
925     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
926     
927     return MMSYSERR_NOERROR;
928 }
929
930 /**************************************************************************
931  *                      wodRestart                              [internal]
932  */
933 static DWORD wodRestart(WORD wDevID)
934 {
935     TRACE("(%u);\n", wDevID);
936     
937     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
938         WARN("bad device ID !\n");
939         return MMSYSERR_BADDEVICEID;
940     }
941     
942     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
943         TRACE("imhere[3-RESTARTING]\n");
944         PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
945         WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
946     }
947     
948     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
949     /* FIXME: Myst crashes with this ... hmm -MM
950        if (OSS_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
951        WARN("can't notify client !\n");
952        return MMSYSERR_INVALPARAM;
953        }
954     */
955     
956     return MMSYSERR_NOERROR;
957 }
958
959 /**************************************************************************
960  *                      wodReset                                [internal]
961  */
962 static DWORD wodReset(WORD wDevID)
963 {
964     TRACE("(%u);\n", wDevID);
965     
966     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
967         WARN("bad device ID !\n");
968         return MMSYSERR_BADDEVICEID;
969     }
970     
971     TRACE("imhere[3-RESET]\n");
972     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
973     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
974     
975     return MMSYSERR_NOERROR;
976 }
977
978
979 /**************************************************************************
980  *                              wodGetPosition                  [internal]
981  */
982 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
983 {
984     int                 time;
985     DWORD               val;
986     WINE_WAVEOUT*       wwo;
987
988     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
989     
990     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
991         WARN("bad device ID !\n");
992         return MMSYSERR_BADDEVICEID;
993     }
994     
995     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
996
997     wwo = &WOutDev[wDevID];
998     val = wwo->dwPlayedTotal;
999
1000     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n", 
1001           lpTime->wType, wwo->format.wBitsPerSample, 
1002           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels, 
1003           wwo->format.wf.nAvgBytesPerSec); 
1004     TRACE("dwTotalPlayed=%lu\n", val);
1005     
1006     switch (lpTime->wType) {
1007     case TIME_BYTES:
1008         lpTime->u.cb = val;
1009         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1010         break;
1011     case TIME_SAMPLES:
1012         lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample;
1013         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1014         break;
1015     case TIME_SMPTE:
1016         time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1017         lpTime->u.smpte.hour = time / 108000;
1018         time -= lpTime->u.smpte.hour * 108000;
1019         lpTime->u.smpte.min = time / 1800;
1020         time -= lpTime->u.smpte.min * 1800;
1021         lpTime->u.smpte.sec = time / 30;
1022         time -= lpTime->u.smpte.sec * 30;
1023         lpTime->u.smpte.frame = time;
1024         lpTime->u.smpte.fps = 30;
1025         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1026               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1027               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1028         break;
1029     default:
1030         FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1031         lpTime->wType = TIME_MS;
1032     case TIME_MS:
1033         lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1034         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1035         break;
1036     }
1037     return MMSYSERR_NOERROR;
1038 }
1039
1040 /**************************************************************************
1041  *                              wodGetVolume                    [internal]
1042  */
1043 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1044 {
1045     int         mixer;
1046     int         volume;
1047     DWORD       left, right;
1048     
1049     TRACE("(%u, %p);\n", wDevID, lpdwVol);
1050     
1051     if (lpdwVol == NULL) 
1052         return MMSYSERR_NOTENABLED;
1053     if ((mixer = open(MIXER_DEV, O_RDONLY|O_NDELAY)) < 0) {
1054         WARN("mixer device not available !\n");
1055         return MMSYSERR_NOTENABLED;
1056     }
1057     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
1058         WARN("unable read mixer !\n");
1059         return MMSYSERR_NOTENABLED;
1060     }
1061     close(mixer);
1062     left = LOBYTE(volume);
1063     right = HIBYTE(volume);
1064     TRACE("left=%ld right=%ld !\n", left, right);
1065     *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1066     return MMSYSERR_NOERROR;
1067 }
1068
1069
1070 /**************************************************************************
1071  *                              wodSetVolume                    [internal]
1072  */
1073 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1074 {
1075     int         mixer;
1076     int         volume;
1077     DWORD       left, right;
1078
1079     TRACE("(%u, %08lX);\n", wDevID, dwParam);
1080
1081     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
1082     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1083     volume = left + (right << 8);
1084     
1085     if ((mixer = open(MIXER_DEV, O_WRONLY|O_NDELAY)) < 0) {
1086         WARN("mixer device not available !\n");
1087         return MMSYSERR_NOTENABLED;
1088     }
1089     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
1090         WARN("unable set mixer !\n");
1091         return MMSYSERR_NOTENABLED;
1092     } else {
1093         TRACE("volume=%04x\n", (unsigned)volume);
1094     }
1095     close(mixer);
1096     return MMSYSERR_NOERROR;
1097 }
1098
1099 /**************************************************************************
1100  *                              wodGetNumDevs                   [internal]
1101  */
1102 static  DWORD   wodGetNumDevs(void)
1103 {
1104     DWORD       ret = 1;
1105     
1106     /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
1107     int audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
1108     
1109     if (audio == -1) {
1110         if (errno != EBUSY)
1111             ret = 0;
1112     } else {
1113         close(audio);
1114     }
1115     return ret;
1116 }
1117
1118 /**************************************************************************
1119  *                              OSS_wodMessage          [sample driver]
1120  */
1121 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
1122                             DWORD dwParam1, DWORD dwParam2)
1123 {
1124     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1125           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1126     
1127     switch (wMsg) {
1128     case DRVM_INIT:
1129     case DRVM_EXIT:
1130     case DRVM_ENABLE:
1131     case DRVM_DISABLE:
1132         /* FIXME: Pretend this is supported */
1133         return 0;
1134     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
1135     case WODM_CLOSE:            return wodClose         (wDevID);
1136     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1137     case WODM_PAUSE:            return wodPause         (wDevID);
1138     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
1139     case WODM_BREAKLOOP:        return MMSYSERR_NOTSUPPORTED;
1140     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1141     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1142     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSA)dwParam1,      dwParam2);
1143     case WODM_GETNUMDEVS:       return wodGetNumDevs    ();
1144     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
1145     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
1146     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1147     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1148     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
1149     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
1150     case WODM_RESTART:          return wodRestart       (wDevID);
1151     case WODM_RESET:            return wodReset         (wDevID);
1152     case 0x810:                 return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
1153     default:
1154         FIXME("unknown message %d!\n", wMsg);
1155     }
1156     return MMSYSERR_NOTSUPPORTED;
1157 }
1158
1159 /*======================================================================*
1160  *                  Low level DSOUND implementation                     *
1161  *======================================================================*/
1162
1163 typedef struct IDsDriverImpl IDsDriverImpl;
1164 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1165
1166 struct IDsDriverImpl
1167 {
1168     /* IUnknown fields */
1169     ICOM_VFIELD(IDsDriver);
1170     DWORD               ref;
1171     /* IDsDriverImpl fields */
1172     UINT                wDevID;
1173     IDsDriverBufferImpl*primary;
1174 };
1175
1176 struct IDsDriverBufferImpl
1177 {
1178     /* IUnknown fields */
1179     ICOM_VFIELD(IDsDriverBuffer);
1180     DWORD               ref;
1181     /* IDsDriverBufferImpl fields */
1182     IDsDriverImpl*      drv;
1183     DWORD               buflen;
1184 };
1185
1186 static HRESULT DSDB_MapPrimary(IDsDriverBufferImpl *dsdb)
1187 {
1188     WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
1189     if (!wwo->mapping) {
1190         wwo->mapping = mmap(NULL, wwo->maplen, PROT_WRITE, MAP_SHARED,
1191                             wwo->unixdev, 0);
1192         if (wwo->mapping == (LPBYTE)-1) {
1193             ERR("(%p): Could not map sound device for direct access (errno=%d)\n", dsdb, errno);
1194             return DSERR_GENERIC;
1195         }
1196         TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dsdb, wwo->mapping, wwo->maplen);
1197     }
1198     return DS_OK;
1199 }
1200
1201 static HRESULT DSDB_UnmapPrimary(IDsDriverBufferImpl *dsdb)
1202 {
1203     WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
1204     if (wwo->mapping) {
1205         if (munmap(wwo->mapping, wwo->maplen) < 0) {
1206             ERR("(%p): Could not unmap sound device (errno=%d)\n", dsdb, errno);
1207             return DSERR_GENERIC;
1208         }
1209         wwo->mapping = NULL;
1210         TRACE("(%p): sound device unmapped\n", dsdb);
1211     }
1212     return DS_OK;
1213 }
1214
1215 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
1216 {
1217     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1218     FIXME("(): stub!\n");
1219     return DSERR_UNSUPPORTED;
1220 }
1221
1222 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
1223 {
1224     ICOM_THIS(IDsDriverBufferImpl,iface);
1225     This->ref++;
1226     return This->ref;
1227 }
1228
1229 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
1230 {
1231     ICOM_THIS(IDsDriverBufferImpl,iface);
1232     if (--This->ref)
1233         return This->ref;
1234     if (This == This->drv->primary)
1235         This->drv->primary = NULL;
1236     DSDB_UnmapPrimary(This);
1237     HeapFree(GetProcessHeap(),0,This);
1238     return 0;
1239 }
1240
1241 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
1242                                                LPVOID*ppvAudio1,LPDWORD pdwLen1,
1243                                                LPVOID*ppvAudio2,LPDWORD pdwLen2,
1244                                                DWORD dwWritePosition,DWORD dwWriteLen,
1245                                                DWORD dwFlags)
1246 {
1247     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1248     /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK,
1249      * and that we don't support secondary buffers, this method will never be called */
1250     TRACE("(%p): stub\n",iface);
1251     return DSERR_UNSUPPORTED;
1252 }
1253
1254 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
1255                                                  LPVOID pvAudio1,DWORD dwLen1,
1256                                                  LPVOID pvAudio2,DWORD dwLen2)
1257 {
1258     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1259     TRACE("(%p): stub\n",iface);
1260     return DSERR_UNSUPPORTED;
1261 }
1262
1263 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
1264                                                     LPWAVEFORMATEX pwfx)
1265 {
1266     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1267
1268     TRACE("(%p,%p)\n",iface,pwfx);
1269     /* On our request (GetDriverDesc flags), DirectSound has by now used
1270      * waveOutClose/waveOutOpen to set the format...
1271      * unfortunately, this means our mmap() is now gone...
1272      * so we need to somehow signal to our DirectSound implementation
1273      * that it should completely recreate this HW buffer...
1274      * this unexpected error code should do the trick... */
1275     return DSERR_BUFFERLOST;
1276 }
1277
1278 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
1279 {
1280     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1281     TRACE("(%p,%ld): stub\n",iface,dwFreq);
1282     return DSERR_UNSUPPORTED;
1283 }
1284
1285 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
1286 {
1287     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1288     FIXME("(%p,%p): stub!\n",iface,pVolPan);
1289     return DSERR_UNSUPPORTED;
1290 }
1291
1292 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
1293 {
1294     /* ICOM_THIS(IDsDriverImpl,iface); */
1295     TRACE("(%p,%ld): stub\n",iface,dwNewPos);
1296     return DSERR_UNSUPPORTED;
1297 }
1298
1299 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
1300                                                       LPDWORD lpdwPlay, LPDWORD lpdwWrite)
1301 {
1302     ICOM_THIS(IDsDriverBufferImpl,iface);
1303     count_info info;
1304     DWORD ptr;
1305
1306     TRACE("(%p)\n",iface);
1307     if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_GETOPTR, &info) < 0) {
1308         ERR("ioctl failed (%d)\n", errno);
1309         return DSERR_GENERIC;
1310     }
1311     ptr = info.ptr & ~3; /* align the pointer, just in case */
1312     if (lpdwPlay) *lpdwPlay = ptr;
1313     if (lpdwWrite) {
1314         /* add some safety margin (not strictly necessary, but...) */
1315         *lpdwWrite = ptr + 32;
1316         while (*lpdwWrite > This->buflen)
1317             *lpdwWrite -= This->buflen;
1318     }
1319     TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
1320     return DSERR_UNSUPPORTED;
1321 }
1322
1323 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
1324 {
1325     ICOM_THIS(IDsDriverBufferImpl,iface);
1326     int enable = PCM_ENABLE_OUTPUT;
1327     TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
1328     if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1329         ERR("ioctl failed (%d)\n", errno);
1330         return DSERR_GENERIC;
1331     }
1332     return DS_OK;
1333 }
1334
1335 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
1336 {
1337     ICOM_THIS(IDsDriverBufferImpl,iface);
1338     int enable = 0;
1339     TRACE("(%p)\n",iface);
1340     /* no more playing */
1341     if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1342         ERR("ioctl failed (%d)\n", errno);
1343         return DSERR_GENERIC;
1344     }
1345 #if 0
1346     /* the play position must be reset to the beginning of the buffer */
1347     if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_RESET, 0) < 0) {
1348         ERR("ioctl failed (%d)\n", errno);
1349         return DSERR_GENERIC;
1350     }
1351 #endif
1352     return DS_OK;
1353 }
1354
1355 static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
1356 {
1357     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1358     IDsDriverBufferImpl_QueryInterface,
1359     IDsDriverBufferImpl_AddRef,
1360     IDsDriverBufferImpl_Release,
1361     IDsDriverBufferImpl_Lock,
1362     IDsDriverBufferImpl_Unlock,
1363     IDsDriverBufferImpl_SetFormat,
1364     IDsDriverBufferImpl_SetFrequency,
1365     IDsDriverBufferImpl_SetVolumePan,
1366     IDsDriverBufferImpl_SetPosition,
1367     IDsDriverBufferImpl_GetPosition,
1368     IDsDriverBufferImpl_Play,
1369     IDsDriverBufferImpl_Stop
1370 };
1371
1372 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
1373 {
1374     /* ICOM_THIS(IDsDriverImpl,iface); */
1375     FIXME("(%p): stub!\n",iface);
1376     return DSERR_UNSUPPORTED;
1377 }
1378
1379 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
1380 {
1381     ICOM_THIS(IDsDriverImpl,iface);
1382     This->ref++;
1383     return This->ref;
1384 }
1385
1386 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
1387 {
1388     ICOM_THIS(IDsDriverImpl,iface);
1389     if (--This->ref)
1390         return This->ref;
1391     HeapFree(GetProcessHeap(),0,This);
1392     return 0;
1393 }
1394
1395 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
1396 {
1397     ICOM_THIS(IDsDriverImpl,iface);
1398     TRACE("(%p,%p)\n",iface,pDesc);
1399     pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
1400         DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
1401     strcpy(pDesc->szDesc,"WineOSS DirectSound Driver");
1402     strcpy(pDesc->szDrvName,"wineoss.drv");
1403     pDesc->dnDevNode            = WOutDev[This->wDevID].waveDesc.dnDevNode;
1404     pDesc->wVxdId               = 0; 
1405     pDesc->wReserved            = 0;
1406     pDesc->ulDeviceNum          = This->wDevID;
1407     pDesc->dwHeapType           = DSDHEAP_NOHEAP;
1408     pDesc->pvDirectDrawHeap     = NULL;
1409     pDesc->dwMemStartAddress    = 0;
1410     pDesc->dwMemEndAddress      = 0;
1411     pDesc->dwMemAllocExtra      = 0;
1412     pDesc->pvReserved1          = NULL;
1413     pDesc->pvReserved2          = NULL;
1414     return DS_OK;
1415 }
1416
1417 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
1418 {
1419     ICOM_THIS(IDsDriverImpl,iface);
1420     int enable = 0;
1421
1422     TRACE("(%p)\n",iface);
1423     /* make sure the card doesn't start playing before we want it to */
1424     if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1425         ERR("ioctl failed (%d)\n", errno);
1426         return DSERR_GENERIC;
1427     }
1428     return DS_OK;
1429 }
1430
1431 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
1432 {
1433     ICOM_THIS(IDsDriverImpl,iface);
1434     TRACE("(%p)\n",iface);
1435     if (This->primary) {
1436         ERR("problem with DirectSound: primary not released\n");
1437         return DSERR_GENERIC;
1438     }
1439     return DS_OK;
1440 }
1441
1442 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
1443 {
1444     /* ICOM_THIS(IDsDriverImpl,iface); */
1445     TRACE("(%p,%p)\n",iface,pCaps);
1446     memset(pCaps, 0, sizeof(*pCaps));
1447     /* FIXME: need to check actual capabilities */
1448     pCaps->dwFlags = DSCAPS_PRIMARYMONO | DSCAPS_PRIMARYSTEREO |
1449         DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARY16BIT;
1450     pCaps->dwPrimaryBuffers = 1;
1451     /* the other fields only apply to secondary buffers, which we don't support
1452      * (unless we want to mess with wavetable synthesizers and MIDI) */
1453     return DS_OK;
1454 }
1455
1456 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
1457                                                       LPWAVEFORMATEX pwfx,
1458                                                       DWORD dwFlags, DWORD dwCardAddress,
1459                                                       LPDWORD pdwcbBufferSize,
1460                                                       LPBYTE *ppbBuffer,
1461                                                       LPVOID *ppvObj)
1462 {
1463     ICOM_THIS(IDsDriverImpl,iface);
1464     IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
1465     HRESULT err;
1466     audio_buf_info info;
1467
1468     TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
1469     /* we only support primary buffers */
1470     if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
1471         return DSERR_UNSUPPORTED;
1472     if (This->primary)
1473         return DSERR_ALLOCATED;
1474     if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
1475         return DSERR_CONTROLUNAVAIL;
1476
1477     *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
1478     if (*ippdsdb == NULL)
1479         return DSERR_OUTOFMEMORY;
1480     ICOM_VTBL(*ippdsdb) = &dsdbvt;
1481     (*ippdsdb)->ref     = 1;
1482     (*ippdsdb)->drv     = This;
1483
1484     /* check how big the DMA buffer is now */
1485     if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
1486         ERR("ioctl failed (%d)\n", errno);
1487         HeapFree(GetProcessHeap(),0,*ippdsdb);
1488         *ippdsdb = NULL;
1489         return DSERR_GENERIC;
1490     }
1491     WOutDev[This->wDevID].maplen = (*ippdsdb)->buflen = info.fragstotal * info.fragsize;
1492
1493     /* map the DMA buffer */
1494     err = DSDB_MapPrimary(*ippdsdb);
1495     if (err != DS_OK) {
1496         HeapFree(GetProcessHeap(),0,*ippdsdb);
1497         *ippdsdb = NULL;
1498         return err;
1499     }
1500
1501     /* primary buffer is ready to go */
1502     *pdwcbBufferSize    = WOutDev[This->wDevID].maplen;
1503     *ppbBuffer          = WOutDev[This->wDevID].mapping;
1504
1505     This->primary = *ippdsdb;
1506
1507     return DS_OK;
1508 }
1509
1510 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
1511                                                          PIDSDRIVERBUFFER pBuffer,
1512                                                          LPVOID *ppvObj)
1513 {
1514     /* ICOM_THIS(IDsDriverImpl,iface); */
1515     TRACE("(%p,%p): stub\n",iface,pBuffer);
1516     return DSERR_INVALIDCALL;
1517 }
1518
1519 static ICOM_VTABLE(IDsDriver) dsdvt =
1520 {
1521     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1522     IDsDriverImpl_QueryInterface,
1523     IDsDriverImpl_AddRef,
1524     IDsDriverImpl_Release,
1525     IDsDriverImpl_GetDriverDesc,
1526     IDsDriverImpl_Open,
1527     IDsDriverImpl_Close,
1528     IDsDriverImpl_GetCaps,
1529     IDsDriverImpl_CreateSoundBuffer,
1530     IDsDriverImpl_DuplicateSoundBuffer
1531 };
1532
1533 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1534 {
1535     IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
1536
1537     /* the HAL isn't much better than the HEL if we can't do mmap() */
1538     if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
1539         ERR("DirectSound flag not set\n");
1540         MESSAGE("This sound card's driver does not support direct access\n");
1541         MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1542         return MMSYSERR_NOTSUPPORTED;
1543     }
1544
1545     *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
1546     if (!*idrv)
1547         return MMSYSERR_NOMEM;
1548     ICOM_VTBL(*idrv)    = &dsdvt;
1549     (*idrv)->ref        = 1;
1550
1551     (*idrv)->wDevID     = wDevID;
1552     (*idrv)->primary    = NULL;
1553     return MMSYSERR_NOERROR;
1554 }
1555
1556 /*======================================================================*
1557  *                  Low level WAVE IN implemantation                    *
1558  *======================================================================*/
1559
1560 /**************************************************************************
1561  *                      widGetDevCaps                           [internal]
1562  */
1563 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1564 {
1565     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1566     
1567     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1568     
1569     if (wDevID >= MAX_WAVEINDRV) {
1570         TRACE("MAX_WAVINDRV reached !\n");
1571         return MMSYSERR_BADDEVICEID;
1572     }
1573
1574     memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1575     return MMSYSERR_NOERROR;
1576 }
1577
1578 /**************************************************************************
1579  *                              widRecorder                     [internal]
1580  */
1581 static  DWORD   CALLBACK        widRecorder(LPVOID pmt)
1582 {
1583     WORD                uDevID = (DWORD)pmt;
1584     WINE_WAVEIN*        wwi = (WINE_WAVEIN*)&WInDev[uDevID];
1585     WAVEHDR*            lpWaveHdr;
1586     DWORD               dwSleepTime;
1587     MSG                 msg;
1588     DWORD               bytesRead;
1589
1590     PeekMessageA(&msg, 0, 0, 0, 0);
1591     wwi->state = WINE_WS_STOPPED;
1592     wwi->dwTotalRecorded = 0;
1593
1594     TRACE("imhere[0]\n");
1595     SetEvent(wwi->hEvent);
1596
1597     /* make sleep time to be # of ms to output a fragment */
1598     dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec;
1599
1600     for (;;) {
1601         /* wait for dwSleepTime or an event in thread's queue */
1602         /* FIXME: could improve wait time depending on queue state,
1603          * ie, number of queued fragments
1604          */
1605         TRACE("imhere[1]\n");
1606
1607         if (wwi->lpQueuePtr != NULL) {
1608             lpWaveHdr = wwi->lpQueuePtr;
1609             TRACE("recording buf=%p size=%lu/read=%lu \n",
1610                   lpWaveHdr->lpData, wwi->lpQueuePtr->dwBufferLength, lpWaveHdr->dwBytesRecorded);
1611
1612             bytesRead = read(wwi->unixdev, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, 
1613                              lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1614
1615             if (bytesRead > 0) {
1616                 TRACE("Read=%lu (%ld)\n", bytesRead, lpWaveHdr->dwBufferLength);
1617                 lpWaveHdr->dwBytesRecorded += bytesRead;
1618                 wwi->dwTotalRecorded += bytesRead;
1619                 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) {
1620                     /* removes the current block from the queue */
1621                     wwi->lpQueuePtr = lpWaveHdr->lpNext;
1622
1623                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1624                     lpWaveHdr->dwFlags |= WHDR_DONE;
1625         
1626                     if (OSS_NotifyClient(uDevID, WIM_DATA, (DWORD)lpWaveHdr, lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
1627                         WARN("can't notify client !\n");
1628                     }
1629
1630                 }
1631             } else {
1632                 TRACE("No data (%s)\n", strerror(errno));
1633             }
1634         }
1635
1636         MsgWaitForMultipleObjects(0, NULL, FALSE, dwSleepTime, QS_POSTMESSAGE);
1637         TRACE("imhere[2] (q=%p)\n", wwi->lpQueuePtr);
1638
1639         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1640             switch (msg.message) {
1641             case WINE_WM_PAUSING:
1642                 wwi->state = WINE_WS_PAUSED;
1643                 SetEvent(wwi->hEvent);
1644                 break;
1645             case WINE_WM_RESTARTING:
1646                 wwi->state = WINE_WS_PLAYING;
1647                 SetEvent(wwi->hEvent);
1648                 break;
1649             case WINE_WM_HEADER:
1650                 lpWaveHdr = (LPWAVEHDR)msg.lParam;
1651                 lpWaveHdr->lpNext = 0;
1652
1653                 /* insert buffer at the end of queue */
1654                 {
1655                     LPWAVEHDR*  wh;
1656                     for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1657                     *wh = lpWaveHdr;
1658                 }
1659                 if (wwi->state == WINE_WS_STOPPED)
1660                     wwi->state = WINE_WS_PLAYING;
1661                 break;
1662             case WINE_WM_RESETTING:
1663                 wwi->state = WINE_WS_STOPPED;
1664                 /* return all buffers to the app */
1665                 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
1666                     TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1667                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1668                     lpWaveHdr->dwFlags |= WHDR_DONE;
1669         
1670                     if (OSS_NotifyClient(uDevID, WIM_DATA, (DWORD)lpWaveHdr, 
1671                                          lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
1672                         WARN("can't notify client !\n");
1673                     }
1674                 }
1675                 wwi->lpQueuePtr = NULL;
1676                 SetEvent(wwi->hEvent);
1677                 break;
1678             case WINE_WM_CLOSING:
1679                 wwi->hThread = 0;
1680                 wwi->state = WINE_WS_CLOSED;
1681                 SetEvent(wwi->hEvent);
1682                 ExitThread(0);
1683                 /* shouldn't go here */
1684             default:
1685                 FIXME("unknown message %d\n", msg.message);
1686                 break;
1687             }
1688         }
1689     }
1690     ExitThread(0);
1691     /* just for not generating compilation warnings... should never be executed */
1692     return 0; 
1693 }
1694
1695
1696 /**************************************************************************
1697  *                              widOpen                         [internal]
1698  */
1699 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1700 {
1701     int                 audio, abuf_size, smplrate, samplesize, dsp_stereo;
1702     LPWAVEFORMAT        lpFormat;
1703     WINE_WAVEIN*        wwi;
1704
1705     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1706     if (lpDesc == NULL) {
1707         WARN("Invalid Parameter !\n");
1708         return MMSYSERR_INVALPARAM;
1709     }
1710     if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_BADDEVICEID;
1711
1712     /* only PCM format is supported so far... */
1713     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1714         lpDesc->lpFormat->nChannels == 0 ||
1715         lpDesc->lpFormat->nSamplesPerSec == 0) {
1716         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
1717              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1718              lpDesc->lpFormat->nSamplesPerSec);
1719         return WAVERR_BADFORMAT;
1720     }
1721
1722     if (dwFlags & WAVE_FORMAT_QUERY) {
1723         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
1724              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1725              lpDesc->lpFormat->nSamplesPerSec);
1726         return MMSYSERR_NOERROR;
1727     }
1728
1729     wwi = &WInDev[wDevID];
1730     wwi->unixdev = 0;
1731     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1732     audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0);
1733     if (audio == -1) {
1734         WARN("can't open (%s)!\n", strerror(errno));
1735         return MMSYSERR_ALLOCATED;
1736     }
1737     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1738     if (abuf_size < 1024 || abuf_size > 65536) {
1739         if (abuf_size == -1)
1740         {
1741             WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1742             close(audio);
1743             return MMSYSERR_NOTENABLED;
1744         }
1745         WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize %d!\n",abuf_size);
1746     }
1747     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1748
1749     if (wwi->lpQueuePtr) {
1750         WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
1751         wwi->lpQueuePtr = NULL;
1752     }
1753     wwi->unixdev = audio;
1754     wwi->dwFragmentSize = abuf_size;
1755     wwi->dwTotalRecorded = 0;
1756     memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1757     lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat; 
1758
1759     memcpy(&wwi->format, lpFormat, sizeof(PCMWAVEFORMAT));
1760     wwi->format.wBitsPerSample = 8; /* <-------------- */
1761     if (wwi->format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1762     if (wwi->format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1763     if (wwi->format.wBitsPerSample == 0) {
1764         wwi->format.wBitsPerSample = 8 *
1765             (wwi->format.wf.nAvgBytesPerSec /
1766              wwi->format.wf.nSamplesPerSec) /
1767             wwi->format.wf.nChannels;
1768     }
1769     samplesize = wwi->format.wBitsPerSample;
1770     smplrate = wwi->format.wf.nSamplesPerSec;
1771     dsp_stereo = (wwi->format.wf.nChannels > 1) ? TRUE : FALSE;
1772     IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1773     IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1774     IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1775     TRACE("wBitsPerSample=%u !\n", wwi->format.wBitsPerSample);
1776     TRACE("nSamplesPerSec=%lu !\n", wwi->format.wf.nSamplesPerSec);
1777     TRACE("nChannels=%u !\n", wwi->format.wf.nChannels);
1778     TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec); 
1779
1780     wwi->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1781     wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
1782     WaitForSingleObject(wwi->hEvent, INFINITE);
1783
1784    if (OSS_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1785         WARN("can't notify client !\n");
1786         return MMSYSERR_INVALPARAM;
1787     }
1788     return MMSYSERR_NOERROR;
1789 }
1790
1791 /**************************************************************************
1792  *                              widClose                        [internal]
1793  */
1794 static DWORD widClose(WORD wDevID)
1795 {
1796     WINE_WAVEIN*        wwi;
1797
1798     TRACE("(%u);\n", wDevID);
1799     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1800         WARN("can't close !\n");
1801         return MMSYSERR_INVALHANDLE;
1802     }
1803
1804     wwi = &WInDev[wDevID];
1805
1806     if (wwi->lpQueuePtr != NULL) {
1807         WARN("still buffers open !\n");
1808         return WAVERR_STILLPLAYING;
1809     }
1810
1811     PostThreadMessageA(wwi->dwThreadID, WINE_WM_CLOSING, 0, 0);
1812     WaitForSingleObject(wwi->hEvent, INFINITE);
1813     CloseHandle(wwi->hEvent);
1814     close(wwi->unixdev);
1815     wwi->unixdev = 0;
1816     wwi->dwFragmentSize = 0;
1817     if (OSS_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1818         WARN("can't notify client !\n");
1819         return MMSYSERR_INVALPARAM;
1820     }
1821     return MMSYSERR_NOERROR;
1822 }
1823
1824 /**************************************************************************
1825  *                              widAddBuffer            [internal]
1826  */
1827 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1828 {
1829     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1830
1831     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1832         WARN("can't do it !\n");
1833         return MMSYSERR_INVALHANDLE;
1834     }
1835     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1836         TRACE("never been prepared !\n");
1837         return WAVERR_UNPREPARED;
1838     }
1839     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1840         TRACE("header already in use !\n");
1841         return WAVERR_STILLPLAYING;
1842     }
1843     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1844     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1845     lpWaveHdr->dwBytesRecorded = 0;
1846     lpWaveHdr->lpNext = NULL;
1847     PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
1848     return MMSYSERR_NOERROR;
1849 }
1850
1851 /**************************************************************************
1852  *                              widPrepare                      [internal]
1853  */
1854 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1855 {
1856     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1857
1858     if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
1859
1860     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1861         return WAVERR_STILLPLAYING;
1862
1863     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1864     lpWaveHdr->dwFlags &= ~(WHDR_INQUEUE|WHDR_DONE);
1865     lpWaveHdr->dwBytesRecorded = 0;
1866     TRACE("header prepared !\n");
1867     return MMSYSERR_NOERROR;
1868 }
1869
1870 /**************************************************************************
1871  *                              widUnprepare                    [internal]
1872  */
1873 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1874 {
1875     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1876     if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
1877
1878     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1879         return WAVERR_STILLPLAYING;
1880
1881     lpWaveHdr->dwFlags &= ~(WHDR_PREPARED|WHDR_INQUEUE);
1882     lpWaveHdr->dwFlags |= WHDR_DONE;
1883     
1884     return MMSYSERR_NOERROR;
1885 }
1886
1887 /**************************************************************************
1888  *                      widStart                                [internal]
1889  */
1890 static DWORD widStart(WORD wDevID)
1891 {
1892     TRACE("(%u);\n", wDevID);
1893     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1894         WARN("can't start recording !\n");
1895         return MMSYSERR_INVALHANDLE;
1896     }
1897     
1898     PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
1899     WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
1900     
1901     return MMSYSERR_NOERROR;
1902 }
1903
1904 /**************************************************************************
1905  *                      widStop                                 [internal]
1906  */
1907 static DWORD widStop(WORD wDevID)
1908 {
1909     TRACE("(%u);\n", wDevID);
1910     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1911         WARN("can't stop !\n");
1912         return MMSYSERR_INVALHANDLE;
1913     }
1914     /* FIXME: reset aint stop */
1915     PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
1916     WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
1917     
1918     return MMSYSERR_NOERROR;
1919 }
1920
1921 /**************************************************************************
1922  *                      widReset                                [internal]
1923  */
1924 static DWORD widReset(WORD wDevID)
1925 {
1926     TRACE("(%u);\n", wDevID);
1927     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1928         WARN("can't reset !\n");
1929         return MMSYSERR_INVALHANDLE;
1930     }
1931     PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
1932     WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
1933     return MMSYSERR_NOERROR;
1934 }
1935
1936 /**************************************************************************
1937  *                              widGetPosition                  [internal]
1938  */
1939 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1940 {
1941     int                 time;
1942     WINE_WAVEIN*        wwi;
1943     
1944     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1945
1946     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1947         WARN("can't get pos !\n");
1948         return MMSYSERR_INVALHANDLE;
1949     }
1950     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1951
1952     wwi = &WInDev[wDevID];
1953
1954     TRACE("wType=%04X !\n", lpTime->wType);
1955     TRACE("wBitsPerSample=%u\n", wwi->format.wBitsPerSample); 
1956     TRACE("nSamplesPerSec=%lu\n", wwi->format.wf.nSamplesPerSec); 
1957     TRACE("nChannels=%u\n", wwi->format.wf.nChannels); 
1958     TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec); 
1959     switch (lpTime->wType) {
1960     case TIME_BYTES:
1961         lpTime->u.cb = wwi->dwTotalRecorded;
1962         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1963         break;
1964     case TIME_SAMPLES:
1965         lpTime->u.sample = wwi->dwTotalRecorded * 8 /
1966             wwi->format.wBitsPerSample;
1967         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1968         break;
1969     case TIME_SMPTE:
1970         time = wwi->dwTotalRecorded /
1971             (wwi->format.wf.nAvgBytesPerSec / 1000);
1972         lpTime->u.smpte.hour = time / 108000;
1973         time -= lpTime->u.smpte.hour * 108000;
1974         lpTime->u.smpte.min = time / 1800;
1975         time -= lpTime->u.smpte.min * 1800;
1976         lpTime->u.smpte.sec = time / 30;
1977         time -= lpTime->u.smpte.sec * 30;
1978         lpTime->u.smpte.frame = time;
1979         lpTime->u.smpte.fps = 30;
1980         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1981               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1982               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1983         break;
1984     case TIME_MS:
1985         lpTime->u.ms = wwi->dwTotalRecorded /
1986             (wwi->format.wf.nAvgBytesPerSec / 1000);
1987         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1988         break;
1989     default:
1990         FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1991         lpTime->wType = TIME_MS;
1992     }
1993     return MMSYSERR_NOERROR;
1994 }
1995
1996 /**************************************************************************
1997  *                              OSS_widMessage                  [sample driver]
1998  */
1999 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
2000                             DWORD dwParam1, DWORD dwParam2)
2001 {
2002     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2003           wDevID, wMsg, dwUser, dwParam1, dwParam2);
2004
2005     switch (wMsg) {
2006     case DRVM_INIT:
2007     case DRVM_EXIT:
2008     case DRVM_ENABLE:
2009     case DRVM_DISABLE:
2010         /* FIXME: Pretend this is supported */
2011         return 0;
2012     case WIDM_OPEN:             return widOpen       (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2013     case WIDM_CLOSE:            return widClose      (wDevID);
2014     case WIDM_ADDBUFFER:        return widAddBuffer  (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2015     case WIDM_PREPARE:          return widPrepare    (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2016     case WIDM_UNPREPARE:        return widUnprepare  (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2017     case WIDM_GETDEVCAPS:       return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
2018     case WIDM_GETNUMDEVS:       return wodGetNumDevs ();        /* same number of devices in output as in input */
2019     case WIDM_GETPOS:           return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
2020     case WIDM_RESET:            return widReset      (wDevID);
2021     case WIDM_START:            return widStart      (wDevID);
2022     case WIDM_STOP:             return widStop       (wDevID);
2023     default:
2024         FIXME("unknown message %u!\n", wMsg);
2025     }
2026     return MMSYSERR_NOTSUPPORTED;
2027 }
2028
2029 #else /* !HAVE_OSS */
2030
2031 /**************************************************************************
2032  *                              OSS_wodMessage                  [sample driver]
2033  */
2034 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
2035                             DWORD dwParam1, DWORD dwParam2)
2036 {
2037     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2038     return MMSYSERR_NOTENABLED;
2039 }
2040
2041 /**************************************************************************
2042  *                              OSS_widMessage                  [sample driver]
2043  */
2044 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
2045                             DWORD dwParam1, DWORD dwParam2)
2046 {
2047     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2048     return MMSYSERR_NOTENABLED;
2049 }
2050
2051 #endif /* HAVE_OSS */