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