Reverse the order for deleting the items in resetcontent to correctly
[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  *      does something need to be done in for WaveIn DirectSound?
36  */
37
38 #include "config.h"
39 #include "wine/port.h"
40
41 #include <math.h>
42 #include <stdlib.h>
43 #include <stdarg.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 "winbase.h"
52 #include "wingdi.h"
53 #include "winerror.h"
54 #include "winuser.h"
55 #include "mmddk.h"
56 #include "dsound.h"
57 #include "dsdriver.h"
58 #include "arts.h"
59 #include "wine/unicode.h"
60 #include "wine/debug.h"
61
62 WINE_DEFAULT_DEBUG_CHANNEL(wave);
63
64 #ifdef HAVE_ARTS
65
66 #include <artsc.h>
67
68 /* The following four #defines allow you to fine-tune the packet
69  * settings in arts for better low-latency support. You must also
70  * adjust the latency in the KDE arts control panel. I recommend 4
71  * fragments, 1024 bytes.
72  *
73  * The following is from the ARTS documentation and explains what CCCC
74  * and SSSS mean:
75  * 
76  * @li ARTS_P_PACKET_SETTINGS (rw) This is a way to configure packet
77  * size & packet count at the same time.  The format is 0xCCCCSSSS,
78  * where 2^SSSS is the packet size, and CCCC is the packet count. Note
79  * that when writing this, you don't necessarily get the settings you
80  * requested.
81  */
82 #define WAVEOUT_PACKET_CCCC 0x000C
83 #define WAVEOUT_PACKET_SSSS 0x0008
84 #define WAVEIN_PACKET_CCCC  0x000C
85 #define WAVEIN_PACKET_SSSS  0x0008
86
87 #define BUFFER_REFILL_THRESHOLD 4
88
89 #define WAVEOUT_PACKET_SETTINGS ((WAVEOUT_PACKET_CCCC << 16) | (WAVEOUT_PACKET_SSSS))
90 #define WAVEIN_PACKET_SETTINGS  ((WAVEIN_PACKET_CCCC << 16) | (WAVEIN_PACKET_SSSS))
91
92 #define MAX_WAVEOUTDRV  (10)
93 #define MAX_WAVEINDRV   (10)
94
95 /* state diagram for waveOut writing:
96  *
97  * +---------+-------------+---------------+---------------------------------+
98  * |  state  |  function   |     event     |            new state            |
99  * +---------+-------------+---------------+---------------------------------+
100  * |         | open()      |               | STOPPED                         |
101  * | PAUSED  | write()     |               | PAUSED                          |
102  * | STOPPED | write()     | <thrd create> | PLAYING                         |
103  * | PLAYING | write()     | HEADER        | PLAYING                         |
104  * | (other) | write()     | <error>       |                                 |
105  * | (any)   | pause()     | PAUSING       | PAUSED                          |
106  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
107  * | (any)   | reset()     | RESETTING     | STOPPED                         |
108  * | (any)   | close()     | CLOSING       | CLOSED                          |
109  * +---------+-------------+---------------+---------------------------------+
110  */
111
112 /* states of the playing device */
113 #define WINE_WS_PLAYING         0
114 #define WINE_WS_PAUSED          1
115 #define WINE_WS_STOPPED         2
116 #define WINE_WS_CLOSED          3
117
118 /* events to be send to device */
119 enum win_wm_message {
120     WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
121     WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
122 };
123
124 typedef struct {
125     enum win_wm_message         msg;    /* message identifier */
126     DWORD                       param;  /* parameter for this message */
127     HANDLE                      hEvent; /* if message is synchronous, handle of event for synchro */
128 } RING_MSG;
129
130 /* implement an in-process message ring for better performance
131  * (compared to passing thru the server)
132  * this ring will be used by the input (resp output) record (resp playback) routine
133  */
134 #define ARTS_RING_BUFFER_INCREMENT      64
135 typedef struct {
136     RING_MSG                    * messages;
137     int                         ring_buffer_size;
138     int                         msg_tosave;
139     int                         msg_toget;
140     HANDLE                      msg_event;
141     CRITICAL_SECTION            msg_crst;
142 } ARTS_MSG_RING;
143
144 typedef struct {
145     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
146     WAVEOPENDESC                waveDesc;
147     WORD                        wFlags;
148     PCMWAVEFORMAT               format;
149     WAVEOUTCAPSW                caps;
150     char                        interface_name[32];
151
152     DWORD                       dwSleepTime;            /* Num of milliseconds to sleep between filling the dsp buffers */
153
154     /* arts information */
155     arts_stream_t               play_stream;            /* the stream structure we get from arts when opening a stream for playing */
156     DWORD                       dwBufferSize;           /* size of whole buffer in bytes */
157     int                         packetSettings;
158
159     char*                       sound_buffer;
160     long                        buffer_size;
161
162     DWORD                       volume_left;            /* volume control information */
163     DWORD                       volume_right;
164
165     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
166     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
167     DWORD                       dwPartialOffset;        /* Offset of not yet written bytes in lpPlayPtr */
168
169     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
170     DWORD                       dwLoops;                /* private copy of loop counter */
171
172     DWORD                       dwPlayedTotal;          /* number of bytes actually played since opening */
173     DWORD                       dwWrittenTotal;         /* number of bytes written to the audio device since opening */
174
175     /* synchronization stuff */
176     HANDLE                      hStartUpEvent;
177     HANDLE                      hThread;
178     DWORD                       dwThreadID;
179     ARTS_MSG_RING               msgRing;
180 } WINE_WAVEOUT;
181
182 typedef struct {
183     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
184     WAVEOPENDESC                waveDesc;
185     WORD                        wFlags;
186     PCMWAVEFORMAT               format;
187     WAVEINCAPSW                 caps;
188     char                        interface_name[32];
189
190     /* arts information */
191     arts_stream_t               record_stream;          /* the stream structure we get from arts when opening a stream for recording */
192     int                         packetSettings;
193
194     LPWAVEHDR                   lpQueuePtr;
195     DWORD                       dwRecordedTotal;
196
197     /* synchronization stuff */
198     HANDLE                      hStartUpEvent;
199     HANDLE                      hThread;
200     DWORD                       dwThreadID;
201     ARTS_MSG_RING               msgRing;
202 } WINE_WAVEIN;
203
204 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
205 static WINE_WAVEIN      WInDev    [MAX_WAVEINDRV];
206
207 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
208 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
209
210 /* These strings used only for tracing */
211 static const char *wodPlayerCmdString[] = {
212     "WINE_WM_PAUSING",
213     "WINE_WM_RESTARTING",
214     "WINE_WM_RESETTING",
215     "WINE_WM_HEADER",
216     "WINE_WM_UPDATE",
217     "WINE_WM_BREAKLOOP",
218     "WINE_WM_CLOSING",
219     "WINE_WM_STARTING",
220     "WINE_WM_STOPPING",
221 };
222
223 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
224                              PCMWAVEFORMAT* format)
225 {
226     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
227           lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
228           format->wf.nChannels, format->wf.nAvgBytesPerSec);
229     TRACE("Position in bytes=%lu\n", position);
230
231     switch (lpTime->wType) {
232     case TIME_SAMPLES:
233         lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
234         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
235         break;
236     case TIME_MS:
237         lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
238         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
239         break;
240     case TIME_SMPTE:
241         position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
242         lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
243         position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
244         lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
245         lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
246         lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
247         lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
248         lpTime->u.smpte.fps = 30;
249         lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
250         position -= lpTime->u.smpte.frame * format->wf.nSamplesPerSec / lpTime->u.smpte.fps;
251         if (position != 0)
252         {
253             /* Round up */
254             lpTime->u.smpte.frame++;
255         }
256         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
257               lpTime->u.smpte.hour, lpTime->u.smpte.min,
258               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
259         break;
260     default:
261         WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
262         lpTime->wType = TIME_BYTES;
263         /* fall through */
264     case TIME_BYTES:
265         lpTime->u.cb = position;
266         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
267         break;
268     }
269     return MMSYSERR_NOERROR;
270 }
271
272 /*======================================================================*
273  *                  Low level WAVE implementation                       *
274  *======================================================================*/
275
276 /* Volume functions derived from Alsaplayer source */
277 /* length is the number of 16 bit samples */
278 static void volume_effect16(void *bufin, void* bufout, int length, int left,
279                             int right, int nChannels)
280 {
281   short *d_out = (short *)bufout;
282   short *d_in = (short *)bufin;
283   int i, v;
284
285 /*
286   TRACE("length == %d, nChannels == %d\n", length, nChannels);
287 */
288
289   if (right == -1) right = left;
290
291   for(i = 0; i < length; i+=(nChannels))
292   {
293     v = (int) ((*(d_in++) * left) / 100);
294     *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
295     if(nChannels == 2)
296     {
297       v = (int) ((*(d_in++) * right) / 100);
298       *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
299     }
300   }
301 }
302
303 /* length is the number of 8 bit samples */
304 static void volume_effect8(void *bufin, void* bufout, int length, int left,
305                            int right, int       nChannels)
306 {
307   BYTE *d_out = (BYTE *)bufout;
308   BYTE *d_in = (BYTE *)bufin;
309   int i, v;
310
311 /*
312   TRACE("length == %d, nChannels == %d\n", length, nChannels);
313 */
314
315   if (right == -1) right = left;
316
317   for(i = 0; i < length; i+=(nChannels))
318   {
319     v = (BYTE) ((*(d_in++) * left) / 100);
320     *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
321     if(nChannels == 2)
322     {
323       v = (BYTE) ((*(d_in++) * right) / 100);
324       *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
325     }
326   }
327 }
328
329 /******************************************************************
330  *              ARTS_CloseWaveOutDevice
331  *
332  */
333 static void ARTS_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
334 {
335   arts_close_stream(wwo->play_stream);  /* close the arts stream */
336   wwo->play_stream = (arts_stream_t*)-1;
337
338   /* free up the buffer we use for volume and reset the size */
339   HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
340   wwo->sound_buffer = NULL;
341   wwo->buffer_size = 0;
342 }
343
344 /******************************************************************
345  *              ARTS_CloseWaveInDevice
346  *
347  */
348 static void ARTS_CloseWaveInDevice(WINE_WAVEIN* wwi)
349 {
350   arts_close_stream(wwi->record_stream);        /* close the arts stream */
351   wwi->record_stream = (arts_stream_t*)-1;
352 }
353
354 /******************************************************************
355  *              ARTS_Init
356  */
357 static int      ARTS_Init(void)
358 {
359   return arts_init();  /* initialize arts and return errorcode */
360 }
361
362 /******************************************************************
363  *              ARTS_WaveClose
364  */
365 LONG            ARTS_WaveClose(void)
366 {
367     int iDevice;
368
369     /* close all open devices */
370     for(iDevice = 0; iDevice < MAX_WAVEOUTDRV; iDevice++)
371     {
372       if(WOutDev[iDevice].play_stream != (arts_stream_t*)-1)
373       {
374         ARTS_CloseWaveOutDevice(&WOutDev[iDevice]);
375       }
376     }
377
378     for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
379     {
380       if(WInDev[iDevice].record_stream != (arts_stream_t*)-1)
381       {
382         ARTS_CloseWaveInDevice(&WInDev[iDevice]);
383       }
384     }
385
386     arts_free();    /* free up arts */
387     return 1;
388 }
389
390 /******************************************************************
391  *              ARTS_WaveInit
392  *
393  * Initialize internal structures from ARTS server info
394  */
395 LONG ARTS_WaveInit(void)
396 {
397     int         i;
398     int         errorcode;
399
400     TRACE("called\n");
401
402     if ((errorcode = ARTS_Init()) < 0)
403     {
404         WARN("arts_init() failed (%d)\n", errorcode);
405         return -1;
406     }
407
408     /* initialize all device handles to -1 */
409     for (i = 0; i < MAX_WAVEOUTDRV; ++i)
410     {
411         static const WCHAR ini[] = {'a','R','t','s',' ','W','a','v','e','O','u','t','D','r','i','v','e','r',0};
412
413         WOutDev[i].play_stream = (arts_stream_t*)-1;
414         memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out
415                                                         caps values */
416         WOutDev[i].caps.wMid = 0x00FF;  /* Manufac ID */
417         WOutDev[i].caps.wPid = 0x0001;  /* Product ID */
418         strcpyW(WOutDev[i].caps.szPname, ini);
419         snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "winearts: %d", i);
420
421         WOutDev[i].caps.vDriverVersion = 0x0100;
422         WOutDev[i].caps.dwFormats = 0x00000000;
423         WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
424
425         WOutDev[i].caps.wChannels = 2;
426         WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME;
427
428         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
429         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
430         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
431         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
432         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
433         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
434         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
435         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
436         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
437         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
438         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
439         WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
440     }
441
442     for (i = 0; i < MAX_WAVEINDRV; ++i)
443     {
444         static const WCHAR ini[] = {'a','R','t','s',' ','W','a','v','e','I','n',' ','D','r','i','v','e','r',0};
445
446         WInDev[i].record_stream = (arts_stream_t*)-1;
447         memset(&WInDev[i].caps, 0, sizeof(WInDev[i].caps)); /* zero out
448                                                         caps values */
449         WInDev[i].caps.wMid = 0x00FF;
450         WInDev[i].caps.wPid = 0x0001;
451         strcpyW(WInDev[i].caps.szPname, ini);
452         snprintf(WInDev[i].interface_name, sizeof(WInDev[i].interface_name), "winearts: %d", i);
453
454         WInDev[i].caps.vDriverVersion = 0x0100;
455         WInDev[i].caps.dwFormats = 0x00000000;
456
457         WInDev[i].caps.wChannels = 2;
458
459         WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
460         WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
461         WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
462         WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
463         WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
464         WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
465         WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
466         WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
467         WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
468         WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
469         WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
470         WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
471
472         WInDev[i].caps.wReserved1 = 0;
473     }
474     return 0;
475 }
476
477 /******************************************************************
478  *              ARTS_InitRingMessage
479  *
480  * Initialize the ring of messages for passing between driver's caller and playback/record
481  * thread
482  */
483 static int ARTS_InitRingMessage(ARTS_MSG_RING* mr)
484 {
485     mr->msg_toget = 0;
486     mr->msg_tosave = 0;
487     mr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
488     mr->ring_buffer_size = ARTS_RING_BUFFER_INCREMENT;
489     mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG));
490     InitializeCriticalSection(&mr->msg_crst);
491     return 0;
492 }
493
494 /******************************************************************
495  *              ARTS_DestroyRingMessage
496  *
497  */
498 static int ARTS_DestroyRingMessage(ARTS_MSG_RING* mr)
499 {
500     CloseHandle(mr->msg_event);
501     HeapFree(GetProcessHeap(),0,mr->messages);
502     mr->messages=NULL;
503     DeleteCriticalSection(&mr->msg_crst);
504     return 0;
505 }
506
507 /******************************************************************
508  *              ARTS_AddRingMessage
509  *
510  * Inserts a new message into the ring (should be called from DriverProc derivated routines)
511  */
512 static int ARTS_AddRingMessage(ARTS_MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
513 {
514     HANDLE      hEvent = INVALID_HANDLE_VALUE;
515
516     EnterCriticalSection(&mr->msg_crst);
517     if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
518     {
519         int old_ring_buffer_size = mr->ring_buffer_size;
520         mr->ring_buffer_size += ARTS_RING_BUFFER_INCREMENT;
521         TRACE("mr->ring_buffer_size=%d\n",mr->ring_buffer_size);
522         mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
523         /* Now we need to rearrange the ring buffer so that the new
524            buffers just allocated are in between mr->msg_tosave and
525            mr->msg_toget.
526         */
527         if (mr->msg_tosave < mr->msg_toget)
528         {
529             memmove(&(mr->messages[mr->msg_toget + ARTS_RING_BUFFER_INCREMENT]),
530                     &(mr->messages[mr->msg_toget]),
531                     sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
532                     );
533             mr->msg_toget += ARTS_RING_BUFFER_INCREMENT;
534         }
535     }
536     if (wait)
537     {
538         hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
539         if (hEvent == INVALID_HANDLE_VALUE)
540         {
541             ERR("can't create event !?\n");
542             LeaveCriticalSection(&mr->msg_crst);
543             return 0;
544         }
545         if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
546             FIXME("two fast messages in the queue!!!!\n");
547
548         /* fast messages have to be added at the start of the queue */
549         mr->msg_toget = (mr->msg_toget + mr->ring_buffer_size - 1) % mr->ring_buffer_size;
550
551         mr->messages[mr->msg_toget].msg = msg;
552         mr->messages[mr->msg_toget].param = param;
553         mr->messages[mr->msg_toget].hEvent = hEvent;
554     }
555     else
556     {
557         mr->messages[mr->msg_tosave].msg = msg;
558         mr->messages[mr->msg_tosave].param = param;
559         mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
560         mr->msg_tosave = (mr->msg_tosave + 1) % mr->ring_buffer_size;
561     }
562
563     LeaveCriticalSection(&mr->msg_crst);
564
565     SetEvent(mr->msg_event);    /* signal a new message */
566
567     if (wait)
568     {
569         /* wait for playback/record thread to have processed the message */
570         WaitForSingleObject(hEvent, INFINITE);
571         CloseHandle(hEvent);
572     }
573
574     return 1;
575 }
576
577 /******************************************************************
578  *              ARTS_RetrieveRingMessage
579  *
580  * Get a message from the ring. Should be called by the playback/record thread.
581  */
582 static int ARTS_RetrieveRingMessage(ARTS_MSG_RING* mr,
583                                    enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
584 {
585     EnterCriticalSection(&mr->msg_crst);
586
587     if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
588     {
589         LeaveCriticalSection(&mr->msg_crst);
590         return 0;
591     }
592
593     *msg = mr->messages[mr->msg_toget].msg;
594     mr->messages[mr->msg_toget].msg = 0;
595     *param = mr->messages[mr->msg_toget].param;
596     *hEvent = mr->messages[mr->msg_toget].hEvent;
597     mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size;
598     LeaveCriticalSection(&mr->msg_crst);
599     return 1;
600 }
601
602 /*======================================================================*
603  *                  Low level WAVE OUT implementation                   *
604  *======================================================================*/
605
606 /**************************************************************************
607  *                      wodNotifyClient                 [internal]
608  */
609 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
610 {
611     TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
612
613     switch (wMsg) {
614     case WOM_OPEN:
615     case WOM_CLOSE:
616     case WOM_DONE:
617         if (wwo->wFlags != DCB_NULL &&
618             !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
619                             wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
620             WARN("can't notify client !\n");
621             return MMSYSERR_ERROR;
622         }
623         break;
624     default:
625         FIXME("Unknown callback message %u\n", wMsg);
626         return MMSYSERR_INVALPARAM;
627     }
628     return MMSYSERR_NOERROR;
629 }
630
631 /**************************************************************************
632  *                              wodUpdatePlayedTotal    [internal]
633  *
634  */
635 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
636 {
637     /* total played is the bytes written less the bytes to write ;-) */
638     wwo->dwPlayedTotal = wwo->dwWrittenTotal -
639         (wwo->dwBufferSize -
640         arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE));
641
642     return TRUE;
643 }
644
645 /**************************************************************************
646  *                              wodPlayer_BeginWaveHdr          [internal]
647  *
648  * Makes the specified lpWaveHdr the currently playing wave header.
649  * If the specified wave header is a begin loop and we're not already in
650  * a loop, setup the loop.
651  */
652 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
653 {
654     wwo->lpPlayPtr = lpWaveHdr;
655
656     if (!lpWaveHdr) return;
657
658     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
659         if (wwo->lpLoopPtr) {
660             WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
661             TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
662         } else {
663             TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
664             wwo->lpLoopPtr = lpWaveHdr;
665             /* Windows does not touch WAVEHDR.dwLoops,
666              * so we need to make an internal copy */
667             wwo->dwLoops = lpWaveHdr->dwLoops;
668         }
669     }
670     wwo->dwPartialOffset = 0;
671 }
672
673 /**************************************************************************
674  *                              wodPlayer_PlayPtrNext           [internal]
675  *
676  * Advance the play pointer to the next waveheader, looping if required.
677  */
678 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
679 {
680     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
681
682     wwo->dwPartialOffset = 0;
683     if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
684         /* We're at the end of a loop, loop if required */
685         if (--wwo->dwLoops > 0) {
686             wwo->lpPlayPtr = wwo->lpLoopPtr;
687         } else {
688             /* Handle overlapping loops correctly */
689             if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
690                 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
691                 /* shall we consider the END flag for the closing loop or for
692                  * the opening one or for both ???
693                  * code assumes for closing loop only
694                  */
695             } else {
696                 lpWaveHdr = lpWaveHdr->lpNext;
697             }
698             wwo->lpLoopPtr = NULL;
699             wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
700         }
701     } else {
702         /* We're not in a loop.  Advance to the next wave header */
703         wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
704     }
705
706     return lpWaveHdr;
707 }
708
709 /**************************************************************************
710  *                           wodPlayer_NotifyWait               [internal]
711  * Returns the number of milliseconds to wait before attempting to notify
712  * completion of the specified wavehdr.
713  * This is based on the number of bytes remaining to be written in the
714  * wave.
715  */
716 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
717 {
718     DWORD dwMillis;
719
720     if(lpWaveHdr->reserved < wwo->dwPlayedTotal)
721     {
722         dwMillis = 1;
723     }
724     else
725     {
726         dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
727         if(!dwMillis) dwMillis = 1;
728     }
729
730     TRACE("dwMillis = %ld\n", dwMillis);
731
732     return dwMillis;
733 }
734
735
736 /**************************************************************************
737  *                           wodPlayer_WriteMaxFrags            [internal]
738  * Writes the maximum number of bytes possible to the DSP and returns
739  * the number of bytes written.
740  */
741 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
742 {
743     /* Only attempt to write to free bytes */
744     DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
745     int toWrite = min(dwLength, *bytes);
746     int written;
747
748     TRACE("Writing wavehdr %p.%lu[%lu]\n",
749           wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
750
751     if (dwLength == 0)
752     {
753         wodPlayer_PlayPtrNext(wwo);
754         return 0;
755     }
756
757     /* see if our buffer isn't large enough for the data we are writing */
758     if(wwo->buffer_size < toWrite)
759     {
760       if(wwo->sound_buffer)
761       {
762         wwo->sound_buffer = HeapReAlloc(GetProcessHeap(), 0, wwo->sound_buffer, toWrite);
763         wwo->buffer_size = toWrite;
764       }
765     }
766
767     /* if we don't have a buffer then get one */
768     if(!wwo->sound_buffer)
769     {
770       /* allocate some memory for the buffer */
771       wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, toWrite);
772       wwo->buffer_size = toWrite;
773     }
774
775     /* if we don't have a buffer then error out */
776     if(!wwo->sound_buffer)
777     {
778       ERR("error allocating sound_buffer memory\n");
779       return 0;
780     }
781
782     TRACE("toWrite == %d\n", toWrite);
783
784     /* apply volume to the bits */
785     /* for single channel audio streams we only use the LEFT volume */
786     if(wwo->format.wBitsPerSample == 16)
787     {
788       /* apply volume to the buffer we are about to send */
789       /* divide toWrite(bytes) by 2 as volume processes by 16 bits */
790       volume_effect16(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
791                 wwo->sound_buffer, toWrite>>1, wwo->volume_left,
792                 wwo->volume_right, wwo->format.wf.nChannels);
793     } else if(wwo->format.wBitsPerSample == 8)
794     {
795       /* apply volume to the buffer we are about to send */
796       volume_effect8(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
797                 wwo->sound_buffer, toWrite, wwo->volume_left,
798                 wwo->volume_right, wwo->format.wf.nChannels);
799     } else
800     {
801       FIXME("unsupported wwo->format.wBitsPerSample of %d\n",
802         wwo->format.wBitsPerSample);
803     }
804
805     /* send the audio data to arts for playing */
806     written = arts_write(wwo->play_stream, wwo->sound_buffer, toWrite);
807
808     TRACE("written = %d\n", written);
809
810     if (written <= 0) 
811     {
812       *bytes = 0; /* apparently arts is actually full */
813       return written; /* if we wrote nothing just return */
814     }
815
816     if (written >= dwLength)
817         wodPlayer_PlayPtrNext(wwo);   /* If we wrote all current wavehdr, skip to the next one */
818     else
819         wwo->dwPartialOffset += written;    /* Remove the amount written */
820
821     if (written < toWrite)
822         *bytes = 0;
823     else
824         *bytes -= written;
825
826     wwo->dwWrittenTotal += written; /* update stats on this wave device */
827
828     return written; /* return the number of bytes written */
829 }
830
831
832 /**************************************************************************
833  *                              wodPlayer_NotifyCompletions     [internal]
834  *
835  * Notifies and remove from queue all wavehdrs which have been played to
836  * the speaker (ie. they have cleared the audio device).  If force is true,
837  * we notify all wavehdrs and remove them all from the queue even if they
838  * are unplayed or part of a loop.
839  */
840 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
841 {
842     LPWAVEHDR           lpWaveHdr;
843
844     if (wwo->lpQueuePtr) {
845         TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), reserved=(%ld), dwWrittenTotal=(%ld), force=(%d)\n", 
846               wwo->lpQueuePtr,
847               wwo->lpPlayPtr,
848               wwo->lpLoopPtr,
849               wwo->lpQueuePtr->reserved,
850               wwo->dwWrittenTotal,
851               force);
852     } else {
853         TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p),  dwWrittenTotal=(%ld), force=(%d)\n", 
854               wwo->lpQueuePtr,
855               wwo->lpPlayPtr,
856               wwo->lpLoopPtr,
857               wwo->dwWrittenTotal,
858               force);
859     }
860
861     /* Start from lpQueuePtr and keep notifying until:
862      * - we hit an unwritten wavehdr
863      * - we hit the beginning of a running loop
864      * - we hit a wavehdr which hasn't finished playing
865      */
866     while ((lpWaveHdr = wwo->lpQueuePtr) &&
867            (force ||
868             (lpWaveHdr != wwo->lpPlayPtr &&
869              lpWaveHdr != wwo->lpLoopPtr &&
870              lpWaveHdr->reserved <= wwo->dwWrittenTotal))) {
871
872         wwo->lpQueuePtr = lpWaveHdr->lpNext;
873
874         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
875         lpWaveHdr->dwFlags |= WHDR_DONE;
876
877         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
878     }
879     return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
880         wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
881 }
882
883 /**************************************************************************
884  *                              wodPlayer_Reset                 [internal]
885  *
886  * wodPlayer helper. Resets current output stream.
887  */
888 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
889 {
890     wodUpdatePlayedTotal(wwo);
891
892     wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */
893
894     /* we aren't able to flush any data that has already been written */
895     /* to arts, otherwise we would do the flushing here */
896
897     if (reset) {
898         enum win_wm_message     msg;
899         DWORD                   param;
900         HANDLE                  ev;
901
902         /* remove any buffer */
903         wodPlayer_NotifyCompletions(wwo, TRUE);
904
905         wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
906         wwo->state = WINE_WS_STOPPED;
907         wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
908
909         wwo->dwPartialOffset = 0;        /* Clear partial wavehdr */
910
911         /* remove any existing message in the ring */
912         EnterCriticalSection(&wwo->msgRing.msg_crst);
913
914         /* return all pending headers in queue */
915         while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
916         {
917             TRACE("flushing msg\n");
918             if (msg != WINE_WM_HEADER)
919             {
920                 FIXME("shouldn't have headers left\n");
921                 SetEvent(ev);
922                 continue;
923             }
924             ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
925             ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
926
927             wodNotifyClient(wwo, WOM_DONE, param, 0);
928         }
929         ResetEvent(wwo->msgRing.msg_event);
930         LeaveCriticalSection(&wwo->msgRing.msg_crst);
931     } else {
932         if (wwo->lpLoopPtr) {
933             /* complicated case, not handled yet (could imply modifying the loop counter */
934             FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
935             wwo->lpPlayPtr = wwo->lpLoopPtr;
936             wwo->dwPartialOffset = 0;
937             wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
938         } else {
939             /* the data already written is going to be played, so take */
940             /* this fact into account here */
941             wwo->dwPlayedTotal = wwo->dwWrittenTotal;
942         }
943         wwo->state = WINE_WS_PAUSED;
944     }
945 }
946
947 /**************************************************************************
948  *                    wodPlayer_ProcessMessages                 [internal]
949  */
950 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
951 {
952     LPWAVEHDR           lpWaveHdr;
953     enum win_wm_message msg;
954     DWORD               param;
955     HANDLE              ev;
956
957     while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
958         TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
959         switch (msg) {
960         case WINE_WM_PAUSING:
961             wodPlayer_Reset(wwo, FALSE);
962             SetEvent(ev);
963             break;
964         case WINE_WM_RESTARTING:
965             wwo->state = WINE_WS_PLAYING;
966             SetEvent(ev);
967             break;
968         case WINE_WM_HEADER:
969             lpWaveHdr = (LPWAVEHDR)param;
970
971             /* insert buffer at the end of queue */
972             {
973                 LPWAVEHDR*      wh;
974                 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
975                 *wh = lpWaveHdr;
976             }
977             if (!wwo->lpPlayPtr)
978                 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
979             if (wwo->state == WINE_WS_STOPPED)
980                 wwo->state = WINE_WS_PLAYING;
981             break;
982         case WINE_WM_RESETTING:
983             wodPlayer_Reset(wwo, TRUE);
984             SetEvent(ev);
985             break;
986         case WINE_WM_UPDATE:
987             wodUpdatePlayedTotal(wwo);
988             SetEvent(ev);
989             break;
990         case WINE_WM_BREAKLOOP:
991             if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
992                 /* ensure exit at end of current loop */
993                 wwo->dwLoops = 1;
994             }
995             SetEvent(ev);
996             break;
997         case WINE_WM_CLOSING:
998             /* sanity check: this should not happen since the device must have been reset before */
999             if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1000             wwo->hThread = 0;
1001             wwo->state = WINE_WS_CLOSED;
1002             SetEvent(ev);
1003             ExitThread(0);
1004             /* shouldn't go here */
1005         default:
1006             FIXME("unknown message %d\n", msg);
1007             break;
1008         }
1009     }
1010 }
1011
1012 /**************************************************************************
1013  *                           wodPlayer_FeedDSP                  [internal]
1014  * Feed as much sound data as we can into the DSP and return the number of
1015  * milliseconds before it will be necessary to feed the DSP again.
1016  */
1017 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1018 {
1019     DWORD       availInQ;
1020
1021     wodUpdatePlayedTotal(wwo);
1022     availInQ = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE);
1023     TRACE("availInQ = %ld\n", availInQ);
1024
1025     /* input queue empty */
1026     if (!wwo->lpPlayPtr) {
1027         TRACE("Run out of wavehdr:s... flushing\n");
1028         return INFINITE;
1029     }
1030
1031     /* no more room... no need to try to feed */
1032     if(!availInQ)
1033     {
1034         TRACE("no more room, no need to try to feed\n");
1035         return wwo->dwSleepTime;
1036     }
1037
1038     /* Feed from partial wavehdr */
1039     if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)
1040     {
1041         TRACE("feeding from partial wavehdr\n");
1042         wodPlayer_WriteMaxFrags(wwo, &availInQ);
1043     }
1044
1045     /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1046     if (!wwo->dwPartialOffset)
1047     {
1048         while(wwo->lpPlayPtr && availInQ)
1049         {
1050             TRACE("feeding waveheaders until we run out of space\n");
1051             /* note the value that dwPlayedTotal will return when this wave finishes playing */
1052             wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1053             TRACE("reserved=(%ld) dwWrittenTotal=(%ld) dwBufferLength=(%ld)\n",
1054                   wwo->lpPlayPtr->reserved,
1055                   wwo->dwWrittenTotal,
1056                   wwo->lpPlayPtr->dwBufferLength
1057                 );
1058             wodPlayer_WriteMaxFrags(wwo, &availInQ);
1059         }
1060     }
1061
1062     if (!wwo->lpPlayPtr) {
1063         TRACE("Ran out of wavehdrs\n");
1064         return INFINITE;
1065     }
1066
1067     return wwo->dwSleepTime;
1068 }
1069
1070
1071 /**************************************************************************
1072  *                              wodPlayer                       [internal]
1073  */
1074 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
1075 {
1076     WORD          uDevID = (DWORD)pmt;
1077     WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1078     DWORD         dwNextFeedTime = INFINITE;   /* Time before DSP needs feeding */
1079     DWORD         dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1080     DWORD         dwSleepTime;
1081
1082     wwo->state = WINE_WS_STOPPED;
1083     SetEvent(wwo->hStartUpEvent);
1084
1085     for (;;) {
1086         /** Wait for the shortest time before an action is required.  If there
1087          *  are no pending actions, wait forever for a command.
1088          */
1089         dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1090         TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1091         WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);
1092         wodPlayer_ProcessMessages(wwo);
1093         if (wwo->state == WINE_WS_PLAYING) {
1094             dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1095             dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1096         } else {
1097             dwNextFeedTime = dwNextNotifyTime = INFINITE;
1098         }
1099     }
1100 }
1101
1102 /**************************************************************************
1103  *                      wodGetDevCaps                           [internal]
1104  */
1105 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
1106 {
1107     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1108
1109     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1110
1111     if (wDevID >= MAX_WAVEOUTDRV) {
1112         TRACE("MAX_WAVOUTDRV reached !\n");
1113         return MMSYSERR_BADDEVICEID;
1114     }
1115
1116     memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1117     return MMSYSERR_NOERROR;
1118 }
1119
1120 /**************************************************************************
1121  *                              wodOpen                         [internal]
1122  */
1123 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1124 {
1125     WINE_WAVEOUT*       wwo;
1126
1127     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1128     if (lpDesc == NULL) {
1129         WARN("Invalid Parameter !\n");
1130         return MMSYSERR_INVALPARAM;
1131     }
1132     if (wDevID >= MAX_WAVEOUTDRV) {
1133         TRACE("MAX_WAVOUTDRV reached !\n");
1134         return MMSYSERR_BADDEVICEID;
1135     }
1136
1137     /* if this device is already open tell the app that it is allocated */
1138     if(WOutDev[wDevID].play_stream != (arts_stream_t*)-1)
1139     {
1140       TRACE("device already allocated\n");
1141       return MMSYSERR_ALLOCATED;
1142     }
1143
1144     /* only PCM format is supported so far... */
1145     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1146         lpDesc->lpFormat->nChannels == 0 ||
1147         lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
1148         lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
1149         (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
1150         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1151              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1152              lpDesc->lpFormat->nSamplesPerSec);
1153         return WAVERR_BADFORMAT;
1154     }
1155
1156     if (dwFlags & WAVE_FORMAT_QUERY) {
1157         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1158              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1159              lpDesc->lpFormat->nSamplesPerSec);
1160         return MMSYSERR_NOERROR;
1161     }
1162
1163     wwo = &WOutDev[wDevID];
1164
1165     /* direct sound not supported, ignore the flag */
1166     dwFlags &= ~WAVE_DIRECTSOUND;
1167
1168     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1169
1170     memcpy(&wwo->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
1171     memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1172
1173     if (wwo->format.wBitsPerSample == 0) {
1174         WARN("Resetting zeroed wBitsPerSample\n");
1175         wwo->format.wBitsPerSample = 8 *
1176             (wwo->format.wf.nAvgBytesPerSec /
1177              wwo->format.wf.nSamplesPerSec) /
1178             wwo->format.wf.nChannels;
1179     }
1180
1181     wwo->play_stream = arts_play_stream(wwo->format.wf.nSamplesPerSec,
1182         wwo->format.wBitsPerSample, wwo->format.wf.nChannels, "winearts");
1183
1184     /* clear these so we don't have any confusion ;-) */
1185     wwo->sound_buffer = 0;
1186     wwo->buffer_size = 0;
1187
1188     arts_stream_set(wwo->play_stream, ARTS_P_BLOCKING, 0);    /* disable blocking on this stream */
1189
1190     if(!wwo->play_stream) return MMSYSERR_ALLOCATED;
1191
1192     /* Try to set the packet settings from constant and store the value that it
1193        was actually set to for future use */
1194     wwo->packetSettings = arts_stream_set(wwo->play_stream, ARTS_P_PACKET_SETTINGS, WAVEOUT_PACKET_SETTINGS);
1195     TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEOUT_PACKET_SETTINGS, wwo->packetSettings);
1196
1197     wwo->dwBufferSize = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SIZE);
1198     TRACE("Buffer size is now (%ld)\n",wwo->dwBufferSize);
1199
1200     wwo->dwPlayedTotal = 0;
1201     wwo->dwWrittenTotal = 0;
1202
1203     wwo->dwSleepTime = ((1 << (wwo->packetSettings & 0xFFFF)) * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->format.wf.nAvgBytesPerSec;
1204
1205     /* Initialize volume to full level */
1206     wwo->volume_left = 100;
1207     wwo->volume_right = 100;
1208
1209     ARTS_InitRingMessage(&wwo->msgRing);
1210
1211     /* create player thread */
1212     if (!(dwFlags & WAVE_DIRECTSOUND)) {
1213         wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1214         wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1215         WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1216         CloseHandle(wwo->hStartUpEvent);
1217     } else {
1218         wwo->hThread = INVALID_HANDLE_VALUE;
1219         wwo->dwThreadID = 0;
1220     }
1221     wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1222
1223     TRACE("stream=0x%lx, dwBufferSize=%ld\n",
1224           (long)wwo->play_stream, wwo->dwBufferSize);
1225
1226     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1227           wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
1228           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1229           wwo->format.wf.nBlockAlign);
1230
1231     return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1232 }
1233
1234 /**************************************************************************
1235  *                              wodClose                        [internal]
1236  */
1237 static DWORD wodClose(WORD wDevID)
1238 {
1239     DWORD               ret = MMSYSERR_NOERROR;
1240     WINE_WAVEOUT*       wwo;
1241
1242     TRACE("(%u);\n", wDevID);
1243
1244     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream  ==
1245         (arts_stream_t*)-1)
1246     {
1247         WARN("bad device ID !\n");
1248         return MMSYSERR_BADDEVICEID;
1249     }
1250
1251     wwo = &WOutDev[wDevID];
1252     if (wwo->lpQueuePtr) {
1253         WARN("buffers still playing !\n");
1254         ret = WAVERR_STILLPLAYING;
1255     } else {
1256         TRACE("imhere[3-close]\n");
1257         if (wwo->hThread != INVALID_HANDLE_VALUE) {
1258             ARTS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1259         }
1260
1261         ARTS_DestroyRingMessage(&wwo->msgRing);
1262
1263         ARTS_CloseWaveOutDevice(wwo);   /* close the stream and clean things up */
1264
1265         ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1266     }
1267     return ret;
1268 }
1269
1270 /**************************************************************************
1271  *                              wodWrite                        [internal]
1272  *
1273  */
1274 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1275 {
1276     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1277
1278     /* first, do the sanity checks... */
1279     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1280                 (arts_stream_t*)-1)
1281     {
1282         WARN("bad dev ID !\n");
1283         return MMSYSERR_BADDEVICEID;
1284     }
1285
1286     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1287     {
1288         TRACE("unprepared\n");
1289         return WAVERR_UNPREPARED;
1290     }
1291
1292     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1293     {
1294         TRACE("still playing\n");
1295         return WAVERR_STILLPLAYING;
1296     }
1297
1298     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1299     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1300     lpWaveHdr->lpNext = 0;
1301
1302     TRACE("adding ring message\n");
1303     ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1304
1305     return MMSYSERR_NOERROR;
1306 }
1307
1308 /**************************************************************************
1309  *                              wodPrepare                      [internal]
1310  */
1311 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1312 {
1313     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1314
1315     if (wDevID >= MAX_WAVEOUTDRV) {
1316         WARN("bad device ID !\n");
1317         return MMSYSERR_BADDEVICEID;
1318     }
1319
1320     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1321         return WAVERR_STILLPLAYING;
1322
1323     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1324     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1325     return MMSYSERR_NOERROR;
1326 }
1327
1328 /**************************************************************************
1329  *                              wodUnprepare                    [internal]
1330  */
1331 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1332 {
1333     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1334
1335     if (wDevID >= MAX_WAVEOUTDRV) {
1336         WARN("bad device ID !\n");
1337         return MMSYSERR_BADDEVICEID;
1338     }
1339
1340     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1341         return WAVERR_STILLPLAYING;
1342
1343     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1344     lpWaveHdr->dwFlags |= WHDR_DONE;
1345
1346     return MMSYSERR_NOERROR;
1347 }
1348
1349 /**************************************************************************
1350  *                      wodPause                                [internal]
1351  */
1352 static DWORD wodPause(WORD wDevID)
1353 {
1354     TRACE("(%u);!\n", wDevID);
1355
1356     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1357                 (arts_stream_t*)-1)
1358     {
1359         WARN("bad device ID !\n");
1360         return MMSYSERR_BADDEVICEID;
1361     }
1362
1363     TRACE("imhere[3-PAUSING]\n");
1364     ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1365
1366     return MMSYSERR_NOERROR;
1367 }
1368
1369 /**************************************************************************
1370  *                      wodRestart                              [internal]
1371  */
1372 static DWORD wodRestart(WORD wDevID)
1373 {
1374     TRACE("(%u);\n", wDevID);
1375
1376     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1377                 (arts_stream_t*)-1)
1378     {
1379         WARN("bad device ID !\n");
1380         return MMSYSERR_BADDEVICEID;
1381     }
1382
1383     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1384         TRACE("imhere[3-RESTARTING]\n");
1385         ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1386     }
1387
1388     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1389     /* FIXME: Myst crashes with this ... hmm -MM
1390        return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1391     */
1392
1393     return MMSYSERR_NOERROR;
1394 }
1395
1396 /**************************************************************************
1397  *                      wodReset                                [internal]
1398  */
1399 static DWORD wodReset(WORD wDevID)
1400 {
1401     TRACE("(%u);\n", wDevID);
1402
1403     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1404         (arts_stream_t*)-1)
1405     {
1406         WARN("bad device ID !\n");
1407         return MMSYSERR_BADDEVICEID;
1408     }
1409
1410     TRACE("imhere[3-RESET]\n");
1411     ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1412
1413     return MMSYSERR_NOERROR;
1414 }
1415
1416 /**************************************************************************
1417  *                              wodGetPosition                  [internal]
1418  */
1419 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1420 {
1421     WINE_WAVEOUT*       wwo;
1422
1423     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1424
1425     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1426         (arts_stream_t*)-1)
1427     {
1428         WARN("bad device ID !\n");
1429         return MMSYSERR_BADDEVICEID;
1430     }
1431
1432     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1433
1434     wwo = &WOutDev[wDevID];
1435     ARTS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1436
1437     return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->format);
1438 }
1439
1440 /**************************************************************************
1441  *                              wodBreakLoop                    [internal]
1442  */
1443 static DWORD wodBreakLoop(WORD wDevID)
1444 {
1445     TRACE("(%u);\n", wDevID);
1446
1447     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1448                 (arts_stream_t*)-1)
1449     {
1450         WARN("bad device ID !\n");
1451         return MMSYSERR_BADDEVICEID;
1452     }
1453     ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1454     return MMSYSERR_NOERROR;
1455 }
1456
1457 /**************************************************************************
1458  *                              wodGetVolume                    [internal]
1459  */
1460 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1461 {
1462     DWORD left, right;
1463
1464     left = WOutDev[wDevID].volume_left;
1465     right = WOutDev[wDevID].volume_right;
1466
1467     TRACE("(%u, %p);\n", wDevID, lpdwVol);
1468
1469     *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) <<
1470                 16);
1471
1472     return MMSYSERR_NOERROR;
1473 }
1474
1475 /**************************************************************************
1476  *                              wodSetVolume                    [internal]
1477  */
1478 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1479 {
1480     DWORD left, right;
1481
1482     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
1483     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1484
1485     TRACE("(%u, %08lX);\n", wDevID, dwParam);
1486
1487     WOutDev[wDevID].volume_left = left;
1488     WOutDev[wDevID].volume_right = right;
1489
1490     return MMSYSERR_NOERROR;
1491 }
1492
1493 /**************************************************************************
1494  *                              wodGetNumDevs                   [internal]
1495  */
1496 static  DWORD   wodGetNumDevs(void)
1497 {
1498     return MAX_WAVEOUTDRV;
1499 }
1500
1501 /**************************************************************************
1502  *                              wodDevInterfaceSize             [internal]
1503  */
1504 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1505 {
1506     TRACE("(%u, %p)\n", wDevID, dwParam1);
1507                                                                                                        
1508     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1509                                     NULL, 0 ) * sizeof(WCHAR);
1510     return MMSYSERR_NOERROR;
1511 }
1512                                                                                                        
1513 /**************************************************************************
1514  *                              wodDevInterface                 [internal]
1515  */
1516 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1517 {
1518     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1519                                         NULL, 0 ) * sizeof(WCHAR))
1520     {
1521         MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
1522                             dwParam1, dwParam2 / sizeof(WCHAR));
1523         return MMSYSERR_NOERROR;
1524     }
1525     return MMSYSERR_INVALPARAM;
1526 }
1527                                                                                                        
1528 /**************************************************************************
1529  *                              wodMessage (WINEARTS.@)
1530  */
1531 DWORD WINAPI ARTS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1532                             DWORD dwParam1, DWORD dwParam2)
1533 {
1534     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1535           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1536
1537     switch (wMsg) {
1538     case DRVM_INIT:
1539     case DRVM_EXIT:
1540     case DRVM_ENABLE:
1541     case DRVM_DISABLE:
1542         /* FIXME: Pretend this is supported */
1543         return 0;
1544     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
1545     case WODM_CLOSE:            return wodClose         (wDevID);
1546     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1547     case WODM_PAUSE:            return wodPause         (wDevID);
1548     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
1549     case WODM_BREAKLOOP:        return wodBreakLoop     (wDevID);
1550     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1551     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
1552     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSW)dwParam1,      dwParam2);
1553     case WODM_GETNUMDEVS:       return wodGetNumDevs    ();
1554     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
1555     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
1556     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1557     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
1558     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
1559     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
1560     case WODM_RESTART:          return wodRestart       (wDevID);
1561     case WODM_RESET:            return wodReset         (wDevID);
1562
1563     case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
1564     case DRV_QUERYDEVICEINTERFACE:     return wodDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
1565     case DRV_QUERYDSOUNDIFACE:  return wodDsCreate      (wDevID, (PIDSDRIVER*)dwParam1);
1566     case DRV_QUERYDSOUNDDESC:   return wodDsDesc        (wDevID, (PDSDRIVERDESC)dwParam1);
1567     default:
1568         FIXME("unknown message %d!\n", wMsg);
1569     }
1570     return MMSYSERR_NOTSUPPORTED;
1571 }
1572
1573 /*======================================================================*
1574  *                  Low level WAVE IN implementation                    *
1575  *======================================================================*/
1576
1577 /**************************************************************************
1578  *                              widGetNumDevs                   [internal]
1579  */
1580 static  DWORD   widGetNumDevs(void)
1581 {
1582     TRACE("%d \n",MAX_WAVEINDRV);
1583     return MAX_WAVEINDRV;
1584 }
1585
1586 /**************************************************************************
1587  *                              widDevInterfaceSize             [internal]
1588  */
1589 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
1590 {
1591     TRACE("(%u, %p)\n", wDevID, dwParam1);
1592                                                                                                        
1593                                                                                                        
1594     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1595                                     NULL, 0 ) * sizeof(WCHAR);
1596     return MMSYSERR_NOERROR;
1597 }
1598
1599 /**************************************************************************
1600  *                              widDevInterface                 [internal]
1601  */
1602 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
1603 {
1604     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1605                                         NULL, 0 ) * sizeof(WCHAR))
1606     {
1607         MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
1608                             dwParam1, dwParam2 / sizeof(WCHAR));
1609         return MMSYSERR_NOERROR;
1610     }
1611     return MMSYSERR_INVALPARAM;
1612 }
1613
1614 /**************************************************************************
1615  *                      widNotifyClient                 [internal]
1616  */
1617 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1618 {
1619     TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
1620
1621     switch (wMsg) {
1622     case WIM_OPEN:
1623     case WIM_CLOSE:
1624     case WIM_DATA:
1625         if (wwi->wFlags != DCB_NULL &&
1626             !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1627                             (HDRVR)wwi->waveDesc.hWave, wMsg,
1628                             wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
1629             WARN("can't notify client !\n");
1630             return MMSYSERR_ERROR;
1631         }
1632         break;
1633     default:
1634         FIXME("Unknown callback message %u\n", wMsg);
1635         return MMSYSERR_INVALPARAM;
1636     }
1637     return MMSYSERR_NOERROR;
1638 }
1639
1640 /**************************************************************************
1641  *                      widGetDevCaps                           [internal]
1642  */
1643 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
1644 {
1645     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1646
1647     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1648
1649     if (wDevID >= MAX_WAVEINDRV) {
1650         TRACE("MAX_WAVINDRV reached !\n");
1651         return MMSYSERR_BADDEVICEID;
1652     }
1653
1654     memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1655     return MMSYSERR_NOERROR;
1656 }
1657
1658 /**************************************************************************
1659  *                              widRecorder                     [internal]
1660  */
1661 static  DWORD   CALLBACK        widRecorder(LPVOID pmt)
1662 {
1663     WORD                uDevID = (DWORD)pmt;
1664     WINE_WAVEIN*        wwi = (WINE_WAVEIN*)&WInDev[uDevID];
1665     WAVEHDR*            lpWaveHdr;
1666     DWORD               dwSleepTime;
1667     DWORD               bytesRead;
1668     int                 dwBufferSpace;
1669     enum win_wm_message msg;
1670     DWORD               param;
1671     HANDLE              ev;
1672
1673     SetEvent(wwi->hStartUpEvent);
1674
1675     /* make sleep time to be # of ms to record one packet */
1676     dwSleepTime = ((1 << (wwi->packetSettings & 0xFFFF)) * 1000) / wwi->format.wf.nAvgBytesPerSec;
1677     TRACE("sleeptime=%ld ms\n", dwSleepTime);
1678
1679     for(;;) {
1680         /* Oddly enough, dwBufferSpace is sometimes negative.... 
1681          * 
1682          * NOTE: If you remove this call to arts_stream_get() and
1683          * remove the && (dwBufferSpace > 0) the code will still
1684          * function correctly. I don't know which way is
1685          * faster/better.
1686          */
1687         dwBufferSpace = arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SPACE);
1688         TRACE("wwi->lpQueuePtr=(%p), wwi->state=(%d), dwBufferSpace=(%d)\n",wwi->lpQueuePtr,wwi->state,dwBufferSpace);
1689
1690         /* read all data is arts input buffer. */
1691         if ((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING) && (dwBufferSpace > 0))
1692         {
1693             lpWaveHdr = wwi->lpQueuePtr;
1694                 
1695             TRACE("read as much as we can\n");
1696             while(wwi->lpQueuePtr)
1697             {
1698                 TRACE("attempt to read %ld bytes\n",lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1699                 bytesRead = arts_read(wwi->record_stream,
1700                                       lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
1701                                       lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1702                 TRACE("bytesRead=%ld\n",bytesRead);
1703                 if (bytesRead==0) break;
1704                 
1705                 lpWaveHdr->dwBytesRecorded      += bytesRead;
1706                 wwi->dwRecordedTotal            += bytesRead;
1707
1708                 /* buffer full. notify client */
1709                 if (lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength)
1710                 {
1711                     /* must copy the value of next waveHdr, because we have no idea of what
1712                      * will be done with the content of lpWaveHdr in callback
1713                      */
1714                     LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
1715
1716                     TRACE("waveHdr full.\n");
1717                     
1718                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1719                     lpWaveHdr->dwFlags |=  WHDR_DONE;
1720                     
1721                     widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1722                     lpWaveHdr = wwi->lpQueuePtr = lpNext;
1723                 }
1724             }
1725         }
1726
1727         /* wait for dwSleepTime or an event in thread's queue */
1728         WaitForSingleObject(wwi->msgRing.msg_event, dwSleepTime);
1729
1730         while (ARTS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
1731         {
1732             TRACE("msg=%s param=0x%lx\n",wodPlayerCmdString[msg - WM_USER - 1], param);
1733             switch(msg) {
1734             case WINE_WM_PAUSING:
1735                 wwi->state = WINE_WS_PAUSED;
1736
1737                 /* Put code here to "pause" arts recording
1738                  */
1739
1740                 SetEvent(ev);
1741                 break;
1742             case WINE_WM_STARTING:
1743                 wwi->state = WINE_WS_PLAYING;
1744
1745                 /* Put code here to "start" arts recording
1746                  */
1747
1748                 SetEvent(ev);
1749                 break;
1750             case WINE_WM_HEADER:
1751                 lpWaveHdr = (LPWAVEHDR)param;
1752                 /* insert buffer at end of queue */
1753                 {
1754                     LPWAVEHDR* wh;
1755                     int num_headers = 0;
1756                     for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1757                     {
1758                         num_headers++;
1759
1760                     }
1761                     *wh=lpWaveHdr;
1762                 }
1763                 break;
1764             case WINE_WM_STOPPING:
1765                 if (wwi->state != WINE_WS_STOPPED)
1766                 {
1767
1768                     /* Put code here to "stop" arts recording
1769                      */
1770
1771                     /* return current buffer to app */
1772                     lpWaveHdr = wwi->lpQueuePtr;
1773                     if (lpWaveHdr)
1774                     {
1775                         LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1776                         TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1777                         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1778                         lpWaveHdr->dwFlags |= WHDR_DONE;
1779                         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1780                         wwi->lpQueuePtr = lpNext;
1781                     }
1782                 }
1783                 wwi->state = WINE_WS_STOPPED;
1784                 SetEvent(ev);
1785                 break;
1786             case WINE_WM_RESETTING:
1787                 wwi->state = WINE_WS_STOPPED;
1788                 wwi->dwRecordedTotal = 0;
1789
1790                 /* return all buffers to the app */
1791                 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
1792                     TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1793                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1794                     lpWaveHdr->dwFlags |= WHDR_DONE;
1795
1796                     widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1797                 }
1798                 wwi->lpQueuePtr = NULL; 
1799                 SetEvent(ev);
1800                 break;
1801             case WINE_WM_CLOSING:
1802                 wwi->hThread = 0;
1803                 wwi->state = WINE_WS_CLOSED;
1804                 SetEvent(ev);
1805                 ExitThread(0);
1806                 /* shouldn't go here */
1807             default:
1808                 FIXME("unknown message %d\n", msg);
1809                 break;
1810             }
1811         }
1812     }
1813     ExitThread(0);
1814     /* just for not generating compilation warnings... should never be executed */
1815     return 0;
1816 }
1817
1818 /**************************************************************************
1819  *                              widOpen                         [internal]
1820  */
1821 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1822 {
1823     WINE_WAVEIN*        wwi;
1824
1825     TRACE("(%u, %p %08lX);\n",wDevID, lpDesc, dwFlags);
1826     if (lpDesc == NULL) {
1827         WARN("Invalid Parametr (lpDesc == NULL)!\n");
1828         return MMSYSERR_INVALPARAM;
1829     }
1830
1831     if (wDevID >= MAX_WAVEINDRV) {
1832         TRACE ("MAX_WAVEINDRV reached !\n");
1833         return MMSYSERR_BADDEVICEID;
1834     }
1835
1836     /* if this device is already open tell the app that it is allocated */
1837     if(WInDev[wDevID].record_stream != (arts_stream_t*)-1)
1838     {
1839         TRACE("device already allocated\n");
1840         return MMSYSERR_ALLOCATED;
1841     }
1842
1843     /* only PCM format is support so far... */
1844     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1845         lpDesc->lpFormat->nChannels == 0 ||
1846         lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
1847         lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
1848         (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
1849         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1850              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1851              lpDesc->lpFormat->nSamplesPerSec);
1852         return WAVERR_BADFORMAT;
1853     }
1854
1855     if (dwFlags & WAVE_FORMAT_QUERY) {
1856         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1857              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1858              lpDesc->lpFormat->nSamplesPerSec);
1859         return MMSYSERR_NOERROR;
1860     }
1861
1862     wwi = &WInDev[wDevID];
1863
1864     /* direct sound not supported, ignore the flag */
1865     dwFlags &= ~WAVE_DIRECTSOUND;
1866
1867     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1868     
1869     memcpy(&wwi->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
1870     memcpy(&wwi->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1871
1872     if (wwi->format.wBitsPerSample == 0) {
1873         WARN("Resetting zerod wBitsPerSample\n");
1874         wwi->format.wBitsPerSample = 8 *
1875             (wwi->format.wf.nAvgBytesPerSec /
1876              wwi->format.wf.nSamplesPerSec) /
1877             wwi->format.wf.nChannels;
1878     }
1879
1880     wwi->record_stream = arts_record_stream(wwi->format.wf.nSamplesPerSec,
1881                                             wwi->format.wBitsPerSample,
1882                                             wwi->format.wf.nChannels,
1883                                             "winearts");
1884     TRACE("(wwi->record_stream=%p)\n",wwi->record_stream);
1885     wwi->state = WINE_WS_STOPPED;
1886
1887     wwi->packetSettings = arts_stream_set(wwi->record_stream, ARTS_P_PACKET_SETTINGS, WAVEIN_PACKET_SETTINGS);
1888     TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEIN_PACKET_SETTINGS, wwi->packetSettings);
1889     TRACE("Buffer size is now (%d)\n", arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SIZE));
1890
1891     if (wwi->lpQueuePtr) {
1892         WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
1893         wwi->lpQueuePtr = NULL;
1894     }
1895     arts_stream_set(wwi->record_stream, ARTS_P_BLOCKING, 0);    /* disable blocking on this stream */
1896
1897     if(!wwi->record_stream) return MMSYSERR_ALLOCATED;
1898
1899     wwi->dwRecordedTotal = 0;
1900     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1901
1902     ARTS_InitRingMessage(&wwi->msgRing);
1903
1904     /* create recorder thread */
1905     if (!(dwFlags & WAVE_DIRECTSOUND)) {
1906         wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1907         wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
1908         WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
1909         CloseHandle(wwi->hStartUpEvent);
1910     } else {
1911         wwi->hThread = INVALID_HANDLE_VALUE;
1912         wwi->dwThreadID = 0;
1913     }
1914     wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
1915
1916     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1917           wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
1918           wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
1919           wwi->format.wf.nBlockAlign);
1920     return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1921 }
1922
1923 /**************************************************************************
1924  *                              widClose                        [internal]
1925  */
1926 static DWORD widClose(WORD wDevID)
1927 {
1928     WINE_WAVEIN*        wwi;
1929
1930     TRACE("(%u);\n", wDevID);
1931     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1932         WARN("can't close !\n");
1933         return MMSYSERR_INVALHANDLE;
1934     }
1935
1936     wwi = &WInDev[wDevID];
1937
1938     if (wwi->lpQueuePtr != NULL) {
1939         WARN("still buffers open !\n");
1940         return WAVERR_STILLPLAYING;
1941     }
1942
1943     ARTS_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
1944     ARTS_CloseWaveInDevice(wwi);
1945     wwi->state = WINE_WS_CLOSED;
1946     ARTS_DestroyRingMessage(&wwi->msgRing);
1947     return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
1948 }
1949
1950 /**************************************************************************
1951  *                              widAddBuffer            [internal]
1952  */
1953 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1954 {
1955     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1956
1957     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1958         WARN("can't do it !\n");
1959         return MMSYSERR_INVALHANDLE;
1960     }
1961     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1962         TRACE("never been prepared !\n");
1963         return WAVERR_UNPREPARED;
1964     }
1965     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1966         TRACE("header already in use !\n");
1967         return WAVERR_STILLPLAYING;
1968     }
1969
1970     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1971     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1972     lpWaveHdr->dwBytesRecorded = 0;
1973     lpWaveHdr->lpNext = NULL;
1974
1975     ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1976     return MMSYSERR_NOERROR;
1977 }
1978
1979 /**************************************************************************
1980  *                              widPrepare                      [internal]
1981  */
1982 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1983 {
1984     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1985
1986     if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
1987
1988     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1989         return WAVERR_STILLPLAYING;
1990
1991     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1992     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1993     lpWaveHdr->dwBytesRecorded = 0;
1994
1995     return MMSYSERR_NOERROR;
1996 }
1997
1998 /**************************************************************************
1999  *                              widUnprepare                    [internal]
2000  */
2001 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2002 {
2003     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2004     if (wDevID >= MAX_WAVEINDRV) {
2005         WARN("bad device ID !\n");
2006         return MMSYSERR_INVALHANDLE;
2007     }
2008
2009     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
2010         TRACE("Still playing...\n");
2011         return WAVERR_STILLPLAYING;
2012     }
2013
2014     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
2015     lpWaveHdr->dwFlags |= WHDR_DONE;
2016
2017     return MMSYSERR_NOERROR;
2018 }
2019
2020 /**************************************************************************
2021  *                      widStart                                [internal]
2022  */
2023 static DWORD widStart(WORD wDevID)
2024 {
2025     TRACE("(%u);\n", wDevID);
2026     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2027         WARN("can't start recording !\n");
2028         return MMSYSERR_INVALHANDLE;
2029     }
2030
2031     ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
2032     return MMSYSERR_NOERROR;
2033 }
2034
2035 /**************************************************************************
2036  *                      widStop                                 [internal]
2037  */
2038 static DWORD widStop(WORD wDevID)
2039 {
2040     TRACE("(%u);\n", wDevID);
2041     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2042         WARN("can't stop !\n");
2043         return MMSYSERR_INVALHANDLE;
2044     }
2045
2046     ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
2047
2048     return MMSYSERR_NOERROR;
2049 }
2050
2051 /**************************************************************************
2052  *                      widReset                                [internal]
2053  */
2054 static DWORD widReset(WORD wDevID)
2055 {
2056     TRACE("(%u);\n", wDevID);
2057     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
2058         WARN("can't reset !\n");
2059         return MMSYSERR_INVALHANDLE;
2060     }
2061     ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2062     return MMSYSERR_NOERROR;
2063 }
2064
2065 /**************************************************************************
2066  *                              widMessage (WINEARTS.6)
2067  */
2068 DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2069                             DWORD dwParam1, DWORD dwParam2)
2070 {
2071     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2072           wDevID, wMsg, dwUser, dwParam1, dwParam2);
2073     switch (wMsg) {
2074     case DRVM_INIT:
2075     case DRVM_EXIT:
2076     case DRVM_ENABLE:
2077     case DRVM_DISABLE:
2078         /* FIXME: Pretend this is supported */
2079         return 0;
2080     case WIDM_OPEN:             return widOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
2081     case WIDM_CLOSE:            return widClose         (wDevID);
2082     case WIDM_ADDBUFFER:        return widAddBuffer     (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2083     case WIDM_PREPARE:          return widPrepare       (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2084     case WIDM_UNPREPARE:        return widUnprepare     (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2085     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (LPWAVEINCAPSW)dwParam1,       dwParam2);
2086     case WIDM_GETNUMDEVS:       return widGetNumDevs    ();
2087     case WIDM_RESET:            return widReset         (wDevID);
2088     case WIDM_START:            return widStart         (wDevID);
2089     case WIDM_STOP:             return widStop          (wDevID);
2090     case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
2091     case DRV_QUERYDEVICEINTERFACE:     return widDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
2092     default:
2093         FIXME("unknown message %d!\n", wMsg);
2094     }
2095     return MMSYSERR_NOTSUPPORTED;
2096 }
2097
2098 /*======================================================================*
2099  *                  Low level DSOUND implementation                     *
2100  *======================================================================*/
2101 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2102 {
2103     /* we can't perform memory mapping as we don't have a file stream
2104         interface with arts like we do with oss */
2105     MESSAGE("This sound card's driver does not support direct access\n");
2106     MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2107     return MMSYSERR_NOTSUPPORTED;
2108 }
2109
2110 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2111 {
2112     memset(desc, 0, sizeof(*desc));
2113     strcpy(desc->szDesc, "Wine aRts DirectSound Driver");
2114     strcpy(desc->szDrvname, "winearts.drv");
2115     return MMSYSERR_NOERROR;
2116 }
2117
2118 #else /* !HAVE_ARTS */
2119
2120 /**************************************************************************
2121  *                              wodMessage (WINEARTS.@)
2122  */
2123 DWORD WINAPI ARTS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2124                             DWORD dwParam1, DWORD dwParam2)
2125 {
2126     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2127     return MMSYSERR_NOTENABLED;
2128 }
2129
2130 /**************************************************************************
2131  *                              widMessage (WINEARTS.6)
2132  */
2133 DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2134                             DWORD dwParam1, DWORD dwParam2)
2135 {
2136     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2137     return MMSYSERR_NOTENABLED;
2138 }
2139
2140 #endif /* HAVE_ARTS */