Separate "Enumerate Hardware Addresses" as function, as other netapi32
[wine] / dlls / winmm / winenas / audio.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Wine Driver for NAS Network Audio System
4  *   http://radscan.com/nas.html
5  *
6  * Copyright 1994 Martin Ayotte
7  *           1999 Eric Pouech (async playing in waveOut/waveIn)
8  *           2000 Eric Pouech (loops in waveOut)
9  *           2002 Chris Morgan (aRts version of this file)
10  *           2002 Nicolas Escuder (NAS version of this file)
11  *
12  * Copyright 2002 Nicolas Escuder <n.escuder@alineanet.com>
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  */
28 /* NOTE:
29  *    with nas we cannot stop the audio that is already in
30  *    the servers buffer.
31  *
32  * FIXME:
33  *      pause in waveOut does not work correctly in loop mode
34  *
35  */
36
37 #include "config.h"
38
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 #ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
47 #endif
48 #include <fcntl.h>
49
50 #if 0
51 #define EMULATE_SB16
52 #endif
53 #define FRAG_SIZE  1024
54 #define FRAG_COUNT 10
55
56 /* avoid type conflicts */
57 #define INT8 X_INT8
58 #define INT16 X_INT16
59 #define INT32 X_INT32
60 #define BOOL X_BOOL
61 #define BYTE X_BYTE
62 #ifdef HAVE_AUDIO_AUDIOLIB_H
63 #include <audio/audiolib.h>
64 #endif
65 #ifdef HAVE_AUDIO_SOUNDLIB_H
66 #include <audio/soundlib.h>
67 #endif
68 #undef INT8
69 #undef INT16
70 #undef INT32
71 #undef BOOL
72 #undef BYTE
73
74 #include "windef.h"
75 #include "wingdi.h"
76 #include "winerror.h"
77 #include "wine/winuser16.h"
78 #include "mmddk.h"
79 #include "dsound.h"
80 #include "dsdriver.h"
81 #include "nas.h"
82 #include "wine/debug.h"
83
84 WINE_DEFAULT_DEBUG_CHANNEL(wave);
85
86 /* Allow 1% deviation for sample rates (some ES137x cards) */
87 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
88
89 #ifdef HAVE_NAS
90
91 static AuServer         *AuServ;
92
93 #define MAX_WAVEOUTDRV  (1)
94
95 /* state diagram for waveOut writing:
96  *
97  * +---------+-------------+---------------+---------------------------------+
98  * |  state  |  function   |     event     |            new state            |
99  * +---------+-------------+---------------+---------------------------------+
100  * |         | open()      |               | STOPPED                         |
101  * | PAUSED  | write()     |               | PAUSED                          |
102  * | STOPPED | write()     | <thrd create> | PLAYING                         |
103  * | PLAYING | write()     | HEADER        | PLAYING                         |
104  * | (other) | write()     | <error>       |                                 |
105  * | (any)   | pause()     | PAUSING       | PAUSED                          |
106  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
107  * | (any)   | reset()     | RESETTING     | STOPPED                         |
108  * | (any)   | close()     | CLOSING       | CLOSED                          |
109  * +---------+-------------+---------------+---------------------------------+
110  */
111
112 /* states of the playing device */
113 #define WINE_WS_PLAYING         0
114 #define WINE_WS_PAUSED          1
115 #define WINE_WS_STOPPED         2
116 #define WINE_WS_CLOSED          3
117
118 /* events to be send to device */
119 enum win_wm_message {
120     WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
121     WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
122 };
123
124 typedef struct {
125     enum win_wm_message         msg;    /* message identifier */
126     DWORD                       param;  /* parameter for this message */
127     HANDLE                      hEvent; /* if message is synchronous, handle of event for synchro */
128 } RING_MSG;
129
130 /* implement an in-process message ring for better performance
131  * (compared to passing thru the server)
132  * this ring will be used by the input (resp output) record (resp playback) routine
133  */
134 typedef struct {
135 #define NAS_RING_BUFFER_SIZE    30
136     RING_MSG                    messages[NAS_RING_BUFFER_SIZE];
137     int                         msg_tosave;
138     int                         msg_toget;
139     HANDLE                      msg_event;
140     CRITICAL_SECTION            msg_crst;
141 } MSG_RING;
142
143 typedef struct {
144     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
145     WAVEOPENDESC                waveDesc;
146     WORD                        wFlags;
147     PCMWAVEFORMAT               format;
148     WAVEOUTCAPSA                caps;
149     int                         Id;
150
151     int                         open;
152     AuServer                    *AuServ;
153     AuDeviceID                  AuDev;
154     AuFlowID                    AuFlow;
155     BOOL                        FlowStarted;
156
157     DWORD                       writeBytes;
158     DWORD                       freeBytes;
159     DWORD                       sendBytes;
160
161     DWORD                       BufferSize;           /* size of whole buffer in bytes */
162
163     char*                       SoundBuffer;
164     long                        BufferUsed;
165
166     DWORD                       volume_left;            /* volume control information */
167     DWORD                       volume_right;
168
169     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
170     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
171
172     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
173     DWORD                       dwLoops;                /* private copy of loop counter */
174
175     DWORD                       PlayedTotal;            /* number of bytes actually played since opening */
176     DWORD                       WrittenTotal;         /* number of bytes written to the audio device since opening */
177
178     /* synchronization stuff */
179     HANDLE                      hStartUpEvent;
180     HANDLE                      hThread;
181     DWORD                       dwThreadID;
182     MSG_RING                    msgRing;
183 } WINE_WAVEOUT;
184
185 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
186
187 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
188
189
190 /* NASFUNC */
191 static AuBool event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd);
192 static int nas_init(void);
193 static int nas_end(void);
194
195 static int nas_finddev(WINE_WAVEOUT* wwo);
196 static int nas_open(WINE_WAVEOUT* wwo);
197 static int nas_free(WINE_WAVEOUT* wwo);
198 static int nas_close(WINE_WAVEOUT* wwo);
199 static void buffer_resize(WINE_WAVEOUT* wwo, int len);
200 static int nas_add_buffer(WINE_WAVEOUT* wwo);
201 static int nas_send_buffer(WINE_WAVEOUT* wwo);
202
203 /* These strings used only for tracing */
204 static const char *wodPlayerCmdString[] = {
205     "WINE_WM_PAUSING",
206     "WINE_WM_RESTARTING",
207     "WINE_WM_RESETTING",
208     "WINE_WM_HEADER",
209     "WINE_WM_UPDATE",
210     "WINE_WM_BREAKLOOP",
211     "WINE_WM_CLOSING",
212 };
213
214 static char *nas_elementnotify_kinds[] = {
215         "LowWater",
216         "HighWater",
217         "State",
218         "Unknown"
219 };
220
221 static char *nas_states[] = {
222         "Stop",
223         "Start",
224         "Pause",
225         "Any"
226 };
227
228 static char *nas_reasons[] = {
229         "User",
230         "Underrun",
231         "Overrun",
232         "EOF",
233         "Watermark",
234         "Hardware",
235         "Any"
236 };
237
238 static char* nas_reason(unsigned int reason)
239 {
240         if (reason > 6) reason = 6;
241         return nas_reasons[reason];
242 }
243
244 static char* nas_elementnotify_kind(unsigned int kind)
245 {
246         if (kind > 2) kind = 3;
247         return nas_elementnotify_kinds[kind];
248 }
249
250
251 #if 0
252 static const char* nas_event_type(unsigned int type)
253 {
254         static const char * const nas_event_types[] =
255         {
256             "Undefined",
257             "Undefined",
258             "ElementNotify",
259             "GrabNotify",
260             "MonitorNotify",
261             "BucketNotify",
262             "DeviceNotify"
263         };
264
265         if (type > 6) type = 0;
266         return nas_event_types[type];
267 }
268 #endif
269
270
271 static char* nas_state(unsigned int state)
272 {
273         if (state > 3) state = 3;
274         return nas_states[state];
275 }
276
277 /*======================================================================*
278  *                  Low level WAVE implementation                       *
279  *======================================================================*/
280
281 /* Volume functions derived from Alsaplayer source */
282 /* length is the number of 16 bit samples */
283 void volume_effect16(void *bufin, void* bufout, int length, int left,
284                 int right, int  nChannels)
285 {
286   short *d_out = (short *)bufout;
287   short *d_in = (short *)bufin;
288   int i, v;
289
290 /*
291   TRACE("length == %d, nChannels == %d\n", length, nChannels);
292 */
293
294   if (right == -1) right = left;
295
296   for(i = 0; i < length; i+=(nChannels))
297   {
298     v = (int) ((*(d_in++) * left) / 100);
299     *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
300     if(nChannels == 2)
301     {
302       v = (int) ((*(d_in++) * right) / 100);
303       *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
304     }
305   }
306 }
307
308 /* length is the number of 8 bit samples */
309 void volume_effect8(void *bufin, void* bufout, int length, int left,
310                 int right, int  nChannels)
311 {
312   char *d_out = (char *)bufout;
313   char *d_in = (char *)bufin;
314   int i, v;
315
316 /*
317   TRACE("length == %d, nChannels == %d\n", length, nChannels);
318 */
319
320   if (right == -1) right = left;
321
322   for(i = 0; i < length; i+=(nChannels))
323   {
324     v = (char) ((*(d_in++) * left) / 100);
325     *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
326     if(nChannels == 2)
327     {
328       v = (char) ((*(d_in++) * right) / 100);
329       *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
330     }
331   }
332 }
333
334 /******************************************************************
335  *              NAS_CloseDevice
336  *
337  */
338 void            NAS_CloseDevice(WINE_WAVEOUT* wwo)
339 {
340   TRACE("NAS_CloseDevice\n");
341   nas_close(wwo);
342 }
343
344 /******************************************************************
345  *              NAS_WaveClose
346  */
347 LONG            NAS_WaveClose(void)
348 {
349     nas_end();    /* free up nas server */
350     return 1;
351 }
352
353 /******************************************************************
354  *              NAS_WaveInit
355  *
356  * Initialize internal structures from NAS server info
357  */
358 LONG NAS_WaveInit(void)
359 {
360     int         i;
361     nas_init();
362
363     /* initialize all device handles to -1 */
364     for (i = 0; i < MAX_WAVEOUTDRV; ++i)
365     {
366         memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out caps values */
367
368         WOutDev[i].AuServ = AuServ;
369         WOutDev[i].AuDev = AuNone;
370         WOutDev[i].Id = i;
371     /* FIXME: some programs compare this string against the content of the registry
372      * for MM drivers. The names have to match in order for the program to work
373      * (e.g. MS win9x mplayer.exe)
374      */
375 #ifdef EMULATE_SB16
376         WOutDev[i].caps.wMid = 0x0002;
377         WOutDev[i].caps.wPid = 0x0104;
378         strcpy(WOutDev[i].caps.szPname, "SB16 Wave Out");
379 #else
380         WOutDev[i].caps.wMid = 0x00FF;  /* Manufac ID */
381         WOutDev[i].caps.wPid = 0x0001;  /* Product ID */
382     /*    strcpy(WOutDev[i].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
383         strcpy(WOutDev[i].caps.szPname, "CS4236/37/38");
384 #endif
385         WOutDev[i].AuFlow = 0;
386         WOutDev[i].caps.vDriverVersion = 0x0100;
387         WOutDev[i].caps.dwFormats = 0x00000000;
388         WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
389
390         WOutDev[i].caps.wChannels = 2;
391         WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME;
392
393         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
394         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
395         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
396         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
397         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
398         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
399         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
400         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
401         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
402         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
403         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
404         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
405     }
406
407
408     return 0;
409 }
410
411 /******************************************************************
412  *              NAS_InitRingMessage
413  *
414  * Initialize the ring of messages for passing between driver's caller and playback/record
415  * thread
416  */
417 static int NAS_InitRingMessage(MSG_RING* mr)
418 {
419     mr->msg_toget = 0;
420     mr->msg_tosave = 0;
421     mr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
422     memset(mr->messages, 0, sizeof(RING_MSG) * NAS_RING_BUFFER_SIZE);
423     InitializeCriticalSection(&mr->msg_crst);
424     return 0;
425 }
426
427 /******************************************************************
428  *              NAS_DestroyRingMessage
429  *
430  */
431 static int NAS_DestroyRingMessage(MSG_RING* mr)
432 {
433     CloseHandle(mr->msg_event);
434     DeleteCriticalSection(&mr->msg_crst);
435     return 0;
436 }
437
438 /******************************************************************
439  *              NAS_AddRingMessage
440  *
441  * Inserts a new message into the ring (should be called from DriverProc derivated routines)
442  */
443 static int NAS_AddRingMessage(MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
444 {
445     HANDLE      hEvent = INVALID_HANDLE_VALUE;
446
447     EnterCriticalSection(&mr->msg_crst);
448     if ((mr->msg_toget == ((mr->msg_tosave + 1) % NAS_RING_BUFFER_SIZE))) /* buffer overflow? */
449     {
450         ERR("buffer overflow !?\n");
451         LeaveCriticalSection(&mr->msg_crst);
452         return 0;
453     }
454     if (wait)
455     {
456         hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
457         if (hEvent == INVALID_HANDLE_VALUE)
458         {
459             ERR("can't create event !?\n");
460             LeaveCriticalSection(&mr->msg_crst);
461             return 0;
462         }
463         if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
464             FIXME("two fast messages in the queue!!!!\n");
465
466         /* fast messages have to be added at the start of the queue */
467         mr->msg_toget = (mr->msg_toget + NAS_RING_BUFFER_SIZE - 1) % NAS_RING_BUFFER_SIZE;
468
469         mr->messages[mr->msg_toget].msg = msg;
470         mr->messages[mr->msg_toget].param = param;
471         mr->messages[mr->msg_toget].hEvent = hEvent;
472     }
473     else
474     {
475         mr->messages[mr->msg_tosave].msg = msg;
476         mr->messages[mr->msg_tosave].param = param;
477         mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
478         mr->msg_tosave = (mr->msg_tosave + 1) % NAS_RING_BUFFER_SIZE;
479     }
480
481     LeaveCriticalSection(&mr->msg_crst);
482
483     SetEvent(mr->msg_event);    /* signal a new message */
484
485     if (wait)
486     {
487         /* wait for playback/record thread to have processed the message */
488         WaitForSingleObject(hEvent, INFINITE);
489         CloseHandle(hEvent);
490     }
491
492     return 1;
493 }
494
495 /******************************************************************
496  *              NAS_RetrieveRingMessage
497  *
498  * Get a message from the ring. Should be called by the playback/record thread.
499  */
500 static int NAS_RetrieveRingMessage(MSG_RING* mr,
501                                    enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
502 {
503     EnterCriticalSection(&mr->msg_crst);
504
505     if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
506     {
507         LeaveCriticalSection(&mr->msg_crst);
508         return 0;
509     }
510
511     *msg = mr->messages[mr->msg_toget].msg;
512     mr->messages[mr->msg_toget].msg = 0;
513     *param = mr->messages[mr->msg_toget].param;
514     *hEvent = mr->messages[mr->msg_toget].hEvent;
515     mr->msg_toget = (mr->msg_toget + 1) % NAS_RING_BUFFER_SIZE;
516     LeaveCriticalSection(&mr->msg_crst);
517     return 1;
518 }
519
520 /*======================================================================*
521  *                  Low level WAVE OUT implementation                   *
522  *======================================================================*/
523
524 /**************************************************************************
525  *                      wodNotifyClient                 [internal]
526  */
527 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
528 {
529     TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
530
531     switch (wMsg) {
532     case WOM_OPEN:
533     case WOM_CLOSE:
534     case WOM_DONE:
535         if (wwo->wFlags != DCB_NULL &&
536             !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
537                             wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
538             WARN("can't notify client !\n");
539             return MMSYSERR_ERROR;
540         }
541         break;
542     default:
543         FIXME("Unknown callback message %u\n", wMsg);
544         return MMSYSERR_INVALPARAM;
545     }
546     return MMSYSERR_NOERROR;
547 }
548
549 /**************************************************************************
550  *                              wodUpdatePlayedTotal    [internal]
551  *
552  */
553 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
554 {
555     wwo->PlayedTotal = wwo->WrittenTotal;
556     return TRUE;
557 }
558
559 /**************************************************************************
560  *                              wodPlayer_BeginWaveHdr          [internal]
561  *
562  * Makes the specified lpWaveHdr the currently playing wave header.
563  * If the specified wave header is a begin loop and we're not already in
564  * a loop, setup the loop.
565  */
566 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
567 {
568     wwo->lpPlayPtr = lpWaveHdr;
569
570     if (!lpWaveHdr) return;
571
572     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
573         if (wwo->lpLoopPtr) {
574             WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
575             TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
576         } else {
577             TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
578             wwo->lpLoopPtr = lpWaveHdr;
579             /* Windows does not touch WAVEHDR.dwLoops,
580              * so we need to make an internal copy */
581             wwo->dwLoops = lpWaveHdr->dwLoops;
582         }
583     }
584 }
585
586 /**************************************************************************
587  *                              wodPlayer_PlayPtrNext           [internal]
588  *
589  * Advance the play pointer to the next waveheader, looping if required.
590  */
591 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
592 {
593     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
594
595     if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
596         /* We're at the end of a loop, loop if required */
597         if (--wwo->dwLoops > 0) {
598             wwo->lpPlayPtr = wwo->lpLoopPtr;
599         } else {
600             /* Handle overlapping loops correctly */
601             if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
602                 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
603                 /* shall we consider the END flag for the closing loop or for
604                  * the opening one or for both ???
605                  * code assumes for closing loop only
606                  */
607             } else {
608                 lpWaveHdr = lpWaveHdr->lpNext;
609             }
610             wwo->lpLoopPtr = NULL;
611             wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
612         }
613     } else {
614         /* We're not in a loop.  Advance to the next wave header */
615         wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
616     }
617     return lpWaveHdr;
618 }
619
620 /**************************************************************************
621  *                              wodPlayer_NotifyCompletions     [internal]
622  *
623  * Notifies and remove from queue all wavehdrs which have been played to
624  * the speaker (ie. they have cleared the audio device).  If force is true,
625  * we notify all wavehdrs and remove them all from the queue even if they
626  * are unplayed or part of a loop.
627  */
628 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
629 {
630     LPWAVEHDR           lpWaveHdr;
631
632     /* Start from lpQueuePtr and keep notifying until:
633      * - we hit an unwritten wavehdr
634      * - we hit the beginning of a running loop
635      * - we hit a wavehdr which hasn't finished playing
636      */
637     wodUpdatePlayedTotal(wwo);
638
639     while ((lpWaveHdr = wwo->lpQueuePtr) && (force || (lpWaveHdr != wwo->lpPlayPtr &&
640             lpWaveHdr != wwo->lpLoopPtr && lpWaveHdr->reserved <= wwo->PlayedTotal))) {
641
642         wwo->lpQueuePtr = lpWaveHdr->lpNext;
643
644         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
645         lpWaveHdr->dwFlags |= WHDR_DONE;
646
647         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
648     }
649     return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
650             1 : 1;
651 }
652
653 /**************************************************************************
654  *                              wodPlayer_Reset                 [internal]
655  *
656  * wodPlayer helper. Resets current output stream.
657  */
658 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
659 {
660     wodUpdatePlayedTotal(wwo);
661     wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */
662
663     /* we aren't able to flush any data that has already been written */
664     /* to nas, otherwise we would do the flushing here */
665
666     nas_free(wwo);
667
668     if (reset) {
669         enum win_wm_message     msg;
670         DWORD                   param;
671         HANDLE                  ev;
672
673         /* remove any buffer */
674         wodPlayer_NotifyCompletions(wwo, TRUE);
675
676         wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
677         wwo->state = WINE_WS_STOPPED;
678         wwo->PlayedTotal = wwo->WrittenTotal = 0;
679
680         /* remove any existing message in the ring */
681         EnterCriticalSection(&wwo->msgRing.msg_crst);
682
683         /* return all pending headers in queue */
684         while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
685         {
686             TRACE("flushing msg\n");
687             if (msg != WINE_WM_HEADER)
688             {
689                 FIXME("shouldn't have headers left\n");
690                 SetEvent(ev);
691                 continue;
692             }
693             ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
694             ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
695
696             wodNotifyClient(wwo, WOM_DONE, param, 0);
697         }
698         ResetEvent(wwo->msgRing.msg_event);
699         LeaveCriticalSection(&wwo->msgRing.msg_crst);
700     } else {
701         if (wwo->lpLoopPtr) {
702             /* complicated case, not handled yet (could imply modifying the loop counter */
703             FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
704             wwo->lpPlayPtr = wwo->lpLoopPtr;
705             wwo->WrittenTotal = wwo->PlayedTotal; /* this is wrong !!! */
706         } else {
707             /* the data already written is going to be played, so take */
708             /* this fact into account here */
709             wwo->PlayedTotal = wwo->WrittenTotal;
710         }
711         wwo->state = WINE_WS_PAUSED;
712     }
713 }
714
715 /**************************************************************************
716  *                    wodPlayer_ProcessMessages                 [internal]
717  */
718 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
719 {
720     LPWAVEHDR           lpWaveHdr;
721     enum win_wm_message msg;
722     DWORD               param;
723     HANDLE              ev;
724
725     while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
726         TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
727         switch (msg) {
728         case WINE_WM_PAUSING:
729             wodPlayer_Reset(wwo, FALSE);
730             SetEvent(ev);
731             break;
732         case WINE_WM_RESTARTING:
733             wwo->state = WINE_WS_PLAYING;
734             SetEvent(ev);
735             break;
736         case WINE_WM_HEADER:
737             lpWaveHdr = (LPWAVEHDR)param;
738
739             /* insert buffer at the end of queue */
740             {
741                 LPWAVEHDR*      wh;
742                 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
743                 *wh = lpWaveHdr;
744             }
745             if (!wwo->lpPlayPtr)
746                 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
747             if (wwo->state == WINE_WS_STOPPED)
748                 wwo->state = WINE_WS_PLAYING;
749             break;
750         case WINE_WM_RESETTING:
751             wodPlayer_Reset(wwo, TRUE);
752             SetEvent(ev);
753             break;
754         case WINE_WM_UPDATE:
755             wodUpdatePlayedTotal(wwo);
756             SetEvent(ev);
757             break;
758         case WINE_WM_BREAKLOOP:
759             if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
760                 /* ensure exit at end of current loop */
761                 wwo->dwLoops = 1;
762             }
763             SetEvent(ev);
764             break;
765         case WINE_WM_CLOSING:
766             /* sanity check: this should not happen since the device must have been reset before */
767             if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
768             wwo->hThread = 0;
769             wwo->state = WINE_WS_CLOSED;
770             SetEvent(ev);
771             ExitThread(0);
772             /* shouldn't go here */
773         default:
774             FIXME("unknown message %d\n", msg);
775             break;
776         }
777     }
778 }
779
780 /**************************************************************************
781  *                              wodPlayer                       [internal]
782  */
783 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
784 {
785     WORD          uDevID = (DWORD)pmt;
786     WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
787
788     wwo->state = WINE_WS_STOPPED;
789     SetEvent(wwo->hStartUpEvent);
790
791     for (;;) {
792
793         if (wwo->FlowStarted) {
794            AuHandleEvents(wwo->AuServ);
795
796            if (wwo->state == WINE_WS_PLAYING && wwo->freeBytes && wwo->BufferUsed)
797               nas_send_buffer(wwo);
798         }
799
800         if (wwo->BufferUsed <= FRAG_SIZE && wwo->writeBytes > 0)
801            wodPlayer_NotifyCompletions(wwo, FALSE);
802
803         WaitForSingleObject(wwo->msgRing.msg_event, 20);
804         wodPlayer_ProcessMessages(wwo);
805
806         while(wwo->lpPlayPtr) {
807            wwo->lpPlayPtr->reserved = wwo->WrittenTotal + wwo->lpPlayPtr->dwBufferLength;
808            nas_add_buffer(wwo);
809            wodPlayer_PlayPtrNext(wwo);
810         }
811
812     }
813 }
814
815 /**************************************************************************
816  *                      wodGetDevCaps                           [internal]
817  */
818 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
819 {
820     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
821
822     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
823
824     if (wDevID >= MAX_WAVEOUTDRV) {
825         TRACE("MAX_WAVOUTDRV reached !\n");
826         return MMSYSERR_BADDEVICEID;
827     }
828
829     memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
830     return MMSYSERR_NOERROR;
831 }
832
833 /**************************************************************************
834  *                              wodOpen                         [internal]
835  */
836 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
837 {
838     WINE_WAVEOUT*       wwo;
839
840     TRACE("wodOpen (%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
841
842     if (lpDesc == NULL) {
843         WARN("Invalid Parameter !\n");
844         return MMSYSERR_INVALPARAM;
845     }
846     if (wDevID >= MAX_WAVEOUTDRV) {
847         TRACE("MAX_WAVOUTDRV reached !\n");
848         return MMSYSERR_BADDEVICEID;
849     }
850
851     /* if this device is already open tell the app that it is allocated */
852
853     wwo = &WOutDev[wDevID];
854
855     if(wwo->open)
856     {
857       TRACE("device already allocated\n");
858       return MMSYSERR_ALLOCATED;
859     }
860
861
862     /* only PCM format is supported so far... */
863     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
864         lpDesc->lpFormat->nChannels == 0 ||
865         lpDesc->lpFormat->nSamplesPerSec == 0) {
866         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
867              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
868              lpDesc->lpFormat->nSamplesPerSec);
869         return WAVERR_BADFORMAT;
870     }
871
872     if (dwFlags & WAVE_FORMAT_QUERY) {
873         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
874              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
875              lpDesc->lpFormat->nSamplesPerSec);
876         return MMSYSERR_NOERROR;
877     }
878
879     /* direct sound not supported, ignore the flag */
880     dwFlags &= ~WAVE_DIRECTSOUND;
881
882     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
883
884     memcpy(&wwo->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
885     memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
886
887     if (wwo->format.wBitsPerSample == 0) {
888         WARN("Resetting zeroed wBitsPerSample\n");
889         wwo->format.wBitsPerSample = 8 *
890             (wwo->format.wf.nAvgBytesPerSec /
891              wwo->format.wf.nSamplesPerSec) /
892             wwo->format.wf.nChannels;
893     }
894
895     if (!nas_open(wwo))
896        return MMSYSERR_ALLOCATED;
897
898     NAS_InitRingMessage(&wwo->msgRing);
899
900     /* create player thread */
901     if (!(dwFlags & WAVE_DIRECTSOUND)) {
902         wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
903         wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
904         WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
905         CloseHandle(wwo->hStartUpEvent);
906     } else {
907         wwo->hThread = INVALID_HANDLE_VALUE;
908         wwo->dwThreadID = 0;
909     }
910     wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
911
912     TRACE("stream=0x%lx, BufferSize=%ld\n", (long)wwo->AuServ, wwo->BufferSize);
913
914     TRACE("wBitsPerSample=%u nAvgBytesPerSec=%lu nSamplesPerSec=%lu nChannels=%u nBlockAlign=%u\n",
915           wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
916           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
917           wwo->format.wf.nBlockAlign);
918
919     return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
920 }
921
922 /**************************************************************************
923  *                              wodClose                        [internal]
924  */
925 static DWORD wodClose(WORD wDevID)
926 {
927     DWORD               ret = MMSYSERR_NOERROR;
928     WINE_WAVEOUT*       wwo;
929
930     TRACE("(%u);\n", wDevID);
931
932     if (wDevID >= MAX_WAVEOUTDRV || AuServ  == NULL)
933     {
934         WARN("bad device ID !\n");
935         return MMSYSERR_BADDEVICEID;
936     }
937
938     wwo = &WOutDev[wDevID];
939     if (wwo->lpQueuePtr) {
940         WARN("buffers still playing !\n");
941         ret = WAVERR_STILLPLAYING;
942     } else {
943         TRACE("imhere[3-close]\n");
944         if (wwo->hThread != INVALID_HANDLE_VALUE) {
945             NAS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
946         }
947
948         NAS_DestroyRingMessage(&wwo->msgRing);
949
950         NAS_CloseDevice(wwo);   /* close the stream and clean things up */
951
952         ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
953     }
954     return ret;
955 }
956
957 /**************************************************************************
958  *                              wodWrite                        [internal]
959  *
960  */
961 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
962 {
963     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
964
965     /* first, do the sanity checks... */
966     if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
967     {
968         WARN("bad dev ID !\n");
969         return MMSYSERR_BADDEVICEID;
970     }
971
972     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
973     {
974         TRACE("unprepared\n");
975         return WAVERR_UNPREPARED;
976     }
977
978     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
979     {
980         TRACE("still playing\n");
981         return WAVERR_STILLPLAYING;
982     }
983
984     lpWaveHdr->dwFlags &= ~WHDR_DONE;
985     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
986     lpWaveHdr->lpNext = 0;
987
988     TRACE("adding ring message\n");
989     NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
990
991     return MMSYSERR_NOERROR;
992 }
993
994 /**************************************************************************
995  *                              wodPrepare                      [internal]
996  */
997 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
998 {
999     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1000
1001     if (wDevID >= MAX_WAVEOUTDRV) {
1002         WARN("bad device ID !\n");
1003         return MMSYSERR_BADDEVICEID;
1004     }
1005
1006     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1007         return WAVERR_STILLPLAYING;
1008
1009     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1010     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1011     return MMSYSERR_NOERROR;
1012 }
1013
1014 /**************************************************************************
1015  *                              wodUnprepare                    [internal]
1016  */
1017 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1018 {
1019     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1020
1021     if (wDevID >= MAX_WAVEOUTDRV) {
1022         WARN("bad device ID !\n");
1023         return MMSYSERR_BADDEVICEID;
1024     }
1025
1026     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1027         return WAVERR_STILLPLAYING;
1028
1029     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1030     lpWaveHdr->dwFlags |= WHDR_DONE;
1031
1032     return MMSYSERR_NOERROR;
1033 }
1034
1035 /**************************************************************************
1036  *                      wodPause                                [internal]
1037  */
1038 static DWORD wodPause(WORD wDevID)
1039 {
1040     TRACE("(%u);!\n", wDevID);
1041
1042     if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1043     {
1044         WARN("bad device ID !\n");
1045         return MMSYSERR_BADDEVICEID;
1046     }
1047
1048     TRACE("imhere[3-PAUSING]\n");
1049     NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1050
1051     return MMSYSERR_NOERROR;
1052 }
1053
1054 /**************************************************************************
1055  *                      wodRestart                              [internal]
1056  */
1057 static DWORD wodRestart(WORD wDevID)
1058 {
1059     TRACE("(%u);\n", wDevID);
1060
1061     if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1062     {
1063         WARN("bad device ID !\n");
1064         return MMSYSERR_BADDEVICEID;
1065     }
1066
1067     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1068         TRACE("imhere[3-RESTARTING]\n");
1069         NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1070     }
1071
1072     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1073     /* FIXME: Myst crashes with this ... hmm -MM
1074        return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1075     */
1076
1077     return MMSYSERR_NOERROR;
1078 }
1079
1080 /**************************************************************************
1081  *                      wodReset                                [internal]
1082  */
1083 static DWORD wodReset(WORD wDevID)
1084 {
1085     TRACE("(%u);\n", wDevID);
1086
1087     if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1088     {
1089         WARN("bad device ID !\n");
1090         return MMSYSERR_BADDEVICEID;
1091     }
1092
1093     TRACE("imhere[3-RESET]\n");
1094     NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1095
1096     return MMSYSERR_NOERROR;
1097 }
1098
1099 /**************************************************************************
1100  *                              wodGetPosition                  [internal]
1101  */
1102 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1103 {
1104     int                 time;
1105     DWORD               val;
1106     WINE_WAVEOUT*       wwo;
1107
1108     TRACE("%u, %p, %lu);\n", wDevID, lpTime, uSize);
1109
1110     if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1111     {
1112         WARN("bad device ID !\n");
1113         return MMSYSERR_BADDEVICEID;
1114     }
1115
1116     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1117
1118     wwo = &WOutDev[wDevID];
1119 #if 0
1120     NAS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1121 #endif
1122     val = wwo->WrittenTotal;
1123
1124     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1125           lpTime->wType, wwo->format.wBitsPerSample,
1126           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1127           wwo->format.wf.nAvgBytesPerSec);
1128     TRACE("PlayedTotal=%lu\n", val);
1129
1130     switch (lpTime->wType) {
1131     case TIME_BYTES:
1132         lpTime->u.cb = val;
1133         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1134         break;
1135     case TIME_SAMPLES:
1136         lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample / wwo->format.wf.nChannels;
1137         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1138         break;
1139     case TIME_SMPTE:
1140         time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1141         lpTime->u.smpte.hour = time / 108000;
1142         time -= lpTime->u.smpte.hour * 108000;
1143         lpTime->u.smpte.min = time / 1800;
1144         time -= lpTime->u.smpte.min * 1800;
1145         lpTime->u.smpte.sec = time / 30;
1146         time -= lpTime->u.smpte.sec * 30;
1147         lpTime->u.smpte.frame = time;
1148         lpTime->u.smpte.fps = 30;
1149         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1150               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1151               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1152         break;
1153     default:
1154         FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1155         lpTime->wType = TIME_MS;
1156     case TIME_MS:
1157         lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1158         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1159         break;
1160     }
1161     return MMSYSERR_NOERROR;
1162 }
1163
1164 /**************************************************************************
1165  *                              wodBreakLoop                    [internal]
1166  */
1167 static DWORD wodBreakLoop(WORD wDevID)
1168 {
1169     TRACE("(%u);\n", wDevID);
1170
1171     if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1172     {
1173         WARN("bad device ID !\n");
1174         return MMSYSERR_BADDEVICEID;
1175     }
1176     NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1177     return MMSYSERR_NOERROR;
1178 }
1179
1180 /**************************************************************************
1181  *                              wodGetVolume                    [internal]
1182  */
1183 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1184 {
1185     DWORD left, right;
1186
1187     left = WOutDev[wDevID].volume_left;
1188     right = WOutDev[wDevID].volume_right;
1189
1190     TRACE("(%u, %p);\n", wDevID, lpdwVol);
1191
1192     *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1193
1194     return MMSYSERR_NOERROR;
1195 }
1196
1197 /**************************************************************************
1198  *                              wodSetVolume                    [internal]
1199  */
1200 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1201 {
1202     DWORD left, right;
1203
1204     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
1205     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1206
1207     TRACE("(%u, %08lX);\n", wDevID, dwParam);
1208
1209     WOutDev[wDevID].volume_left = left;
1210     WOutDev[wDevID].volume_right = right;
1211
1212     return MMSYSERR_NOERROR;
1213 }
1214
1215 /**************************************************************************
1216  *                              wodGetNumDevs                   [internal]
1217  */
1218 static  DWORD   wodGetNumDevs(void)
1219 {
1220     return MAX_WAVEOUTDRV;
1221 }
1222
1223 /**************************************************************************
1224  *                              wodMessage (WINENAS.@)
1225  */
1226 DWORD WINAPI NAS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1227                             DWORD dwParam1, DWORD dwParam2)
1228 {
1229     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1230
1231     switch (wMsg) {
1232     case DRVM_INIT:
1233     case DRVM_EXIT:
1234     case DRVM_ENABLE:
1235     case DRVM_DISABLE:
1236         /* FIXME: Pretend this is supported */
1237         return 0;
1238     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
1239     case WODM_CLOSE:            return wodClose         (wDevID);
1240     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1241     case WODM_PAUSE:            return wodPause         (wDevID);
1242     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
1243     case WODM_BREAKLOOP:        return wodBreakLoop     (wDevID);
1244     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1245     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1246     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSA)dwParam1,      dwParam2);
1247     case WODM_GETNUMDEVS:       return wodGetNumDevs    ();
1248     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
1249     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
1250     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1251     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1252     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
1253     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
1254     case WODM_RESTART:          return wodRestart       (wDevID);
1255     case WODM_RESET:            return wodReset         (wDevID);
1256
1257     case DRV_QUERYDSOUNDIFACE:  return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
1258     default:
1259         FIXME("unknown message %d!\n", wMsg);
1260     }
1261     return MMSYSERR_NOTSUPPORTED;
1262 }
1263
1264 /*======================================================================*
1265  *                  Low level DSOUND implementation                     *
1266  *======================================================================*/
1267 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1268 {
1269     /* we can't perform memory mapping as we don't have a file stream
1270         interface with nas like we do with oss */
1271     MESSAGE("This sound card s driver does not support direct access\n");
1272     MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1273     return MMSYSERR_NOTSUPPORTED;
1274 }
1275
1276 static int nas_init(void) {
1277     TRACE("NAS INIT\n");
1278     if (!(AuServ = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL)))
1279        return 0;
1280
1281     return 1;
1282 }
1283
1284 static int nas_finddev(WINE_WAVEOUT* wwo) {
1285    int i;
1286
1287     for (i = 0; i < AuServerNumDevices(wwo->AuServ); i++) {
1288         if ((AuDeviceKind(AuServerDevice(wwo->AuServ, i)) ==
1289              AuComponentKindPhysicalOutput) &&
1290              AuDeviceNumTracks(AuServerDevice(wwo->AuServ, i)) == wwo->format.wf.nChannels)
1291         {
1292             wwo->AuDev = AuDeviceIdentifier(AuServerDevice(wwo->AuServ, i));
1293             break;
1294         }
1295     }
1296
1297     if (wwo->AuDev == AuNone)
1298        return 0;
1299     return 1;
1300 }
1301
1302 static int nas_open(WINE_WAVEOUT* wwo) {
1303     AuElement elements[3];
1304
1305     if (!wwo->AuServ)
1306        return 0;
1307
1308     if (!nas_finddev(wwo))
1309        return 0;
1310
1311     if (!(wwo->AuFlow = AuCreateFlow(wwo->AuServ, NULL)))
1312        return 0;
1313
1314     wwo->BufferSize = FRAG_SIZE * FRAG_COUNT;
1315
1316     AuMakeElementImportClient(&elements[0], wwo->format.wf.nSamplesPerSec,
1317            wwo->format.wBitsPerSample == 16 ? AuFormatLinearSigned16LSB : AuFormatLinearUnsigned8,
1318            wwo->format.wf.nChannels, AuTrue, wwo->BufferSize, wwo->BufferSize / 2, 0, NULL);
1319
1320     AuMakeElementExportDevice(&elements[1], 0, wwo->AuDev, wwo->format.wf.nSamplesPerSec,
1321                               AuUnlimitedSamples, 0, NULL);
1322
1323     AuSetElements(wwo->AuServ, wwo->AuFlow, AuTrue, 2, elements, NULL);
1324
1325     AuRegisterEventHandler(wwo->AuServ, AuEventHandlerIDMask, 0, wwo->AuFlow,
1326                            event_handler, (AuPointer) wwo);
1327
1328
1329     wwo->PlayedTotal = 0;
1330     wwo->WrittenTotal = 0;
1331     wwo->open = 1;
1332
1333     wwo->BufferUsed = 0;
1334     wwo->writeBytes = 0;
1335     wwo->freeBytes = 0;
1336     wwo->sendBytes = 0;
1337     wwo->SoundBuffer = NULL;
1338     wwo->FlowStarted = 0;
1339
1340     AuStartFlow(wwo->AuServ, wwo->AuFlow, NULL);
1341     AuPauseFlow(wwo->AuServ, wwo->AuFlow, NULL);
1342     wwo->FlowStarted = 1;
1343
1344     return 1;
1345 }
1346
1347 static AuBool
1348 event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
1349 {
1350   WINE_WAVEOUT *wwo = (WINE_WAVEOUT *)hnd->data;
1351         switch (ev->type) {
1352
1353         case AuEventTypeElementNotify: {
1354                 AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
1355
1356
1357                 switch (event->kind) {
1358                    case AuElementNotifyKindLowWater:
1359                      wwo->freeBytes += event->num_bytes;
1360                      if (wwo->writeBytes > 0)
1361                         wwo->sendBytes += event->num_bytes;
1362                     if (wwo->freeBytes && wwo->BufferUsed)
1363                         nas_send_buffer(wwo);
1364                    break;
1365
1366                    case AuElementNotifyKindState:
1367                      TRACE("ev: kind %s state %s->%s reason %s numbytes %ld freeB %lu\n",
1368                                      nas_elementnotify_kind(event->kind),
1369                                      nas_state(event->prev_state),
1370                                      nas_state(event->cur_state),
1371                                      nas_reason(event->reason),
1372                                      event->num_bytes, wwo->freeBytes);
1373
1374                      if (event->cur_state ==  AuStatePause && event->reason != AuReasonUser) {
1375                         wwo->freeBytes += event->num_bytes;
1376                         if (wwo->writeBytes > 0)
1377                            wwo->sendBytes += event->num_bytes;
1378                         if (wwo->sendBytes > wwo->writeBytes)
1379                            wwo->sendBytes = wwo->writeBytes;
1380                        if (wwo->freeBytes && wwo->BufferUsed)
1381                            nas_send_buffer(wwo);
1382                      }
1383                    break;
1384                 }
1385            }
1386         }
1387         return AuTrue;
1388 }
1389
1390 static void
1391 buffer_resize(WINE_WAVEOUT* wwo, int len)
1392 {
1393         void *newbuf = malloc(wwo->BufferUsed + len);
1394         void *oldbuf = wwo->SoundBuffer;
1395         memcpy(newbuf, oldbuf, wwo->BufferUsed);
1396         wwo->SoundBuffer = newbuf;
1397         if (oldbuf != NULL)
1398            free(oldbuf);
1399 }
1400
1401 static int nas_add_buffer(WINE_WAVEOUT* wwo) {
1402     int len = wwo->lpPlayPtr->dwBufferLength;
1403
1404     buffer_resize(wwo, len);
1405     memcpy(wwo->SoundBuffer + wwo->BufferUsed, wwo->lpPlayPtr->lpData, len);
1406     wwo->BufferUsed += len;
1407     wwo->WrittenTotal += len;
1408     return len;
1409 }
1410
1411 static int nas_send_buffer(WINE_WAVEOUT* wwo) {
1412   int oldb , len;
1413   char *ptr, *newdata;
1414   newdata = NULL;
1415   oldb = len = 0;
1416
1417   if (wwo->freeBytes <= 0)
1418      return 0;
1419
1420   if (wwo->SoundBuffer == NULL || wwo->BufferUsed == 0) {
1421      return 0;
1422   }
1423
1424   if (wwo->BufferUsed <= wwo->freeBytes) {
1425      len = wwo->BufferUsed;
1426      ptr = wwo->SoundBuffer;
1427   } else {
1428      len = wwo->freeBytes;
1429      ptr = malloc(len);
1430      memcpy(ptr,wwo->SoundBuffer,len);
1431      newdata = malloc(wwo->BufferUsed - len);
1432      memcpy(newdata, wwo->SoundBuffer + len, wwo->BufferUsed - len);
1433   }
1434
1435  TRACE("envoye de %d bytes / %lu bytes / freeBytes %lu\n", len, wwo->BufferUsed, wwo->freeBytes);
1436
1437  AuWriteElement(wwo->AuServ, wwo->AuFlow, 0, len, ptr, AuFalse, NULL);
1438
1439  wwo->BufferUsed -= len;
1440  wwo->freeBytes -= len;
1441  wwo->writeBytes += len;
1442
1443  free(ptr);
1444
1445  wwo->SoundBuffer = NULL;
1446
1447  if (newdata != NULL)
1448     wwo->SoundBuffer = newdata;
1449
1450  return len;
1451 }
1452
1453 static int nas_free(WINE_WAVEOUT* wwo)
1454 {
1455
1456   if (!wwo->FlowStarted && wwo->BufferUsed) {
1457      AuStartFlow(wwo->AuServ, wwo->AuFlow, NULL);
1458      wwo->FlowStarted = 1;
1459   }
1460
1461   while (wwo->BufferUsed || wwo->writeBytes != wwo->sendBytes) {
1462     if (wwo->freeBytes)
1463        nas_send_buffer(wwo);
1464     AuHandleEvents(wwo->AuServ);
1465   }
1466
1467   AuFlush(wwo->AuServ);
1468   return TRUE;
1469 }
1470
1471 static int nas_close(WINE_WAVEOUT* wwo)
1472 {
1473   AuEvent ev;
1474
1475   nas_free(wwo);
1476
1477   AuStopFlow(wwo->AuServ, wwo->AuFlow, NULL);
1478   AuDestroyFlow(wwo->AuServ, wwo->AuFlow, NULL);
1479   AuFlush(wwo->AuServ);
1480   AuNextEvent(wwo->AuServ, AuTrue, &ev);
1481   AuDispatchEvent(wwo->AuServ, &ev);
1482
1483   wwo->AuFlow = 0;
1484   wwo->open = 0;
1485   wwo->BufferUsed = 0;
1486   wwo->freeBytes = 0;
1487   wwo->SoundBuffer = NULL;
1488   return 1;
1489 }
1490
1491 static int nas_end(void)
1492 {
1493   AuCloseServer(AuServ);
1494   AuServ = 0;
1495   return 1;
1496 }
1497
1498 #else /* !HAVE_NAS */
1499
1500 /**************************************************************************
1501  *                              wodMessage (WINENAS.@)
1502  */
1503 DWORD WINAPI NAS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
1504 {
1505     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1506     return MMSYSERR_NOTENABLED;
1507 }
1508 #endif