2 * Alsa MIXER Wine Driver for Linux
3 * Very loosely based on wineoss mixer driver
5 * Copyright 2007 Maarten Lankhorst
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.
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.
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
23 #include "wine/port.h"
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.h>
39 #define NONAMELESSUNION
40 #define NONAMELESSSTRUCT
51 #include "wine/unicode.h"
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(mixer);
58 #define WINE_MIXER_MANUF_ID 0xAA
59 #define WINE_MIXER_PRODUCT_ID 0x55
60 #define WINE_MIXER_VERSION 0x0100
63 * In windows it seems to be required for all controls to have a volume switch
64 * In alsa that's optional
66 * I assume for playback controls, that there is always a playback volume switch available
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
75 * MUX means that only capture source can be exclusively selected,
76 * MIXER means that multiple sources can be selected simultaneously.
79 static const char * getMessage(UINT uMsg)
82 #define MSG_TO_STR(x) case x: return #x;
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);
99 sprintf(str, "UNKNOWN(%08x)", uMsg);
103 static const char * getControlType(DWORD dwControlType)
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);
140 return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType);
143 /* A simple declaration of a line control
144 * These are each of the channels that show up
146 typedef struct line {
147 /* Name we present to outside world */
148 WCHAR name[MAXPNAMELEN];
154 snd_mixer_elem_t *elem;
157 /* A control structure, with toggle enabled switch
158 * Control structures control volume, muted, which capture source
160 typedef struct control {
169 WCHAR mixername[MAXPNAMELEN];
172 LPDRVCALLBACK callback;
173 DWORD_PTR callbackpriv;
180 #define MAX_MIXERS 32
181 #define CONTROLSPERLINE 3
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];
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
199 static const struct mixerlinetype {
200 const char *name; DWORD cmpt;
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, },
215 /* Map name to MIXERLINE_COMPONENTTYPE_XXX */
216 static int getcomponenttype(const char *name)
219 for (x=0; x< sizeof(converttable)/sizeof(converttable[0]); ++x)
220 if (!strcasecmp(name, converttable[x].name))
222 TRACE("%d -> %s\n", x, name);
223 return converttable[x].cmpt;
225 WARN("Unknown mixer name %s, probably harmless\n", name);
226 return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
229 /* Is this control suited for showing up? */
230 static int blacklisted(snd_mixer_elem_t *elem)
232 const char *name = snd_mixer_selem_get_name(elem);
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)))
240 TRACE("%s: %x\n", name, blisted);
244 static void fillcontrols(mixer *mmixer)
247 for (id = 0; id < mmixer->chans; ++id)
249 line *mline = &mmixer->lines[id];
250 int ofs = CONTROLSPERLINE * id;
254 if (mline->capt && snd_mixer_selem_has_capture_volume(mline->elem))
255 snd_mixer_selem_get_capture_volume_range(mline->elem, &min, &max);
257 snd_mixer_selem_get_playback_volume_range(mline->elem, &min, &max);
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);
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;
279 if (mline->capt && snd_mixer_selem_has_capture_switch_exclusive(mline->elem))
280 mmixer->controls[CONTROLSPERLINE+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
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;
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;
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;
299 for (x=0; x<CONTROLSPERLINE; ++x)
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));
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)
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))
319 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
320 if (snd_mixer_selem_has_playback_channel(elem, chn))
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"));
328 static void ALSA_MixerInit(void)
332 for (x = 0; x < MAX_MIXERS; ++x)
335 char cardind[6], cardname[10];
336 BOOL hascapt=0, hasmast=0;
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;
346 snprintf(cardind, sizeof(cardind), "%d", x);
347 card = snd_card_get_index(cardind);
350 snprintf(cardname, sizeof(cardname), "hw:%d", card);
352 err = snd_ctl_open(&ctl, cardname, 0);
355 WARN("Cannot open card: %s\n", snd_strerror(err));
359 err = snd_ctl_card_info(ctl, info);
362 WARN("Cannot get card info: %s\n", snd_strerror(err));
367 MultiByteToWideChar(CP_UNIXCP, 0, snd_ctl_card_info_get_name(info), -1, mixdev[mixnum].mixername, sizeof(mixdev[mixnum].mixername)/sizeof(WCHAR));
370 err = snd_mixer_open(&mixdev[mixnum].mix,0);
373 WARN("Error occured opening mixer: %s\n", snd_strerror(err));
377 err = snd_mixer_attach(mixdev[mixnum].mix, cardname);
381 err = snd_mixer_selem_register(mixdev[mixnum].mix, NULL, NULL);
385 err = snd_mixer_load(mixdev[mixnum].mix);
389 mixdev[mixnum].chans = 0;
390 mixdev[mixnum].dests = 1; /* Master, Capture will be enabled if needed */
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"))
398 else if (!strcasecmp(snd_mixer_selem_get_name(elem), "Capture"))
403 else if (!blacklisted(elem))
405 if (snd_mixer_selem_has_capture_switch(elem))
407 ++mixdev[mixnum].chans;
408 mixdev[mixnum].dests = 2;
410 if (snd_mixer_selem_has_playback_volume(elem))
411 ++mixdev[mixnum].chans;
414 /* If there is only 'Capture' and 'Master', this device is not worth it */
415 if (!mixdev[mixnum].chans)
417 WARN("No channels found, skipping device!\n");
418 snd_mixer_close(mixdev[mixnum].mix);
422 /* If there are no 'Capture' and 'Master', something is wrong */
423 if (hasmast != 1 || hascapt != 1)
426 FIXME("Should have found 1 channel for 'Master', but instead found %d\n", hasmast);
428 FIXME("Should have found 1 channel for 'Capture', but instead found %d\n", hascapt);
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);
436 if (!mixdev[mixnum].lines || !mixdev[mixnum].controls)
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");
445 mline->elem = mastelem;
446 mline->chans = chans(&mixdev[mixnum], mastelem, 0);
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 */
454 MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(captelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
455 mline->component = getcomponenttype("Capture");
458 mline->elem = captelem;
459 mline->chans = chans(&mixdev[mixnum], captelem, 1);
461 snd_mixer_elem_set_callback(mastelem, &elem_callback);
462 snd_mixer_elem_set_callback_private(mastelem, &mixdev[mixnum]);
464 if (mixdev[mixnum].dests == 2)
466 snd_mixer_elem_set_callback(captelem, &elem_callback);
467 snd_mixer_elem_set_callback_private(captelem, &mixdev[mixnum]);
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))
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]);
479 if (snd_mixer_selem_has_playback_volume(elem))
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;
486 mline->chans = chans(&mixdev[mixnum], elem, 0);
488 if (snd_mixer_selem_has_capture_switch(elem))
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;
495 mline->chans = chans(&mixdev[mixnum], elem, 1);
499 fillcontrols(&mixdev[mixnum]);
501 TRACE("%s: Amount of controls: %i/%i, name: %s\n", cardname, mixdev[mixnum].dests, mixdev[mixnum].chans, debugstr_w(mixdev[mixnum].mixername));
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);
515 InitializeCriticalSection(&elem_crst);
516 elem_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MIXER.elem_crst");
520 static void ALSA_MixerExit(void)
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);
533 TRACE("Cleaning up\n");
535 elem_crst.DebugInfo->Spare[0] = 0;
536 DeleteCriticalSection(&elem_crst);
537 for (x = 0; x < cards; ++x)
539 snd_mixer_close(mixdev[x].mix);
540 HeapFree(GetProcessHeap(), 0, mixdev[x].lines);
541 HeapFree(GetProcessHeap(), 0, mixdev[x].controls);
546 static mixer* MIX_GetMix(UINT wDevID)
550 if (wDevID < 0 || wDevID >= cards)
552 WARN("Invalid mixer id: %d\n", wDevID);
556 mmixer = &mixdev[wDevID];
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)
563 mixer *mmixer = snd_mixer_elem_get_callback_private(elem);
565 BOOL captchanged = 0;
567 if (type != SND_CTL_EVENT_MASK_VALUE)
572 EnterCriticalSection(&elem_crst);
574 if (!mmixer->callback)
577 for (x=0; x<mmixer->chans; ++x)
579 const int ofs = CONTROLSPERLINE*x;
580 if (elem != mmixer->lines[x].elem)
583 if (mmixer->lines[x].capt)
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);
590 if (mmixer->controls[ofs+OFS_MUTE].enabled)
591 mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs+OFS_MUTE, 0);
594 mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, CONTROLSPERLINE+OFS_MUX, 0);
597 LeaveCriticalSection(&elem_crst);
602 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam)
604 struct pollfd *pfds = NULL;
605 int x, y, err, mcnt, count = 1;
607 TRACE("%p\n", lParam);
609 for (x = 0; x < cards; ++x)
610 count += snd_mixer_poll_descriptors_count(mixdev[x].mix);
612 TRACE("Counted %d descriptors\n", count);
613 pfds = HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct pollfd));
617 WARN("Out of memory\n");
621 pfds[0].fd = msg_pipe[0];
622 pfds[0].events = POLLIN;
625 for (x = 0; x < cards; ++x)
626 y += snd_mixer_poll_descriptors(mixdev[x].mix, &pfds[y], count - y);
628 while ((err = poll(pfds, (unsigned int) count, -1)) >= 0 || errno == EINTR || errno == EAGAIN)
630 if (pfds[0].revents & POLLIN)
634 for (x = y = 0; x < cards; ++x)
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)
640 y += snd_mixer_handle_events(mixdev[x].mix);
646 TRACE("Handled %d events\n", y);
650 TRACE("Shutting down\n");
652 HeapFree(GetProcessHeap(), 0, pfds);
654 y = read(msg_pipe[0], &x, sizeof(x));
660 static DWORD MIX_Open(UINT wDevID, LPMIXEROPENDESC desc, DWORD_PTR flags)
662 mixer *mmixer = MIX_GetMix(wDevID);
664 return MMSYSERR_BADDEVICEID;
666 flags &= CALLBACK_TYPEMASK;
672 case CALLBACK_FUNCTION:
676 FIXME("Unhandled callback type: %08lx\n", flags & CALLBACK_TYPEMASK);
677 return MIXERR_INVALVALUE;
680 mmixer->callback = (LPDRVCALLBACK)desc->dwCallback;
681 mmixer->callbackpriv = desc->dwInstance;
682 mmixer->hmx = (HDRVR)desc->hmx;
685 if (InterlockedIncrement(&refcnt) == 1)
687 if (pipe(msg_pipe) >= 0)
689 thread = CreateThread(NULL, 0, ALSA_MixerPollThread, NULL, 0, NULL);
694 msg_pipe[0] = msg_pipe[1] = -1;
698 msg_pipe[0] = msg_pipe[1] = -1;
701 return MMSYSERR_NOERROR;
704 static DWORD MIX_Close(UINT wDevID)
707 mixer *mmixer = MIX_GetMix(wDevID);
709 return MMSYSERR_BADDEVICEID;
711 EnterCriticalSection(&elem_crst);
712 mmixer->callback = 0;
713 LeaveCriticalSection(&elem_crst);
715 if (!InterlockedDecrement(&refcnt))
717 if (write(msg_pipe[1], &x, sizeof(x)) > 0)
719 TRACE("Shutting down thread...\n");
720 WaitForSingleObject(thread, INFINITE);
725 return MMSYSERR_NOERROR;
728 static DWORD MIX_GetDevCaps(UINT wDevID, LPMIXERCAPS2W caps, DWORD_PTR parm2)
730 mixer *mmixer = MIX_GetMix(wDevID);
734 return MMSYSERR_INVALPARAM;
737 return MMSYSERR_BADDEVICEID;
739 memset(&capsW, 0, sizeof(MIXERCAPS2W));
741 capsW.wMid = WINE_MIXER_MANUF_ID;
742 capsW.wPid = WINE_MIXER_PRODUCT_ID;
743 capsW.vDriverVersion = WINE_MIXER_VERSION;
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;
752 /* get amount of sources for dest */
753 static int getsrccntfromchan(mixer *mmixer, int dad)
757 for (i=0; i<mmixer->chans; ++i)
758 if (i != dad && mmixer->lines[i].dst == dad)
763 FIXME("No src found for %i (%s)?\n", dad, debugstr_w(mmixer->lines[dad].name));
767 /* find lineid for source 'num' with dest 'dad' */
768 static int getsrclinefromchan(mixer *mmixer, int dad, int num)
771 for (i=0; i<mmixer->chans; ++i)
772 if (i != dad && mmixer->lines[i].dst == dad)
778 WARN("No src found for src %i from dest %i\n", num, dad);
782 /* get the source number belonging to line */
783 static int getsrcfromline(mixer *mmixer, int line)
785 int i, j=0, dad = mmixer->lines[line].dst;
787 for (i=0; i<mmixer->chans; ++i)
788 if (i != dad && mmixer->lines[i].dst == dad)
794 WARN("No src found for line %i with dad %i\n", line, dad);
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:
806 static DWORD MIX_GetLineInfo(UINT wDevID, LPMIXERLINEW Ml, DWORD_PTR flags)
808 DWORD_PTR qf = flags & MIXER_GETLINEINFOF_QUERYMASK;
809 mixer *mmixer = MIX_GetMix(wDevID);
816 return MMSYSERR_INVALPARAM;
821 WARN("Device %u not found\n", wDevID);
822 return MMSYSERR_BADDEVICEID;
825 if (Ml->cbStruct != sizeof(*Ml))
827 WARN("invalid parameter: Ml->cbStruct = %d != %d\n", Ml->cbStruct, sizeof(*Ml));
828 return MMSYSERR_INVALPARAM;
831 Ml->fdwLine = MIXERLINE_LINEF_ACTIVE;
836 case MIXER_GETLINEINFOF_COMPONENTTYPE:
838 Ml->dwLineID = 0xFFFF;
839 for (idx = 0; idx < mmixer->chans; ++idx)
840 if (mmixer->lines[idx].component == Ml->dwComponentType)
845 if (Ml->dwLineID == 0xFFFF)
846 return MMSYSERR_KEYNOTFOUND;
847 /* Now that we have lineid, fallback to lineid*/
850 case MIXER_GETLINEINFOF_LINEID:
851 if (Ml->dwLineID < 0 || Ml->dwLineID >= mmixer->chans)
852 return MIXERR_INVALLINE;
854 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", Ml->dwLineID);
855 Ml->dwDestination = mmixer->lines[Ml->dwLineID].dst;
857 if (Ml->dwDestination != Ml->dwLineID)
859 Ml->dwSource = getsrcfromline(mmixer, Ml->dwLineID);
860 Ml->cConnections = 1;
864 Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
865 Ml->dwSource = 0xFFFFFFFF;
867 TRACE("Connections %d, source %d\n", Ml->cConnections, Ml->dwSource);
870 case MIXER_GETLINEINFOF_DESTINATION:
871 if (Ml->dwDestination < 0 || Ml->dwDestination >= mmixer->dests)
873 WARN("dest %d out of bounds\n", Ml->dwDestination);
874 return MIXERR_INVALLINE;
877 Ml->dwLineID = Ml->dwDestination;
878 Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
879 Ml->dwSource = 0xFFFFFFFF;
882 case MIXER_GETLINEINFOF_SOURCE:
883 if (Ml->dwDestination < 0 || Ml->dwDestination >= mmixer->dests)
885 WARN("dest %d for source out of bounds\n", Ml->dwDestination);
886 return MIXERR_INVALLINE;
889 if (Ml->dwSource < 0 || Ml->dwSource >= getsrccntfromchan(mmixer, Ml->dwDestination))
891 WARN("src %d out of bounds\n", Ml->dwSource);
892 return MIXERR_INVALLINE;
895 Ml->dwLineID = getsrclinefromchan(mmixer, Ml->dwDestination, Ml->dwSource);
896 Ml->cConnections = 1;
899 case MIXER_GETLINEINFOF_TARGETTYPE:
900 FIXME("TODO: TARGETTYPE, stub\n");
901 return MMSYSERR_INVALPARAM;
904 FIXME("Unknown query flag: %08lx\n", qf);
905 return MMSYSERR_INVALPARAM;
908 if (Ml->dwLineID >= mmixer->dests)
909 Ml->fdwLine |= MIXERLINE_LINEF_SOURCE;
911 mline = &mmixer->lines[Ml->dwLineID];
912 Ml->dwComponentType = mline->component;
913 Ml->cChannels = mmixer->lines[Ml->dwLineID].chans;
916 for (i=CONTROLSPERLINE*Ml->dwLineID;i<CONTROLSPERLINE*(Ml->dwLineID+1); ++i)
917 if (mmixer->controls[i].enabled)
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));
923 Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
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;
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)
937 mixer *mmixer = MIX_GetMix(wDevID);
941 if (!mlc || mlc->cbStruct != sizeof(*mlc))
943 WARN("Invalid mlc %p, cbStruct: %d\n", mlc, (!mlc ? -1 : mlc->cbStruct));
944 return MMSYSERR_INVALPARAM;
947 if (mlc->cbmxctrl != sizeof(MIXERCONTROLW))
949 WARN("cbmxctrl %d\n", mlc->cbmxctrl);
950 return MMSYSERR_INVALPARAM;
954 return MMSYSERR_BADDEVICEID;
956 flags &= MIXER_GETLINECONTROLSF_QUERYMASK;
958 if (flags == MIXER_GETLINECONTROLSF_ONEBYID)
959 mlc->dwLineID = mlc->u.dwControlID / CONTROLSPERLINE;
961 if (mlc->dwLineID < 0 || mlc->dwLineID >= mmixer->chans)
963 TRACE("Invalid dwLineID %d\n", mlc->dwLineID);
964 return MIXERR_INVALLINE;
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)
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));
977 if (j > mlc->cControls)
979 WARN("invalid parameter\n");
980 return MMSYSERR_INVALPARAM;
984 if (!j || mlc->cControls > j)
986 WARN("invalid parameter\n");
987 return MMSYSERR_INVALPARAM;
990 case MIXER_GETLINECONTROLSF_ONEBYID:
991 TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n", mlc->dwLineID, mlc->u.dwControlID);
993 if (!mmixer->controls[mlc->u.dwControlID].enabled)
994 return MIXERR_INVALCONTROL;
996 mlc->pamxctrl[0] = mmixer->controls[mlc->u.dwControlID].c;
998 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
999 TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", mlc->dwLineID, getControlType(mlc->u.dwControlType));
1001 ct = mlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
1002 for (i = 0; i <= CONTROLSPERLINE; ++i)
1004 const int ofs = i+mlc->dwLineID*CONTROLSPERLINE;
1005 if (i == CONTROLSPERLINE)
1007 WARN("invalid parameter: control %s not found\n", getControlType(mlc->u.dwControlType));
1008 return MIXERR_INVALCONTROL;
1010 if (mmixer->controls[ofs].enabled && (mmixer->controls[ofs].c.dwControlType & MIXERCONTROL_CT_CLASS_MASK) == ct)
1012 mlc->pamxctrl[0] = mmixer->controls[ofs].c;
1018 FIXME("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
1019 return MMSYSERR_INVALPARAM;
1022 return MMSYSERR_NOERROR;
1025 #endif /*HAVE_ALSA*/
1027 /**************************************************************************
1028 * mxdMessage (WINEALSA.3)
1030 DWORD WINAPI ALSA_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1031 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1035 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
1036 dwUser, dwParam1, dwParam2);
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 */
1046 ret = MMSYSERR_NOERROR; break;
1049 ret = MIX_Open(wDevID, (LPMIXEROPENDESC) dwParam1, dwParam2); break;
1052 ret = MIX_Close(wDevID); break;
1054 case MXDM_GETDEVCAPS:
1055 ret = MIX_GetDevCaps(wDevID, (LPMIXERCAPS2W)dwParam1, dwParam2); break;
1057 case MXDM_GETLINEINFO:
1058 ret = MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2); break;
1060 case MXDM_GETLINECONTROLS:
1061 ret = MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2); break;
1063 case MXDM_GETNUMDEVS:
1067 WARN("unknown message %s!\n", getMessage(wMsg));
1068 return MMSYSERR_NOTSUPPORTED;
1071 TRACE("Returning %08X\n", ret);
1074 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1076 return MMSYSERR_NOTENABLED;
1077 #endif /*HAVE_ALSA*/