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