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