mshtml: Implement IHTMLDOMNode previousSibling.
[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 (!mline->elem)
254             break;
255         if (id == 1 && !mline->elem)
256             continue;
257
258         if (mline->capt && snd_mixer_selem_has_capture_volume(mline->elem))
259             snd_mixer_selem_get_capture_volume_range(mline->elem, &min, &max);
260         else
261             snd_mixer_selem_get_playback_volume_range(mline->elem, &min, &max);
262
263         /* (!snd_mixer_selem_has_playback_volume(elem) || snd_mixer_selem_has_capture_volume(elem)) */
264         /* Volume, always enabled by definition of blacklisted channels */
265         mmixer->controls[ofs].enabled = 1;
266         mmixer->controls[ofs].c.cbStruct = sizeof(mmixer->controls[ofs].c);
267         mmixer->controls[ofs].c.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
268         mmixer->controls[ofs].c.dwControlID = ofs;
269         mmixer->controls[ofs].c.Bounds.s1.dwMinimum = 0;
270         mmixer->controls[ofs].c.Bounds.s1.dwMaximum = 65535;
271         mmixer->controls[ofs].c.Metrics.cSteps = 65536/(max-min);
272
273         if ((id == 1 && snd_mixer_selem_has_capture_switch(mline->elem)) ||
274             (!mline->capt && snd_mixer_selem_has_playback_switch(mline->elem)))
275         { /* MUTE button optional, main capture channel should have one too */
276             mmixer->controls[ofs+OFS_MUTE].enabled = 1;
277             mmixer->controls[ofs+OFS_MUTE].c.cbStruct = sizeof(mmixer->controls[ofs].c);
278             mmixer->controls[ofs+OFS_MUTE].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
279             mmixer->controls[ofs+OFS_MUTE].c.dwControlID = ofs+OFS_MUTE;
280             mmixer->controls[ofs+OFS_MUTE].c.Bounds.s1.dwMaximum = 1;
281         }
282
283         if (mline->capt && snd_mixer_selem_has_capture_switch_exclusive(mline->elem))
284             mmixer->controls[CONTROLSPERLINE+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
285
286         if (id == 1)
287         { /* Capture select, in case cMultipleItems is 0, it means capture is disabled anyway */
288             mmixer->controls[ofs+OFS_MUX].enabled = 1;
289             mmixer->controls[ofs+OFS_MUX].c.cbStruct = sizeof(mmixer->controls[ofs].c);
290             mmixer->controls[ofs+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER;
291             mmixer->controls[ofs+OFS_MUX].c.dwControlID = ofs+OFS_MUX;
292             mmixer->controls[ofs+OFS_MUX].c.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
293
294             for (x = 0; x<mmixer->chans; ++x)
295                 if (x != id && mmixer->lines[x].dst == id)
296                     ++(mmixer->controls[ofs+OFS_MUX].c.cMultipleItems);
297             if (!mmixer->controls[ofs+OFS_MUX].c.cMultipleItems)
298                 mmixer->controls[ofs+OFS_MUX].enabled = 0;
299
300             mmixer->controls[ofs+OFS_MUX].c.Bounds.s1.dwMaximum = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems - 1;
301             mmixer->controls[ofs+OFS_MUX].c.Metrics.cSteps = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems;
302         }
303         for (x=0; x<CONTROLSPERLINE; ++x)
304         {
305             lstrcpynW(mmixer->controls[ofs+x].c.szShortName, mline->name, sizeof(mmixer->controls[ofs+x].c.szShortName)/sizeof(WCHAR));
306             lstrcpynW(mmixer->controls[ofs+x].c.szName, mline->name, sizeof(mmixer->controls[ofs+x].c.szName)/sizeof(WCHAR));
307         }
308     }
309 }
310
311 /* get amount of channels for elem */
312 /* Officially we should keep capture/playback separated,
313  * but that's not going to work in the alsa api */
314 static int chans(mixer *mmixer, snd_mixer_elem_t * elem, DWORD capt)
315 {
316     int ret=0, chn;
317
318     if (capt && snd_mixer_selem_has_capture_volume(elem)) {
319         for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
320             if (snd_mixer_selem_has_capture_channel(elem, chn))
321                 ++ret;
322     } else {
323         for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
324             if (snd_mixer_selem_has_playback_channel(elem, chn))
325                 ++ret;
326     }
327     if (!ret)
328         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"));
329     return ret;
330 }
331
332 static void filllines(mixer *mmixer, snd_mixer_elem_t *mastelem, snd_mixer_elem_t *captelem, int capt)
333 {
334     snd_mixer_elem_t *elem;
335     line *mline = mmixer->lines;
336
337     if (mastelem) {
338         /* Master control */
339         MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(mastelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
340         mline->component = getcomponenttype(snd_mixer_selem_get_name(mastelem));
341         mline->dst = 0;
342         mline->capt = 0;
343         mline->elem = mastelem;
344         mline->chans = chans(mmixer, mastelem, 0);
345
346         snd_mixer_elem_set_callback(mastelem, &elem_callback);
347         snd_mixer_elem_set_callback_private(mastelem, mmixer);
348     } else {
349         MultiByteToWideChar(CP_UNIXCP, 0, "Empty Master Element", -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
350     }
351
352     /* Capture control
353      * Note: since mmixer->dests = 1, it means only playback control is visible
354      * This makes sense, because if there are no capture sources capture control
355      * can't do anything and should be invisible */
356
357     /* Control 1 is reserved for capture even when not enabled */
358     ++mline;
359     if (capt)
360     {
361         MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(captelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
362         mline->component = getcomponenttype(snd_mixer_selem_get_name(captelem));
363         mline->dst = 1;
364         mline->capt = 1;
365         mline->elem = captelem;
366         mline->chans = chans(mmixer, captelem, 1);
367
368         snd_mixer_elem_set_callback(captelem, &elem_callback);
369         snd_mixer_elem_set_callback_private(captelem, mmixer);
370     }
371
372     for (elem = snd_mixer_first_elem(mmixer->mix); elem; elem = snd_mixer_elem_next(elem))
373         if (elem != mastelem && elem != captelem && !blacklisted(elem))
374         {
375             const char * name = snd_mixer_selem_get_name(elem);
376             DWORD comp = getcomponenttype(name);
377
378             if (snd_mixer_selem_has_playback_volume(elem) &&
379                (snd_mixer_selem_has_capture_volume(elem) || comp != MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE))
380             {
381                 (++mline)->component = comp;
382                 MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
383                 mline->capt = mline->dst = 0;
384                 mline->elem = elem;
385                 mline->chans = chans(mmixer, elem, 0);
386             }
387             else if (!capt)
388                 continue;
389
390             if (capt && (snd_mixer_selem_has_capture_volume(elem) || comp == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE))
391             {
392                 (++mline)->component = comp;
393                 MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
394                 mline->capt = mline->dst = 1;
395                 mline->elem = elem;
396                 mline->chans = chans(mmixer, elem, 1);
397             }
398
399             snd_mixer_elem_set_callback(elem, &elem_callback);
400             snd_mixer_elem_set_callback_private(elem, mmixer);
401         }
402 }
403
404 static void filllines_no_master(mixer *mmixer, snd_mixer_elem_t *captelem, int capt)
405 {
406     line *mline = mmixer->lines;
407
408     MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(captelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
409     mline->component = getcomponenttype(snd_mixer_selem_get_name(captelem));
410     mline->dst = 0;
411     mline->capt = 1;
412     mline->elem = captelem;
413     mline->chans = chans(mmixer, captelem, 1);
414
415     snd_mixer_elem_set_callback(captelem, &elem_callback);
416     snd_mixer_elem_set_callback_private(captelem, mmixer);
417 }
418
419 /* Windows api wants to have a 'master' device to which all slaves are attached
420  * There are 2 ones in this code:
421  * - 'Master', fall back to 'Headphone' if unavailable, and if that's not available 'PCM'
422  * - 'Capture'
423  * Capture might not always be available, so should be prepared to be without if needed
424  */
425
426 static void ALSA_MixerInit(void)
427 {
428     int x, mixnum = 0;
429     snd_ctl_card_info_t *info;
430
431     info = HeapAlloc( GetProcessHeap(), 0, snd_ctl_card_info_sizeof());
432     for (x = 0; x < MAX_MIXERS; ++x)
433     {
434         int card, err, capcontrols = 0, total_elems = 0;
435         char cardind[6], cardname[10];
436
437         snd_ctl_t *ctl;
438         snd_mixer_elem_t *elem, *mastelem = NULL, *headelem = NULL, *captelem = NULL, *pcmelem = NULL, *lineelem = NULL, *micelem = NULL;
439
440         memset(info, 0, snd_ctl_card_info_sizeof());
441         memset(&mixdev[mixnum], 0, sizeof(*mixdev));
442         snprintf(cardind, sizeof(cardind), "%d", x);
443         card = snd_card_get_index(cardind);
444         if (card < 0)
445             continue;
446
447         snprintf(cardname, sizeof(cardname), "hw:%d", card);
448
449         err = snd_ctl_open(&ctl, cardname, 0);
450         if (err < 0)
451         {
452             WARN("Cannot open card: %s\n", snd_strerror(err));
453             continue;
454         }
455
456         err = snd_ctl_card_info(ctl, info);
457         if (err < 0)
458         {
459             WARN("Cannot get card info: %s\n", snd_strerror(err));
460             snd_ctl_close(ctl);
461             continue;
462         }
463
464         MultiByteToWideChar(CP_UNIXCP, 0, snd_ctl_card_info_get_name(info), -1, mixdev[mixnum].mixername, sizeof(mixdev[mixnum].mixername)/sizeof(WCHAR));
465         snd_ctl_close(ctl);
466
467         err = snd_mixer_open(&mixdev[mixnum].mix, 0);
468         if (err < 0)
469         {
470             WARN("Error occurred opening mixer: %s\n", snd_strerror(err));
471             continue;
472         }
473
474         err = snd_mixer_attach(mixdev[mixnum].mix, cardname);
475         if (err < 0)
476             goto eclose;
477
478         err = snd_mixer_selem_register(mixdev[mixnum].mix, NULL, NULL);
479         if (err < 0)
480             goto eclose;
481
482         err = snd_mixer_load(mixdev[mixnum].mix);
483         if (err < 0)
484             goto eclose;
485
486         /* First, lets see what's available..
487          * If there are multiple Master or Captures, all except 1 will be added as slaves
488          */
489         total_elems = snd_mixer_get_count(mixdev[mixnum].mix);
490         TRACE("Total elems: %d\n", total_elems);
491
492         for (elem = snd_mixer_first_elem(mixdev[mixnum].mix); elem; elem = snd_mixer_elem_next(elem))
493             if (!strcasecmp(snd_mixer_selem_get_name(elem), "Master") && !mastelem)
494             {
495                 mastelem = elem;
496                 ++(mixdev[mixnum].chans);
497             }
498             else if (!strcasecmp(snd_mixer_selem_get_name(elem), "Capture") && !captelem)
499                 captelem = elem;
500             else if (!strcasecmp(snd_mixer_selem_get_name(elem), "Mic") && !micelem && !mastelem && total_elems == 1)
501                 /* this is what snd-usb-audio mics look like; just a Mic control and that's it.*/
502                 micelem = elem;
503             else if (!blacklisted(elem))
504             {
505                 DWORD comp = getcomponenttype(snd_mixer_selem_get_name(elem));
506                 DWORD skip = 0;
507
508                 /* Work around buggy drivers: Make this a capture control if the name is recognised as a microphone */
509                 if (snd_mixer_selem_has_capture_volume(elem))
510                     ++capcontrols;
511                 else if (comp == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
512                 {
513                     ++capcontrols;
514                     skip = 1;
515                 }
516
517                 if (!skip && snd_mixer_selem_has_playback_volume(elem))
518                 {
519                     if (!strcasecmp(snd_mixer_selem_get_name(elem), "Headphone") && !headelem)
520                         headelem = elem;
521                     else if (!strcasecmp(snd_mixer_selem_get_name(elem), "PCM") && !pcmelem)
522                         pcmelem = elem;
523                     else if (!strcasecmp(snd_mixer_selem_get_name(elem), "Line") && !lineelem)
524                         lineelem = elem;
525                     ++(mixdev[mixnum].chans);
526                 }
527             }
528
529         /* Add dummy capture channel, wanted by Windows  */
530         mixdev[mixnum].chans += 1;
531
532         /* If there is only 'Capture' and 'Master', this device is not worth it */
533         if (mixdev[mixnum].chans == 2)
534         {
535             WARN("No channels found, skipping device!\n");
536             goto close;
537         }
538
539         /* Master element can't have a capture control in this code, so
540          * if Headphone or PCM is promoted to master, unset its capture control */
541         if (headelem && !mastelem)
542         {
543             /* Using 'Headphone' as master device */
544             mastelem = headelem;
545             capcontrols -= !!snd_mixer_selem_has_capture_switch(mastelem);
546         }
547         else if (pcmelem && !mastelem)
548         {
549             /* Use 'PCM' as master device */
550             mastelem = pcmelem;
551             capcontrols -= !!snd_mixer_selem_has_capture_switch(mastelem);
552         }
553         else if (lineelem && !mastelem)
554         {
555             /* Use 'Line' as master device */
556             mastelem = lineelem;
557             capcontrols -= !!snd_mixer_selem_has_capture_switch(mastelem);
558         }
559         else if (!mastelem && !captelem && !micelem)
560         {
561             /* If there is nothing sensible that can act as 'Master' control, something is wrong */
562             FIXME("No master control found on %s, disabling mixer\n", snd_ctl_card_info_get_name(info));
563             goto close;
564         }
565
566         if (!captelem || !capcontrols)
567         {
568             /* Can't enable capture, so disabling it
569              * Note: capture control will still exist because
570              * dwLineID 0 and 1 are reserved for Master and Capture
571              */
572             WARN("No use enabling capture part of mixer, capture control found: %s, amount of capture controls: %d\n",
573                  (!captelem ? "no" : "yes"), capcontrols);
574             capcontrols = 0;
575             mixdev[mixnum].dests = 1;
576         }
577         else
578         {
579             mixdev[mixnum].chans += capcontrols;
580             mixdev[mixnum].dests = 2;
581         }
582
583         mixdev[mixnum].lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(line) * mixdev[mixnum].chans);
584         mixdev[mixnum].controls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(control) * CONTROLSPERLINE*mixdev[mixnum].chans);
585         err = -ENOMEM;
586         if (!mixdev[mixnum].lines || !mixdev[mixnum].controls)
587             goto close;
588
589         if (mastelem)
590             filllines(&mixdev[mixnum], mastelem, captelem, capcontrols);
591         else if (micelem)
592             filllines_no_master(&mixdev[mixnum], micelem, 1);
593         fillcontrols(&mixdev[mixnum]);
594
595         TRACE("%s: Amount of controls: %i/%i, name: %s\n", cardname, mixdev[mixnum].dests, mixdev[mixnum].chans, debugstr_w(mixdev[mixnum].mixername));
596         mixnum++;
597         continue;
598
599         eclose:
600         WARN("Error occurred initialising mixer: %s\n", snd_strerror(err));
601         close:
602         HeapFree(GetProcessHeap(), 0, mixdev[mixnum].lines);
603         HeapFree(GetProcessHeap(), 0, mixdev[mixnum].controls);
604         snd_mixer_close(mixdev[mixnum].mix);
605     }
606     cards = mixnum;
607     HeapFree( GetProcessHeap(), 0, info );
608
609     /* There is no trouble with already assigning callbacks without initialising critsect:
610      * Callbacks only occur when snd_mixer_handle_events is called (only happens in thread)
611      */
612     InitializeCriticalSection(&elem_crst);
613     elem_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MIXER.elem_crst");
614     TRACE("\n");
615 }
616
617 static void ALSA_MixerExit(void)
618 {
619     int x;
620
621     if (refcnt)
622     {
623         WARN("Callback thread still alive, terminating uncleanly, refcnt: %d\n", refcnt);
624         /* Least we can do is making sure we're not in 'foreign' code */
625         EnterCriticalSection(&elem_crst);
626         TerminateThread(thread, 1);
627         refcnt = 0;
628         LeaveCriticalSection(&elem_crst);
629     }
630
631     TRACE("Cleaning up\n");
632
633     elem_crst.DebugInfo->Spare[0] = 0;
634     DeleteCriticalSection(&elem_crst);
635     for (x = 0; x < cards; ++x)
636     {
637         snd_mixer_close(mixdev[x].mix);
638         HeapFree(GetProcessHeap(), 0, mixdev[x].lines);
639         HeapFree(GetProcessHeap(), 0, mixdev[x].controls);
640     }
641     cards = 0;
642 }
643
644 static mixer* MIX_GetMix(UINT wDevID)
645 {
646     mixer *mmixer;
647
648     if (wDevID >= cards)
649     {
650         WARN("Invalid mixer id: %d\n", wDevID);
651         return NULL;
652     }
653
654     mmixer = &mixdev[wDevID];
655     return mmixer;
656 }
657
658 /* Since alsa doesn't tell what exactly changed, just assume all affected controls changed */
659 static int elem_callback(snd_mixer_elem_t *elem, unsigned int type)
660 {
661     mixer *mmixer = snd_mixer_elem_get_callback_private(elem);
662     int x;
663     BOOL captchanged = 0;
664
665     if (type != SND_CTL_EVENT_MASK_VALUE)
666         return 0;
667
668     assert(mmixer);
669
670     EnterCriticalSection(&elem_crst);
671
672     if (!mmixer->callback)
673         goto out;
674
675     for (x=0; x<mmixer->chans; ++x)
676     {
677         const int ofs = CONTROLSPERLINE*x;
678         if (elem != mmixer->lines[x].elem)
679             continue;
680
681         if (mmixer->lines[x].capt)
682             ++captchanged;
683
684         TRACE("Found changed control %s\n", debugstr_w(mmixer->lines[x].name));
685         mmixer->callback(mmixer->hmx, MM_MIXM_LINE_CHANGE, mmixer->callbackpriv, x, 0);
686         mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs, 0);
687
688         if (mmixer->controls[ofs+OFS_MUTE].enabled)
689             mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs+OFS_MUTE, 0);
690     }
691     if (captchanged)
692         mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, CONTROLSPERLINE+OFS_MUX, 0);
693
694     out:
695     LeaveCriticalSection(&elem_crst);
696
697     return 0;
698 }
699
700 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam)
701 {
702     struct pollfd *pfds = NULL;
703     int x, y, err, mcnt, count = 1;
704
705     TRACE("%p\n", lParam);
706
707     for (x = 0; x < cards; ++x)
708         count += snd_mixer_poll_descriptors_count(mixdev[x].mix);
709
710     TRACE("Counted %d descriptors\n", count);
711     pfds = HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct pollfd));
712
713     if (!pfds)
714     {
715         WARN("Out of memory\n");
716         goto die;
717     }
718
719     pfds[0].fd = msg_pipe[0];
720     pfds[0].events = POLLIN;
721
722     y = 1;
723     for (x = 0; x < cards; ++x)
724         y += snd_mixer_poll_descriptors(mixdev[x].mix, &pfds[y], count - y);
725
726     while ((err = poll(pfds, (unsigned int) count, -1)) >= 0 || errno == EINTR || errno == EAGAIN)
727     {
728         if (pfds[0].revents & POLLIN)
729             break;
730
731         mcnt = 1;
732         for (x = y = 0; x < cards; ++x)
733         {
734             int j, max = snd_mixer_poll_descriptors_count(mixdev[x].mix);
735             for (j = 0; j < max; ++j)
736                 if (pfds[mcnt+j].revents)
737                 {
738                     y += snd_mixer_handle_events(mixdev[x].mix);
739                     break;
740                 }
741             mcnt += max;
742         }
743         if (y)
744             TRACE("Handled %d events\n", y);
745     }
746
747     die:
748     TRACE("Shutting down\n");
749     HeapFree(GetProcessHeap(), 0, pfds);
750
751     y = read(msg_pipe[0], &x, sizeof(x));
752     close(msg_pipe[1]);
753     close(msg_pipe[0]);
754     return 0;
755 }
756
757 static DWORD MIX_Open(UINT wDevID, LPMIXEROPENDESC desc, DWORD_PTR flags)
758 {
759     mixer *mmixer = MIX_GetMix(wDevID);
760     if (!mmixer)
761         return MMSYSERR_BADDEVICEID;
762
763     flags &= CALLBACK_TYPEMASK;
764     switch (flags)
765     {
766     case CALLBACK_NULL:
767         goto done;
768
769     case CALLBACK_FUNCTION:
770         break;
771
772     default:
773         FIXME("Unhandled callback type: %08lx\n", flags & CALLBACK_TYPEMASK);
774         return MIXERR_INVALVALUE;
775     }
776
777     mmixer->callback = (LPDRVCALLBACK)desc->dwCallback;
778     mmixer->callbackpriv = desc->dwInstance;
779     mmixer->hmx = (HDRVR)desc->hmx;
780
781     done:
782     if (InterlockedIncrement(&refcnt) == 1)
783     {
784         if (pipe(msg_pipe) >= 0)
785         {
786             thread = CreateThread(NULL, 0, ALSA_MixerPollThread, NULL, 0, NULL);
787             if (!thread)
788             {
789                 close(msg_pipe[0]);
790                 close(msg_pipe[1]);
791                 msg_pipe[0] = msg_pipe[1] = -1;
792             }
793         }
794         else
795             msg_pipe[0] = msg_pipe[1] = -1;
796     }
797
798     return MMSYSERR_NOERROR;
799 }
800
801 static DWORD MIX_Close(UINT wDevID)
802 {
803     int x = 0;
804     mixer *mmixer = MIX_GetMix(wDevID);
805     if (!mmixer)
806         return MMSYSERR_BADDEVICEID;
807
808     EnterCriticalSection(&elem_crst);
809     mmixer->callback = 0;
810     LeaveCriticalSection(&elem_crst);
811
812     if (!InterlockedDecrement(&refcnt))
813     {
814         if (write(msg_pipe[1], &x, sizeof(x)) > 0)
815         {
816             TRACE("Shutting down thread...\n");
817             WaitForSingleObject(thread, INFINITE);
818             TRACE("Done\n");
819         }
820     }
821
822     return MMSYSERR_NOERROR;
823 }
824
825 static DWORD MIX_GetDevCaps(UINT wDevID, LPMIXERCAPS2W caps, DWORD_PTR parm2)
826 {
827     mixer *mmixer = MIX_GetMix(wDevID);
828     MIXERCAPS2W capsW;
829
830     if (!caps)
831         return MMSYSERR_INVALPARAM;
832
833     if (!mmixer)
834         return MMSYSERR_BADDEVICEID;
835
836     memset(&capsW, 0, sizeof(MIXERCAPS2W));
837
838     capsW.wMid = WINE_MIXER_MANUF_ID;
839     capsW.wPid = WINE_MIXER_PRODUCT_ID;
840     capsW.vDriverVersion = WINE_MIXER_VERSION;
841
842     lstrcpynW(capsW.szPname, mmixer->mixername, sizeof(capsW.szPname)/sizeof(WCHAR));
843     capsW.cDestinations = mmixer->dests;
844     memcpy(caps, &capsW, min(parm2, sizeof(capsW)));
845     return MMSYSERR_NOERROR;
846 }
847
848 /* convert win32 volume to alsa volume, and vice versa */
849 static INT normalized(INT value, INT prevmax, INT nextmax)
850 {
851     int ret = MulDiv(value, nextmax, prevmax);
852
853     /* Have to stay in range */
854     TRACE("%d/%d -> %d/%d\n", value, prevmax, ret, nextmax);
855     if (ret > nextmax)
856         ret = nextmax;
857     else if (ret < 0)
858         ret = 0;
859
860     return ret;
861 }
862
863 /* get amount of sources for dest */
864 static int getsrccntfromchan(mixer *mmixer, int dad)
865 {
866     int i, j=0;
867
868     for (i=0; i<mmixer->chans; ++i)
869         if (i != dad && mmixer->lines[i].dst == dad)
870         {
871             ++j;
872         }
873     if (!j)
874         FIXME("No src found for %i (%s)?\n", dad, debugstr_w(mmixer->lines[dad].name));
875     return j;
876 }
877
878 /* find lineid for source 'num' with dest 'dad' */
879 static int getsrclinefromchan(mixer *mmixer, int dad, int num)
880 {
881     int i, j=0;
882     for (i=0; i<mmixer->chans; ++i)
883         if (i != dad && mmixer->lines[i].dst == dad)
884         {
885             if (num == j)
886                 return i;
887             ++j;
888         }
889     WARN("No src found for src %i from dest %i\n", num, dad);
890     return 0;
891 }
892
893 /* get the source number belonging to line */
894 static int getsrcfromline(mixer *mmixer, int line)
895 {
896     int i, j=0, dad = mmixer->lines[line].dst;
897
898     for (i=0; i<mmixer->chans; ++i)
899         if (i != dad && mmixer->lines[i].dst == dad)
900         {
901             if (line == i)
902                 return j;
903             ++j;
904         }
905     WARN("No src found for line %i with dad %i\n", line, dad);
906     return 0;
907 }
908
909 /* Get volume/muted/capture channel */
910 static DWORD MIX_GetControlDetails(UINT wDevID, LPMIXERCONTROLDETAILS mctrld, DWORD_PTR flags)
911 {
912     mixer *mmixer = MIX_GetMix(wDevID);
913     DWORD ctrl;
914     DWORD line;
915     control *ct;
916
917     if (!mctrld)
918         return MMSYSERR_INVALPARAM;
919
920     ctrl = mctrld->dwControlID;
921     line = ctrl/CONTROLSPERLINE;
922
923     if (mctrld->cbStruct != sizeof(*mctrld))
924         return MMSYSERR_INVALPARAM;
925
926     if (!mmixer)
927         return MMSYSERR_BADDEVICEID;
928
929     if (line >= mmixer->chans || !mmixer->controls[ctrl].enabled)
930         return MIXERR_INVALCONTROL;
931
932     ct = &mmixer->controls[ctrl];
933
934     flags &= MIXER_GETCONTROLDETAILSF_QUERYMASK;
935
936     switch (flags) {
937     case MIXER_GETCONTROLDETAILSF_VALUE:
938         TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%d/%d)\n", ctrl, line);
939         switch (ct->c.dwControlType)
940         {
941         case MIXERCONTROL_CONTROLTYPE_VOLUME:
942         {
943             long min = 0, max = 0, vol = 0;
944             int chn;
945             LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
946             snd_mixer_elem_t * elem = mmixer->lines[line].elem;
947
948             if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED))
949             {
950                 WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
951                 return MMSYSERR_INVALPARAM;
952             }
953
954             TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
955
956             mcdu = mctrld->paDetails;
957
958             if (mctrld->cChannels != 1 && mmixer->lines[line].chans != mctrld->cChannels)
959             {
960                 WARN("Unsupported cChannels (%d instead of %d)\n", mctrld->cChannels, mmixer->lines[line].chans);
961                 return MMSYSERR_INVALPARAM;
962             }
963
964             if (mmixer->lines[line].capt && snd_mixer_selem_has_capture_volume(elem)) {
965                 snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
966                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
967                     if (snd_mixer_selem_has_capture_channel(elem, chn))
968                     {
969                         snd_mixer_selem_get_capture_volume(elem, chn, &vol);
970                         mcdu->dwValue = normalized(vol - min, max, 65535);
971                         if (mctrld->cChannels == 1)
972                             break;
973                         ++mcdu;
974                     }
975             } else {
976                 snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
977
978                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
979                     if (snd_mixer_selem_has_playback_channel(elem, chn))
980                     {
981                         snd_mixer_selem_get_playback_volume(elem, chn, &vol);
982                         mcdu->dwValue = normalized(vol - min, max, 65535);
983                         if (mctrld->cChannels == 1)
984                             break;
985                         ++mcdu;
986                     }
987             }
988
989             return MMSYSERR_NOERROR;
990         }
991
992         case MIXERCONTROL_CONTROLTYPE_ONOFF:
993         case MIXERCONTROL_CONTROLTYPE_MUTE:
994         {
995             LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
996             int chn, ival;
997             snd_mixer_elem_t * elem = mmixer->lines[line].elem;
998
999             if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
1000             {
1001                 WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1002                 return MMSYSERR_INVALPARAM;
1003             }
1004
1005             TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1006
1007             mcdb = mctrld->paDetails;
1008
1009             if (line == 1)
1010                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1011                 {
1012                     if (!snd_mixer_selem_has_capture_channel(elem, chn))
1013                         continue;
1014                     snd_mixer_selem_get_capture_switch(elem, chn, &ival);
1015                     break;
1016                 }
1017             else
1018                 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1019                 {
1020                     if (!snd_mixer_selem_has_playback_channel(elem, chn))
1021                         continue;
1022                     snd_mixer_selem_get_playback_switch(elem, chn, &ival);
1023                     break;
1024                 }
1025
1026             if (chn > SND_MIXER_SCHN_LAST)
1027             {
1028                 TRACE("can't find active channel\n");
1029                 return MMSYSERR_INVALPARAM;  /* fixme: what's right error? */
1030             }
1031
1032             mcdb->fValue = !ival;
1033             TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
1034             return MMSYSERR_NOERROR;
1035         }
1036         case MIXERCONTROL_CONTROLTYPE_MIXER:
1037         case MIXERCONTROL_CONTROLTYPE_MUX:
1038         {
1039             LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
1040             int x, i=0, ival = 0, chn;
1041
1042             if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
1043             {
1044                 WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1045                 return MMSYSERR_INVALPARAM;
1046             }
1047
1048             TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1049
1050             mcdb = mctrld->paDetails;
1051
1052             for (x = 0; x<mmixer->chans; ++x)
1053                 if (line != x && mmixer->lines[x].dst == line)
1054                 {
1055                     ival = 0;
1056                     for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1057                     {
1058                         if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
1059                             continue;
1060                         snd_mixer_selem_get_capture_switch(mmixer->lines[x].elem, chn, &ival);
1061                         if (ival)
1062                             break;
1063                     }
1064                     if (i >= mctrld->u.cMultipleItems)
1065                     {
1066                         TRACE("overflow\n");
1067                         return MMSYSERR_INVALPARAM;
1068                     }
1069                     TRACE("fVal[%i] = %sselected\n", i, (!ival ? "un" : ""));
1070                     mcdb[i++].fValue = ival;
1071                 }
1072             break;
1073         }
1074         default:
1075
1076             FIXME("Unhandled controltype %s\n", getControlType(ct->c.dwControlType));
1077             return MMSYSERR_INVALPARAM;
1078         }
1079         return MMSYSERR_NOERROR;
1080
1081     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
1082         TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%d)\n", ctrl);
1083
1084         if (ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX || ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
1085         {
1086             LPMIXERCONTROLDETAILS_LISTTEXTW mcdlt = mctrld->paDetails;
1087             int i, j;
1088
1089             for (i = j = 0; j < mmixer->chans; ++j)
1090                 if (j != line && mmixer->lines[j].dst == line)
1091                 {
1092                     if (i > mctrld->u.cMultipleItems)
1093                         return MMSYSERR_INVALPARAM;
1094                     mcdlt->dwParam1 = j;
1095                     mcdlt->dwParam2 = mmixer->lines[j].component;
1096                     lstrcpynW(mcdlt->szName, mmixer->lines[j].name, sizeof(mcdlt->szName) / sizeof(WCHAR));
1097                     TRACE("Adding %i as %s\n", j, debugstr_w(mcdlt->szName));
1098                     ++i; ++mcdlt;
1099                 }
1100             if (i < mctrld->u.cMultipleItems)
1101                 return MMSYSERR_INVALPARAM;
1102             return MMSYSERR_NOERROR;
1103         }
1104         FIXME ("Imagine this code being horribly broken and incomplete, introducing: reality\n");
1105         return MMSYSERR_INVALPARAM;
1106
1107     default:
1108         WARN("Unknown flag (%08lx)\n", flags);
1109         return MMSYSERR_INVALPARAM;
1110     }
1111 }
1112
1113 /* Set volume/capture channel/muted for control */
1114 static DWORD MIX_SetControlDetails(UINT wDevID, LPMIXERCONTROLDETAILS mctrld, DWORD_PTR flags)
1115 {
1116     mixer *mmixer = MIX_GetMix(wDevID);
1117     DWORD ctrl, line, i;
1118     control *ct;
1119     snd_mixer_elem_t * elem;
1120
1121     if (!mctrld)
1122         return MMSYSERR_INVALPARAM;
1123
1124     ctrl = mctrld->dwControlID;
1125     line = ctrl/CONTROLSPERLINE;
1126
1127     if (mctrld->cbStruct != sizeof(*mctrld))
1128     {
1129         WARN("Invalid size of mctrld %d\n", mctrld->cbStruct);
1130         return MMSYSERR_INVALPARAM;
1131     }
1132
1133     if (!mmixer)
1134         return MMSYSERR_BADDEVICEID;
1135
1136     if (line >= mmixer->chans)
1137     {
1138         WARN("Invalid line id: %d not in range of 0-%d\n", line, mmixer->chans-1);
1139         return MMSYSERR_INVALPARAM;
1140     }
1141
1142     if (!mmixer->controls[ctrl].enabled)
1143     {
1144         WARN("Control %d not enabled\n", ctrl);
1145         return MIXERR_INVALCONTROL;
1146     }
1147
1148     ct = &mmixer->controls[ctrl];
1149     elem = mmixer->lines[line].elem;
1150     flags &= MIXER_SETCONTROLDETAILSF_QUERYMASK;
1151
1152     switch (flags) {
1153     case MIXER_SETCONTROLDETAILSF_VALUE:
1154         TRACE("MIXER_SETCONTROLDETAILSF_VALUE (%d)\n", ctrl);
1155         break;
1156
1157     default:
1158         WARN("Unknown flag (%08lx)\n", flags);
1159         return MMSYSERR_INVALPARAM;
1160     }
1161
1162     switch (ct->c.dwControlType)
1163     {
1164     case MIXERCONTROL_CONTROLTYPE_VOLUME:
1165     {
1166         long min = 0, max = 0;
1167         int chn;
1168         LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
1169         snd_mixer_elem_t * elem = mmixer->lines[line].elem;
1170
1171         if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED))
1172         {
1173             WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1174             return MMSYSERR_INVALPARAM;
1175         }
1176
1177         if (mctrld->cChannels != 1 && mmixer->lines[line].chans != mctrld->cChannels)
1178         {
1179             WARN("Unsupported cChannels (%d instead of %d)\n", mctrld->cChannels, mmixer->lines[line].chans);
1180             return MMSYSERR_INVALPARAM;
1181         }
1182
1183         TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1184         mcdu = mctrld->paDetails;
1185
1186         for (chn=0; chn<mctrld->cChannels;++chn)
1187         {
1188             TRACE("Chan %d value %d\n", chn, mcdu[chn].dwValue);
1189         }
1190
1191         /* There isn't always a capture volume, so in that case change playback volume */
1192         if (mmixer->lines[line].capt && snd_mixer_selem_has_capture_volume(elem))
1193         {
1194             snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
1195
1196             for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1197                 if (snd_mixer_selem_has_capture_channel(elem, chn))
1198                 {
1199                     snd_mixer_selem_set_capture_volume(elem, chn, min + normalized(mcdu->dwValue, 65535, max));
1200                     if (mctrld->cChannels != 1)
1201                         mcdu++;
1202                 }
1203         }
1204         else
1205         {
1206             snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
1207
1208             for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1209                 if (snd_mixer_selem_has_playback_channel(elem, chn))
1210                 {
1211                     snd_mixer_selem_set_playback_volume(elem, chn, min + normalized(mcdu->dwValue, 65535, max));
1212                     if (mctrld->cChannels != 1)
1213                         mcdu++;
1214                 }
1215         }
1216
1217         break;
1218     }
1219     case MIXERCONTROL_CONTROLTYPE_MUTE:
1220     case MIXERCONTROL_CONTROLTYPE_ONOFF:
1221     {
1222         LPMIXERCONTROLDETAILS_BOOLEAN   mcdb;
1223
1224         if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
1225         {
1226             WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1227             return MMSYSERR_INVALPARAM;
1228         }
1229
1230         TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1231
1232         mcdb = mctrld->paDetails;
1233         if (line == 1) /* Mute/unmute capturing */
1234             for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
1235             {
1236                 if (snd_mixer_selem_has_capture_channel(elem, i))
1237                     snd_mixer_selem_set_capture_switch(elem, i, !mcdb->fValue);
1238             }
1239         else
1240             for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
1241                 if (snd_mixer_selem_has_playback_channel(elem, i))
1242                     snd_mixer_selem_set_playback_switch(elem, i, !mcdb->fValue);
1243         break;
1244     }
1245
1246     case MIXERCONTROL_CONTROLTYPE_MIXER:
1247     case MIXERCONTROL_CONTROLTYPE_MUX:
1248     {
1249         LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
1250         int x, i=0, chn;
1251         int didone = 0, canone = (ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX);
1252
1253         if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
1254         {
1255             WARN("invalid parameter: cbDetails %d\n", mctrld->cbDetails);
1256             return MMSYSERR_INVALPARAM;
1257         }
1258
1259         TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
1260         mcdb = mctrld->paDetails;
1261
1262         for (x=i=0; x < mmixer->chans; ++x)
1263             if (line != x && mmixer->lines[x].dst == line)
1264             {
1265                 TRACE("fVal[%i] (%s) = %i\n", i, debugstr_w(mmixer->lines[x].name), mcdb[i].fValue);
1266                 if (i >= mctrld->u.cMultipleItems)
1267                 {
1268                     TRACE("Too many items to fit, overflowing\n");
1269                     return MIXERR_INVALVALUE;
1270                 }
1271                 if (mcdb[i].fValue && canone && didone)
1272                 {
1273                     TRACE("Nice try, but it's not going to work\n");
1274                     elem_callback(mmixer->lines[1].elem, SND_CTL_EVENT_MASK_VALUE);
1275                     return MIXERR_INVALVALUE;
1276                 }
1277                 if (mcdb[i].fValue)
1278                     didone = 1;
1279                 ++i;
1280             }
1281
1282         if (canone && !didone)
1283         {
1284             TRACE("Nice try, this is not going to work either\n");
1285             elem_callback(mmixer->lines[1].elem, SND_CTL_EVENT_MASK_VALUE);
1286             return MIXERR_INVALVALUE;
1287         }
1288
1289         for (x = i = 0; x<mmixer->chans; ++x)
1290             if (line != x && mmixer->lines[x].dst == line)
1291             {
1292                 if (mcdb[i].fValue)
1293                     for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1294                     {
1295                         if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
1296                             continue;
1297                         snd_mixer_selem_set_capture_switch(mmixer->lines[x].elem, chn, mcdb[i].fValue);
1298                     }
1299                 ++i;
1300             }
1301
1302         /* If it's a MUX, it means that only 1 channel can be selected
1303          * and the other channels are unselected
1304          *
1305          * For MIXER multiple sources are allowed, so unselect here
1306          */
1307         if (canone)
1308             break;
1309
1310         for (x = i = 0; x<mmixer->chans; ++x)
1311             if (line != x && mmixer->lines[x].dst == line)
1312             {
1313                 if (!mcdb[i].fValue)
1314                     for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
1315                     {
1316                         if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
1317                             continue;
1318                         snd_mixer_selem_set_capture_switch(mmixer->lines[x].elem, chn, mcdb[i].fValue);
1319                     }
1320                 ++i;
1321             }
1322         break;
1323     }
1324     default:
1325         FIXME("Unhandled type %s\n", getControlType(ct->c.dwControlType));
1326         return MMSYSERR_INVALPARAM;
1327     }
1328     return MMSYSERR_NOERROR;
1329 }
1330
1331 /* Here we give info over the source/dest line given by dwSource+dwDest or dwDest, respectively
1332  * It is also possible that a line is found by componenttype or target type, latter is not implemented yet
1333  * Most important values returned in struct:
1334  * dwLineID
1335  * sz(Short)Name
1336  * line control count
1337  * amount of channels
1338  */
1339 static DWORD MIX_GetLineInfo(UINT wDevID, LPMIXERLINEW Ml, DWORD_PTR flags)
1340 {
1341     DWORD_PTR qf = flags & MIXER_GETLINEINFOF_QUERYMASK;
1342     mixer *mmixer = MIX_GetMix(wDevID);
1343     line *mline;
1344     int idx, i;
1345
1346     if (!Ml)
1347     {
1348         WARN("No Ml\n");
1349         return MMSYSERR_INVALPARAM;
1350     }
1351
1352     if (!mmixer)
1353     {
1354         WARN("Device %u not found\n", wDevID);
1355         return MMSYSERR_BADDEVICEID;
1356     }
1357
1358     if (Ml->cbStruct != sizeof(*Ml))
1359     {
1360         WARN("invalid parameter: Ml->cbStruct = %d\n", Ml->cbStruct);
1361         return MMSYSERR_INVALPARAM;
1362     }
1363
1364     Ml->dwUser  = 0;
1365     Ml->fdwLine = MIXERLINE_LINEF_DISCONNECTED;
1366     switch (qf)
1367     {
1368     case MIXER_GETLINEINFOF_COMPONENTTYPE:
1369     {
1370         Ml->dwLineID = 0xFFFF;
1371         TRACE("Looking for componenttype %d/%x\n", Ml->dwComponentType, Ml->dwComponentType);
1372         for (idx = 0; idx < mmixer->chans; ++idx)
1373             if (mmixer->lines[idx].component == Ml->dwComponentType)
1374             {
1375                 Ml->dwLineID = idx;
1376                 break;
1377             }
1378         if (Ml->dwLineID == 0xFFFF)
1379             return MMSYSERR_KEYNOTFOUND;
1380         /* Now that we have lineid, fallback to lineid*/
1381     }
1382
1383     case MIXER_GETLINEINFOF_LINEID:
1384         if (Ml->dwLineID >= mmixer->chans)
1385             return MIXERR_INVALLINE;
1386
1387         TRACE("MIXER_GETLINEINFOF_LINEID %d\n", Ml->dwLineID);
1388         Ml->dwDestination = mmixer->lines[Ml->dwLineID].dst;
1389
1390         if (Ml->dwDestination != Ml->dwLineID)
1391         {
1392             Ml->dwSource = getsrcfromline(mmixer, Ml->dwLineID);
1393             Ml->cConnections = 1;
1394         }
1395         else
1396         {
1397             Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
1398             Ml->dwSource = 0xFFFFFFFF;
1399         }
1400         TRACE("Connections %d, source %d\n", Ml->cConnections, Ml->dwSource);
1401         break;
1402
1403     case MIXER_GETLINEINFOF_DESTINATION:
1404         if (Ml->dwDestination >= mmixer->dests)
1405         {
1406             WARN("dest %d out of bounds\n", Ml->dwDestination);
1407             return MIXERR_INVALLINE;
1408         }
1409
1410         Ml->dwLineID = Ml->dwDestination;
1411         Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
1412         Ml->dwSource = 0xFFFFFFFF;
1413         break;
1414
1415     case MIXER_GETLINEINFOF_SOURCE:
1416         if (Ml->dwDestination >= mmixer->dests)
1417         {
1418             WARN("dest %d for source out of bounds\n", Ml->dwDestination);
1419             return MIXERR_INVALLINE;
1420         }
1421
1422         if (Ml->dwSource >= getsrccntfromchan(mmixer, Ml->dwDestination))
1423         {
1424             WARN("src %d out of bounds\n", Ml->dwSource);
1425             return MIXERR_INVALLINE;
1426         }
1427
1428         Ml->dwLineID = getsrclinefromchan(mmixer, Ml->dwDestination, Ml->dwSource);
1429         Ml->cConnections = 1;
1430         break;
1431
1432     case MIXER_GETLINEINFOF_TARGETTYPE:
1433         FIXME("TODO: TARGETTYPE, stub\n");
1434         return MMSYSERR_INVALPARAM;
1435
1436     default:
1437         FIXME("Unknown query flag: %08lx\n", qf);
1438         return MMSYSERR_INVALPARAM;
1439     }
1440
1441     Ml->fdwLine &= ~MIXERLINE_LINEF_DISCONNECTED;
1442     Ml->fdwLine |= MIXERLINE_LINEF_ACTIVE;
1443     if (Ml->dwLineID >= mmixer->dests)
1444         Ml->fdwLine |= MIXERLINE_LINEF_SOURCE;
1445
1446     mline = &mmixer->lines[Ml->dwLineID];
1447     Ml->dwComponentType = mline->component;
1448     Ml->cChannels = mmixer->lines[Ml->dwLineID].chans;
1449     Ml->cControls = 0;
1450
1451     for (i=CONTROLSPERLINE*Ml->dwLineID;i<CONTROLSPERLINE*(Ml->dwLineID+1); ++i)
1452         if (mmixer->controls[i].enabled)
1453             ++(Ml->cControls);
1454
1455     lstrcpynW(Ml->szShortName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szShortName)/sizeof(WCHAR));
1456     lstrcpynW(Ml->szName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szName)/sizeof(WCHAR));
1457     if (mline->capt)
1458         Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
1459     else
1460         Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
1461     Ml->Target.dwDeviceID = 0xFFFFFFFF;
1462     Ml->Target.wMid = WINE_MIXER_MANUF_ID;
1463     Ml->Target.wPid = WINE_MIXER_PRODUCT_ID;
1464     Ml->Target.vDriverVersion = WINE_MIXER_VERSION;
1465     lstrcpynW(Ml->Target.szPname, mmixer->mixername, sizeof(Ml->Target.szPname)/sizeof(WCHAR));
1466     return MMSYSERR_NOERROR;
1467 }
1468
1469 /* Get the controls that belong to a certain line, either all or 1 */
1470 static DWORD MIX_GetLineControls(UINT wDevID, LPMIXERLINECONTROLSW mlc, DWORD_PTR flags)
1471 {
1472     mixer *mmixer = MIX_GetMix(wDevID);
1473     int i,j = 0;
1474     DWORD ct;
1475
1476     if (!mlc || mlc->cbStruct != sizeof(*mlc))
1477     {
1478         WARN("Invalid mlc %p, cbStruct: %d\n", mlc, (!mlc ? -1 : mlc->cbStruct));
1479         return MMSYSERR_INVALPARAM;
1480     }
1481
1482     if (mlc->cbmxctrl != sizeof(MIXERCONTROLW))
1483     {
1484         WARN("cbmxctrl %d\n", mlc->cbmxctrl);
1485         return MMSYSERR_INVALPARAM;
1486     }
1487
1488     if (!mmixer)
1489         return MMSYSERR_BADDEVICEID;
1490
1491     flags &= MIXER_GETLINECONTROLSF_QUERYMASK;
1492
1493     if (flags == MIXER_GETLINECONTROLSF_ONEBYID)
1494         mlc->dwLineID = mlc->u.dwControlID / CONTROLSPERLINE;
1495
1496     if (mlc->dwLineID >= mmixer->chans)
1497     {
1498         TRACE("Invalid dwLineID %d\n", mlc->dwLineID);
1499         return MIXERR_INVALLINE;
1500     }
1501
1502     switch (flags)
1503     {
1504     case MIXER_GETLINECONTROLSF_ALL:
1505        TRACE("line=%08x MIXER_GETLINECONTROLSF_ALL (%d)\n", mlc->dwLineID, mlc->cControls);
1506        for (i = 0; i < CONTROLSPERLINE; ++i)
1507            if (mmixer->controls[i+mlc->dwLineID * CONTROLSPERLINE].enabled)
1508            {
1509                memcpy(&mlc->pamxctrl[j], &mmixer->controls[i+mlc->dwLineID * CONTROLSPERLINE].c, sizeof(MIXERCONTROLW));
1510                TRACE("Added %s (%s)\n", debugstr_w(mlc->pamxctrl[j].szShortName), debugstr_w(mlc->pamxctrl[j].szName));
1511                ++j;
1512                if (j > mlc->cControls)
1513                {
1514                    WARN("invalid parameter\n");
1515                    return MMSYSERR_INVALPARAM;
1516                }
1517            }
1518
1519         if (!j || mlc->cControls > j)
1520         {
1521             WARN("invalid parameter\n");
1522             return MMSYSERR_INVALPARAM;
1523         }
1524         break;
1525     case MIXER_GETLINECONTROLSF_ONEBYID:
1526         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n", mlc->dwLineID, mlc->u.dwControlID);
1527
1528         if (!mmixer->controls[mlc->u.dwControlID].enabled)
1529            return MIXERR_INVALCONTROL;
1530
1531         mlc->pamxctrl[0] = mmixer->controls[mlc->u.dwControlID].c;
1532         break;
1533     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
1534         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", mlc->dwLineID, getControlType(mlc->u.dwControlType));
1535
1536         ct = mlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
1537         for (i = 0; i <= CONTROLSPERLINE; ++i)
1538         {
1539             const int ofs = i+mlc->dwLineID*CONTROLSPERLINE;
1540             if (i == CONTROLSPERLINE)
1541             {
1542                 WARN("invalid parameter: control %s not found\n", getControlType(mlc->u.dwControlType));
1543                 return MIXERR_INVALCONTROL;
1544             }
1545             if (mmixer->controls[ofs].enabled && (mmixer->controls[ofs].c.dwControlType & MIXERCONTROL_CT_CLASS_MASK) == ct)
1546             {
1547                 mlc->pamxctrl[0] = mmixer->controls[ofs].c;
1548                 break;
1549             }
1550         }
1551     break;
1552     default:
1553         FIXME("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
1554         return MMSYSERR_INVALPARAM;
1555     }
1556
1557     return MMSYSERR_NOERROR;
1558 }
1559
1560 #endif /*HAVE_ALSA*/
1561
1562 /**************************************************************************
1563  *                        mxdMessage (WINEALSA.3)
1564  */
1565 DWORD WINAPI ALSA_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1566                              DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1567 {
1568 #ifdef HAVE_ALSA
1569     DWORD ret;
1570     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
1571           dwUser, dwParam1, dwParam2);
1572
1573     switch (wMsg)
1574     {
1575     case DRVM_INIT: ALSA_MixerInit(); ret = MMSYSERR_NOERROR; break;
1576     case DRVM_EXIT: ALSA_MixerExit(); ret = MMSYSERR_NOERROR; break;
1577     /* All taken care of by driver initialisation */
1578     /* Unimplemented, and not needed */
1579     case DRVM_ENABLE:
1580     case DRVM_DISABLE:
1581         ret = MMSYSERR_NOERROR; break;
1582
1583     case MXDM_OPEN:
1584         ret = MIX_Open(wDevID, (LPMIXEROPENDESC) dwParam1, dwParam2); break;
1585
1586     case MXDM_CLOSE:
1587         ret = MIX_Close(wDevID); break;
1588
1589     case MXDM_GETDEVCAPS:
1590         ret = MIX_GetDevCaps(wDevID, (LPMIXERCAPS2W)dwParam1, dwParam2); break;
1591
1592     case MXDM_GETLINEINFO:
1593         ret = MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2); break;
1594
1595     case MXDM_GETLINECONTROLS:
1596         ret = MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2); break;
1597
1598     case MXDM_GETCONTROLDETAILS:
1599         ret = MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); break;
1600
1601     case MXDM_SETCONTROLDETAILS:
1602         ret = MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); break;
1603
1604     case MXDM_GETNUMDEVS:
1605         ret = cards; break;
1606
1607     default:
1608         WARN("unknown message %s!\n", getMessage(wMsg));
1609         return MMSYSERR_NOTSUPPORTED;
1610     }
1611
1612     TRACE("Returning %08X\n", ret);
1613     return ret;
1614 #else /*HAVE_ALSA*/
1615     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1616
1617     return MMSYSERR_NOTENABLED;
1618 #endif /*HAVE_ALSA*/
1619 }