winealsa: Implement GetLineControls in mixer.
[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 "winerror.h"
46 #include "winuser.h"
47 #include "winnls.h"
48 #include "mmddk.h"
49 #include "mmsystem.h"
50 #include "alsa.h"
51 #include "wine/unicode.h"
52 #include "wine/debug.h"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(mixer);
55
56 #ifdef HAVE_ALSA
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 wether 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     static char str[64];
82 #define MSG_TO_STR(x) case x: return #x;
83     switch (uMsg){
84     MSG_TO_STR(DRVM_INIT);
85     MSG_TO_STR(DRVM_EXIT);
86     MSG_TO_STR(DRVM_ENABLE);
87     MSG_TO_STR(DRVM_DISABLE);
88     MSG_TO_STR(MXDM_GETDEVCAPS);
89     MSG_TO_STR(MXDM_GETLINEINFO);
90     MSG_TO_STR(MXDM_GETNUMDEVS);
91     MSG_TO_STR(MXDM_OPEN);
92     MSG_TO_STR(MXDM_CLOSE);
93     MSG_TO_STR(MXDM_GETLINECONTROLS);
94     MSG_TO_STR(MXDM_GETCONTROLDETAILS);
95     MSG_TO_STR(MXDM_SETCONTROLDETAILS);
96     default: break;
97     }
98 #undef MSG_TO_STR
99     sprintf(str, "UNKNOWN(%08x)", uMsg);
100     return str;
101 }
102
103 static const char * getControlType(DWORD dwControlType)
104 {
105 #define TYPE_TO_STR(x) case x: return #x;
106     switch (dwControlType) {
107     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
108     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
109     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
110     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
111     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
112     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
113     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
114     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
115     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
116     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
117     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
118     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
119     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
120     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
121     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
122     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
123     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
124     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
125     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
126     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
127     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
128     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
129     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
130     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
131     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
132     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
133     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
134     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
135     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
136     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
137     TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
138     }
139 #undef TYPE_TO_STR
140     return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType);
141 }
142
143 /* A simple declaration of a line control
144  * These are each of the channels that show up
145  */
146 typedef struct line {
147     /* Name we present to outside world */
148     WCHAR name[MAXPNAMELEN];
149
150     DWORD component;
151     DWORD dst;
152     DWORD capt;
153     DWORD chans;
154     snd_mixer_elem_t *elem;
155 } line;
156
157 /* A control structure, with toggle enabled switch
158  * Control structures control volume, muted, which capture source
159  */
160 typedef struct control {
161     BOOL enabled;
162     MIXERCONTROLW c;
163 } control;
164
165 /* Mixer device */
166 typedef struct mixer
167 {
168     snd_mixer_t *mix;
169     WCHAR mixername[MAXPNAMELEN];
170
171     int chans, dests;
172     LPDRVCALLBACK callback;
173     DWORD_PTR callbackpriv;
174     HDRVR hmx;
175
176     line *lines;
177     control *controls;
178 } mixer;
179
180 #define MAX_MIXERS 32
181 #define CONTROLSPERLINE 3
182 #define OFS_MUTE 2
183 #define OFS_MUX 1
184
185 static int cards = 0;
186 static mixer mixdev[MAX_MIXERS];
187 static HANDLE thread;
188 static int elem_callback(snd_mixer_elem_t *elem, unsigned int mask);
189 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam);
190 static CRITICAL_SECTION elem_crst;
191 static int msg_pipe[2];
192 static LONG refcnt;
193
194 /* found channel names in alsa lib, alsa api doesn't have another way for this
195  * map name -> componenttype, worst case we get a wrong componenttype which is
196  * mostly harmless
197  */
198
199 static const struct mixerlinetype {
200     const char *name;  DWORD cmpt;
201 } converttable[] = {
202     { "Master",     MIXERLINE_COMPONENTTYPE_DST_SPEAKERS,    },
203     { "Capture",    MIXERLINE_COMPONENTTYPE_DST_WAVEIN,      },
204     { "PCM",        MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT,     },
205     { "PC Speaker", MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER,   },
206     { "Synth",      MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER, },
207     { "Headphone",  MIXERLINE_COMPONENTTYPE_DST_HEADPHONES,  },
208     { "Mic",        MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE,  },
209     { "Aux",        MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED,   },
210     { "CD",         MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC, },
211     { "Line",       MIXERLINE_COMPONENTTYPE_SRC_LINE,        },
212     { "Phone",      MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE,   },
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          !snd_mixer_selem_has_capture_switch(elem)))
238         blisted = 1;
239
240     TRACE("%s: %x\n", name, blisted);
241     return blisted;
242 }
243
244 static void fillcontrols(mixer *mmixer)
245 {
246     int id;
247     for (id = 0; id < mmixer->chans; ++id)
248     {
249         line *mline = &mmixer->lines[id];
250         int ofs = CONTROLSPERLINE * id;
251         int x;
252         long min, max;
253
254         if (mline->capt && snd_mixer_selem_has_capture_volume(mline->elem))
255             snd_mixer_selem_get_capture_volume_range(mline->elem, &min, &max);
256         else
257             snd_mixer_selem_get_playback_volume_range(mline->elem, &min, &max);
258
259         /* (!snd_mixer_selem_has_playback_volume(elem) || snd_mixer_selem_has_capture_volume(elem)) */
260         /* Volume, always enabled by definition of blacklisted channels */
261         mmixer->controls[ofs].enabled = 1;
262         mmixer->controls[ofs].c.cbStruct = sizeof(mmixer->controls[ofs].c);
263         mmixer->controls[ofs].c.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
264         mmixer->controls[ofs].c.dwControlID = ofs;
265         mmixer->controls[ofs].c.Bounds.s1.dwMinimum = 0;
266         mmixer->controls[ofs].c.Bounds.s1.dwMaximum = 65535;
267         mmixer->controls[ofs].c.Metrics.cSteps = 65536/(max-min);
268
269         if ((id == 1 && snd_mixer_selem_has_capture_switch(mline->elem)) ||
270             (!mline->capt && snd_mixer_selem_has_playback_switch(mline->elem)))
271         { /* MUTE button optional, main capture channel should have one too */
272             mmixer->controls[ofs+OFS_MUTE].enabled = 1;
273             mmixer->controls[ofs+OFS_MUTE].c.cbStruct = sizeof(mmixer->controls[ofs].c);
274             mmixer->controls[ofs+OFS_MUTE].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
275             mmixer->controls[ofs+OFS_MUTE].c.dwControlID = ofs+OFS_MUTE;
276             mmixer->controls[ofs+OFS_MUTE].c.Bounds.s1.dwMaximum = 1;
277         }
278
279         if (mline->capt && snd_mixer_selem_has_capture_switch_exclusive(mline->elem))
280             mmixer->controls[CONTROLSPERLINE+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
281
282         if (id == 1)
283         { /* Capture select, in case cMultipleItems is 0, it means capture is disabled anyway */
284             mmixer->controls[ofs+OFS_MUX].enabled = 1;
285             mmixer->controls[ofs+OFS_MUX].c.cbStruct = sizeof(mmixer->controls[ofs].c);
286             mmixer->controls[ofs+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER;
287             mmixer->controls[ofs+OFS_MUX].c.dwControlID = ofs+OFS_MUX;
288             mmixer->controls[ofs+OFS_MUX].c.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
289
290             for (x = 0; x<mmixer->chans; ++x)
291                 if (x != id && mmixer->lines[x].dst == id)
292                     ++(mmixer->controls[ofs+OFS_MUX].c.cMultipleItems);
293             if (!mmixer->controls[ofs+OFS_MUX].c.cMultipleItems)
294                 mmixer->controls[ofs+OFS_MUX].enabled = 0;
295
296             mmixer->controls[ofs+OFS_MUX].c.Bounds.s1.dwMaximum = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems - 1;
297             mmixer->controls[ofs+OFS_MUX].c.Metrics.cSteps = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems;
298         }
299         for (x=0; x<CONTROLSPERLINE; ++x)
300         {
301             lstrcpynW(mmixer->controls[ofs+x].c.szShortName, mline->name, sizeof(mmixer->controls[ofs+x].c.szShortName)/sizeof(WCHAR));
302             lstrcpynW(mmixer->controls[ofs+x].c.szName, mline->name, sizeof(mmixer->controls[ofs+x].c.szName)/sizeof(WCHAR));
303         }
304     }
305 }
306
307 /* get amount of channels for elem */
308 /* Officially we should keep capture/playback seperated,
309  * but that's not going to work in the alsa api */
310 static int chans(mixer *mmixer, snd_mixer_elem_t * elem, DWORD capt)
311 {
312     int ret=0, chn;
313
314     if (capt && snd_mixer_selem_has_capture_volume(elem)) {
315         for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
316             if (snd_mixer_selem_has_capture_channel(elem, chn))
317                 ++ret;
318     } else {
319         for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
320             if (snd_mixer_selem_has_playback_channel(elem, chn))
321                 ++ret;
322     }
323     if (!ret)
324         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"));
325     return ret;
326 }
327
328 static void ALSA_MixerInit(void)
329 {
330     int x, mixnum = 0;
331
332     for (x = 0; x < MAX_MIXERS; ++x)
333     {
334         int card, err, y;
335         char cardind[6], cardname[10];
336         BOOL hascapt=0, hasmast=0;
337         line *mline;
338
339         snd_ctl_t *ctl;
340         snd_mixer_elem_t *elem, *mastelem = NULL, *captelem = NULL;
341         snd_ctl_card_info_t *info = NULL;
342         snd_ctl_card_info_alloca(&info);
343         mixdev[mixnum].lines = NULL;
344         mixdev[mixnum].callback = 0;
345
346         snprintf(cardind, sizeof(cardind), "%d", x);
347         card = snd_card_get_index(cardind);
348         if (card < 0)
349             continue;
350         snprintf(cardname, sizeof(cardname), "hw:%d", card);
351
352         err = snd_ctl_open(&ctl, cardname, 0);
353         if (err < 0)
354         {
355             WARN("Cannot open card: %s\n", snd_strerror(err));
356             continue;
357         }
358
359         err = snd_ctl_card_info(ctl, info);
360         if (err < 0)
361         {
362             WARN("Cannot get card info: %s\n", snd_strerror(err));
363             snd_ctl_close(ctl);
364             continue;
365         }
366
367         MultiByteToWideChar(CP_UNIXCP, 0, snd_ctl_card_info_get_name(info), -1, mixdev[mixnum].mixername, sizeof(mixdev[mixnum].mixername)/sizeof(WCHAR));
368         snd_ctl_close(ctl);
369
370         err = snd_mixer_open(&mixdev[mixnum].mix,0);
371         if (err < 0)
372         {
373             WARN("Error occured opening mixer: %s\n", snd_strerror(err));
374             continue;
375         }
376
377         err = snd_mixer_attach(mixdev[mixnum].mix, cardname);
378         if (err < 0)
379             goto eclose;
380
381         err = snd_mixer_selem_register(mixdev[mixnum].mix, NULL, NULL);
382         if (err < 0)
383             goto eclose;
384
385         err = snd_mixer_load(mixdev[mixnum].mix);
386         if (err < 0)
387             goto eclose;
388
389         mixdev[mixnum].chans = 0;
390         mixdev[mixnum].dests = 1; /* Master, Capture will be enabled if needed */
391
392         for (elem = snd_mixer_first_elem(mixdev[mixnum].mix); elem; elem = snd_mixer_elem_next(elem))
393             if (!strcasecmp(snd_mixer_selem_get_name(elem), "Master"))
394             {
395                 mastelem = elem;
396                 ++hasmast;
397             }
398             else if (!strcasecmp(snd_mixer_selem_get_name(elem), "Capture"))
399             {
400                 captelem = elem;
401                 ++hascapt;
402             }
403             else if (!blacklisted(elem))
404             {
405                 if (snd_mixer_selem_has_capture_switch(elem))
406                 {
407                     ++mixdev[mixnum].chans;
408                     mixdev[mixnum].dests = 2;
409                 }
410                 if (snd_mixer_selem_has_playback_volume(elem))
411                     ++mixdev[mixnum].chans;
412             }
413
414         /* If there is only 'Capture' and 'Master', this device is not worth it */
415         if (!mixdev[mixnum].chans)
416         {
417             WARN("No channels found, skipping device!\n");
418             snd_mixer_close(mixdev[mixnum].mix);
419             continue;
420         }
421
422         /* If there are no 'Capture' and 'Master', something is wrong */
423         if (hasmast != 1 || hascapt != 1)
424         {
425             if (hasmast != 1)
426                 FIXME("Should have found 1 channel for 'Master', but instead found %d\n", hasmast);
427             if (hascapt != 1)
428                 FIXME("Should have found 1 channel for 'Capture', but instead found %d\n", hascapt);
429             goto eclose;
430         }
431
432         mixdev[mixnum].chans += 2; /* Capture/Master */
433         mixdev[mixnum].lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(line) * mixdev[mixnum].chans);
434         mixdev[mixnum].controls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(control) * CONTROLSPERLINE*mixdev[mixnum].chans);
435         err = -ENOMEM;
436         if (!mixdev[mixnum].lines || !mixdev[mixnum].controls)
437             goto eclose;
438
439         /* Master control */
440         mline = &mixdev[mixnum].lines[0];
441         MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(mastelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
442         mline->component = getcomponenttype("Master");
443         mline->dst = 0;
444         mline->capt = 0;
445         mline->elem = mastelem;
446         mline->chans = chans(&mixdev[mixnum], mastelem, 0);
447
448         /* Capture control
449          * Note: since mmixer->dests = 1, it means only playback control is visible
450          * This makes sense, because if there are no capture sources capture control
451          * can't do anything and should be invisible */
452
453         mline++;
454         MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(captelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
455         mline->component = getcomponenttype("Capture");
456         mline->dst = 1;
457         mline->capt = 1;
458         mline->elem = captelem;
459         mline->chans = chans(&mixdev[mixnum], captelem, 1);
460
461         snd_mixer_elem_set_callback(mastelem, &elem_callback);
462         snd_mixer_elem_set_callback_private(mastelem, &mixdev[mixnum]);
463
464         if (mixdev[mixnum].dests == 2)
465         {
466             snd_mixer_elem_set_callback(captelem, &elem_callback);
467             snd_mixer_elem_set_callback_private(captelem, &mixdev[mixnum]);
468         }
469
470         y=2;
471         for (elem = snd_mixer_first_elem(mixdev[mixnum].mix); elem; elem = snd_mixer_elem_next(elem))
472             if (elem != mastelem && elem != captelem && !blacklisted(elem))
473             {
474                 const char * name = snd_mixer_selem_get_name(elem);
475                 DWORD comp = getcomponenttype(name);
476                 snd_mixer_elem_set_callback(elem, &elem_callback);
477                 snd_mixer_elem_set_callback_private(elem, &mixdev[mixnum]);
478
479                 if (snd_mixer_selem_has_playback_volume(elem))
480                 {
481                     mline = &mixdev[mixnum].lines[y++];
482                     mline->component = comp;
483                     MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
484                     mline->capt = mline->dst = 0;
485                     mline->elem = elem;
486                     mline->chans = chans(&mixdev[mixnum], elem, 0);
487                 }
488                 if (snd_mixer_selem_has_capture_switch(elem))
489                 {
490                     mline = &mixdev[mixnum].lines[y++];
491                     mline->component = comp;
492                     MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
493                     mline->capt = mline->dst = 1;
494                     mline->elem = elem;
495                     mline->chans = chans(&mixdev[mixnum], elem, 1);
496                 }
497             }
498
499         fillcontrols(&mixdev[mixnum]);
500
501         TRACE("%s: Amount of controls: %i/%i, name: %s\n", cardname, mixdev[mixnum].dests, mixdev[mixnum].chans, debugstr_w(mixdev[mixnum].mixername));
502         mixnum++;
503         continue;
504
505         eclose:
506         WARN("Error occured initialising mixer: %s\n", snd_strerror(err));
507         if (mixdev[mixnum].lines)
508             HeapFree(GetProcessHeap(), 0, mixdev[mixnum].lines);
509         if (mixdev[mixnum].controls)
510             HeapFree(GetProcessHeap(), 0, mixdev[mixnum].controls);
511         snd_mixer_close(mixdev[mixnum].mix);
512     }
513     cards = mixnum;
514
515     InitializeCriticalSection(&elem_crst);
516     elem_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MIXER.elem_crst");
517     TRACE("\n");
518 }
519
520 static void ALSA_MixerExit(void)
521 {
522     int x;
523
524     if (refcnt)
525     {
526         WARN("Callback thread still alive, terminating uncleanly, refcnt: %d\n", refcnt);
527         /* Least we can do is making sure we're not in 'foreign' code */
528         EnterCriticalSection(&elem_crst);
529         TerminateThread(thread, 1);
530         refcnt = 0;
531     }
532
533     TRACE("Cleaning up\n");
534
535     elem_crst.DebugInfo->Spare[0] = 0;
536     DeleteCriticalSection(&elem_crst);
537     for (x = 0; x < cards; ++x)
538     {
539         snd_mixer_close(mixdev[x].mix);
540         HeapFree(GetProcessHeap(), 0, mixdev[x].lines);
541         HeapFree(GetProcessHeap(), 0, mixdev[x].controls);
542     }
543     cards = 0;
544 }
545
546 static mixer* MIX_GetMix(UINT wDevID)
547 {
548     mixer *mmixer;
549
550     if (wDevID < 0 || wDevID >= cards)
551     {
552         WARN("Invalid mixer id: %d\n", wDevID);
553         return NULL;
554     }
555
556     mmixer = &mixdev[wDevID];
557     return mmixer;
558 }
559
560 /* Since alsa doesn't tell what exactly changed, just assume all affected controls changed */
561 static int elem_callback(snd_mixer_elem_t *elem, unsigned int type)
562 {
563     mixer *mmixer = snd_mixer_elem_get_callback_private(elem);
564     int x;
565     BOOL captchanged = 0;
566
567     if (type != SND_CTL_EVENT_MASK_VALUE)
568         return 0;
569
570     assert(mmixer);
571
572     EnterCriticalSection(&elem_crst);
573
574     if (!mmixer->callback)
575         goto out;
576
577     for (x=0; x<mmixer->chans; ++x)
578     {
579         const int ofs = CONTROLSPERLINE*x;
580         if (elem != mmixer->lines[x].elem)
581             continue;
582
583         if (mmixer->lines[x].capt)
584             ++captchanged;
585
586         TRACE("Found changed control %s\n", debugstr_w(mmixer->lines[x].name));
587         mmixer->callback(mmixer->hmx, MM_MIXM_LINE_CHANGE, mmixer->callbackpriv, x, 0);
588         mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs, 0);
589
590         if (mmixer->controls[ofs+OFS_MUTE].enabled)
591             mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs+OFS_MUTE, 0);
592     }
593     if (captchanged)
594         mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, CONTROLSPERLINE+OFS_MUX, 0);
595
596     out:
597     LeaveCriticalSection(&elem_crst);
598
599     return 0;
600 }
601
602 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam)
603 {
604     struct pollfd *pfds = NULL;
605     int x, y, err, mcnt, count = 1;
606
607     TRACE("%p\n", lParam);
608
609     for (x = 0; x < cards; ++x)
610         count += snd_mixer_poll_descriptors_count(mixdev[x].mix);
611
612     TRACE("Counted %d descriptors\n", count);
613     pfds = HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct pollfd));
614
615     if (!pfds)
616     {
617         WARN("Out of memory\n");
618         goto die;
619     }
620
621     pfds[0].fd = msg_pipe[0];
622     pfds[0].events = POLLIN;
623
624     y = 1;
625     for (x = 0; x < cards; ++x)
626         y += snd_mixer_poll_descriptors(mixdev[x].mix, &pfds[y], count - y);
627
628     while ((err = poll(pfds, (unsigned int) count, -1)) >= 0 || errno == EINTR || errno == EAGAIN)
629     {
630         if (pfds[0].revents & POLLIN)
631             break;
632
633         mcnt = 1;
634         for (x = y = 0; x < cards; ++x)
635         {
636             int j, max = snd_mixer_poll_descriptors_count(mixdev[x].mix);
637             for (j = 0; j < max; ++j)
638                 if (pfds[mcnt+j].revents)
639                 {
640                     y += snd_mixer_handle_events(mixdev[x].mix);
641                     break;
642                 }
643             mcnt += max;
644         }
645         if (y)
646             TRACE("Handled %d events\n", y);
647     }
648
649     die:
650     TRACE("Shutting down\n");
651     if (pfds)
652         HeapFree(GetProcessHeap(), 0, pfds);
653
654     y = read(msg_pipe[0], &x, sizeof(x));
655     close(msg_pipe[1]);
656     close(msg_pipe[0]);
657     return 0;
658 }
659
660 static DWORD MIX_Open(UINT wDevID, LPMIXEROPENDESC desc, DWORD_PTR flags)
661 {
662     mixer *mmixer = MIX_GetMix(wDevID);
663     if (!mmixer)
664         return MMSYSERR_BADDEVICEID;
665
666     flags &= CALLBACK_TYPEMASK;
667     switch (flags)
668     {
669     case CALLBACK_NULL:
670         goto done;
671
672     case CALLBACK_FUNCTION:
673         break;
674
675     default:
676         FIXME("Unhandled callback type: %08lx\n", flags & CALLBACK_TYPEMASK);
677         return MIXERR_INVALVALUE;
678     }
679
680     mmixer->callback = (LPDRVCALLBACK)desc->dwCallback;
681     mmixer->callbackpriv = desc->dwInstance;
682     mmixer->hmx = (HDRVR)desc->hmx;
683
684     done:
685     if (InterlockedIncrement(&refcnt) == 1)
686     {
687         if (pipe(msg_pipe) >= 0)
688         {
689             thread = CreateThread(NULL, 0, ALSA_MixerPollThread, NULL, 0, NULL);
690             if (!thread)
691             {
692                 close(msg_pipe[0]);
693                 close(msg_pipe[1]);
694                 msg_pipe[0] = msg_pipe[1] = -1;
695             }
696         }
697         else
698             msg_pipe[0] = msg_pipe[1] = -1;
699     }
700
701     return MMSYSERR_NOERROR;
702 }
703
704 static DWORD MIX_Close(UINT wDevID)
705 {
706     int x;
707     mixer *mmixer = MIX_GetMix(wDevID);
708     if (!mmixer)
709         return MMSYSERR_BADDEVICEID;
710
711     EnterCriticalSection(&elem_crst);
712     mmixer->callback = 0;
713     LeaveCriticalSection(&elem_crst);
714
715     if (!InterlockedDecrement(&refcnt))
716     {
717         if (write(msg_pipe[1], &x, sizeof(x)) > 0)
718         {
719             TRACE("Shutting down thread...\n");
720             WaitForSingleObject(thread, INFINITE);
721             TRACE("Done\n");
722         }
723     }
724
725     return MMSYSERR_NOERROR;
726 }
727
728 static DWORD MIX_GetDevCaps(UINT wDevID, LPMIXERCAPS2W caps, DWORD_PTR parm2)
729 {
730     mixer *mmixer = MIX_GetMix(wDevID);
731     MIXERCAPS2W capsW;
732
733     if (!caps)
734         return MMSYSERR_INVALPARAM;
735
736     if (!mmixer)
737         return MMSYSERR_BADDEVICEID;
738
739     memset(&capsW, 0, sizeof(MIXERCAPS2W));
740
741     capsW.wMid = WINE_MIXER_MANUF_ID;
742     capsW.wPid = WINE_MIXER_PRODUCT_ID;
743     capsW.vDriverVersion = WINE_MIXER_VERSION;
744
745     lstrcpynW(capsW.szPname, mmixer->mixername, sizeof(capsW.szPname)/sizeof(WCHAR));
746     capsW.cDestinations = mmixer->dests;
747     memcpy(caps, &capsW, min(parm2, sizeof(capsW)));
748     return MMSYSERR_NOERROR;
749 }
750
751
752 /* get amount of sources for dest */
753 static int getsrccntfromchan(mixer *mmixer, int dad)
754 {
755     int i, j=0;
756
757     for (i=0; i<mmixer->chans; ++i)
758         if (i != dad && mmixer->lines[i].dst == dad)
759         {
760             ++j;
761         }
762     if (!j)
763         FIXME("No src found for %i (%s)?\n", dad, debugstr_w(mmixer->lines[dad].name));
764     return j;
765 }
766
767 /* find lineid for source 'num' with dest 'dad' */
768 static int getsrclinefromchan(mixer *mmixer, int dad, int num)
769 {
770     int i, j=0;
771     for (i=0; i<mmixer->chans; ++i)
772         if (i != dad && mmixer->lines[i].dst == dad)
773         {
774             if (num == j)
775                 return i;
776             ++j;
777         }
778     WARN("No src found for src %i from dest %i\n", num, dad);
779     return 0;
780 }
781
782 /* get the source number belonging to line */
783 static int getsrcfromline(mixer *mmixer, int line)
784 {
785     int i, j=0, dad = mmixer->lines[line].dst;
786
787     for (i=0; i<mmixer->chans; ++i)
788         if (i != dad && mmixer->lines[i].dst == dad)
789         {
790             if (line == i)
791                 return j;
792             ++j;
793         }
794     WARN("No src found for line %i with dad %i\n", line, dad);
795     return 0;
796 }
797
798 /* Here we give info over the source/dest line given by dwSource+dwDest or dwDest, respectively
799  * It is also possible that a line is found by componenttype or target type, latter is not implemented yet
800  * Most important values returned in struct:
801  * dwLineID
802  * sz(Short)Name
803  * line control count
804  * amount of channels
805  */
806 static DWORD MIX_GetLineInfo(UINT wDevID, LPMIXERLINEW Ml, DWORD_PTR flags)
807 {
808     DWORD_PTR qf = flags & MIXER_GETLINEINFOF_QUERYMASK;
809     mixer *mmixer = MIX_GetMix(wDevID);
810     line *mline;
811     int idx, i;
812
813     if (!Ml)
814     {
815         WARN("No Ml\n");
816         return MMSYSERR_INVALPARAM;
817     }
818
819     if (!mmixer)
820     {
821         WARN("Device %u not found\n", wDevID);
822         return MMSYSERR_BADDEVICEID;
823     }
824
825     if (Ml->cbStruct != sizeof(*Ml))
826     {
827         WARN("invalid parameter: Ml->cbStruct = %d != %d\n", Ml->cbStruct, sizeof(*Ml));
828         return MMSYSERR_INVALPARAM;
829     }
830
831     Ml->fdwLine = MIXERLINE_LINEF_ACTIVE;
832     Ml->dwUser  = 0;
833
834     switch (qf)
835     {
836     case MIXER_GETLINEINFOF_COMPONENTTYPE:
837     {
838         Ml->dwLineID = 0xFFFF;
839         for (idx = 0; idx < mmixer->chans; ++idx)
840             if (mmixer->lines[idx].component == Ml->dwComponentType)
841             {
842                 Ml->dwLineID = idx;
843                 break;
844             }
845         if (Ml->dwLineID == 0xFFFF)
846             return MMSYSERR_KEYNOTFOUND;
847         /* Now that we have lineid, fallback to lineid*/
848     }
849
850     case MIXER_GETLINEINFOF_LINEID:
851         if (Ml->dwLineID < 0 || Ml->dwLineID >= mmixer->chans)
852             return MIXERR_INVALLINE;
853
854         TRACE("MIXER_GETLINEINFOF_LINEID %d\n", Ml->dwLineID);
855         Ml->dwDestination = mmixer->lines[Ml->dwLineID].dst;
856
857         if (Ml->dwDestination != Ml->dwLineID)
858         {
859             Ml->dwSource = getsrcfromline(mmixer, Ml->dwLineID);
860             Ml->cConnections = 1;
861         }
862         else
863         {
864             Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
865             Ml->dwSource = 0xFFFFFFFF;
866         }
867         TRACE("Connections %d, source %d\n", Ml->cConnections, Ml->dwSource);
868         break;
869
870     case MIXER_GETLINEINFOF_DESTINATION:
871         if (Ml->dwDestination < 0 || Ml->dwDestination >= mmixer->dests)
872         {
873             WARN("dest %d out of bounds\n", Ml->dwDestination);
874             return MIXERR_INVALLINE;
875         }
876
877         Ml->dwLineID = Ml->dwDestination;
878         Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
879         Ml->dwSource = 0xFFFFFFFF;
880         break;
881
882     case MIXER_GETLINEINFOF_SOURCE:
883         if (Ml->dwDestination < 0 || Ml->dwDestination >= mmixer->dests)
884         {
885             WARN("dest %d for source out of bounds\n", Ml->dwDestination);
886             return MIXERR_INVALLINE;
887         }
888
889         if (Ml->dwSource < 0 || Ml->dwSource >= getsrccntfromchan(mmixer, Ml->dwDestination))
890         {
891             WARN("src %d out of bounds\n", Ml->dwSource);
892             return MIXERR_INVALLINE;
893         }
894
895         Ml->dwLineID = getsrclinefromchan(mmixer, Ml->dwDestination, Ml->dwSource);
896         Ml->cConnections = 1;
897         break;
898
899     case MIXER_GETLINEINFOF_TARGETTYPE:
900         FIXME("TODO: TARGETTYPE, stub\n");
901         return MMSYSERR_INVALPARAM;
902
903     default:
904         FIXME("Unknown query flag: %08lx\n", qf);
905         return MMSYSERR_INVALPARAM;
906     }
907
908     if (Ml->dwLineID >= mmixer->dests)
909         Ml->fdwLine |= MIXERLINE_LINEF_SOURCE;
910
911     mline = &mmixer->lines[Ml->dwLineID];
912     Ml->dwComponentType = mline->component;
913     Ml->cChannels = mmixer->lines[Ml->dwLineID].chans;
914     Ml->cControls = 0;
915
916     for (i=CONTROLSPERLINE*Ml->dwLineID;i<CONTROLSPERLINE*(Ml->dwLineID+1); ++i)
917         if (mmixer->controls[i].enabled)
918             ++(Ml->cControls);
919
920     lstrcpynW(Ml->szShortName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szShortName)/sizeof(WCHAR));
921     lstrcpynW(Ml->szName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szName)/sizeof(WCHAR));
922     if (mline->capt)
923         Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
924     else
925         Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
926     Ml->Target.dwDeviceID = 0xFFFFFFFF;
927     Ml->Target.wMid = WINE_MIXER_MANUF_ID;
928     Ml->Target.wPid = WINE_MIXER_PRODUCT_ID;
929     Ml->Target.vDriverVersion = WINE_MIXER_VERSION;
930     lstrcpynW(Ml->Target.szPname, mmixer->mixername, sizeof(Ml->Target.szPname)/sizeof(WCHAR));
931     return MMSYSERR_NOERROR;
932 }
933
934 /* Get the controls that belong to a certain line, either all or 1 */
935 static DWORD MIX_GetLineControls(UINT wDevID, LPMIXERLINECONTROLSW mlc, DWORD_PTR flags)
936 {
937     mixer *mmixer = MIX_GetMix(wDevID);
938     int i,j = 0;
939     DWORD ct;
940
941     if (!mlc || mlc->cbStruct != sizeof(*mlc))
942     {
943         WARN("Invalid mlc %p, cbStruct: %d\n", mlc, (!mlc ? -1 : mlc->cbStruct));
944         return MMSYSERR_INVALPARAM;
945     }
946
947     if (mlc->cbmxctrl != sizeof(MIXERCONTROLW))
948     {
949         WARN("cbmxctrl %d\n", mlc->cbmxctrl);
950         return MMSYSERR_INVALPARAM;
951     }
952
953     if (!mmixer)
954         return MMSYSERR_BADDEVICEID;
955
956     flags &= MIXER_GETLINECONTROLSF_QUERYMASK;
957
958     if (flags == MIXER_GETLINECONTROLSF_ONEBYID)
959         mlc->dwLineID = mlc->u.dwControlID / CONTROLSPERLINE;
960
961     if (mlc->dwLineID < 0 || mlc->dwLineID >= mmixer->chans)
962     {
963         TRACE("Invalid dwLineID %d\n", mlc->dwLineID);
964         return MIXERR_INVALLINE;
965     }
966
967     switch (flags)
968     {
969     case MIXER_GETLINECONTROLSF_ALL:
970        TRACE("line=%08x MIXER_GETLINECONTROLSF_ALL (%d)\n", mlc->dwLineID, mlc->cControls);
971        for (i = 0; i < CONTROLSPERLINE; ++i)
972            if (mmixer->controls[i+mlc->dwLineID * CONTROLSPERLINE].enabled)
973            {
974                memcpy(&mlc->pamxctrl[j], &mmixer->controls[i+mlc->dwLineID * CONTROLSPERLINE].c, sizeof(MIXERCONTROLW));
975                TRACE("Added %s (%s)\n", debugstr_w(mlc->pamxctrl[j].szShortName), debugstr_w(mlc->pamxctrl[j].szName));
976                ++j;
977                if (j > mlc->cControls)
978                {
979                    WARN("invalid parameter\n");
980                    return MMSYSERR_INVALPARAM;
981                }
982            }
983
984         if (!j || mlc->cControls > j)
985         {
986             WARN("invalid parameter\n");
987             return MMSYSERR_INVALPARAM;
988         }
989         break;
990     case MIXER_GETLINECONTROLSF_ONEBYID:
991         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n", mlc->dwLineID, mlc->u.dwControlID);
992
993         if (!mmixer->controls[mlc->u.dwControlID].enabled)
994            return MIXERR_INVALCONTROL;
995
996         mlc->pamxctrl[0] = mmixer->controls[mlc->u.dwControlID].c;
997         break;
998     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
999         TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", mlc->dwLineID, getControlType(mlc->u.dwControlType));
1000
1001         ct = mlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
1002         for (i = 0; i <= CONTROLSPERLINE; ++i)
1003         {
1004             const int ofs = i+mlc->dwLineID*CONTROLSPERLINE;
1005             if (i == CONTROLSPERLINE)
1006             {
1007                 WARN("invalid parameter: control %s not found\n", getControlType(mlc->u.dwControlType));
1008                 return MIXERR_INVALCONTROL;
1009             }
1010             if (mmixer->controls[ofs].enabled && (mmixer->controls[ofs].c.dwControlType & MIXERCONTROL_CT_CLASS_MASK) == ct)
1011             {
1012                 mlc->pamxctrl[0] = mmixer->controls[ofs].c;
1013                 break;
1014             }
1015         }
1016     break;
1017     default:
1018         FIXME("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
1019         return MMSYSERR_INVALPARAM;
1020     }
1021
1022     return MMSYSERR_NOERROR;
1023 }
1024
1025 #endif /*HAVE_ALSA*/
1026
1027 /**************************************************************************
1028  *                        mxdMessage (WINEALSA.3)
1029  */
1030 DWORD WINAPI ALSA_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1031                              DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1032 {
1033 #ifdef HAVE_ALSA
1034     DWORD ret;
1035     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
1036           dwUser, dwParam1, dwParam2);
1037
1038     switch (wMsg)
1039     {
1040     case DRVM_INIT: ALSA_MixerInit(); ret = MMSYSERR_NOERROR; break;
1041     case DRVM_EXIT: ALSA_MixerExit(); ret = MMSYSERR_NOERROR; break;
1042     /* All taken care of by driver initialisation */
1043     /* Unimplemented, and not needed */
1044     case DRVM_ENABLE:
1045     case DRVM_DISABLE:
1046         ret = MMSYSERR_NOERROR; break;
1047
1048     case MXDM_OPEN:
1049         ret = MIX_Open(wDevID, (LPMIXEROPENDESC) dwParam1, dwParam2); break;
1050
1051     case MXDM_CLOSE:
1052         ret = MIX_Close(wDevID); break;
1053
1054     case MXDM_GETDEVCAPS:
1055         ret = MIX_GetDevCaps(wDevID, (LPMIXERCAPS2W)dwParam1, dwParam2); break;
1056
1057     case MXDM_GETLINEINFO:
1058         ret = MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2); break;
1059
1060     case MXDM_GETLINECONTROLS:
1061         ret = MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2); break;
1062
1063     case MXDM_GETNUMDEVS:
1064         ret = cards; break;
1065
1066     default:
1067         WARN("unknown message %s!\n", getMessage(wMsg));
1068         return MMSYSERR_NOTSUPPORTED;
1069     }
1070
1071     TRACE("Returning %08X\n", ret);
1072     return ret;
1073 #else /*HAVE_ALSA*/
1074     TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1075
1076     return MMSYSERR_NOTENABLED;
1077 #endif /*HAVE_ALSA*/
1078 }