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