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