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