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