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