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