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