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