mshtml: Update Dutch translation.
[wine] / dlls / winealsa.drv / mixer.c
1 /*
2  * Alsa MIXER Wine Driver for Linux
3  * Very loosely based on wineoss mixer driver
4  *
5  * Copyright 2007 Maarten Lankhorst
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <assert.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.h>
37 #endif
38
39 #define NONAMELESSUNION
40 #define NONAMELESSSTRUCT
41
42 #include "windef.h"
43 #include "winbase.h"
44 #include "wingdi.h"
45 #include "winuser.h"
46 #include "winnls.h"
47 #include "mmddk.h"
48 #include "mmsystem.h"
49 #include "alsa.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(mixer);
54
55 #ifdef HAVE_ALSA
56
57 #define WINE_MIXER_MANUF_ID      0xAA
58 #define WINE_MIXER_PRODUCT_ID    0x55
59 #define WINE_MIXER_VERSION       0x0100
60
61 /* Generic notes:
62  * In windows it seems to be required for all controls to have a volume switch
63  * In alsa that's optional
64  *
65  * I assume for playback controls, that there is always a playback volume switch available
66  * Mute is optional
67  *
68  * For capture controls, it is needed that there is a capture switch and a volume switch,
69  * It doesn't matter whether it is a playback volume switch or a capture volume switch.
70  * The code will first try to get/adjust capture volume, if that fails it tries playback volume
71  * It is not pretty, but under my 3 test cards it seems that there is no other choice:
72  * Most capture controls don't have a capture volume setting
73  *
74  * MUX means that only capture source can be exclusively selected,
75  * MIXER means that multiple sources can be selected simultaneously.
76  */
77
78 static const char * getMessage(UINT uMsg)
79 {
80 #define MSG_TO_STR(x) case x: return #x;
81     switch (uMsg){
82     MSG_TO_STR(DRVM_INIT);
83     MSG_TO_STR(DRVM_EXIT);
84     MSG_TO_STR(DRVM_ENABLE);
85     MSG_TO_STR(DRVM_DISABLE);
86     MSG_TO_STR(MXDM_GETDEVCAPS);
87     MSG_TO_STR(MXDM_GETLINEINFO);
88     MSG_TO_STR(MXDM_GETNUMDEVS);
89     MSG_TO_STR(MXDM_OPEN);
90     MSG_TO_STR(MXDM_CLOSE);
91     MSG_TO_STR(MXDM_GETLINECONTROLS);
92     MSG_TO_STR(MXDM_GETCONTROLDETAILS);
93     MSG_TO_STR(MXDM_SETCONTROLDETAILS);
94     default: break;
95     }
96 #undef MSG_TO_STR
97     return wine_dbg_sprintf("UNKNOWN(%08x)", uMsg);
98 }
99
100 static const char * getControlType(DWORD dwControlType)
101 {
102 #define TYPE_TO_STR(x) case x: return #x;
103     switch (dwControlType) {
104     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
105     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
106     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
107     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
108     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
109     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
110     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
111     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
112     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
113     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
114     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
115     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
116     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
117     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
118     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
119     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
120     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
121     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
122     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
123     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
124     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
125     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
126     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
127     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
128     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
129     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
130     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
131     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
132     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
133     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
134     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
135     }
136 #undef TYPE_TO_STR
137     return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType);
138 }
139
140 /* A simple declaration of a line control
141  * These are each of the channels that show up
142  */
143 typedef struct line {
144     /* Name we present to outside world */
145     WCHAR name[MAXPNAMELEN];
146
147     DWORD component;
148     DWORD dst;
149     DWORD capt;
150     DWORD chans;
151     snd_mixer_elem_t *elem;
152 } line;
153
154 /* A control structure, with toggle enabled switch
155  * Control structures control volume, muted, which capture source
156  */
157 typedef struct control {
158     BOOL enabled;
159     MIXERCONTROLW c;
160 } control;
161
162 /* Mixer device */
163 typedef struct mixer
164 {
165     snd_mixer_t *mix;
166     WCHAR mixername[MAXPNAMELEN];
167
168     int chans, dests;
169     LPDRVCALLBACK callback;
170     DWORD_PTR callbackpriv;
171     HDRVR hmx;
172
173     line *lines;
174     control *controls;
175 } mixer;
176
177 #define MAX_MIXERS 32
178 #define CONTROLSPERLINE 3
179 #define OFS_MUTE 2
180 #define OFS_MUX 1
181
182 static int cards = 0;
183 static mixer mixdev[MAX_MIXERS];
184 static HANDLE thread;
185 static int elem_callback(snd_mixer_elem_t *elem, unsigned int mask);
186 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam);
187 static CRITICAL_SECTION elem_crst;
188 static int msg_pipe[2];
189 static LONG refcnt;
190
191 /* found channel names in alsa lib, alsa api doesn't have another way for this
192  * map name -> componenttype, worst case we get a wrong componenttype which is
193  * mostly harmless
194  */
195
196 static const struct mixerlinetype {
197     const char *name;  DWORD cmpt;
198 } converttable[] = {
199     { "Master",     MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,    },
200     { "Capture",    MIXERLINE_COMPONENTTYPE_DST_WAVEIN,      },
201     { "PCM",        MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT,     },
202     { "PC Speaker", MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER,   },
203     { "Synth",      MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER, },
204     { "Headphone",  MIXERLINE_COMPONENTTYPE_DST_HEADPHONES,  },
205     { "Mic",        MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,  },
206     { "Aux",        MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED,   },
207     { "CD",         MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC, },
208     { "Line",       MIXERLINE_COMPONENTTYPE_SRC_LINE,        },
209     { "Phone",      MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE,   },
210     { "Digital",    MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,  },
211     { "Front Mic",  MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,  },
212 };
213
214 /* Map name to MIXERLINE_COMPONENTTYPE_XXX */
215 static int getcomponenttype(const char *name)
216 {
217     int x;
218     for (x=0; x< sizeof(converttable)/sizeof(converttable[0]); ++x)
219         if (!strcasecmp(name, converttable[x].name))
220         {
221             TRACE("%d -> %s\n", x, name);
222             return converttable[x].cmpt;
223         }
224     WARN("Unknown mixer name %s, probably harmless\n", name);
225     return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
226 }
227
228 /* Is this control suited for showing up? */
229 static int blacklisted(snd_mixer_elem_t *elem)
230 {
231     const char *name = snd_mixer_selem_get_name(elem);
232     BOOL blisted = 0;
233
234     if (!snd_mixer_selem_has_playback_volume(elem) &&
235         !snd_mixer_selem_has_capture_volume(elem))
236         blisted = 1;
237
238     TRACE("%s: %x\n", name, blisted);
239     return blisted;
240 }
241
242 static void fillcontrols(mixer *mmixer)
243 {
244     int id;
245     for (id = 0; id < mmixer->chans; ++id)
246     {
247         line *mline = &mmixer->lines[id];
248         int ofs = CONTROLSPERLINE * id;
249         int x;
250         long min, max;
251
252         TRACE("Filling control %d\n", id);
253         if (id == 1 && !mline->elem)
254             continue;
255
256         if (mline->capt && snd_mixer_selem_has_capture_volume(mline->elem))
257             snd_mixer_selem_get_capture_volume_range(mline->elem, &min, &max);
258         else
259             snd_mixer_selem_get_playback_volume_range(mline->elem, &min, &max);
260
261         /* (!snd_mixer_selem_has_playback_volume(elem) || snd_mixer_selem_has_capture_volume(elem)) */
262         /* Volume, always enabled by definition of blacklisted channels */
263         mmixer->controls[ofs].enabled = 1;
264         mmixer->controls[ofs].c.cbStruct = sizeof(mmixer->controls[ofs].c);
265         mmixer->controls[ofs].c.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
266         mmixer->controls[ofs].c.dwControlID = ofs;
267         mmixer->controls[ofs].c.Bounds.s1.dwMinimum = 0;
268         mmixer->controls[ofs].c.Bounds.s1.dwMaximum = 65535;
269         mmixer->controls[ofs].c.Metrics.cSteps = 65536/(max-min);
270
271         if ((id == 1 && snd_mixer_selem_has_capture_switch(mline->elem)) ||
272             (!mline->capt && snd_mixer_selem_has_playback_switch(mline->elem)))
273         { /* MUTE button optional, main capture channel should have one too */
274             mmixer->controls[ofs+OFS_MUTE].enabled = 1;
275             mmixer->controls[ofs+OFS_MUTE].c.cbStruct = sizeof(mmixer->controls[ofs].c);
276             mmixer->controls[ofs+OFS_MUTE].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
277             mmixer->controls[ofs+OFS_MUTE].c.dwControlID = ofs+OFS_MUTE;
278             mmixer->controls[ofs+OFS_MUTE].c.Bounds.s1.dwMaximum = 1;
279         }
280
281         if (mline->capt && snd_mixer_selem_has_capture_switch_exclusive(mline->elem))
282             mmixer->controls[CONTROLSPERLINE+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
283
284         if (id == 1)
285         { /* Capture select, in case cMultipleItems is 0, it means capture is disabled anyway */
286             mmixer->controls[ofs+OFS_MUX].enabled = 1;
287             mmixer->controls[ofs+OFS_MUX].c.cbStruct = sizeof(mmixer->controls[ofs].c);
288             mmixer->controls[ofs+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER;
289             mmixer->controls[ofs+OFS_MUX].c.dwControlID = ofs+OFS_MUX;
290             mmixer->controls[ofs+OFS_MUX].c.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
291
292             for (x = 0; x<mmixer->chans; ++x)
293                 if (x != id && mmixer->lines[x].dst == id)
294                     ++(mmixer->controls[ofs+OFS_MUX].c.cMultipleItems);
295             if (!mmixer->controls[ofs+OFS_MUX].c.cMultipleItems)
296                 mmixer->controls[ofs+OFS_MUX].enabled = 0;
297
298             mmixer->controls[ofs+OFS_MUX].c.Bounds.s1.dwMaximum = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems - 1;
299             mmixer->controls[ofs+OFS_MUX].c.Metrics.cSteps = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems;
300         }
301         for (x=0; x<CONTROLSPERLINE; ++x)
302         {
303             lstrcpynW(mmixer->controls[ofs+x].c.szShortName, mline->name, sizeof(mmixer->controls[ofs+x].c.szShortName)/sizeof(WCHAR));
304             lstrcpynW(mmixer->controls[ofs+x].c.szName, mline->name, sizeof(mmixer->controls[ofs+x].c.szName)/sizeof(WCHAR));
305         }
306     }
307 }
308
309 /* get amount of channels for elem */
310 /* Officially we should keep capture/playback separated,
311  * but that's not going to work in the alsa api */
312 static int chans(mixer *mmixer, snd_mixer_elem_t * elem, DWORD capt)
313 {
314     int ret=0, chn;
315
316     if (capt && snd_mixer_selem_has_capture_volume(elem)) {
317         for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
318             if (snd_mixer_selem_has_capture_channel(elem, chn))
319                 ++ret;
320     } else {
321         for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
322             if (snd_mixer_selem_has_playback_channel(elem, chn))
323                 ++ret;
324     }
325     if (!ret)
326         FIXME("Mixer channel %s was found for %s, but no channels were found? Wrong selection!\n", snd_mixer_selem_get_name(elem), (snd_mixer_selem_has_playback_volume(elem) ? "playback" : "capture"));
327     return ret;
328 }
329
330 static void filllines(mixer *mmixer, snd_mixer_elem_t *mastelem, snd_mixer_elem_t *captelem, int capt)
331 {
332     snd_mixer_elem_t *elem;
333     line *mline = mmixer->lines;
334
335     /* Master control */
336     MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(mastelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
337     mline->component = getcomponenttype(snd_mixer_selem_get_name(mastelem));
338     mline->dst = 0;
339     mline->capt = 0;
340     mline->elem = mastelem;
341     mline->chans = chans(mmixer, mastelem, 0);
342
343     snd_mixer_elem_set_callback(mastelem, &elem_callback);
344     snd_mixer_elem_set_callback_private(mastelem, mmixer);
345
346     /* Capture control
347      * Note: since mmixer->dests = 1, it means only playback control is visible
348      * This makes sense, because if there are no capture sources capture control
349      * can't do anything and should be invisible */
350
351     /* Control 1 is reserved for capture even when not enabled */
352     ++mline;
353     if (capt)
354     {
355         MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(captelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
356         mline->component = getcomponenttype(snd_mixer_selem_get_name(captelem));
357         mline->dst = 1;
358         mline->capt = 1;
359         mline->elem = captelem;
360         mline->chans = chans(mmixer, captelem, 1);
361
362         snd_mixer_elem_set_callback(captelem, &elem_callback);
363         snd_mixer_elem_set_callback_private(captelem, mmixer);
364     }
365
366     for (elem = snd_mixer_first_elem(mmixer->mix); elem; elem = snd_mixer_elem_next(elem))
367         if (elem != mastelem && elem != captelem && !blacklisted(elem))
368         {
369             const char * name = snd_mixer_selem_get_name(elem);
370             DWORD comp = getcomponenttype(name);
371
372             if (snd_mixer_selem_has_playback_volume(elem) &&
373                (snd_mixer_selem_has_capture_volume(elem) || comp != MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE))
374             {
375                 (++mline)->component = comp;
376                 MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
377                 mline->capt = mline->dst = 0;
378                 mline->elem = elem;
379                 mline->chans = chans(mmixer, elem, 0);
380             }
381             else if (!capt)
382                 continue;
383
384             if (capt && (snd_mixer_selem_has_capture_volume(elem) || comp == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE))
385             {
386                 (++mline)->component = comp;
387                 MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
388                 mline->capt = mline->dst = 1;
389                 mline->elem = elem;
390                 mline->chans = chans(mmixer, elem, 1);
391             }
392
393             snd_mixer_elem_set_callback(elem, &elem_callback);
394             snd_mixer_elem_set_callback_private(elem, mmixer);
395         }
396 }
397
398 /* Windows api wants to have a 'master' device to which all slaves are attached
399  * There are 2 ones in this code:
400  * - 'Master', fall back to 'Headphone' if unavailable, and if that's not available 'PCM'
401  * - 'Capture'
402  * Capture might not always be available, so should be prepared to be without if needed
403  */
404
405 static void ALSA_MixerInit(void)
406 {
407     int x, mixnum = 0;
408     snd_ctl_card_info_t *info;
409
410     info = HeapAlloc( GetProcessHeap(), 0, snd_ctl_card_info_sizeof());
411     for (x = 0; x < MAX_MIXERS; ++x)
412     {
413         int card, err, capcontrols = 0;
414         char cardind[6], cardname[10];
415
416         snd_ctl_t *ctl;
417         snd_mixer_elem_t *elem, *mastelem = NULL, *headelem = NULL, *captelem = NULL, *pcmelem = NULL;
418
419         memset(info, 0, snd_ctl_card_info_sizeof());
420         memset(&mixdev[mixnum], 0, sizeof(*mixdev));
421         snprintf(cardind, sizeof(cardind), "%d", x);
422         card = snd_card_get_index(cardind);
423         if (card < 0)
424             continue;
425
426         snprintf(cardname, sizeof(cardname), "hw:%d", card);
427
428         err = snd_ctl_open(&ctl, cardname, 0);
429         if (err < 0)
430         {
431             WARN("Cannot open card: %s\n", snd_strerror(err));
432             continue;
433         }
434
435         err = snd_ctl_card_info(ctl, info);
436         if (err < 0)
437         {
438             WARN("Cannot get card info: %s\n", snd_strerror(err));
439             snd_ctl_close(ctl);
440             continue;
441         }
442
443         MultiByteToWideChar(CP_UNIXCP, 0, snd_ctl_card_info_get_name(info), -1, mixdev[mixnum].mixername, sizeof(mixdev[mixnum].mixername)/sizeof(WCHAR));
444         snd_ctl_close(ctl);
445
446         err = snd_mixer_open(&mixdev[mixnum].mix, 0);
447         if (err < 0)
448         {
449             WARN("Error occurred opening mixer: %s\n", snd_strerror(err));
450             continue;
451         }
452
453         err = snd_mixer_attach(mixdev[mixnum].mix, cardname);
454         if (err < 0)
455             goto eclose;
456
457         err = snd_mixer_selem_register(mixdev[mixnum].mix, NULL, NULL);
458         if (err < 0)
459             goto eclose;
460
461         err = snd_mixer_load(mixdev[mixnum].mix);
462         if (err < 0)
463             goto eclose;
464
465         /* First, lets see what's available..
466          * If there are multiple Master or Captures, all except 1 will be added as slaves
467          */
468         for (elem = snd_mixer_first_elem(mixdev[mixnum].mix); elem; elem = snd_mixer_elem_next(elem))
469             if (!strcasecmp(snd_mixer_selem_get_name(elem), "Master") && !mastelem)
470                 mastelem = elem;
471             else if (!strcasecmp(snd_mixer_selem_get_name(elem), "Capture") && !captelem)
472                 captelem = elem;
473             else if (!blacklisted(elem))
474             {
475                 DWORD comp = getcomponenttype(snd_mixer_selem_get_name(elem));
476                 DWORD skip = 0;
477
478                 /* Work around buggy drivers: Make this a capture control if the name is recognised as a microphone */
479                 if (snd_mixer_selem_has_capture_volume(elem))
480                     ++capcontrols;
481                 else if (comp == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
482                 {
483                     ++capcontrols;
484                     skip = 1;
485                 }
486
487                 if (!skip && snd_mixer_selem_has_playback_volume(elem))
488                 {
489                     if (!strcasecmp(snd_mixer_selem_get_name(elem), "Headphone") && !headelem)
490                         headelem = elem;
491                     else if (!strcasecmp(snd_mixer_selem_get_name(elem), "PCM") && !pcmelem)
492                         pcmelem = elem;
493                     else
494                         ++(mixdev[mixnum].chans);
495                 }
496             }
497
498         /* Add master channel, uncounted channels and an extra for capture  */
499         mixdev[mixnum].chans += !!mastelem + !!headelem + !!pcmelem + 1;
500
501         /* If there is only 'Capture' and 'Master', this device is not worth it */
502         if (mixdev[mixnum].chans == 2)
503         {
504             WARN("No channels found, skipping device!\n");
505             goto close;
506         }
507
508         /* Master element can't have a capture control in this code, so
509          * if Headphone or PCM is promoted to master, unset its capture control */
510         if (headelem && !mastelem)
511         {
512             /* Using 'Headphone' as master device */
513             mastelem = headelem;
514             capcontrols -= !!snd_mixer_selem_has_capture_switch(mastelem);
515         }
516         else if (pcmelem && !mastelem)
517         {
518             /* Use 'PCM' as master device */
519             mastelem = pcmelem;
520             capcontrols -= !!snd_mixer_selem_has_capture_switch(mastelem);
521         }
522         else if (!mastelem)
523         {
524             /* If there is nothing sensible that can act as 'Master' control, something is wrong */
525             FIXME("No master control found on %s, disabling mixer\n", snd_ctl_card_info_get_name(info));
526             goto close;
527         }
528
529         if (!captelem || !capcontrols)
530         {
531             /* Can't enable capture, so disabling it
532              * Note: capture control will still exist because
533              * dwLineID 0 and 1 are reserved for Master and Capture
534              */
535             WARN("No use enabling capture part of mixer, capture control found: %s, amount of capture controls: %d\n",
536                  (!captelem ? "no" : "yes"), capcontrols);
537             capcontrols = 0;
538             mixdev[mixnum].dests = 1;
539         }
540         else
541         {
542             mixdev[mixnum].chans += capcontrols;
543             mixdev[mixnum].dests = 2;
544         }
545
546         mixdev[mixnum].lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(line) * mixdev[mixnum].chans);
547         mixdev[mixnum].controls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(control) * CONTROLSPERLINE*mixdev[mixnum].chans);
548         err = -ENOMEM;
549         if (!mixdev[mixnum].lines || !mixdev[mixnum].controls)
550             goto close;
551
552         filllines(&mixdev[mixnum], mastelem, captelem, capcontrols);
553         fillcontrols(&mixdev[mixnum]);
554
555         TRACE("%s: Amount of controls: %i/%i, name: %s\n", cardname, mixdev[mixnum].dests, mixdev[mixnum].chans, debugstr_w(mixdev[mixnum].mixername));
556         mixnum++;
557         continue;
558
559         eclose:
560         WARN("Error occurred initialising mixer: %s\n", snd_strerror(err));
561         close:
562         HeapFree(GetProcessHeap(), 0, mixdev[mixnum].lines);
563         HeapFree(GetProcessHeap(), 0, mixdev[mixnum].controls);
564         snd_mixer_close(mixdev[mixnum].mix);
565     }
566     cards = mixnum;
567     HeapFree( GetProcessHeap(), 0, info );
568
569     /* There is no trouble with already assigning callbacks without initialising critsect:
570      * Callbacks only occur when snd_mixer_handle_events is called (only happens in thread)
571      */
572     InitializeCriticalSection(&elem_crst);
573     elem_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MIXER.elem_crst");
574     TRACE("\n");
575 }
576
577 static void ALSA_MixerExit(void)
578 {
579     int x;
580
581     if (refcnt)
582     {
583         WARN("Callback thread still alive, terminating uncleanly, refcnt: %d\n", refcnt);
584         /* Least we can do is making sure we're not in 'foreign' code */
585         EnterCriticalSection(&elem_crst);
586         TerminateThread(thread, 1);
587         refcnt = 0;
588         LeaveCriticalSection(&elem_crst);
589     }
590
591     TRACE("Cleaning up\n");
592
593     elem_crst.DebugInfo->Spare[0] = 0;
594     DeleteCriticalSection(&elem_crst);
595     for (x = 0; x < cards; ++x)
596     {
597         snd_mixer_close(mixdev[x].mix);
598         HeapFree(GetProcessHeap(), 0, mixdev[x].lines);
599         HeapFree(GetProcessHeap(), 0, mixdev[x].controls);
600     }
601     cards = 0;
602 }
603
604 static mixer* MIX_GetMix(UINT wDevID)
605 {
606     mixer *mmixer;
607
608     if (wDevID >= cards)
609     {
610         WARN("Invalid mixer id: %d\n", wDevID);
611         return NULL;
612     }
613
614     mmixer = &mixdev[wDevID];
615     return mmixer;
616 }
617
618 /* Since alsa doesn't tell what exactly changed, just assume all affected controls changed */
619 static int elem_callback(snd_mixer_elem_t *elem, unsigned int type)
620 {
621     mixer *mmixer = snd_mixer_elem_get_callback_private(elem);
622     int x;
623     BOOL captchanged = 0;
624
625     if (type != SND_CTL_EVENT_MASK_VALUE)
626         return 0;
627
628     assert(mmixer);
629
630     EnterCriticalSection(&elem_crst);
631
632     if (!mmixer->callback)
633         goto out;
634
635     for (x=0; x<mmixer->chans; ++x)
636     {
637         const int ofs = CONTROLSPERLINE*x;
638         if (elem != mmixer->lines[x].elem)
639             continue;
640
641         if (mmixer->lines[x].capt)
642             ++captchanged;
643
644         TRACE("Found changed control %s\n", debugstr_w(mmixer->lines[x].name));
645         mmixer->callback(mmixer->hmx, MM_MIXM_LINE_CHANGE, mmixer->callbackpriv, x, 0);
646         mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs, 0);
647
648         if (mmixer->controls[ofs+OFS_MUTE].enabled)
649             mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs+OFS_MUTE, 0);
650     }
651     if (captchanged)
652         mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, CONTROLSPERLINE+OFS_MUX, 0);
653
654     out:
655     LeaveCriticalSection(&elem_crst);
656
657     return 0;
658 }
659
660 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam)
661 {
662     struct pollfd *pfds = NULL;
663     int x, y, err, mcnt, count = 1;
664
665     TRACE("%p\n", lParam);
666
667     for (x = 0; x < cards; ++x)
668         count += snd_mixer_poll_descriptors_count(mixdev[x].mix);
669
670     TRACE("Counted %d descriptors\n", count);
671     pfds = HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct pollfd));
672
673     if (!pfds)
674     {
675         WARN("Out of memory\n");
676         goto die;
677     }
678
679     pfds[0].fd = msg_pipe[0];
680     pfds[0].events = POLLIN;
681
682     y = 1;
683     for (x = 0; x < cards; ++x)
684         y += snd_mixer_poll_descriptors(mixdev[x].mix, &pfds[y], count - y);
685
686     while ((err = poll(pfds, (unsigned int) count, -1)) >= 0 || errno == EINTR || errno == EAGAIN)
687     {
688         if (pfds[0].revents & POLLIN)
689             break;
690
691         mcnt = 1;
692         for (x = y = 0; x < cards; ++x)
693         {
694             int j, max = snd_mixer_poll_descriptors_count(mixdev[x].mix);
695             for (j = 0; j < max; ++j)
696                 if (pfds[mcnt+j].revents)
697                 {
698                     y += snd_mixer_handle_events(mixdev[x].mix);
699                     break;
700                 }
701             mcnt += max;
702         }
703         if (y)
704             TRACE("Handled %d events\n", y);
705     }
706
707     die:
708     TRACE("Shutting down\n");
709     HeapFree(GetProcessHeap(), 0, pfds);
710
711     y = read(msg_pipe[0], &x, sizeof(x));
712     close(msg_pipe[1]);
713     close(msg_pipe[0]);
714     return 0;
715 }
716
717 static DWORD MIX_Open(UINT wDevID, LPMIXEROPENDESC desc, DWORD_PTR flags)
718 {
719     mixer *mmixer = MIX_GetMix(wDevID);
720     if (!mmixer)
721         return MMSYSERR_BADDEVICEID;
722
723     flags &= CALLBACK_TYPEMASK;
724     switch (flags)
725     {
726     case CALLBACK_NULL:
727         goto done;
728
729     case CALLBACK_FUNCTION:
730         break;
731
732     default:
733         FIXME("Unhandled callback type: %08lx\n", flags & CALLBACK_TYPEMASK);
734         return MIXERR_INVALVALUE;
735     }
736
737     mmixer->callback = (LPDRVCALLBACK)desc->dwCallback;
738     mmixer->callbackpriv = desc->dwInstance;
739     mmixer->hmx = (HDRVR)desc->hmx;
740
741     done:
742     if (InterlockedIncrement(&refcnt) == 1)
743     {
744         if (pipe(msg_pipe) >= 0)
745         {
746             thread = CreateThread(NULL, 0, ALSA_MixerPollThread, NULL, 0, NULL);
747             if (!thread)
748             {
749                 close(msg_pipe[0]);
750                 close(msg_pipe[1]);
751                 msg_pipe[0] = msg_pipe[1] = -1;
752             }
753         }
754         else
755             msg_pipe[0] = msg_pipe[1] = -1;
756     }
757
758     return MMSYSERR_NOERROR;
759 }
760
761 static DWORD MIX_Close(UINT wDevID)
762 {
763     int x = 0;
764     mixer *mmixer = MIX_GetMix(wDevID);
765     if (!mmixer)
766         return MMSYSERR_BADDEVICEID;
767
768     EnterCriticalSection(&elem_crst);
769     mmixer->callback = 0;
770     LeaveCriticalSection(&elem_crst);
771
772     if (!InterlockedDecrement(&refcnt))
773     {
774         if (write(msg_pipe[1], &x, sizeof(x)) > 0)
775         {
776             TRACE("Shutting down thread...\n");
777             WaitForSingleObject(thread, INFINITE);
778             TRACE("Done\n");
779         }
780     }
781
782     return MMSYSERR_NOERROR;
783 }
784
785 static DWORD MIX_GetDevCaps(UINT wDevID, LPMIXERCAPS2W caps, DWORD_PTR parm2)
786 {
787     mixer *mmixer = MIX_GetMix(wDevID);
788     MIXERCAPS2W capsW;
789
790     if (!caps)
791         return MMSYSERR_INVALPARAM;
792
793     if (!mmixer)
794         return MMSYSERR_BADDEVICEID;
795
796     memset(&capsW, 0, sizeof(MIXERCAPS2W));
797
798     capsW.wMid = WINE_MIXER_MANUF_ID;
799     capsW.wPid = WINE_MIXER_PRODUCT_ID;
800     capsW.vDriverVersion = WINE_MIXER_VERSION;
801
802     lstrcpynW(capsW.szPname, mmixer->mixername, sizeof(capsW.szPname)/sizeof(WCHAR));
803     capsW.cDestinations = mmixer->dests;
804     memcpy(caps, &capsW, min(parm2, sizeof(capsW)));
805     return MMSYSERR_NOERROR;
806 }
807
808 /* convert win32 volume to alsa volume, and vice versa */
809 static INT normalized(INT value, INT prevmax, INT nextmax)
810 {
811     int ret = MulDiv(value, nextmax, prevmax);
812
813     /* Have to stay in range */
814     TRACE("%d/%d -> %d/%d\n", value, prevmax, ret, nextmax);
815     if (ret > nextmax)
816         ret = nextmax;
817     else if (ret < 0)
818         ret = 0;
819
820     return ret;
821 }
822
823 /* get amount of sources for dest */
824 static int getsrccntfromchan(mixer *mmixer, int dad)
825 {
826     int i, j=0;
827
828     for (i=0; i<mmixer->chans; ++i)
829         if (i != dad && mmixer->lines[i].dst == dad)
830         {
831             ++j;
832         }
833     if (!j)
834         FIXME("No src found for %i (%s)?\n", dad, debugstr_w(mmixer->lines[dad].name));
835     return j;
836 }
837
838 /* find lineid for source 'num' with dest 'dad' */
839 static int getsrclinefromchan(mixer *mmixer, int dad, int num)
840 {
841     int i, j=0;
842     for (i=0; i<mmixer->chans; ++i)
843         if (i != dad && mmixer->lines[i].dst == dad)
844         {
845             if (num == j)
846                 return i;
847             ++j;
848         }
849     WARN("No src found for src %i from dest %i\n", num, dad);
850     return 0;
851 }
852
853 /* get the source number belonging to line */
854 static int getsrcfromline(mixer *mmixer, int line)
855 {
856     int i, j=0, dad = mmixer->lines[line].dst;
857
858     for (i=0; i<mmixer->chans; ++i)
859         if (i != dad && mmixer->lines[i].dst == dad)
860         {
861             if (line == i)
862                 return j;
863             ++j;
864         }
865     WARN("No src found for line %i with dad %i\n", line, dad);
866     return 0;
867 }
868
869 /* Get volume/muted/capture channel */
870 static DWORD MIX_GetControlDetails(UINT wDevID, LPMIXERCONTROLDETAILS mctrld, DWORD_PTR flags)
871 {
872     mixer *mmixer = MIX_GetMix(wDevID);
873     DWORD ctrl;
874     DWORD line;
875     control *ct;
876
877     if (!mctrld)
878         return MMSYSERR_INVALPARAM;
879
880     ctrl = mctrld->dwControlID;
881     line = ctrl/CONTROLSPERLINE;
882
883     if (mctrld->cbStruct != sizeof(*mctrld))
884         return MMSYSERR_INVALPARAM;
885
886     if (!mmixer)
887         return MMSYSERR_BADDEVICEID;
888
889     if (line >= mmixer->chans || !mmixer->controls[ctrl].enabled)
890         return MIXERR_INVALCONTROL;
891
892     ct = &mmixer->controls[ctrl];
893
894     flags &= MIXER_GETCONTROLDETAILSF_QUERYMASK;
895
896     switch (flags) {
897     case MIXER_GETCONTROLDETAILSF_VALUE:
898         TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%d/%d)\n", ctrl, line);
899         switch (ct->c.dwControlType)
900         {
901         case MIXERCONTROL_CONTROLTYPE_VOLUME:
902         {
903             long min = 0, max = 0, vol = 0;
904             int chn;
905             LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
906             snd_mixer_elem_t * elem = mmixer->lines[line].elem;
907
908             if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED))
909             {
910                 WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
911                 return MMSYSERR_INVALPARAM;
912             }
913
914             TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
915
916             mcdu = mctrld->paDetails;
917
918             if (mctrld->cChannels != 1 && mmixer->lines[line].chans != mctrld->cChannels)
919             {
920                 WARN("Unsupported cChannels (%d instead of %d)\n", mctrld->cChannels, mmixer->lines[line].chans);
921                 return MMSYSERR_INVALPARAM;
922             }
923
924             if (mmixer->lines[line].capt && snd_mixer_selem_has_capture_volume(elem)) {
925                 snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
926                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
927                     if (snd_mixer_selem_has_capture_channel(elem, chn))
928                     {
929                         snd_mixer_selem_get_capture_volume(elem, chn, &vol);
930                         mcdu->dwValue = normalized(vol - min, max, 65535);
931                         if (mctrld->cChannels == 1)
932                             break;
933                         ++mcdu;
934                     }
935             } else {
936                 snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
937
938                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
939                     if (snd_mixer_selem_has_playback_channel(elem, chn))
940                     {
941                         snd_mixer_selem_get_playback_volume(elem, chn, &vol);
942                         mcdu->dwValue = normalized(vol - min, max, 65535);
943                         if (mctrld->cChannels == 1)
944                             break;
945                         ++mcdu;
946                     }
947             }
948
949             return MMSYSERR_NOERROR;
950         }
951
952         case MIXERCONTROL_CONTROLTYPE_ONOFF:
953         case MIXERCONTROL_CONTROLTYPE_MUTE:
954         {
955             LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
956             int chn, ival;
957             snd_mixer_elem_t * elem = mmixer->lines[line].elem;
958
959             if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
960             {
961                 WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
962                 return MMSYSERR_INVALPARAM;
963             }
964
965             TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
966
967             mcdb = mctrld->paDetails;
968
969             if (line == 1)
970                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
971                 {
972                     if (!snd_mixer_selem_has_capture_channel(elem, chn))
973                         continue;
974                     snd_mixer_selem_get_capture_switch(elem, chn, &ival);
975                     break;
976                 }
977             else
978                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
979                 {
980                     if (!snd_mixer_selem_has_playback_channel(elem, chn))
981                         continue;
982                     snd_mixer_selem_get_playback_switch(elem, chn, &ival);
983                     break;
984                 }
985
986             if (chn > SND_MIXER_SCHN_LAST)
987             {
988                 TRACE("can't find active channel\n");
989                 return MMSYSERR_INVALPARAM;  /* fixme: what's right error? */
990             }
991
992             mcdb->fValue = !ival;
993             TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
994             return MMSYSERR_NOERROR;
995         }
996         case MIXERCONTROL_CONTROLTYPE_MIXER:
997         case MIXERCONTROL_CONTROLTYPE_MUX:
998         {
999             LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
1000             int x, i=0, ival = 0, chn;
1001
1002             if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
1003             {
1004                 WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1005                 return MMSYSERR_INVALPARAM;
1006             }
1007
1008             TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1009
1010             mcdb = mctrld->paDetails;
1011
1012             for (x = 0; x<mmixer->chans; ++x)
1013                 if (line != x && mmixer->lines[x].dst == line)
1014                 {
1015                     ival = 0;
1016                     for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1017                     {
1018                         if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
1019                             continue;
1020                         snd_mixer_selem_get_capture_switch(mmixer->lines[x].elem, chn, &ival);
1021                         if (ival)
1022                             break;
1023                     }
1024                     if (i >= mctrld->u.cMultipleItems)
1025                     {
1026                         TRACE("overflow\n");
1027                         return MMSYSERR_INVALPARAM;
1028                     }
1029                     TRACE("fVal[%i] = %sselected\n", i, (!ival ? "un" : ""));
1030                     mcdb[i++].fValue = ival;
1031                 }
1032             break;
1033         }
1034         default:
1035
1036             FIXME("Unhandled controltype %s\n", getControlType(ct->c.dwControlType));
1037             return MMSYSERR_INVALPARAM;
1038         }
1039         return MMSYSERR_NOERROR;
1040
1041     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
1042         TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%d)\n", ctrl);
1043
1044         if (ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX || ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
1045         {
1046             LPMIXERCONTROLDETAILS_LISTTEXTW mcdlt = mctrld->paDetails;
1047             int i, j;
1048
1049             for (i = j = 0; j < mmixer->chans; ++j)
1050                 if (j != line && mmixer->lines[j].dst == line)
1051                 {
1052                     if (i > mctrld->u.cMultipleItems)
1053                         return MMSYSERR_INVALPARAM;
1054                     mcdlt->dwParam1 = j;
1055                     mcdlt->dwParam2 = mmixer->lines[j].component;
1056                     lstrcpynW(mcdlt->szName, mmixer->lines[j].name, sizeof(mcdlt->szName) / sizeof(WCHAR));
1057                     TRACE("Adding %i as %s\n", j, debugstr_w(mcdlt->szName));
1058                     ++i; ++mcdlt;
1059                 }
1060             if (i < mctrld->u.cMultipleItems)
1061                 return MMSYSERR_INVALPARAM;
1062             return MMSYSERR_NOERROR;
1063         }
1064         FIXME ("Imagine this code being horribly broken and incomplete, introducing: reality\n");
1065         return MMSYSERR_INVALPARAM;
1066
1067     default:
1068         WARN("Unknown flag (%08lx)\n", flags);
1069         return MMSYSERR_INVALPARAM;
1070     }
1071 }
1072
1073 /* Set volume/capture channel/muted for control */
1074 static DWORD MIX_SetControlDetails(UINT wDevID, LPMIXERCONTROLDETAILS mctrld, DWORD_PTR flags)
1075 {
1076     mixer *mmixer = MIX_GetMix(wDevID);
1077     DWORD ctrl, line, i;
1078     control *ct;
1079     snd_mixer_elem_t * elem;
1080
1081     if (!mctrld)
1082         return MMSYSERR_INVALPARAM;
1083
1084     ctrl = mctrld->dwControlID;
1085     line = ctrl/CONTROLSPERLINE;
1086
1087     if (mctrld->cbStruct != sizeof(*mctrld))
1088     {
1089         WARN("Invalid size of mctrld %d\n", mctrld->cbStruct);
1090         return MMSYSERR_INVALPARAM;
1091     }
1092
1093     if (!mmixer)
1094         return MMSYSERR_BADDEVICEID;
1095
1096     if (line >= mmixer->chans)
1097     {
1098         WARN("Invalid line id: %d not in range of 0-%d\n", line, mmixer->chans-1);
1099         return MMSYSERR_INVALPARAM;
1100     }
1101
1102     if (!mmixer->controls[ctrl].enabled)
1103     {
1104         WARN("Control %d not enabled\n", ctrl);
1105         return MIXERR_INVALCONTROL;
1106     }
1107
1108     ct = &mmixer->controls[ctrl];
1109     elem = mmixer->lines[line].elem;
1110     flags &= MIXER_SETCONTROLDETAILSF_QUERYMASK;
1111
1112     switch (flags) {
1113     case MIXER_SETCONTROLDETAILSF_VALUE:
1114         TRACE("MIXER_SETCONTROLDETAILSF_VALUE (%d)\n", ctrl);
1115         break;
1116
1117     default:
1118         WARN("Unknown flag (%08lx)\n", flags);
1119         return MMSYSERR_INVALPARAM;
1120     }
1121
1122     switch (ct->c.dwControlType)
1123     {
1124     case MIXERCONTROL_CONTROLTYPE_VOLUME:
1125     {
1126         long min = 0, max = 0;
1127         int chn;
1128         LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
1129         snd_mixer_elem_t * elem = mmixer->lines[line].elem;
1130
1131         if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED))
1132         {
1133             WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1134             return MMSYSERR_INVALPARAM;
1135         }
1136
1137         if (mctrld->cChannels != 1 && mmixer->lines[line].chans != mctrld->cChannels)
1138         {
1139             WARN("Unsupported cChannels (%d instead of %d)\n", mctrld->cChannels, mmixer->lines[line].chans);
1140             return MMSYSERR_INVALPARAM;
1141         }
1142
1143         TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1144         mcdu = mctrld->paDetails;
1145
1146         for (chn=0; chn<mctrld->cChannels;++chn)
1147         {
1148             TRACE("Chan %d value %d\n", chn, mcdu[chn].dwValue);
1149         }
1150
1151         /* There isn't always a capture volume, so in that case change playback volume */
1152         if (mmixer->lines[line].capt && snd_mixer_selem_has_capture_volume(elem))
1153         {
1154             snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
1155
1156             for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1157                 if (snd_mixer_selem_has_capture_channel(elem, chn))
1158                 {
1159                     snd_mixer_selem_set_capture_volume(elem, chn, min + normalized(mcdu->dwValue, 65535, max));
1160                     if (mctrld->cChannels != 1)
1161                         mcdu++;
1162                 }
1163         }
1164         else
1165         {
1166             snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
1167
1168             for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1169                 if (snd_mixer_selem_has_playback_channel(elem, chn))
1170                 {
1171                     snd_mixer_selem_set_playback_volume(elem, chn, min + normalized(mcdu->dwValue, 65535, max));
1172                     if (mctrld->cChannels != 1)
1173                         mcdu++;
1174                 }
1175         }
1176
1177         break;
1178     }
1179     case MIXERCONTROL_CONTROLTYPE_MUTE:
1180     case MIXERCONTROL_CONTROLTYPE_ONOFF:
1181     {
1182         LPMIXERCONTROLDETAILS_BOOLEAN   mcdb;
1183
1184         if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
1185         {
1186             WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1187             return MMSYSERR_INVALPARAM;
1188         }
1189
1190         TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1191
1192         mcdb = mctrld->paDetails;
1193         if (line == 1) /* Mute/unmute capturing */
1194             for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
1195             {
1196                 if (snd_mixer_selem_has_capture_channel(elem, i))
1197                     snd_mixer_selem_set_capture_switch(elem, i, !mcdb->fValue);
1198             }
1199         else
1200             for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
1201                 if (snd_mixer_selem_has_playback_channel(elem, i))
1202                     snd_mixer_selem_set_playback_switch(elem, i, !mcdb->fValue);
1203         break;
1204     }
1205
1206     case MIXERCONTROL_CONTROLTYPE_MIXER:
1207     case MIXERCONTROL_CONTROLTYPE_MUX:
1208     {
1209         LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
1210         int x, i=0, chn;
1211         int didone = 0, canone = (ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX);
1212
1213         if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
1214         {
1215             WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1216             return MMSYSERR_INVALPARAM;
1217         }
1218
1219         TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1220         mcdb = mctrld->paDetails;
1221
1222         for (x=i=0; x < mmixer->chans; ++x)
1223             if (line != x && mmixer->lines[x].dst == line)
1224             {
1225                 TRACE("fVal[%i] (%s) = %i\n", i, debugstr_w(mmixer->lines[x].name), mcdb[i].fValue);
1226                 if (i >= mctrld->u.cMultipleItems)
1227                 {
1228                     TRACE("Too many items to fit, overflowing\n");
1229                     return MIXERR_INVALVALUE;
1230                 }
1231                 if (mcdb[i].fValue && canone && didone)
1232                 {
1233                     TRACE("Nice try, but it's not going to work\n");
1234                     elem_callback(mmixer->lines[1].elem, SND_CTL_EVENT_MASK_VALUE);
1235                     return MIXERR_INVALVALUE;
1236                 }
1237                 if (mcdb[i].fValue)
1238                     didone = 1;
1239                 ++i;
1240             }
1241
1242         if (canone && !didone)
1243         {
1244             TRACE("Nice try, this is not going to work either\n");
1245             elem_callback(mmixer->lines[1].elem, SND_CTL_EVENT_MASK_VALUE);
1246             return MIXERR_INVALVALUE;
1247         }
1248
1249         for (x = i = 0; x<mmixer->chans; ++x)
1250             if (line != x && mmixer->lines[x].dst == line)
1251             {
1252                 if (mcdb[i].fValue)
1253                     for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1254                     {
1255                         if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
1256                             continue;
1257                         snd_mixer_selem_set_capture_switch(mmixer->lines[x].elem, chn, mcdb[i].fValue);
1258                     }
1259                 ++i;
1260             }
1261
1262         /* If it's a MUX, it means that only 1 channel can be selected
1263          * and the other channels are unselected
1264          *
1265          * For MIXER multiple sources are allowed, so unselect here
1266          */
1267         if (canone)
1268             break;
1269
1270         for (x = i = 0; x<mmixer->chans; ++x)
1271             if (line != x && mmixer->lines[x].dst == line)
1272             {
1273                 if (!mcdb[i].fValue)
1274                     for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1275                     {
1276                         if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
1277                             continue;
1278                         snd_mixer_selem_set_capture_switch(mmixer->lines[x].elem, chn, mcdb[i].fValue);
1279                     }
1280                 ++i;
1281             }
1282         break;
1283     }
1284     default:
1285         FIXME("Unhandled type %s\n", getControlType(ct->c.dwControlType));
1286         return MMSYSERR_INVALPARAM;
1287     }
1288     return MMSYSERR_NOERROR;
1289 }
1290
1291 /* Here we give info over the source/dest line given by dwSource+dwDest or dwDest, respectively
1292  * It is also possible that a line is found by componenttype or target type, latter is not implemented yet
1293  * Most important values returned in struct:
1294  * dwLineID
1295  * sz(Short)Name
1296  * line control count
1297  * amount of channels
1298  */
1299 static DWORD MIX_GetLineInfo(UINT wDevID, LPMIXERLINEW Ml, DWORD_PTR flags)
1300 {
1301     DWORD_PTR qf = flags & MIXER_GETLINEINFOF_QUERYMASK;
1302     mixer *mmixer = MIX_GetMix(wDevID);
1303     line *mline;
1304     int idx, i;
1305
1306     if (!Ml)
1307     {
1308         WARN("No Ml\n");
1309         return MMSYSERR_INVALPARAM;
1310     }
1311
1312     if (!mmixer)
1313     {
1314         WARN("Device %u not found\n", wDevID);
1315         return MMSYSERR_BADDEVICEID;
1316     }
1317
1318     if (Ml->cbStruct != sizeof(*Ml))
1319     {
1320         WARN("invalid parameter: Ml->cbStruct = %d\n", Ml->cbStruct);
1321         return MMSYSERR_INVALPARAM;
1322     }
1323
1324     Ml->dwUser  = 0;
1325     Ml->fdwLine = MIXERLINE_LINEF_DISCONNECTED;
1326     switch (qf)
1327     {
1328     case MIXER_GETLINEINFOF_COMPONENTTYPE:
1329     {
1330         Ml->dwLineID = 0xFFFF;
1331         TRACE("Looking for componenttype %d/%x\n", Ml->dwComponentType, Ml->dwComponentType);
1332         for (idx = 0; idx < mmixer->chans; ++idx)
1333             if (mmixer->lines[idx].component == Ml->dwComponentType)
1334             {
1335                 Ml->dwLineID = idx;
1336                 break;
1337             }
1338         if (Ml->dwLineID == 0xFFFF)
1339             return MMSYSERR_KEYNOTFOUND;
1340         /* Now that we have lineid, fallback to lineid*/
1341     }
1342
1343     case MIXER_GETLINEINFOF_LINEID:
1344         if (Ml->dwLineID >= mmixer->chans)
1345             return MIXERR_INVALLINE;
1346
1347         TRACE("MIXER_GETLINEINFOF_LINEID %d\n", Ml->dwLineID);
1348         Ml->dwDestination = mmixer->lines[Ml->dwLineID].dst;
1349
1350         if (Ml->dwDestination != Ml->dwLineID)
1351         {
1352             Ml->dwSource = getsrcfromline(mmixer, Ml->dwLineID);
1353             Ml->cConnections = 1;
1354         }
1355         else
1356         {
1357             Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
1358             Ml->dwSource = 0xFFFFFFFF;
1359         }
1360         TRACE("Connections %d, source %d\n", Ml->cConnections, Ml->dwSource);
1361         break;
1362
1363     case MIXER_GETLINEINFOF_DESTINATION:
1364         if (Ml->dwDestination >= mmixer->dests)
1365         {
1366             WARN("dest %d out of bounds\n", Ml->dwDestination);
1367             return MIXERR_INVALLINE;
1368         }
1369
1370         Ml->dwLineID = Ml->dwDestination;
1371         Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
1372         Ml->dwSource = 0xFFFFFFFF;
1373         break;
1374
1375     case MIXER_GETLINEINFOF_SOURCE:
1376         if (Ml->dwDestination >= mmixer->dests)
1377         {
1378             WARN("dest %d for source out of bounds\n", Ml->dwDestination);
1379             return MIXERR_INVALLINE;
1380         }
1381
1382         if (Ml->dwSource >= getsrccntfromchan(mmixer, Ml->dwDestination))
1383         {
1384             WARN("src %d out of bounds\n", Ml->dwSource);
1385             return MIXERR_INVALLINE;
1386         }
1387
1388         Ml->dwLineID = getsrclinefromchan(mmixer, Ml->dwDestination, Ml->dwSource);
1389         Ml->cConnections = 1;
1390         break;
1391
1392     case MIXER_GETLINEINFOF_TARGETTYPE:
1393         FIXME("TODO: TARGETTYPE, stub\n");
1394         return MMSYSERR_INVALPARAM;
1395
1396     default:
1397         FIXME("Unknown query flag: %08lx\n", qf);
1398         return MMSYSERR_INVALPARAM;
1399     }
1400
1401     Ml->fdwLine &= ~MIXERLINE_LINEF_DISCONNECTED;
1402     Ml->fdwLine |= MIXERLINE_LINEF_ACTIVE;
1403     if (Ml->dwLineID >= mmixer->dests)
1404         Ml->fdwLine |= MIXERLINE_LINEF_SOURCE;
1405
1406     mline = &mmixer->lines[Ml->dwLineID];
1407     Ml->dwComponentType = mline->component;
1408     Ml->cChannels = mmixer->lines[Ml->dwLineID].chans;
1409     Ml->cControls = 0;
1410
1411     for (i=CONTROLSPERLINE*Ml->dwLineID;i<CONTROLSPERLINE*(Ml->dwLineID+1); ++i)
1412         if (mmixer->controls[i].enabled)
1413             ++(Ml->cControls);
1414
1415     lstrcpynW(Ml->szShortName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szShortName)/sizeof(WCHAR));
1416     lstrcpynW(Ml->szName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szName)/sizeof(WCHAR));
1417     if (mline->capt)
1418         Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
1419     else
1420         Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
1421     Ml->Target.dwDeviceID = 0xFFFFFFFF;
1422     Ml->Target.wMid = WINE_MIXER_MANUF_ID;
1423     Ml->Target.wPid = WINE_MIXER_PRODUCT_ID;
1424     Ml->Target.vDriverVersion = WINE_MIXER_VERSION;
1425     lstrcpynW(Ml->Target.szPname, mmixer->mixername, sizeof(Ml->Target.szPname)/sizeof(WCHAR));
1426     return MMSYSERR_NOERROR;
1427 }
1428
1429 /* Get the controls that belong to a certain line, either all or 1 */
1430 static DWORD MIX_GetLineControls(UINT wDevID, LPMIXERLINECONTROLSW mlc, DWORD_PTR flags)
1431 {
1432     mixer *mmixer = MIX_GetMix(wDevID);
1433     int i,j = 0;
1434     DWORD ct;
1435
1436     if (!mlc || mlc->cbStruct != sizeof(*mlc))
1437     {
1438         WARN("Invalid mlc %p, cbStruct: %d\n", mlc, (!mlc ? -1 : mlc->cbStruct));
1439         return MMSYSERR_INVALPARAM;
1440     }
1441
1442     if (mlc->cbmxctrl != sizeof(MIXERCONTROLW))
1443     {
1444         WARN("cbmxctrl %d\n", mlc->cbmxctrl);
1445         return MMSYSERR_INVALPARAM;
1446     }
1447
1448     if (!mmixer)
1449         return MMSYSERR_BADDEVICEID;
1450
1451     flags &= MIXER_GETLINECONTROLSF_QUERYMASK;
1452
1453     if (flags == MIXER_GETLINECONTROLSF_ONEBYID)
1454         mlc->dwLineID = mlc->u.dwControlID / CONTROLSPERLINE;
1455
1456     if (mlc->dwLineID >= mmixer->chans)
1457     {
1458         TRACE("Invalid dwLineID %d\n", mlc->dwLineID);
1459         return MIXERR_INVALLINE;
1460     }
1461
1462     switch (flags)
1463     {
1464     case MIXER_GETLINECONTROLSF_ALL:
1465        TRACE("line=%08x MIXER_GETLINECONTROLSF_ALL (%d)\n", mlc->dwLineID, mlc->cControls);
1466        for (i = 0; i < CONTROLSPERLINE; ++i)
1467            if (mmixer->controls[i+mlc->dwLineID * CONTROLSPERLINE].enabled)
1468            {
1469                memcpy(&mlc->pamxctrl[j], &mmixer->controls[i+mlc->dwLineID * CONTROLSPERLINE].c, sizeof(MIXERCONTROLW));
1470                TRACE("Added %s (%s)\n", debugstr_w(mlc->pamxctrl[j].szShortName), debugstr_w(mlc->pamxctrl[j].szName));
1471                ++j;
1472                if (j > mlc->cControls)
1473                {
1474                    WARN("invalid parameter\n");
1475                    return MMSYSERR_INVALPARAM;
1476                }
1477            }
1478
1479         if (!j || mlc->cControls > j)
1480         {
1481             WARN("invalid parameter\n");
1482             return MMSYSERR_INVALPARAM;
1483         }
1484         break;
1485     case MIXER_GETLINECONTROLSF_ONEBYID:
1486         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n", mlc->dwLineID, mlc->u.dwControlID);
1487
1488         if (!mmixer->controls[mlc->u.dwControlID].enabled)
1489            return MIXERR_INVALCONTROL;
1490
1491         mlc->pamxctrl[0] = mmixer->controls[mlc->u.dwControlID].c;
1492         break;
1493     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
1494         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", mlc->dwLineID, getControlType(mlc->u.dwControlType));
1495
1496         ct = mlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
1497         for (i = 0; i <= CONTROLSPERLINE; ++i)
1498         {
1499             const int ofs = i+mlc->dwLineID*CONTROLSPERLINE;
1500             if (i == CONTROLSPERLINE)
1501             {
1502                 WARN("invalid parameter: control %s not found\n", getControlType(mlc->u.dwControlType));
1503                 return MIXERR_INVALCONTROL;
1504             }
1505             if (mmixer->controls[ofs].enabled && (mmixer->controls[ofs].c.dwControlType & MIXERCONTROL_CT_CLASS_MASK) == ct)
1506             {
1507                 mlc->pamxctrl[0] = mmixer->controls[ofs].c;
1508                 break;
1509             }
1510         }
1511     break;
1512     default:
1513         FIXME("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
1514         return MMSYSERR_INVALPARAM;
1515     }
1516
1517     return MMSYSERR_NOERROR;
1518 }
1519
1520 #endif /*HAVE_ALSA*/
1521
1522 /**************************************************************************
1523  *                        mxdMessage (WINEALSA.3)
1524  */
1525 DWORD WINAPI ALSA_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1526                              DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1527 {
1528 #ifdef HAVE_ALSA
1529     DWORD ret;
1530     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
1531           dwUser, dwParam1, dwParam2);
1532
1533     switch (wMsg)
1534     {
1535     case DRVM_INIT: ALSA_MixerInit(); ret = MMSYSERR_NOERROR; break;
1536     case DRVM_EXIT: ALSA_MixerExit(); ret = MMSYSERR_NOERROR; break;
1537     /* All taken care of by driver initialisation */
1538     /* Unimplemented, and not needed */
1539     case DRVM_ENABLE:
1540     case DRVM_DISABLE:
1541         ret = MMSYSERR_NOERROR; break;
1542
1543     case MXDM_OPEN:
1544         ret = MIX_Open(wDevID, (LPMIXEROPENDESC) dwParam1, dwParam2); break;
1545
1546     case MXDM_CLOSE:
1547         ret = MIX_Close(wDevID); break;
1548
1549     case MXDM_GETDEVCAPS:
1550         ret = MIX_GetDevCaps(wDevID, (LPMIXERCAPS2W)dwParam1, dwParam2); break;
1551
1552     case MXDM_GETLINEINFO:
1553         ret = MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2); break;
1554
1555     case MXDM_GETLINECONTROLS:
1556         ret = MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2); break;
1557
1558     case MXDM_GETCONTROLDETAILS:
1559         ret = MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); break;
1560
1561     case MXDM_SETCONTROLDETAILS:
1562         ret = MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); break;
1563
1564     case MXDM_GETNUMDEVS:
1565         ret = cards; break;
1566
1567     default:
1568         WARN("unknown message %s!\n", getMessage(wMsg));
1569         return MMSYSERR_NOTSUPPORTED;
1570     }
1571
1572     TRACE("Returning %08X\n", ret);
1573     return ret;
1574 #else /*HAVE_ALSA*/
1575     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1576
1577     return MMSYSERR_NOTENABLED;
1578 #endif /*HAVE_ALSA*/
1579 }