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