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