Added parser template and made AVISplitter use it.
[wine] / dlls / winmm / wineoss / audio.c
1 /*
2  * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
3  *
4  * Copyright 1994 Martin Ayotte
5  *           1999 Eric Pouech (async playing in waveOut/waveIn)
6  *           2000 Eric Pouech (loops in waveOut)
7  *           2002 Eric Pouech (full duplex)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 /*
24  * FIXME:
25  *      pause in waveOut does not work correctly in loop mode
26  *      Direct Sound Capture driver does not work (not complete yet)
27  */
28
29 /* an exact wodGetPosition is usually not worth the extra context switches,
30  * as we're going to have near fragment accuracy anyway */
31 #define EXACT_WODPOSITION
32 #define EXACT_WIDPOSITION
33
34 #include "config.h"
35 #include "wine/port.h"
36
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <string.h>
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 #include <errno.h>
45 #include <fcntl.h>
46 #ifdef HAVE_SYS_IOCTL_H
47 # include <sys/ioctl.h>
48 #endif
49 #ifdef HAVE_SYS_MMAN_H
50 # include <sys/mman.h>
51 #endif
52 #ifdef HAVE_SYS_POLL_H
53 # include <sys/poll.h>
54 #endif
55
56 #include "windef.h"
57 #include "winbase.h"
58 #include "wingdi.h"
59 #include "winuser.h"
60 #include "winnls.h"
61 #include "winerror.h"
62 #include "mmddk.h"
63 #include "mmreg.h"
64 #include "dsound.h"
65 #include "dsdriver.h"
66 #include "ks.h"
67 #include "ksguid.h"
68 #include "ksmedia.h"
69 #include "oss.h"
70 #include "wine/debug.h"
71
72 #include "audio.h"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(wave);
75
76 /* Allow 1% deviation for sample rates (some ES137x cards) */
77 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
78
79 #ifdef HAVE_OSS
80
81 OSS_DEVICE      OSS_Devices[MAX_WAVEDRV];
82 WINE_WAVEOUT    WOutDev[MAX_WAVEDRV];
83 WINE_WAVEIN     WInDev[MAX_WAVEDRV];
84 unsigned        numOutDev;
85 unsigned        numInDev;
86
87 /* state diagram for waveOut writing:
88  *
89  * +---------+-------------+---------------+---------------------------------+
90  * |  state  |  function   |     event     |            new state            |
91  * +---------+-------------+---------------+---------------------------------+
92  * |         | open()      |               | STOPPED                         |
93  * | PAUSED  | write()     |               | PAUSED                          |
94  * | STOPPED | write()     | <thrd create> | PLAYING                         |
95  * | PLAYING | write()     | HEADER        | PLAYING                         |
96  * | (other) | write()     | <error>       |                                 |
97  * | (any)   | pause()     | PAUSING       | PAUSED                          |
98  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
99  * | (any)   | reset()     | RESETTING     | STOPPED                         |
100  * | (any)   | close()     | CLOSING       | CLOSED                          |
101  * +---------+-------------+---------------+---------------------------------+
102  */
103
104 /* These strings used only for tracing */
105 static const char * getCmdString(enum win_wm_message msg)
106 {
107     static char unknown[32];
108 #define MSG_TO_STR(x) case x: return #x
109     switch(msg) {
110     MSG_TO_STR(WINE_WM_PAUSING);
111     MSG_TO_STR(WINE_WM_RESTARTING);
112     MSG_TO_STR(WINE_WM_RESETTING);
113     MSG_TO_STR(WINE_WM_HEADER);
114     MSG_TO_STR(WINE_WM_UPDATE);
115     MSG_TO_STR(WINE_WM_BREAKLOOP);
116     MSG_TO_STR(WINE_WM_CLOSING);
117     MSG_TO_STR(WINE_WM_STARTING);
118     MSG_TO_STR(WINE_WM_STOPPING);
119     }
120 #undef MSG_TO_STR
121     sprintf(unknown, "UNKNOWN(0x%08x)", msg);
122     return unknown;
123 }
124
125 int getEnables(OSS_DEVICE *ossdev)
126 {
127     return ( (ossdev->bOutputEnabled ? PCM_ENABLE_OUTPUT : 0) |
128              (ossdev->bInputEnabled  ? PCM_ENABLE_INPUT  : 0) );
129 }
130
131 static const char * getMessage(UINT msg)
132 {
133     static char unknown[32];
134 #define MSG_TO_STR(x) case x: return #x
135     switch(msg) {
136     MSG_TO_STR(DRVM_INIT);
137     MSG_TO_STR(DRVM_EXIT);
138     MSG_TO_STR(DRVM_ENABLE);
139     MSG_TO_STR(DRVM_DISABLE);
140     MSG_TO_STR(WIDM_OPEN);
141     MSG_TO_STR(WIDM_CLOSE);
142     MSG_TO_STR(WIDM_ADDBUFFER);
143     MSG_TO_STR(WIDM_PREPARE);
144     MSG_TO_STR(WIDM_UNPREPARE);
145     MSG_TO_STR(WIDM_GETDEVCAPS);
146     MSG_TO_STR(WIDM_GETNUMDEVS);
147     MSG_TO_STR(WIDM_GETPOS);
148     MSG_TO_STR(WIDM_RESET);
149     MSG_TO_STR(WIDM_START);
150     MSG_TO_STR(WIDM_STOP);
151     MSG_TO_STR(WODM_OPEN);
152     MSG_TO_STR(WODM_CLOSE);
153     MSG_TO_STR(WODM_WRITE);
154     MSG_TO_STR(WODM_PAUSE);
155     MSG_TO_STR(WODM_GETPOS);
156     MSG_TO_STR(WODM_BREAKLOOP);
157     MSG_TO_STR(WODM_PREPARE);
158     MSG_TO_STR(WODM_UNPREPARE);
159     MSG_TO_STR(WODM_GETDEVCAPS);
160     MSG_TO_STR(WODM_GETNUMDEVS);
161     MSG_TO_STR(WODM_GETPITCH);
162     MSG_TO_STR(WODM_SETPITCH);
163     MSG_TO_STR(WODM_GETPLAYBACKRATE);
164     MSG_TO_STR(WODM_SETPLAYBACKRATE);
165     MSG_TO_STR(WODM_GETVOLUME);
166     MSG_TO_STR(WODM_SETVOLUME);
167     MSG_TO_STR(WODM_RESTART);
168     MSG_TO_STR(WODM_RESET);
169     MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
170     MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
171     MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
172     MSG_TO_STR(DRV_QUERYDSOUNDDESC);
173     }
174 #undef MSG_TO_STR
175     sprintf(unknown, "UNKNOWN(0x%04x)", msg);
176     return unknown;
177 }
178
179 static DWORD wdDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
180 {
181     TRACE("(%u, %p)\n", wDevID, dwParam1);
182
183     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, OSS_Devices[wDevID].interface_name, -1,
184                                     NULL, 0 ) * sizeof(WCHAR);
185     return MMSYSERR_NOERROR;
186 }
187
188 static DWORD wdDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
189 {
190     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, OSS_Devices[wDevID].interface_name, -1,
191                                         NULL, 0 ) * sizeof(WCHAR))
192     {
193         MultiByteToWideChar(CP_ACP, 0, OSS_Devices[wDevID].interface_name, -1,
194                             dwParam1, dwParam2 / sizeof(WCHAR));
195         return MMSYSERR_NOERROR;
196     }
197
198     return MMSYSERR_INVALPARAM;
199 }
200
201 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
202                              WAVEFORMATPCMEX* format)
203 {
204     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
205           lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
206           format->Format.nChannels, format->Format.nAvgBytesPerSec);
207     TRACE("Position in bytes=%lu\n", position);
208
209     switch (lpTime->wType) {
210     case TIME_SAMPLES:
211         lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
212         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
213         break;
214     case TIME_MS:
215         lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
216         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
217         break;
218     case TIME_SMPTE:
219         position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
220         lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
221         position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
222         lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
223         lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
224         lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
225         lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
226         lpTime->u.smpte.fps = 30;
227         lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
228         position -= lpTime->u.smpte.frame * format->Format.nSamplesPerSec / lpTime->u.smpte.fps;
229         if (position != 0)
230         {
231             /* Round up */
232             lpTime->u.smpte.frame++;
233         }
234         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
235               lpTime->u.smpte.hour, lpTime->u.smpte.min,
236               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
237         break;
238     default:
239         WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
240         lpTime->wType = TIME_BYTES;
241         /* fall through */
242     case TIME_BYTES:
243         lpTime->u.cb = position;
244         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
245         break;
246     }
247     return MMSYSERR_NOERROR;
248 }
249
250 static BOOL supportedFormat(LPWAVEFORMATEX wf)
251 {
252     TRACE("(%p)\n",wf);
253
254     if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
255         return FALSE;
256
257     if (wf->wFormatTag == WAVE_FORMAT_PCM) {
258         if (wf->nChannels==1||wf->nChannels==2) {
259             if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
260                 return TRUE;
261         }
262     } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
263         WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE *)wf;
264
265         if (wf->cbSize == 22 && IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
266             if (wf->nChannels==1||wf->nChannels==2) {
267                 if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
268                     if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
269                         return TRUE;
270                 } else
271                     WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
272             }
273         } else
274             WARN("only KSDATAFORMAT_SUBTYPE_PCM supported\n");
275     } else
276         WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
277
278     return FALSE;
279 }
280
281 void copy_format(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
282 {
283     ZeroMemory(wf2, sizeof(wf2));
284     if (wf1->wFormatTag == WAVE_FORMAT_PCM)
285         memcpy(wf2, wf1, sizeof(PCMWAVEFORMAT));
286     else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
287         memcpy(wf2, wf1, sizeof(WAVEFORMATPCMEX));
288     else
289         memcpy(wf2, wf1, sizeof(WAVEFORMATEX) + wf1->cbSize);
290 }
291
292 /*======================================================================*
293  *                  Low level WAVE implementation                       *
294  *======================================================================*/
295
296 /******************************************************************
297  *              OSS_RawOpenDevice
298  *
299  * Low level device opening (from values stored in ossdev)
300  */
301 static DWORD      OSS_RawOpenDevice(OSS_DEVICE* ossdev, int strict_format)
302 {
303     int fd, val, rc;
304     TRACE("(%p,%d)\n",ossdev,strict_format);
305
306     TRACE("open_access=%s\n",
307         ossdev->open_access == O_RDONLY ? "O_RDONLY" :
308         ossdev->open_access == O_WRONLY ? "O_WRONLY" :
309         ossdev->open_access == O_RDWR ? "O_RDWR" : "Unknown");
310
311     if ((fd = open(ossdev->dev_name, ossdev->open_access|O_NDELAY, 0)) == -1)
312     {
313         WARN("Couldn't open %s (%s)\n", ossdev->dev_name, strerror(errno));
314         return (errno == EBUSY) ? MMSYSERR_ALLOCATED : MMSYSERR_ERROR;
315     }
316     fcntl(fd, F_SETFD, 1); /* set close on exec flag */
317     /* turn full duplex on if it has been requested */
318     if (ossdev->open_access == O_RDWR && ossdev->full_duplex) {
319         rc = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
320         /* on *BSD, as full duplex is always enabled by default, this ioctl
321          * will fail with EINVAL
322          * so, we don't consider EINVAL an error here
323          */
324         if (rc != 0 && errno != EINVAL) {
325             WARN("ioctl(%s, SNDCTL_DSP_SETDUPLEX) failed (%s)\n", ossdev->dev_name, strerror(errno));
326             goto error2;
327         }
328     }
329
330     if (ossdev->audio_fragment) {
331         rc = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossdev->audio_fragment);
332         if (rc != 0) {
333             ERR("ioctl(%s, SNDCTL_DSP_SETFRAGMENT) failed (%s)\n", ossdev->dev_name, strerror(errno));
334             goto error2;
335         }
336     }
337
338     /* First size and stereo then samplerate */
339     if (ossdev->format>=0)
340     {
341         val = ossdev->format;
342         rc = ioctl(fd, SNDCTL_DSP_SETFMT, &ossdev->format);
343         if (rc != 0 || val != ossdev->format) {
344             TRACE("Can't set format to %d (returned %d)\n", val, ossdev->format);
345             if (strict_format)
346                 goto error;
347         }
348     }
349     if (ossdev->stereo>=0)
350     {
351         val = ossdev->stereo;
352         rc = ioctl(fd, SNDCTL_DSP_STEREO, &ossdev->stereo);
353         if (rc != 0 || val != ossdev->stereo) {
354             TRACE("Can't set stereo to %u (returned %d)\n", val, ossdev->stereo);
355             if (strict_format)
356                 goto error;
357         }
358     }
359     if (ossdev->sample_rate>=0)
360     {
361         val = ossdev->sample_rate;
362         rc = ioctl(fd, SNDCTL_DSP_SPEED, &ossdev->sample_rate);
363         if (rc != 0 || !NEAR_MATCH(val, ossdev->sample_rate)) {
364             TRACE("Can't set sample_rate to %u (returned %d)\n", val, ossdev->sample_rate);
365             if (strict_format)
366                 goto error;
367         }
368     }
369     ossdev->fd = fd;
370
371     if (ossdev->bTriggerSupport) {
372         int trigger;
373         rc = ioctl(fd, SNDCTL_DSP_GETTRIGGER, &trigger);
374         if (rc != 0) {
375             ERR("ioctl(%s, SNDCTL_DSP_GETTRIGGER) failed (%s)\n",
376                 ossdev->dev_name, strerror(errno));
377             goto error;
378         }
379         
380         ossdev->bOutputEnabled = ((trigger & PCM_ENABLE_OUTPUT) == PCM_ENABLE_OUTPUT);
381         ossdev->bInputEnabled  = ((trigger & PCM_ENABLE_INPUT) == PCM_ENABLE_INPUT);
382     } else {
383         ossdev->bOutputEnabled = TRUE;  /* OSS enables by default */
384         ossdev->bInputEnabled  = TRUE;  /* OSS enables by default */
385     }
386
387     return MMSYSERR_NOERROR;
388
389 error:
390     close(fd);
391     return WAVERR_BADFORMAT;
392 error2:
393     close(fd);
394     return MMSYSERR_ERROR;
395 }
396
397 /******************************************************************
398  *              OSS_OpenDevice
399  *
400  * since OSS has poor capabilities in full duplex, we try here to let a program
401  * open the device for both waveout and wavein streams...
402  * this is hackish, but it's the way OSS interface is done...
403  */
404 DWORD OSS_OpenDevice(OSS_DEVICE* ossdev, unsigned req_access,
405                             int* frag, int strict_format,
406                             int sample_rate, int stereo, int fmt)
407 {
408     DWORD       ret;
409     DWORD open_access;
410     TRACE("(%p,%u,%p,%d,%d,%d,%x)\n",ossdev,req_access,frag,strict_format,sample_rate,stereo,fmt);
411
412     if (ossdev->full_duplex && (req_access == O_RDONLY || req_access == O_WRONLY))
413     {
414         TRACE("Opening RDWR because full_duplex=%d and req_access=%d\n",
415               ossdev->full_duplex,req_access);
416         open_access = O_RDWR;
417     }
418     else
419     {
420         open_access=req_access;
421     }
422
423     /* FIXME: this should be protected, and it also contains a race with OSS_CloseDevice */
424     if (ossdev->open_count == 0)
425     {
426         if (access(ossdev->dev_name, 0) != 0) return MMSYSERR_NODRIVER;
427
428         ossdev->audio_fragment = (frag) ? *frag : 0;
429         ossdev->sample_rate = sample_rate;
430         ossdev->stereo = stereo;
431         ossdev->format = fmt;
432         ossdev->open_access = open_access;
433         ossdev->owner_tid = GetCurrentThreadId();
434
435         if ((ret = OSS_RawOpenDevice(ossdev,strict_format)) != MMSYSERR_NOERROR) return ret;
436         if (ossdev->full_duplex && ossdev->bTriggerSupport &&
437             (req_access == O_RDONLY || req_access == O_WRONLY))
438         {
439             int enable;
440             if (req_access == O_WRONLY)
441                 ossdev->bInputEnabled=0;
442             else
443                 ossdev->bOutputEnabled=0;
444             enable = getEnables(ossdev);
445             TRACE("Calling SNDCTL_DSP_SETTRIGGER with %x\n",enable);
446             if (ioctl(ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0)
447                 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER, %d) failed (%s)\n",ossdev->dev_name, enable, strerror(errno));
448         }
449     }
450     else
451     {
452         /* check we really open with the same parameters */
453         if (ossdev->open_access != open_access)
454         {
455             ERR("FullDuplex: Mismatch in access. Your sound device is not full duplex capable.\n");
456             return WAVERR_BADFORMAT;
457         }
458
459         /* check if the audio parameters are the same */
460         if (ossdev->sample_rate != sample_rate ||
461             ossdev->stereo != stereo ||
462             ossdev->format != fmt)
463         {
464             /* This is not a fatal error because MSACM might do the remapping */
465             WARN("FullDuplex: mismatch in PCM parameters for input and output\n"
466                  "OSS doesn't allow us different parameters\n"
467                  "audio_frag(%x/%x) sample_rate(%d/%d) stereo(%d/%d) fmt(%d/%d)\n",
468                  ossdev->audio_fragment, frag ? *frag : 0,
469                  ossdev->sample_rate, sample_rate,
470                  ossdev->stereo, stereo,
471                  ossdev->format, fmt);
472             return WAVERR_BADFORMAT;
473         }
474         /* check if the fragment sizes are the same */
475         if (ossdev->audio_fragment != (frag ? *frag : 0) ) {
476             ERR("FullDuplex: Playback and Capture hardware acceleration levels are different.\n"
477                 "Use: \"HardwareAcceleration\" = \"Emulation\" in the [dsound] section of your config file.\n");
478             return WAVERR_BADFORMAT;
479         }
480         if (GetCurrentThreadId() != ossdev->owner_tid)
481         {
482             WARN("Another thread is trying to access audio...\n");
483             return MMSYSERR_ERROR;
484         }
485         if (ossdev->full_duplex && ossdev->bTriggerSupport &&
486             (req_access == O_RDONLY || req_access == O_WRONLY))
487         {
488             int enable;
489             if (req_access == O_WRONLY)
490                 ossdev->bOutputEnabled=1;
491             else
492                 ossdev->bInputEnabled=1;
493             enable = getEnables(ossdev);
494             TRACE("Calling SNDCTL_DSP_SETTRIGGER with %x\n",enable);
495             if (ioctl(ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0)
496                 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER, %d) failed (%s)\n",ossdev->dev_name, enable, strerror(errno));
497         }
498     }
499
500     ossdev->open_count++;
501
502     return MMSYSERR_NOERROR;
503 }
504
505 /******************************************************************
506  *              OSS_CloseDevice
507  *
508  *
509  */
510 void    OSS_CloseDevice(OSS_DEVICE* ossdev)
511 {
512     TRACE("(%p)\n",ossdev);
513     if (ossdev->open_count>0) {
514         ossdev->open_count--;
515     } else {
516         WARN("OSS_CloseDevice called too many times\n");
517     }
518     if (ossdev->open_count == 0)
519     {
520         fcntl(ossdev->fd, F_SETFL, fcntl(ossdev->fd, F_GETFL) & ~O_NDELAY);
521         /* reset the device before we close it in case it is in a bad state */
522         ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
523         if (close(ossdev->fd) != 0) FIXME("Cannot close %d: %s\n", ossdev->fd, strerror(errno));
524     }
525 }
526
527 /******************************************************************
528  *              OSS_ResetDevice
529  *
530  * Resets the device. OSS Commercial requires the device to be closed
531  * after a SNDCTL_DSP_RESET ioctl call... this function implements
532  * this behavior...
533  * FIXME: This causes problems when doing full duplex so we really
534  * only reset when not doing full duplex. We need to do this better
535  * someday.
536  */
537 static DWORD     OSS_ResetDevice(OSS_DEVICE* ossdev)
538 {
539     DWORD       ret = MMSYSERR_NOERROR;
540     int         old_fd = ossdev->fd;
541     TRACE("(%p)\n", ossdev);
542
543     if (ossdev->open_count == 1) {
544         if (ioctl(ossdev->fd, SNDCTL_DSP_RESET, NULL) == -1)
545         {
546             perror("ioctl SNDCTL_DSP_RESET");
547             return -1;
548         }
549         close(ossdev->fd);
550         ret = OSS_RawOpenDevice(ossdev, 1);
551         TRACE("Changing fd from %d to %d\n", old_fd, ossdev->fd);
552     } else
553         WARN("Not resetting device because it is in full duplex mode!\n");
554
555     return ret;
556 }
557
558 static const int win_std_oss_fmts[2]={AFMT_U8,AFMT_S16_LE};
559 static const int win_std_rates[5]={96000,48000,44100,22050,11025};
560 static const int win_std_formats[2][2][5]=
561     {{{WAVE_FORMAT_96M08, WAVE_FORMAT_48M08, WAVE_FORMAT_4M08,
562        WAVE_FORMAT_2M08,  WAVE_FORMAT_1M08},
563       {WAVE_FORMAT_96S08, WAVE_FORMAT_48S08, WAVE_FORMAT_4S08,
564        WAVE_FORMAT_2S08,  WAVE_FORMAT_1S08}},
565      {{WAVE_FORMAT_96M16, WAVE_FORMAT_48M16, WAVE_FORMAT_4M16,
566        WAVE_FORMAT_2M16,  WAVE_FORMAT_1M16},
567       {WAVE_FORMAT_96S16, WAVE_FORMAT_48S16, WAVE_FORMAT_4S16,
568        WAVE_FORMAT_2S16,  WAVE_FORMAT_1S16}},
569     };
570
571 static void OSS_Info(int fd)
572 {
573     /* Note that this only reports the formats supported by the hardware.
574      * The driver may support other formats and do the conversions in
575      * software which is why we don't use this value
576      */
577     int oss_mask, oss_caps;
578     if (ioctl(fd, SNDCTL_DSP_GETFMTS, &oss_mask) >= 0) {
579         TRACE("Formats=%08x ( ", oss_mask);
580         if (oss_mask & AFMT_MU_LAW) TRACE("AFMT_MU_LAW ");
581         if (oss_mask & AFMT_A_LAW) TRACE("AFMT_A_LAW ");
582         if (oss_mask & AFMT_IMA_ADPCM) TRACE("AFMT_IMA_ADPCM ");
583         if (oss_mask & AFMT_U8) TRACE("AFMT_U8 ");
584         if (oss_mask & AFMT_S16_LE) TRACE("AFMT_S16_LE ");
585         if (oss_mask & AFMT_S16_BE) TRACE("AFMT_S16_BE ");
586         if (oss_mask & AFMT_S8) TRACE("AFMT_S8 ");
587         if (oss_mask & AFMT_U16_LE) TRACE("AFMT_U16_LE ");
588         if (oss_mask & AFMT_U16_BE) TRACE("AFMT_U16_BE ");
589         if (oss_mask & AFMT_MPEG) TRACE("AFMT_MPEG ");
590 #ifdef AFMT_AC3
591         if (oss_mask & AFMT_AC3) TRACE("AFMT_AC3 ");
592 #endif
593 #ifdef AFMT_VORBIS
594         if (oss_mask & AFMT_VORBIS) TRACE("AFMT_VORBIS ");
595 #endif
596 #ifdef AFMT_S32_LE
597         if (oss_mask & AFMT_S32_LE) TRACE("AFMT_S32_LE ");
598 #endif
599 #ifdef AFMT_S32_BE
600         if (oss_mask & AFMT_S32_BE) TRACE("AFMT_S32_BE ");
601 #endif
602 #ifdef AFMT_FLOAT
603         if (oss_mask & AFMT_FLOAT) TRACE("AFMT_FLOAT ");
604 #endif
605 #ifdef AFMT_S24_LE
606         if (oss_mask & AFMT_S24_LE) TRACE("AFMT_S24_LE ");
607 #endif
608 #ifdef AFMT_S24_BE
609         if (oss_mask & AFMT_S24_BE) TRACE("AFMT_S24_BE ");
610 #endif
611 #ifdef AFMT_SPDIF_RAW
612         if (oss_mask & AFMT_SPDIF_RAW) TRACE("AFMT_SPDIF_RAW ");
613 #endif
614         TRACE(")\n");
615     }
616     if (ioctl(fd, SNDCTL_DSP_GETCAPS, &oss_caps) >= 0) {
617         TRACE("Caps=%08x\n",oss_caps);
618         TRACE("\tRevision: %d\n", oss_caps&DSP_CAP_REVISION);
619         TRACE("\tDuplex: %s\n", oss_caps & DSP_CAP_DUPLEX ? "true" : "false");
620         TRACE("\tRealtime: %s\n", oss_caps & DSP_CAP_REALTIME ? "true" : "false");
621         TRACE("\tBatch: %s\n", oss_caps & DSP_CAP_BATCH ? "true" : "false");
622         TRACE("\tCoproc: %s\n", oss_caps & DSP_CAP_COPROC ? "true" : "false");
623         TRACE("\tTrigger: %s\n", oss_caps & DSP_CAP_TRIGGER ? "true" : "false");
624         TRACE("\tMmap: %s\n", oss_caps & DSP_CAP_MMAP ? "true" : "false");
625 #ifdef DSP_CAP_MULTI
626         TRACE("\tMulti: %s\n", oss_caps & DSP_CAP_MULTI ? "true" : "false");
627 #endif
628 #ifdef DSP_CAP_BIND
629         TRACE("\tBind: %s\n", oss_caps & DSP_CAP_BIND ? "true" : "false");
630 #endif
631 #ifdef DSP_CAP_INPUT
632         TRACE("\tInput: %s\n", oss_caps & DSP_CAP_INPUT ? "true" : "false");
633 #endif
634 #ifdef DSP_CAP_OUTPUT
635         TRACE("\tOutput: %s\n", oss_caps & DSP_CAP_OUTPUT ? "true" : "false");
636 #endif
637 #ifdef DSP_CAP_VIRTUAL
638         TRACE("\tVirtual: %s\n", oss_caps & DSP_CAP_VIRTUAL ? "true" : "false");
639 #endif
640 #ifdef DSP_CAP_ANALOGOUT
641         TRACE("\tAnalog Out: %s\n", oss_caps & DSP_CAP_ANALOGOUT ? "true" : "false");
642 #endif
643 #ifdef DSP_CAP_ANALOGIN
644         TRACE("\tAnalog In: %s\n", oss_caps & DSP_CAP_ANALOGIN ? "true" : "false");
645 #endif
646 #ifdef DSP_CAP_DIGITALOUT
647         TRACE("\tDigital Out: %s\n", oss_caps & DSP_CAP_DIGITALOUT ? "true" : "false");
648 #endif
649 #ifdef DSP_CAP_DIGITALIN
650         TRACE("\tDigital In: %s\n", oss_caps & DSP_CAP_DIGITALIN ? "true" : "false");
651 #endif
652 #ifdef DSP_CAP_ADMASK
653         TRACE("\tA/D Mask: %s\n", oss_caps & DSP_CAP_ADMASK ? "true" : "false");
654 #endif
655 #ifdef DSP_CAP_SHADOW
656         TRACE("\tShadow: %s\n", oss_caps & DSP_CAP_SHADOW ? "true" : "false");
657 #endif
658 #ifdef DSP_CH_MASK
659         TRACE("\tChannel Mask: %x\n", oss_caps & DSP_CH_MASK);
660 #endif
661 #ifdef DSP_CAP_SLAVE
662         TRACE("\tSlave: %s\n", oss_caps & DSP_CAP_SLAVE ? "true" : "false");
663 #endif
664     }
665 }
666
667 /******************************************************************
668  *              OSS_WaveOutInit
669  *
670  *
671  */
672 static BOOL OSS_WaveOutInit(OSS_DEVICE* ossdev)
673 {
674     int rc,arg;
675     int f,c,r;
676     TRACE("(%p) %s\n", ossdev, ossdev->dev_name);
677
678     if (OSS_OpenDevice(ossdev, O_WRONLY, NULL, 0,-1,-1,-1) != 0)
679         return FALSE;
680
681     ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
682
683 #ifdef SOUND_MIXER_INFO
684     {
685         int mixer;
686         if ((mixer = open(ossdev->mixer_name, O_RDONLY|O_NDELAY)) >= 0) {
687             mixer_info info;
688             if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
689                 strncpy(ossdev->ds_desc.szDesc, info.name, sizeof(info.name));
690                 strcpy(ossdev->ds_desc.szDrvname, "wineoss.drv");
691                 MultiByteToWideChar(CP_ACP, 0, info.name, sizeof(info.name), 
692                                     ossdev->out_caps.szPname, 
693                                     sizeof(ossdev->out_caps.szPname) / sizeof(WCHAR));
694                 TRACE("%s\n", ossdev->ds_desc.szDesc);
695             } else {
696                 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
697                  * implement it properly, and there are probably similar issues
698                  * on other platforms, so we warn but try to go ahead.
699                  */
700                 WARN("%s: cannot read SOUND_MIXER_INFO!\n", ossdev->mixer_name);
701             }
702             close(mixer);
703         } else {
704             ERR("%s: %s\n", ossdev->mixer_name , strerror( errno ));
705             OSS_CloseDevice(ossdev);
706             return FALSE;
707         }
708     }
709 #endif /* SOUND_MIXER_INFO */
710
711     if (WINE_TRACE_ON(wave))
712         OSS_Info(ossdev->fd);
713
714     ossdev->out_caps.wMid = 0x00FF; /* Manufac ID */
715     ossdev->out_caps.wPid = 0x0001; /* Product ID */
716
717     ossdev->out_caps.vDriverVersion = 0x0100;
718     ossdev->out_caps.wChannels = 1;
719     ossdev->out_caps.dwFormats = 0x00000000;
720     ossdev->out_caps.wReserved1 = 0;
721     ossdev->out_caps.dwSupport = WAVECAPS_VOLUME;
722
723     /* direct sound caps */
724     ossdev->ds_caps.dwFlags = DSCAPS_CERTIFIED;
725     ossdev->ds_caps.dwPrimaryBuffers = 1;
726     ossdev->ds_caps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
727     ossdev->ds_caps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
728
729     /* We must first set the format and the stereo mode as some sound cards
730      * may support 44kHz mono but not 44kHz stereo. Also we must
731      * systematically check the return value of these ioctls as they will
732      * always succeed (see OSS Linux) but will modify the parameter to match
733      * whatever they support. The OSS specs also say we must first set the
734      * sample size, then the stereo and then the sample rate.
735      */
736     for (f=0;f<2;f++) {
737         arg=win_std_oss_fmts[f];
738         rc=ioctl(ossdev->fd, SNDCTL_DSP_SAMPLESIZE, &arg);
739         if (rc!=0 || arg!=win_std_oss_fmts[f]) {
740             TRACE("DSP_SAMPLESIZE: rc=%d returned %d for %d\n",
741                   rc,arg,win_std_oss_fmts[f]);
742             continue;
743         }
744         if (f == 0)
745             ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARY8BIT;
746         else if (f == 1)
747             ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARY16BIT;
748
749         for (c=0;c<2;c++) {
750             arg=c;
751             rc=ioctl(ossdev->fd, SNDCTL_DSP_STEREO, &arg);
752             if (rc!=0 || arg!=c) {
753                 TRACE("DSP_STEREO: rc=%d returned %d for %d\n",rc,arg,c);
754                 continue;
755             }
756             if (c == 0) {
757                 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYMONO;
758             } else if (c==1) {
759                 ossdev->out_caps.wChannels=2;
760                 ossdev->out_caps.dwSupport|=WAVECAPS_LRVOLUME;
761                 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYSTEREO;
762             }
763
764             for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
765                 arg=win_std_rates[r];
766                 rc=ioctl(ossdev->fd, SNDCTL_DSP_SPEED, &arg);
767                 TRACE("DSP_SPEED: rc=%d returned %d for %dx%dx%d\n",
768                       rc,arg,win_std_rates[r],win_std_oss_fmts[f],c+1);
769                 if (rc==0 && arg!=0 && NEAR_MATCH(arg,win_std_rates[r]))
770                     ossdev->out_caps.dwFormats|=win_std_formats[f][c][r];
771             }
772         }
773     }
774
775     if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &arg) == 0) {
776         if (arg & DSP_CAP_TRIGGER)
777             ossdev->bTriggerSupport = TRUE;
778         if ((arg & DSP_CAP_REALTIME) && !(arg & DSP_CAP_BATCH)) {
779             ossdev->out_caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
780         }
781         /* well, might as well use the DirectSound cap flag for something */
782         if ((arg & DSP_CAP_TRIGGER) && (arg & DSP_CAP_MMAP) &&
783             !(arg & DSP_CAP_BATCH)) {
784             ossdev->out_caps.dwSupport |= WAVECAPS_DIRECTSOUND;
785         } else {
786             ossdev->ds_caps.dwFlags |= DSCAPS_EMULDRIVER;
787         }
788 #ifdef DSP_CAP_MULTI    /* not every oss has this */
789         /* check for hardware secondary buffer support (multi open) */
790         if ((arg & DSP_CAP_MULTI) &&
791             (ossdev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
792             TRACE("hardware secondary buffer support available\n");
793             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARY8BIT)
794                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARY8BIT;
795             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARY16BIT)
796                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARY16BIT;
797             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARYMONO)
798                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARYMONO;
799             if (ossdev->ds_caps.dwFlags & DSCAPS_PRIMARYSTEREO)
800                 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARYSTEREO;
801
802             ossdev->ds_caps.dwMaxHwMixingAllBuffers = 16;
803             ossdev->ds_caps.dwMaxHwMixingStaticBuffers = 0;
804             ossdev->ds_caps.dwMaxHwMixingStreamingBuffers = 16;
805
806             ossdev->ds_caps.dwFreeHwMixingAllBuffers = 16;
807             ossdev->ds_caps.dwFreeHwMixingStaticBuffers = 0;
808             ossdev->ds_caps.dwFreeHwMixingStreamingBuffers = 16;
809         }
810 #endif
811     }
812     OSS_CloseDevice(ossdev);
813     TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
814           ossdev->out_caps.dwFormats, ossdev->out_caps.dwSupport);
815     return TRUE;
816 }
817
818 /******************************************************************
819  *              OSS_WaveInInit
820  *
821  *
822  */
823 static BOOL OSS_WaveInInit(OSS_DEVICE* ossdev)
824 {
825     int rc,arg;
826     int f,c,r;
827     TRACE("(%p) %s\n", ossdev, ossdev->dev_name);
828
829     if (OSS_OpenDevice(ossdev, O_RDONLY, NULL, 0,-1,-1,-1) != 0)
830         return FALSE;
831
832     ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
833
834 #ifdef SOUND_MIXER_INFO
835     {
836         int mixer;
837         if ((mixer = open(ossdev->mixer_name, O_RDONLY|O_NDELAY)) >= 0) {
838             mixer_info info;
839             if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
840                 MultiByteToWideChar(CP_ACP, 0, info.name, -1, 
841                                     ossdev->in_caps.szPname, 
842                                     sizeof(ossdev->in_caps.szPname) / sizeof(WCHAR));
843                 TRACE("%s\n", ossdev->ds_desc.szDesc);
844             } else {
845                 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
846                  * implement it properly, and there are probably similar issues
847                  * on other platforms, so we warn but try to go ahead.
848                  */
849                 WARN("%s: cannot read SOUND_MIXER_INFO!\n", ossdev->mixer_name);
850             }
851             close(mixer);
852         } else {
853             ERR("%s: %s\n", ossdev->mixer_name, strerror(errno));
854             OSS_CloseDevice(ossdev);
855             return FALSE;
856         }
857     }
858 #endif /* SOUND_MIXER_INFO */
859
860     if (WINE_TRACE_ON(wave))
861         OSS_Info(ossdev->fd);
862
863     ossdev->in_caps.wMid = 0x00FF; /* Manufac ID */
864     ossdev->in_caps.wPid = 0x0001; /* Product ID */
865
866     ossdev->in_caps.dwFormats = 0x00000000;
867     ossdev->in_caps.wChannels = 1;
868     ossdev->in_caps.wReserved1 = 0;
869
870     /* direct sound caps */
871     ossdev->dsc_caps.dwSize = sizeof(ossdev->dsc_caps);
872     ossdev->dsc_caps.dwFlags = 0;
873     ossdev->dsc_caps.dwFormats = 0x00000000;
874     ossdev->dsc_caps.dwChannels = 1;
875
876     /* See the comment in OSS_WaveOutInit for the loop order */
877     for (f=0;f<2;f++) {
878         arg=win_std_oss_fmts[f];
879         rc=ioctl(ossdev->fd, SNDCTL_DSP_SAMPLESIZE, &arg);
880         if (rc!=0 || arg!=win_std_oss_fmts[f]) {
881             TRACE("DSP_SAMPLESIZE: rc=%d returned 0x%x for 0x%x\n",
882                   rc,arg,win_std_oss_fmts[f]);
883             continue;
884         }
885
886         for (c=0;c<2;c++) {
887             arg=c;
888             rc=ioctl(ossdev->fd, SNDCTL_DSP_STEREO, &arg);
889             if (rc!=0 || arg!=c) {
890                 TRACE("DSP_STEREO: rc=%d returned %d for %d\n",rc,arg,c);
891                 continue;
892             }
893             if (c==1) {
894                 ossdev->in_caps.wChannels=2;
895                 ossdev->dsc_caps.dwChannels=2;
896             }
897
898             for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
899                 arg=win_std_rates[r];
900                 rc=ioctl(ossdev->fd, SNDCTL_DSP_SPEED, &arg);
901                 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);
902                 if (rc==0 && NEAR_MATCH(arg,win_std_rates[r]))
903                     ossdev->in_caps.dwFormats|=win_std_formats[f][c][r];
904                     ossdev->dsc_caps.dwFormats|=win_std_formats[f][c][r];
905             }
906         }
907     }
908
909     if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &arg) == 0) {
910         if (arg & DSP_CAP_TRIGGER)
911             ossdev->bTriggerSupport = TRUE;
912         if ((arg & DSP_CAP_TRIGGER) && (arg & DSP_CAP_MMAP) &&
913             !(arg & DSP_CAP_BATCH)) {
914             /* FIXME: enable the next statement if you want to work on the driver */
915 #if 0
916             ossdev->in_caps_support |= WAVECAPS_DIRECTSOUND;
917 #endif
918         }
919         if ((arg & DSP_CAP_REALTIME) && !(arg & DSP_CAP_BATCH))
920             ossdev->in_caps_support |= WAVECAPS_SAMPLEACCURATE;
921     }
922     OSS_CloseDevice(ossdev);
923     TRACE("in dwFormats = %08lX, in_caps_support = %08lX\n",
924         ossdev->in_caps.dwFormats, ossdev->in_caps_support);
925     return TRUE;
926 }
927
928 /******************************************************************
929  *              OSS_WaveFullDuplexInit
930  *
931  *
932  */
933 static void OSS_WaveFullDuplexInit(OSS_DEVICE* ossdev)
934 {
935     int rc,arg;
936     int f,c,r;
937     int caps;
938     TRACE("(%p) %s\n", ossdev, ossdev->dev_name);
939
940     /* The OSS documentation says we must call SNDCTL_SETDUPLEX
941      * *before* checking for SNDCTL_DSP_GETCAPS otherwise we may
942      * get the wrong result. This ioctl must even be done before
943      * setting the fragment size so that only OSS_RawOpenDevice is
944      * in a position to do it. So we set full_duplex speculatively
945      * and adjust right after.
946      */
947     ossdev->full_duplex=1;
948     rc=OSS_OpenDevice(ossdev, O_RDWR, NULL, 0,-1,-1,-1);
949     ossdev->full_duplex=0;
950     if (rc != 0)
951         return;
952
953     ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
954     TRACE("%s\n", ossdev->ds_desc.szDesc);
955
956
957     if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &caps) == 0)
958         ossdev->full_duplex = (caps & DSP_CAP_DUPLEX);
959
960     ossdev->duplex_out_caps = ossdev->out_caps;
961
962     ossdev->duplex_out_caps.wChannels = 1;
963     ossdev->duplex_out_caps.dwFormats = 0x00000000;
964     ossdev->duplex_out_caps.dwSupport = WAVECAPS_VOLUME;
965
966     if (WINE_TRACE_ON(wave))
967         OSS_Info(ossdev->fd);
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 == 0) {
987                 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYMONO;
988             } else if (c==1) {
989                 ossdev->duplex_out_caps.wChannels=2;
990                 ossdev->duplex_out_caps.dwSupport|=WAVECAPS_LRVOLUME;
991                 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYSTEREO;
992             }
993
994             for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
995                 arg=win_std_rates[r];
996                 rc=ioctl(ossdev->fd, SNDCTL_DSP_SPEED, &arg);
997                 TRACE("DSP_SPEED: rc=%d returned %d for %dx%dx%d\n",
998                       rc,arg,win_std_rates[r],win_std_oss_fmts[f],c+1);
999                 if (rc==0 && arg!=0 && NEAR_MATCH(arg,win_std_rates[r]))
1000                     ossdev->duplex_out_caps.dwFormats|=win_std_formats[f][c][r];
1001             }
1002         }
1003     }
1004
1005     if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &arg) == 0) {
1006         if ((arg & DSP_CAP_REALTIME) && !(arg & DSP_CAP_BATCH)) {
1007             ossdev->duplex_out_caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
1008         }
1009         /* well, might as well use the DirectSound cap flag for something */
1010         if ((arg & DSP_CAP_TRIGGER) && (arg & DSP_CAP_MMAP) &&
1011             !(arg & DSP_CAP_BATCH)) {
1012             ossdev->duplex_out_caps.dwSupport |= WAVECAPS_DIRECTSOUND;
1013         }
1014     }
1015     OSS_CloseDevice(ossdev);
1016     TRACE("duplex dwFormats = %08lX, dwSupport = %08lX\n",
1017           ossdev->duplex_out_caps.dwFormats, ossdev->duplex_out_caps.dwSupport);
1018 }
1019
1020 static char* StrDup(const char* str, const char* def)
1021 {
1022     char* dst;
1023     if (str==NULL)
1024         str=def;
1025     dst=HeapAlloc(GetProcessHeap(),0,strlen(str)+1);
1026     strcpy(dst, str);
1027     return dst;
1028 }
1029
1030 /******************************************************************
1031  *              OSS_WaveInit
1032  *
1033  * Initialize internal structures from OSS information
1034  */
1035 LONG OSS_WaveInit(void)
1036 {
1037     char* str;
1038     int i;
1039
1040     TRACE("()\n");
1041
1042     str=getenv("AUDIODEV");
1043     if (str!=NULL)
1044     {
1045         OSS_Devices[0].dev_name=StrDup(str,"");
1046         OSS_Devices[0].mixer_name=StrDup(getenv("MIXERDEV"),"/dev/mixer");
1047         for (i = 1; i < MAX_WAVEDRV; ++i)
1048         {
1049             OSS_Devices[i].dev_name=StrDup("",NULL);
1050             OSS_Devices[i].mixer_name=StrDup("",NULL);
1051         }
1052     }
1053     else
1054     {
1055         OSS_Devices[0].dev_name=StrDup("/dev/dsp",NULL);
1056         OSS_Devices[0].mixer_name=StrDup("/dev/mixer",NULL);
1057         for (i = 1; i < MAX_WAVEDRV; ++i)
1058         {
1059             OSS_Devices[i].dev_name=HeapAlloc(GetProcessHeap(),0,11);
1060             sprintf(OSS_Devices[i].dev_name, "/dev/dsp%d", i);
1061             OSS_Devices[i].mixer_name=HeapAlloc(GetProcessHeap(),0,13);
1062             sprintf(OSS_Devices[i].mixer_name, "/dev/mixer%d", i);
1063         }
1064     }
1065
1066     for (i = 0; i < MAX_WAVEDRV; ++i)
1067     {
1068         OSS_Devices[i].interface_name=HeapAlloc(GetProcessHeap(),0,9+strlen(OSS_Devices[i].dev_name)+1);
1069         sprintf(OSS_Devices[i].interface_name, "wineoss: %s", OSS_Devices[i].dev_name);
1070     }
1071
1072     /* start with output devices */
1073     for (i = 0; i < MAX_WAVEDRV; ++i)
1074     {
1075         if (*OSS_Devices[i].dev_name=='\0' || OSS_WaveOutInit(&OSS_Devices[i]))
1076         {
1077             WOutDev[numOutDev].state = WINE_WS_CLOSED;
1078             WOutDev[numOutDev].ossdev = &OSS_Devices[i];
1079             WOutDev[numOutDev].volume = 0xffffffff;
1080             numOutDev++;
1081         }
1082     }
1083
1084     /* then do input devices */
1085     for (i = 0; i < MAX_WAVEDRV; ++i)
1086     {
1087         if (*OSS_Devices[i].dev_name=='\0' || OSS_WaveInInit(&OSS_Devices[i]))
1088         {
1089             WInDev[numInDev].state = WINE_WS_CLOSED;
1090             WInDev[numInDev].ossdev = &OSS_Devices[i];
1091             numInDev++;
1092         }
1093     }
1094
1095     /* finish with the full duplex bits */
1096     for (i = 0; i < MAX_WAVEDRV; i++)
1097         if (*OSS_Devices[i].dev_name!='\0')
1098             OSS_WaveFullDuplexInit(&OSS_Devices[i]);
1099
1100     return 0;
1101 }
1102
1103 /******************************************************************
1104  *              OSS_InitRingMessage
1105  *
1106  * Initialize the ring of messages for passing between driver's caller and playback/record
1107  * thread
1108  */
1109 static int OSS_InitRingMessage(OSS_MSG_RING* omr)
1110 {
1111     omr->msg_toget = 0;
1112     omr->msg_tosave = 0;
1113 #ifdef USE_PIPE_SYNC
1114     if (pipe(omr->msg_pipe) < 0) {
1115         omr->msg_pipe[0] = -1;
1116         omr->msg_pipe[1] = -1;
1117         ERR("could not create pipe, error=%s\n", strerror(errno));
1118     }
1119 #else
1120     omr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1121 #endif
1122     omr->ring_buffer_size = OSS_RING_BUFFER_INCREMENT;
1123     omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(OSS_MSG));
1124     InitializeCriticalSection(&omr->msg_crst);
1125     omr->msg_crst.DebugInfo->Spare[1] = (DWORD)"WINEOSS_msg_crst";
1126     return 0;
1127 }
1128
1129 /******************************************************************
1130  *              OSS_DestroyRingMessage
1131  *
1132  */
1133 static int OSS_DestroyRingMessage(OSS_MSG_RING* omr)
1134 {
1135 #ifdef USE_PIPE_SYNC
1136     close(omr->msg_pipe[0]);
1137     close(omr->msg_pipe[1]);
1138 #else
1139     CloseHandle(omr->msg_event);
1140 #endif
1141     HeapFree(GetProcessHeap(),0,omr->messages);
1142     DeleteCriticalSection(&omr->msg_crst);
1143     return 0;
1144 }
1145
1146 /******************************************************************
1147  *              OSS_AddRingMessage
1148  *
1149  * Inserts a new message into the ring (should be called from DriverProc derivated routines)
1150  */
1151 static int OSS_AddRingMessage(OSS_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
1152 {
1153     HANDLE      hEvent = INVALID_HANDLE_VALUE;
1154
1155     EnterCriticalSection(&omr->msg_crst);
1156     if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
1157     {
1158         int old_ring_buffer_size = omr->ring_buffer_size;
1159         omr->ring_buffer_size += OSS_RING_BUFFER_INCREMENT;
1160         TRACE("omr->ring_buffer_size=%d\n",omr->ring_buffer_size);
1161         omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(OSS_MSG));
1162         /* Now we need to rearrange the ring buffer so that the new
1163            buffers just allocated are in between omr->msg_tosave and
1164            omr->msg_toget.
1165         */
1166         if (omr->msg_tosave < omr->msg_toget)
1167         {
1168             memmove(&(omr->messages[omr->msg_toget + OSS_RING_BUFFER_INCREMENT]),
1169                     &(omr->messages[omr->msg_toget]),
1170                     sizeof(OSS_MSG)*(old_ring_buffer_size - omr->msg_toget)
1171                     );
1172             omr->msg_toget += OSS_RING_BUFFER_INCREMENT;
1173         }
1174     }
1175     if (wait)
1176     {
1177         hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1178         if (hEvent == INVALID_HANDLE_VALUE)
1179         {
1180             ERR("can't create event !?\n");
1181             LeaveCriticalSection(&omr->msg_crst);
1182             return 0;
1183         }
1184         if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
1185             FIXME("two fast messages in the queue!!!! toget = %d(%s), tosave=%d(%s)\n",
1186             omr->msg_toget,getCmdString(omr->messages[omr->msg_toget].msg),
1187             omr->msg_tosave,getCmdString(omr->messages[omr->msg_tosave].msg));
1188
1189         /* fast messages have to be added at the start of the queue */
1190         omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
1191         omr->messages[omr->msg_toget].msg = msg;
1192         omr->messages[omr->msg_toget].param = param;
1193         omr->messages[omr->msg_toget].hEvent = hEvent;
1194     }
1195     else
1196     {
1197         omr->messages[omr->msg_tosave].msg = msg;
1198         omr->messages[omr->msg_tosave].param = param;
1199         omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
1200         omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
1201     }
1202     LeaveCriticalSection(&omr->msg_crst);
1203     /* signal a new message */
1204     SIGNAL_OMR(omr);
1205     if (wait)
1206     {
1207         /* wait for playback/record thread to have processed the message */
1208         WaitForSingleObject(hEvent, INFINITE);
1209         CloseHandle(hEvent);
1210     }
1211     return 1;
1212 }
1213
1214 /******************************************************************
1215  *              OSS_RetrieveRingMessage
1216  *
1217  * Get a message from the ring. Should be called by the playback/record thread.
1218  */
1219 static int OSS_RetrieveRingMessage(OSS_MSG_RING* omr,
1220                                    enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
1221 {
1222     EnterCriticalSection(&omr->msg_crst);
1223
1224     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1225     {
1226         LeaveCriticalSection(&omr->msg_crst);
1227         return 0;
1228     }
1229
1230     *msg = omr->messages[omr->msg_toget].msg;
1231     omr->messages[omr->msg_toget].msg = 0;
1232     *param = omr->messages[omr->msg_toget].param;
1233     *hEvent = omr->messages[omr->msg_toget].hEvent;
1234     omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
1235     CLEAR_OMR(omr);
1236     LeaveCriticalSection(&omr->msg_crst);
1237     return 1;
1238 }
1239
1240 /******************************************************************
1241  *              OSS_PeekRingMessage
1242  *
1243  * Peek at a message from the ring but do not remove it.
1244  * Should be called by the playback/record thread.
1245  */
1246 static int OSS_PeekRingMessage(OSS_MSG_RING* omr,
1247                                enum win_wm_message *msg,
1248                                DWORD *param, HANDLE *hEvent)
1249 {
1250     EnterCriticalSection(&omr->msg_crst);
1251
1252     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1253     {
1254         LeaveCriticalSection(&omr->msg_crst);
1255         return 0;
1256     }
1257
1258     *msg = omr->messages[omr->msg_toget].msg;
1259     *param = omr->messages[omr->msg_toget].param;
1260     *hEvent = omr->messages[omr->msg_toget].hEvent;
1261     LeaveCriticalSection(&omr->msg_crst);
1262     return 1;
1263 }
1264
1265 /*======================================================================*
1266  *                  Low level WAVE OUT implementation                   *
1267  *======================================================================*/
1268
1269 /**************************************************************************
1270  *                      wodNotifyClient                 [internal]
1271  */
1272 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1273 {
1274     TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg,
1275         wMsg == WOM_OPEN ? "WOM_OPEN" : wMsg == WOM_CLOSE ? "WOM_CLOSE" :
1276         wMsg == WOM_DONE ? "WOM_DONE" : "Unknown", dwParam1, dwParam2);
1277
1278     switch (wMsg) {
1279     case WOM_OPEN:
1280     case WOM_CLOSE:
1281     case WOM_DONE:
1282         if (wwo->wFlags != DCB_NULL &&
1283             !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
1284                             (HDRVR)wwo->waveDesc.hWave, wMsg,
1285                             wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
1286             WARN("can't notify client !\n");
1287             return MMSYSERR_ERROR;
1288         }
1289         break;
1290     default:
1291         FIXME("Unknown callback message %u\n", wMsg);
1292         return MMSYSERR_INVALPARAM;
1293     }
1294     return MMSYSERR_NOERROR;
1295 }
1296
1297 /**************************************************************************
1298  *                              wodUpdatePlayedTotal    [internal]
1299  *
1300  */
1301 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, audio_buf_info* info)
1302 {
1303     audio_buf_info dspspace;
1304     if (!info) info = &dspspace;
1305
1306     if (ioctl(wwo->ossdev->fd, SNDCTL_DSP_GETOSPACE, info) < 0) {
1307         ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n", wwo->ossdev->dev_name, strerror(errno));
1308         return FALSE;
1309     }
1310     wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwBufferSize - info->bytes);
1311     return TRUE;
1312 }
1313
1314 /**************************************************************************
1315  *                              wodPlayer_BeginWaveHdr          [internal]
1316  *
1317  * Makes the specified lpWaveHdr the currently playing wave header.
1318  * If the specified wave header is a begin loop and we're not already in
1319  * a loop, setup the loop.
1320  */
1321 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1322 {
1323     wwo->lpPlayPtr = lpWaveHdr;
1324
1325     if (!lpWaveHdr) return;
1326
1327     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
1328         if (wwo->lpLoopPtr) {
1329             WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
1330         } else {
1331             TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1332             wwo->lpLoopPtr = lpWaveHdr;
1333             /* Windows does not touch WAVEHDR.dwLoops,
1334              * so we need to make an internal copy */
1335             wwo->dwLoops = lpWaveHdr->dwLoops;
1336         }
1337     }
1338     wwo->dwPartialOffset = 0;
1339 }
1340
1341 /**************************************************************************
1342  *                              wodPlayer_PlayPtrNext           [internal]
1343  *
1344  * Advance the play pointer to the next waveheader, looping if required.
1345  */
1346 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
1347 {
1348     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1349
1350     wwo->dwPartialOffset = 0;
1351     if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
1352         /* We're at the end of a loop, loop if required */
1353         if (--wwo->dwLoops > 0) {
1354             wwo->lpPlayPtr = wwo->lpLoopPtr;
1355         } else {
1356             /* Handle overlapping loops correctly */
1357             if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
1358                 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1359                 /* shall we consider the END flag for the closing loop or for
1360                  * the opening one or for both ???
1361                  * code assumes for closing loop only
1362                  */
1363             } else {
1364                 lpWaveHdr = lpWaveHdr->lpNext;
1365             }
1366             wwo->lpLoopPtr = NULL;
1367             wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
1368         }
1369     } else {
1370         /* We're not in a loop.  Advance to the next wave header */
1371         wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
1372     }
1373
1374     return lpWaveHdr;
1375 }
1376
1377 /**************************************************************************
1378  *                           wodPlayer_DSPWait                  [internal]
1379  * Returns the number of milliseconds to wait for the DSP buffer to write
1380  * one fragment.
1381  */
1382 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
1383 {
1384     /* time for one fragment to be played */
1385     return wwo->dwFragmentSize * 1000 / wwo->waveFormat.Format.nAvgBytesPerSec;
1386 }
1387
1388 /**************************************************************************
1389  *                           wodPlayer_NotifyWait               [internal]
1390  * Returns the number of milliseconds to wait before attempting to notify
1391  * completion of the specified wavehdr.
1392  * This is based on the number of bytes remaining to be written in the
1393  * wave.
1394  */
1395 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1396 {
1397     DWORD dwMillis;
1398
1399     if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
1400         dwMillis = 1;
1401     } else {
1402         dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->waveFormat.Format.nAvgBytesPerSec;
1403         if (!dwMillis) dwMillis = 1;
1404     }
1405
1406     return dwMillis;
1407 }
1408
1409
1410 /**************************************************************************
1411  *                           wodPlayer_WriteMaxFrags            [internal]
1412  * Writes the maximum number of bytes possible to the DSP and returns
1413  * TRUE iff the current playPtr has been fully played
1414  */
1415 static BOOL wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
1416 {
1417     DWORD       dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
1418     DWORD       toWrite = min(dwLength, *bytes);
1419     int         written;
1420     BOOL        ret = FALSE;
1421
1422     TRACE("Writing wavehdr %p.%lu[%lu]/%lu\n",
1423           wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength, toWrite);
1424
1425     if (toWrite > 0)
1426     {
1427         written = write(wwo->ossdev->fd, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite);
1428         if (written <= 0) {
1429             TRACE("write(%s, %p, %ld) failed (%s) returned %d\n", wwo->ossdev->dev_name,
1430                 wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite, strerror(errno), written);
1431             return FALSE;
1432         }
1433     }
1434     else
1435         written = 0;
1436
1437     if (written >= dwLength) {
1438         /* If we wrote all current wavehdr, skip to the next one */
1439         wodPlayer_PlayPtrNext(wwo);
1440         ret = TRUE;
1441     } else {
1442         /* Remove the amount written */
1443         wwo->dwPartialOffset += written;
1444     }
1445     *bytes -= written;
1446     wwo->dwWrittenTotal += written;
1447     TRACE("dwWrittenTotal=%lu\n", wwo->dwWrittenTotal);
1448     return ret;
1449 }
1450
1451
1452 /**************************************************************************
1453  *                              wodPlayer_NotifyCompletions     [internal]
1454  *
1455  * Notifies and remove from queue all wavehdrs which have been played to
1456  * the speaker (ie. they have cleared the OSS buffer).  If force is true,
1457  * we notify all wavehdrs and remove them all from the queue even if they
1458  * are unplayed or part of a loop.
1459  */
1460 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1461 {
1462     LPWAVEHDR           lpWaveHdr;
1463
1464     /* Start from lpQueuePtr and keep notifying until:
1465      * - we hit an unwritten wavehdr
1466      * - we hit the beginning of a running loop
1467      * - we hit a wavehdr which hasn't finished playing
1468      */
1469 #if 0
1470     while ((lpWaveHdr = wwo->lpQueuePtr) && 
1471            (force || 
1472             (lpWaveHdr != wwo->lpPlayPtr &&
1473              lpWaveHdr != wwo->lpLoopPtr &&
1474              lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
1475
1476         wwo->lpQueuePtr = lpWaveHdr->lpNext;
1477
1478         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1479         lpWaveHdr->dwFlags |= WHDR_DONE;
1480
1481         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1482     }
1483 #else
1484     for (;;)
1485     {
1486         lpWaveHdr = wwo->lpQueuePtr;
1487         if (!lpWaveHdr) {TRACE("Empty queue\n"); break;}
1488         if (!force)
1489         {
1490             if (lpWaveHdr == wwo->lpPlayPtr) {TRACE("play %p\n", lpWaveHdr); break;}
1491             if (lpWaveHdr == wwo->lpLoopPtr) {TRACE("loop %p\n", lpWaveHdr); break;}
1492             if (lpWaveHdr->reserved > wwo->dwPlayedTotal){TRACE("still playing %p (%lu/%lu)\n", lpWaveHdr, lpWaveHdr->reserved, wwo->dwPlayedTotal);break;}
1493         }
1494         wwo->lpQueuePtr = lpWaveHdr->lpNext;
1495
1496         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1497         lpWaveHdr->dwFlags |= WHDR_DONE;
1498
1499         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1500     }
1501 #endif
1502     return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ? 
1503         wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
1504 }
1505
1506 /**************************************************************************
1507  *                              wodPlayer_Reset                 [internal]
1508  *
1509  * wodPlayer helper. Resets current output stream.
1510  */
1511 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
1512 {
1513     wodUpdatePlayedTotal(wwo, NULL);
1514     /* updates current notify list */
1515     wodPlayer_NotifyCompletions(wwo, FALSE);
1516
1517     /* flush all possible output */
1518     if (OSS_ResetDevice(wwo->ossdev) != MMSYSERR_NOERROR)
1519     {
1520         wwo->hThread = 0;
1521         wwo->state = WINE_WS_STOPPED;
1522         ExitThread(-1);
1523     }
1524
1525     if (reset) {
1526         enum win_wm_message     msg;
1527         DWORD                   param;
1528         HANDLE                  ev;
1529
1530         /* remove any buffer */
1531         wodPlayer_NotifyCompletions(wwo, TRUE);
1532
1533         wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1534         wwo->state = WINE_WS_STOPPED;
1535         wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1536         /* Clear partial wavehdr */
1537         wwo->dwPartialOffset = 0;
1538
1539         /* remove any existing message in the ring */
1540         EnterCriticalSection(&wwo->msgRing.msg_crst);
1541         /* return all pending headers in queue */
1542         while (OSS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
1543         {
1544             if (msg != WINE_WM_HEADER)
1545             {
1546                 FIXME("shouldn't have headers left\n");
1547                 SetEvent(ev);
1548                 continue;
1549             }
1550             ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
1551             ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
1552
1553             wodNotifyClient(wwo, WOM_DONE, param, 0);
1554         }
1555         RESET_OMR(&wwo->msgRing);
1556         LeaveCriticalSection(&wwo->msgRing.msg_crst);
1557     } else {
1558         if (wwo->lpLoopPtr) {
1559             /* complicated case, not handled yet (could imply modifying the loop counter */
1560             FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
1561             wwo->lpPlayPtr = wwo->lpLoopPtr;
1562             wwo->dwPartialOffset = 0;
1563             wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
1564         } else {
1565             LPWAVEHDR   ptr;
1566             DWORD       sz = wwo->dwPartialOffset;
1567
1568             /* reset all the data as if we had written only up to lpPlayedTotal bytes */
1569             /* compute the max size playable from lpQueuePtr */
1570             for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) {
1571                 sz += ptr->dwBufferLength;
1572             }
1573             /* because the reset lpPlayPtr will be lpQueuePtr */
1574             if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("grin\n");
1575             wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
1576             wwo->dwWrittenTotal = wwo->dwPlayedTotal;
1577             wwo->lpPlayPtr = wwo->lpQueuePtr;
1578         }
1579         wwo->state = WINE_WS_PAUSED;
1580     }
1581 }
1582
1583 /**************************************************************************
1584  *                    wodPlayer_ProcessMessages                 [internal]
1585  */
1586 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
1587 {
1588     LPWAVEHDR           lpWaveHdr;
1589     enum win_wm_message msg;
1590     DWORD               param;
1591     HANDLE              ev;
1592
1593     while (OSS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
1594         TRACE("Received %s %lx\n", getCmdString(msg), param);
1595         switch (msg) {
1596         case WINE_WM_PAUSING:
1597             wodPlayer_Reset(wwo, FALSE);
1598             SetEvent(ev);
1599             break;
1600         case WINE_WM_RESTARTING:
1601             if (wwo->state == WINE_WS_PAUSED)
1602             {
1603                 wwo->state = WINE_WS_PLAYING;
1604             }
1605             SetEvent(ev);
1606             break;
1607         case WINE_WM_HEADER:
1608             lpWaveHdr = (LPWAVEHDR)param;
1609
1610             /* insert buffer at the end of queue */
1611             {
1612                 LPWAVEHDR*      wh;
1613                 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1614                 *wh = lpWaveHdr;
1615             }
1616             if (!wwo->lpPlayPtr)
1617                 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
1618             if (wwo->state == WINE_WS_STOPPED)
1619                 wwo->state = WINE_WS_PLAYING;
1620             break;
1621         case WINE_WM_RESETTING:
1622             wodPlayer_Reset(wwo, TRUE);
1623             SetEvent(ev);
1624             break;
1625         case WINE_WM_UPDATE:
1626             wodUpdatePlayedTotal(wwo, NULL);
1627             SetEvent(ev);
1628             break;
1629         case WINE_WM_BREAKLOOP:
1630             if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
1631                 /* ensure exit at end of current loop */
1632                 wwo->dwLoops = 1;
1633             }
1634             SetEvent(ev);
1635             break;
1636         case WINE_WM_CLOSING:
1637             /* sanity check: this should not happen since the device must have been reset before */
1638             if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1639             wwo->hThread = 0;
1640             wwo->state = WINE_WS_CLOSED;
1641             SetEvent(ev);
1642             ExitThread(0);
1643             /* shouldn't go here */
1644         default:
1645             FIXME("unknown message %d\n", msg);
1646             break;
1647         }
1648     }
1649 }
1650
1651 /**************************************************************************
1652  *                           wodPlayer_FeedDSP                  [internal]
1653  * Feed as much sound data as we can into the DSP and return the number of
1654  * milliseconds before it will be necessary to feed the DSP again.
1655  */
1656 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1657 {
1658     audio_buf_info dspspace;
1659     DWORD       availInQ;
1660
1661     if (!wodUpdatePlayedTotal(wwo, &dspspace)) return INFINITE;
1662     availInQ = dspspace.bytes;
1663     TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
1664           dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);
1665
1666     /* input queue empty and output buffer with less than one fragment to play
1667      * actually some cards do not play the fragment before the last if this one is partially feed
1668      * so we need to test for full the availability of 2 fragments
1669      */
1670     if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize &&
1671         !wwo->bNeedPost) {
1672         TRACE("Run out of wavehdr:s...\n");
1673         return INFINITE;
1674     }
1675
1676     /* no more room... no need to try to feed */
1677     if (dspspace.fragments != 0) {
1678         /* Feed from partial wavehdr */
1679         if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
1680             wodPlayer_WriteMaxFrags(wwo, &availInQ);
1681         }
1682
1683         /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1684         if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
1685             do {
1686                 TRACE("Setting time to elapse for %p to %lu\n",
1687                       wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
1688                 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1689                 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1690             } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
1691         }
1692
1693         if (wwo->bNeedPost) {
1694             /* OSS doesn't start before it gets either 2 fragments or a SNDCTL_DSP_POST;
1695              * if it didn't get one, we give it the other */
1696             if (wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize)
1697                 ioctl(wwo->ossdev->fd, SNDCTL_DSP_POST, 0);
1698             wwo->bNeedPost = FALSE;
1699         }
1700     }
1701
1702     return wodPlayer_DSPWait(wwo);
1703 }
1704
1705
1706 /**************************************************************************
1707  *                              wodPlayer                       [internal]
1708  */
1709 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
1710 {
1711     WORD          uDevID = (DWORD)pmt;
1712     WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1713     DWORD         dwNextFeedTime = INFINITE;   /* Time before DSP needs feeding */
1714     DWORD         dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1715     DWORD         dwSleepTime;
1716
1717     wwo->state = WINE_WS_STOPPED;
1718     SetEvent(wwo->hStartUpEvent);
1719
1720     for (;;) {
1721         /** Wait for the shortest time before an action is required.  If there
1722          *  are no pending actions, wait forever for a command.
1723          */
1724         dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1725         TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1726         WAIT_OMR(&wwo->msgRing, dwSleepTime);
1727         wodPlayer_ProcessMessages(wwo);
1728         if (wwo->state == WINE_WS_PLAYING) {
1729             dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1730             dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1731             if (dwNextFeedTime == INFINITE) {
1732                 /* FeedDSP ran out of data, but before flushing, */
1733                 /* check that a notification didn't give us more */
1734                 wodPlayer_ProcessMessages(wwo);
1735                 if (!wwo->lpPlayPtr) {
1736                     TRACE("flushing\n");
1737                     ioctl(wwo->ossdev->fd, SNDCTL_DSP_SYNC, 0);
1738                     wwo->dwPlayedTotal = wwo->dwWrittenTotal;
1739                     dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1740                 } else {
1741                     TRACE("recovering\n");
1742                     dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1743                 }
1744             }
1745         } else {
1746             dwNextFeedTime = dwNextNotifyTime = INFINITE;
1747         }
1748     }
1749 }
1750
1751 /**************************************************************************
1752  *                      wodGetDevCaps                           [internal]
1753  */
1754 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
1755 {
1756     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1757
1758     if (lpCaps == NULL) {
1759         WARN("not enabled\n");
1760         return MMSYSERR_NOTENABLED;
1761     }
1762
1763     if (wDevID >= numOutDev) {
1764         WARN("numOutDev reached !\n");
1765         return MMSYSERR_BADDEVICEID;
1766     }
1767
1768     if (WOutDev[wDevID].ossdev->open_access == O_RDWR)
1769         memcpy(lpCaps, &WOutDev[wDevID].ossdev->duplex_out_caps, min(dwSize, sizeof(*lpCaps)));
1770     else
1771         memcpy(lpCaps, &WOutDev[wDevID].ossdev->out_caps, min(dwSize, sizeof(*lpCaps)));
1772
1773     return MMSYSERR_NOERROR;
1774 }
1775
1776 /**************************************************************************
1777  *                              wodOpen                         [internal]
1778  */
1779 DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1780 {
1781     int                 audio_fragment;
1782     WINE_WAVEOUT*       wwo;
1783     audio_buf_info      info;
1784     DWORD               ret;
1785
1786     TRACE("(%u, %p[cb=%08lx], %08lX);\n", wDevID, lpDesc, lpDesc->dwCallback, dwFlags);
1787     if (lpDesc == NULL) {
1788         WARN("Invalid Parameter !\n");
1789         return MMSYSERR_INVALPARAM;
1790     }
1791     if (wDevID >= numOutDev) {
1792         TRACE("MAX_WAVOUTDRV reached !\n");
1793         return MMSYSERR_BADDEVICEID;
1794     }
1795
1796     /* only PCM format is supported so far... */
1797     if (!supportedFormat(lpDesc->lpFormat)) {
1798         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1799              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1800              lpDesc->lpFormat->nSamplesPerSec);
1801         return WAVERR_BADFORMAT;
1802     }
1803
1804     if (dwFlags & WAVE_FORMAT_QUERY) {
1805         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1806              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1807              lpDesc->lpFormat->nSamplesPerSec);
1808         return MMSYSERR_NOERROR;
1809     }
1810
1811     TRACE("OSS_OpenDevice requested this format: %ldx%dx%d %s\n",
1812           lpDesc->lpFormat->nSamplesPerSec,
1813           lpDesc->lpFormat->wBitsPerSample,
1814           lpDesc->lpFormat->nChannels,
1815           lpDesc->lpFormat->wFormatTag == WAVE_FORMAT_PCM ? "WAVE_FORMAT_PCM" :
1816           lpDesc->lpFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? "WAVE_FORMAT_EXTENSIBLE" :
1817           "UNSUPPORTED");
1818
1819     wwo = &WOutDev[wDevID];
1820
1821     if ((dwFlags & WAVE_DIRECTSOUND) &&
1822         !(wwo->ossdev->duplex_out_caps.dwSupport & WAVECAPS_DIRECTSOUND))
1823         /* not supported, ignore it */
1824         dwFlags &= ~WAVE_DIRECTSOUND;
1825
1826     if (dwFlags & WAVE_DIRECTSOUND) {
1827         if (wwo->ossdev->duplex_out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
1828             /* we have realtime DirectSound, fragments just waste our time,
1829              * but a large buffer is good, so choose 64KB (32 * 2^11) */
1830             audio_fragment = 0x0020000B;
1831         else
1832             /* to approximate realtime, we must use small fragments,
1833              * let's try to fragment the above 64KB (256 * 2^8) */
1834             audio_fragment = 0x01000008;
1835     } else {
1836         /* A wave device must have a worst case latency of 10 ms so calculate
1837          * the largest fragment size less than 10 ms long.
1838          */
1839         int     fsize = lpDesc->lpFormat->nAvgBytesPerSec / 100;        /* 10 ms chunk */
1840         int     shift = 0;
1841         while ((1 << shift) <= fsize)
1842             shift++;
1843         shift--;
1844         audio_fragment = 0x00100000 + shift;    /* 16 fragments of 2^shift */
1845     }
1846
1847     TRACE("requesting %d %d byte fragments (%ld ms/fragment)\n",
1848         audio_fragment >> 16, 1 << (audio_fragment & 0xffff),
1849         ((1 << (audio_fragment & 0xffff)) * 1000) / lpDesc->lpFormat->nAvgBytesPerSec);
1850
1851     if (wwo->state != WINE_WS_CLOSED) {
1852         WARN("already allocated\n");
1853         return MMSYSERR_ALLOCATED;
1854     }
1855
1856     /* we want to be able to mmap() the device, which means it must be opened readable,
1857      * otherwise mmap() will fail (at least under Linux) */
1858     ret = OSS_OpenDevice(wwo->ossdev,
1859                          (dwFlags & WAVE_DIRECTSOUND) ? O_RDWR : O_WRONLY,
1860                          &audio_fragment,
1861                          (dwFlags & WAVE_DIRECTSOUND) ? 0 : 1,
1862                          lpDesc->lpFormat->nSamplesPerSec,
1863                          (lpDesc->lpFormat->nChannels > 1) ? 1 : 0,
1864                          (lpDesc->lpFormat->wBitsPerSample == 16)
1865                              ? AFMT_S16_LE : AFMT_U8);
1866     if ((ret==MMSYSERR_NOERROR) && (dwFlags & WAVE_DIRECTSOUND)) {
1867         lpDesc->lpFormat->nSamplesPerSec=wwo->ossdev->sample_rate;
1868         lpDesc->lpFormat->nChannels=(wwo->ossdev->stereo ? 2 : 1);
1869         lpDesc->lpFormat->wBitsPerSample=(wwo->ossdev->format == AFMT_U8 ? 8 : 16);
1870         lpDesc->lpFormat->nBlockAlign=lpDesc->lpFormat->nChannels*lpDesc->lpFormat->wBitsPerSample/8;
1871         lpDesc->lpFormat->nAvgBytesPerSec=lpDesc->lpFormat->nSamplesPerSec*lpDesc->lpFormat->nBlockAlign;
1872         TRACE("OSS_OpenDevice returned this format: %ldx%dx%d\n",
1873               lpDesc->lpFormat->nSamplesPerSec,
1874               lpDesc->lpFormat->wBitsPerSample,
1875               lpDesc->lpFormat->nChannels);
1876     }
1877     if (ret != 0) return ret;
1878     wwo->state = WINE_WS_STOPPED;
1879
1880     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1881
1882     memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1883     copy_format(lpDesc->lpFormat, &wwo->waveFormat);
1884
1885     if (wwo->waveFormat.Format.wBitsPerSample == 0) {
1886         WARN("Resetting zeroed wBitsPerSample\n");
1887         wwo->waveFormat.Format.wBitsPerSample = 8 *
1888             (wwo->waveFormat.Format.nAvgBytesPerSec /
1889              wwo->waveFormat.Format.nSamplesPerSec) /
1890             wwo->waveFormat.Format.nChannels;
1891     }
1892     /* Read output space info for future reference */
1893     if (ioctl(wwo->ossdev->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
1894         ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n", wwo->ossdev->dev_name, strerror(errno));
1895         OSS_CloseDevice(wwo->ossdev);
1896         wwo->state = WINE_WS_CLOSED;
1897         return MMSYSERR_NOTENABLED;
1898     }
1899
1900     TRACE("got %d %d byte fragments (%d ms/fragment)\n", info.fragstotal,
1901         info.fragsize, (info.fragsize * 1000) / (wwo->ossdev->sample_rate *
1902         (wwo->ossdev->stereo ? 2 : 1) *
1903         (wwo->ossdev->format == AFMT_U8 ? 1 : 2)));
1904
1905     /* Check that fragsize is correct per our settings above */
1906     if ((info.fragsize > 1024) && (LOWORD(audio_fragment) <= 10)) {
1907         /* we've tried to set 1K fragments or less, but it didn't work */
1908         ERR("fragment size set failed, size is now %d\n", info.fragsize);
1909         MESSAGE("Your Open Sound System driver did not let us configure small enough sound fragments.\n");
1910         MESSAGE("This may cause delays and other problems in audio playback with certain applications.\n");
1911     }
1912
1913     /* Remember fragsize and total buffer size for future use */
1914     wwo->dwFragmentSize = info.fragsize;
1915     wwo->dwBufferSize = info.fragstotal * info.fragsize;
1916     wwo->dwPlayedTotal = 0;
1917     wwo->dwWrittenTotal = 0;
1918     wwo->bNeedPost = TRUE;
1919
1920     TRACE("fd=%d fragstotal=%d fragsize=%d BufferSize=%ld\n",
1921           wwo->ossdev->fd, info.fragstotal, info.fragsize, wwo->dwBufferSize);
1922     if (wwo->dwFragmentSize % wwo->waveFormat.Format.nBlockAlign) {
1923         ERR("Fragment doesn't contain an integral number of data blocks fragsize=%ld BlockAlign=%d\n",wwo->dwFragmentSize,wwo->waveFormat.Format.nBlockAlign);
1924         /* Some SoundBlaster 16 cards return an incorrect (odd) fragment
1925          * size for 16 bit sound. This will cause a system crash when we try
1926          * to write just the specified odd number of bytes. So if we
1927          * detect something is wrong we'd better fix it.
1928          */
1929         wwo->dwFragmentSize-=wwo->dwFragmentSize % wwo->waveFormat.Format.nBlockAlign;
1930     }
1931
1932     OSS_InitRingMessage(&wwo->msgRing);
1933
1934     wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1935     wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1936     WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1937     CloseHandle(wwo->hStartUpEvent);
1938     wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1939
1940     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1941           wwo->waveFormat.Format.wBitsPerSample, wwo->waveFormat.Format.nAvgBytesPerSec,
1942           wwo->waveFormat.Format.nSamplesPerSec, wwo->waveFormat.Format.nChannels,
1943           wwo->waveFormat.Format.nBlockAlign);
1944
1945     return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1946 }
1947
1948 /**************************************************************************
1949  *                              wodClose                        [internal]
1950  */
1951 static DWORD wodClose(WORD wDevID)
1952 {
1953     DWORD               ret = MMSYSERR_NOERROR;
1954     WINE_WAVEOUT*       wwo;
1955
1956     TRACE("(%u);\n", wDevID);
1957
1958     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1959         WARN("bad device ID !\n");
1960         return MMSYSERR_BADDEVICEID;
1961     }
1962
1963     wwo = &WOutDev[wDevID];
1964     if (wwo->lpQueuePtr) {
1965         WARN("buffers still playing !\n");
1966         ret = WAVERR_STILLPLAYING;
1967     } else {
1968         if (wwo->hThread != INVALID_HANDLE_VALUE) {
1969             OSS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1970         }
1971
1972         OSS_DestroyRingMessage(&wwo->msgRing);
1973
1974         OSS_CloseDevice(wwo->ossdev);
1975         wwo->state = WINE_WS_CLOSED;
1976         wwo->dwFragmentSize = 0;
1977         ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1978     }
1979     return ret;
1980 }
1981
1982 /**************************************************************************
1983  *                              wodWrite                        [internal]
1984  *
1985  */
1986 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1987 {
1988     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1989
1990     /* first, do the sanity checks... */
1991     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
1992         WARN("bad dev ID !\n");
1993         return MMSYSERR_BADDEVICEID;
1994     }
1995
1996     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1997         return WAVERR_UNPREPARED;
1998
1999     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2000         return WAVERR_STILLPLAYING;
2001
2002     lpWaveHdr->dwFlags &= ~WHDR_DONE;
2003     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
2004     lpWaveHdr->lpNext = 0;
2005
2006     if ((lpWaveHdr->dwBufferLength & (WOutDev[wDevID].waveFormat.Format.nBlockAlign - 1)) != 0)
2007     {
2008         WARN("WaveHdr length isn't a multiple of the PCM block size: %ld %% %d\n",lpWaveHdr->dwBufferLength,WOutDev[wDevID].waveFormat.Format.nBlockAlign);
2009         lpWaveHdr->dwBufferLength &= ~(WOutDev[wDevID].waveFormat.Format.nBlockAlign - 1);
2010     }
2011
2012     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
2013
2014     return MMSYSERR_NOERROR;
2015 }
2016
2017 /**************************************************************************
2018  *                              wodPrepare                      [internal]
2019  */
2020 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2021 {
2022     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2023
2024     if (wDevID >= numOutDev) {
2025         WARN("bad device ID !\n");
2026         return MMSYSERR_BADDEVICEID;
2027     }
2028
2029     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2030         return WAVERR_STILLPLAYING;
2031
2032     lpWaveHdr->dwFlags |= WHDR_PREPARED;
2033     lpWaveHdr->dwFlags &= ~WHDR_DONE;
2034     return MMSYSERR_NOERROR;
2035 }
2036
2037 /**************************************************************************
2038  *                              wodUnprepare                    [internal]
2039  */
2040 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2041 {
2042     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2043
2044     if (wDevID >= numOutDev) {
2045         WARN("bad device ID !\n");
2046         return MMSYSERR_BADDEVICEID;
2047     }
2048
2049     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2050         return WAVERR_STILLPLAYING;
2051
2052     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
2053     lpWaveHdr->dwFlags |= WHDR_DONE;
2054
2055     return MMSYSERR_NOERROR;
2056 }
2057
2058 /**************************************************************************
2059  *                      wodPause                                [internal]
2060  */
2061 static DWORD wodPause(WORD wDevID)
2062 {
2063     TRACE("(%u);!\n", wDevID);
2064
2065     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
2066         WARN("bad device ID !\n");
2067         return MMSYSERR_BADDEVICEID;
2068     }
2069
2070     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
2071
2072     return MMSYSERR_NOERROR;
2073 }
2074
2075 /**************************************************************************
2076  *                      wodRestart                              [internal]
2077  */
2078 static DWORD wodRestart(WORD wDevID)
2079 {
2080     TRACE("(%u);\n", wDevID);
2081
2082     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
2083         WARN("bad device ID !\n");
2084         return MMSYSERR_BADDEVICEID;
2085     }
2086
2087     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
2088
2089     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
2090     /* FIXME: Myst crashes with this ... hmm -MM
2091        return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
2092     */
2093
2094     return MMSYSERR_NOERROR;
2095 }
2096
2097 /**************************************************************************
2098  *                      wodReset                                [internal]
2099  */
2100 static DWORD wodReset(WORD wDevID)
2101 {
2102     TRACE("(%u);\n", wDevID);
2103
2104     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
2105         WARN("bad device ID !\n");
2106         return MMSYSERR_BADDEVICEID;
2107     }
2108
2109     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2110
2111     return MMSYSERR_NOERROR;
2112 }
2113
2114 /**************************************************************************
2115  *                              wodGetPosition                  [internal]
2116  */
2117 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
2118 {
2119     WINE_WAVEOUT*       wwo;
2120
2121     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
2122
2123     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
2124         WARN("bad device ID !\n");
2125         return MMSYSERR_BADDEVICEID;
2126     }
2127
2128     if (lpTime == NULL) {
2129         WARN("invalid parameter: lpTime == NULL\n");
2130         return MMSYSERR_INVALPARAM;
2131     }
2132
2133     wwo = &WOutDev[wDevID];
2134 #ifdef EXACT_WODPOSITION
2135     if (wwo->ossdev->open_access == O_RDWR) {
2136         if (wwo->ossdev->duplex_out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
2137             OSS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
2138     } else {
2139         if (wwo->ossdev->out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
2140             OSS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
2141     }
2142 #endif
2143
2144     return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->waveFormat);
2145 }
2146
2147 /**************************************************************************
2148  *                              wodBreakLoop                    [internal]
2149  */
2150 static DWORD wodBreakLoop(WORD wDevID)
2151 {
2152     TRACE("(%u);\n", wDevID);
2153
2154     if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
2155         WARN("bad device ID !\n");
2156         return MMSYSERR_BADDEVICEID;
2157     }
2158     OSS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
2159     return MMSYSERR_NOERROR;
2160 }
2161
2162 /**************************************************************************
2163  *                              wodGetVolume                    [internal]
2164  */
2165 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
2166 {
2167     int         mixer;
2168     int         volume;
2169     DWORD       left, right;
2170     DWORD       last_left, last_right;
2171
2172     TRACE("(%u, %p);\n", wDevID, lpdwVol);
2173
2174     if (lpdwVol == NULL) {
2175         WARN("not enabled\n");
2176         return MMSYSERR_NOTENABLED;
2177     }
2178     if (wDevID >= numOutDev) {
2179         WARN("invalid parameter\n");
2180         return MMSYSERR_INVALPARAM;
2181     }
2182
2183     if ((mixer = open(WOutDev[wDevID].ossdev->mixer_name, O_RDONLY|O_NDELAY)) < 0) {
2184         WARN("mixer device not available !\n");
2185         return MMSYSERR_NOTENABLED;
2186     }
2187     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
2188         WARN("ioctl(%s, SOUND_MIXER_READ_PCM) failed (%s)\n",
2189              WOutDev[wDevID].ossdev->mixer_name, strerror(errno));
2190         return MMSYSERR_NOTENABLED;
2191     }
2192     close(mixer);
2193
2194     left = LOBYTE(volume);
2195     right = HIBYTE(volume);
2196     TRACE("left=%ld right=%ld !\n", left, right);
2197     last_left  = (LOWORD(WOutDev[wDevID].volume) * 100) / 0xFFFFl;
2198     last_right = (HIWORD(WOutDev[wDevID].volume) * 100) / 0xFFFFl;
2199     TRACE("last_left=%ld last_right=%ld !\n", last_left, last_right);
2200     if (last_left == left && last_right == right)
2201         *lpdwVol = WOutDev[wDevID].volume;
2202     else
2203         *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
2204     return MMSYSERR_NOERROR;
2205 }
2206
2207 /**************************************************************************
2208  *                              wodSetVolume                    [internal]
2209  */
2210 DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
2211 {
2212     int         mixer;
2213     int         volume;
2214     DWORD       left, right;
2215
2216     TRACE("(%u, %08lX);\n", wDevID, dwParam);
2217
2218     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
2219     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
2220     volume = left + (right << 8);
2221
2222     if (wDevID >= numOutDev) {
2223         WARN("invalid parameter: wDevID > %d\n", numOutDev);
2224         return MMSYSERR_INVALPARAM;
2225     }
2226     if ((mixer = open(WOutDev[wDevID].ossdev->mixer_name, O_WRONLY|O_NDELAY)) < 0) {
2227         WARN("mixer device not available !\n");
2228         return MMSYSERR_NOTENABLED;
2229     }
2230     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
2231         WARN("ioctl(%s, SOUND_MIXER_WRITE_PCM) failed (%s)\n",
2232             WOutDev[wDevID].ossdev->mixer_name, strerror(errno));
2233         return MMSYSERR_NOTENABLED;
2234     } else {
2235         TRACE("volume=%04x\n", (unsigned)volume);
2236     }
2237     close(mixer);
2238
2239     /* save requested volume */
2240     WOutDev[wDevID].volume = dwParam;
2241
2242     return MMSYSERR_NOERROR;
2243 }
2244
2245 /**************************************************************************
2246  *                              wodMessage (WINEOSS.7)
2247  */
2248 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2249                             DWORD dwParam1, DWORD dwParam2)
2250 {
2251     TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
2252           wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
2253
2254     switch (wMsg) {
2255     case DRVM_INIT:
2256     case DRVM_EXIT:
2257     case DRVM_ENABLE:
2258     case DRVM_DISABLE:
2259         /* FIXME: Pretend this is supported */
2260         return 0;
2261     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
2262     case WODM_CLOSE:            return wodClose         (wDevID);
2263     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2264     case WODM_PAUSE:            return wodPause         (wDevID);
2265     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
2266     case WODM_BREAKLOOP:        return wodBreakLoop     (wDevID);
2267     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2268     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2269     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSW)dwParam1,      dwParam2);
2270     case WODM_GETNUMDEVS:       return numOutDev;
2271     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
2272     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
2273     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
2274     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
2275     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
2276     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
2277     case WODM_RESTART:          return wodRestart       (wDevID);
2278     case WODM_RESET:            return wodReset         (wDevID);
2279
2280     case DRV_QUERYDEVICEINTERFACESIZE: return wdDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
2281     case DRV_QUERYDEVICEINTERFACE:     return wdDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
2282     case DRV_QUERYDSOUNDIFACE:  return wodDsCreate      (wDevID, (PIDSDRIVER*)dwParam1);
2283     case DRV_QUERYDSOUNDDESC:   return wodDsDesc        (wDevID, (PDSDRIVERDESC)dwParam1);
2284     default:
2285         FIXME("unknown message %d!\n", wMsg);
2286     }
2287     return MMSYSERR_NOTSUPPORTED;
2288 }
2289
2290 /*======================================================================*
2291  *                  Low level WAVE IN implementation                    *
2292  *======================================================================*/
2293
2294 /**************************************************************************
2295  *                      widNotifyClient                 [internal]
2296  */
2297 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
2298 {
2299     TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg,
2300         wMsg == WIM_OPEN ? "WIM_OPEN" : wMsg == WIM_CLOSE ? "WIM_CLOSE" :
2301         wMsg == WIM_DATA ? "WIM_DATA" : "Unknown", dwParam1, dwParam2);
2302
2303     switch (wMsg) {
2304     case WIM_OPEN:
2305     case WIM_CLOSE:
2306     case WIM_DATA:
2307         if (wwi->wFlags != DCB_NULL &&
2308             !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
2309                             (HDRVR)wwi->waveDesc.hWave, wMsg,
2310                             wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
2311             WARN("can't notify client !\n");
2312             return MMSYSERR_ERROR;
2313         }
2314         break;
2315     default:
2316         FIXME("Unknown callback message %u\n", wMsg);
2317         return MMSYSERR_INVALPARAM;
2318     }
2319     return MMSYSERR_NOERROR;
2320 }
2321
2322 /**************************************************************************
2323  *                      widGetDevCaps                           [internal]
2324  */
2325 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
2326 {
2327     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
2328
2329     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
2330
2331     if (wDevID >= numInDev) {
2332         TRACE("numOutDev reached !\n");
2333         return MMSYSERR_BADDEVICEID;
2334     }
2335
2336     memcpy(lpCaps, &WInDev[wDevID].ossdev->in_caps, min(dwSize, sizeof(*lpCaps)));
2337     return MMSYSERR_NOERROR;
2338 }
2339
2340 /**************************************************************************
2341  *                              widRecorder_ReadHeaders         [internal]
2342  */
2343 static void widRecorder_ReadHeaders(WINE_WAVEIN * wwi)
2344 {
2345     enum win_wm_message tmp_msg;
2346     DWORD               tmp_param;
2347     HANDLE              tmp_ev;
2348     WAVEHDR*            lpWaveHdr;
2349
2350     while (OSS_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
2351         if (tmp_msg == WINE_WM_HEADER) {
2352             LPWAVEHDR*  wh;
2353             lpWaveHdr = (LPWAVEHDR)tmp_param;
2354             lpWaveHdr->lpNext = 0;
2355
2356             if (wwi->lpQueuePtr == 0)
2357                 wwi->lpQueuePtr = lpWaveHdr;
2358             else {
2359                 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2360                 *wh = lpWaveHdr;
2361             }
2362         } else {
2363             ERR("should only have headers left\n");
2364         }
2365     }
2366 }
2367
2368 /**************************************************************************
2369  *                              widRecorder                     [internal]
2370  */
2371 static  DWORD   CALLBACK        widRecorder(LPVOID pmt)
2372 {
2373     WORD                uDevID = (DWORD)pmt;
2374     WINE_WAVEIN*        wwi = (WINE_WAVEIN*)&WInDev[uDevID];
2375     WAVEHDR*            lpWaveHdr;
2376     DWORD               dwSleepTime;
2377     DWORD               bytesRead;
2378     LPVOID              buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwFragmentSize);
2379     char               *pOffset = buffer;
2380     audio_buf_info      info;
2381     int                 xs;
2382     enum win_wm_message msg;
2383     DWORD               param;
2384     HANDLE              ev;
2385     int                 enable;
2386
2387     wwi->state = WINE_WS_STOPPED;
2388     wwi->dwTotalRecorded = 0;
2389     wwi->dwTotalRead = 0;
2390     wwi->lpQueuePtr = NULL;
2391
2392     SetEvent(wwi->hStartUpEvent);
2393
2394     /* disable input so capture will begin when triggered */
2395     wwi->ossdev->bInputEnabled = FALSE;
2396     enable = getEnables(wwi->ossdev);
2397     if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0)
2398         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
2399
2400     /* the soundblaster live needs a micro wake to get its recording started
2401      * (or GETISPACE will have 0 frags all the time)
2402      */
2403     read(wwi->ossdev->fd, &xs, 4);
2404
2405     /* make sleep time to be # of ms to output a fragment */
2406     dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->waveFormat.Format.nAvgBytesPerSec;
2407     TRACE("sleeptime=%ld ms\n", dwSleepTime);
2408
2409     for (;;) {
2410         /* wait for dwSleepTime or an event in thread's queue */
2411         /* FIXME: could improve wait time depending on queue state,
2412          * ie, number of queued fragments
2413          */
2414
2415         if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
2416         {
2417             lpWaveHdr = wwi->lpQueuePtr;
2418
2419             ioctl(wwi->ossdev->fd, SNDCTL_DSP_GETISPACE, &info);
2420             TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", info.fragments, info.fragsize, info.fragstotal, info.bytes);
2421
2422             /* read all the fragments accumulated so far */
2423             while ((info.fragments > 0) && (wwi->lpQueuePtr))
2424             {
2425                 info.fragments --;
2426
2427                 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwFragmentSize)
2428                 {
2429                     /* directly read fragment in wavehdr */
2430                     bytesRead = read(wwi->ossdev->fd,
2431                                      lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
2432                                      wwi->dwFragmentSize);
2433
2434                     TRACE("bytesRead=%ld (direct)\n", bytesRead);
2435                     if (bytesRead != (DWORD) -1)
2436                     {
2437                         /* update number of bytes recorded in current buffer and by this device */
2438                         lpWaveHdr->dwBytesRecorded += bytesRead;
2439                         wwi->dwTotalRead           += bytesRead;
2440                         wwi->dwTotalRecorded = wwi->dwTotalRead;
2441
2442                         /* buffer is full. notify client */
2443                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2444                         {
2445                             /* must copy the value of next waveHdr, because we have no idea of what
2446                              * will be done with the content of lpWaveHdr in callback
2447                              */
2448                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
2449
2450                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2451                             lpWaveHdr->dwFlags |=  WHDR_DONE;
2452
2453                             wwi->lpQueuePtr = lpNext;
2454                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2455                             lpWaveHdr = lpNext;
2456                         }
2457                     } else {
2458                         TRACE("read(%s, %p, %ld) failed (%s)\n", wwi->ossdev->dev_name,
2459                             lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
2460                             wwi->dwFragmentSize, strerror(errno));
2461                     }
2462                 }
2463                 else
2464                 {
2465                     /* read the fragment in a local buffer */
2466                     bytesRead = read(wwi->ossdev->fd, buffer, wwi->dwFragmentSize);
2467                     pOffset = buffer;
2468
2469                     TRACE("bytesRead=%ld (local)\n", bytesRead);
2470
2471                     if (bytesRead == (DWORD) -1) {
2472                         TRACE("read(%s, %p, %ld) failed (%s)\n", wwi->ossdev->dev_name,
2473                             buffer, wwi->dwFragmentSize, strerror(errno));
2474                         continue;
2475                     }
2476
2477                     /* copy data in client buffers */
2478                     while (bytesRead != (DWORD) -1 && bytesRead > 0)
2479                     {
2480                         DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
2481
2482                         memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
2483                                pOffset,
2484                                dwToCopy);
2485
2486                         /* update number of bytes recorded in current buffer and by this device */
2487                         lpWaveHdr->dwBytesRecorded += dwToCopy;
2488                         wwi->dwTotalRead           += dwToCopy;
2489                         wwi->dwTotalRecorded = wwi->dwTotalRead;
2490                         bytesRead -= dwToCopy;
2491                         pOffset   += dwToCopy;
2492
2493                         /* client buffer is full. notify client */
2494                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2495                         {
2496                             /* must copy the value of next waveHdr, because we have no idea of what
2497                              * will be done with the content of lpWaveHdr in callback
2498                              */
2499                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
2500                             TRACE("lpNext=%p\n", lpNext);
2501
2502                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2503                             lpWaveHdr->dwFlags |=  WHDR_DONE;
2504
2505                             wwi->lpQueuePtr = lpNext;
2506                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2507
2508                             lpWaveHdr = lpNext;
2509                             if (!lpNext && bytesRead) {
2510                                 /* before we give up, check for more header messages */
2511                                 while (OSS_PeekRingMessage(&wwi->msgRing, &msg, &param, &ev))
2512                                 {
2513                                     if (msg == WINE_WM_HEADER) {
2514                                         LPWAVEHDR hdr;
2515                                         OSS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev);
2516                                         hdr = ((LPWAVEHDR)param);
2517                                         TRACE("msg = %s, hdr = %p, ev = %p\n", getCmdString(msg), hdr, ev);
2518                                         hdr->lpNext = 0;
2519                                         if (lpWaveHdr == 0) {
2520                                             /* new head of queue */
2521                                             wwi->lpQueuePtr = lpWaveHdr = hdr;
2522                                         } else {
2523                                             /* insert buffer at the end of queue */
2524                                             LPWAVEHDR*  wh;
2525                                             for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2526                                             *wh = hdr;
2527                                         }
2528                                     } else
2529                                         break;
2530                                 }
2531
2532                                 if (lpWaveHdr == 0) {
2533                                     /* no more buffer to copy data to, but we did read more.
2534                                      * what hasn't been copied will be dropped
2535                                      */
2536                                     WARN("buffer under run! %lu bytes dropped.\n", bytesRead);
2537                                     wwi->lpQueuePtr = NULL;
2538                                     break;
2539                                 }
2540                             }
2541                         }
2542                     }
2543                 }
2544             }
2545         }
2546
2547         WAIT_OMR(&wwi->msgRing, dwSleepTime);
2548
2549         while (OSS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
2550         {
2551             TRACE("msg=%s param=0x%lx\n", getCmdString(msg), param);
2552             switch (msg) {
2553             case WINE_WM_PAUSING:
2554                 wwi->state = WINE_WS_PAUSED;
2555                 /*FIXME("Device should stop recording\n");*/
2556                 SetEvent(ev);
2557                 break;
2558             case WINE_WM_STARTING:
2559                 wwi->state = WINE_WS_PLAYING;
2560
2561                 if (wwi->ossdev->bTriggerSupport)
2562                 {
2563                     /* start the recording */
2564                     wwi->ossdev->bInputEnabled = TRUE;
2565                     enable = getEnables(wwi->ossdev);
2566                     if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
2567                         wwi->ossdev->bInputEnabled = FALSE;
2568                         ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
2569                     }
2570                 }
2571                 else
2572                 {
2573                     unsigned char data[4];
2574                     /* read 4 bytes to start the recording */
2575                     read(wwi->ossdev->fd, data, 4);
2576                 }
2577
2578                 SetEvent(ev);
2579                 break;
2580             case WINE_WM_HEADER:
2581                 lpWaveHdr = (LPWAVEHDR)param;
2582                 lpWaveHdr->lpNext = 0;
2583
2584                 /* insert buffer at the end of queue */
2585                 {
2586                     LPWAVEHDR*  wh;
2587                     for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2588                     *wh = lpWaveHdr;
2589                 }
2590                 break;
2591             case WINE_WM_STOPPING:
2592                 if (wwi->state != WINE_WS_STOPPED)
2593                 {
2594                     if (wwi->ossdev->bTriggerSupport)
2595                     {
2596                         /* stop the recording */
2597                         wwi->ossdev->bInputEnabled = FALSE;
2598                         enable = getEnables(wwi->ossdev);
2599                         if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
2600                             wwi->ossdev->bInputEnabled = FALSE;
2601                             ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
2602                         }
2603                     }
2604
2605                     /* read any headers in queue */
2606                     widRecorder_ReadHeaders(wwi);
2607
2608                     /* return current buffer to app */
2609                     lpWaveHdr = wwi->lpQueuePtr;
2610                     if (lpWaveHdr)
2611                     {
2612                         LPWAVEHDR       lpNext = lpWaveHdr->lpNext;
2613                         TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
2614                         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2615                         lpWaveHdr->dwFlags |= WHDR_DONE;
2616                         wwi->lpQueuePtr = lpNext;
2617                         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2618                     }
2619                 }
2620                 wwi->state = WINE_WS_STOPPED;
2621                 SetEvent(ev);
2622                 break;
2623             case WINE_WM_RESETTING:
2624                 if (wwi->state != WINE_WS_STOPPED)
2625                 {
2626                     if (wwi->ossdev->bTriggerSupport)
2627                     {
2628                         /* stop the recording */
2629                         wwi->ossdev->bInputEnabled = FALSE;
2630                         enable = getEnables(wwi->ossdev);
2631                         if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
2632                             wwi->ossdev->bInputEnabled = FALSE;
2633                             ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
2634                         }
2635                     }
2636                 }
2637                 wwi->state = WINE_WS_STOPPED;
2638                 wwi->dwTotalRecorded = 0;
2639                 wwi->dwTotalRead = 0;
2640
2641                 /* read any headers in queue */
2642                 widRecorder_ReadHeaders(wwi);
2643
2644                 /* return all buffers to the app */
2645                 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
2646                     TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
2647                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2648                     lpWaveHdr->dwFlags |= WHDR_DONE;
2649                     wwi->lpQueuePtr = lpWaveHdr->lpNext;
2650                     widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2651                 }
2652
2653                 wwi->lpQueuePtr = NULL;
2654                 SetEvent(ev);
2655                 break;
2656             case WINE_WM_UPDATE:
2657                 if (wwi->state == WINE_WS_PLAYING) {
2658                     audio_buf_info tmp_info;
2659                     if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_GETISPACE, &tmp_info) < 0)
2660                         ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n", wwi->ossdev->dev_name, strerror(errno));
2661                     else
2662                         wwi->dwTotalRecorded = wwi->dwTotalRead + tmp_info.bytes;
2663                 }
2664                 SetEvent(ev);
2665                 break;
2666             case WINE_WM_CLOSING:
2667                 wwi->hThread = 0;
2668                 wwi->state = WINE_WS_CLOSED;
2669                 SetEvent(ev);
2670                 HeapFree(GetProcessHeap(), 0, buffer);
2671                 ExitThread(0);
2672                 /* shouldn't go here */
2673             default:
2674                 FIXME("unknown message %d\n", msg);
2675                 break;
2676             }
2677         }
2678     }
2679     ExitThread(0);
2680     /* just for not generating compilation warnings... should never be executed */
2681     return 0;
2682 }
2683
2684
2685 /**************************************************************************
2686  *                              widOpen                         [internal]
2687  */
2688 DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
2689 {
2690     WINE_WAVEIN*        wwi;
2691     audio_buf_info      info;
2692     int                 audio_fragment;
2693     DWORD               ret;
2694
2695     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
2696     if (lpDesc == NULL) {
2697         WARN("Invalid Parameter !\n");
2698         return MMSYSERR_INVALPARAM;
2699     }
2700     if (wDevID >= numInDev) {
2701         WARN("bad device id: %d >= %d\n", wDevID, numInDev);
2702         return MMSYSERR_BADDEVICEID;
2703     }
2704
2705     /* only PCM format is supported so far... */
2706     if (!supportedFormat(lpDesc->lpFormat)) {
2707         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
2708              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
2709              lpDesc->lpFormat->nSamplesPerSec);
2710         return WAVERR_BADFORMAT;
2711     }
2712
2713     if (dwFlags & WAVE_FORMAT_QUERY) {
2714         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
2715              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
2716              lpDesc->lpFormat->nSamplesPerSec);
2717         return MMSYSERR_NOERROR;
2718     }
2719
2720     TRACE("OSS_OpenDevice requested this format: %ldx%dx%d %s\n",
2721           lpDesc->lpFormat->nSamplesPerSec,
2722           lpDesc->lpFormat->wBitsPerSample,
2723           lpDesc->lpFormat->nChannels,
2724           lpDesc->lpFormat->wFormatTag == WAVE_FORMAT_PCM ? "WAVE_FORMAT_PCM" :
2725           lpDesc->lpFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? "WAVE_FORMAT_EXTENSIBLE" :
2726           "UNSUPPORTED");
2727
2728     wwi = &WInDev[wDevID];
2729
2730     if (wwi->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED;
2731
2732     if ((dwFlags & WAVE_DIRECTSOUND) &&
2733         !(wwi->ossdev->in_caps_support & WAVECAPS_DIRECTSOUND))
2734         /* not supported, ignore it */
2735         dwFlags &= ~WAVE_DIRECTSOUND;
2736
2737     if (dwFlags & WAVE_DIRECTSOUND) {
2738         TRACE("has DirectSoundCapture driver\n");
2739         if (wwi->ossdev->in_caps_support & WAVECAPS_SAMPLEACCURATE)
2740             /* we have realtime DirectSound, fragments just waste our time,
2741              * but a large buffer is good, so choose 64KB (32 * 2^11) */
2742             audio_fragment = 0x0020000B;
2743         else
2744             /* to approximate realtime, we must use small fragments,
2745              * let's try to fragment the above 64KB (256 * 2^8) */
2746             audio_fragment = 0x01000008;
2747     } else {
2748         TRACE("doesn't have DirectSoundCapture driver\n");
2749         if (wwi->ossdev->open_count > 0) {
2750             TRACE("Using output device audio_fragment\n");
2751             /* FIXME: This may not be optimal for capture but it allows us
2752              * to do hardware playback without hardware capture. */
2753             audio_fragment = wwi->ossdev->audio_fragment;
2754         } else {
2755             /* A wave device must have a worst case latency of 10 ms so calculate
2756              * the largest fragment size less than 10 ms long.
2757              */
2758             int fsize = lpDesc->lpFormat->nAvgBytesPerSec / 100;        /* 10 ms chunk */
2759             int shift = 0;
2760             while ((1 << shift) <= fsize)
2761                 shift++;
2762             shift--;
2763             audio_fragment = 0x00100000 + shift;        /* 16 fragments of 2^shift */
2764         }
2765     }
2766
2767     TRACE("requesting %d %d byte fragments (%ld ms)\n", audio_fragment >> 16,
2768         1 << (audio_fragment & 0xffff),
2769         ((1 << (audio_fragment & 0xffff)) * 1000) / lpDesc->lpFormat->nAvgBytesPerSec);
2770
2771     ret = OSS_OpenDevice(wwi->ossdev, O_RDONLY, &audio_fragment,
2772                          1,
2773                          lpDesc->lpFormat->nSamplesPerSec,
2774                          (lpDesc->lpFormat->nChannels > 1) ? 1 : 0,
2775                          (lpDesc->lpFormat->wBitsPerSample == 16)
2776                          ? AFMT_S16_LE : AFMT_U8);
2777     if (ret != 0) return ret;
2778     wwi->state = WINE_WS_STOPPED;
2779
2780     if (wwi->lpQueuePtr) {
2781         WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
2782         wwi->lpQueuePtr = NULL;
2783     }
2784     wwi->dwTotalRecorded = 0;
2785     wwi->dwTotalRead = 0;
2786     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2787
2788     memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
2789     copy_format(lpDesc->lpFormat, &wwi->waveFormat);
2790
2791     if (wwi->waveFormat.Format.wBitsPerSample == 0) {
2792         WARN("Resetting zeroed wBitsPerSample\n");
2793         wwi->waveFormat.Format.wBitsPerSample = 8 *
2794             (wwi->waveFormat.Format.nAvgBytesPerSec /
2795              wwi->waveFormat.Format.nSamplesPerSec) /
2796             wwi->waveFormat.Format.nChannels;
2797     }
2798
2799     if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
2800         ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
2801             wwi->ossdev->dev_name, strerror(errno));
2802         OSS_CloseDevice(wwi->ossdev);
2803         wwi->state = WINE_WS_CLOSED;
2804         return MMSYSERR_NOTENABLED;
2805     }
2806
2807     TRACE("got %d %d byte fragments (%d ms/fragment)\n", info.fragstotal,
2808         info.fragsize, (info.fragsize * 1000) / (wwi->ossdev->sample_rate *
2809         (wwi->ossdev->stereo ? 2 : 1) *
2810         (wwi->ossdev->format == AFMT_U8 ? 1 : 2)));
2811
2812     wwi->dwFragmentSize = info.fragsize;
2813
2814     TRACE("dwFragmentSize=%lu\n", wwi->dwFragmentSize);
2815     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
2816           wwi->waveFormat.Format.wBitsPerSample, wwi->waveFormat.Format.nAvgBytesPerSec,
2817           wwi->waveFormat.Format.nSamplesPerSec, wwi->waveFormat.Format.nChannels,
2818           wwi->waveFormat.Format.nBlockAlign);
2819
2820     OSS_InitRingMessage(&wwi->msgRing);
2821
2822     wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2823     wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
2824     WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
2825     CloseHandle(wwi->hStartUpEvent);
2826     wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
2827
2828     return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
2829 }
2830
2831 /**************************************************************************
2832  *                              widClose                        [internal]
2833  */
2834 static DWORD widClose(WORD wDevID)
2835 {
2836     WINE_WAVEIN*        wwi;
2837
2838     TRACE("(%u);\n", wDevID);
2839     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
2840         WARN("can't close !\n");
2841         return MMSYSERR_INVALHANDLE;
2842     }
2843
2844     wwi = &WInDev[wDevID];
2845
2846     if (wwi->lpQueuePtr != NULL) {
2847         WARN("still buffers open !\n");
2848         return WAVERR_STILLPLAYING;
2849     }
2850
2851     OSS_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
2852     OSS_CloseDevice(wwi->ossdev);
2853     wwi->state = WINE_WS_CLOSED;
2854     wwi->dwFragmentSize = 0;
2855     OSS_DestroyRingMessage(&wwi->msgRing);
2856     return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
2857 }
2858
2859 /**************************************************************************
2860  *                              widAddBuffer            [internal]
2861  */
2862 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2863 {
2864     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2865
2866     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
2867         WARN("can't do it !\n");
2868         return MMSYSERR_INVALHANDLE;
2869     }
2870     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
2871         TRACE("never been prepared !\n");
2872         return WAVERR_UNPREPARED;
2873     }
2874     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
2875         TRACE("header already in use !\n");
2876         return WAVERR_STILLPLAYING;
2877     }
2878
2879     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
2880     lpWaveHdr->dwFlags &= ~WHDR_DONE;
2881     lpWaveHdr->dwBytesRecorded = 0;
2882     lpWaveHdr->lpNext = NULL;
2883
2884     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
2885     return MMSYSERR_NOERROR;
2886 }
2887
2888 /**************************************************************************
2889  *                              widPrepare                      [internal]
2890  */
2891 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2892 {
2893     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2894
2895     if (wDevID >= numInDev) return MMSYSERR_INVALHANDLE;
2896
2897     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2898         return WAVERR_STILLPLAYING;
2899
2900     lpWaveHdr->dwFlags |= WHDR_PREPARED;
2901     lpWaveHdr->dwFlags &= ~WHDR_DONE;
2902     lpWaveHdr->dwBytesRecorded = 0;
2903     TRACE("header prepared !\n");
2904     return MMSYSERR_NOERROR;
2905 }
2906
2907 /**************************************************************************
2908  *                              widUnprepare                    [internal]
2909  */
2910 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2911 {
2912     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2913     if (wDevID >= numInDev) return MMSYSERR_INVALHANDLE;
2914
2915     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2916         return WAVERR_STILLPLAYING;
2917
2918     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
2919     lpWaveHdr->dwFlags |= WHDR_DONE;
2920
2921     return MMSYSERR_NOERROR;
2922 }
2923
2924 /**************************************************************************
2925  *                      widStart                                [internal]
2926  */
2927 static DWORD widStart(WORD wDevID)
2928 {
2929     TRACE("(%u);\n", wDevID);
2930     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
2931         WARN("can't start recording !\n");
2932         return MMSYSERR_INVALHANDLE;
2933     }
2934
2935     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
2936     return MMSYSERR_NOERROR;
2937 }
2938
2939 /**************************************************************************
2940  *                      widStop                                 [internal]
2941  */
2942 static DWORD widStop(WORD wDevID)
2943 {
2944     TRACE("(%u);\n", wDevID);
2945     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
2946         WARN("can't stop !\n");
2947         return MMSYSERR_INVALHANDLE;
2948     }
2949
2950     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
2951
2952     return MMSYSERR_NOERROR;
2953 }
2954
2955 /**************************************************************************
2956  *                      widReset                                [internal]
2957  */
2958 static DWORD widReset(WORD wDevID)
2959 {
2960     TRACE("(%u);\n", wDevID);
2961     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
2962         WARN("can't reset !\n");
2963         return MMSYSERR_INVALHANDLE;
2964     }
2965     OSS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2966     return MMSYSERR_NOERROR;
2967 }
2968
2969 /**************************************************************************
2970  *                              widGetPosition                  [internal]
2971  */
2972 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
2973 {
2974     WINE_WAVEIN*        wwi;
2975
2976     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
2977
2978     if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
2979         WARN("can't get pos !\n");
2980         return MMSYSERR_INVALHANDLE;
2981     }
2982
2983     if (lpTime == NULL) {
2984         WARN("invalid parameter: lpTime == NULL\n");
2985         return MMSYSERR_INVALPARAM;
2986     }
2987
2988     wwi = &WInDev[wDevID];
2989 #ifdef EXACT_WIDPOSITION
2990     if (wwi->ossdev->in_caps_support & WAVECAPS_SAMPLEACCURATE)
2991         OSS_AddRingMessage(&(wwi->msgRing), WINE_WM_UPDATE, 0, TRUE);
2992 #endif
2993
2994     return bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->waveFormat);
2995 }
2996
2997 /**************************************************************************
2998  *                              widMessage (WINEOSS.6)
2999  */
3000 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3001                             DWORD dwParam1, DWORD dwParam2)
3002 {
3003     TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
3004           wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
3005
3006     switch (wMsg) {
3007     case DRVM_INIT:
3008     case DRVM_EXIT:
3009     case DRVM_ENABLE:
3010     case DRVM_DISABLE:
3011         /* FIXME: Pretend this is supported */
3012         return 0;
3013     case WIDM_OPEN:             return widOpen       (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
3014     case WIDM_CLOSE:            return widClose      (wDevID);
3015     case WIDM_ADDBUFFER:        return widAddBuffer  (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3016     case WIDM_PREPARE:          return widPrepare    (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3017     case WIDM_UNPREPARE:        return widUnprepare  (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3018     case WIDM_GETDEVCAPS:       return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
3019     case WIDM_GETNUMDEVS:       return numInDev;
3020     case WIDM_GETPOS:           return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
3021     case WIDM_RESET:            return widReset      (wDevID);
3022     case WIDM_START:            return widStart      (wDevID);
3023     case WIDM_STOP:             return widStop       (wDevID);
3024     case DRV_QUERYDEVICEINTERFACESIZE: return wdDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
3025     case DRV_QUERYDEVICEINTERFACE:     return wdDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
3026     case DRV_QUERYDSOUNDIFACE:  return widDsCreate   (wDevID, (PIDSCDRIVER*)dwParam1);
3027     case DRV_QUERYDSOUNDDESC:   return widDsDesc     (wDevID, (PDSDRIVERDESC)dwParam1);
3028     default:
3029         FIXME("unknown message %u!\n", wMsg);
3030     }
3031     return MMSYSERR_NOTSUPPORTED;
3032 }
3033
3034 #else /* !HAVE_OSS */
3035
3036 /**************************************************************************
3037  *                              wodMessage (WINEOSS.7)
3038  */
3039 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3040                             DWORD dwParam1, DWORD dwParam2)
3041 {
3042     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3043     return MMSYSERR_NOTENABLED;
3044 }
3045
3046 /**************************************************************************
3047  *                              widMessage (WINEOSS.6)
3048  */
3049 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3050                             DWORD dwParam1, DWORD dwParam2)
3051 {
3052     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3053     return MMSYSERR_NOTENABLED;
3054 }
3055
3056 #endif /* HAVE_OSS */