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