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