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