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