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