Enable hardware secondary buffer support on cards that support it (SB
[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  *           2002 Eric Pouech (full duplex)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24 /*
25  * FIXME:
26  *      pause in waveOut does not work correctly in loop mode
27  *      Direct Sound Capture driver does not work (not complete yet)
28  */
29
30 /*#define EMULATE_SB16*/
31
32 /* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
33 #define USE_PIPE_SYNC
34
35 /* an exact wodGetPosition is usually not worth the extra context switches,
36  * as we're going to have near fragment accuracy anyway */
37 /* #define EXACT_WODPOSITION */
38
39 #include "config.h"
40 #include "wine/port.h"
41
42 #include <stdlib.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <string.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <errno.h>
50 #include <fcntl.h>
51 #ifdef HAVE_SYS_IOCTL_H
52 # include <sys/ioctl.h>
53 #endif
54 #ifdef HAVE_SYS_MMAN_H
55 # include <sys/mman.h>
56 #endif
57 #ifdef HAVE_SYS_POLL_H
58 # include <sys/poll.h>
59 #endif
60
61 #include "windef.h"
62 #include "winbase.h"
63 #include "wingdi.h"
64 #include "winerror.h"
65 #include "wine/winuser16.h"
66 #include "mmddk.h"
67 #include "dsound.h"
68 #include "dsdriver.h"
69 #include "oss.h"
70 #include "wine/debug.h"
71
72 WINE_DEFAULT_DEBUG_CHANNEL(wave);
73
74 /* Allow 1% deviation for sample rates (some ES137x cards) */
75 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
76
77 #ifdef HAVE_OSS
78
79 #define MAX_WAVEDRV     (6)
80
81 /* state diagram for waveOut writing:
82  *
83  * +---------+-------------+---------------+---------------------------------+
84  * |  state  |  function   |     event     |            new state            |
85  * +---------+-------------+---------------+---------------------------------+
86  * |         | open()      |               | STOPPED                         |
87  * | PAUSED  | write()     |               | PAUSED                          |
88  * | STOPPED | write()     | <thrd create> | PLAYING                         |
89  * | PLAYING | write()     | HEADER        | PLAYING                         |
90  * | (other) | write()     | <error>       |                                 |
91  * | (any)   | pause()     | PAUSING       | PAUSED                          |
92  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
93  * | (any)   | reset()     | RESETTING     | STOPPED                         |
94  * | (any)   | close()     | CLOSING       | CLOSED                          |
95  * +---------+-------------+---------------+---------------------------------+
96  */
97
98 /* states of the playing device */
99 #define WINE_WS_PLAYING         0
100 #define WINE_WS_PAUSED          1
101 #define WINE_WS_STOPPED         2
102 #define WINE_WS_CLOSED          3
103
104 /* events to be send to device */
105 enum win_wm_message {
106     WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
107     WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
108 };
109
110 #ifdef USE_PIPE_SYNC
111 #define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
112 #define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
113 #define RESET_OMR(omr) do { } while (0)
114 #define WAIT_OMR(omr, sleep) \
115   do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
116        pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
117 #else
118 #define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
119 #define CLEAR_OMR(omr) do { } while (0)
120 #define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
121 #define WAIT_OMR(omr, sleep) \
122   do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
123 #endif
124
125 typedef struct {
126     enum win_wm_message         msg;    /* message identifier */
127     DWORD                       param;  /* parameter for this message */
128     HANDLE                      hEvent; /* if message is synchronous, handle of event for synchro */
129 } OSS_MSG;
130
131 /* implement an in-process message ring for better performance
132  * (compared to passing thru the server)
133  * this ring will be used by the input (resp output) record (resp playback) routine
134  */
135 #define OSS_RING_BUFFER_INCREMENT       64
136 typedef struct {
137     int                         ring_buffer_size;
138     OSS_MSG                     * messages;
139     int                         msg_tosave;
140     int                         msg_toget;
141 #ifdef USE_PIPE_SYNC
142     int                         msg_pipe[2];
143 #else
144     HANDLE                      msg_event;
145 #endif
146     CRITICAL_SECTION            msg_crst;
147 } OSS_MSG_RING;
148
149 typedef struct tagOSS_DEVICE {
150     char                        dev_name[32];
151     char                        mixer_name[32];
152     unsigned                    open_count;
153     WAVEOUTCAPSA                out_caps;
154     WAVEINCAPSA                 in_caps;
155     DWORD                       in_caps_support;
156     unsigned                    open_access;
157     int                         fd;
158     DWORD                       owner_tid;
159     int                         sample_rate;
160     int                         stereo;
161     int                         format;
162     unsigned                    audio_fragment;
163     BOOL                        full_duplex;
164     BOOL                        bTriggerSupport;
165     BOOL                        bOutputEnabled;
166     BOOL                        bInputEnabled;
167     DSDRIVERDESC                ds_desc;
168     DSDRIVERCAPS                ds_caps;
169     DSCDRIVERCAPS               dsc_caps;
170     GUID                        ds_guid;
171     GUID                        dsc_guid;
172 } OSS_DEVICE;
173
174 static OSS_DEVICE   OSS_Devices[MAX_WAVEDRV];
175
176 typedef struct {
177     OSS_DEVICE*                 ossdev;
178     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
179     WAVEOPENDESC                waveDesc;
180     WORD                        wFlags;
181     PCMWAVEFORMAT               format;
182     DWORD                       volume;
183
184     /* OSS information */
185     DWORD                       dwFragmentSize;         /* size of OSS buffer fragment */
186     DWORD                       dwBufferSize;           /* size of whole OSS buffer in bytes */
187     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
188     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
189     DWORD                       dwPartialOffset;        /* Offset of not yet written bytes in lpPlayPtr */
190
191     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
192     DWORD                       dwLoops;                /* private copy of loop counter */
193
194     DWORD                       dwPlayedTotal;          /* number of bytes actually played since opening */
195     DWORD                       dwWrittenTotal;         /* number of bytes written to OSS buffer since opening */
196     BOOL                        bNeedPost;              /* whether audio still needs to be physically started */
197
198     /* synchronization stuff */
199     HANDLE                      hStartUpEvent;
200     HANDLE                      hThread;
201     DWORD                       dwThreadID;
202     OSS_MSG_RING                msgRing;
203 } WINE_WAVEOUT;
204
205 typedef struct {
206     OSS_DEVICE*                 ossdev;
207     volatile int                state;
208     DWORD                       dwFragmentSize;         /* OpenSound '/dev/dsp' give us that size */
209     WAVEOPENDESC                waveDesc;
210     WORD                        wFlags;
211     PCMWAVEFORMAT               format;
212     LPWAVEHDR                   lpQueuePtr;
213     DWORD                       dwTotalRecorded;
214
215     /* synchronization stuff */
216     HANDLE                      hThread;
217     DWORD                       dwThreadID;
218     HANDLE                      hStartUpEvent;
219     OSS_MSG_RING                msgRing;
220 } WINE_WAVEIN;
221
222 static WINE_WAVEOUT     WOutDev   [MAX_WAVEDRV];
223 static WINE_WAVEIN      WInDev    [MAX_WAVEDRV];
224 static unsigned         numOutDev;
225 static unsigned         numInDev;
226
227 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
228 static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv);
229 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
230 static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc);
231 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);
232 static DWORD widDsGuid(UINT wDevID, LPGUID pGuid);
233
234 /* These strings used only for tracing */
235 static const char *wodPlayerCmdString[] = {
236     "WINE_WM_PAUSING",
237     "WINE_WM_RESTARTING",
238     "WINE_WM_RESETTING",
239     "WINE_WM_HEADER",
240     "WINE_WM_UPDATE",
241     "WINE_WM_BREAKLOOP",
242     "WINE_WM_CLOSING",
243     "WINE_WM_STARTING",
244     "WINE_WM_STOPPING",
245 };
246
247 static int getEnables(OSS_DEVICE *ossdev)
248 {
249     return ( (ossdev->bOutputEnabled ? PCM_ENABLE_OUTPUT : 0) | 
250              (ossdev->bInputEnabled  ? PCM_ENABLE_INPUT  : 0) );
251 }
252
253 static DWORD wdDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
254 {
255     TRACE("(%u, %p)\n", wDevID, dwParam1);
256
257     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, OSS_Devices[wDevID].dev_name, -1,
258                                     NULL, 0 ) * sizeof(WCHAR);
259     return MMSYSERR_NOERROR;
260 }
261
262 static DWORD wdDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
263 {
264     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, OSS_Devices[wDevID].dev_name, -1,
265                                         NULL, 0 ) * sizeof(WCHAR))
266     {
267         MultiByteToWideChar(CP_ACP, 0, OSS_Devices[wDevID].dev_name, -1,
268                             dwParam1, dwParam2 / sizeof(WCHAR));
269         return MMSYSERR_NOERROR;
270     }
271
272     return MMSYSERR_INVALPARAM;
273 }
274
275 /*======================================================================*
276  *                  Low level WAVE implementation                       *
277  *======================================================================*/
278
279 /******************************************************************
280  *              OSS_RawOpenDevice
281  *
282  * Low level device opening (from values stored in ossdev)
283  */
284 static DWORD      OSS_RawOpenDevice(OSS_DEVICE* ossdev, int strict_format)
285 {
286     int fd, val, rc;
287     TRACE("(%p,%d)\n",ossdev,strict_format);
288
289     if ((fd = open(ossdev->dev_name, ossdev->open_access|O_NDELAY, 0)) == -1)
290     {
291         WARN("Couldn't open %s (%s)\n", ossdev->dev_name, strerror(errno));
292         return (errno == EBUSY) ? MMSYSERR_ALLOCATED : MMSYSERR_ERROR;
293     }
294     fcntl(fd, F_SETFD, 1); /* set close on exec flag */
295     /* turn full duplex on if it has been requested */
296     if (ossdev->open_access == O_RDWR && ossdev->full_duplex) {
297         rc = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
298         /* on *BSD, as full duplex is always enabled by default, this ioctl
299          * will fail with EINVAL
300          * so, we don't consider EINVAL an error here
301          */
302         if (rc != 0 && errno != EINVAL) {
303             ERR("ioctl(%s, SNDCTL_DSP_SETDUPLEX) failed (%s)\n", ossdev->dev_name, strerror(errno));
304             goto error2;
305         }
306     }
307
308     if (ossdev->audio_fragment) {
309         rc = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossdev->audio_fragment);
310         if (rc != 0) {
311             ERR("ioctl(%s, SNDCTL_DSP_SETFRAGMENT) failed (%s)\n", ossdev->dev_name, strerror(errno));
312             goto error2;
313         }
314     }
315
316     /* First size and stereo then samplerate */
317     if (ossdev->format>=0)
318     {
319         val = ossdev->format;
320         rc = ioctl(fd, SNDCTL_DSP_SETFMT, &ossdev->format);
321         if (rc != 0 || val != ossdev->format) {
322             TRACE("Can't set format to %d (returned %d)\n", val, ossdev->format);
323             if (strict_format)
324                 goto error;
325         }
326     }
327     if (ossdev->stereo>=0)
328     {
329         val = ossdev->stereo;
330         rc = ioctl(fd, SNDCTL_DSP_STEREO, &ossdev->stereo);
331         if (rc != 0 || val != ossdev->stereo) {
332             TRACE("Can't set stereo to %u (returned %d)\n", val, ossdev->stereo);
333             if (strict_format)
334                 goto error;
335         }
336     }
337     if (ossdev->sample_rate>=0)
338     {
339         val = ossdev->sample_rate;
340         rc = ioctl(fd, SNDCTL_DSP_SPEED, &ossdev->sample_rate);
341         if (rc != 0 || !NEAR_MATCH(val, ossdev->sample_rate)) {
342             TRACE("Can't set sample_rate to %u (returned %d)\n", val, ossdev->sample_rate);
343             if (strict_format)
344                 goto error;
345         }
346     }
347     ossdev->fd = fd;
348
349     if (ossdev->bTriggerSupport) {
350         int trigger;
351         rc = ioctl(fd, SNDCTL_DSP_GETTRIGGER, &trigger);
352         if (rc != 0) {
353             ERR("ioctl(%s, SNDCTL_DSP_GETTRIGGER) failed (%s)\n", 
354                 ossdev->dev_name, strerror(errno));
355             goto error;
356         }
357         
358         ossdev->bOutputEnabled = ((trigger & PCM_ENABLE_OUTPUT) == PCM_ENABLE_OUTPUT);
359         ossdev->bInputEnabled  = ((trigger & PCM_ENABLE_INPUT) == PCM_ENABLE_INPUT);
360     } else {
361         ossdev->bOutputEnabled = TRUE;  /* OSS enables by default */
362         ossdev->bInputEnabled  = TRUE;  /* OSS enables by default */
363     }
364
365     return MMSYSERR_NOERROR;
366
367 error:
368     close(fd);
369     return WAVERR_BADFORMAT;
370 error2:
371     close(fd);
372     return MMSYSERR_ERROR;
373 }
374
375 /******************************************************************
376  *              OSS_OpenDevice
377  *
378  * since OSS has poor capabilities in full duplex, we try here to let a program
379  * open the device for both waveout and wavein streams...
380  * this is hackish, but it's the way OSS interface is done...
381  */
382 static DWORD OSS_OpenDevice(OSS_DEVICE* ossdev, unsigned req_access,
383                             int* frag, int strict_format,
384                             int sample_rate, int stereo, int fmt)
385 {
386     DWORD       ret;
387     TRACE("(%p,%u,%p,%d,%d,%d,%x)\n",ossdev,req_access,frag,strict_format,sample_rate,stereo,fmt);
388
389     if (ossdev->full_duplex && (req_access == O_RDONLY || req_access == O_WRONLY))
390         req_access = O_RDWR;
391
392     /* FIXME: this should be protected, and it also contains a race with OSS_CloseDevice */
393     if (ossdev->open_count == 0)
394     {
395         if (access(ossdev->dev_name, 0) != 0) return MMSYSERR_NODRIVER;
396
397         ossdev->audio_fragment = (frag) ? *frag : 0;
398         ossdev->sample_rate = sample_rate;
399         ossdev->stereo = stereo;
400         ossdev->format = fmt;
401         ossdev->open_access = req_access;
402         ossdev->owner_tid = GetCurrentThreadId();
403
404         if ((ret = OSS_RawOpenDevice(ossdev,strict_format)) != MMSYSERR_NOERROR) return ret;
405     }
406     else
407     {
408         /* check we really open with the same parameters */
409         if (ossdev->open_access != req_access)
410         {
411             ERR("FullDuplex: Mismatch in access. Your sound device is not full duplex capable.\n");
412             return WAVERR_BADFORMAT;
413         }
414
415         /* check if the audio parameters are the same */
416         if (ossdev->sample_rate != sample_rate ||
417             ossdev->stereo != stereo ||
418             ossdev->format != fmt)
419         {
420             /* This is not a fatal error because MSACM might do the remapping */ 
421             WARN("FullDuplex: mismatch in PCM parameters for input and output\n"
422                  "OSS doesn't allow us different parameters\n"
423                  "audio_frag(%x/%x) sample_rate(%d/%d) stereo(%d/%d) fmt(%d/%d)\n",
424                  ossdev->audio_fragment, frag ? *frag : 0,
425                  ossdev->sample_rate, sample_rate,
426                  ossdev->stereo, stereo,
427                  ossdev->format, fmt);
428             return WAVERR_BADFORMAT;
429         }
430         /* check if the fragment sizes are the same */
431         if (ossdev->audio_fragment != (frag ? *frag : 0) ) {
432             ERR("FullDuplex: Playback and Capture hardware acceleration levels are different.\n"
433                 "Use: \"HardwareAcceleration\" = \"Emulation\" in the [dsound] section of your config file.\n");
434             return WAVERR_BADFORMAT;
435         }
436         if (GetCurrentThreadId() != ossdev->owner_tid)
437         {
438             WARN("Another thread is trying to access audio...\n");
439             return MMSYSERR_ERROR;
440         }
441     }
442
443     ossdev->open_count++;
444
445     return MMSYSERR_NOERROR;
446 }
447
448 /******************************************************************
449  *              OSS_CloseDevice
450  *
451  *
452  */
453 static void     OSS_CloseDevice(OSS_DEVICE* ossdev)
454 {
455     TRACE("(%p)\n",ossdev);
456     if (ossdev->open_count>0) {
457         ossdev->open_count--;
458     } else {
459         WARN("OSS_CloseDevice called too many times\n");
460     }
461     if (ossdev->open_count == 0)
462     {
463        /* reset the device before we close it in case it is in a bad state */
464        ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
465        close(ossdev->fd);
466     }
467 }
468
469 /******************************************************************
470  *              OSS_ResetDevice
471  *
472  * Resets the device. OSS Commercial requires the device to be closed
473  * after a SNDCTL_DSP_RESET ioctl call... this function implements
474  * this behavior...
475  * FIXME: This causes problems when doing full duplex so we really
476  * only reset when not doing full duplex. We need to do this better
477  * someday. 
478  */
479 static DWORD     OSS_ResetDevice(OSS_DEVICE* ossdev)
480 {
481     DWORD       ret = MMSYSERR_NOERROR;
482     int         old_fd = ossdev->fd;
483     TRACE("(%p)\n", ossdev);
484
485     if (ossdev->open_count == 1) {
486         if (ioctl(ossdev->fd, SNDCTL_DSP_RESET, NULL) == -1) 
487         {
488             perror("ioctl SNDCTL_DSP_RESET");
489             return -1;
490         }
491         close(ossdev->fd);
492         ret = OSS_RawOpenDevice(ossdev, 1);
493         TRACE("Changing fd from %d to %d\n", old_fd, ossdev->fd);
494     } else 
495         WARN("Not resetting device because it is in full duplex mode!\n");
496     
497     return ret;
498 }
499
500 const static int win_std_oss_fmts[2]={AFMT_U8,AFMT_S16_LE};
501 const static int win_std_rates[5]={96000,48000,44100,22050,11025};
502 const static int win_std_formats[2][2][5]=
503     {{{WAVE_FORMAT_96M08, WAVE_FORMAT_48M08, WAVE_FORMAT_4M08,
504        WAVE_FORMAT_2M08,  WAVE_FORMAT_1M08},
505       {WAVE_FORMAT_96S08, WAVE_FORMAT_48S08, WAVE_FORMAT_4S08,
506        WAVE_FORMAT_2S08,  WAVE_FORMAT_1S08}},
507      {{WAVE_FORMAT_96M16, WAVE_FORMAT_48M16, WAVE_FORMAT_4M16,
508        WAVE_FORMAT_2M16,  WAVE_FORMAT_1M16},
509       {WAVE_FORMAT_96S16, WAVE_FORMAT_48S16, WAVE_FORMAT_4S16,
510        WAVE_FORMAT_2S16,  WAVE_FORMAT_1S16}},
511     };
512
513 /******************************************************************
514  *              OSS_WaveOutInit
515  *
516  *
517  */
518 static BOOL OSS_WaveOutInit(OSS_DEVICE* ossdev)
519 {
520     int rc,arg;
521     int f,c,r;
522     TRACE("(%p) %s\n", ossdev, ossdev->dev_name);
523
524     if (OSS_OpenDevice(ossdev, O_WRONLY, NULL, 0,-1,-1,-1) != 0) return FALSE;
525     ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
526
527 #ifdef SOUND_MIXER_INFO
528     {
529         int mixer;
530         if ((mixer = open(ossdev->mixer_name, O_RDONLY|O_NDELAY)) >= 0) {
531             mixer_info info;
532             if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
533                 strncpy(ossdev->ds_desc.szDesc, info.name, sizeof(info.name));
534                 strcpy(ossdev->ds_desc.szDrvName, "wineoss.drv");
535                 strncpy(ossdev->out_caps.szPname, info.name, sizeof(info.name));
536                 TRACE("%s\n", ossdev->ds_desc.szDesc);
537             } else {
538                 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
539                  * implement it properly, and there are probably similar issues
540                  * on other platforms, so we warn but try to go ahead.
541                  */
542                 WARN("%s: cannot read SOUND_MIXER_INFO!\n", ossdev->mixer_name);
543             }
544             close(mixer);
545         } else {
546             ERR("%s: %s\n", ossdev->mixer_name , strerror( errno ));
547             OSS_CloseDevice(ossdev);
548             return FALSE;
549         }
550     }
551 #endif /* SOUND_MIXER_INFO */
552
553     /* FIXME: some programs compare this string against the content of the
554      * registry for MM drivers. The names have to match in order for the
555      * program to work (e.g. MS win9x mplayer.exe)
556      */
557 #ifdef EMULATE_SB16
558     ossdev->out_caps.wMid = 0x0002;
559     ossdev->out_caps.wPid = 0x0104;
560     strcpy(ossdev->out_caps.szPname, "SB16 Wave Out");
561 #else
562     ossdev->out_caps.wMid = 0x00FF; /* Manufac ID */
563     ossdev->out_caps.wPid = 0x0001; /* Product ID */
564 #endif
565     ossdev->out_caps.vDriverVersion = 0x0100;
566     ossdev->out_caps.wChannels = 1;
567     ossdev->out_caps.dwFormats = 0x00000000;
568     ossdev->out_caps.wReserved1 = 0;
569     ossdev->out_caps.dwSupport = WAVECAPS_VOLUME;
570
571     /* direct sound caps */
572     ossdev->ds_caps.dwFlags = 0;
573     ossdev->ds_caps.dwPrimaryBuffers = 1;
574     ossdev->ds_caps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
575     ossdev->ds_caps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
576                                                                                 
577     if (WINE_TRACE_ON(wave)) {
578         /* Note that this only reports the formats supported by the hardware.
579          * The driver may support other formats and do the conversions in
580          * software which is why we don't use this value
581          */
582         int oss_mask, oss_caps;
583         ioctl(ossdev->fd, SNDCTL_DSP_GETFMTS, &oss_mask);
584         TRACE("OSS dsp out mask=%08x ( ", oss_mask);
585         if (oss_mask & AFMT_MU_LAW) TRACE("AFMT_MU_LAW ");
586         if (oss_mask & AFMT_A_LAW) TRACE("AFMT_A_LAW ");
587         if (oss_mask & AFMT_IMA_ADPCM) TRACE("AFMT_IMA_ADPCM ");
588         if (oss_mask & AFMT_U8) TRACE("AFMT_U8 ");
589         if (oss_mask & AFMT_S16_LE) TRACE("AFMT_S16_LE ");
590         if (oss_mask & AFMT_S16_BE) TRACE("AFMT_S16_BE ");
591         if (oss_mask & AFMT_S8) TRACE("AFMT_S8 ");
592         if (oss_mask & AFMT_U16_LE) TRACE("AFMT_U16_LE ");
593         if (oss_mask & AFMT_U16_BE) TRACE("AFMT_U16_BE ");
594         if (oss_mask & AFMT_MPEG) TRACE("AFMT_MPEG ");
595 #ifdef AFMT_AC3
596         if (oss_mask & AFMT_AC3) TRACE("AFMT_AC3 ");
597 #endif
598         TRACE(")\n");
599         ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &oss_caps);
600         TRACE("Caps=%08x\n",oss_caps);
601         TRACE("\tRevision: %d\n", oss_caps&DSP_CAP_REVISION);
602         TRACE("\tDuplex: %s\n", oss_caps & DSP_CAP_DUPLEX ? "true" : "false");
603         TRACE("\tRealtime: %s\n", oss_caps & DSP_CAP_REALTIME ? "true" : "false");
604         TRACE("\tBatch: %s\n", oss_caps & DSP_CAP_BATCH ? "true" : "false");
605         TRACE("\tCoproc: %s\n", oss_caps & DSP_CAP_COPROC ? "true" : "false");
606         TRACE("\tTrigger: %s\n", oss_caps & DSP_CAP_TRIGGER ? "true" : "false");
607         TRACE("\tMmap: %s\n", oss_caps & DSP_CAP_MMAP ? "true" : "false");
608 #ifdef DSP_CAP_MULTI
609         TRACE("\tMulti: %s\n", oss_caps & DSP_CAP_MULTI ? "true" : "false");
610 #endif
611 #ifdef DSP_CAP_BIND
612         TRACE("\tBind: %s\n", oss_caps & DSP_CAP_BIND ? "true" : "false");
613 #endif
614     }
615
616     /* We must first set the format and the stereo mode as some sound cards
617      * may support 44kHz mono but not 44kHz stereo. Also we must
618      * systematically check the return value of these ioctls as they will
619      * always succeed (see OSS Linux) but will modify the parameter to match
620      * whatever they support. The OSS specs also say we must first set the
621      * sample size, then the stereo and then the sample rate.
622      */
623     for (f=0;f<2;f++) {
624         arg=win_std_oss_fmts[f];
625         rc=ioctl(ossdev->fd, SNDCTL_DSP_SAMPLESIZE, &arg);
626         if (rc!=0 || arg!=win_std_oss_fmts[f]) {
627             TRACE("DSP_SAMPLESIZE: rc=%d returned %d for %d\n",
628                   rc,arg,win_std_oss_fmts[f]);
629             continue;
630         }
631         if (f == 0) 
632             ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARY8BIT;
633         else if (f == 1)
634             ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARY16BIT;
635
636         for (c=0;c<2;c++) {
637             arg=c;
638             rc=ioctl(ossdev->fd, SNDCTL_DSP_STEREO, &arg);
639             if (rc!=0 || arg!=c) {
640                 TRACE("DSP_STEREO: rc=%d returned %d for %d\n",rc,arg,c);
641                 continue;
642             }
643             if (c == 0) {
644                 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYMONO;
645             } else if (c==1) {
646                 ossdev->out_caps.wChannels=2;
647                 ossdev->out_caps.dwSupport|=WAVECAPS_LRVOLUME;
648                 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYSTEREO;
649             }
650
651             for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
652                 arg=win_std_rates[r];
653                 rc=ioctl(ossdev->fd, SNDCTL_DSP_SPEED, &arg);
654                 TRACE("DSP_SPEED: rc=%d returned %d for %dx%dx%d\n",
655                       rc,arg,win_std_rates[r],win_std_oss_fmts[f],c+1);
656                 if (rc==0 && arg!=0 && NEAR_MATCH(arg,win_std_rates[r]))
657                     ossdev->out_caps.dwFormats|=win_std_formats[f][c][r];
658             }
659         }
660     }
661
662     if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &arg) == 0) {
663         TRACE("OSS dsp out caps=%08X\n", arg);
664         if (arg & DSP_CAP_TRIGGER)
665             ossdev->bTriggerSupport = TRUE;
666         if ((arg & DSP_CAP_REALTIME) && !(arg & DSP_CAP_BATCH)) {
667             ossdev->out_caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
668         }
669         /* well, might as well use the DirectSound cap flag for something */
670         if ((arg & DSP_CAP_TRIGGER) && (arg & DSP_CAP_MMAP) &&
671             !(arg & DSP_CAP_BATCH)) {
672             ossdev->out_caps.dwSupport |= WAVECAPS_DIRECTSOUND;
673         } else {
674             ossdev->ds_caps.dwFlags |= DSCAPS_EMULDRIVER;
675         }
676 #ifdef DSP_CAP_MULTI    /* not every oss has this */
677         /* check for hardware secondary buffer support (multi open) */
678         if ((arg & DSP_CAP_MULTI) &&
679             (ossdev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
680             TRACE("hardware secondary buffer support available\n");
681             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARY8BIT)
682                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARY8BIT;
683             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARY16BIT)
684                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARY16BIT;
685             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARYMONO)
686                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARYMONO;
687             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARYSTEREO)
688                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARYSTEREO;
689
690             ossdev->ds_caps.dwMaxHwMixingAllBuffers = 16;
691             ossdev->ds_caps.dwMaxHwMixingStaticBuffers = 0;
692             ossdev->ds_caps.dwMaxHwMixingStreamingBuffers = 16;
693
694             ossdev->ds_caps.dwFreeHwMixingAllBuffers = 16;
695             ossdev->ds_caps.dwFreeHwMixingStaticBuffers = 0;
696             ossdev->ds_caps.dwFreeHwMixingStreamingBuffers = 16;
697         }
698 #endif
699     }
700     OSS_CloseDevice(ossdev);
701     TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
702           ossdev->out_caps.dwFormats, ossdev->out_caps.dwSupport);
703     return TRUE;
704 }
705
706 /******************************************************************
707  *              OSS_WaveInInit
708  *
709  *
710  */
711 static BOOL OSS_WaveInInit(OSS_DEVICE* ossdev)
712 {
713     int rc,arg;
714     int f,c,r;
715     TRACE("(%p) %s\n", ossdev, ossdev->dev_name);
716
717     if (OSS_OpenDevice(ossdev, O_RDONLY, NULL, 0,-1,-1,-1) != 0)
718         return FALSE;
719
720     ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
721
722 #ifdef SOUND_MIXER_INFO
723     {
724         int mixer;
725         if ((mixer = open(ossdev->mixer_name, O_RDONLY|O_NDELAY)) >= 0) {
726             mixer_info info;
727             if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
728                 strncpy(ossdev->in_caps.szPname, info.name, sizeof(info.name));
729                 TRACE("%s\n", ossdev->ds_desc.szDesc);
730             } else {
731                 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
732                  * implement it properly, and there are probably similar issues
733                  * on other platforms, so we warn but try to go ahead.
734                  */
735                 WARN("%s: cannot read SOUND_MIXER_INFO!\n", ossdev->mixer_name);
736             }
737             close(mixer);
738         } else {
739             ERR("%s: %s\n", ossdev->mixer_name, strerror(errno));
740             OSS_CloseDevice(ossdev);
741             return FALSE;
742         }
743     }
744 #endif /* SOUND_MIXER_INFO */
745
746     /* See comment in OSS_WaveOutInit */
747 #ifdef EMULATE_SB16
748     ossdev->in_caps.wMid = 0x0002;
749     ossdev->in_caps.wPid = 0x0004;
750     strcpy(ossdev->in_caps.szPname, "SB16 Wave In");
751 #else
752     ossdev->in_caps.wMid = 0x00FF; /* Manufac ID */
753     ossdev->in_caps.wPid = 0x0001; /* Product ID */
754 #endif
755     ossdev->in_caps.dwFormats = 0x00000000;
756     ossdev->in_caps.wChannels = 1;
757     ossdev->in_caps.wReserved1 = 0;
758
759     /* direct sound caps */
760     ossdev->dsc_caps.dwSize = sizeof(ossdev->dsc_caps);
761     ossdev->dsc_caps.dwFlags = 0;
762     ossdev->dsc_caps.dwFormats = 0x00000000;
763     ossdev->dsc_caps.dwChannels = 1;
764
765     if (WINE_TRACE_ON(wave)) {
766         /* Note that this only reports the formats supported by the hardware.
767          * The driver may support other formats and do the conversions in
768          * software which is why we don't use this value
769          */
770         int oss_mask;
771         ioctl(ossdev->fd, SNDCTL_DSP_GETFMTS, &oss_mask);
772         TRACE("OSS dsp out mask=%08x\n", oss_mask);
773     }
774
775     /* See the comment in OSS_WaveOutInit */
776     for (f=0;f<2;f++) {
777         arg=win_std_oss_fmts[f];
778         rc=ioctl(ossdev->fd, SNDCTL_DSP_SAMPLESIZE, &arg);
779         if (rc!=0 || arg!=win_std_oss_fmts[f]) {
780             TRACE("DSP_SAMPLESIZE: rc=%d returned 0x%x for 0x%x\n",
781                   rc,arg,win_std_oss_fmts[f]);
782             continue;
783         }
784
785         for (c=0;c<2;c++) {
786             arg=c;
787             rc=ioctl(ossdev->fd, SNDCTL_DSP_STEREO, &arg);
788             if (rc!=0 || arg!=c) {
789                 TRACE("DSP_STEREO: rc=%d returned %d for %d\n",rc,arg,c);
790                 continue;
791             }
792             if (c==1) {
793                 ossdev->in_caps.wChannels=2;
794                 ossdev->dsc_caps.dwChannels=2;
795             }
796
797             for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
798                 arg=win_std_rates[r];
799                 rc=ioctl(ossdev->fd, SNDCTL_DSP_SPEED, &arg);
800                 TRACE("DSP_SPEED: rc=%d returned %d for %dx%dx%d\n",rc,arg,win_std_rates[r],win_std_oss_fmts[f],c+1);
801                 if (rc==0 && NEAR_MATCH(arg,win_std_rates[r]))
802                     ossdev->in_caps.dwFormats|=win_std_formats[f][c][r];
803                     ossdev->dsc_caps.dwFormats|=win_std_formats[f][c][r];
804             }
805         }
806     }
807
808     if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &arg) == 0) {
809         TRACE("OSS dsp in caps=%08X\n", arg);
810         if (arg & DSP_CAP_TRIGGER)
811             ossdev->bTriggerSupport = TRUE;
812         if ((arg & DSP_CAP_TRIGGER) && (arg & DSP_CAP_MMAP) &&
813             !(arg & DSP_CAP_BATCH)) {
814             /* FIXME: enable the next statement if you want to work on the driver */
815 #if 0
816             ossdev->in_caps_support |= WAVECAPS_DIRECTSOUND;
817 #endif
818         }
819         if ((arg & DSP_CAP_REALTIME) && !(arg & DSP_CAP_BATCH))
820             ossdev->in_caps_support |= WAVECAPS_SAMPLEACCURATE;
821     }
822     OSS_CloseDevice(ossdev);
823     TRACE("in dwFormats = %08lX\n", ossdev->in_caps.dwFormats);
824     return TRUE;
825 }
826
827 /******************************************************************
828  *              OSS_WaveFullDuplexInit
829  *
830  *
831  */
832 static void OSS_WaveFullDuplexInit(OSS_DEVICE* ossdev)
833 {
834     int         caps;
835     TRACE("(%p)\n",ossdev);
836
837     if (OSS_OpenDevice(ossdev, O_RDWR, NULL, 0,-1,-1,-1) != 0) return;
838     if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &caps) == 0)
839     {
840         ossdev->full_duplex = (caps & DSP_CAP_DUPLEX);
841     }
842     OSS_CloseDevice(ossdev);
843 }
844
845 #define INIT_GUID(guid, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)      \
846         guid.Data1 = l; guid.Data2 = w1; guid.Data3 = w2;               \
847         guid.Data4[0] = b1; guid.Data4[1] = b2; guid.Data4[2] = b3;     \
848         guid.Data4[3] = b4; guid.Data4[4] = b5; guid.Data4[5] = b6;     \
849         guid.Data4[6] = b7; guid.Data4[7] = b8;
850 /******************************************************************
851  *              OSS_WaveInit
852  *
853  * Initialize internal structures from OSS information
854  */
855 LONG OSS_WaveInit(void)
856 {
857     int         i;
858     TRACE("()\n");
859
860     for (i = 0; i < MAX_WAVEDRV; ++i)
861     {
862         if (i == 0) {
863             sprintf((char *)OSS_Devices[i].dev_name, "/dev/dsp");
864             sprintf((char *)OSS_Devices[i].mixer_name, "/dev/mixer");
865         } else {
866             sprintf((char *)OSS_Devices[i].dev_name, "/dev/dsp%d", i);
867             sprintf((char *)OSS_Devices[i].mixer_name, "/dev/mixer%d", i);
868         }
869
870         INIT_GUID(OSS_Devices[i].ds_guid,  0xbd6dd71a, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i);
871         INIT_GUID(OSS_Devices[i].dsc_guid, 0xbd6dd71b, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i);
872     }
873
874     /* start with output devices */
875     for (i = 0; i < MAX_WAVEDRV; ++i)
876     {
877         if (OSS_WaveOutInit(&OSS_Devices[i]))
878         {
879             WOutDev[numOutDev].state = WINE_WS_CLOSED;
880             WOutDev[numOutDev].ossdev = &OSS_Devices[i];
881             WOutDev[numOutDev].volume = 0xffffffff;
882             numOutDev++;
883         }
884     }
885
886     /* then do input devices */
887     for (i = 0; i < MAX_WAVEDRV; ++i)
888     {
889         if (OSS_WaveInInit(&OSS_Devices[i]))
890         {
891             WInDev[numInDev].state = WINE_WS_CLOSED;
892             WInDev[numInDev].ossdev = &OSS_Devices[i];
893             numInDev++;
894         }
895     }
896
897     /* finish with the full duplex bits */
898     for (i = 0; i < MAX_WAVEDRV; i++)
899         OSS_WaveFullDuplexInit(&OSS_Devices[i]);
900
901     return 0;
902 }
903
904 /******************************************************************
905  *              OSS_InitRingMessage
906  *
907  * Initialize the ring of messages for passing between driver's caller and playback/record
908  * thread
909  */
910 static int OSS_InitRingMessage(OSS_MSG_RING* omr)
911 {
912     omr->msg_toget = 0;
913     omr->msg_tosave = 0;
914 #ifdef USE_PIPE_SYNC
915     if (pipe(omr->msg_pipe) < 0) {
916         omr->msg_pipe[0] = -1;
917         omr->msg_pipe[1] = -1;
918         ERR("could not create pipe, error=%s\n", strerror(errno));
919     }
920 #else
921     omr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
922 #endif
923     omr->ring_buffer_size = OSS_RING_BUFFER_INCREMENT;
924     omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(OSS_MSG));
925     InitializeCriticalSection(&omr->msg_crst);
926     return 0;
927 }
928
929 /******************************************************************
930  *              OSS_DestroyRingMessage
931  *
932  */
933 static int OSS_DestroyRingMessage(OSS_MSG_RING* omr)
934 {
935 #ifdef USE_PIPE_SYNC
936     close(omr->msg_pipe[0]);
937     close(omr->msg_pipe[1]);
938 #else
939     CloseHandle(omr->msg_event);
940 #endif
941     HeapFree(GetProcessHeap(),0,omr->messages);
942     DeleteCriticalSection(&omr->msg_crst);
943     return 0;
944 }
945
946 /******************************************************************
947  *              OSS_AddRingMessage
948  *
949  * Inserts a new message into the ring (should be called from DriverProc derivated routines)
950  */
951 static int OSS_AddRingMessage(OSS_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
952 {
953     HANDLE      hEvent = INVALID_HANDLE_VALUE;
954
955     EnterCriticalSection(&omr->msg_crst);
956     if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
957     {
958         int old_ring_buffer_size = omr->ring_buffer_size;
959         omr->ring_buffer_size += OSS_RING_BUFFER_INCREMENT;
960         TRACE("omr->ring_buffer_size=%d\n",omr->ring_buffer_size);
961         omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(OSS_MSG));
962         /* Now we need to rearrange the ring buffer so that the new
963            buffers just allocated are in between omr->msg_tosave and
964            omr->msg_toget.
965         */
966         if (omr->msg_tosave < omr->msg_toget)
967         {
968             memmove(&(omr->messages[omr->msg_toget + OSS_RING_BUFFER_INCREMENT]),
969                     &(omr->messages[omr->msg_toget]),
970                     sizeof(OSS_MSG)*(old_ring_buffer_size - omr->msg_toget)
971                     );
972             omr->msg_toget += OSS_RING_BUFFER_INCREMENT;
973         }
974     }
975     if (wait)
976     {
977         hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
978         if (hEvent == INVALID_HANDLE_VALUE)
979         {
980             ERR("can't create event !?\n");
981             LeaveCriticalSection(&omr->msg_crst);
982             return 0;
983         }
984         if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
985             FIXME("two fast messages in the queue!!!!\n");
986
987         /* fast messages have to be added at the start of the queue */
988         omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
989
990         omr->messages[omr->msg_toget].msg = msg;
991         omr->messages[omr->msg_toget].param = param;
992         omr->messages[omr->msg_toget].hEvent = hEvent;
993     }
994     else
995     {
996         omr->messages[omr->msg_tosave].msg = msg;
997         omr->messages[omr->msg_tosave].param = param;
998         omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
999         omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
1000     }
1001     LeaveCriticalSection(&omr->msg_crst);
1002     /* signal a new message */
1003     SIGNAL_OMR(omr);
1004     if (wait)
1005     {
1006         /* wait for playback/record thread to have processed the message */
1007         WaitForSingleObject(hEvent, INFINITE);
1008         CloseHandle(hEvent);
1009     }
1010     return 1;
1011 }
1012
1013 /******************************************************************
1014  *              OSS_RetrieveRingMessage
1015  *
1016  * Get a message from the ring. Should be called by the playback/record thread.
1017  */
1018 static int OSS_RetrieveRingMessage(OSS_MSG_RING* omr,
1019                                    enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
1020 {
1021     EnterCriticalSection(&omr->msg_crst);
1022
1023     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1024     {
1025         LeaveCriticalSection(&omr->msg_crst);
1026         return 0;
1027     }
1028
1029     *msg = omr->messages[omr->msg_toget].msg;
1030     omr->messages[omr->msg_toget].msg = 0;
1031     *param = omr->messages[omr->msg_toget].param;
1032     *hEvent = omr->messages[omr->msg_toget].hEvent;
1033     omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
1034     CLEAR_OMR(omr);
1035     LeaveCriticalSection(&omr->msg_crst);
1036     return 1;
1037 }
1038
1039 /******************************************************************
1040  *              OSS_PeekRingMessage
1041  *
1042  * Peek at a message from the ring but do not remove it.
1043  * Should be called by the playback/record thread.
1044  */
1045 static int OSS_PeekRingMessage(OSS_MSG_RING* omr,
1046                                enum win_wm_message *msg,
1047                                DWORD *param, HANDLE *hEvent)
1048 {
1049     EnterCriticalSection(&omr->msg_crst);
1050
1051     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1052     {
1053         LeaveCriticalSection(&omr->msg_crst);
1054         return 0;
1055     }
1056
1057     *msg = omr->messages[omr->msg_toget].msg;
1058     *param = omr->messages[omr->msg_toget].param;
1059     *hEvent = omr->messages[omr->msg_toget].hEvent;
1060     LeaveCriticalSection(&omr->msg_crst);
1061     return 1;
1062 }
1063
1064 /*======================================================================*
1065  *                  Low level WAVE OUT implementation                   *
1066  *======================================================================*/
1067
1068 /**************************************************************************
1069  *                      wodNotifyClient                 [internal]
1070  */
1071 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1072 {
1073     TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg,
1074         wMsg == WOM_OPEN ? "WOM_OPEN" : wMsg == WOM_CLOSE ? "WOM_CLOSE" :
1075         wMsg == WOM_DONE ? "WOM_DONE" : "Unknown", dwParam1, dwParam2);
1076
1077     switch (wMsg) {
1078     case WOM_OPEN:
1079     case WOM_CLOSE:
1080     case WOM_DONE:
1081         if (wwo->wFlags != DCB_NULL &&
1082             !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
1083                             (HDRVR)wwo->waveDesc.hWave, wMsg,
1084                             wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
1085             WARN("can't notify client !\n");
1086             return MMSYSERR_ERROR;
1087         }
1088         break;
1089     default:
1090         FIXME("Unknown callback message %u\n", wMsg);
1091         return MMSYSERR_INVALPARAM;
1092     }
1093     return MMSYSERR_NOERROR;
1094 }
1095
1096 /**************************************************************************
1097  *                              wodUpdatePlayedTotal    [internal]
1098  *
1099  */
1100 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, audio_buf_info* info)
1101 {
1102     audio_buf_info dspspace;
1103     if (!info) info = &dspspace;
1104
1105     if (ioctl(wwo->ossdev->fd, SNDCTL_DSP_GETOSPACE, info) < 0) {
1106         ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n", wwo->ossdev->dev_name, strerror(errno));
1107         return FALSE;
1108     }
1109     wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwBufferSize - info->bytes);
1110     return TRUE;
1111 }
1112
1113 /**************************************************************************
1114  *                              wodPlayer_BeginWaveHdr          [internal]
1115  *
1116  * Makes the specified lpWaveHdr the currently playing wave header.
1117  * If the specified wave header is a begin loop and we're not already in
1118  * a loop, setup the loop.
1119  */
1120 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1121 {
1122     wwo->lpPlayPtr = lpWaveHdr;
1123
1124     if (!lpWaveHdr) return;
1125
1126     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
1127         if (wwo->lpLoopPtr) {
1128             WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
1129         } else {
1130             TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1131             wwo->lpLoopPtr = lpWaveHdr;
1132             /* Windows does not touch WAVEHDR.dwLoops,
1133              * so we need to make an internal copy */
1134             wwo->dwLoops = lpWaveHdr->dwLoops;
1135         }
1136     }
1137     wwo->dwPartialOffset = 0;
1138 }
1139
1140 /**************************************************************************
1141  *                              wodPlayer_PlayPtrNext           [internal]
1142  *
1143  * Advance the play pointer to the next waveheader, looping if required.
1144  */
1145 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
1146 {
1147     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1148
1149     wwo->dwPartialOffset = 0;
1150     if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
1151         /* We're at the end of a loop, loop if required */
1152         if (--wwo->dwLoops > 0) {
1153             wwo->lpPlayPtr = wwo->lpLoopPtr;
1154         } else {
1155             /* Handle overlapping loops correctly */
1156             if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
1157                 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1158                 /* shall we consider the END flag for the closing loop or for
1159                  * the opening one or for both ???
1160                  * code assumes for closing loop only
1161                  */
1162             } else {
1163                 lpWaveHdr = lpWaveHdr->lpNext;
1164             }
1165             wwo->lpLoopPtr = NULL;
1166             wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
1167         }
1168     } else {
1169         /* We're not in a loop.  Advance to the next wave header */
1170         wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
1171     }
1172
1173     return lpWaveHdr;
1174 }
1175
1176 /**************************************************************************
1177  *                           wodPlayer_DSPWait                  [internal]
1178  * Returns the number of milliseconds to wait for the DSP buffer to write
1179  * one fragment.
1180  */
1181 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
1182 {
1183     /* time for one fragment to be played */
1184     return wwo->dwFragmentSize * 1000 / wwo->format.wf.nAvgBytesPerSec;
1185 }
1186
1187 /**************************************************************************
1188  *                           wodPlayer_NotifyWait               [internal]
1189  * Returns the number of milliseconds to wait before attempting to notify
1190  * completion of the specified wavehdr.
1191  * This is based on the number of bytes remaining to be written in the
1192  * wave.
1193  */
1194 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1195 {
1196     DWORD dwMillis;
1197
1198     if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
1199         dwMillis = 1;
1200     } else {
1201         dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
1202         if (!dwMillis) dwMillis = 1;
1203     }
1204
1205     return dwMillis;
1206 }
1207
1208
1209 /**************************************************************************
1210  *                           wodPlayer_WriteMaxFrags            [internal]
1211  * Writes the maximum number of bytes possible to the DSP and returns
1212  * TRUE iff the current playPtr has been fully played
1213  */
1214 static BOOL wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
1215 {
1216     DWORD       dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1217     DWORD       toWrite = min(dwLength, *bytes);
1218     int         written;
1219     BOOL        ret = FALSE;
1220
1221     TRACE("Writing wavehdr %p.%lu[%lu]/%lu\n",
1222           wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength, toWrite);
1223
1224     if (toWrite > 0)
1225     {
1226         written = write(wwo->ossdev->fd, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite);
1227         if (written <= 0) return FALSE;
1228     }
1229     else
1230         written = 0;
1231
1232     if (written >= dwLength) {
1233         /* If we wrote all current wavehdr, skip to the next one */
1234         wodPlayer_PlayPtrNext(wwo);
1235         ret = TRUE;
1236     } else {
1237         /* Remove the amount written */
1238         wwo->dwPartialOffset += written;
1239     }
1240     *bytes -= written;
1241     wwo->dwWrittenTotal += written;
1242     TRACE("dwWrittenTotal=%lu\n", wwo->dwWrittenTotal);
1243     return ret;
1244 }
1245
1246
1247 /**************************************************************************
1248  *                              wodPlayer_NotifyCompletions     [internal]
1249  *
1250  * Notifies and remove from queue all wavehdrs which have been played to
1251  * the speaker (ie. they have cleared the OSS buffer).  If force is true,
1252  * we notify all wavehdrs and remove them all from the queue even if they
1253  * are unplayed or part of a loop.
1254  */
1255 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1256 {
1257     LPWAVEHDR           lpWaveHdr;
1258
1259     /* Start from lpQueuePtr and keep notifying until:
1260      * - we hit an unwritten wavehdr
1261      * - we hit the beginning of a running loop
1262      * - we hit a wavehdr which hasn't finished playing
1263      */
1264     while ((lpWaveHdr = wwo->lpQueuePtr) &&
1265            (force ||
1266             (lpWaveHdr != wwo->lpPlayPtr &&
1267              lpWaveHdr != wwo->lpLoopPtr &&
1268              lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
1269
1270         wwo->lpQueuePtr = lpWaveHdr->lpNext;
1271
1272         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1273         lpWaveHdr->dwFlags |= WHDR_DONE;
1274
1275         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1276     }
1277     return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
1278         wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
1279 }
1280
1281 /**************************************************************************
1282  *                              wodPlayer_Reset                 [internal]
1283  *
1284  * wodPlayer helper. Resets current output stream.
1285  */
1286 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
1287 {
1288     wodUpdatePlayedTotal(wwo, NULL);
1289     /* updates current notify list */
1290     wodPlayer_NotifyCompletions(wwo, FALSE);
1291
1292     /* flush all possible output */
1293     if (OSS_ResetDevice(wwo->ossdev) != MMSYSERR_NOERROR)
1294     {
1295         wwo->hThread = 0;
1296         wwo->state = WINE_WS_STOPPED;
1297         ExitThread(-1);
1298     }
1299
1300     if (reset) {
1301         enum win_wm_message     msg;
1302         DWORD                   param;
1303         HANDLE                  ev;
1304
1305         /* remove any buffer */
1306         wodPlayer_NotifyCompletions(wwo, TRUE);
1307
1308         wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1309         wwo->state = WINE_WS_STOPPED;
1310         wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1311         /* Clear partial wavehdr */
1312         wwo->dwPartialOffset = 0;
1313
1314         /* remove any existing message in the ring */
1315         EnterCriticalSection(&wwo->msgRing.msg_crst);
1316         /* return all pending headers in queue */
1317         while (OSS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
1318         {
1319             if (msg != WINE_WM_HEADER)
1320             {
1321                 FIXME("shouldn't have headers left\n");
1322                 SetEvent(ev);
1323                 continue;
1324             }
1325             ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
1326             ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
1327
1328             wodNotifyClient(wwo, WOM_DONE, param, 0);
1329         }
1330         RESET_OMR(&wwo->msgRing);
1331         LeaveCriticalSection(&wwo->msgRing.msg_crst);
1332     } else {
1333         if (wwo->lpLoopPtr) {
1334             /* complicated case, not handled yet (could imply modifying the loop counter */
1335             FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
1336             wwo->lpPlayPtr = wwo->lpLoopPtr;
1337             wwo->dwPartialOffset = 0;
1338             wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
1339         } else {
1340             LPWAVEHDR   ptr;
1341             DWORD       sz = wwo->dwPartialOffset;
1342
1343             /* reset all the data as if we had written only up to lpPlayedTotal bytes */
1344             /* compute the max size playable from lpQueuePtr */
1345             for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) {
1346                 sz += ptr->dwBufferLength;
1347             }
1348             /* because the reset lpPlayPtr will be lpQueuePtr */
1349             if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("grin\n");
1350             wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
1351             wwo->dwWrittenTotal = wwo->dwPlayedTotal;
1352             wwo->lpPlayPtr = wwo->lpQueuePtr;
1353         }
1354         wwo->state = WINE_WS_PAUSED;
1355     }
1356 }
1357
1358 /**************************************************************************
1359  *                    wodPlayer_ProcessMessages                 [internal]
1360  */
1361 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
1362 {
1363     LPWAVEHDR           lpWaveHdr;
1364     enum win_wm_message msg;
1365     DWORD               param;
1366     HANDLE              ev;
1367
1368     while (OSS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
1369         TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
1370         switch (msg) {
1371         case WINE_WM_PAUSING:
1372             wodPlayer_Reset(wwo, FALSE);
1373             SetEvent(ev);
1374             break;
1375         case WINE_WM_RESTARTING:
1376             if (wwo->state == WINE_WS_PAUSED)
1377             {
1378                 wwo->state = WINE_WS_PLAYING;
1379             }
1380             SetEvent(ev);
1381             break;
1382         case WINE_WM_HEADER:
1383             lpWaveHdr = (LPWAVEHDR)param;
1384
1385             /* insert buffer at the end of queue */
1386             {
1387                 LPWAVEHDR*      wh;
1388                 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1389                 *wh = lpWaveHdr;
1390             }
1391             if (!wwo->lpPlayPtr)
1392                 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
1393             if (wwo->state == WINE_WS_STOPPED)
1394                 wwo->state = WINE_WS_PLAYING;
1395             break;
1396         case WINE_WM_RESETTING:
1397             wodPlayer_Reset(wwo, TRUE);
1398             SetEvent(ev);
1399             break;
1400         case WINE_WM_UPDATE:
1401             wodUpdatePlayedTotal(wwo, NULL);
1402             SetEvent(ev);
1403             break;
1404         case WINE_WM_BREAKLOOP:
1405             if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
1406                 /* ensure exit at end of current loop */
1407                 wwo->dwLoops = 1;
1408             }
1409             SetEvent(ev);
1410             break;
1411         case WINE_WM_CLOSING:
1412             /* sanity check: this should not happen since the device must have been reset before */
1413             if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1414             wwo->hThread = 0;
1415             wwo->state = WINE_WS_CLOSED;
1416             SetEvent(ev);
1417             ExitThread(0);
1418             /* shouldn't go here */
1419         default:
1420             FIXME("unknown message %d\n", msg);
1421             break;
1422         }
1423     }
1424 }
1425
1426 /**************************************************************************
1427  *                           wodPlayer_FeedDSP                  [internal]
1428  * Feed as much sound data as we can into the DSP and return the number of
1429  * milliseconds before it will be necessary to feed the DSP again.
1430  */
1431 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1432 {
1433     audio_buf_info dspspace;
1434     DWORD       availInQ;
1435
1436     wodUpdatePlayedTotal(wwo, &dspspace);
1437     availInQ = dspspace.bytes;
1438     TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
1439           dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);
1440
1441     /* input queue empty and output buffer with less than one fragment to play 
1442      * actually some cards do not play the fragment before the last if this one is partially feed
1443      * so we need to test for full the availability of 2 fragments
1444      */
1445     if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize && 
1446         !wwo->bNeedPost) {
1447         TRACE("Run out of wavehdr:s...\n");
1448         return INFINITE;
1449     }
1450
1451     /* no more room... no need to try to feed */
1452     if (dspspace.fragments != 0) {
1453         /* Feed from partial wavehdr */
1454         if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
1455             wodPlayer_WriteMaxFrags(wwo, &availInQ);
1456         }
1457
1458         /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1459         if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
1460             do {
1461                 TRACE("Setting time to elapse for %p to %lu\n",
1462                       wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
1463                 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1464                 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1465             } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
1466         }
1467
1468         if (wwo->bNeedPost) {
1469             /* OSS doesn't start before it gets either 2 fragments or a SNDCTL_DSP_POST;
1470              * if it didn't get one, we give it the other */
1471             if (wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize)
1472                 ioctl(wwo->ossdev->fd, SNDCTL_DSP_POST, 0);
1473             wwo->bNeedPost = FALSE;
1474         }
1475     }
1476
1477     return wodPlayer_DSPWait(wwo);
1478 }
1479
1480
1481 /**************************************************************************
1482  *                              wodPlayer                       [internal]
1483  */
1484 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
1485 {
1486     WORD          uDevID = (DWORD)pmt;
1487     WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1488     DWORD         dwNextFeedTime = INFINITE;   /* Time before DSP needs feeding */
1489     DWORD         dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1490     DWORD         dwSleepTime;
1491
1492     wwo->state = WINE_WS_STOPPED;
1493     SetEvent(wwo->hStartUpEvent);
1494
1495     for (;;) {
1496         /** Wait for the shortest time before an action is required.  If there
1497          *  are no pending actions, wait forever for a command.
1498          */
1499         dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1500         TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1501         WAIT_OMR(&wwo->msgRing, dwSleepTime);
1502         wodPlayer_ProcessMessages(wwo);
1503         if (wwo->state == WINE_WS_PLAYING) {
1504             dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1505             dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1506             if (dwNextFeedTime == INFINITE) {
1507                 /* FeedDSP ran out of data, but before flushing, */
1508                 /* check that a notification didn't give us more */
1509                 wodPlayer_ProcessMessages(wwo);
1510                 if (!wwo->lpPlayPtr) {
1511                     TRACE("flushing\n");
1512                     ioctl(wwo->ossdev->fd, SNDCTL_DSP_SYNC, 0);
1513                     wwo->dwPlayedTotal = wwo->dwWrittenTotal;
1514                     dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1515                 } else {
1516                     TRACE("recovering\n");
1517                     dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1518                 }
1519             }
1520         } else {
1521             dwNextFeedTime = dwNextNotifyTime = INFINITE;
1522         }
1523     }
1524 }
1525
1526 /**************************************************************************
1527  *                      wodGetDevCaps                           [internal]
1528  */
1529 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
1530 {
1531     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1532
1533     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1534
1535     if (wDevID >= numOutDev) {
1536         TRACE("numOutDev reached !\n");
1537         return MMSYSERR_BADDEVICEID;
1538     }
1539
1540     memcpy(lpCaps, &WOutDev[wDevID].ossdev->out_caps, min(dwSize, sizeof(*lpCaps)));
1541     return MMSYSERR_NOERROR;
1542 }
1543
1544 /**************************************************************************
1545  *                              wodOpen                         [internal]
1546  */
1547 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1548 {
1549     int                 audio_fragment;
1550     WINE_WAVEOUT*       wwo;
1551     audio_buf_info      info;
1552     DWORD               ret;
1553
1554     TRACE("(%u, %p[cb=%08lx], %08lX);\n", wDevID, lpDesc, lpDesc->dwCallback, dwFlags);
1555     if (lpDesc == NULL) {
1556         WARN("Invalid Parameter !\n");
1557         return MMSYSERR_INVALPARAM;
1558     }
1559     if (wDevID >= numOutDev) {
1560         TRACE("MAX_WAVOUTDRV reached !\n");
1561         return MMSYSERR_BADDEVICEID;
1562     }
1563
1564     /* only PCM format is supported so far... */
1565     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1566         lpDesc->lpFormat->nChannels == 0 ||
1567         lpDesc->lpFormat->nSamplesPerSec == 0) {
1568         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1569              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1570              lpDesc->lpFormat->nSamplesPerSec);
1571         return WAVERR_BADFORMAT;
1572     }
1573
1574     if (dwFlags & WAVE_FORMAT_QUERY) {
1575         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1576              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1577              lpDesc->lpFormat->nSamplesPerSec);
1578         return MMSYSERR_NOERROR;
1579     }
1580
1581     wwo = &WOutDev[wDevID];
1582
1583     if ((dwFlags & WAVE_DIRECTSOUND) && 
1584         !(wwo->ossdev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND))
1585         /* not supported, ignore it */
1586         dwFlags &= ~WAVE_DIRECTSOUND;
1587
1588     if (dwFlags & WAVE_DIRECTSOUND) {
1589         if (wwo->ossdev->out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
1590             /* we have realtime DirectSound, fragments just waste our time,
1591              * but a large buffer is good, so choose 64KB (32 * 2^11) */
1592             audio_fragment = 0x0020000B;
1593         else
1594             /* to approximate realtime, we must use small fragments,
1595              * let's try to fragment the above 64KB (256 * 2^8) */
1596             audio_fragment = 0x01000008;
1597     } else {
1598         /* A wave device must have a worst case latency of 10 ms so calculate
1599          * the largest fragment size less than 10 ms long.
1600          */
1601         int     fsize = lpDesc->lpFormat->nAvgBytesPerSec / 100;        /* 10 ms chunk */
1602         int     shift = 0;
1603         while ((1 << shift) <= fsize)
1604             shift++;
1605         shift--;
1606         audio_fragment = 0x00100000 + shift;    /* 16 fragments of 2^shift */
1607     }
1608
1609     TRACE("using %d %d byte fragments (%ld ms)\n", audio_fragment >> 16,
1610         1 << (audio_fragment & 0xffff), 
1611         ((1 << (audio_fragment & 0xffff)) * 1000) / lpDesc->lpFormat->nAvgBytesPerSec);
1612
1613     if (wwo->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED;
1614     /* we want to be able to mmap() the device, which means it must be opened readable,
1615      * otherwise mmap() will fail (at least under Linux) */
1616     ret = OSS_OpenDevice(wwo->ossdev,
1617                          (dwFlags & WAVE_DIRECTSOUND) ? O_RDWR : O_WRONLY,
1618                          &audio_fragment,
1619                          (dwFlags & WAVE_DIRECTSOUND) ? 0 : 1,
1620                          lpDesc->lpFormat->nSamplesPerSec,
1621                          (lpDesc->lpFormat->nChannels > 1) ? 1 : 0,
1622                          (lpDesc->lpFormat->wBitsPerSample == 16)
1623                              ? AFMT_S16_LE : AFMT_U8);
1624     if ((ret==MMSYSERR_NOERROR) && (dwFlags & WAVE_DIRECTSOUND)) {
1625         lpDesc->lpFormat->nSamplesPerSec=wwo->ossdev->sample_rate;
1626         lpDesc->lpFormat->nChannels=(wwo->ossdev->stereo ? 2 : 1);
1627         lpDesc->lpFormat->wBitsPerSample=(wwo->ossdev->format == AFMT_U8 ? 8 : 16);
1628         lpDesc->lpFormat->nBlockAlign=lpDesc->lpFormat->nChannels*lpDesc->lpFormat->wBitsPerSample/8;
1629         lpDesc->lpFormat->nAvgBytesPerSec=lpDesc->lpFormat->nSamplesPerSec*lpDesc->lpFormat->nBlockAlign;
1630         TRACE("OSS_OpenDevice returned this format: %ldx%dx%d\n",
1631               lpDesc->lpFormat->nSamplesPerSec,
1632               lpDesc->lpFormat->wBitsPerSample,
1633               lpDesc->lpFormat->nChannels);
1634     }
1635     if (ret != 0) return ret;
1636     wwo->state = WINE_WS_STOPPED;
1637
1638     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1639
1640     memcpy(&wwo->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
1641     memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1642
1643     if (wwo->format.wBitsPerSample == 0) {
1644         WARN("Resetting zeroed wBitsPerSample\n");
1645         wwo->format.wBitsPerSample = 8 *
1646             (wwo->format.wf.nAvgBytesPerSec /
1647              wwo->format.wf.nSamplesPerSec) /
1648             wwo->format.wf.nChannels;
1649     }
1650     /* Read output space info for future reference */
1651     if (ioctl(wwo->ossdev->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
1652         ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n", wwo->ossdev->dev_name, strerror(errno));
1653         OSS_CloseDevice(wwo->ossdev);
1654         wwo->state = WINE_WS_CLOSED;
1655         return MMSYSERR_NOTENABLED;
1656     }
1657
1658     /* Check that fragsize is correct per our settings above */
1659     if ((info.fragsize > 1024) && (LOWORD(audio_fragment) <= 10)) {
1660         /* we've tried to set 1K fragments or less, but it didn't work */
1661         ERR("fragment size set failed, size is now %d\n", info.fragsize);
1662         MESSAGE("Your Open Sound System driver did not let us configure small enough sound fragments.\n");
1663         MESSAGE("This may cause delays and other problems in audio playback with certain applications.\n");
1664     }
1665
1666     /* Remember fragsize and total buffer size for future use */
1667     wwo->dwFragmentSize = info.fragsize;
1668     wwo->dwBufferSize = info.fragstotal * info.fragsize;
1669     wwo->dwPlayedTotal = 0;
1670     wwo->dwWrittenTotal = 0;
1671     wwo->bNeedPost = TRUE;
1672
1673     OSS_InitRingMessage(&wwo->msgRing);
1674
1675     wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1676     wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1677     WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1678     CloseHandle(wwo->hStartUpEvent);
1679     wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1680
1681     TRACE("fd=%d fragmentSize=%ld\n",
1682           wwo->ossdev->fd, wwo->dwFragmentSize);
1683     if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
1684         ERR("Fragment doesn't contain an integral number of data blocks\n");
1685
1686     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1687           wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
1688           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1689           wwo->format.wf.nBlockAlign);
1690
1691     return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1692 }
1693
1694 /**************************************************************************
1695  *                              wodClose                        [internal]
1696  */
1697 static DWORD wodClose(WORD wDevID)
1698 {
1699     DWORD               ret = MMSYSERR_NOERROR;
1700     WINE_WAVEOUT*       wwo;
1701
1702     TRACE("(%u);\n", wDevID);
1703
1704     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1705         WARN("bad device ID !\n");
1706         return MMSYSERR_BADDEVICEID;
1707     }
1708
1709     wwo = &WOutDev[wDevID];
1710     if (wwo->lpQueuePtr) {
1711         WARN("buffers still playing !\n");
1712         ret = WAVERR_STILLPLAYING;
1713     } else {
1714         if (wwo->hThread != INVALID_HANDLE_VALUE) {
1715             OSS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1716         }
1717
1718         OSS_DestroyRingMessage(&wwo->msgRing);
1719
1720         OSS_CloseDevice(wwo->ossdev);
1721         wwo->state = WINE_WS_CLOSED;
1722         wwo->dwFragmentSize = 0;
1723         ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1724     }
1725     return ret;
1726 }
1727
1728 /**************************************************************************
1729  *                              wodWrite                        [internal]
1730  *
1731  */
1732 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1733 {
1734     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1735
1736     /* first, do the sanity checks... */
1737     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1738         WARN("bad dev ID !\n");
1739         return MMSYSERR_BADDEVICEID;
1740     }
1741
1742     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1743         return WAVERR_UNPREPARED;
1744
1745     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1746         return WAVERR_STILLPLAYING;
1747
1748     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1749     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1750     lpWaveHdr->lpNext = 0;
1751
1752     if ((lpWaveHdr->dwBufferLength & (WOutDev[wDevID].format.wf.nBlockAlign - 1)) != 0)
1753     {
1754         WARN("WaveHdr length isn't a multiple of the PCM block size: %ld %% %d\n",lpWaveHdr->dwBufferLength,WOutDev[wDevID].format.wf.nBlockAlign);
1755         lpWaveHdr->dwBufferLength &= ~(WOutDev[wDevID].format.wf.nBlockAlign - 1);
1756     }
1757
1758     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1759
1760     return MMSYSERR_NOERROR;
1761 }
1762
1763 /**************************************************************************
1764  *                              wodPrepare                      [internal]
1765  */
1766 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1767 {
1768     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1769
1770     if (wDevID >= numOutDev) {
1771         WARN("bad device ID !\n");
1772         return MMSYSERR_BADDEVICEID;
1773     }
1774
1775     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1776         return WAVERR_STILLPLAYING;
1777
1778     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1779     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1780     return MMSYSERR_NOERROR;
1781 }
1782
1783 /**************************************************************************
1784  *                              wodUnprepare                    [internal]
1785  */
1786 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1787 {
1788     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1789
1790     if (wDevID >= numOutDev) {
1791         WARN("bad device ID !\n");
1792         return MMSYSERR_BADDEVICEID;
1793     }
1794
1795     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1796         return WAVERR_STILLPLAYING;
1797
1798     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1799     lpWaveHdr->dwFlags |= WHDR_DONE;
1800
1801     return MMSYSERR_NOERROR;
1802 }
1803
1804 /**************************************************************************
1805  *                      wodPause                                [internal]
1806  */
1807 static DWORD wodPause(WORD wDevID)
1808 {
1809     TRACE("(%u);!\n", wDevID);
1810
1811     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1812         WARN("bad device ID !\n");
1813         return MMSYSERR_BADDEVICEID;
1814     }
1815
1816     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1817
1818     return MMSYSERR_NOERROR;
1819 }
1820
1821 /**************************************************************************
1822  *                      wodRestart                              [internal]
1823  */
1824 static DWORD wodRestart(WORD wDevID)
1825 {
1826     TRACE("(%u);\n", wDevID);
1827
1828     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1829         WARN("bad device ID !\n");
1830         return MMSYSERR_BADDEVICEID;
1831     }
1832
1833     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1834
1835     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1836     /* FIXME: Myst crashes with this ... hmm -MM
1837        return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1838     */
1839
1840     return MMSYSERR_NOERROR;
1841 }
1842
1843 /**************************************************************************
1844  *                      wodReset                                [internal]
1845  */
1846 static DWORD wodReset(WORD wDevID)
1847 {
1848     TRACE("(%u);\n", wDevID);
1849
1850     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1851         WARN("bad device ID !\n");
1852         return MMSYSERR_BADDEVICEID;
1853     }
1854
1855     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1856
1857     return MMSYSERR_NOERROR;
1858 }
1859
1860 /**************************************************************************
1861  *                              wodGetPosition                  [internal]
1862  */
1863 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1864 {
1865     int                 time;
1866     DWORD               val;
1867     WINE_WAVEOUT*       wwo;
1868
1869     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1870
1871     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1872         WARN("bad device ID !\n");
1873         return MMSYSERR_BADDEVICEID;
1874     }
1875
1876     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1877
1878     wwo = &WOutDev[wDevID];
1879 #ifdef EXACT_WODPOSITION
1880     OSS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1881 #endif
1882     val = wwo->dwPlayedTotal;
1883
1884     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1885           lpTime->wType, wwo->format.wBitsPerSample,
1886           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1887           wwo->format.wf.nAvgBytesPerSec);
1888     TRACE("dwPlayedTotal=%lu\n", val);
1889
1890     switch (lpTime->wType) {
1891     case TIME_BYTES:
1892         lpTime->u.cb = val;
1893         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1894         break;
1895     case TIME_SAMPLES:
1896         lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels;
1897         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1898         break;
1899     case TIME_SMPTE:
1900         time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1901         lpTime->u.smpte.hour = time / (60 * 60 * 1000);
1902         time -= lpTime->u.smpte.hour * (60 * 60 * 1000);
1903         lpTime->u.smpte.min = time / (60 * 1000);
1904         time -= lpTime->u.smpte.min * (60 * 1000);
1905         lpTime->u.smpte.sec = time / 1000;
1906         time -= lpTime->u.smpte.sec * 1000;
1907         lpTime->u.smpte.frame = time * 30 / 1000;
1908         lpTime->u.smpte.fps = 30;
1909         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1910               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1911               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1912         break;
1913     default:
1914         FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1915         lpTime->wType = TIME_MS;
1916     case TIME_MS:
1917         lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1918         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1919         break;
1920     }
1921     return MMSYSERR_NOERROR;
1922 }
1923
1924 /**************************************************************************
1925  *                              wodBreakLoop                    [internal]
1926  */
1927 static DWORD wodBreakLoop(WORD wDevID)
1928 {
1929     TRACE("(%u);\n", wDevID);
1930
1931     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1932         WARN("bad device ID !\n");
1933         return MMSYSERR_BADDEVICEID;
1934     }
1935     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1936     return MMSYSERR_NOERROR;
1937 }
1938
1939 /**************************************************************************
1940  *                              wodGetVolume                    [internal]
1941  */
1942 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1943 {
1944     int         mixer;
1945     int         volume;
1946     DWORD       left, right;
1947     DWORD       last_left, last_right;
1948
1949     TRACE("(%u, %p);\n", wDevID, lpdwVol);
1950
1951     if (lpdwVol == NULL)
1952         return MMSYSERR_NOTENABLED;
1953     if (wDevID >= numOutDev) 
1954         return MMSYSERR_INVALPARAM;
1955
1956     if ((mixer = open(WOutDev[wDevID].ossdev->mixer_name, O_RDONLY|O_NDELAY)) < 0) {
1957         WARN("mixer device not available !\n");
1958         return MMSYSERR_NOTENABLED;
1959     }
1960     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
1961         WARN("ioctl(%s, SOUND_MIXER_READ_PCM) failed (%s)\n", WOutDev[wDevID].ossdev->mixer_name, strerror(errno));
1962         return MMSYSERR_NOTENABLED;
1963     }
1964     close(mixer);
1965     left = LOBYTE(volume);
1966     right = HIBYTE(volume);
1967     TRACE("left=%ld right=%ld !\n", left, right);
1968     last_left  = (LOWORD(WOutDev[wDevID].volume) * 100) / 0xFFFFl;
1969     last_right = (HIWORD(WOutDev[wDevID].volume) * 100) / 0xFFFFl;
1970     TRACE("last_left=%ld last_right=%ld !\n", last_left, last_right);
1971     if (last_left == left && last_right == right)
1972         *lpdwVol = WOutDev[wDevID].volume;
1973     else
1974         *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1975     return MMSYSERR_NOERROR;
1976 }
1977
1978 /**************************************************************************
1979  *                              wodSetVolume                    [internal]
1980  */
1981 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1982 {
1983     int         mixer;
1984     int         volume;
1985     DWORD       left, right;
1986
1987     TRACE("(%u, %08lX);\n", wDevID, dwParam);
1988
1989     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
1990     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1991     volume = left + (right << 8);
1992
1993     if (wDevID >= numOutDev) {
1994         WARN("invalid parameter: wDevID > %d\n", numOutDev);
1995         return MMSYSERR_INVALPARAM;
1996     }
1997
1998     if ((mixer = open(WOutDev[wDevID].ossdev->mixer_name, O_WRONLY|O_NDELAY)) < 0) {
1999         WARN("mixer device not available !\n");
2000         return MMSYSERR_NOTENABLED;
2001     }
2002     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
2003         WARN("ioctl(%s, SOUND_MIXER_WRITE_PCM) failed (%s)\n", WOutDev[wDevID].ossdev->mixer_name, strerror(errno));
2004         return MMSYSERR_NOTENABLED;
2005     } else {
2006         TRACE("volume=%04x\n", (unsigned)volume);
2007     }
2008     close(mixer);
2009
2010     /* save requested volume */
2011     WOutDev[wDevID].volume = dwParam;
2012
2013     return MMSYSERR_NOERROR;
2014 }
2015
2016 /**************************************************************************
2017  *                              wodMessage (WINEOSS.7)
2018  */
2019 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2020                             DWORD dwParam1, DWORD dwParam2)
2021 {
2022     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2023           wDevID, wMsg, dwUser, dwParam1, dwParam2);
2024
2025     switch (wMsg) {
2026     case DRVM_INIT:
2027     case DRVM_EXIT:
2028     case DRVM_ENABLE:
2029     case DRVM_DISABLE:
2030         /* FIXME: Pretend this is supported */
2031         return 0;
2032     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
2033     case WODM_CLOSE:            return wodClose         (wDevID);
2034     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2035     case WODM_PAUSE:            return wodPause         (wDevID);
2036     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
2037     case WODM_BREAKLOOP:        return wodBreakLoop     (wDevID);
2038     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2039     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2040     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSA)dwParam1,      dwParam2);
2041     case WODM_GETNUMDEVS:       return numOutDev;
2042     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
2043     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
2044     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
2045     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
2046     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
2047     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
2048     case WODM_RESTART:          return wodRestart       (wDevID);
2049     case WODM_RESET:            return wodReset         (wDevID);
2050
2051     case DRV_QUERYDEVICEINTERFACESIZE: return wdDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
2052     case DRV_QUERYDEVICEINTERFACE:     return wdDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
2053     case DRV_QUERYDSOUNDIFACE:  return wodDsCreate      (wDevID, (PIDSDRIVER*)dwParam1);
2054     case DRV_QUERYDSOUNDDESC:   return wodDsDesc        (wDevID, (PDSDRIVERDESC)dwParam1);
2055     case DRV_QUERYDSOUNDGUID:   return wodDsGuid        (wDevID, (LPGUID)dwParam1);
2056     default:
2057         FIXME("unknown message %d!\n", wMsg);
2058     }
2059     return MMSYSERR_NOTSUPPORTED;
2060 }
2061
2062 /*======================================================================*
2063  *                  Low level DSOUND definitions                        *
2064  *======================================================================*/
2065
2066 typedef struct IDsDriverPropertySetImpl IDsDriverPropertySetImpl;
2067 typedef struct IDsDriverNotifyImpl IDsDriverNotifyImpl;
2068 typedef struct IDsDriverImpl IDsDriverImpl;
2069 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
2070
2071 struct IDsDriverPropertySetImpl
2072 {
2073     /* IUnknown fields */
2074     ICOM_VFIELD(IDsDriverPropertySet);
2075     DWORD                       ref;
2076
2077     IDsDriverBufferImpl*        buffer;
2078 };
2079
2080 struct IDsDriverNotifyImpl
2081 {
2082     /* IUnknown fields */
2083     ICOM_VFIELD(IDsDriverNotify);
2084     DWORD                       ref;
2085
2086     /* IDsDriverNotifyImpl fields */
2087     LPDSBPOSITIONNOTIFY         notifies;
2088     int                         nrofnotifies;
2089
2090     IDsDriverBufferImpl*        buffer;
2091 };
2092
2093 struct IDsDriverImpl
2094 {
2095     /* IUnknown fields */
2096     ICOM_VFIELD(IDsDriver);
2097     DWORD                       ref;
2098
2099     /* IDsDriverImpl fields */
2100     UINT                        wDevID;
2101     IDsDriverBufferImpl*        primary;
2102 };
2103
2104 struct IDsDriverBufferImpl
2105 {
2106     /* IUnknown fields */
2107     ICOM_VFIELD(IDsDriverBuffer);
2108     DWORD                       ref;
2109
2110     /* IDsDriverBufferImpl fields */
2111     IDsDriverImpl*              drv;
2112     DWORD                       buflen;
2113     WAVEFORMATEX                wfx;
2114     LPBYTE                      mapping;
2115     DWORD                       maplen;
2116     int                         fd;
2117     DWORD                       dwFlags;
2118
2119     /* IDsDriverNotifyImpl fields */
2120     IDsDriverNotifyImpl*        notify;
2121     int                         notify_index;
2122
2123     /* IDsDriverPropertySetImpl fields */
2124     IDsDriverPropertySetImpl*   property_set;
2125 };
2126
2127 static HRESULT WINAPI IDsDriverPropertySetImpl_Create(
2128     IDsDriverBufferImpl * dsdb,
2129     IDsDriverPropertySetImpl **pdsdps);
2130
2131 static HRESULT WINAPI IDsDriverNotifyImpl_Create(
2132     IDsDriverBufferImpl * dsdb,
2133     IDsDriverNotifyImpl **pdsdn);
2134
2135 /*======================================================================*
2136  *                  Low level DSOUND property set implementation        *
2137  *======================================================================*/
2138
2139 static HRESULT WINAPI IDsDriverPropertySetImpl_QueryInterface(
2140     PIDSDRIVERPROPERTYSET iface,
2141     REFIID riid,
2142     LPVOID *ppobj) 
2143 {
2144     ICOM_THIS(IDsDriverPropertySetImpl,iface);
2145     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2146
2147     if ( IsEqualGUID(riid, &IID_IUnknown) ||
2148          IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
2149         IDsDriverPropertySet_AddRef(iface);
2150         *ppobj = (LPVOID)This;
2151         return DS_OK;
2152     }
2153
2154     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
2155
2156     *ppobj = 0;
2157     return E_NOINTERFACE;
2158 }
2159
2160 static ULONG WINAPI IDsDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface) 
2161 {
2162     ICOM_THIS(IDsDriverPropertySetImpl,iface);
2163     DWORD ref;
2164     TRACE("(%p) ref was %ld\n", This, This->ref);
2165
2166     ref = InterlockedIncrement(&(This->ref));
2167     return ref;
2168 }
2169
2170 static ULONG WINAPI IDsDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface) 
2171 {
2172     ICOM_THIS(IDsDriverPropertySetImpl,iface);
2173     DWORD ref;
2174     TRACE("(%p) ref was %ld\n", This, This->ref);
2175
2176     ref = InterlockedDecrement(&(This->ref));
2177     if (ref == 0) {
2178         IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer);
2179         HeapFree(GetProcessHeap(),0,This);
2180         TRACE("(%p) released\n",This);
2181     }
2182     return ref;
2183 }
2184
2185 static HRESULT WINAPI IDsDriverPropertySetImpl_Get(
2186     PIDSDRIVERPROPERTYSET iface,
2187     PDSPROPERTY pDsProperty,
2188     LPVOID pPropertyParams,
2189     ULONG cbPropertyParams,
2190     LPVOID pPropertyData,
2191     ULONG cbPropertyData,
2192     PULONG pcbReturnedData )
2193 {
2194     ICOM_THIS(IDsDriverPropertySetImpl,iface);
2195     FIXME("(%p,%p,%p,%lx,%p,%lx,%p)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
2196     return DSERR_UNSUPPORTED;
2197 }
2198
2199 static HRESULT WINAPI IDsDriverPropertySetImpl_Set(
2200     PIDSDRIVERPROPERTYSET iface,
2201     PDSPROPERTY pDsProperty,
2202     LPVOID pPropertyParams,
2203     ULONG cbPropertyParams,
2204     LPVOID pPropertyData,
2205     ULONG cbPropertyData )
2206 {
2207     ICOM_THIS(IDsDriverPropertySetImpl,iface);
2208     FIXME("(%p,%p,%p,%lx,%p,%lx)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData);
2209     return DSERR_UNSUPPORTED;
2210 }
2211
2212 static HRESULT WINAPI IDsDriverPropertySetImpl_QuerySupport(
2213     PIDSDRIVERPROPERTYSET iface,
2214     REFGUID PropertySetId,
2215     ULONG PropertyId,
2216     PULONG pSupport )
2217 {
2218     ICOM_THIS(IDsDriverPropertySetImpl,iface);
2219     FIXME("(%p,%s,%lx,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,pSupport);
2220     return DSERR_UNSUPPORTED;
2221 }
2222
2223 ICOM_VTABLE(IDsDriverPropertySet) dsdpsvt =
2224 {
2225     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2226     IDsDriverPropertySetImpl_QueryInterface,
2227     IDsDriverPropertySetImpl_AddRef,
2228     IDsDriverPropertySetImpl_Release,
2229     IDsDriverPropertySetImpl_Get,
2230     IDsDriverPropertySetImpl_Set,
2231     IDsDriverPropertySetImpl_QuerySupport,
2232 };
2233
2234 /*======================================================================*
2235  *                  Low level DSOUND notify implementation              *
2236  *======================================================================*/
2237
2238 static HRESULT WINAPI IDsDriverNotifyImpl_QueryInterface(
2239     PIDSDRIVERNOTIFY iface,
2240     REFIID riid,
2241     LPVOID *ppobj) 
2242 {
2243     ICOM_THIS(IDsDriverNotifyImpl,iface);
2244     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2245
2246     if ( IsEqualGUID(riid, &IID_IUnknown) ||
2247          IsEqualGUID(riid, &IID_IDsDriverNotify) ) {
2248         IDsDriverNotify_AddRef(iface);
2249         *ppobj = This;
2250         return DS_OK;
2251     }
2252
2253     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
2254
2255     *ppobj = 0;
2256     return E_NOINTERFACE;
2257 }
2258
2259 static ULONG WINAPI IDsDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface) 
2260 {
2261     ICOM_THIS(IDsDriverNotifyImpl,iface);
2262     DWORD ref;
2263     TRACE("(%p) ref was %ld\n", This, This->ref);
2264
2265     ref = InterlockedIncrement(&(This->ref));
2266     return ref;
2267 }
2268
2269 static ULONG WINAPI IDsDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface) 
2270 {
2271     ICOM_THIS(IDsDriverNotifyImpl,iface);
2272     DWORD ref;
2273     TRACE("(%p) ref was %ld\n", This, This->ref);
2274
2275     ref = InterlockedDecrement(&(This->ref));
2276     if (ref == 0) {
2277         IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer);
2278         if (This->notifies != NULL)
2279             HeapFree(GetProcessHeap(), 0, This->notifies);
2280
2281         HeapFree(GetProcessHeap(),0,This);
2282         TRACE("(%p) released\n",This);
2283     }
2284
2285     return ref;
2286 }
2287
2288 static HRESULT WINAPI IDsDriverNotifyImpl_SetNotificationPositions(
2289     PIDSDRIVERNOTIFY iface,
2290     DWORD howmuch,
2291     LPCDSBPOSITIONNOTIFY notify) 
2292 {
2293     ICOM_THIS(IDsDriverNotifyImpl,iface);
2294     TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
2295
2296     if (!notify) {
2297         WARN("invalid parameter\n");
2298         return DSERR_INVALIDPARAM;
2299     }
2300
2301     if (TRACE_ON(wave)) {
2302         int i;
2303         for (i=0;i<howmuch;i++)
2304             TRACE("notify at %ld to 0x%08lx\n",
2305                 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
2306     }
2307
2308     /* Make an internal copy of the caller-supplied array.
2309      * Replace the existing copy if one is already present. */
2310     if (This->notifies) 
2311         This->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2312         This->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));
2313     else 
2314         This->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2315         howmuch * sizeof(DSBPOSITIONNOTIFY));
2316
2317     memcpy(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
2318     This->nrofnotifies = howmuch;
2319
2320     return S_OK;
2321 }
2322
2323 ICOM_VTABLE(IDsDriverNotify) dsdnvt =
2324 {
2325     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2326     IDsDriverNotifyImpl_QueryInterface,
2327     IDsDriverNotifyImpl_AddRef,
2328     IDsDriverNotifyImpl_Release,
2329     IDsDriverNotifyImpl_SetNotificationPositions,
2330 };
2331
2332 /*======================================================================*
2333  *                  Low level DSOUND implementation                     *
2334  *======================================================================*/
2335
2336 static HRESULT DSDB_MapBuffer(IDsDriverBufferImpl *dsdb)
2337 {
2338     TRACE("(%p)\n",dsdb);
2339     if (!dsdb->mapping) {
2340         dsdb->mapping = mmap(NULL, dsdb->maplen, PROT_WRITE, MAP_SHARED,
2341                              dsdb->fd, 0);
2342         if (dsdb->mapping == (LPBYTE)-1) {
2343             TRACE("(%p): Could not map sound device for direct access (%s)\n", dsdb, strerror(errno));
2344             return DSERR_GENERIC;
2345         }
2346         TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dsdb, dsdb->mapping, dsdb->maplen);
2347
2348         /* for some reason, es1371 and sblive! sometimes have junk in here.
2349          * clear it, or we get junk noise */
2350         /* some libc implementations are buggy: their memset reads from the buffer...
2351          * to work around it, we have to zero the block by hand. We don't do the expected:
2352          * memset(dsdb->mapping,0, dsdb->maplen);
2353          */
2354         {
2355             unsigned char*      p1 = dsdb->mapping;
2356             unsigned            len = dsdb->maplen;
2357             unsigned char       silence = (dsdb->wfx.wBitsPerSample == 8) ? 128 : 0;
2358             unsigned long       ulsilence = (dsdb->wfx.wBitsPerSample == 8) ? 0x80808080 : 0;
2359
2360             if (len >= 16) /* so we can have at least a 4 long area to store... */
2361             {
2362                 /* the mmap:ed value is (at least) dword aligned
2363                  * so, start filling the complete unsigned long:s
2364                  */
2365                 int             b = len >> 2;
2366                 unsigned long*  p4 = (unsigned long*)p1;
2367
2368                 while (b--) *p4++ = ulsilence;
2369                 /* prepare for filling the rest */
2370                 len &= 3;
2371                 p1 = (unsigned char*)p4;
2372             }
2373             /* in all cases, fill the remaining bytes */
2374             while (len-- != 0) *p1++ = silence;
2375         }
2376     }
2377     return DS_OK;
2378 }
2379
2380 static HRESULT DSDB_UnmapBuffer(IDsDriverBufferImpl *dsdb)
2381 {
2382     TRACE("(%p)\n",dsdb);
2383     if (dsdb->mapping) {
2384         if (munmap(dsdb->mapping, dsdb->maplen) < 0) {
2385             ERR("(%p): Could not unmap sound device (%s)\n", dsdb, strerror(errno));
2386             return DSERR_GENERIC;
2387         }
2388         dsdb->mapping = NULL;
2389         TRACE("(%p): sound device unmapped\n", dsdb);
2390     }
2391     return DS_OK;
2392 }
2393
2394 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
2395 {
2396     ICOM_THIS(IDsDriverBufferImpl,iface);
2397     TRACE("(%p,%s,%p)\n",iface,debugstr_guid(riid),*ppobj);
2398
2399     if ( IsEqualGUID(riid, &IID_IUnknown) ||
2400          IsEqualGUID(riid, &IID_IDsDriverBuffer) ) {
2401         IDsDriverBuffer_AddRef(iface);
2402         *ppobj = (LPVOID)This;
2403         return DS_OK;
2404     }
2405
2406     if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
2407         if (!This->notify)
2408             IDsDriverNotifyImpl_Create(This, &(This->notify));
2409         if (This->notify) {
2410             IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
2411             *ppobj = (LPVOID)This->notify;
2412             return DS_OK;
2413         }
2414         *ppobj = 0;
2415         return E_FAIL;
2416     }
2417
2418     if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
2419         if (!This->property_set)
2420             IDsDriverPropertySetImpl_Create(This, &(This->property_set));
2421         if (This->property_set) {
2422             IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
2423             *ppobj = (LPVOID)This->property_set;
2424             return DS_OK;
2425         }
2426         *ppobj = 0;
2427         return E_FAIL;
2428     }
2429
2430     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
2431
2432     *ppobj = 0;
2433
2434     return E_NOINTERFACE;
2435 }
2436
2437 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
2438 {
2439     ICOM_THIS(IDsDriverBufferImpl,iface);
2440     TRACE("(%p)\n",This);
2441     This->ref++;
2442     TRACE("ref=%ld\n",This->ref);
2443     return This->ref;
2444 }
2445
2446 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
2447 {
2448     ICOM_THIS(IDsDriverBufferImpl,iface);
2449     TRACE("(%p)\n",This);
2450     if (--This->ref) {
2451         TRACE("ref=%ld\n",This->ref);
2452         return This->ref;
2453     }
2454     if (This == This->drv->primary)
2455         This->drv->primary = NULL;
2456     DSDB_UnmapBuffer(This);
2457     HeapFree(GetProcessHeap(),0,This);
2458     TRACE("ref=0\n");
2459     return 0;
2460 }
2461
2462 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
2463                                                LPVOID*ppvAudio1,LPDWORD pdwLen1,
2464                                                LPVOID*ppvAudio2,LPDWORD pdwLen2,
2465                                                DWORD dwWritePosition,DWORD dwWriteLen,
2466                                                DWORD dwFlags)
2467 {
2468     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2469     /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK,
2470      * and that we don't support secondary buffers, this method will never be called */
2471     TRACE("(%p): stub\n",iface);
2472     return DSERR_UNSUPPORTED;
2473 }
2474
2475 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
2476                                                  LPVOID pvAudio1,DWORD dwLen1,
2477                                                  LPVOID pvAudio2,DWORD dwLen2)
2478 {
2479     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2480     TRACE("(%p): stub\n",iface);
2481     return DSERR_UNSUPPORTED;
2482 }
2483
2484 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
2485                                                     LPWAVEFORMATEX pwfx)
2486 {
2487     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2488
2489     TRACE("(%p,%p)\n",iface,pwfx);
2490     /* On our request (GetDriverDesc flags), DirectSound has by now used
2491      * waveOutClose/waveOutOpen to set the format...
2492      * unfortunately, this means our mmap() is now gone...
2493      * so we need to somehow signal to our DirectSound implementation
2494      * that it should completely recreate this HW buffer...
2495      * this unexpected error code should do the trick... */
2496     return DSERR_BUFFERLOST;
2497 }
2498
2499 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
2500 {
2501     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2502     TRACE("(%p,%ld): stub\n",iface,dwFreq);
2503     return DSERR_UNSUPPORTED;
2504 }
2505
2506 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
2507 {
2508     DWORD vol;
2509     ICOM_THIS(IDsDriverBufferImpl,iface);
2510     TRACE("(%p,%p)\n",This,pVolPan);
2511
2512     vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16);
2513
2514     if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) {
2515         WARN("wodSetVolume failed\n");
2516         return DSERR_INVALIDPARAM;
2517     }
2518
2519     return DS_OK;
2520 }
2521
2522 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
2523 {
2524     /* ICOM_THIS(IDsDriverImpl,iface); */
2525     TRACE("(%p,%ld): stub\n",iface,dwNewPos);
2526     return DSERR_UNSUPPORTED;
2527 }
2528
2529 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
2530                                                       LPDWORD lpdwPlay, LPDWORD lpdwWrite)
2531 {
2532     ICOM_THIS(IDsDriverBufferImpl,iface);
2533     count_info info;
2534     DWORD ptr;
2535
2536     TRACE("(%p)\n",iface);
2537     if (WOutDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
2538         ERR("device not open, but accessing?\n");
2539         return DSERR_UNINITIALIZED;
2540     }
2541     if (ioctl(This->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
2542         ERR("ioctl(%s, SNDCTL_DSP_GETOPTR) failed (%s)\n",
2543             WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
2544         return DSERR_GENERIC;
2545     }
2546     ptr = info.ptr & ~3; /* align the pointer, just in case */
2547     if (lpdwPlay) *lpdwPlay = ptr;
2548     if (lpdwWrite) {
2549         /* add some safety margin (not strictly necessary, but...) */
2550         if (WOutDev[This->drv->wDevID].ossdev->out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
2551             *lpdwWrite = ptr + 32;
2552         else
2553             *lpdwWrite = ptr + WOutDev[This->drv->wDevID].dwFragmentSize;
2554         while (*lpdwWrite > This->buflen)
2555             *lpdwWrite -= This->buflen;
2556     }
2557     TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
2558     return DS_OK;
2559 }
2560
2561 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
2562 {
2563     ICOM_THIS(IDsDriverBufferImpl,iface);
2564     int enable;
2565     TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
2566     WOutDev[This->drv->wDevID].ossdev->bOutputEnabled = TRUE;
2567     enable = getEnables(WOutDev[This->drv->wDevID].ossdev);
2568     if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
2569         if (errno == EINVAL) {
2570             /* Don't give up yet. OSS trigger support is inconsistent. */
2571             if (WOutDev[This->drv->wDevID].ossdev->open_count == 1) {
2572                 /* try the opposite input enable */
2573                 if (WOutDev[This->drv->wDevID].ossdev->bInputEnabled == FALSE)
2574                     WOutDev[This->drv->wDevID].ossdev->bInputEnabled = TRUE;
2575                 else
2576                     WOutDev[This->drv->wDevID].ossdev->bInputEnabled = FALSE;
2577                 /* try it again */
2578                 enable = getEnables(WOutDev[This->drv->wDevID].ossdev);
2579                 if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) >= 0)
2580                     return DS_OK;
2581             }    
2582         }
2583         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
2584         WOutDev[This->drv->wDevID].ossdev->bOutputEnabled = FALSE;
2585         return DSERR_GENERIC;
2586     }
2587     return DS_OK;
2588 }
2589
2590 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
2591 {
2592     ICOM_THIS(IDsDriverBufferImpl,iface);
2593     int enable;
2594     TRACE("(%p)\n",iface);
2595     /* no more playing */
2596     WOutDev[This->drv->wDevID].ossdev->bOutputEnabled = FALSE;
2597     enable = getEnables(WOutDev[This->drv->wDevID].ossdev);
2598     if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
2599         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
2600         return DSERR_GENERIC;
2601     }
2602 #if 0
2603     /* the play position must be reset to the beginning of the buffer */
2604     if (ioctl(This->fd, SNDCTL_DSP_RESET, 0) < 0) {
2605         ERR("ioctl(%s, SNDCTL_DSP_RESET) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
2606         return DSERR_GENERIC;
2607     }
2608 #endif
2609     /* Most OSS drivers just can't stop the playback without closing the device...
2610      * so we need to somehow signal to our DirectSound implementation
2611      * that it should completely recreate this HW buffer...
2612      * this unexpected error code should do the trick... */
2613     /* FIXME: ...unless we are doing full duplex, then its not nice to close the device */
2614     if (WOutDev[This->drv->wDevID].ossdev->open_count == 1)
2615         return DSERR_BUFFERLOST;
2616
2617     return DS_OK;
2618 }
2619
2620 static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
2621 {
2622     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2623     IDsDriverBufferImpl_QueryInterface,
2624     IDsDriverBufferImpl_AddRef,
2625     IDsDriverBufferImpl_Release,
2626     IDsDriverBufferImpl_Lock,
2627     IDsDriverBufferImpl_Unlock,
2628     IDsDriverBufferImpl_SetFormat,
2629     IDsDriverBufferImpl_SetFrequency,
2630     IDsDriverBufferImpl_SetVolumePan,
2631     IDsDriverBufferImpl_SetPosition,
2632     IDsDriverBufferImpl_GetPosition,
2633     IDsDriverBufferImpl_Play,
2634     IDsDriverBufferImpl_Stop
2635 };
2636
2637 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
2638 {
2639     ICOM_THIS(IDsDriverImpl,iface);
2640     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2641
2642     if ( IsEqualGUID(riid, &IID_IUnknown) ||
2643          IsEqualGUID(riid, &IID_IDsDriver) ) {
2644         IDsDriver_AddRef(iface);
2645         *ppobj = (LPVOID)This;
2646         return DS_OK;
2647     }
2648
2649     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
2650
2651     *ppobj = 0;
2652
2653     return E_NOINTERFACE;
2654 }
2655
2656 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
2657 {
2658     ICOM_THIS(IDsDriverImpl,iface);
2659     TRACE("(%p)\n",This);
2660     This->ref++;
2661     TRACE("ref=%ld\n",This->ref);
2662     return This->ref;
2663 }
2664
2665 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
2666 {
2667     ICOM_THIS(IDsDriverImpl,iface);
2668     TRACE("(%p)\n",This);
2669     if (--This->ref) {
2670         TRACE("ref=%ld\n",This->ref);
2671         return This->ref;
2672     }
2673     HeapFree(GetProcessHeap(),0,This);
2674     TRACE("ref=0\n");
2675     return 0;
2676 }
2677
2678 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
2679 {
2680     ICOM_THIS(IDsDriverImpl,iface);
2681     TRACE("(%p,%p)\n",iface,pDesc);
2682
2683     /* copy version from driver */
2684     memcpy(pDesc, &(WOutDev[This->wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC));
2685
2686     pDesc->dwFlags |= DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
2687         DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
2688     pDesc->dnDevNode            = WOutDev[This->wDevID].waveDesc.dnDevNode;
2689     pDesc->wVxdId               = 0;
2690     pDesc->wReserved            = 0;
2691     pDesc->ulDeviceNum          = This->wDevID;
2692     pDesc->dwHeapType           = DSDHEAP_NOHEAP;
2693     pDesc->pvDirectDrawHeap     = NULL;
2694     pDesc->dwMemStartAddress    = 0;
2695     pDesc->dwMemEndAddress      = 0;
2696     pDesc->dwMemAllocExtra      = 0;
2697     pDesc->pvReserved1          = NULL;
2698     pDesc->pvReserved2          = NULL;
2699     return DS_OK;
2700 }
2701
2702 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
2703 {
2704     ICOM_THIS(IDsDriverImpl,iface);
2705     int enable;
2706     TRACE("(%p)\n",iface);
2707
2708     /* make sure the card doesn't start playing before we want it to */
2709     WOutDev[This->wDevID].ossdev->bOutputEnabled = FALSE;
2710     enable = getEnables(WOutDev[This->wDevID].ossdev);
2711     if (ioctl(WOutDev[This->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
2712         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",WOutDev[This->wDevID].ossdev->dev_name, strerror(errno));
2713         return DSERR_GENERIC;
2714     }
2715     return DS_OK;
2716 }
2717
2718 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
2719 {
2720     ICOM_THIS(IDsDriverImpl,iface);
2721     TRACE("(%p)\n",iface);
2722     if (This->primary) {
2723         ERR("problem with DirectSound: primary not released\n");
2724         return DSERR_GENERIC;
2725     }
2726     return DS_OK;
2727 }
2728
2729 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
2730 {
2731     ICOM_THIS(IDsDriverImpl,iface);
2732     TRACE("(%p,%p)\n",iface,pCaps);
2733     memcpy(pCaps, &(WOutDev[This->wDevID].ossdev->ds_caps), sizeof(DSDRIVERCAPS));
2734     return DS_OK;
2735 }
2736
2737 static HRESULT WINAPI DSD_CreatePrimaryBuffer(PIDSDRIVER iface,
2738                                               LPWAVEFORMATEX pwfx,
2739                                               DWORD dwFlags, 
2740                                               DWORD dwCardAddress,
2741                                               LPDWORD pdwcbBufferSize,
2742                                               LPBYTE *ppbBuffer,
2743                                               LPVOID *ppvObj)
2744 {
2745     ICOM_THIS(IDsDriverImpl,iface);
2746     IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
2747     HRESULT err;
2748     audio_buf_info info;
2749     int enable = 0;
2750     TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
2751
2752     if (This->primary)
2753         return DSERR_ALLOCATED;
2754     if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
2755         return DSERR_CONTROLUNAVAIL;
2756
2757     *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverBufferImpl));
2758     if (*ippdsdb == NULL)
2759         return DSERR_OUTOFMEMORY;
2760     (*ippdsdb)->lpVtbl  = &dsdbvt;
2761     (*ippdsdb)->ref     = 1;
2762     (*ippdsdb)->drv     = This;
2763     (*ippdsdb)->wfx     = *pwfx;
2764     (*ippdsdb)->fd      = WOutDev[This->wDevID].ossdev->fd;
2765     (*ippdsdb)->dwFlags = dwFlags;
2766
2767     /* check how big the DMA buffer is now */
2768     if (ioctl((*ippdsdb)->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
2769         ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n",
2770             WOutDev[This->wDevID].ossdev->dev_name, strerror(errno));
2771         HeapFree(GetProcessHeap(),0,*ippdsdb);
2772         *ippdsdb = NULL;
2773         return DSERR_GENERIC;
2774     }
2775     (*ippdsdb)->maplen = (*ippdsdb)->buflen = info.fragstotal * info.fragsize;
2776
2777     /* map the DMA buffer */
2778     err = DSDB_MapBuffer(*ippdsdb);
2779     if (err != DS_OK) {
2780         HeapFree(GetProcessHeap(),0,*ippdsdb);
2781         *ippdsdb = NULL;
2782         return err;
2783     }
2784
2785     /* primary buffer is ready to go */
2786     *pdwcbBufferSize    = (*ippdsdb)->maplen;
2787     *ppbBuffer          = (*ippdsdb)->mapping;
2788
2789     /* some drivers need some extra nudging after mapping */
2790     WOutDev[This->wDevID].ossdev->bOutputEnabled = FALSE;
2791     enable = getEnables(WOutDev[This->wDevID].ossdev);
2792     if (ioctl((*ippdsdb)->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
2793         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
2794             WOutDev[This->wDevID].ossdev->dev_name, strerror(errno));
2795         return DSERR_GENERIC;
2796     }
2797
2798     This->primary = *ippdsdb;
2799
2800     return DS_OK;
2801 }
2802
2803 static HRESULT WINAPI DSD_CreateSecondaryBuffer(PIDSDRIVER iface,
2804                                                 LPWAVEFORMATEX pwfx,
2805                                                 DWORD dwFlags, 
2806                                                 DWORD dwCardAddress,
2807                                                 LPDWORD pdwcbBufferSize,
2808                                                 LPBYTE *ppbBuffer,
2809                                                 LPVOID *ppvObj)
2810 {
2811     ICOM_THIS(IDsDriverImpl,iface);
2812     IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
2813     FIXME("(%p,%p,%lx,%lx,%p,%p,%p): stub\n",This,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
2814
2815     *ippdsdb = 0;
2816     return DSERR_UNSUPPORTED;
2817 }
2818
2819 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
2820                                                       LPWAVEFORMATEX pwfx,
2821                                                       DWORD dwFlags, 
2822                                                       DWORD dwCardAddress,
2823                                                       LPDWORD pdwcbBufferSize,
2824                                                       LPBYTE *ppbBuffer,
2825                                                       LPVOID *ppvObj)
2826 {
2827     TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
2828
2829     if (dwFlags & DSBCAPS_PRIMARYBUFFER)
2830         return DSD_CreatePrimaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
2831
2832     return DSD_CreateSecondaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
2833 }
2834
2835 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
2836                                                          PIDSDRIVERBUFFER pBuffer,
2837                                                          LPVOID *ppvObj)
2838 {
2839     /* ICOM_THIS(IDsDriverImpl,iface); */
2840     TRACE("(%p,%p): stub\n",iface,pBuffer);
2841     return DSERR_INVALIDCALL;
2842 }
2843
2844 static ICOM_VTABLE(IDsDriver) dsdvt =
2845 {
2846     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2847     IDsDriverImpl_QueryInterface,
2848     IDsDriverImpl_AddRef,
2849     IDsDriverImpl_Release,
2850     IDsDriverImpl_GetDriverDesc,
2851     IDsDriverImpl_Open,
2852     IDsDriverImpl_Close,
2853     IDsDriverImpl_GetCaps,
2854     IDsDriverImpl_CreateSoundBuffer,
2855     IDsDriverImpl_DuplicateSoundBuffer
2856 };
2857
2858 static HRESULT WINAPI IDsDriverPropertySetImpl_Create(
2859     IDsDriverBufferImpl * dsdb,
2860     IDsDriverPropertySetImpl **pdsdps)
2861 {
2862     IDsDriverPropertySetImpl * dsdps;
2863     TRACE("(%p,%p)\n",dsdb,pdsdps);
2864
2865     dsdps = (IDsDriverPropertySetImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsdps));
2866     if (dsdps == NULL) {
2867         WARN("out of memory\n");
2868         return DSERR_OUTOFMEMORY;
2869     }
2870                                                                                 
2871     dsdps->ref = 0;
2872     dsdps->lpVtbl = &dsdpsvt;
2873     dsdps->buffer = dsdb;
2874     dsdb->property_set = dsdps;
2875     IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb);
2876                                                                                 
2877     *pdsdps = dsdps;
2878     return DS_OK;
2879 }
2880
2881 static HRESULT WINAPI IDsDriverNotifyImpl_Create(
2882     IDsDriverBufferImpl * dsdb,
2883     IDsDriverNotifyImpl **pdsdn)
2884 {
2885     IDsDriverNotifyImpl * dsdn;
2886     TRACE("(%p,%p)\n",dsdb,pdsdn);
2887
2888     dsdn = (IDsDriverNotifyImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsdn));
2889                                                                                 
2890     if (dsdn == NULL) {
2891         WARN("out of memory\n");
2892         return DSERR_OUTOFMEMORY;
2893     }
2894                                                                                 
2895     dsdn->ref = 0;
2896     dsdn->lpVtbl = &dsdnvt;
2897     dsdn->buffer = dsdb;
2898     dsdb->notify = dsdn;
2899     IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb);
2900                                                                                 
2901     *pdsdn = dsdn;
2902     return DS_OK;
2903 };
2904
2905 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2906 {
2907     IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
2908     TRACE("(%d,%p)\n",wDevID,drv);
2909
2910     /* the HAL isn't much better than the HEL if we can't do mmap() */
2911     if (!(WOutDev[wDevID].ossdev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
2912         ERR("DirectSound flag not set\n");
2913         MESSAGE("This sound card's driver does not support direct access\n");
2914         MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2915         return MMSYSERR_NOTSUPPORTED;
2916     }
2917
2918     *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverImpl));
2919     if (!*idrv)
2920         return MMSYSERR_NOMEM;
2921     (*idrv)->lpVtbl     = &dsdvt;
2922     (*idrv)->ref        = 1;
2923
2924     (*idrv)->wDevID     = wDevID;
2925     (*idrv)->primary    = NULL;
2926     return MMSYSERR_NOERROR;
2927 }
2928
2929 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2930 {
2931     TRACE("(%d,%p)\n",wDevID,desc);
2932     memcpy(desc, &(WOutDev[wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC));
2933     return MMSYSERR_NOERROR;
2934 }
2935
2936 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid)
2937 {
2938     TRACE("(%d,%p)\n",wDevID,pGuid);
2939     memcpy(pGuid, &(WOutDev[wDevID].ossdev->ds_guid), sizeof(GUID));
2940     return MMSYSERR_NOERROR;
2941 }
2942
2943 /*======================================================================*
2944  *                  Low level WAVE IN implementation                    *
2945  *======================================================================*/
2946
2947 /**************************************************************************
2948  *                      widNotifyClient                 [internal]
2949  */
2950 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
2951 {
2952     TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg,
2953         wMsg == WIM_OPEN ? "WIM_OPEN" : wMsg == WIM_CLOSE ? "WIM_CLOSE" :
2954         wMsg == WIM_DATA ? "WIM_DATA" : "Unknown", dwParam1, dwParam2);
2955
2956     switch (wMsg) {
2957     case WIM_OPEN:
2958     case WIM_CLOSE:
2959     case WIM_DATA:
2960         if (wwi->wFlags != DCB_NULL &&
2961             !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
2962                             (HDRVR)wwi->waveDesc.hWave, wMsg,
2963                             wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
2964             WARN("can't notify client !\n");
2965             return MMSYSERR_ERROR;
2966         }
2967         break;
2968     default:
2969         FIXME("Unknown callback message %u\n", wMsg);
2970         return MMSYSERR_INVALPARAM;
2971     }
2972     return MMSYSERR_NOERROR;
2973 }
2974
2975 /**************************************************************************
2976  *                      widGetDevCaps                           [internal]
2977  */
2978 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
2979 {
2980     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
2981
2982     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
2983
2984     if (wDevID >= numInDev) {
2985         TRACE("numOutDev reached !\n");
2986         return MMSYSERR_BADDEVICEID;
2987     }
2988
2989     memcpy(lpCaps, &WInDev[wDevID].ossdev->in_caps, min(dwSize, sizeof(*lpCaps)));
2990     return MMSYSERR_NOERROR;
2991 }
2992
2993 /**************************************************************************
2994  *                              widRecorder_ReadHeaders         [internal]
2995  */
2996 static void widRecorder_ReadHeaders(WINE_WAVEIN * wwi)
2997 {
2998     enum win_wm_message tmp_msg;
2999     DWORD               tmp_param;
3000     HANDLE              tmp_ev;
3001     WAVEHDR*            lpWaveHdr;
3002
3003     while (OSS_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
3004         if (tmp_msg == WINE_WM_HEADER) {
3005             LPWAVEHDR*  wh;
3006             lpWaveHdr = (LPWAVEHDR)tmp_param;
3007             lpWaveHdr->lpNext = 0;
3008
3009             if (wwi->lpQueuePtr == 0)
3010                 wwi->lpQueuePtr = lpWaveHdr;
3011             else {
3012                 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3013                 *wh = lpWaveHdr;
3014             }
3015         } else {
3016             ERR("should only have headers left\n");
3017         }
3018     }
3019 }
3020
3021 /**************************************************************************
3022  *                              widRecorder                     [internal]
3023  */
3024 static  DWORD   CALLBACK        widRecorder(LPVOID pmt)
3025 {
3026     WORD                uDevID = (DWORD)pmt;
3027     WINE_WAVEIN*        wwi = (WINE_WAVEIN*)&WInDev[uDevID];
3028     WAVEHDR*            lpWaveHdr;
3029     DWORD               dwSleepTime;
3030     DWORD               bytesRead;
3031     LPVOID              buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwFragmentSize);
3032     char               *pOffset = buffer;
3033     audio_buf_info      info;
3034     int                 xs;
3035     enum win_wm_message msg;
3036     DWORD               param;
3037     HANDLE              ev;
3038     int                 enable;
3039
3040     wwi->state = WINE_WS_STOPPED;
3041     wwi->dwTotalRecorded = 0;
3042     wwi->lpQueuePtr = NULL;
3043
3044     SetEvent(wwi->hStartUpEvent);
3045
3046     /* disable input so capture will begin when triggered */
3047     wwi->ossdev->bInputEnabled = FALSE;
3048     enable = getEnables(wwi->ossdev);
3049     if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0)
3050         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
3051
3052     /* the soundblaster live needs a micro wake to get its recording started
3053      * (or GETISPACE will have 0 frags all the time)
3054      */
3055     read(wwi->ossdev->fd, &xs, 4);
3056
3057     /* make sleep time to be # of ms to output a fragment */
3058     dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec;
3059     TRACE("sleeptime=%ld ms\n", dwSleepTime);
3060
3061     for (;;) {
3062         /* wait for dwSleepTime or an event in thread's queue */
3063         /* FIXME: could improve wait time depending on queue state,
3064          * ie, number of queued fragments
3065          */
3066
3067         if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
3068         {
3069             lpWaveHdr = wwi->lpQueuePtr;
3070
3071             ioctl(wwi->ossdev->fd, SNDCTL_DSP_GETISPACE, &info);
3072             TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", info.fragments, info.fragsize, info.fragstotal, info.bytes);
3073
3074             /* read all the fragments accumulated so far */
3075             while ((info.fragments > 0) && (wwi->lpQueuePtr))
3076             {
3077                 info.fragments --;
3078
3079                 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwFragmentSize)
3080                 {
3081                     /* directly read fragment in wavehdr */
3082                     bytesRead = read(wwi->ossdev->fd,
3083                                      lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
3084                                      wwi->dwFragmentSize);
3085
3086                     TRACE("bytesRead=%ld (direct)\n", bytesRead);
3087                     if (bytesRead != (DWORD) -1)
3088                     {
3089                         /* update number of bytes recorded in current buffer and by this device */
3090                         lpWaveHdr->dwBytesRecorded += bytesRead;
3091                         wwi->dwTotalRecorded       += bytesRead;
3092
3093                         /* buffer is full. notify client */
3094                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
3095                         {
3096                             /* must copy the value of next waveHdr, because we have no idea of what
3097                              * will be done with the content of lpWaveHdr in callback
3098                              */
3099                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
3100
3101                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3102                             lpWaveHdr->dwFlags |=  WHDR_DONE;
3103
3104                             wwi->lpQueuePtr = lpNext;
3105                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3106                             lpWaveHdr = lpNext;
3107                         }
3108                     }
3109                 }
3110                 else
3111                 {
3112                     /* read the fragment in a local buffer */
3113                     bytesRead = read(wwi->ossdev->fd, buffer, wwi->dwFragmentSize);
3114                     pOffset = buffer;
3115
3116                     TRACE("bytesRead=%ld (local)\n", bytesRead);
3117
3118                     /* copy data in client buffers */
3119                     while (bytesRead != (DWORD) -1 && bytesRead > 0)
3120                     {
3121                         DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
3122
3123                         memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
3124                                pOffset,
3125                                dwToCopy);
3126
3127                         /* update number of bytes recorded in current buffer and by this device */
3128                         lpWaveHdr->dwBytesRecorded += dwToCopy;
3129                         wwi->dwTotalRecorded += dwToCopy;
3130                         bytesRead -= dwToCopy;
3131                         pOffset   += dwToCopy;
3132
3133                         /* client buffer is full. notify client */
3134                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
3135                         {
3136                             /* must copy the value of next waveHdr, because we have no idea of what
3137                              * will be done with the content of lpWaveHdr in callback
3138                              */
3139                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
3140                             TRACE("lpNext=%p\n", lpNext);
3141
3142                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3143                             lpWaveHdr->dwFlags |=  WHDR_DONE;
3144
3145                             wwi->lpQueuePtr = lpNext;
3146                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3147
3148                             lpWaveHdr = lpNext;
3149                             if (!lpNext && bytesRead) {
3150                                 /* before we give up, check for more header messages */
3151                                 while (OSS_PeekRingMessage(&wwi->msgRing, &msg, &param, &ev))
3152                                 {
3153                                     if (msg == WINE_WM_HEADER) {
3154                                         LPWAVEHDR hdr;
3155                                         OSS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev);
3156                                         hdr = ((LPWAVEHDR)param);
3157                                         TRACE("msg = %s, hdr = %p, ev = %p\n", wodPlayerCmdString[msg - WM_USER - 1], hdr, ev);
3158                                         hdr->lpNext = 0;
3159                                         if (lpWaveHdr == 0) {
3160                                             /* new head of queue */
3161                                             wwi->lpQueuePtr = lpWaveHdr = hdr;
3162                                         } else {
3163                                             /* insert buffer at the end of queue */
3164                                             LPWAVEHDR*  wh;
3165                                             for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3166                                             *wh = hdr;
3167                                         }
3168                                     } else
3169                                         break;
3170                                 }
3171
3172                                 if (lpWaveHdr == 0) {
3173                                     /* no more buffer to copy data to, but we did read more.
3174                                      * what hasn't been copied will be dropped
3175                                      */
3176                                     WARN("buffer under run! %lu bytes dropped.\n", bytesRead);
3177                                     wwi->lpQueuePtr = NULL;
3178                                     break;
3179                                 }
3180                             }
3181                         }
3182                     }
3183                 }
3184             }
3185         }
3186
3187         WAIT_OMR(&wwi->msgRing, dwSleepTime);
3188
3189         while (OSS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
3190         {
3191             TRACE("msg=%s param=0x%lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
3192             switch (msg) {
3193             case WINE_WM_PAUSING:
3194                 wwi->state = WINE_WS_PAUSED;
3195                 /*FIXME("Device should stop recording\n");*/
3196                 SetEvent(ev);
3197                 break;
3198             case WINE_WM_STARTING:
3199                 wwi->state = WINE_WS_PLAYING;
3200
3201                 if (wwi->ossdev->bTriggerSupport)
3202                 {
3203                     /* start the recording */
3204                     wwi->ossdev->bInputEnabled = TRUE;
3205                     enable = getEnables(wwi->ossdev);
3206                     if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
3207                         wwi->ossdev->bInputEnabled = FALSE;
3208                         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
3209                     }
3210                 }
3211                 else
3212                 {
3213                     unsigned char data[4];
3214                     /* read 4 bytes to start the recording */
3215                     read(wwi->ossdev->fd, data, 4);
3216                 }
3217
3218                 SetEvent(ev);
3219                 break;
3220             case WINE_WM_HEADER:
3221                 lpWaveHdr = (LPWAVEHDR)param;
3222                 lpWaveHdr->lpNext = 0;
3223
3224                 /* insert buffer at the end of queue */
3225                 {
3226                     LPWAVEHDR*  wh;
3227                     for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3228                     *wh = lpWaveHdr;
3229                 }
3230                 break;
3231             case WINE_WM_STOPPING:
3232                 if (wwi->state != WINE_WS_STOPPED)
3233                 {
3234                     if (wwi->ossdev->bTriggerSupport)
3235                     {
3236                         /* stop the recording */
3237                         wwi->ossdev->bInputEnabled = FALSE;
3238                         enable = getEnables(wwi->ossdev);
3239                         if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
3240                             wwi->ossdev->bInputEnabled = FALSE;
3241                             ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
3242                         }
3243                     }
3244
3245                     /* read any headers in queue */
3246                     widRecorder_ReadHeaders(wwi);
3247
3248                     /* return current buffer to app */
3249                     lpWaveHdr = wwi->lpQueuePtr;
3250                     if (lpWaveHdr)
3251                     {
3252                         LPWAVEHDR       lpNext = lpWaveHdr->lpNext;
3253                         TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
3254                         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3255                         lpWaveHdr->dwFlags |= WHDR_DONE;
3256                         wwi->lpQueuePtr = lpNext;
3257                         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3258                     }
3259                 }
3260                 wwi->state = WINE_WS_STOPPED;
3261                 SetEvent(ev);
3262                 break;
3263             case WINE_WM_RESETTING:
3264                 if (wwi->state != WINE_WS_STOPPED)
3265                 {
3266                     if (wwi->ossdev->bTriggerSupport)
3267                     {
3268                         /* stop the recording */
3269                         wwi->ossdev->bInputEnabled = FALSE;
3270                         enable = getEnables(wwi->ossdev);
3271                         if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
3272                             wwi->ossdev->bInputEnabled = FALSE;
3273                             ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
3274                         }
3275                     }
3276                 }
3277                 wwi->state = WINE_WS_STOPPED;
3278                 wwi->dwTotalRecorded = 0;
3279
3280                 /* read any headers in queue */
3281                 widRecorder_ReadHeaders(wwi);
3282
3283                 /* return all buffers to the app */
3284                 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
3285                     TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
3286                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3287                     lpWaveHdr->dwFlags |= WHDR_DONE;
3288                     wwi->lpQueuePtr = lpWaveHdr->lpNext;
3289                     widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3290                 }
3291
3292                 wwi->lpQueuePtr = NULL;
3293                 SetEvent(ev);
3294                 break;
3295             case WINE_WM_CLOSING:
3296                 wwi->hThread = 0;
3297                 wwi->state = WINE_WS_CLOSED;
3298                 SetEvent(ev);
3299                 HeapFree(GetProcessHeap(), 0, buffer);
3300                 ExitThread(0);
3301                 /* shouldn't go here */
3302             default:
3303                 FIXME("unknown message %d\n", msg);
3304                 break;
3305             }
3306         }
3307     }
3308     ExitThread(0);
3309     /* just for not generating compilation warnings... should never be executed */
3310     return 0;
3311 }
3312
3313
3314 /**************************************************************************
3315  *                              widOpen                         [internal]
3316  */
3317 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
3318 {
3319     WINE_WAVEIN*        wwi;
3320     int                 fragment_size;
3321     int                 audio_fragment;
3322     DWORD               ret;
3323
3324     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
3325     if (lpDesc == NULL) {
3326         WARN("Invalid Parameter !\n");
3327         return MMSYSERR_INVALPARAM;
3328     }
3329     if (wDevID >= numInDev) return MMSYSERR_BADDEVICEID;
3330
3331     /* only PCM format is supported so far... */
3332     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
3333         lpDesc->lpFormat->nChannels == 0 ||
3334         lpDesc->lpFormat->nSamplesPerSec == 0) {
3335         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3336              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3337              lpDesc->lpFormat->nSamplesPerSec);
3338         return WAVERR_BADFORMAT;
3339     }
3340
3341     if (dwFlags & WAVE_FORMAT_QUERY) {
3342         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3343              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3344              lpDesc->lpFormat->nSamplesPerSec);
3345         return MMSYSERR_NOERROR;
3346     }
3347
3348     wwi = &WInDev[wDevID];
3349
3350     if (wwi->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED;
3351
3352     if ((dwFlags & WAVE_DIRECTSOUND) && 
3353         !(wwi->ossdev->in_caps_support & WAVECAPS_DIRECTSOUND))
3354         /* not supported, ignore it */
3355         dwFlags &= ~WAVE_DIRECTSOUND;
3356
3357     if (dwFlags & WAVE_DIRECTSOUND) {
3358         TRACE("has DirectSoundCapture driver\n");
3359         if (wwi->ossdev->in_caps_support & WAVECAPS_SAMPLEACCURATE)
3360             /* we have realtime DirectSound, fragments just waste our time,
3361              * but a large buffer is good, so choose 64KB (32 * 2^11) */
3362             audio_fragment = 0x0020000B;
3363         else
3364             /* to approximate realtime, we must use small fragments,
3365              * let's try to fragment the above 64KB (256 * 2^8) */
3366             audio_fragment = 0x01000008;
3367     } else {
3368         TRACE("doesn't have DirectSoundCapture driver\n");
3369         if (wwi->ossdev->open_count > 0) {
3370             TRACE("Using output device audio_fragment\n");
3371             /* FIXME: This may not be optimal for capture but it allows us 
3372              * to do hardware playback without hardware capture. */
3373             audio_fragment = wwi->ossdev->audio_fragment;
3374         } else {
3375             /* A wave device must have a worst case latency of 10 ms so calculate
3376              * the largest fragment size less than 10 ms long.
3377              */
3378             int fsize = lpDesc->lpFormat->nAvgBytesPerSec / 100;        /* 10 ms chunk */
3379             int shift = 0;
3380             while ((1 << shift) <= fsize)
3381                 shift++;
3382             shift--;
3383             audio_fragment = 0x00100000 + shift;        /* 16 fragments of 2^shift */
3384         }
3385     }
3386
3387     TRACE("using %d %d byte fragments (%ld ms)\n", audio_fragment >> 16,
3388         1 << (audio_fragment & 0xffff), 
3389         ((1 << (audio_fragment & 0xffff)) * 1000) / lpDesc->lpFormat->nAvgBytesPerSec);
3390
3391     ret = OSS_OpenDevice(wwi->ossdev, O_RDONLY, &audio_fragment,
3392                          1,
3393                          lpDesc->lpFormat->nSamplesPerSec,
3394                          (lpDesc->lpFormat->nChannels > 1) ? 1 : 0,
3395                          (lpDesc->lpFormat->wBitsPerSample == 16)
3396                          ? AFMT_S16_LE : AFMT_U8);
3397     if (ret != 0) return ret;
3398     wwi->state = WINE_WS_STOPPED;
3399
3400     if (wwi->lpQueuePtr) {
3401         WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
3402         wwi->lpQueuePtr = NULL;
3403     }
3404     wwi->dwTotalRecorded = 0;
3405     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
3406
3407     memcpy(&wwi->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
3408     memcpy(&wwi->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
3409
3410     if (wwi->format.wBitsPerSample == 0) {
3411         WARN("Resetting zeroed wBitsPerSample\n");
3412         wwi->format.wBitsPerSample = 8 *
3413             (wwi->format.wf.nAvgBytesPerSec /
3414              wwi->format.wf.nSamplesPerSec) /
3415             wwi->format.wf.nChannels;
3416     }
3417
3418     ioctl(wwi->ossdev->fd, SNDCTL_DSP_GETBLKSIZE, &fragment_size);
3419     if (fragment_size == -1) {
3420         WARN("ioctl(%s, SNDCTL_DSP_GETBLKSIZE) failed (%s)\n",
3421             wwi->ossdev->dev_name, strerror(errno));
3422         OSS_CloseDevice(wwi->ossdev);
3423         wwi->state = WINE_WS_CLOSED;
3424         return MMSYSERR_NOTENABLED;
3425     }
3426     wwi->dwFragmentSize = fragment_size;
3427
3428     TRACE("dwFragmentSize=%lu\n", wwi->dwFragmentSize);
3429     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
3430           wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
3431           wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
3432           wwi->format.wf.nBlockAlign);
3433
3434     OSS_InitRingMessage(&wwi->msgRing);
3435
3436     wwi->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
3437     wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
3438     WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
3439     CloseHandle(wwi->hStartUpEvent);
3440     wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
3441
3442     return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
3443 }
3444
3445 /**************************************************************************
3446  *                              widClose                        [internal]
3447  */
3448 static DWORD widClose(WORD wDevID)
3449 {
3450     WINE_WAVEIN*        wwi;
3451
3452     TRACE("(%u);\n", wDevID);
3453     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
3454         WARN("can't close !\n");
3455         return MMSYSERR_INVALHANDLE;
3456     }
3457
3458     wwi = &WInDev[wDevID];
3459
3460     if (wwi->lpQueuePtr != NULL) {
3461         WARN("still buffers open !\n");
3462         return WAVERR_STILLPLAYING;
3463     }
3464
3465     OSS_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
3466     OSS_CloseDevice(wwi->ossdev);
3467     wwi->state = WINE_WS_CLOSED;
3468     wwi->dwFragmentSize = 0;
3469     OSS_DestroyRingMessage(&wwi->msgRing);
3470     return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
3471 }
3472
3473 /**************************************************************************
3474  *                              widAddBuffer            [internal]
3475  */
3476 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3477 {
3478     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3479
3480     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
3481         WARN("can't do it !\n");
3482         return MMSYSERR_INVALHANDLE;
3483     }
3484     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
3485         TRACE("never been prepared !\n");
3486         return WAVERR_UNPREPARED;
3487     }
3488     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
3489         TRACE("header already in use !\n");
3490         return WAVERR_STILLPLAYING;
3491     }
3492
3493     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
3494     lpWaveHdr->dwFlags &= ~WHDR_DONE;
3495     lpWaveHdr->dwBytesRecorded = 0;
3496     lpWaveHdr->lpNext = NULL;
3497
3498     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
3499     return MMSYSERR_NOERROR;
3500 }
3501
3502 /**************************************************************************
3503  *                              widPrepare                      [internal]
3504  */
3505 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3506 {
3507     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3508
3509     if (wDevID >= numInDev) return MMSYSERR_INVALHANDLE;
3510
3511     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3512         return WAVERR_STILLPLAYING;
3513
3514     lpWaveHdr->dwFlags |= WHDR_PREPARED;
3515     lpWaveHdr->dwFlags &= ~WHDR_DONE;
3516     lpWaveHdr->dwBytesRecorded = 0;
3517     TRACE("header prepared !\n");
3518     return MMSYSERR_NOERROR;
3519 }
3520
3521 /**************************************************************************
3522  *                              widUnprepare                    [internal]
3523  */
3524 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3525 {
3526     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3527     if (wDevID >= numInDev) return MMSYSERR_INVALHANDLE;
3528
3529     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3530         return WAVERR_STILLPLAYING;
3531
3532     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
3533     lpWaveHdr->dwFlags |= WHDR_DONE;
3534
3535     return MMSYSERR_NOERROR;
3536 }
3537
3538 /**************************************************************************
3539  *                      widStart                                [internal]
3540  */
3541 static DWORD widStart(WORD wDevID)
3542 {
3543     TRACE("(%u);\n", wDevID);
3544     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
3545         WARN("can't start recording !\n");
3546         return MMSYSERR_INVALHANDLE;
3547     }
3548
3549     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
3550     return MMSYSERR_NOERROR;
3551 }
3552
3553 /**************************************************************************
3554  *                      widStop                                 [internal]
3555  */
3556 static DWORD widStop(WORD wDevID)
3557 {
3558     TRACE("(%u);\n", wDevID);
3559     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
3560         WARN("can't stop !\n");
3561         return MMSYSERR_INVALHANDLE;
3562     }
3563
3564     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
3565
3566     return MMSYSERR_NOERROR;
3567 }
3568
3569 /**************************************************************************
3570  *                      widReset                                [internal]
3571  */
3572 static DWORD widReset(WORD wDevID)
3573 {
3574     TRACE("(%u);\n", wDevID);
3575     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
3576         WARN("can't reset !\n");
3577         return MMSYSERR_INVALHANDLE;
3578     }
3579     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
3580     return MMSYSERR_NOERROR;
3581 }
3582
3583 /**************************************************************************
3584  *                              widGetPosition                  [internal]
3585  */
3586 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
3587 {
3588     int                 time;
3589     WINE_WAVEIN*        wwi;
3590
3591     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
3592
3593     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
3594         WARN("can't get pos !\n");
3595         return MMSYSERR_INVALHANDLE;
3596     }
3597     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
3598
3599     wwi = &WInDev[wDevID];
3600
3601     TRACE("wType=%04X !\n", lpTime->wType);
3602     TRACE("wBitsPerSample=%u\n", wwi->format.wBitsPerSample);
3603     TRACE("nSamplesPerSec=%lu\n", wwi->format.wf.nSamplesPerSec);
3604     TRACE("nChannels=%u\n", wwi->format.wf.nChannels);
3605     TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec);
3606     TRACE("dwTotalRecorded=%lu\n",wwi->dwTotalRecorded);
3607     switch (lpTime->wType) {
3608     case TIME_BYTES:
3609         lpTime->u.cb = wwi->dwTotalRecorded;
3610         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
3611         break;
3612     case TIME_SAMPLES:
3613         lpTime->u.sample = wwi->dwTotalRecorded * 8 /
3614             wwi->format.wBitsPerSample / wwi->format.wf.nChannels;
3615         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
3616         break;
3617     case TIME_SMPTE:
3618         time = wwi->dwTotalRecorded /
3619             (wwi->format.wf.nAvgBytesPerSec / 1000);
3620         lpTime->u.smpte.hour = time / (60 * 60 * 1000);
3621         time -= lpTime->u.smpte.hour * (60 * 60 * 1000);
3622         lpTime->u.smpte.min = time / (60 * 1000);
3623         time -= lpTime->u.smpte.min * (60 * 1000);
3624         lpTime->u.smpte.sec = time / 1000;
3625         time -= lpTime->u.smpte.sec * 1000;
3626         lpTime->u.smpte.frame = time * 30 / 1000;
3627         lpTime->u.smpte.fps = 30;
3628         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
3629               lpTime->u.smpte.hour, lpTime->u.smpte.min,
3630               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
3631         break;
3632     default:
3633         FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
3634         lpTime->wType = TIME_MS;
3635     case TIME_MS:
3636         lpTime->u.ms = wwi->dwTotalRecorded /
3637             (wwi->format.wf.nAvgBytesPerSec / 1000);
3638         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
3639         break;
3640     }
3641     return MMSYSERR_NOERROR;
3642 }
3643
3644 /**************************************************************************
3645  *                              widMessage (WINEOSS.6)
3646  */
3647 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3648                             DWORD dwParam1, DWORD dwParam2)
3649 {
3650     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
3651           wDevID, wMsg, dwUser, dwParam1, dwParam2);
3652
3653     switch (wMsg) {
3654     case DRVM_INIT:
3655     case DRVM_EXIT:
3656     case DRVM_ENABLE:
3657     case DRVM_DISABLE:
3658         /* FIXME: Pretend this is supported */
3659         return 0;
3660     case WIDM_OPEN:             return widOpen       (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
3661     case WIDM_CLOSE:            return widClose      (wDevID);
3662     case WIDM_ADDBUFFER:        return widAddBuffer  (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3663     case WIDM_PREPARE:          return widPrepare    (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3664     case WIDM_UNPREPARE:        return widUnprepare  (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3665     case WIDM_GETDEVCAPS:       return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
3666     case WIDM_GETNUMDEVS:       return numInDev;
3667     case WIDM_GETPOS:           return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
3668     case WIDM_RESET:            return widReset      (wDevID);
3669     case WIDM_START:            return widStart      (wDevID);
3670     case WIDM_STOP:             return widStop       (wDevID);
3671     case DRV_QUERYDEVICEINTERFACESIZE: return wdDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
3672     case DRV_QUERYDEVICEINTERFACE:     return wdDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
3673     case DRV_QUERYDSOUNDIFACE:  return widDsCreate   (wDevID, (PIDSCDRIVER*)dwParam1);
3674     case DRV_QUERYDSOUNDDESC:   return widDsDesc     (wDevID, (PDSDRIVERDESC)dwParam1);
3675     case DRV_QUERYDSOUNDGUID:   return widDsGuid     (wDevID, (LPGUID)dwParam1);
3676     default:
3677         FIXME("unknown message %u!\n", wMsg);
3678     }
3679     return MMSYSERR_NOTSUPPORTED;
3680 }
3681
3682 /*======================================================================*
3683  *           Low level DSOUND capture definitions                       *
3684  *======================================================================*/
3685
3686 typedef struct IDsCaptureDriverPropertySetImpl IDsCaptureDriverPropertySetImpl;
3687 typedef struct IDsCaptureDriverNotifyImpl IDsCaptureDriverNotifyImpl;
3688 typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl;
3689 typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl;
3690
3691 struct IDsCaptureDriverPropertySetImpl
3692 {
3693     /* IUnknown fields */
3694     ICOM_VFIELD(IDsDriverPropertySet);
3695     DWORD                               ref;
3696
3697     IDsCaptureDriverBufferImpl*         capture_buffer;
3698 };
3699
3700 struct IDsCaptureDriverNotifyImpl
3701 {
3702     /* IUnknown fields */
3703     ICOM_VFIELD(IDsDriverNotify);
3704     DWORD                               ref;
3705
3706     /* IDsDriverNotifyImpl fields */
3707     LPDSBPOSITIONNOTIFY                 notifies;
3708     int                                 nrofnotifies;
3709
3710     IDsCaptureDriverBufferImpl*        capture_buffer;
3711 };
3712
3713 struct IDsCaptureDriverImpl
3714 {
3715     /* IUnknown fields */
3716     ICOM_VFIELD(IDsCaptureDriver);
3717     DWORD                               ref;
3718
3719     /* IDsCaptureDriverImpl fields */
3720     UINT                                wDevID;
3721     IDsCaptureDriverBufferImpl*         capture_buffer;
3722 };
3723
3724 struct IDsCaptureDriverBufferImpl
3725 {
3726     /* IUnknown fields */
3727     ICOM_VFIELD(IDsCaptureDriverBuffer);
3728     DWORD                               ref;
3729
3730     /* IDsCaptureDriverBufferImpl fields */
3731     IDsCaptureDriverImpl*               drv;
3732     DWORD                               buflen;
3733     LPBYTE                              buffer;
3734     DWORD                               writeptr;
3735     LPBYTE                              mapping;
3736     DWORD                               maplen;
3737
3738     /* IDsDriverNotifyImpl fields */
3739     IDsCaptureDriverNotifyImpl*         notify;
3740     int                                 notify_index;
3741
3742     /* IDsDriverPropertySetImpl fields */
3743     IDsCaptureDriverPropertySetImpl*    property_set;
3744 };
3745
3746 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Create(
3747     IDsCaptureDriverBufferImpl * dscdb,
3748     IDsCaptureDriverPropertySetImpl **pdscdps);
3749
3750 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_Create(
3751     IDsCaptureDriverBufferImpl * dsdcb,
3752     IDsCaptureDriverNotifyImpl **pdscdn);
3753
3754 /*======================================================================*
3755  *           Low level DSOUND capture property set implementation       *
3756  *======================================================================*/
3757
3758 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QueryInterface(
3759     PIDSDRIVERPROPERTYSET iface,
3760     REFIID riid,
3761     LPVOID *ppobj) 
3762 {
3763     ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
3764     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
3765
3766     if ( IsEqualGUID(riid, &IID_IUnknown) ||
3767          IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
3768         IDsDriverPropertySet_AddRef(iface);
3769         *ppobj = (LPVOID)This;
3770         return DS_OK;
3771     }
3772
3773     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
3774
3775     *ppobj = 0;
3776     return E_NOINTERFACE;
3777 }
3778
3779 static ULONG WINAPI IDsCaptureDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface) 
3780 {
3781     ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
3782     DWORD ref;
3783     TRACE("(%p) ref was %ld\n", This, This->ref);
3784
3785     ref = InterlockedIncrement(&(This->ref));
3786     return ref;
3787 }
3788
3789 static ULONG WINAPI IDsCaptureDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface) 
3790 {
3791     ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
3792     DWORD ref;
3793     TRACE("(%p) ref was %ld\n", This, This->ref);
3794
3795     ref = InterlockedDecrement(&(This->ref));
3796     if (ref == 0) {
3797         IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
3798         HeapFree(GetProcessHeap(),0,This);
3799         TRACE("(%p) released\n",This);
3800     }
3801     return ref;
3802 }
3803
3804 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Get(
3805     PIDSDRIVERPROPERTYSET iface,
3806     PDSPROPERTY pDsProperty,
3807     LPVOID pPropertyParams,
3808     ULONG cbPropertyParams,
3809     LPVOID pPropertyData,
3810     ULONG cbPropertyData,
3811     PULONG pcbReturnedData )
3812 {
3813     ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
3814     FIXME("(%p,%p,%p,%lx,%p,%lx,%p)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
3815     return DSERR_UNSUPPORTED;
3816 }
3817
3818 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Set(
3819     PIDSDRIVERPROPERTYSET iface,
3820     PDSPROPERTY pDsProperty,
3821     LPVOID pPropertyParams,
3822     ULONG cbPropertyParams,
3823     LPVOID pPropertyData,
3824     ULONG cbPropertyData )
3825 {
3826     ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
3827     FIXME("(%p,%p,%p,%lx,%p,%lx)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData);
3828     return DSERR_UNSUPPORTED;
3829 }
3830
3831 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QuerySupport(
3832     PIDSDRIVERPROPERTYSET iface,
3833     REFGUID PropertySetId,
3834     ULONG PropertyId,
3835     PULONG pSupport )
3836 {
3837     ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
3838     FIXME("(%p,%s,%lx,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,pSupport);
3839     return DSERR_UNSUPPORTED;
3840 }
3841
3842 ICOM_VTABLE(IDsDriverPropertySet) dscdpsvt =
3843 {
3844     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3845     IDsCaptureDriverPropertySetImpl_QueryInterface,
3846     IDsCaptureDriverPropertySetImpl_AddRef,
3847     IDsCaptureDriverPropertySetImpl_Release,
3848     IDsCaptureDriverPropertySetImpl_Get,
3849     IDsCaptureDriverPropertySetImpl_Set,
3850     IDsCaptureDriverPropertySetImpl_QuerySupport,
3851 };
3852
3853 /*======================================================================*
3854  *                  Low level DSOUND capture notify implementation      *
3855  *======================================================================*/
3856
3857 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_QueryInterface(
3858     PIDSDRIVERNOTIFY iface,
3859     REFIID riid,
3860     LPVOID *ppobj) 
3861 {
3862     ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
3863     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
3864
3865     if ( IsEqualGUID(riid, &IID_IUnknown) ||
3866          IsEqualGUID(riid, &IID_IDsDriverNotify) ) {
3867         IDsDriverNotify_AddRef(iface);
3868         *ppobj = This;
3869         return DS_OK;
3870     }
3871
3872     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
3873
3874     *ppobj = 0;
3875     return E_NOINTERFACE;
3876 }
3877
3878 static ULONG WINAPI IDsCaptureDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface) 
3879 {
3880     ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
3881     DWORD ref;
3882     TRACE("(%p) ref was %ld\n", This, This->ref);
3883
3884     ref = InterlockedIncrement(&(This->ref));
3885     return ref;
3886 }
3887
3888 static ULONG WINAPI IDsCaptureDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface) 
3889 {
3890     ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
3891     DWORD ref;
3892     TRACE("(%p) ref was %ld\n", This, This->ref);
3893
3894     ref = InterlockedDecrement(&(This->ref));
3895     if (ref == 0) {
3896         IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
3897         if (This->notifies != NULL)
3898             HeapFree(GetProcessHeap(), 0, This->notifies);
3899
3900         HeapFree(GetProcessHeap(),0,This);
3901         TRACE("(%p) released\n",This);
3902     }
3903
3904     return ref;
3905 }
3906
3907 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_SetNotificationPositions(
3908     PIDSDRIVERNOTIFY iface,
3909     DWORD howmuch,
3910     LPCDSBPOSITIONNOTIFY notify) 
3911 {
3912     ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
3913     TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
3914
3915     if (!notify) {
3916         WARN("invalid parameter\n");
3917         return DSERR_INVALIDPARAM;
3918     }
3919
3920     if (TRACE_ON(wave)) {
3921         int i;
3922         for (i=0;i<howmuch;i++)
3923             TRACE("notify at %ld to 0x%08lx\n",
3924                 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
3925     }
3926
3927     /* Make an internal copy of the caller-supplied array.
3928      * Replace the existing copy if one is already present. */
3929     if (This->notifies) 
3930         This->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3931             This->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));
3932     else 
3933         This->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3934             howmuch * sizeof(DSBPOSITIONNOTIFY));
3935
3936     memcpy(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
3937     This->nrofnotifies = howmuch;
3938
3939     return S_OK;
3940 }
3941
3942 ICOM_VTABLE(IDsDriverNotify) dscdnvt =
3943 {
3944     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3945     IDsCaptureDriverNotifyImpl_QueryInterface,
3946     IDsCaptureDriverNotifyImpl_AddRef,
3947     IDsCaptureDriverNotifyImpl_Release,
3948     IDsCaptureDriverNotifyImpl_SetNotificationPositions,
3949 };
3950
3951 /*======================================================================*
3952  *                  Low level DSOUND capture implementation             *
3953  *======================================================================*/
3954
3955 static HRESULT DSCDB_MapBuffer(IDsCaptureDriverBufferImpl *dscdb)
3956 {
3957     if (!dscdb->mapping) {
3958         dscdb->mapping = mmap(NULL, dscdb->maplen, PROT_READ, MAP_SHARED,
3959                               WInDev[dscdb->drv->wDevID].ossdev->fd, 0);
3960         if (dscdb->mapping == (LPBYTE)-1) {
3961             TRACE("(%p): Could not map sound device for direct access (%s)\n", dscdb, strerror(errno));
3962             return DSERR_GENERIC;
3963         }
3964         TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dscdb, dscdb->mapping, dscdb->maplen);
3965     }
3966     return DS_OK;
3967 }
3968
3969 static HRESULT DSCDB_UnmapBuffer(IDsCaptureDriverBufferImpl *dscdb)
3970 {
3971     if (dscdb->mapping) {
3972         if (munmap(dscdb->mapping, dscdb->maplen) < 0) {
3973             ERR("(%p): Could not unmap sound device (%s)\n", dscdb, strerror(errno));
3974             return DSERR_GENERIC;
3975         }
3976         dscdb->mapping = NULL;
3977         TRACE("(%p): sound device unmapped\n", dscdb);
3978     }
3979     return DS_OK;
3980 }
3981
3982 static HRESULT WINAPI IDsCaptureDriverBufferImpl_QueryInterface(PIDSCDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
3983 {
3984     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
3985     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
3986
3987     if ( IsEqualGUID(riid, &IID_IUnknown) ||
3988          IsEqualGUID(riid, &IID_IDsCaptureDriverBuffer) ) {
3989         IDsCaptureDriverBuffer_AddRef(iface);
3990         *ppobj = (LPVOID)This;
3991         return DS_OK;
3992     }
3993
3994     if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
3995         if (!This->notify)
3996             IDsCaptureDriverNotifyImpl_Create(This, &(This->notify));
3997         if (This->notify) {
3998             IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
3999             *ppobj = (LPVOID)This->notify;
4000             return DS_OK;
4001         }
4002         *ppobj = 0;
4003         return E_FAIL;
4004     }
4005
4006     if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
4007         if (!This->property_set)
4008             IDsCaptureDriverPropertySetImpl_Create(This, &(This->property_set));
4009         if (This->property_set) {
4010             IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
4011             *ppobj = (LPVOID)This->property_set;
4012             return DS_OK;
4013         }
4014         *ppobj = 0;
4015         return E_FAIL;
4016     }
4017
4018     FIXME("(%p,%s,%p) unsupported GUID\n", This, debugstr_guid(riid), ppobj);
4019
4020     *ppobj = 0;
4021
4022     return DSERR_UNSUPPORTED;
4023 }
4024
4025 static ULONG WINAPI IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface)
4026 {
4027     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4028     This->ref++;
4029     return This->ref;
4030 }
4031
4032 static ULONG WINAPI IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface)
4033 {
4034     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4035     if (--This->ref)
4036         return This->ref;
4037     DSCDB_UnmapBuffer(This);
4038     if (This->notify)
4039         IDsDriverNotify_Release((PIDSDRIVERNOTIFY)This->notify);
4040     if (This->property_set)
4041         IDsDriverPropertySet_Release((PIDSDRIVERPROPERTYSET)This->property_set);
4042     HeapFree(GetProcessHeap(),0,This);
4043     return 0;
4044 }
4045
4046 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Lock(PIDSCDRIVERBUFFER iface,
4047                                                       LPVOID*ppvAudio1,LPDWORD pdwLen1,
4048                                                       LPVOID*ppvAudio2,LPDWORD pdwLen2,
4049                                                       DWORD dwWritePosition,DWORD dwWriteLen,
4050                                                       DWORD dwFlags)
4051 {
4052     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4053     FIXME("(%p,%p,%p,%p,%p,%ld,%ld,0x%08lx): stub!\n",This,ppvAudio1,pdwLen1,ppvAudio2,pdwLen2,
4054         dwWritePosition,dwWriteLen,dwFlags);
4055     return DS_OK;
4056 }
4057
4058 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Unlock(PIDSCDRIVERBUFFER iface,
4059                                                         LPVOID pvAudio1,DWORD dwLen1,
4060                                                         LPVOID pvAudio2,DWORD dwLen2)
4061 {
4062     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4063     FIXME("(%p,%p,%ld,%p,%ld): stub!\n",This,pvAudio1,dwLen1,pvAudio2,dwLen2);
4064     return DS_OK;
4065 }
4066
4067 static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetPosition(PIDSCDRIVERBUFFER iface,
4068                                                              LPDWORD lpdwCapture, 
4069                                                              LPDWORD lpdwRead)
4070 {
4071     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4072     count_info info;
4073     DWORD ptr;
4074     TRACE("(%p,%p,%p)\n",This,lpdwCapture,lpdwRead);
4075
4076     if (WInDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
4077         ERR("device not open, but accessing?\n");
4078         return DSERR_UNINITIALIZED;
4079     }
4080     if (ioctl(WInDev[This->drv->wDevID].ossdev->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
4081         ERR("ioctl(%s, SNDCTL_DSP_GETIPTR) failed (%s)\n", WInDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
4082         return DSERR_GENERIC;
4083     }
4084     ptr = info.ptr & ~3; /* align the pointer, just in case */
4085     if (lpdwCapture) *lpdwCapture = ptr;
4086     if (lpdwRead) {
4087         /* add some safety margin (not strictly necessary, but...) */
4088         if (WInDev[This->drv->wDevID].ossdev->in_caps_support & WAVECAPS_SAMPLEACCURATE)
4089             *lpdwRead = ptr + 32;
4090         else
4091             *lpdwRead = ptr + WInDev[This->drv->wDevID].dwFragmentSize;
4092         while (*lpdwRead > This->buflen)
4093             *lpdwRead -= This->buflen;
4094     }
4095     TRACE("capturepos=%ld, readpos=%ld\n", lpdwCapture?*lpdwCapture:0, lpdwRead?*lpdwRead:0);
4096     return DS_OK;
4097 }
4098
4099 static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetStatus(PIDSCDRIVERBUFFER iface, LPDWORD lpdwStatus)
4100 {
4101     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4102     FIXME("(%p,%p): stub!\n",This,lpdwStatus);
4103     return DSERR_UNSUPPORTED;
4104 }
4105
4106 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Start(PIDSCDRIVERBUFFER iface, DWORD dwFlags)
4107 {
4108     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4109     int enable;
4110     TRACE("(%p,%lx)\n",This,dwFlags);
4111     WInDev[This->drv->wDevID].ossdev->bInputEnabled = TRUE;
4112     enable = getEnables(WInDev[This->drv->wDevID].ossdev);
4113     if (ioctl(WInDev[This->drv->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
4114         if (errno == EINVAL) {
4115             /* Don't give up yet. OSS trigger support is inconsistent. */
4116             if (WInDev[This->drv->wDevID].ossdev->open_count == 1) {
4117                 /* try the opposite output enable */
4118                 if (WInDev[This->drv->wDevID].ossdev->bOutputEnabled == FALSE)
4119                     WInDev[This->drv->wDevID].ossdev->bOutputEnabled = TRUE;
4120                 else
4121                     WInDev[This->drv->wDevID].ossdev->bOutputEnabled = FALSE;
4122                 /* try it again */
4123                 enable = getEnables(WInDev[This->drv->wDevID].ossdev);
4124                 if (ioctl(WInDev[This->drv->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) >= 0)
4125                     return DS_OK;
4126             }
4127         }
4128         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WInDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
4129         WInDev[This->drv->wDevID].ossdev->bInputEnabled = FALSE;
4130         return DSERR_GENERIC;
4131     }
4132     return DS_OK;
4133 }
4134
4135 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface)
4136 {
4137     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4138     int enable;
4139     TRACE("(%p)\n",This);
4140     /* no more captureing */
4141     WInDev[This->drv->wDevID].ossdev->bInputEnabled = FALSE;
4142     enable = getEnables(WInDev[This->drv->wDevID].ossdev);
4143     if (ioctl(WInDev[This->drv->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
4144         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WInDev[This->drv->wDevID].ossdev->dev_name,  strerror(errno));
4145         return DSERR_GENERIC;
4146     }
4147
4148     /* Most OSS drivers just can't stop capturing without closing the device...
4149      * so we need to somehow signal to our DirectSound implementation
4150      * that it should completely recreate this HW buffer...
4151      * this unexpected error code should do the trick... */
4152     return DSERR_BUFFERLOST;
4153 }
4154
4155 static HRESULT WINAPI IDsCaptureDriverBufferImpl_SetFormat(PIDSCDRIVERBUFFER iface, LPWAVEFORMATEX pwfx)
4156 {
4157     ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
4158     FIXME("(%p): stub!\n",This);
4159     return DSERR_UNSUPPORTED;
4160 }
4161
4162 static ICOM_VTABLE(IDsCaptureDriverBuffer) dscdbvt =
4163 {
4164     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4165     IDsCaptureDriverBufferImpl_QueryInterface,
4166     IDsCaptureDriverBufferImpl_AddRef,
4167     IDsCaptureDriverBufferImpl_Release,
4168     IDsCaptureDriverBufferImpl_Lock,
4169     IDsCaptureDriverBufferImpl_Unlock,
4170     IDsCaptureDriverBufferImpl_SetFormat,
4171     IDsCaptureDriverBufferImpl_GetPosition,
4172     IDsCaptureDriverBufferImpl_GetStatus,
4173     IDsCaptureDriverBufferImpl_Start,
4174     IDsCaptureDriverBufferImpl_Stop
4175 };
4176
4177 static HRESULT WINAPI IDsCaptureDriverImpl_QueryInterface(PIDSCDRIVER iface, REFIID riid, LPVOID *ppobj)
4178 {
4179     ICOM_THIS(IDsCaptureDriverImpl,iface);
4180     TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
4181
4182     if ( IsEqualGUID(riid, &IID_IUnknown) ||
4183          IsEqualGUID(riid, &IID_IDsCaptureDriver) ) {
4184         IDsCaptureDriver_AddRef(iface);
4185         *ppobj = (LPVOID)This;
4186         return DS_OK;
4187     }
4188
4189     FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
4190
4191     *ppobj = 0;
4192
4193     return E_NOINTERFACE;
4194 }
4195
4196 static ULONG WINAPI IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface)
4197 {
4198     ICOM_THIS(IDsCaptureDriverImpl,iface);
4199     TRACE("(%p)\n",This);
4200     This->ref++;
4201     TRACE("ref=%ld\n",This->ref);
4202     return This->ref;
4203 }
4204
4205 static ULONG WINAPI IDsCaptureDriverImpl_Release(PIDSCDRIVER iface)
4206 {
4207     ICOM_THIS(IDsCaptureDriverImpl,iface);
4208     TRACE("(%p)\n",This);
4209     if (--This->ref) {
4210         TRACE("ref=%ld\n",This->ref);
4211         return This->ref;
4212     }
4213     HeapFree(GetProcessHeap(),0,This);
4214     TRACE("ref=0\n");
4215     return 0;
4216 }
4217
4218 static HRESULT WINAPI IDsCaptureDriverImpl_GetDriverDesc(PIDSCDRIVER iface, PDSDRIVERDESC pDesc)
4219 {
4220     ICOM_THIS(IDsCaptureDriverImpl,iface);
4221     TRACE("(%p,%p)\n",This,pDesc);
4222
4223     if (!pDesc) {
4224         TRACE("invalid parameter\n");
4225         return DSERR_INVALIDPARAM;
4226     }
4227
4228     /* copy version from driver */
4229     memcpy(pDesc, &(WInDev[This->wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC));
4230
4231     pDesc->dwFlags |= DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
4232         DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK | 
4233         DSDDESC_DONTNEEDSECONDARYLOCK;
4234     pDesc->dnDevNode            = WInDev[This->wDevID].waveDesc.dnDevNode;
4235     pDesc->wVxdId               = 0;
4236     pDesc->wReserved            = 0;
4237     pDesc->ulDeviceNum          = This->wDevID;
4238     pDesc->dwHeapType           = DSDHEAP_NOHEAP;
4239     pDesc->pvDirectDrawHeap     = NULL;
4240     pDesc->dwMemStartAddress    = 0;
4241     pDesc->dwMemEndAddress      = 0;
4242     pDesc->dwMemAllocExtra      = 0;
4243     pDesc->pvReserved1          = NULL;
4244     pDesc->pvReserved2          = NULL;
4245     return DS_OK;
4246 }
4247
4248 static HRESULT WINAPI IDsCaptureDriverImpl_Open(PIDSCDRIVER iface)
4249 {
4250     ICOM_THIS(IDsCaptureDriverImpl,iface);
4251     TRACE("(%p)\n",This);
4252     return DS_OK;
4253 }
4254
4255 static HRESULT WINAPI IDsCaptureDriverImpl_Close(PIDSCDRIVER iface)
4256 {
4257     ICOM_THIS(IDsCaptureDriverImpl,iface);
4258     TRACE("(%p)\n",This);
4259     if (This->capture_buffer) {
4260         ERR("problem with DirectSound: capture buffer not released\n");
4261         return DSERR_GENERIC;
4262     }
4263     return DS_OK;
4264 }
4265
4266 static HRESULT WINAPI IDsCaptureDriverImpl_GetCaps(PIDSCDRIVER iface, PDSCDRIVERCAPS pCaps)
4267 {
4268     ICOM_THIS(IDsCaptureDriverImpl,iface);
4269     TRACE("(%p,%p)\n",This,pCaps);
4270     memcpy(pCaps, &(WInDev[This->wDevID].ossdev->dsc_caps), sizeof(DSCDRIVERCAPS)); 
4271     return DS_OK;
4272 }
4273
4274 static HRESULT WINAPI IDsCaptureDriverImpl_CreateCaptureBuffer(PIDSCDRIVER iface,
4275                                                                LPWAVEFORMATEX pwfx,
4276                                                                DWORD dwFlags, 
4277                                                                DWORD dwCardAddress,
4278                                                                LPDWORD pdwcbBufferSize,
4279                                                                LPBYTE *ppbBuffer,
4280                                                                LPVOID *ppvObj)
4281 {
4282     ICOM_THIS(IDsCaptureDriverImpl,iface);
4283     IDsCaptureDriverBufferImpl** ippdscdb = (IDsCaptureDriverBufferImpl**)ppvObj;
4284     HRESULT err;
4285     audio_buf_info info;
4286     int enable;
4287     TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",This,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
4288
4289     if (This->capture_buffer) {
4290         TRACE("already allocated\n");
4291         return DSERR_ALLOCATED;
4292     }
4293
4294     *ippdscdb = (IDsCaptureDriverBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverBufferImpl));
4295     if (*ippdscdb == NULL) {
4296         TRACE("out of memory\n");
4297         return DSERR_OUTOFMEMORY;
4298     }
4299
4300     (*ippdscdb)->lpVtbl = &dscdbvt;
4301     (*ippdscdb)->ref          = 1;
4302     (*ippdscdb)->drv          = This;
4303     (*ippdscdb)->notify       = 0;
4304     (*ippdscdb)->notify_index = 0;
4305     (*ippdscdb)->property_set = 0;
4306
4307     if (WInDev[This->wDevID].state == WINE_WS_CLOSED) {
4308         WAVEOPENDESC desc;
4309         desc.hWave = 0;
4310         desc.lpFormat = pwfx; 
4311         desc.dwCallback = 0;
4312         desc.dwInstance = 0;
4313         desc.uMappedDeviceID = 0;
4314         desc.dnDevNode = 0;
4315         err = widOpen(This->wDevID, &desc, dwFlags | WAVE_DIRECTSOUND);
4316         if (err != MMSYSERR_NOERROR) {
4317             TRACE("widOpen failed\n");
4318             return err;
4319         }
4320     }
4321
4322     /* check how big the DMA buffer is now */
4323     if (ioctl(WInDev[This->wDevID].ossdev->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
4324         ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n", WInDev[This->wDevID].ossdev->dev_name, strerror(errno));
4325         HeapFree(GetProcessHeap(),0,*ippdscdb);
4326         *ippdscdb = NULL;
4327         return DSERR_GENERIC;
4328     }
4329     (*ippdscdb)->maplen = (*ippdscdb)->buflen = info.fragstotal * info.fragsize;
4330
4331     /* map the DMA buffer */
4332     err = DSCDB_MapBuffer(*ippdscdb);
4333     if (err != DS_OK) {
4334         HeapFree(GetProcessHeap(),0,*ippdscdb);
4335         *ippdscdb = NULL;
4336         return err;
4337     }
4338
4339     /* capture buffer is ready to go */
4340     *pdwcbBufferSize    = (*ippdscdb)->maplen;
4341     *ppbBuffer          = (*ippdscdb)->mapping;
4342
4343     /* some drivers need some extra nudging after mapping */
4344     WInDev[This->wDevID].ossdev->bInputEnabled = FALSE;
4345     enable = getEnables(WInDev[This->wDevID].ossdev);
4346     if (ioctl(WInDev[This->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
4347         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WInDev[This->wDevID].ossdev->dev_name, strerror(errno));
4348         return DSERR_GENERIC;
4349     }
4350
4351     This->capture_buffer = *ippdscdb;
4352
4353     return DS_OK;
4354 }
4355
4356 static ICOM_VTABLE(IDsCaptureDriver) dscdvt =
4357 {
4358     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4359     IDsCaptureDriverImpl_QueryInterface,
4360     IDsCaptureDriverImpl_AddRef,
4361     IDsCaptureDriverImpl_Release,
4362     IDsCaptureDriverImpl_GetDriverDesc,
4363     IDsCaptureDriverImpl_Open,
4364     IDsCaptureDriverImpl_Close,
4365     IDsCaptureDriverImpl_GetCaps,
4366     IDsCaptureDriverImpl_CreateCaptureBuffer
4367 };
4368
4369 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Create(
4370     IDsCaptureDriverBufferImpl * dscdb,
4371     IDsCaptureDriverPropertySetImpl **pdscdps)
4372 {
4373     IDsCaptureDriverPropertySetImpl * dscdps;
4374     TRACE("(%p,%p)\n",dscdb,pdscdps);
4375
4376     dscdps = (IDsCaptureDriverPropertySetImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dscdps));
4377     if (dscdps == NULL) {
4378         WARN("out of memory\n");
4379         return DSERR_OUTOFMEMORY;
4380     }
4381                                                                                 
4382     dscdps->ref = 0;
4383     dscdps->lpVtbl = &dscdpsvt;
4384     dscdps->capture_buffer = dscdb;
4385     dscdb->property_set = dscdps;
4386     IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
4387                                                                                 
4388     *pdscdps = dscdps;
4389     return DS_OK;
4390 }
4391
4392 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_Create(
4393     IDsCaptureDriverBufferImpl * dscdb,
4394     IDsCaptureDriverNotifyImpl **pdscdn)
4395 {
4396     IDsCaptureDriverNotifyImpl * dscdn;
4397     TRACE("(%p,%p)\n",dscdb,pdscdn);
4398
4399     dscdn = (IDsCaptureDriverNotifyImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dscdn));
4400     if (dscdn == NULL) {
4401         WARN("out of memory\n");
4402         return DSERR_OUTOFMEMORY;
4403     }
4404                                                                                 
4405     dscdn->ref = 0;
4406     dscdn->lpVtbl = &dscdnvt;
4407     dscdn->capture_buffer = dscdb;
4408     dscdb->notify = dscdn;
4409     IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
4410                                                                                 
4411     *pdscdn = dscdn;
4412     return DS_OK;
4413 };
4414
4415 static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
4416 {
4417     IDsCaptureDriverImpl** idrv = (IDsCaptureDriverImpl**)drv;
4418     TRACE("(%d,%p)\n",wDevID,drv);
4419
4420     /* the HAL isn't much better than the HEL if we can't do mmap() */
4421     if (!(WInDev[wDevID].ossdev->in_caps_support & WAVECAPS_DIRECTSOUND)) {
4422         ERR("DirectSoundCapture flag not set\n");
4423         MESSAGE("This sound card's driver does not support direct access\n");
4424         MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
4425         return MMSYSERR_NOTSUPPORTED;
4426     }
4427
4428     *idrv = (IDsCaptureDriverImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverImpl));
4429     if (!*idrv)
4430         return MMSYSERR_NOMEM;
4431     (*idrv)->lpVtbl     = &dscdvt;
4432     (*idrv)->ref        = 1;
4433
4434     (*idrv)->wDevID     = wDevID;
4435     (*idrv)->capture_buffer = NULL;
4436     return MMSYSERR_NOERROR;
4437 }
4438
4439 static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
4440 {
4441     memcpy(desc, &(WInDev[wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC));
4442     return MMSYSERR_NOERROR;
4443 }
4444
4445 static DWORD widDsGuid(UINT wDevID, LPGUID pGuid)
4446 {
4447     TRACE("(%d,%p)\n",wDevID,pGuid);
4448
4449     memcpy(pGuid, &(WInDev[wDevID].ossdev->dsc_guid), sizeof(GUID));
4450
4451     return MMSYSERR_NOERROR;
4452 }
4453
4454 #else /* !HAVE_OSS */
4455
4456 /**************************************************************************
4457  *                              wodMessage (WINEOSS.7)
4458  */
4459 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
4460                             DWORD dwParam1, DWORD dwParam2)
4461 {
4462     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
4463     return MMSYSERR_NOTENABLED;
4464 }
4465
4466 /**************************************************************************
4467  *                              widMessage (WINEOSS.6)
4468  */
4469 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
4470                             DWORD dwParam1, DWORD dwParam2)
4471 {
4472     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
4473     return MMSYSERR_NOTENABLED;
4474 }
4475
4476 #endif /* HAVE_OSS */