Implement A->W call for GetNamedSecurityInfo.
[wine] / dlls / winmm / winealsa / audio.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Sample Wine Driver for Advanced Linux Sound System (ALSA)
4  *      Based on version <final> of the ALSA API
5  *
6  * Copyright    2002 Eric Pouech
7  *              2002 Marco Pietrobono
8  *              2003 Christian Costa : WaveIn support
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 /* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
26 #define USE_PIPE_SYNC
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <string.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <errno.h>
39 #include <limits.h>
40 #include <fcntl.h>
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
43 #endif
44 #ifdef HAVE_SYS_MMAN_H
45 # include <sys/mman.h>
46 #endif
47 #include "windef.h"
48 #include "winbase.h"
49 #include "wingdi.h"
50 #include "winerror.h"
51 #include "winuser.h"
52 #include "winnls.h"
53 #include "winreg.h"
54 #include "mmddk.h"
55 #include "dsound.h"
56 #include "dsdriver.h"
57 #define ALSA_PCM_NEW_HW_PARAMS_API
58 #define ALSA_PCM_NEW_SW_PARAMS_API
59 #include "alsa.h"
60 #include "wine/library.h"
61 #include "wine/debug.h"
62
63 WINE_DEFAULT_DEBUG_CHANNEL(wave);
64
65
66 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
67
68 /* internal ALSALIB functions */
69 snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);
70
71
72 #define MAX_WAVEOUTDRV  (1)
73 #define MAX_WAVEINDRV   (1)
74
75 /* state diagram for waveOut writing:
76  *
77  * +---------+-------------+---------------+---------------------------------+
78  * |  state  |  function   |     event     |            new state            |
79  * +---------+-------------+---------------+---------------------------------+
80  * |         | open()      |               | STOPPED                         |
81  * | PAUSED  | write()     |               | PAUSED                          |
82  * | STOPPED | write()     | <thrd create> | PLAYING                         |
83  * | PLAYING | write()     | HEADER        | PLAYING                         |
84  * | (other) | write()     | <error>       |                                 |
85  * | (any)   | pause()     | PAUSING       | PAUSED                          |
86  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
87  * | (any)   | reset()     | RESETTING     | STOPPED                         |
88  * | (any)   | close()     | CLOSING       | CLOSED                          |
89  * +---------+-------------+---------------+---------------------------------+
90  */
91
92 /* states of the playing device */
93 #define WINE_WS_PLAYING         0
94 #define WINE_WS_PAUSED          1
95 #define WINE_WS_STOPPED         2
96 #define WINE_WS_CLOSED          3
97
98 /* events to be send to device */
99 enum win_wm_message {
100     WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
101     WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
102 };
103
104 #ifdef USE_PIPE_SYNC
105 #define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
106 #define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
107 #define RESET_OMR(omr) do { } while (0)
108 #define WAIT_OMR(omr, sleep) \
109   do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
110        pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
111 #else
112 #define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
113 #define CLEAR_OMR(omr) do { } while (0)
114 #define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
115 #define WAIT_OMR(omr, sleep) \
116   do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
117 #endif
118
119 typedef struct {
120     enum win_wm_message         msg;    /* message identifier */
121     DWORD                       param;  /* parameter for this message */
122     HANDLE                      hEvent; /* if message is synchronous, handle of event for synchro */
123 } ALSA_MSG;
124
125 /* implement an in-process message ring for better performance
126  * (compared to passing thru the server)
127  * this ring will be used by the input (resp output) record (resp playback) routine
128  */
129 #define ALSA_RING_BUFFER_INCREMENT      64
130 typedef struct {
131     ALSA_MSG                    * messages;
132     int                         ring_buffer_size;
133     int                         msg_tosave;
134     int                         msg_toget;
135 #ifdef USE_PIPE_SYNC
136     int                         msg_pipe[2];
137 #else
138     HANDLE                      msg_event;
139 #endif
140     CRITICAL_SECTION            msg_crst;
141 } ALSA_MSG_RING;
142
143 typedef struct {
144     /* Windows information */
145     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
146     WAVEOPENDESC                waveDesc;
147     WORD                        wFlags;
148     PCMWAVEFORMAT               format;
149     WAVEOUTCAPSA                caps;
150
151     /* ALSA information (ALSA 0.9/1.x uses two different devices for playback/capture) */
152     char*                       device;
153     char                        interface_name[64];
154     snd_pcm_t*                  p_handle;                 /* handle to ALSA playback device */
155     snd_pcm_t*                  c_handle;                 /* handle to ALSA capture device */
156     snd_pcm_hw_params_t *       hw_params;              /* ALSA Hw params */
157
158     snd_ctl_t *                 ctl;                    /* control handle for the playback volume */
159     snd_ctl_elem_id_t *         playback_eid;           /* element id of the playback volume control */
160     snd_ctl_elem_value_t *      playback_evalue;        /* element value of the playback volume control */
161     snd_ctl_elem_info_t *       playback_einfo;         /* element info of the playback volume control */
162
163     snd_pcm_sframes_t           (*write)(snd_pcm_t *, const void *, snd_pcm_uframes_t );
164
165     struct pollfd               *ufds;
166     int                         count;
167
168     DWORD                       dwBufferSize;           /* size of whole ALSA buffer in bytes */
169     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
170     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
171     DWORD                       dwPartialOffset;        /* Offset of not yet written bytes in lpPlayPtr */
172
173     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
174     DWORD                       dwLoops;                /* private copy of loop counter */
175
176     DWORD                       dwPlayedTotal;          /* number of bytes actually played since opening */
177     DWORD                       dwWrittenTotal;         /* number of bytes written to ALSA buffer since opening */
178
179     /* synchronization stuff */
180     HANDLE                      hStartUpEvent;
181     HANDLE                      hThread;
182     DWORD                       dwThreadID;
183     ALSA_MSG_RING               msgRing;
184
185     /* DirectSound stuff */
186     DSDRIVERDESC                ds_desc;
187     GUID                        ds_guid;
188 } WINE_WAVEOUT;
189
190 typedef struct {
191     /* Windows information */
192     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
193     WAVEOPENDESC                waveDesc;
194     WORD                        wFlags;
195     PCMWAVEFORMAT               format;
196     WAVEOUTCAPSA                caps;
197
198     /* ALSA information (ALSA 0.9/1.x uses two different devices for playback/capture) */
199     char*                       device;
200     char                        interface_name[64];
201     snd_pcm_t*                  p_handle;                 /* handle to ALSA playback device */
202     snd_pcm_t*                  c_handle;                 /* handle to ALSA capture device */
203     snd_pcm_hw_params_t *       hw_params;              /* ALSA Hw params */
204
205     snd_ctl_t *                 ctl;                    /* control handle for the playback volume */
206     snd_ctl_elem_id_t *         playback_eid;           /* element id of the playback volume control */
207     snd_ctl_elem_value_t *      playback_evalue;        /* element value of the playback volume control */
208     snd_ctl_elem_info_t *       playback_einfo;         /* element info of the playback volume control */
209
210     snd_pcm_sframes_t           (*read)(snd_pcm_t *, void *, snd_pcm_uframes_t );
211
212     struct pollfd               *ufds;
213     int                         count;
214
215     DWORD                       dwPeriodSize;           /* size of OSS buffer period */
216     DWORD                       dwBufferSize;           /* size of whole ALSA buffer in bytes */
217     LPWAVEHDR                   lpQueuePtr;             /* start of queued WAVEHDRs (waiting to be notified) */
218     LPWAVEHDR                   lpPlayPtr;              /* start of not yet fully played buffers */
219
220     LPWAVEHDR                   lpLoopPtr;              /* pointer of first buffer in loop, if any */
221     DWORD                       dwLoops;                /* private copy of loop counter */
222
223     /*DWORD                     dwPlayedTotal; */
224     DWORD                       dwTotalRecorded;
225
226     /* synchronization stuff */
227     HANDLE                      hStartUpEvent;
228     HANDLE                      hThread;
229     DWORD                       dwThreadID;
230     ALSA_MSG_RING               msgRing;
231
232     /* DirectSound stuff */
233     DSDRIVERDESC                ds_desc;
234     GUID                        ds_guid;
235 } WINE_WAVEIN;
236
237 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
238 static DWORD            ALSA_WodNumDevs;
239 static WINE_WAVEIN      WInDev   [MAX_WAVEINDRV];
240 static DWORD            ALSA_WidNumDevs;
241
242 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
243 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
244 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);
245
246 /* These strings used only for tracing */
247 #if 1
248 static const char *wodPlayerCmdString[] = {
249     "WINE_WM_PAUSING",
250     "WINE_WM_RESTARTING",
251     "WINE_WM_RESETTING",
252     "WINE_WM_HEADER",
253     "WINE_WM_UPDATE",
254     "WINE_WM_BREAKLOOP",
255     "WINE_WM_CLOSING",
256     "WINE_WM_STARTING",
257     "WINE_WM_STOPPING",
258 };
259 #endif
260
261 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
262                              PCMWAVEFORMAT* format)
263 {
264     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
265           lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
266           format->wf.nChannels, format->wf.nAvgBytesPerSec);
267     TRACE("Position in bytes=%lu\n", position);
268
269     switch (lpTime->wType) {
270     case TIME_SAMPLES:
271         lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
272         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
273         break;
274     case TIME_MS:
275         lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
276         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
277         break;
278     case TIME_SMPTE:
279         position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
280         lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
281         position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
282         lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
283         lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
284         lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
285         lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
286         lpTime->u.smpte.fps = 30;
287         lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
288         position -= lpTime->u.smpte.frame * format->wf.nSamplesPerSec / lpTime->u.smpte.fps;
289         if (position != 0)
290         {
291             /* Round up */
292             lpTime->u.smpte.frame++;
293         }
294         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
295               lpTime->u.smpte.hour, lpTime->u.smpte.min,
296               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
297         break;
298     default:
299         FIXME("Format %d not supported ! use TIME_BYTES !\n", lpTime->wType);
300         lpTime->wType = TIME_BYTES;
301         /* fall through */
302     case TIME_BYTES:
303         lpTime->u.cb = position;
304         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
305         break;
306     }
307     return MMSYSERR_NOERROR;
308 }
309
310 /*======================================================================*
311  *                  Low level WAVE implementation                       *
312  *======================================================================*/
313
314 /**************************************************************************
315  *                      ALSA_InitializeVolumeCtl                [internal]
316  *
317  * used to initialize the PCM Volume Control
318  */
319 static int ALSA_InitializeVolumeCtl(WINE_WAVEOUT * wwo)
320 {
321     snd_ctl_t *                 ctl = NULL;
322     snd_ctl_card_info_t *       cardinfo;
323     snd_ctl_elem_list_t *       elemlist;
324     snd_ctl_elem_id_t *         e_id;
325     snd_ctl_elem_info_t *       einfo;
326     snd_hctl_t *                hctl = NULL;
327     snd_hctl_elem_t *           elem;
328     int                         nCtrls;
329     int                         i;
330
331     snd_ctl_card_info_alloca(&cardinfo);
332     memset(cardinfo,0,snd_ctl_card_info_sizeof());
333
334     snd_ctl_elem_list_alloca(&elemlist);
335     memset(elemlist,0,snd_ctl_elem_list_sizeof());
336
337     snd_ctl_elem_id_alloca(&e_id);
338     memset(e_id,0,snd_ctl_elem_id_sizeof());
339
340     snd_ctl_elem_info_alloca(&einfo);
341     memset(einfo,0,snd_ctl_elem_info_sizeof());
342
343 #define EXIT_ON_ERROR(f,txt) do \
344 { \
345     int err; \
346     if ( (err = (f) ) < 0) \
347     { \
348         ERR(txt ": %s\n", snd_strerror(err)); \
349         if (hctl) \
350             snd_hctl_close(hctl); \
351         if (ctl) \
352             snd_ctl_close(ctl); \
353         return -1; \
354     } \
355 } while(0)
356
357     EXIT_ON_ERROR( snd_ctl_open(&ctl,"hw",0) , "ctl open failed" );
358     EXIT_ON_ERROR( snd_ctl_card_info(ctl, cardinfo), "card info failed");
359     EXIT_ON_ERROR( snd_ctl_elem_list(ctl, elemlist), "elem list failed");
360
361     nCtrls = snd_ctl_elem_list_get_count(elemlist);
362
363     EXIT_ON_ERROR( snd_hctl_open(&hctl,"hw",0), "hctl open failed");
364     EXIT_ON_ERROR( snd_hctl_load(hctl), "hctl load failed" );
365
366     elem=snd_hctl_first_elem(hctl);
367     for ( i= 0; i<nCtrls; i++) {
368
369         memset(e_id,0,snd_ctl_elem_id_sizeof());
370
371         snd_hctl_elem_get_id(elem,e_id);
372 /*
373         TRACE("ctl: #%d '%s'%d\n",
374                                    snd_ctl_elem_id_get_numid(e_id),
375                                    snd_ctl_elem_id_get_name(e_id),
376                                    snd_ctl_elem_id_get_index(e_id));
377 */
378         if ( !strcmp("PCM Playback Volume", snd_ctl_elem_id_get_name(e_id)) )
379         {
380             EXIT_ON_ERROR( snd_hctl_elem_info(elem,einfo), "hctl elem info failed" );
381
382             /* few sanity checks... you'll never know... */
383             if ( snd_ctl_elem_info_get_type(einfo) != SND_CTL_ELEM_TYPE_INTEGER )
384                 WARN("playback volume control is not an integer\n");
385             if ( !snd_ctl_elem_info_is_readable(einfo) )
386                 WARN("playback volume control is readable\n");
387             if ( !snd_ctl_elem_info_is_writable(einfo) )
388                 WARN("playback volume control is readable\n");
389
390             TRACE("   ctrl range: min=%ld  max=%ld  step=%ld\n",
391                  snd_ctl_elem_info_get_min(einfo),
392                  snd_ctl_elem_info_get_max(einfo),
393                  snd_ctl_elem_info_get_step(einfo));
394
395             EXIT_ON_ERROR( snd_ctl_elem_id_malloc(&wwo->playback_eid), "elem id malloc failed" );
396             EXIT_ON_ERROR( snd_ctl_elem_info_malloc(&wwo->playback_einfo), "elem info malloc failed" );
397             EXIT_ON_ERROR( snd_ctl_elem_value_malloc(&wwo->playback_evalue), "elem value malloc failed" );
398
399             /* ok, now we can safely save these objects for later */
400             snd_ctl_elem_id_copy(wwo->playback_eid, e_id);
401             snd_ctl_elem_info_copy(wwo->playback_einfo, einfo);
402             snd_ctl_elem_value_set_id(wwo->playback_evalue, wwo->playback_eid);
403             wwo->ctl = ctl;
404         }
405
406         elem=snd_hctl_elem_next(elem);
407     }
408     snd_hctl_close(hctl);
409 #undef EXIT_ON_ERROR
410     return 0;
411 }
412
413 /**************************************************************************
414  *                      ALSA_XRUNRecovery               [internal]
415  *
416  * used to recovery from XRUN errors (buffer underflow/overflow)
417  */
418 static int ALSA_XRUNRecovery(WINE_WAVEOUT * wwo, int err)
419 {
420     if (err == -EPIPE) {    /* under-run */
421         err = snd_pcm_prepare(wwo->p_handle);
422         if (err < 0)
423              ERR( "underrun recovery failed. prepare failed: %s\n", snd_strerror(err));
424         return 0;
425     } else if (err == -ESTRPIPE) {
426         while ((err = snd_pcm_resume(wwo->p_handle)) == -EAGAIN)
427             sleep(1);       /* wait until the suspend flag is released */
428         if (err < 0) {
429             err = snd_pcm_prepare(wwo->p_handle);
430             if (err < 0)
431                 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
432         }
433         return 0;
434     }
435     return err;
436 }
437
438 /**************************************************************************
439  *                      ALSA_TraceParameters            [internal]
440  *
441  * used to trace format changes, hw and sw parameters
442  */
443 static void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full)
444 {
445     int err;
446     snd_pcm_format_t   format;
447     snd_pcm_access_t   access;
448
449     err = snd_pcm_hw_params_get_access(hw_params, &access);
450     err = snd_pcm_hw_params_get_format(hw_params, &format);
451
452 #define X(x) ((x)? "true" : "false")
453     if (full)
454         TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
455               "halfd=%s joint=%s \n",
456               X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)),
457               X(snd_pcm_hw_params_can_overrange(hw_params)),
458               X(snd_pcm_hw_params_can_pause(hw_params)),
459               X(snd_pcm_hw_params_can_resume(hw_params)),
460               X(snd_pcm_hw_params_can_sync_start(hw_params)),
461               X(snd_pcm_hw_params_is_batch(hw_params)),
462               X(snd_pcm_hw_params_is_block_transfer(hw_params)),
463               X(snd_pcm_hw_params_is_double(hw_params)),
464               X(snd_pcm_hw_params_is_half_duplex(hw_params)),
465               X(snd_pcm_hw_params_is_joint_duplex(hw_params)));
466 #undef X
467
468     if (access >= 0)
469         TRACE("access=%s\n", snd_pcm_access_name(access));
470     else
471     {
472         snd_pcm_access_mask_t * acmask;
473         snd_pcm_access_mask_alloca(&acmask);
474         snd_pcm_hw_params_get_access_mask(hw_params, acmask);
475         for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++)
476             if (snd_pcm_access_mask_test(acmask, access))
477                 TRACE("access=%s\n", snd_pcm_access_name(access));
478     }
479
480     if (format >= 0)
481     {
482         TRACE("format=%s\n", snd_pcm_format_name(format));
483
484     }
485     else
486     {
487         snd_pcm_format_mask_t *     fmask;
488
489         snd_pcm_format_mask_alloca(&fmask);
490         snd_pcm_hw_params_get_format_mask(hw_params, fmask);
491         for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++)
492             if ( snd_pcm_format_mask_test(fmask, format) )
493                 TRACE("format=%s\n", snd_pcm_format_name(format));
494     }
495
496     do {
497       int err=0;
498       unsigned int val=0;
499       err = snd_pcm_hw_params_get_channels(hw_params, &val); 
500       if (err<0) {
501         unsigned int min = 0;
502         unsigned int max = 0;
503         err = snd_pcm_hw_params_get_channels_min(hw_params, &min), 
504         err = snd_pcm_hw_params_get_channels_max(hw_params, &max); 
505         TRACE("channels_min=%u, channels_min_max=%u\n", min, max);
506       } else {
507         TRACE("channels_min=%d\n", val);
508       }
509     } while(0);
510     do {
511       int err=0;
512       snd_pcm_uframes_t val=0;
513       err = snd_pcm_hw_params_get_buffer_size(hw_params, &val); 
514       if (err<0) {
515         snd_pcm_uframes_t min = 0;
516         snd_pcm_uframes_t max = 0;
517         err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min), 
518         err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max); 
519         TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max);
520       } else {
521         TRACE("buffer_size_min=%lu\n", val);
522       }
523     } while(0);
524
525 #define X(x) do { \
526 int err=0; \
527 int dir=0; \
528 unsigned int val=0; \
529 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
530 if (err<0) { \
531   unsigned int min = 0; \
532   unsigned int max = 0; \
533   err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
534   err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
535   TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
536 } else \
537     TRACE(#x "=%d\n", val); \
538 } while(0)
539
540     X(rate);
541     X(buffer_time);
542     X(periods);
543     do {
544       int err=0;
545       int dir=0;
546       snd_pcm_uframes_t val=0;
547       err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir); 
548       if (err<0) {
549         snd_pcm_uframes_t min = 0;
550         snd_pcm_uframes_t max = 0;
551         err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir), 
552         err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir); 
553         TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max);
554       } else {
555         TRACE("period_size_min=%lu\n", val);
556       }
557     } while(0);
558
559     X(period_time);
560     X(tick_time);
561 #undef X
562
563     if (!sw)
564         return;
565
566
567 }
568
569 /* return a string duplicated on the win32 process heap, free with HeapFree */
570 static char* ALSA_strdup(char *s) {
571     char *result = HeapAlloc(GetProcessHeap(), 0, strlen(s)+1);
572     strcpy(result, s);
573     return result;
574 }
575
576 /******************************************************************
577  *             ALSA_GetDeviceFromReg
578  *
579  * Returns either "default" or reads the registry so the user can
580  * override the playback/record device used.
581  */
582 char *ALSA_GetDeviceFromReg(char *value)
583 {
584     DWORD res;
585     DWORD type;
586     HKEY playbackKey = 0;
587     char *result = NULL;
588     DWORD resultSize;
589
590     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ALSA", 0, KEY_QUERY_VALUE, &playbackKey);
591     if (res != ERROR_SUCCESS) goto end;
592
593     res = RegQueryValueExA(playbackKey, value, NULL, &type, NULL, &resultSize);
594     if (res != ERROR_SUCCESS) goto end;
595
596     if (type != REG_SZ) {
597        ERR("Registry key [HKEY_LOCAL_MACHINE\\Software\\Wine\\Wine\\ALSA\\%s] must be a string\n", value);
598        goto end;
599     }
600
601     result = HeapAlloc(GetProcessHeap(), 0, resultSize);
602     res = RegQueryValueExA(playbackKey, value, NULL, NULL, result, &resultSize);
603
604 end:
605     if (!result) result = ALSA_strdup("default");
606     if (playbackKey) RegCloseKey(playbackKey);
607     return result;
608 }
609
610 /******************************************************************
611  *              ALSA_WaveInit
612  *
613  * Initialize internal structures from ALSA information
614  */
615 LONG ALSA_WaveInit(void)
616 {
617     snd_pcm_t*                  h;
618     snd_pcm_info_t *            info;
619     snd_pcm_hw_params_t *       hw_params;
620     unsigned int ratemin=0;
621     unsigned int ratemax=0;
622     unsigned int chmin=0;
623     unsigned int chmax=0;
624     int dir=0;
625     int err=0;
626     WINE_WAVEOUT*               wwo;
627     WINE_WAVEIN*                wwi;
628
629     wwo = &WOutDev[0];
630
631     /* FIXME: use better values */
632     wwo->device = ALSA_GetDeviceFromReg("PlaybackDevice");
633     TRACE("using waveout device \"%s\"\n", wwo->device);
634
635     snprintf(wwo->interface_name, sizeof(wwo->interface_name), "winealsa: %s", wwo->device);
636
637     wwo->caps.wMid = 0x0002;
638     wwo->caps.wPid = 0x0104;
639     strcpy(wwo->caps.szPname, "SB16 Wave Out");
640     wwo->caps.vDriverVersion = 0x0100;
641     wwo->caps.dwFormats = 0x00000000;
642     wwo->caps.dwSupport = WAVECAPS_VOLUME;
643     strcpy(wwo->ds_desc.szDesc, "WineALSA DirectSound Driver");
644     strcpy(wwo->ds_desc.szDrvName, "winealsa.drv");
645     wwo->ds_guid = DSDEVID_DefaultPlayback;
646
647     if (!wine_dlopen("libasound.so.2", RTLD_LAZY|RTLD_GLOBAL, NULL, 0))
648     {
649         ERR("Error: ALSA lib needs to be loaded with flags RTLD_LAZY and RTLD_GLOBAL.\n");
650         return -1;
651     }
652
653     snd_pcm_info_alloca(&info);
654     snd_pcm_hw_params_alloca(&hw_params);
655
656 #define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0)
657
658     h = NULL;
659     ALSA_WodNumDevs = 0;
660     EXIT_ON_ERROR( snd_pcm_open(&h, wwo->device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) , "open pcm" );
661     if (!h) return -1;
662     ALSA_WodNumDevs++;
663
664     EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
665
666     TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
667        snd_pcm_info_get_device(info),
668        snd_pcm_info_get_id(info),
669        snd_pcm_info_get_name(info),
670        snd_pcm_info_get_subdevice(info),
671        snd_pcm_info_get_subdevice_name(info),
672        snd_pcm_info_get_subdevices_avail(info),
673        snd_pcm_info_get_subdevices_count(info),
674        snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
675        (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
676
677     EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
678 #undef EXIT_ON_ERROR
679
680     err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
681     err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
682     err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
683     err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
684     if (TRACE_ON(wave))
685         ALSA_TraceParameters(hw_params, NULL, TRUE);
686
687     {
688         snd_pcm_format_mask_t *     fmask;
689
690         snd_pcm_format_mask_alloca(&fmask);
691         snd_pcm_hw_params_get_format_mask(hw_params, fmask);
692
693 #define X(r,v) \
694        if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
695        { \
696           if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
697           { \
698               if (chmin <= 1 && 1 <= chmax) \
699                   wwo->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
700               if (chmin <= 2 && 2 <= chmax) \
701                   wwo->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
702           } \
703           if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
704           { \
705               if (chmin <= 1 && 1 <= chmax) \
706                   wwo->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
707               if (chmin <= 2 && 2 <= chmax) \
708                   wwo->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
709           } \
710        }
711        X(11025,1);
712        X(22050,2);
713        X(44100,4);
714        X(48000,48);
715        X(96000,96);
716 #undef X
717     }
718
719     if ( chmin > 1) FIXME("-\n");
720     wwo->caps.wChannels = (chmax >= 2) ? 2 : 1;
721     if (chmin <= 2 && 2 <= chmax)
722         wwo->caps.dwSupport |= WAVECAPS_LRVOLUME;
723
724     /* FIXME: always true ? */
725     wwo->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
726
727     {
728         snd_pcm_access_mask_t *     acmask;
729         snd_pcm_access_mask_alloca(&acmask);
730         snd_pcm_hw_params_get_access_mask(hw_params, acmask);
731
732         /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
733         if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) )
734             wwo->caps.dwSupport |= WAVECAPS_DIRECTSOUND;
735     }
736
737     TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
738           wwo->caps.dwFormats, wwo->caps.dwSupport);
739
740     snd_pcm_close(h);
741
742     ALSA_InitializeVolumeCtl(wwo);
743
744     wwi = &WInDev[0];
745
746     /* FIXME: use better values */
747     wwi->device = ALSA_GetDeviceFromReg("RecordDevice");
748     TRACE("using wavein device \"%s\"\n", wwi->device);
749
750     snprintf(wwi->interface_name, sizeof(wwi->interface_name), "winealsa: %s", wwi->device);
751
752     wwi->caps.wMid = 0x0002;
753     wwi->caps.wPid = 0x0104;
754     strcpy(wwi->caps.szPname, "SB16 Wave In");
755     wwi->caps.vDriverVersion = 0x0100;
756     wwi->caps.dwFormats = 0x00000000;
757     wwi->caps.dwSupport = WAVECAPS_VOLUME;
758     strcpy(wwi->ds_desc.szDesc, "WineALSA DirectSound Driver");
759     strcpy(wwi->ds_desc.szDrvName, "winealsa.drv");
760     wwi->ds_guid = DSDEVID_DefaultPlayback;
761
762     snd_pcm_info_alloca(&info);
763     snd_pcm_hw_params_alloca(&hw_params);
764
765 #define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0)
766
767     h = NULL;
768     ALSA_WidNumDevs = 0;
769     EXIT_ON_ERROR( snd_pcm_open(&h, wwi->device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) , "open pcm" );
770     if (!h) return -1;
771     ALSA_WidNumDevs++;
772
773     EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
774
775     TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
776        snd_pcm_info_get_device(info),
777        snd_pcm_info_get_id(info),
778        snd_pcm_info_get_name(info),
779        snd_pcm_info_get_subdevice(info),
780        snd_pcm_info_get_subdevice_name(info),
781        snd_pcm_info_get_subdevices_avail(info),
782        snd_pcm_info_get_subdevices_count(info),
783        snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
784        (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
785
786     EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
787 #undef EXIT_ON_ERROR
788     err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
789     err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
790     err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
791     err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
792
793     if (TRACE_ON(wave))
794         ALSA_TraceParameters(hw_params, NULL, TRUE);
795
796     {
797         snd_pcm_format_mask_t *     fmask;
798
799         snd_pcm_format_mask_alloca(&fmask);
800         snd_pcm_hw_params_get_format_mask(hw_params, fmask);
801
802 #define X(r,v) \
803        if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
804        { \
805           if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
806           { \
807               if (chmin <= 1 && 1 <= chmax) \
808                   wwi->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
809               if (chmin <= 2 && 2 <= chmax) \
810                   wwi->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
811           } \
812           if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
813           { \
814               if (chmin <= 1 && 1 <= chmax) \
815                   wwi->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
816               if (chmin <= 2 && 2 <= chmax) \
817                   wwi->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
818           } \
819        }
820        X(11025,1);
821        X(22050,2);
822        X(44100,4);
823        X(48000,48);
824        X(96000,96);
825 #undef X
826     }
827
828     if ( chmin > 1) FIXME("-\n");
829     wwi->caps.wChannels = (chmax >= 2) ? 2 : 1;
830     if (chmin <= 2 && 2 <= chmax)
831         wwi->caps.dwSupport |= WAVECAPS_LRVOLUME;
832
833     /* FIXME: always true ? */
834     wwi->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
835
836     {
837         snd_pcm_access_mask_t *     acmask;
838         snd_pcm_access_mask_alloca(&acmask);
839         snd_pcm_hw_params_get_access_mask(hw_params, acmask);
840
841         /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
842         if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) )
843             wwi->caps.dwSupport |= WAVECAPS_DIRECTSOUND;
844     }
845
846     TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
847           wwi->caps.dwFormats, wwi->caps.dwSupport);
848
849     snd_pcm_close(h);
850     
851     return 0;
852 }
853
854 /******************************************************************
855  *              ALSA_InitRingMessage
856  *
857  * Initialize the ring of messages for passing between driver's caller and playback/record
858  * thread
859  */
860 static int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
861 {
862     omr->msg_toget = 0;
863     omr->msg_tosave = 0;
864 #ifdef USE_PIPE_SYNC
865     if (pipe(omr->msg_pipe) < 0) {
866         omr->msg_pipe[0] = -1;
867         omr->msg_pipe[1] = -1;
868         ERR("could not create pipe, error=%s\n", strerror(errno));
869     }
870 #else
871     omr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
872 #endif
873     omr->ring_buffer_size = ALSA_RING_BUFFER_INCREMENT;
874     omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(ALSA_MSG));
875
876     InitializeCriticalSection(&omr->msg_crst);
877     return 0;
878 }
879
880 /******************************************************************
881  *              ALSA_DestroyRingMessage
882  *
883  */
884 static int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
885 {
886 #ifdef USE_PIPE_SYNC
887     close(omr->msg_pipe[0]);
888     close(omr->msg_pipe[1]);
889 #else
890     CloseHandle(omr->msg_event);
891 #endif
892     HeapFree(GetProcessHeap(),0,omr->messages);
893     DeleteCriticalSection(&omr->msg_crst);
894     return 0;
895 }
896
897 /******************************************************************
898  *              ALSA_AddRingMessage
899  *
900  * Inserts a new message into the ring (should be called from DriverProc derivated routines)
901  */
902 static int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
903 {
904     HANDLE      hEvent = INVALID_HANDLE_VALUE;
905
906     EnterCriticalSection(&omr->msg_crst);
907     if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
908     {
909         int old_ring_buffer_size = omr->ring_buffer_size;
910         omr->ring_buffer_size += ALSA_RING_BUFFER_INCREMENT;
911         TRACE("omr->ring_buffer_size=%d\n",omr->ring_buffer_size);
912         omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(ALSA_MSG));
913         /* Now we need to rearrange the ring buffer so that the new
914            buffers just allocated are in between omr->msg_tosave and
915            omr->msg_toget.
916         */
917         if (omr->msg_tosave < omr->msg_toget)
918         {
919             memmove(&(omr->messages[omr->msg_toget + ALSA_RING_BUFFER_INCREMENT]),
920                     &(omr->messages[omr->msg_toget]),
921                     sizeof(ALSA_MSG)*(old_ring_buffer_size - omr->msg_toget)
922                     );
923             omr->msg_toget += ALSA_RING_BUFFER_INCREMENT;
924         }
925     }
926     if (wait)
927     {
928         hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
929         if (hEvent == INVALID_HANDLE_VALUE)
930         {
931             ERR("can't create event !?\n");
932             LeaveCriticalSection(&omr->msg_crst);
933             return 0;
934         }
935         if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
936             FIXME("two fast messages in the queue!!!!\n");
937
938         /* fast messages have to be added at the start of the queue */
939         omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
940
941         omr->messages[omr->msg_toget].msg = msg;
942         omr->messages[omr->msg_toget].param = param;
943         omr->messages[omr->msg_toget].hEvent = hEvent;
944     }
945     else
946     {
947         omr->messages[omr->msg_tosave].msg = msg;
948         omr->messages[omr->msg_tosave].param = param;
949         omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
950         omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
951     }
952     LeaveCriticalSection(&omr->msg_crst);
953     /* signal a new message */
954     SIGNAL_OMR(omr);
955     if (wait)
956     {
957         /* wait for playback/record thread to have processed the message */
958         WaitForSingleObject(hEvent, INFINITE);
959         CloseHandle(hEvent);
960     }
961     return 1;
962 }
963
964 /******************************************************************
965  *              ALSA_RetrieveRingMessage
966  *
967  * Get a message from the ring. Should be called by the playback/record thread.
968  */
969 static int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,
970                                    enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
971 {
972     EnterCriticalSection(&omr->msg_crst);
973
974     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
975     {
976         LeaveCriticalSection(&omr->msg_crst);
977         return 0;
978     }
979
980     *msg = omr->messages[omr->msg_toget].msg;
981     omr->messages[omr->msg_toget].msg = 0;
982     *param = omr->messages[omr->msg_toget].param;
983     *hEvent = omr->messages[omr->msg_toget].hEvent;
984     omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
985     CLEAR_OMR(omr);
986     LeaveCriticalSection(&omr->msg_crst);
987     return 1;
988 }
989
990 /******************************************************************
991  *              ALSA_PeekRingMessage
992  *
993  * Peek at a message from the ring but do not remove it.
994  * Should be called by the playback/record thread.
995  */
996 static int ALSA_PeekRingMessage(ALSA_MSG_RING* omr,
997                                enum win_wm_message *msg,
998                                DWORD *param, HANDLE *hEvent)
999 {
1000     EnterCriticalSection(&omr->msg_crst);
1001
1002     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1003     {
1004         LeaveCriticalSection(&omr->msg_crst);
1005         return 0;
1006     }
1007
1008     *msg = omr->messages[omr->msg_toget].msg;
1009     *param = omr->messages[omr->msg_toget].param;
1010     *hEvent = omr->messages[omr->msg_toget].hEvent;
1011     LeaveCriticalSection(&omr->msg_crst);
1012     return 1;
1013 }
1014
1015 /*======================================================================*
1016  *                  Low level WAVE OUT implementation                   *
1017  *======================================================================*/
1018
1019 /**************************************************************************
1020  *                      wodNotifyClient                 [internal]
1021  */
1022 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1023 {
1024     TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
1025
1026     switch (wMsg) {
1027     case WOM_OPEN:
1028     case WOM_CLOSE:
1029     case WOM_DONE:
1030         if (wwo->wFlags != DCB_NULL &&
1031             !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
1032                             wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
1033             WARN("can't notify client !\n");
1034             return MMSYSERR_ERROR;
1035         }
1036         break;
1037     default:
1038         FIXME("Unknown callback message %u\n", wMsg);
1039         return MMSYSERR_INVALPARAM;
1040     }
1041     return MMSYSERR_NOERROR;
1042 }
1043
1044 /**************************************************************************
1045  *                              wodUpdatePlayedTotal    [internal]
1046  *
1047  */
1048 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, snd_pcm_status_t* ps)
1049 {
1050     snd_pcm_sframes_t delay = 0;
1051     snd_pcm_delay(wwo->p_handle, &delay);
1052     if (snd_pcm_state(wwo->p_handle) != SND_PCM_STATE_RUNNING)
1053         delay=0;
1054     wwo->dwPlayedTotal = wwo->dwWrittenTotal - snd_pcm_frames_to_bytes(wwo->p_handle, delay);
1055     return TRUE;
1056 }
1057
1058 /**************************************************************************
1059  *                              wodPlayer_BeginWaveHdr          [internal]
1060  *
1061  * Makes the specified lpWaveHdr the currently playing wave header.
1062  * If the specified wave header is a begin loop and we're not already in
1063  * a loop, setup the loop.
1064  */
1065 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1066 {
1067     wwo->lpPlayPtr = lpWaveHdr;
1068
1069     if (!lpWaveHdr) return;
1070
1071     if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
1072         if (wwo->lpLoopPtr) {
1073             WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
1074         } else {
1075             TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1076             wwo->lpLoopPtr = lpWaveHdr;
1077             /* Windows does not touch WAVEHDR.dwLoops,
1078              * so we need to make an internal copy */
1079             wwo->dwLoops = lpWaveHdr->dwLoops;
1080         }
1081     }
1082     wwo->dwPartialOffset = 0;
1083 }
1084
1085 /**************************************************************************
1086  *                              wodPlayer_PlayPtrNext           [internal]
1087  *
1088  * Advance the play pointer to the next waveheader, looping if required.
1089  */
1090 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
1091 {
1092     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1093
1094     wwo->dwPartialOffset = 0;
1095     if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
1096         /* We're at the end of a loop, loop if required */
1097         if (--wwo->dwLoops > 0) {
1098             wwo->lpPlayPtr = wwo->lpLoopPtr;
1099         } else {
1100             /* Handle overlapping loops correctly */
1101             if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
1102                 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1103                 /* shall we consider the END flag for the closing loop or for
1104                  * the opening one or for both ???
1105                  * code assumes for closing loop only
1106                  */
1107             } else {
1108                 lpWaveHdr = lpWaveHdr->lpNext;
1109             }
1110             wwo->lpLoopPtr = NULL;
1111             wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
1112         }
1113     } else {
1114         /* We're not in a loop.  Advance to the next wave header */
1115         wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
1116     }
1117
1118     return lpWaveHdr;
1119 }
1120
1121 /**************************************************************************
1122  *                           wodPlayer_DSPWait                  [internal]
1123  * Returns the number of milliseconds to wait for the DSP buffer to play a
1124  * period
1125  */
1126 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
1127 {
1128     /* time for one period to be played */
1129     unsigned int val=0;
1130     int dir=0;
1131     int err=0;
1132     err = snd_pcm_hw_params_get_period_time(wwo->hw_params, &val, &dir);
1133     return val / 1000;
1134 }
1135
1136 /**************************************************************************
1137  *                           wodPlayer_NotifyWait               [internal]
1138  * Returns the number of milliseconds to wait before attempting to notify
1139  * completion of the specified wavehdr.
1140  * This is based on the number of bytes remaining to be written in the
1141  * wave.
1142  */
1143 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1144 {
1145     DWORD dwMillis;
1146
1147     if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
1148         dwMillis = 1;
1149     } else {
1150         dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
1151         if (!dwMillis) dwMillis = 1;
1152     }
1153
1154     return dwMillis;
1155 }
1156
1157
1158 /**************************************************************************
1159  *                           wodPlayer_WriteMaxFrags            [internal]
1160  * Writes the maximum number of frames possible to the DSP and returns
1161  * the number of frames written.
1162  */
1163 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* frames)
1164 {
1165     /* Only attempt to write to free frames */
1166     LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1167     DWORD dwLength = snd_pcm_bytes_to_frames(wwo->p_handle, lpWaveHdr->dwBufferLength - wwo->dwPartialOffset);
1168     int toWrite = min(dwLength, *frames);
1169     int written;
1170
1171     TRACE("Writing wavehdr %p.%lu[%lu]\n", lpWaveHdr, wwo->dwPartialOffset, lpWaveHdr->dwBufferLength);
1172
1173     written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1174     if ( written < 0)
1175     {
1176         /* XRUN occurred. let's try to recover */
1177         ALSA_XRUNRecovery(wwo, written);
1178         written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1179     }
1180     if (written <= 0)
1181     {
1182         /* still in error */
1183         ERR("Error in writing wavehdr. Reason: %s\n", snd_strerror(written));
1184         return written;
1185     }
1186
1187     wwo->dwPartialOffset += snd_pcm_frames_to_bytes(wwo->p_handle, written);
1188     if ( wwo->dwPartialOffset >= lpWaveHdr->dwBufferLength) {
1189         /* this will be used to check if the given wave header has been fully played or not... */
1190         wwo->dwPartialOffset = lpWaveHdr->dwBufferLength;
1191         /* If we wrote all current wavehdr, skip to the next one */
1192         wodPlayer_PlayPtrNext(wwo);
1193     }
1194     *frames -= written;
1195     wwo->dwWrittenTotal += snd_pcm_frames_to_bytes(wwo->p_handle, written);
1196
1197     return written;
1198 }
1199
1200
1201 /**************************************************************************
1202  *                              wodPlayer_NotifyCompletions     [internal]
1203  *
1204  * Notifies and remove from queue all wavehdrs which have been played to
1205  * the speaker (ie. they have cleared the ALSA buffer).  If force is true,
1206  * we notify all wavehdrs and remove them all from the queue even if they
1207  * are unplayed or part of a loop.
1208  */
1209 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1210 {
1211     LPWAVEHDR           lpWaveHdr;
1212
1213     /* Start from lpQueuePtr and keep notifying until:
1214      * - we hit an unwritten wavehdr
1215      * - we hit the beginning of a running loop
1216      * - we hit a wavehdr which hasn't finished playing
1217      */
1218 #if 0
1219     while ((lpWaveHdr = wwo->lpQueuePtr) &&
1220            (force ||
1221             (lpWaveHdr != wwo->lpPlayPtr &&
1222              lpWaveHdr != wwo->lpLoopPtr &&
1223              lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
1224
1225         wwo->lpQueuePtr = lpWaveHdr->lpNext;
1226
1227         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1228         lpWaveHdr->dwFlags |= WHDR_DONE;
1229
1230         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1231     }
1232 #else
1233     for (;;)
1234     {
1235         lpWaveHdr = wwo->lpQueuePtr;
1236         if (!lpWaveHdr) {TRACE("Empty queue\n"); break;}
1237         if (!force)
1238         {
1239             if (lpWaveHdr == wwo->lpPlayPtr) {TRACE("play %p\n", lpWaveHdr); break;}
1240             if (lpWaveHdr == wwo->lpLoopPtr) {TRACE("loop %p\n", lpWaveHdr); break;}
1241             if (lpWaveHdr->reserved > wwo->dwPlayedTotal){TRACE("still playing %p (%lu/%lu)\n", lpWaveHdr, lpWaveHdr->reserved, wwo->dwPlayedTotal);break;}
1242         }
1243         wwo->lpQueuePtr = lpWaveHdr->lpNext;
1244
1245         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1246         lpWaveHdr->dwFlags |= WHDR_DONE;
1247
1248         wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1249     }
1250 #endif
1251     return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
1252         wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
1253 }
1254
1255
1256 void wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
1257 {
1258     unsigned short revents;
1259
1260     if (snd_pcm_state(handle) != SND_PCM_STATE_RUNNING)
1261         return;
1262
1263     while (1) {
1264         poll(ufds, count, -1);
1265         snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
1266
1267         if (revents & POLLERR)
1268             return;
1269
1270         /*if (revents & POLLOUT)
1271                 return 0;*/
1272     }
1273 }
1274
1275
1276 /**************************************************************************
1277  *                              wodPlayer_Reset                 [internal]
1278  *
1279  * wodPlayer helper. Resets current output stream.
1280  */
1281 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo)
1282 {
1283     enum win_wm_message msg;
1284     DWORD                       param;
1285     HANDLE                      ev;
1286     int                         err;
1287
1288     /* flush all possible output */
1289     wait_for_poll(wwo->p_handle, wwo->ufds, wwo->count);
1290
1291     wodUpdatePlayedTotal(wwo, NULL);
1292     /* updates current notify list */
1293     wodPlayer_NotifyCompletions(wwo, FALSE);
1294
1295     if ( (err = snd_pcm_drop(wwo->p_handle)) < 0) {
1296         FIXME("flush: %s\n", snd_strerror(err));
1297         wwo->hThread = 0;
1298         wwo->state = WINE_WS_STOPPED;
1299         ExitThread(-1);
1300     }
1301     if ( (err = snd_pcm_prepare(wwo->p_handle)) < 0 )
1302         ERR("pcm prepare failed: %s\n", snd_strerror(err));
1303
1304     /* remove any buffer */
1305     wodPlayer_NotifyCompletions(wwo, TRUE);
1306
1307     wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1308     wwo->state = WINE_WS_STOPPED;
1309     wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1310     /* Clear partial wavehdr */
1311     wwo->dwPartialOffset = 0;
1312
1313     /* remove any existing message in the ring */
1314     EnterCriticalSection(&wwo->msgRing.msg_crst);
1315     /* return all pending headers in queue */
1316     while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
1317     {
1318         if (msg != WINE_WM_HEADER)
1319         {
1320             FIXME("shouldn't have headers left\n");
1321             SetEvent(ev);
1322             continue;
1323         }
1324         ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
1325         ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
1326
1327         wodNotifyClient(wwo, WOM_DONE, param, 0);
1328     }
1329     RESET_OMR(&wwo->msgRing);
1330     LeaveCriticalSection(&wwo->msgRing.msg_crst);
1331 }
1332
1333 /**************************************************************************
1334  *                    wodPlayer_ProcessMessages                 [internal]
1335  */
1336 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
1337 {
1338     LPWAVEHDR           lpWaveHdr;
1339     enum win_wm_message msg;
1340     DWORD               param;
1341     HANDLE              ev;
1342     int                 err;
1343
1344     while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
1345      TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param); 
1346
1347         switch (msg) {
1348         case WINE_WM_PAUSING:
1349             if ( snd_pcm_state(wwo->p_handle) == SND_PCM_STATE_RUNNING )
1350              {
1351                 err = snd_pcm_pause(wwo->p_handle, 1);
1352                 if ( err < 0 )
1353                     ERR("pcm_pause failed: %s\n", snd_strerror(err));
1354              }
1355             wwo->state = WINE_WS_PAUSED;
1356             SetEvent(ev);
1357             break;
1358         case WINE_WM_RESTARTING:
1359             if (wwo->state == WINE_WS_PAUSED)
1360             {
1361                 if ( snd_pcm_state(wwo->p_handle) == SND_PCM_STATE_PAUSED )
1362                  {
1363                     err = snd_pcm_pause(wwo->p_handle, 0);
1364                     if ( err < 0 )
1365                         ERR("pcm_pause failed: %s\n", snd_strerror(err));
1366                  }
1367                 wwo->state = WINE_WS_PLAYING;
1368             }
1369             SetEvent(ev);
1370             break;
1371         case WINE_WM_HEADER:
1372             lpWaveHdr = (LPWAVEHDR)param;
1373
1374             /* insert buffer at the end of queue */
1375             {
1376                 LPWAVEHDR*      wh;
1377                 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1378                 *wh = lpWaveHdr;
1379             }
1380             if (!wwo->lpPlayPtr)
1381                 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
1382             if (wwo->state == WINE_WS_STOPPED)
1383                 wwo->state = WINE_WS_PLAYING;
1384             break;
1385         case WINE_WM_RESETTING:
1386             wodPlayer_Reset(wwo);
1387             SetEvent(ev);
1388             break;
1389         case WINE_WM_UPDATE:
1390             wodUpdatePlayedTotal(wwo, NULL);
1391             SetEvent(ev);
1392             break;
1393         case WINE_WM_BREAKLOOP:
1394             if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
1395                 /* ensure exit at end of current loop */
1396                 wwo->dwLoops = 1;
1397             }
1398             SetEvent(ev);
1399             break;
1400         case WINE_WM_CLOSING:
1401             /* sanity check: this should not happen since the device must have been reset before */
1402             if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1403             wwo->hThread = 0;
1404             wwo->state = WINE_WS_CLOSED;
1405             SetEvent(ev);
1406             ExitThread(0);
1407             /* shouldn't go here */
1408         default:
1409             FIXME("unknown message %d\n", msg);
1410             break;
1411         }
1412     }
1413 }
1414
1415 /**************************************************************************
1416  *                           wodPlayer_FeedDSP                  [internal]
1417  * Feed as much sound data as we can into the DSP and return the number of
1418  * milliseconds before it will be necessary to feed the DSP again.
1419  */
1420 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1421 {
1422     DWORD               availInQ;
1423
1424     wodUpdatePlayedTotal(wwo, NULL);
1425     availInQ = snd_pcm_avail_update(wwo->p_handle);
1426
1427 #if 0
1428     /* input queue empty and output buffer with less than one fragment to play */
1429     if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + wwo->dwFragmentSize) {
1430         TRACE("Run out of wavehdr:s...\n");
1431         return INFINITE;
1432     }
1433 #endif
1434
1435     /* no more room... no need to try to feed */
1436     if (availInQ > 0) {
1437         /* Feed from partial wavehdr */
1438         if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
1439             wodPlayer_WriteMaxFrags(wwo, &availInQ);
1440         }
1441
1442         /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1443         if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
1444             do {
1445                 TRACE("Setting time to elapse for %p to %lu\n",
1446                       wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
1447                 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1448                 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1449             } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
1450         }
1451     }
1452
1453     return wodPlayer_DSPWait(wwo);
1454 }
1455
1456 /**************************************************************************
1457  *                              wodPlayer                       [internal]
1458  */
1459 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
1460 {
1461     WORD          uDevID = (DWORD)pmt;
1462     WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1463     DWORD         dwNextFeedTime = INFINITE;   /* Time before DSP needs feeding */
1464     DWORD         dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1465     DWORD         dwSleepTime;
1466
1467     wwo->state = WINE_WS_STOPPED;
1468     SetEvent(wwo->hStartUpEvent);
1469
1470     for (;;) {
1471         /** Wait for the shortest time before an action is required.  If there
1472          *  are no pending actions, wait forever for a command.
1473          */
1474         dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1475         TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1476         WAIT_OMR(&wwo->msgRing, dwSleepTime);
1477         wodPlayer_ProcessMessages(wwo);
1478         if (wwo->state == WINE_WS_PLAYING) {
1479             dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1480             dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1481             if (dwNextFeedTime == INFINITE) {
1482                 /* FeedDSP ran out of data, but before giving up, */
1483                 /* check that a notification didn't give us more */
1484                 wodPlayer_ProcessMessages(wwo);
1485                 if (wwo->lpPlayPtr) {
1486                     TRACE("recovering\n");
1487                     dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1488                 }
1489             }
1490         } else {
1491             dwNextFeedTime = dwNextNotifyTime = INFINITE;
1492         }
1493     }
1494 }
1495
1496 /**************************************************************************
1497  *                      wodGetDevCaps                           [internal]
1498  */
1499 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
1500 {
1501     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1502
1503     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1504
1505     if (wDevID >= MAX_WAVEOUTDRV) {
1506         TRACE("MAX_WAVOUTDRV reached !\n");
1507         return MMSYSERR_BADDEVICEID;
1508     }
1509
1510     memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1511     return MMSYSERR_NOERROR;
1512 }
1513
1514 /**************************************************************************
1515  *                              wodOpen                         [internal]
1516  */
1517 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1518 {
1519     WINE_WAVEOUT*               wwo;
1520     snd_pcm_hw_params_t *       hw_params;
1521     snd_pcm_sw_params_t *       sw_params;
1522     snd_pcm_access_t            access;
1523     snd_pcm_format_t            format;
1524     unsigned int                rate;
1525     unsigned int                buffer_time = 500000;
1526     unsigned int                period_time = 10000;
1527     snd_pcm_uframes_t           buffer_size;
1528     snd_pcm_uframes_t           period_size;
1529     int                         flags;
1530     snd_pcm_t *                 pcm;
1531     int                         err=0;
1532     int                         dir=0;
1533
1534     snd_pcm_hw_params_alloca(&hw_params);
1535     snd_pcm_sw_params_alloca(&sw_params);
1536
1537     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1538     if (lpDesc == NULL) {
1539         WARN("Invalid Parameter !\n");
1540         return MMSYSERR_INVALPARAM;
1541     }
1542     if (wDevID >= MAX_WAVEOUTDRV) {
1543         TRACE("MAX_WAVOUTDRV reached !\n");
1544         return MMSYSERR_BADDEVICEID;
1545     }
1546
1547     /* only PCM format is supported so far... */
1548     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1549         lpDesc->lpFormat->nChannels == 0 ||
1550         lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
1551         lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
1552         (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
1553         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1554              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1555              lpDesc->lpFormat->nSamplesPerSec);
1556         return WAVERR_BADFORMAT;
1557     }
1558
1559     if (dwFlags & WAVE_FORMAT_QUERY) {
1560         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1561              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1562              lpDesc->lpFormat->nSamplesPerSec);
1563         return MMSYSERR_NOERROR;
1564     }
1565
1566     wwo = &WOutDev[wDevID];
1567
1568     if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
1569         /* not supported, ignore it */
1570         dwFlags &= ~WAVE_DIRECTSOUND;
1571
1572     wwo->p_handle = 0;
1573     flags = SND_PCM_NONBLOCK;
1574     if ( dwFlags & WAVE_DIRECTSOUND )
1575         flags |= SND_PCM_ASYNC;
1576
1577     if ( (err = snd_pcm_open(&pcm, wwo->device, SND_PCM_STREAM_PLAYBACK, flags)) < 0)
1578     {
1579         ERR("Error open: %s\n", snd_strerror(err));
1580         return MMSYSERR_NOTENABLED;
1581     }
1582
1583     wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1584
1585     memcpy(&wwo->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
1586     memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1587
1588     if (wwo->format.wBitsPerSample == 0) {
1589         WARN("Resetting zeroed wBitsPerSample\n");
1590         wwo->format.wBitsPerSample = 8 *
1591             (wwo->format.wf.nAvgBytesPerSec /
1592              wwo->format.wf.nSamplesPerSec) /
1593             wwo->format.wf.nChannels;
1594     }
1595
1596     snd_pcm_hw_params_any(pcm, hw_params);
1597
1598 #define EXIT_ON_ERROR(f,e,txt) do \
1599 { \
1600     int err; \
1601     if ( (err = (f) ) < 0) \
1602     { \
1603         ERR(txt ": %s\n", snd_strerror(err)); \
1604         snd_pcm_close(pcm); \
1605         return e; \
1606     } \
1607 } while(0)
1608
1609     access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
1610     if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
1611         WARN("mmap not available. switching to standard write.\n");
1612         access = SND_PCM_ACCESS_RW_INTERLEAVED;
1613         EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
1614         wwo->write = snd_pcm_writei;
1615     }
1616     else
1617         wwo->write = snd_pcm_mmap_writei;
1618
1619     EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.wf.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");
1620
1621     format = (wwo->format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8;
1622     EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");
1623
1624     rate = wwo->format.wf.nSamplesPerSec;
1625     dir=0;
1626     err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
1627     if (err < 0) {
1628         ERR("Rate %ld Hz not available for playback: %s\n", wwo->format.wf.nSamplesPerSec, snd_strerror(rate));
1629         snd_pcm_close(pcm);
1630         return WAVERR_BADFORMAT;
1631     }
1632     if (rate != wwo->format.wf.nSamplesPerSec) {
1633         ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwo->format.wf.nSamplesPerSec, rate);
1634         snd_pcm_close(pcm);
1635         return WAVERR_BADFORMAT;
1636     }
1637     dir=0; 
1638     EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
1639     dir=0; 
1640     EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
1641
1642     EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
1643     
1644     err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
1645     err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
1646
1647     snd_pcm_sw_params_current(pcm, sw_params);
1648     EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, dwFlags & WAVE_DIRECTSOUND ? INT_MAX : 1 ), MMSYSERR_ERROR, "unable to set start threshold");
1649     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
1650     EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
1651     EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
1652     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
1653     EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
1654 #undef EXIT_ON_ERROR
1655
1656     snd_pcm_prepare(pcm);
1657
1658     if (TRACE_ON(wave))
1659         ALSA_TraceParameters(hw_params, sw_params, FALSE);
1660
1661     /* now, we can save all required data for later use... */
1662     if ( wwo->hw_params )
1663         snd_pcm_hw_params_free(wwo->hw_params);
1664     snd_pcm_hw_params_malloc(&(wwo->hw_params));
1665     snd_pcm_hw_params_copy(wwo->hw_params, hw_params);
1666
1667     wwo->dwBufferSize = buffer_size;
1668     wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
1669     wwo->p_handle = pcm;
1670     wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1671     wwo->dwPartialOffset = 0;
1672
1673     ALSA_InitRingMessage(&wwo->msgRing);
1674
1675     wwo->count = snd_pcm_poll_descriptors_count (wwo->p_handle);
1676     if (wwo->count <= 0) {
1677         ERR("Invalid poll descriptors count\n");
1678         return MMSYSERR_ERROR;
1679     }
1680
1681     wwo->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwo->count);
1682     if (wwo->ufds == NULL) {
1683         ERR("No enough memory\n");
1684         return MMSYSERR_NOMEM;
1685     }
1686     if ((err = snd_pcm_poll_descriptors(wwo->p_handle, wwo->ufds, wwo->count)) < 0) {
1687         ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
1688         return MMSYSERR_ERROR;
1689     }
1690
1691     if (!(dwFlags & WAVE_DIRECTSOUND)) {
1692         wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1693         wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1694         WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1695         CloseHandle(wwo->hStartUpEvent);
1696     } else {
1697         wwo->hThread = INVALID_HANDLE_VALUE;
1698         wwo->dwThreadID = 0;
1699     }
1700     wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1701
1702     TRACE("handle=%08lx \n", (DWORD)wwo->p_handle);
1703 /*    if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
1704         ERR("Fragment doesn't contain an integral number of data blocks\n");
1705 */
1706     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1707           wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
1708           wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1709           wwo->format.wf.nBlockAlign);
1710
1711     return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1712 }
1713
1714
1715 /**************************************************************************
1716  *                              wodClose                        [internal]
1717  */
1718 static DWORD wodClose(WORD wDevID)
1719 {
1720     DWORD               ret = MMSYSERR_NOERROR;
1721     WINE_WAVEOUT*       wwo;
1722
1723     TRACE("(%u);\n", wDevID);
1724
1725     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1726         WARN("bad device ID !\n");
1727         return MMSYSERR_BADDEVICEID;
1728     }
1729
1730     wwo = &WOutDev[wDevID];
1731     if (wwo->lpQueuePtr) {
1732         WARN("buffers still playing !\n");
1733         ret = WAVERR_STILLPLAYING;
1734     } else {
1735         if (wwo->hThread != INVALID_HANDLE_VALUE) {
1736             ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1737         }
1738         ALSA_DestroyRingMessage(&wwo->msgRing);
1739
1740         snd_pcm_hw_params_free(wwo->hw_params);
1741         wwo->hw_params = NULL;
1742
1743         snd_pcm_close(wwo->p_handle);
1744         wwo->p_handle = NULL;
1745
1746         ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1747     }
1748
1749     HeapFree(GetProcessHeap(), 0, wwo->ufds);
1750     return ret;
1751 }
1752
1753
1754 /**************************************************************************
1755  *                              wodWrite                        [internal]
1756  *
1757  */
1758 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1759 {
1760     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1761
1762     /* first, do the sanity checks... */
1763     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1764         WARN("bad dev ID !\n");
1765         return MMSYSERR_BADDEVICEID;
1766     }
1767
1768     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1769         return WAVERR_UNPREPARED;
1770
1771     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1772         return WAVERR_STILLPLAYING;
1773
1774     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1775     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1776     lpWaveHdr->lpNext = 0;
1777
1778     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1779
1780     return MMSYSERR_NOERROR;
1781 }
1782
1783 /**************************************************************************
1784  *                              wodPrepare                      [internal]
1785  */
1786 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1787 {
1788     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1789
1790     if (wDevID >= MAX_WAVEOUTDRV) {
1791         WARN("bad device ID !\n");
1792         return MMSYSERR_BADDEVICEID;
1793     }
1794
1795     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1796         return WAVERR_STILLPLAYING;
1797
1798     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1799     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1800     return MMSYSERR_NOERROR;
1801 }
1802
1803 /**************************************************************************
1804  *                              wodUnprepare                    [internal]
1805  */
1806 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1807 {
1808     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1809
1810     if (wDevID >= MAX_WAVEOUTDRV) {
1811         WARN("bad device ID !\n");
1812         return MMSYSERR_BADDEVICEID;
1813     }
1814
1815     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1816         return WAVERR_STILLPLAYING;
1817
1818     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1819     lpWaveHdr->dwFlags |= WHDR_DONE;
1820
1821     return MMSYSERR_NOERROR;
1822 }
1823
1824 /**************************************************************************
1825  *                      wodPause                                [internal]
1826  */
1827 static DWORD wodPause(WORD wDevID)
1828 {
1829     TRACE("(%u);!\n", wDevID);
1830
1831     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1832         WARN("bad device ID !\n");
1833         return MMSYSERR_BADDEVICEID;
1834     }
1835
1836     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1837
1838     return MMSYSERR_NOERROR;
1839 }
1840
1841 /**************************************************************************
1842  *                      wodRestart                              [internal]
1843  */
1844 static DWORD wodRestart(WORD wDevID)
1845 {
1846     TRACE("(%u);\n", wDevID);
1847
1848     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1849         WARN("bad device ID !\n");
1850         return MMSYSERR_BADDEVICEID;
1851     }
1852
1853     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1854         ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1855     }
1856
1857     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1858     /* FIXME: Myst crashes with this ... hmm -MM
1859        return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1860     */
1861
1862     return MMSYSERR_NOERROR;
1863 }
1864
1865 /**************************************************************************
1866  *                      wodReset                                [internal]
1867  */
1868 static DWORD wodReset(WORD wDevID)
1869 {
1870     TRACE("(%u);\n", wDevID);
1871
1872     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1873         WARN("bad device ID !\n");
1874         return MMSYSERR_BADDEVICEID;
1875     }
1876
1877     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1878
1879     return MMSYSERR_NOERROR;
1880 }
1881
1882 /**************************************************************************
1883  *                              wodGetPosition                  [internal]
1884  */
1885 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1886 {
1887     WINE_WAVEOUT*       wwo;
1888
1889     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1890
1891     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1892         WARN("bad device ID !\n");
1893         return MMSYSERR_BADDEVICEID;
1894     }
1895
1896     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1897
1898     wwo = &WOutDev[wDevID];
1899     ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1900
1901     return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->format);
1902 }
1903
1904 /**************************************************************************
1905  *                              wodBreakLoop                    [internal]
1906  */
1907 static DWORD wodBreakLoop(WORD wDevID)
1908 {
1909     TRACE("(%u);\n", wDevID);
1910
1911     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1912         WARN("bad device ID !\n");
1913         return MMSYSERR_BADDEVICEID;
1914     }
1915     ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1916     return MMSYSERR_NOERROR;
1917 }
1918
1919 /**************************************************************************
1920  *                              wodGetVolume                    [internal]
1921  */
1922 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1923 {
1924     WORD               left, right;
1925     WINE_WAVEOUT*      wwo;
1926     int                count;
1927     long               min, max;
1928
1929     TRACE("(%u, %p);\n", wDevID, lpdwVol);
1930     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1931         WARN("bad device ID !\n");
1932         return MMSYSERR_BADDEVICEID;
1933     }
1934     wwo = &WOutDev[wDevID];
1935     count = snd_ctl_elem_info_get_count(wwo->playback_einfo);
1936     min = snd_ctl_elem_info_get_min(wwo->playback_einfo);
1937     max = snd_ctl_elem_info_get_max(wwo->playback_einfo);
1938
1939 #define VOLUME_ALSA_TO_WIN(x) (((x)-min) * 65536 /(max-min))
1940     if (lpdwVol == NULL)
1941         return MMSYSERR_NOTENABLED;
1942
1943     switch (count)
1944     {
1945         case 2:
1946             left = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 0));
1947             right = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 1));
1948             break;
1949         case 1:
1950             left = right = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 0));
1951             break;
1952         default:
1953             WARN("%d channels mixer not supported\n", count);
1954             return MMSYSERR_NOERROR;
1955      }
1956 #undef VOLUME_ALSA_TO_WIN
1957
1958     TRACE("left=%d right=%d !\n", left, right);
1959     *lpdwVol = MAKELONG( left, right );
1960     return MMSYSERR_NOERROR;
1961 }
1962
1963 /**************************************************************************
1964  *                              wodSetVolume                    [internal]
1965  */
1966 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1967 {
1968     WORD               left, right;
1969     WINE_WAVEOUT*      wwo;
1970     int                count, err;
1971     long               min, max;
1972
1973     TRACE("(%u, %08lX);\n", wDevID, dwParam);
1974     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1975         WARN("bad device ID !\n");
1976         return MMSYSERR_BADDEVICEID;
1977     }
1978     wwo = &WOutDev[wDevID];
1979     count=snd_ctl_elem_info_get_count(wwo->playback_einfo);
1980     min = snd_ctl_elem_info_get_min(wwo->playback_einfo);
1981     max = snd_ctl_elem_info_get_max(wwo->playback_einfo);
1982
1983     left  = LOWORD(dwParam);
1984     right = HIWORD(dwParam);
1985
1986 #define VOLUME_WIN_TO_ALSA(x) ( (((x) * (max-min)) / 65536) + min )
1987     switch (count)
1988     {
1989         case 2:
1990             snd_ctl_elem_value_set_integer(wwo->playback_evalue, 0, VOLUME_WIN_TO_ALSA(left));
1991             snd_ctl_elem_value_set_integer(wwo->playback_evalue, 1, VOLUME_WIN_TO_ALSA(right));
1992             break;
1993         case 1:
1994             snd_ctl_elem_value_set_integer(wwo->playback_evalue, 0, VOLUME_WIN_TO_ALSA(left));
1995             break;
1996         default:
1997             WARN("%d channels mixer not supported\n", count);
1998      }
1999 #undef VOLUME_WIN_TO_ALSA
2000     if ( (err = snd_ctl_elem_write(wwo->ctl, wwo->playback_evalue)) < 0)
2001     {
2002         ERR("error writing snd_ctl_elem_value: %s\n", snd_strerror(err));
2003     }
2004     return MMSYSERR_NOERROR;
2005 }
2006
2007 /**************************************************************************
2008  *                              wodGetNumDevs                   [internal]
2009  */
2010 static  DWORD   wodGetNumDevs(void)
2011 {
2012     return ALSA_WodNumDevs;
2013 }
2014
2015 /**************************************************************************
2016  *                              wodDevInterfaceSize             [internal]
2017  */
2018 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
2019 {
2020     TRACE("(%u, %p)\n", wDevID, dwParam1);
2021
2022     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2023                                     NULL, 0 ) * sizeof(WCHAR);
2024     return MMSYSERR_NOERROR;
2025 }
2026
2027 /**************************************************************************
2028  *                              wodDevInterface                 [internal]
2029  */
2030 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
2031 {
2032     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2033                                         NULL, 0 ) * sizeof(WCHAR))
2034     {
2035         MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2036                             dwParam1, dwParam2 / sizeof(WCHAR));
2037         return MMSYSERR_NOERROR;
2038     }
2039     return MMSYSERR_INVALPARAM;
2040 }
2041
2042 /**************************************************************************
2043  *                              wodMessage (WINEALSA.@)
2044  */
2045 DWORD WINAPI ALSA_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2046                              DWORD dwParam1, DWORD dwParam2)
2047 {
2048     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2049           wDevID, wMsg, dwUser, dwParam1, dwParam2);
2050
2051     switch (wMsg) {
2052     case DRVM_INIT:
2053     case DRVM_EXIT:
2054     case DRVM_ENABLE:
2055     case DRVM_DISABLE:
2056         /* FIXME: Pretend this is supported */
2057         return 0;
2058     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
2059     case WODM_CLOSE:            return wodClose         (wDevID);
2060     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPSA)dwParam1,      dwParam2);
2061     case WODM_GETNUMDEVS:       return wodGetNumDevs    ();
2062     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
2063     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
2064     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
2065     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
2066     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2067     case WODM_PAUSE:            return wodPause         (wDevID);
2068     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
2069     case WODM_BREAKLOOP:        return wodBreakLoop     (wDevID);
2070     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2071     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
2072     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
2073     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
2074     case WODM_RESTART:          return wodRestart       (wDevID);
2075     case WODM_RESET:            return wodReset         (wDevID);
2076     case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
2077     case DRV_QUERYDEVICEINTERFACE:     return wodDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
2078     case DRV_QUERYDSOUNDIFACE:  return wodDsCreate      (wDevID, (PIDSDRIVER*)dwParam1);
2079     case DRV_QUERYDSOUNDDESC:   return wodDsDesc        (wDevID, (PDSDRIVERDESC)dwParam1);
2080     case DRV_QUERYDSOUNDGUID:   return wodDsGuid        (wDevID, (LPGUID)dwParam1);
2081
2082     default:
2083         FIXME("unknown message %d!\n", wMsg);
2084     }
2085     return MMSYSERR_NOTSUPPORTED;
2086 }
2087
2088 /*======================================================================*
2089  *                  Low level DSOUND implementation                     *
2090  *======================================================================*/
2091
2092 typedef struct IDsDriverImpl IDsDriverImpl;
2093 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
2094
2095 struct IDsDriverImpl
2096 {
2097     /* IUnknown fields */
2098     ICOM_VFIELD(IDsDriver);
2099     DWORD               ref;
2100     /* IDsDriverImpl fields */
2101     UINT                wDevID;
2102     IDsDriverBufferImpl*primary;
2103 };
2104
2105 struct IDsDriverBufferImpl
2106 {
2107     /* IUnknown fields */
2108     ICOM_VFIELD(IDsDriverBuffer);
2109     DWORD                     ref;
2110     /* IDsDriverBufferImpl fields */
2111     IDsDriverImpl*            drv;
2112
2113     CRITICAL_SECTION          mmap_crst;
2114     LPVOID                    mmap_buffer;
2115     DWORD                     mmap_buflen_bytes;
2116     snd_pcm_uframes_t         mmap_buflen_frames;
2117     snd_pcm_channel_area_t *  mmap_areas;
2118     snd_async_handler_t *     mmap_async_handler;
2119 };
2120
2121 static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi)
2122 {
2123     WINE_WAVEOUT *     wwo = &(WOutDev[pdbi->drv->wDevID]);
2124     snd_pcm_state_t    state = snd_pcm_state(wwo->p_handle);
2125
2126     if ( state == SND_PCM_STATE_XRUN )
2127     {
2128         int            err = snd_pcm_prepare(wwo->p_handle);
2129         TRACE("xrun occurred\n");
2130         if ( err < 0 )
2131             ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err));
2132     }
2133     else if ( state == SND_PCM_STATE_SUSPENDED )
2134     {
2135         int            err = snd_pcm_resume(wwo->p_handle);
2136         TRACE("recovery from suspension occurred\n");
2137         if (err < 0 && err != -EAGAIN){
2138             err = snd_pcm_prepare(wwo->p_handle);
2139             if (err < 0)
2140                 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
2141         }
2142     }
2143 }
2144
2145 static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi)
2146 {
2147     WINE_WAVEOUT *     wwo = &(WOutDev[pdbi->drv->wDevID]);
2148     unsigned int       channels;
2149     snd_pcm_format_t   format;
2150     snd_pcm_uframes_t  period_size;
2151     snd_pcm_sframes_t  avail;
2152     int err;
2153     int dir=0;
2154
2155     if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->p_handle)
2156         return;
2157
2158     err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2159     err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2160     dir=0;
2161     err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2162     avail = snd_pcm_avail_update(wwo->p_handle);
2163
2164     DSDB_CheckXRUN(pdbi);
2165
2166     TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels );
2167
2168     while (avail >= period_size)
2169     {
2170         const snd_pcm_channel_area_t *areas;
2171         snd_pcm_uframes_t     ofs;
2172         snd_pcm_uframes_t     frames;
2173         int                   err;
2174
2175         frames = avail / period_size * period_size; /* round down to a multiple of period_size */
2176
2177         EnterCriticalSection(&pdbi->mmap_crst);
2178
2179         snd_pcm_mmap_begin(wwo->p_handle, &areas, &ofs, &frames);
2180         snd_pcm_areas_copy(areas, ofs, pdbi->mmap_areas, ofs, channels, frames, format);
2181         err = snd_pcm_mmap_commit(wwo->p_handle, ofs, frames);
2182
2183         LeaveCriticalSection(&pdbi->mmap_crst);
2184
2185         if ( err != (snd_pcm_sframes_t) frames)
2186             ERR("mmap partially failed.\n");
2187
2188         avail = snd_pcm_avail_update(wwo->p_handle);
2189     }
2190  }
2191
2192 static void DSDB_PCMCallback(snd_async_handler_t *ahandler)
2193 {
2194     /* snd_pcm_t *               handle = snd_async_handler_get_pcm(ahandler); */
2195     IDsDriverBufferImpl*      pdbi = snd_async_handler_get_callback_private(ahandler);
2196     TRACE("callback called\n");
2197     DSDB_MMAPCopy(pdbi);
2198 }
2199
2200 static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi)
2201  {
2202     WINE_WAVEOUT *            wwo = &(WOutDev[pdbi->drv->wDevID]);
2203     snd_pcm_format_t          format;
2204     snd_pcm_uframes_t         frames;
2205     unsigned int              channels;
2206     unsigned int              bits_per_sample;
2207     unsigned int              bits_per_frame;
2208     snd_pcm_channel_area_t *  a;
2209     unsigned int              c;
2210     int                       err;
2211
2212     err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2213     err = snd_pcm_hw_params_get_buffer_size(wwo->hw_params, &frames);
2214     err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2215     bits_per_sample = snd_pcm_format_physical_width(format);
2216     bits_per_frame = bits_per_sample * channels;
2217
2218
2219     if (TRACE_ON(wave))
2220         ALSA_TraceParameters(wwo->hw_params, NULL, FALSE);
2221
2222     TRACE("format=%s  frames=%ld  channels=%d  bits_per_sample=%d  bits_per_frame=%d\n",
2223           snd_pcm_format_name(format), frames, channels, bits_per_sample, bits_per_frame);
2224
2225     pdbi->mmap_buflen_frames = frames;
2226     pdbi->mmap_buflen_bytes = snd_pcm_frames_to_bytes( wwo->p_handle, frames );
2227     pdbi->mmap_buffer = HeapAlloc(GetProcessHeap(),0,pdbi->mmap_buflen_bytes);
2228     if (!pdbi->mmap_buffer)
2229         return DSERR_OUTOFMEMORY;
2230
2231     snd_pcm_format_set_silence(format, pdbi->mmap_buffer, frames );
2232
2233     TRACE("created mmap buffer of %ld frames (%ld bytes) at %p\n",
2234         frames, pdbi->mmap_buflen_bytes, pdbi->mmap_buffer);
2235
2236     pdbi->mmap_areas = HeapAlloc(GetProcessHeap(),0,channels*sizeof(snd_pcm_channel_area_t));
2237     if (!pdbi->mmap_areas)
2238         return DSERR_OUTOFMEMORY;
2239
2240     a = pdbi->mmap_areas;
2241     for (c = 0; c < channels; c++, a++)
2242     {
2243         a->addr = pdbi->mmap_buffer;
2244         a->first = bits_per_sample * c;
2245         a->step = bits_per_frame;
2246         TRACE("Area %d: addr=%p  first=%d  step=%d\n", c, a->addr, a->first, a->step);
2247     }
2248
2249     InitializeCriticalSection(&pdbi->mmap_crst);
2250
2251     err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, wwo->p_handle, DSDB_PCMCallback, pdbi);
2252     if ( err < 0 )
2253      {
2254         ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
2255         return DSERR_GENERIC;
2256      }
2257
2258     return DS_OK;
2259  }
2260
2261 static void DSDB_DestroyMMAP(IDsDriverBufferImpl* pdbi)
2262 {
2263     TRACE("mmap buffer %p destroyed\n", pdbi->mmap_buffer);
2264     HeapFree(GetProcessHeap(), 0, pdbi->mmap_areas);
2265     HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer);
2266     pdbi->mmap_areas = NULL;
2267     pdbi->mmap_buffer = NULL;
2268     DeleteCriticalSection(&pdbi->mmap_crst);
2269 }
2270
2271
2272 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
2273 {
2274     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2275     FIXME("(): stub!\n");
2276     return DSERR_UNSUPPORTED;
2277 }
2278
2279 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
2280 {
2281     ICOM_THIS(IDsDriverBufferImpl,iface);
2282     TRACE("(%p)\n",iface);
2283     return ++This->ref;
2284 }
2285
2286 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
2287 {
2288     ICOM_THIS(IDsDriverBufferImpl,iface);
2289     TRACE("(%p)\n",iface);
2290     if (--This->ref)
2291         return This->ref;
2292     if (This == This->drv->primary)
2293         This->drv->primary = NULL;
2294     DSDB_DestroyMMAP(This);
2295     HeapFree(GetProcessHeap(), 0, This);
2296     return 0;
2297 }
2298
2299 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
2300                                                LPVOID*ppvAudio1,LPDWORD pdwLen1,
2301                                                LPVOID*ppvAudio2,LPDWORD pdwLen2,
2302                                                DWORD dwWritePosition,DWORD dwWriteLen,
2303                                                DWORD dwFlags)
2304 {
2305     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2306     TRACE("(%p)\n",iface);
2307     return DSERR_UNSUPPORTED;
2308 }
2309
2310 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
2311                                                  LPVOID pvAudio1,DWORD dwLen1,
2312                                                  LPVOID pvAudio2,DWORD dwLen2)
2313 {
2314     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2315     TRACE("(%p)\n",iface);
2316     return DSERR_UNSUPPORTED;
2317 }
2318
2319 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
2320                                                     LPWAVEFORMATEX pwfx)
2321 {
2322     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2323     TRACE("(%p,%p)\n",iface,pwfx);
2324     return DSERR_BUFFERLOST;
2325 }
2326
2327 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
2328 {
2329     /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2330     TRACE("(%p,%ld): stub\n",iface,dwFreq);
2331     return DSERR_UNSUPPORTED;
2332 }
2333
2334 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
2335 {
2336     DWORD vol;
2337     ICOM_THIS(IDsDriverBufferImpl,iface);
2338     TRACE("(%p,%p)\n",iface,pVolPan);
2339     vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16);
2340                                                                                 
2341     if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) {
2342         WARN("wodSetVolume failed\n");
2343         return DSERR_INVALIDPARAM;
2344     }
2345
2346     return DS_OK;
2347 }
2348
2349 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
2350 {
2351     /* ICOM_THIS(IDsDriverImpl,iface); */
2352     TRACE("(%p,%ld): stub\n",iface,dwNewPos);
2353     return DSERR_UNSUPPORTED;
2354 }
2355
2356 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
2357                                                       LPDWORD lpdwPlay, LPDWORD lpdwWrite)
2358 {
2359     ICOM_THIS(IDsDriverBufferImpl,iface);
2360     WINE_WAVEOUT *      wwo = &(WOutDev[This->drv->wDevID]);
2361     snd_pcm_uframes_t   hw_ptr;
2362     snd_pcm_uframes_t   period_size;
2363     int dir;
2364     int err;
2365
2366     if (wwo->hw_params == NULL) return DSERR_GENERIC;
2367
2368     dir=0;
2369     err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2370
2371     if (wwo->p_handle == NULL) return DSERR_GENERIC;
2372     /** we need to track down buffer underruns */
2373     DSDB_CheckXRUN(This);
2374
2375     EnterCriticalSection(&This->mmap_crst);
2376     /* FIXME: snd_pcm_mmap_hw_ptr() should not be accessed by a user app. */
2377     /*        It will NOT return what why want anyway. */
2378     hw_ptr = _snd_pcm_mmap_hw_ptr(wwo->p_handle);
2379     if (lpdwPlay)
2380         *lpdwPlay = snd_pcm_frames_to_bytes(wwo->p_handle, hw_ptr/ period_size  * period_size) % This->mmap_buflen_bytes;
2381     if (lpdwWrite)
2382         *lpdwWrite = snd_pcm_frames_to_bytes(wwo->p_handle, (hw_ptr / period_size + 1) * period_size ) % This->mmap_buflen_bytes;
2383     LeaveCriticalSection(&This->mmap_crst);
2384
2385     TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1);
2386     return DS_OK;
2387 }
2388
2389 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
2390 {
2391     ICOM_THIS(IDsDriverBufferImpl,iface);
2392     WINE_WAVEOUT *       wwo = &(WOutDev[This->drv->wDevID]);
2393     snd_pcm_state_t      state;
2394     int                  err;
2395
2396     TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
2397
2398     if (wwo->p_handle == NULL) return DSERR_GENERIC;
2399
2400     state = snd_pcm_state(wwo->p_handle);
2401     if ( state == SND_PCM_STATE_SETUP )
2402     {
2403         err = snd_pcm_prepare(wwo->p_handle);
2404         state = snd_pcm_state(wwo->p_handle);
2405     }
2406     if ( state == SND_PCM_STATE_PREPARED )
2407      {
2408         DSDB_MMAPCopy(This);
2409         err = snd_pcm_start(wwo->p_handle);
2410      }
2411     return DS_OK;
2412 }
2413
2414 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
2415 {
2416     ICOM_THIS(IDsDriverBufferImpl,iface);
2417     WINE_WAVEOUT *    wwo = &(WOutDev[This->drv->wDevID]);
2418     int               err;
2419     DWORD             play;
2420     DWORD             write;
2421
2422     TRACE("(%p)\n",iface);
2423
2424     if (wwo->p_handle == NULL) return DSERR_GENERIC;
2425
2426     /* ring buffer wrap up detection */
2427     IDsDriverBufferImpl_GetPosition(iface, &play, &write);
2428     if ( play > write)
2429     {
2430         TRACE("writepos wrapper up\n");
2431         return DS_OK;
2432     }
2433
2434     if ( ( err = snd_pcm_drop(wwo->p_handle)) < 0 )
2435     {
2436         ERR("error while stopping pcm: %s\n", snd_strerror(err));
2437         return DSERR_GENERIC;
2438     }
2439     return DS_OK;
2440 }
2441
2442 static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
2443 {
2444     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2445     IDsDriverBufferImpl_QueryInterface,
2446     IDsDriverBufferImpl_AddRef,
2447     IDsDriverBufferImpl_Release,
2448     IDsDriverBufferImpl_Lock,
2449     IDsDriverBufferImpl_Unlock,
2450     IDsDriverBufferImpl_SetFormat,
2451     IDsDriverBufferImpl_SetFrequency,
2452     IDsDriverBufferImpl_SetVolumePan,
2453     IDsDriverBufferImpl_SetPosition,
2454     IDsDriverBufferImpl_GetPosition,
2455     IDsDriverBufferImpl_Play,
2456     IDsDriverBufferImpl_Stop
2457 };
2458
2459 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
2460 {
2461     /* ICOM_THIS(IDsDriverImpl,iface); */
2462     FIXME("(%p): stub!\n",iface);
2463     return DSERR_UNSUPPORTED;
2464 }
2465
2466 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
2467 {
2468     ICOM_THIS(IDsDriverImpl,iface);
2469     TRACE("(%p)\n",iface);
2470     This->ref++;
2471     return This->ref;
2472 }
2473
2474 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
2475 {
2476     ICOM_THIS(IDsDriverImpl,iface);
2477     TRACE("(%p)\n",iface);
2478     if (--This->ref)
2479         return This->ref;
2480     HeapFree(GetProcessHeap(),0,This);
2481     return 0;
2482 }
2483
2484 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
2485 {
2486     ICOM_THIS(IDsDriverImpl,iface);
2487     TRACE("(%p,%p)\n",iface,pDesc);
2488     memcpy(pDesc, &(WOutDev[This->wDevID].ds_desc), sizeof(DSDRIVERDESC));
2489     pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
2490         DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
2491     pDesc->dnDevNode            = WOutDev[This->wDevID].waveDesc.dnDevNode;
2492     pDesc->wVxdId               = 0;
2493     pDesc->wReserved            = 0;
2494     pDesc->ulDeviceNum          = This->wDevID;
2495     pDesc->dwHeapType           = DSDHEAP_NOHEAP;
2496     pDesc->pvDirectDrawHeap     = NULL;
2497     pDesc->dwMemStartAddress    = 0;
2498     pDesc->dwMemEndAddress      = 0;
2499     pDesc->dwMemAllocExtra      = 0;
2500     pDesc->pvReserved1          = NULL;
2501     pDesc->pvReserved2          = NULL;
2502     return DS_OK;
2503 }
2504
2505 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
2506 {
2507     /* ICOM_THIS(IDsDriverImpl,iface); */
2508     TRACE("(%p)\n",iface);
2509     return DS_OK;
2510 }
2511
2512 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
2513 {
2514     /* ICOM_THIS(IDsDriverImpl,iface); */
2515     TRACE("(%p)\n",iface);
2516     return DS_OK;
2517 }
2518
2519 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
2520 {
2521     ICOM_THIS(IDsDriverImpl,iface);
2522     TRACE("(%p,%p)\n",iface,pCaps);
2523     memset(pCaps, 0, sizeof(*pCaps));
2524
2525     pCaps->dwFlags = DSCAPS_PRIMARYMONO;
2526     if ( WOutDev[This->wDevID].caps.wChannels == 2 )
2527         pCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
2528
2529     if ( WOutDev[This->wDevID].caps.dwFormats & (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 ) )
2530         pCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
2531
2532     if ( WOutDev[This->wDevID].caps.dwFormats & (WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16))
2533         pCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
2534
2535     pCaps->dwPrimaryBuffers = 1;
2536     TRACE("caps=0x%X\n",(unsigned int)pCaps->dwFlags);
2537     pCaps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2538     pCaps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2539
2540     /* the other fields only apply to secondary buffers, which we don't support
2541      * (unless we want to mess with wavetable synthesizers and MIDI) */
2542     return DS_OK;
2543 }
2544
2545 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
2546                                                       LPWAVEFORMATEX pwfx,
2547                                                       DWORD dwFlags, DWORD dwCardAddress,
2548                                                       LPDWORD pdwcbBufferSize,
2549                                                       LPBYTE *ppbBuffer,
2550                                                       LPVOID *ppvObj)
2551 {
2552     ICOM_THIS(IDsDriverImpl,iface);
2553     IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
2554     int err;
2555
2556     TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
2557     /* we only support primary buffers */
2558     if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
2559         return DSERR_UNSUPPORTED;
2560     if (This->primary)
2561         return DSERR_ALLOCATED;
2562     if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
2563         return DSERR_CONTROLUNAVAIL;
2564
2565     *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
2566     if (*ippdsdb == NULL)
2567         return DSERR_OUTOFMEMORY;
2568     (*ippdsdb)->lpVtbl  = &dsdbvt;
2569     (*ippdsdb)->ref     = 1;
2570     (*ippdsdb)->drv     = This;
2571
2572     err = DSDB_CreateMMAP((*ippdsdb));
2573     if ( err != DS_OK )
2574      {
2575         HeapFree(GetProcessHeap(), 0, *ippdsdb);
2576         *ippdsdb = NULL;
2577         return err;
2578      }
2579     *ppbBuffer = (*ippdsdb)->mmap_buffer;
2580     *pdwcbBufferSize = (*ippdsdb)->mmap_buflen_bytes;
2581
2582     This->primary = *ippdsdb;
2583
2584     /* buffer is ready to go */
2585     TRACE("buffer created at %p\n", *ippdsdb);
2586     return DS_OK;
2587 }
2588
2589 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
2590                                                          PIDSDRIVERBUFFER pBuffer,
2591                                                          LPVOID *ppvObj)
2592 {
2593     /* ICOM_THIS(IDsDriverImpl,iface); */
2594     TRACE("(%p,%p): stub\n",iface,pBuffer);
2595     return DSERR_INVALIDCALL;
2596 }
2597
2598 static ICOM_VTABLE(IDsDriver) dsdvt =
2599 {
2600     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2601     IDsDriverImpl_QueryInterface,
2602     IDsDriverImpl_AddRef,
2603     IDsDriverImpl_Release,
2604     IDsDriverImpl_GetDriverDesc,
2605     IDsDriverImpl_Open,
2606     IDsDriverImpl_Close,
2607     IDsDriverImpl_GetCaps,
2608     IDsDriverImpl_CreateSoundBuffer,
2609     IDsDriverImpl_DuplicateSoundBuffer
2610 };
2611
2612 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2613 {
2614     IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
2615
2616     TRACE("driver created\n");
2617
2618     /* the HAL isn't much better than the HEL if we can't do mmap() */
2619     if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
2620         ERR("DirectSound flag not set\n");
2621         MESSAGE("This sound card's driver does not support direct access\n");
2622         MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2623         return MMSYSERR_NOTSUPPORTED;
2624     }
2625
2626     *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
2627     if (!*idrv)
2628         return MMSYSERR_NOMEM;
2629     (*idrv)->lpVtbl     = &dsdvt;
2630     (*idrv)->ref        = 1;
2631
2632     (*idrv)->wDevID     = wDevID;
2633     (*idrv)->primary    = NULL;
2634     return MMSYSERR_NOERROR;
2635 }
2636
2637 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2638 {
2639     memcpy(desc, &(WOutDev[wDevID].ds_desc), sizeof(DSDRIVERDESC));
2640     return MMSYSERR_NOERROR;
2641 }
2642
2643 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid)
2644 {
2645     TRACE("(%d,%p)\n",wDevID,pGuid);
2646
2647     memcpy(pGuid, &(WOutDev[wDevID].ds_guid), sizeof(GUID));
2648
2649     return MMSYSERR_NOERROR;
2650 }
2651
2652 /*======================================================================*
2653 *                  Low level WAVE IN implementation                     *
2654 *======================================================================*/
2655
2656 /**************************************************************************
2657 *                       widNotifyClient                 [internal]
2658 */
2659 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
2660 {
2661    TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
2662
2663    switch (wMsg) {
2664    case WIM_OPEN:
2665    case WIM_CLOSE:
2666    case WIM_DATA:
2667        if (wwi->wFlags != DCB_NULL &&
2668            !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave,
2669                            wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
2670            WARN("can't notify client !\n");
2671            return MMSYSERR_ERROR;
2672        }
2673        break;
2674    default:
2675        FIXME("Unknown callback message %u\n", wMsg);
2676        return MMSYSERR_INVALPARAM;
2677    }
2678    return MMSYSERR_NOERROR;
2679 }
2680
2681 /**************************************************************************
2682  *                      widGetDevCaps                           [internal]
2683  */
2684 static DWORD widGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
2685 {
2686     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
2687
2688     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
2689
2690     if (wDevID >= MAX_WAVEINDRV) {
2691         TRACE("MAX_WAVOUTDRV reached !\n");
2692         return MMSYSERR_BADDEVICEID;
2693     }
2694
2695     memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
2696     return MMSYSERR_NOERROR;
2697 }
2698
2699 /**************************************************************************
2700  *                              widRecorder_ReadHeaders         [internal]
2701  */
2702 static void widRecorder_ReadHeaders(WINE_WAVEIN * wwi)
2703 {
2704     enum win_wm_message tmp_msg;
2705     DWORD               tmp_param;
2706     HANDLE              tmp_ev;
2707     WAVEHDR*            lpWaveHdr;
2708
2709     while (ALSA_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
2710         if (tmp_msg == WINE_WM_HEADER) {
2711             LPWAVEHDR*  wh;
2712             lpWaveHdr = (LPWAVEHDR)tmp_param;
2713             lpWaveHdr->lpNext = 0;
2714
2715             if (wwi->lpQueuePtr == 0)
2716                 wwi->lpQueuePtr = lpWaveHdr;
2717             else {
2718                 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2719                 *wh = lpWaveHdr;
2720             }
2721         } else {
2722             ERR("should only have headers left\n");
2723         }
2724     }
2725 }
2726
2727 /**************************************************************************
2728  *                              widRecorder                     [internal]
2729  */
2730 static  DWORD   CALLBACK        widRecorder(LPVOID pmt)
2731 {
2732     WORD                uDevID = (DWORD)pmt;
2733     WINE_WAVEIN*        wwi = (WINE_WAVEIN*)&WInDev[uDevID];
2734     WAVEHDR*            lpWaveHdr;
2735     DWORD               dwSleepTime;
2736     DWORD               bytesRead;
2737     LPVOID              buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwPeriodSize);
2738     char               *pOffset = buffer;
2739     enum win_wm_message msg;
2740     DWORD               param;
2741     HANDLE              ev;
2742     DWORD               frames_per_period;
2743
2744     wwi->state = WINE_WS_STOPPED;
2745     wwi->dwTotalRecorded = 0;
2746     wwi->lpQueuePtr = NULL;
2747
2748     SetEvent(wwi->hStartUpEvent);
2749
2750     /* make sleep time to be # of ms to output a period */
2751     dwSleepTime = (1024/*wwi-dwPeriodSize => overrun!*/ * 1000) / wwi->format.wf.nAvgBytesPerSec;
2752     frames_per_period = snd_pcm_bytes_to_frames(wwi->p_handle, wwi->dwPeriodSize); 
2753     TRACE("sleeptime=%ld ms\n", dwSleepTime);
2754
2755     for (;;) {
2756         /* wait for dwSleepTime or an event in thread's queue */
2757         /* FIXME: could improve wait time depending on queue state,
2758          * ie, number of queued fragments
2759          */
2760         if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
2761         {
2762             int periods;
2763             DWORD frames;
2764             DWORD bytes;
2765             DWORD read;
2766
2767             lpWaveHdr = wwi->lpQueuePtr;
2768             /* read all the fragments accumulated so far */
2769             frames = snd_pcm_avail_update(wwi->p_handle);
2770             bytes = snd_pcm_frames_to_bytes(wwi->p_handle, frames);
2771             TRACE("frames = %ld  bytes = %ld\n", frames, bytes);
2772             periods = bytes / wwi->dwPeriodSize;
2773             while ((periods > 0) && (wwi->lpQueuePtr))
2774             {
2775                 periods--;
2776                 bytes = wwi->dwPeriodSize;
2777                 TRACE("bytes = %ld\n",bytes);
2778                 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwPeriodSize)
2779                 {
2780                     /* directly read fragment in wavehdr */
2781                     read = wwi->read(wwi->p_handle, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames_per_period);
2782                     bytesRead = snd_pcm_frames_to_bytes(wwi->p_handle, read);
2783                         
2784                     TRACE("bytesRead=%ld (direct)\n", bytesRead);
2785                     if (bytesRead != (DWORD) -1)
2786                     {
2787                         /* update number of bytes recorded in current buffer and by this device */
2788                         lpWaveHdr->dwBytesRecorded += bytesRead;
2789                         wwi->dwTotalRecorded       += bytesRead;
2790
2791                         /* buffer is full. notify client */
2792                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2793                         {
2794                             /* must copy the value of next waveHdr, because we have no idea of what
2795                              * will be done with the content of lpWaveHdr in callback
2796                              */
2797                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
2798
2799                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2800                             lpWaveHdr->dwFlags |=  WHDR_DONE;
2801
2802                             wwi->lpQueuePtr = lpNext;
2803                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2804                             lpWaveHdr = lpNext;
2805                         }
2806                     }
2807                 }
2808                 else
2809                 {
2810                     /* read the fragment in a local buffer */
2811                     read = wwi->read(wwi->p_handle, buffer, frames_per_period);
2812                     bytesRead = snd_pcm_frames_to_bytes(wwi->p_handle, read);
2813                     pOffset = buffer;
2814
2815                     TRACE("bytesRead=%ld (local)\n", bytesRead);
2816
2817                     /* copy data in client buffers */
2818                     while (bytesRead != (DWORD) -1 && bytesRead > 0)
2819                     {
2820                         DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
2821
2822                         memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
2823                                pOffset,
2824                                dwToCopy);
2825
2826                         /* update number of bytes recorded in current buffer and by this device */
2827                         lpWaveHdr->dwBytesRecorded += dwToCopy;
2828                         wwi->dwTotalRecorded += dwToCopy;
2829                         bytesRead -= dwToCopy;
2830                         pOffset   += dwToCopy;
2831
2832                         /* client buffer is full. notify client */
2833                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2834                         {
2835                             /* must copy the value of next waveHdr, because we have no idea of what
2836                              * will be done with the content of lpWaveHdr in callback
2837                              */
2838                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
2839                             TRACE("lpNext=%p\n", lpNext);
2840
2841                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2842                             lpWaveHdr->dwFlags |=  WHDR_DONE;
2843
2844                             wwi->lpQueuePtr = lpNext;
2845                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2846
2847                             lpWaveHdr = lpNext;
2848                             if (!lpNext && bytesRead) {
2849                                 /* before we give up, check for more header messages */
2850                                 while (ALSA_PeekRingMessage(&wwi->msgRing, &msg, &param, &ev))
2851                                 {
2852                                     if (msg == WINE_WM_HEADER) {
2853                                         LPWAVEHDR hdr;
2854                                         ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev);
2855                                         hdr = ((LPWAVEHDR)param);
2856                                         TRACE("msg = %s, hdr = %p, ev = %p\n", wodPlayerCmdString[msg - WM_USER - 1], hdr, ev);
2857                                         hdr->lpNext = 0;
2858                                         if (lpWaveHdr == 0) {
2859                                             /* new head of queue */
2860                                             wwi->lpQueuePtr = lpWaveHdr = hdr;
2861                                         } else {
2862                                             /* insert buffer at the end of queue */
2863                                             LPWAVEHDR*  wh;
2864                                             for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2865                                             *wh = hdr;
2866                                         }
2867                                     } else
2868                                         break;
2869                                 }
2870
2871                                 if (lpWaveHdr == 0) {
2872                                     /* no more buffer to copy data to, but we did read more.
2873                                      * what hasn't been copied will be dropped
2874                                      */
2875                                     WARN("buffer under run! %lu bytes dropped.\n", bytesRead);
2876                                     wwi->lpQueuePtr = NULL;
2877                                     break;
2878                                 }
2879                             }
2880                         }
2881                     }
2882                 }
2883             }
2884         }
2885
2886         WAIT_OMR(&wwi->msgRing, dwSleepTime);
2887
2888         while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
2889         {
2890             TRACE("msg=%s param=0x%lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
2891             switch (msg) {
2892             case WINE_WM_PAUSING:
2893                 wwi->state = WINE_WS_PAUSED;
2894                 /*FIXME("Device should stop recording\n");*/
2895                 SetEvent(ev);
2896                 break;
2897             case WINE_WM_STARTING:
2898                 wwi->state = WINE_WS_PLAYING;
2899                 snd_pcm_start(wwi->p_handle);
2900                 SetEvent(ev);
2901                 break;
2902             case WINE_WM_HEADER:
2903                 lpWaveHdr = (LPWAVEHDR)param;
2904                 lpWaveHdr->lpNext = 0;
2905
2906                 /* insert buffer at the end of queue */
2907                 {
2908                     LPWAVEHDR*  wh;
2909                     for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2910                     *wh = lpWaveHdr;
2911                 }
2912                 break;
2913             case WINE_WM_STOPPING:
2914                 if (wwi->state != WINE_WS_STOPPED)
2915                 {
2916                     snd_pcm_drain(wwi->p_handle);
2917
2918                     /* read any headers in queue */
2919                     widRecorder_ReadHeaders(wwi);
2920
2921                     /* return current buffer to app */
2922                     lpWaveHdr = wwi->lpQueuePtr;
2923                     if (lpWaveHdr)
2924                     {
2925                         LPWAVEHDR       lpNext = lpWaveHdr->lpNext;
2926                         TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
2927                         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2928                         lpWaveHdr->dwFlags |= WHDR_DONE;
2929                         wwi->lpQueuePtr = lpNext;
2930                         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2931                     }
2932                 }
2933                 wwi->state = WINE_WS_STOPPED;
2934                 SetEvent(ev);
2935                 break;
2936             case WINE_WM_RESETTING:
2937                 if (wwi->state != WINE_WS_STOPPED)
2938                 {
2939                     snd_pcm_drain(wwi->p_handle);
2940                 }
2941                 wwi->state = WINE_WS_STOPPED;
2942                 wwi->dwTotalRecorded = 0;
2943
2944                 /* read any headers in queue */
2945                 widRecorder_ReadHeaders(wwi);
2946
2947                 /* return all buffers to the app */
2948                 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
2949                     TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
2950                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2951                     lpWaveHdr->dwFlags |= WHDR_DONE;
2952                     wwi->lpQueuePtr = lpWaveHdr->lpNext;
2953                     widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2954                 }
2955
2956                 wwi->lpQueuePtr = NULL;
2957                 SetEvent(ev);
2958                 break;
2959             case WINE_WM_CLOSING:
2960                 wwi->hThread = 0;
2961                 wwi->state = WINE_WS_CLOSED;
2962                 SetEvent(ev);
2963                 HeapFree(GetProcessHeap(), 0, buffer);
2964                 ExitThread(0);
2965                 /* shouldn't go here */
2966             case WINE_WM_UPDATE:
2967                 SetEvent(ev);
2968                 break;
2969
2970             default:
2971                 FIXME("unknown message %d\n", msg);
2972                 break;
2973             }
2974         }
2975     }
2976     ExitThread(0);
2977     /* just for not generating compilation warnings... should never be executed */
2978     return 0;
2979 }
2980
2981 /**************************************************************************
2982  *                              widOpen                         [internal]
2983  */
2984 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
2985 {
2986     WINE_WAVEIN*                wwi;
2987     snd_pcm_hw_params_t *       hw_params;
2988     snd_pcm_sw_params_t *       sw_params;
2989     snd_pcm_access_t            access;
2990     snd_pcm_format_t            format;
2991     unsigned int                rate;
2992     unsigned int                buffer_time = 500000;
2993     unsigned int                period_time = 10000;
2994     snd_pcm_uframes_t           buffer_size;
2995     snd_pcm_uframes_t           period_size;
2996     int                         flags;
2997     snd_pcm_t *                 pcm;
2998     int                         err;
2999     int                         dir;
3000
3001     snd_pcm_hw_params_alloca(&hw_params);
3002     snd_pcm_sw_params_alloca(&sw_params);
3003
3004     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
3005     if (lpDesc == NULL) {
3006         WARN("Invalid Parameter !\n");
3007         return MMSYSERR_INVALPARAM;
3008     }
3009     if (wDevID >= MAX_WAVEOUTDRV) {
3010         TRACE("MAX_WAVOUTDRV reached !\n");
3011         return MMSYSERR_BADDEVICEID;
3012     }
3013
3014     /* only PCM format is supported so far... */
3015     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
3016         lpDesc->lpFormat->nChannels == 0 ||
3017         lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
3018         lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
3019         (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
3020         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3021              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3022              lpDesc->lpFormat->nSamplesPerSec);
3023         return WAVERR_BADFORMAT;
3024     }
3025
3026     if (dwFlags & WAVE_FORMAT_QUERY) {
3027         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3028              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3029              lpDesc->lpFormat->nSamplesPerSec);
3030         return MMSYSERR_NOERROR;
3031     }
3032
3033     wwi = &WInDev[wDevID];
3034
3035     if ((dwFlags & WAVE_DIRECTSOUND) && !(wwi->caps.dwSupport & WAVECAPS_DIRECTSOUND))
3036         /* not supported, ignore it */
3037         dwFlags &= ~WAVE_DIRECTSOUND;
3038
3039     wwi->p_handle = 0;
3040     flags = SND_PCM_NONBLOCK;
3041     if ( dwFlags & WAVE_DIRECTSOUND )
3042         flags |= SND_PCM_ASYNC;
3043
3044     if ( (err=snd_pcm_open(&pcm, wwi->device, SND_PCM_STREAM_CAPTURE, dwFlags)) < 0 )
3045     {
3046         ERR("Error open: %s\n", snd_strerror(err));
3047         return MMSYSERR_NOTENABLED;
3048     }
3049
3050     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
3051
3052     memcpy(&wwi->waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
3053     memcpy(&wwi->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
3054
3055     if (wwi->format.wBitsPerSample == 0) {
3056         WARN("Resetting zeroed wBitsPerSample\n");
3057         wwi->format.wBitsPerSample = 8 *
3058             (wwi->format.wf.nAvgBytesPerSec /
3059              wwi->format.wf.nSamplesPerSec) /
3060             wwi->format.wf.nChannels;
3061     }
3062
3063     snd_pcm_hw_params_any(pcm, hw_params);
3064
3065 #define EXIT_ON_ERROR(f,e,txt) do \
3066 { \
3067     int err; \
3068     if ( (err = (f) ) < 0) \
3069     { \
3070         ERR(txt ": %s\n", snd_strerror(err)); \
3071         snd_pcm_close(pcm); \
3072         return e; \
3073     } \
3074 } while(0)
3075
3076     access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
3077     if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
3078         WARN("mmap not available. switching to standard write.\n");
3079         access = SND_PCM_ACCESS_RW_INTERLEAVED;
3080         EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
3081         wwi->read = snd_pcm_readi;
3082     }
3083     else
3084         wwi->read = snd_pcm_mmap_readi;
3085
3086     EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.wf.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");
3087
3088     format = (wwi->format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8;
3089     EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");
3090
3091     rate = wwi->format.wf.nSamplesPerSec;
3092     dir = 0;
3093     err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
3094     if (err < 0) {
3095         ERR("Rate %ld Hz not available for playback: %s\n", wwi->format.wf.nSamplesPerSec, snd_strerror(rate));
3096         snd_pcm_close(pcm);
3097         return WAVERR_BADFORMAT;
3098     }
3099     if (rate != wwi->format.wf.nSamplesPerSec) {
3100         ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwi->format.wf.nSamplesPerSec, rate);
3101         snd_pcm_close(pcm);
3102         return WAVERR_BADFORMAT;
3103     }
3104     
3105     dir=0; 
3106     EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
3107     dir=0; 
3108     EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
3109
3110     EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
3111     
3112     dir=0;
3113     err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
3114     err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
3115
3116     snd_pcm_sw_params_current(pcm, sw_params);
3117     EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, dwFlags & WAVE_DIRECTSOUND ? INT_MAX : 1 ), MMSYSERR_ERROR, "unable to set start threshold");
3118     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
3119     EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
3120     EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
3121     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
3122     EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
3123 #undef EXIT_ON_ERROR
3124
3125     snd_pcm_prepare(pcm);
3126
3127     if (TRACE_ON(wave))
3128         ALSA_TraceParameters(hw_params, sw_params, FALSE);
3129
3130     /* now, we can save all required data for later use... */
3131     if ( wwi->hw_params )
3132         snd_pcm_hw_params_free(wwi->hw_params);
3133     snd_pcm_hw_params_malloc(&(wwi->hw_params));
3134     snd_pcm_hw_params_copy(wwi->hw_params, hw_params);
3135
3136     wwi->dwBufferSize = buffer_size;
3137     wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
3138     wwi->p_handle = pcm;
3139
3140     ALSA_InitRingMessage(&wwi->msgRing);
3141
3142     wwi->count = snd_pcm_poll_descriptors_count (wwi->p_handle);
3143     if (wwi->count <= 0) {
3144         ERR("Invalid poll descriptors count\n");
3145         return MMSYSERR_ERROR;
3146     }
3147
3148     wwi->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwi->count);
3149     if (wwi->ufds == NULL) {
3150         ERR("No enough memory\n");
3151         return MMSYSERR_NOMEM;
3152     }
3153     if ((err = snd_pcm_poll_descriptors(wwi->p_handle, wwi->ufds, wwi->count)) < 0) {
3154         ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
3155         return MMSYSERR_ERROR;
3156     }
3157
3158     wwi->dwPeriodSize = period_size;
3159     /*if (wwi->dwFragmentSize % wwi->format.wf.nBlockAlign)
3160         ERR("Fragment doesn't contain an integral number of data blocks\n");
3161     */
3162     TRACE("dwPeriodSize=%lu\n", wwi->dwPeriodSize);
3163     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
3164           wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
3165           wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
3166           wwi->format.wf.nBlockAlign);
3167
3168     if (!(dwFlags & WAVE_DIRECTSOUND)) {
3169         wwi->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
3170         wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
3171         WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
3172         CloseHandle(wwi->hStartUpEvent);
3173     } else {
3174         wwi->hThread = INVALID_HANDLE_VALUE;
3175         wwi->dwThreadID = 0;
3176     }
3177     wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
3178
3179     return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
3180 }
3181
3182
3183 /**************************************************************************
3184  *                              widClose                        [internal]
3185  */
3186 static DWORD widClose(WORD wDevID)
3187 {
3188     DWORD               ret = MMSYSERR_NOERROR;
3189     WINE_WAVEIN*        wwi;
3190
3191     TRACE("(%u);\n", wDevID);
3192
3193     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3194         WARN("bad device ID !\n");
3195         return MMSYSERR_BADDEVICEID;
3196     }
3197
3198     wwi = &WInDev[wDevID];
3199     if (wwi->lpQueuePtr) {
3200         WARN("buffers still playing !\n");
3201         ret = WAVERR_STILLPLAYING;
3202     } else {
3203         if (wwi->hThread != INVALID_HANDLE_VALUE) {
3204             ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
3205         }
3206         ALSA_DestroyRingMessage(&wwi->msgRing);
3207
3208         snd_pcm_hw_params_free(wwi->hw_params);
3209         wwi->hw_params = NULL;
3210
3211         snd_pcm_close(wwi->p_handle);
3212         wwi->p_handle = NULL;
3213
3214         ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
3215     }
3216
3217     HeapFree(GetProcessHeap(), 0, wwi->ufds);
3218     return ret;
3219 }
3220
3221 /**************************************************************************
3222  *                              widAddBuffer                    [internal]
3223  *
3224  */
3225 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3226 {
3227     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3228
3229     /* first, do the sanity checks... */
3230     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3231         WARN("bad dev ID !\n");
3232         return MMSYSERR_BADDEVICEID;
3233     }
3234
3235     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
3236         return WAVERR_UNPREPARED;
3237
3238     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3239         return WAVERR_STILLPLAYING;
3240
3241     lpWaveHdr->dwFlags &= ~WHDR_DONE;
3242     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
3243     lpWaveHdr->lpNext = 0;
3244
3245     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
3246
3247     return MMSYSERR_NOERROR;
3248 }
3249
3250 /**************************************************************************
3251  *                              widPrepare                      [internal]
3252  */
3253 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3254 {
3255     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3256
3257     if (wDevID >= MAX_WAVEINDRV) {
3258         WARN("bad device ID !\n");
3259         return MMSYSERR_BADDEVICEID;
3260     }
3261
3262     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3263         return WAVERR_STILLPLAYING;
3264
3265     lpWaveHdr->dwFlags |= WHDR_PREPARED;
3266     lpWaveHdr->dwFlags &= ~WHDR_DONE;
3267     return MMSYSERR_NOERROR;
3268 }
3269
3270 /**************************************************************************
3271  *                              widUnprepare                    [internal]
3272  */
3273 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3274 {
3275     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3276
3277     if (wDevID >= MAX_WAVEINDRV) {
3278         WARN("bad device ID !\n");
3279         return MMSYSERR_BADDEVICEID;
3280     }
3281
3282     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3283         return WAVERR_STILLPLAYING;
3284
3285     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
3286     lpWaveHdr->dwFlags |= WHDR_DONE;
3287
3288     return MMSYSERR_NOERROR;
3289 }
3290
3291 /**************************************************************************
3292  *                              widStart                        [internal]
3293  *
3294  */
3295 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3296 {
3297     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3298
3299     /* first, do the sanity checks... */
3300     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3301         WARN("bad dev ID !\n");
3302         return MMSYSERR_BADDEVICEID;
3303     }
3304     
3305     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
3306
3307     Sleep(500);
3308
3309     return MMSYSERR_NOERROR;
3310 }
3311
3312 /**************************************************************************
3313  *                              widStop                 [internal]
3314  *
3315  */
3316 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3317 {
3318     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3319
3320     /* first, do the sanity checks... */
3321     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3322         WARN("bad dev ID !\n");
3323         return MMSYSERR_BADDEVICEID;
3324     }
3325
3326     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
3327
3328     return MMSYSERR_NOERROR;
3329 }
3330
3331 /**************************************************************************
3332  *                      widReset                                [internal]
3333  */
3334 static DWORD widReset(WORD wDevID)
3335 {
3336     TRACE("(%u);\n", wDevID);
3337     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3338         WARN("can't reset !\n");
3339         return MMSYSERR_INVALHANDLE;
3340     }
3341     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
3342     return MMSYSERR_NOERROR;
3343 }
3344
3345 /**************************************************************************
3346  *                              widGetPosition                  [internal]
3347  */
3348 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
3349 {
3350     WINE_WAVEIN*        wwi;
3351
3352     FIXME("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
3353
3354     if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3355         WARN("can't get pos !\n");
3356         return MMSYSERR_INVALHANDLE;
3357     }
3358     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
3359
3360     wwi = &WInDev[wDevID];
3361     ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_UPDATE, 0, TRUE);
3362
3363     return bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
3364 }
3365
3366 /**************************************************************************
3367  *                              widGetNumDevs                   [internal]
3368  */
3369 static  DWORD   widGetNumDevs(void)
3370 {
3371     return ALSA_WidNumDevs;
3372 }
3373
3374 /**************************************************************************
3375  *                              widDevInterfaceSize             [internal]
3376  */
3377 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
3378 {
3379     TRACE("(%u, %p)\n", wDevID, dwParam1);
3380                                                                                                                                              
3381     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3382                                     NULL, 0 ) * sizeof(WCHAR);
3383     return MMSYSERR_NOERROR;
3384 }
3385
3386 /**************************************************************************
3387  *                              widDevInterface                 [internal]
3388  */
3389 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
3390 {
3391     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3392                                         NULL, 0 ) * sizeof(WCHAR))
3393     {
3394         MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3395                             dwParam1, dwParam2 / sizeof(WCHAR));
3396         return MMSYSERR_NOERROR;
3397     }
3398     return MMSYSERR_INVALPARAM;
3399 }
3400
3401 /**************************************************************************
3402  *                              widMessage (WINEALSA.@)
3403  */
3404 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
3405                              DWORD dwParam1, DWORD dwParam2)
3406 {
3407     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
3408           wDevID, wMsg, dwUser, dwParam1, dwParam2);
3409
3410     switch (wMsg) {
3411     case DRVM_INIT:
3412     case DRVM_EXIT:
3413     case DRVM_ENABLE:
3414     case DRVM_DISABLE:
3415         /* FIXME: Pretend this is supported */
3416         return 0;
3417     case WIDM_OPEN:             return widOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
3418     case WIDM_CLOSE:            return widClose         (wDevID);
3419     case WIDM_ADDBUFFER:        return widAddBuffer     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
3420     case WIDM_PREPARE:          return widPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
3421     case WIDM_UNPREPARE:        return widUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
3422     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (LPWAVEOUTCAPSA)dwParam1,      dwParam2);
3423     case WIDM_GETNUMDEVS:       return widGetNumDevs    ();
3424     case WIDM_GETPOS:           return widGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
3425     case WIDM_RESET:            return widReset         (wDevID);
3426     case WIDM_START:            return widStart (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
3427     case WIDM_STOP:             return widStop  (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
3428     case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
3429     case DRV_QUERYDEVICEINTERFACE:     return widDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
3430     /*case DRV_QUERYDSOUNDIFACE:        return widDsCreate   (wDevID, (PIDSCDRIVER*)dwParam1);
3431     case DRV_QUERYDSOUNDDESC:   return widDsDesc     (wDevID, (PDSDRIVERDESC)dwParam1);
3432     case DRV_QUERYDSOUNDGUID:   return widDsGuid     (wDevID, (LPGUID)dwParam1);*/
3433     default:
3434         FIXME("unknown message %d!\n", wMsg);
3435     }
3436     return MMSYSERR_NOTSUPPORTED;
3437 }
3438
3439 #endif
3440
3441 #if !(defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1))
3442
3443 /**************************************************************************
3444  *                              widMessage (WINEALSA.@)
3445  */
3446 DWORD WINAPI ALSA_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3447                              DWORD dwParam1, DWORD dwParam2)
3448 {
3449     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3450     return MMSYSERR_NOTENABLED;
3451 }
3452
3453 #endif
3454
3455 #ifndef HAVE_ALSA
3456
3457 /**************************************************************************
3458  *                              wodMessage (WINEALSA.@)
3459  */
3460 DWORD WINAPI ALSA_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3461                              DWORD dwParam1, DWORD dwParam2)
3462 {
3463     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3464     return MMSYSERR_NOTENABLED;
3465 }
3466
3467 #endif /* HAVE_ALSA */