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