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