winealsa: Implement mixer controls, and add GetLineInfo.
[wine/wine-gecko.git] / dlls / winealsa.drv / mixer.c
blob465b18bece9e75ec7460e827df9201c1c810bee5
1 /*
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
22 #include "config.h"
23 #include "wine/port.h"
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
39 #define NONAMELESSUNION
40 #define NONAMELESSSTRUCT
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"
54 WINE_DEFAULT_DEBUG_CHANNEL(mixer);
56 #ifdef HAVE_ALSA
58 #define WINE_MIXER_MANUF_ID 0xAA
59 #define WINE_MIXER_PRODUCT_ID 0x55
60 #define WINE_MIXER_VERSION 0x0100
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
66 * I assume for playback controls, that there is always a playback volume switch available
67 * Mute is optional
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)
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;
98 #undef MSG_TO_STR
99 sprintf(str, "UNKNOWN(%08x)", uMsg);
100 return str;
103 /* A simple declaration of a line control
104 * These are each of the channels that show up
106 typedef struct line {
107 /* Name we present to outside world */
108 WCHAR name[MAXPNAMELEN];
110 DWORD component;
111 DWORD dst;
112 DWORD capt;
113 DWORD chans;
114 snd_mixer_elem_t *elem;
115 } line;
117 /* A control structure, with toggle enabled switch
118 * Control structures control volume, muted, which capture source
120 typedef struct control {
121 BOOL enabled;
122 MIXERCONTROLW c;
123 } control;
125 /* Mixer device */
126 typedef struct mixer
128 snd_mixer_t *mix;
129 WCHAR mixername[MAXPNAMELEN];
131 int chans, dests;
132 LPDRVCALLBACK callback;
133 DWORD_PTR callbackpriv;
134 HDRVR hmx;
136 line *lines;
137 control *controls;
138 } mixer;
140 #define MAX_MIXERS 32
141 #define CONTROLSPERLINE 3
142 #define OFS_MUTE 2
143 #define OFS_MUX 1
145 static int cards = 0;
146 static mixer mixdev[MAX_MIXERS];
147 static HANDLE thread;
148 static int elem_callback(snd_mixer_elem_t *elem, unsigned int mask);
149 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam);
150 static CRITICAL_SECTION elem_crst;
151 static int msg_pipe[2];
152 static LONG refcnt;
154 /* found channel names in alsa lib, alsa api doesn't have another way for this
155 * map name -> componenttype, worst case we get a wrong componenttype which is
156 * mostly harmless
159 static const struct mixerlinetype {
160 const char *name; DWORD cmpt;
161 } converttable[] = {
162 { "Master", MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, },
163 { "Capture", MIXERLINE_COMPONENTTYPE_DST_WAVEIN, },
164 { "PCM", MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, },
165 { "PC Speaker", MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER, },
166 { "Synth", MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER, },
167 { "Headphone", MIXERLINE_COMPONENTTYPE_DST_HEADPHONES, },
168 { "Mic", MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE, },
169 { "Aux", MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED, },
170 { "CD", MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC, },
171 { "Line", MIXERLINE_COMPONENTTYPE_SRC_LINE, },
172 { "Phone", MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE, },
175 /* Map name to MIXERLINE_COMPONENTTYPE_XXX */
176 static int getcomponenttype(const char *name)
178 int x;
179 for (x=0; x< sizeof(converttable)/sizeof(converttable[0]); ++x)
180 if (!strcasecmp(name, converttable[x].name))
182 TRACE("%d -> %s\n", x, name);
183 return converttable[x].cmpt;
185 WARN("Unknown mixer name %s, probably harmless\n", name);
186 return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
189 /* Is this control suited for showing up? */
190 static int blacklisted(snd_mixer_elem_t *elem)
192 const char *name = snd_mixer_selem_get_name(elem);
193 BOOL blisted = 0;
195 if (!snd_mixer_selem_has_playback_volume(elem) &&
196 (!snd_mixer_selem_has_capture_volume(elem) ||
197 !snd_mixer_selem_has_capture_switch(elem)))
198 blisted = 1;
200 TRACE("%s: %x\n", name, blisted);
201 return blisted;
204 static void fillcontrols(mixer *mmixer)
206 int id;
207 for (id = 0; id < mmixer->chans; ++id)
209 line *mline = &mmixer->lines[id];
210 int ofs = CONTROLSPERLINE * id;
211 int x;
212 long min, max;
214 if (mline->capt && snd_mixer_selem_has_capture_volume(mline->elem))
215 snd_mixer_selem_get_capture_volume_range(mline->elem, &min, &max);
216 else
217 snd_mixer_selem_get_playback_volume_range(mline->elem, &min, &max);
219 /* (!snd_mixer_selem_has_playback_volume(elem) || snd_mixer_selem_has_capture_volume(elem)) */
220 /* Volume, always enabled by definition of blacklisted channels */
221 mmixer->controls[ofs].enabled = 1;
222 mmixer->controls[ofs].c.cbStruct = sizeof(mmixer->controls[ofs].c);
223 mmixer->controls[ofs].c.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
224 mmixer->controls[ofs].c.dwControlID = ofs;
225 mmixer->controls[ofs].c.Bounds.s1.dwMinimum = 0;
226 mmixer->controls[ofs].c.Bounds.s1.dwMaximum = 65535;
227 mmixer->controls[ofs].c.Metrics.cSteps = 65536/(max-min);
229 if ((id == 1 && snd_mixer_selem_has_capture_switch(mline->elem)) ||
230 (!mline->capt && snd_mixer_selem_has_playback_switch(mline->elem)))
231 { /* MUTE button optional, main capture channel should have one too */
232 mmixer->controls[ofs+OFS_MUTE].enabled = 1;
233 mmixer->controls[ofs+OFS_MUTE].c.cbStruct = sizeof(mmixer->controls[ofs].c);
234 mmixer->controls[ofs+OFS_MUTE].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
235 mmixer->controls[ofs+OFS_MUTE].c.dwControlID = ofs+OFS_MUTE;
236 mmixer->controls[ofs+OFS_MUTE].c.Bounds.s1.dwMaximum = 1;
239 if (mline->capt && snd_mixer_selem_has_capture_switch_exclusive(mline->elem))
240 mmixer->controls[CONTROLSPERLINE+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
242 if (id == 1)
243 { /* Capture select, in case cMultipleItems is 0, it means capture is disabled anyway */
244 mmixer->controls[ofs+OFS_MUX].enabled = 1;
245 mmixer->controls[ofs+OFS_MUX].c.cbStruct = sizeof(mmixer->controls[ofs].c);
246 mmixer->controls[ofs+OFS_MUX].c.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER;
247 mmixer->controls[ofs+OFS_MUX].c.dwControlID = ofs+OFS_MUX;
248 mmixer->controls[ofs+OFS_MUX].c.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
250 for (x = 0; x<mmixer->chans; ++x)
251 if (x != id && mmixer->lines[x].dst == id)
252 ++(mmixer->controls[ofs+OFS_MUX].c.cMultipleItems);
253 if (!mmixer->controls[ofs+OFS_MUX].c.cMultipleItems)
254 mmixer->controls[ofs+OFS_MUX].enabled = 0;
256 mmixer->controls[ofs+OFS_MUX].c.Bounds.s1.dwMaximum = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems - 1;
257 mmixer->controls[ofs+OFS_MUX].c.Metrics.cSteps = mmixer->controls[ofs+OFS_MUX].c.cMultipleItems;
259 for (x=0; x<CONTROLSPERLINE; ++x)
261 lstrcpynW(mmixer->controls[ofs+x].c.szShortName, mline->name, sizeof(mmixer->controls[ofs+x].c.szShortName)/sizeof(WCHAR));
262 lstrcpynW(mmixer->controls[ofs+x].c.szName, mline->name, sizeof(mmixer->controls[ofs+x].c.szName)/sizeof(WCHAR));
267 /* get amount of channels for elem */
268 /* Officially we should keep capture/playback seperated,
269 * but that's not going to work in the alsa api */
270 static int chans(mixer *mmixer, snd_mixer_elem_t * elem, DWORD capt)
272 int ret=0, chn;
274 if (capt && snd_mixer_selem_has_capture_volume(elem)) {
275 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
276 if (snd_mixer_selem_has_capture_channel(elem, chn))
277 ++ret;
278 } else {
279 for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
280 if (snd_mixer_selem_has_playback_channel(elem, chn))
281 ++ret;
283 if (!ret)
284 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"));
285 return ret;
288 static void ALSA_MixerInit(void)
290 int x, mixnum = 0;
292 for (x = 0; x < MAX_MIXERS; ++x)
294 int card, err, y;
295 char cardind[6], cardname[10];
296 BOOL hascapt=0, hasmast=0;
297 line *mline;
299 snd_ctl_t *ctl;
300 snd_mixer_elem_t *elem, *mastelem = NULL, *captelem = NULL;
301 snd_ctl_card_info_t *info = NULL;
302 snd_ctl_card_info_alloca(&info);
303 mixdev[mixnum].lines = NULL;
304 mixdev[mixnum].callback = 0;
306 snprintf(cardind, sizeof(cardind), "%d", x);
307 card = snd_card_get_index(cardind);
308 if (card < 0)
309 continue;
310 snprintf(cardname, sizeof(cardname), "hw:%d", card);
312 err = snd_ctl_open(&ctl, cardname, 0);
313 if (err < 0)
315 WARN("Cannot open card: %s\n", snd_strerror(err));
316 continue;
319 err = snd_ctl_card_info(ctl, info);
320 if (err < 0)
322 WARN("Cannot get card info: %s\n", snd_strerror(err));
323 snd_ctl_close(ctl);
324 continue;
327 MultiByteToWideChar(CP_UNIXCP, 0, snd_ctl_card_info_get_name(info), -1, mixdev[mixnum].mixername, sizeof(mixdev[mixnum].mixername)/sizeof(WCHAR));
328 snd_ctl_close(ctl);
330 err = snd_mixer_open(&mixdev[mixnum].mix,0);
331 if (err < 0)
333 WARN("Error occured opening mixer: %s\n", snd_strerror(err));
334 continue;
337 err = snd_mixer_attach(mixdev[mixnum].mix, cardname);
338 if (err < 0)
339 goto eclose;
341 err = snd_mixer_selem_register(mixdev[mixnum].mix, NULL, NULL);
342 if (err < 0)
343 goto eclose;
345 err = snd_mixer_load(mixdev[mixnum].mix);
346 if (err < 0)
347 goto eclose;
349 mixdev[mixnum].chans = 0;
350 mixdev[mixnum].dests = 1; /* Master, Capture will be enabled if needed */
352 for (elem = snd_mixer_first_elem(mixdev[mixnum].mix); elem; elem = snd_mixer_elem_next(elem))
353 if (!strcasecmp(snd_mixer_selem_get_name(elem), "Master"))
355 mastelem = elem;
356 ++hasmast;
358 else if (!strcasecmp(snd_mixer_selem_get_name(elem), "Capture"))
360 captelem = elem;
361 ++hascapt;
363 else if (!blacklisted(elem))
365 if (snd_mixer_selem_has_capture_switch(elem))
367 ++mixdev[mixnum].chans;
368 mixdev[mixnum].dests = 2;
370 if (snd_mixer_selem_has_playback_volume(elem))
371 ++mixdev[mixnum].chans;
374 /* If there is only 'Capture' and 'Master', this device is not worth it */
375 if (!mixdev[mixnum].chans)
377 WARN("No channels found, skipping device!\n");
378 snd_mixer_close(mixdev[mixnum].mix);
379 continue;
382 /* If there are no 'Capture' and 'Master', something is wrong */
383 if (hasmast != 1 || hascapt != 1)
385 if (hasmast != 1)
386 FIXME("Should have found 1 channel for 'Master', but instead found %d\n", hasmast);
387 if (hascapt != 1)
388 FIXME("Should have found 1 channel for 'Capture', but instead found %d\n", hascapt);
389 goto eclose;
392 mixdev[mixnum].chans += 2; /* Capture/Master */
393 mixdev[mixnum].lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(line) * mixdev[mixnum].chans);
394 mixdev[mixnum].controls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(control) * CONTROLSPERLINE*mixdev[mixnum].chans);
395 err = -ENOMEM;
396 if (!mixdev[mixnum].lines || !mixdev[mixnum].controls)
397 goto eclose;
399 /* Master control */
400 mline = &mixdev[mixnum].lines[0];
401 MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(mastelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
402 mline->component = getcomponenttype("Master");
403 mline->dst = 0;
404 mline->capt = 0;
405 mline->elem = mastelem;
406 mline->chans = chans(&mixdev[mixnum], mastelem, 0);
408 /* Capture control
409 * Note: since mmixer->dests = 1, it means only playback control is visible
410 * This makes sense, because if there are no capture sources capture control
411 * can't do anything and should be invisible */
413 mline++;
414 MultiByteToWideChar(CP_UNIXCP, 0, snd_mixer_selem_get_name(captelem), -1, mline->name, sizeof(mline->name)/sizeof(WCHAR));
415 mline->component = getcomponenttype("Capture");
416 mline->dst = 1;
417 mline->capt = 1;
418 mline->elem = captelem;
419 mline->chans = chans(&mixdev[mixnum], captelem, 1);
421 snd_mixer_elem_set_callback(mastelem, &elem_callback);
422 snd_mixer_elem_set_callback_private(mastelem, &mixdev[mixnum]);
424 if (mixdev[mixnum].dests == 2)
426 snd_mixer_elem_set_callback(captelem, &elem_callback);
427 snd_mixer_elem_set_callback_private(captelem, &mixdev[mixnum]);
430 y=2;
431 for (elem = snd_mixer_first_elem(mixdev[mixnum].mix); elem; elem = snd_mixer_elem_next(elem))
432 if (elem != mastelem && elem != captelem && !blacklisted(elem))
434 const char * name = snd_mixer_selem_get_name(elem);
435 DWORD comp = getcomponenttype(name);
436 snd_mixer_elem_set_callback(elem, &elem_callback);
437 snd_mixer_elem_set_callback_private(elem, &mixdev[mixnum]);
439 if (snd_mixer_selem_has_playback_volume(elem))
441 mline = &mixdev[mixnum].lines[y++];
442 mline->component = comp;
443 MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
444 mline->capt = mline->dst = 0;
445 mline->elem = elem;
446 mline->chans = chans(&mixdev[mixnum], elem, 0);
448 if (snd_mixer_selem_has_capture_switch(elem))
450 mline = &mixdev[mixnum].lines[y++];
451 mline->component = comp;
452 MultiByteToWideChar(CP_UNIXCP, 0, name, -1, mline->name, MAXPNAMELEN);
453 mline->capt = mline->dst = 1;
454 mline->elem = elem;
455 mline->chans = chans(&mixdev[mixnum], elem, 1);
459 fillcontrols(&mixdev[mixnum]);
461 TRACE("%s: Amount of controls: %i/%i, name: %s\n", cardname, mixdev[mixnum].dests, mixdev[mixnum].chans, debugstr_w(mixdev[mixnum].mixername));
462 mixnum++;
463 continue;
465 eclose:
466 WARN("Error occured initialising mixer: %s\n", snd_strerror(err));
467 if (mixdev[mixnum].lines)
468 HeapFree(GetProcessHeap(), 0, mixdev[mixnum].lines);
469 if (mixdev[mixnum].controls)
470 HeapFree(GetProcessHeap(), 0, mixdev[mixnum].controls);
471 snd_mixer_close(mixdev[mixnum].mix);
473 cards = mixnum;
475 InitializeCriticalSection(&elem_crst);
476 elem_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MIXER.elem_crst");
477 TRACE("\n");
480 static void ALSA_MixerExit(void)
482 int x;
484 if (refcnt)
486 WARN("Callback thread still alive, terminating uncleanly, refcnt: %d\n", refcnt);
487 /* Least we can do is making sure we're not in 'foreign' code */
488 EnterCriticalSection(&elem_crst);
489 TerminateThread(thread, 1);
490 refcnt = 0;
493 TRACE("Cleaning up\n");
495 elem_crst.DebugInfo->Spare[0] = 0;
496 DeleteCriticalSection(&elem_crst);
497 for (x = 0; x < cards; ++x)
499 snd_mixer_close(mixdev[x].mix);
500 HeapFree(GetProcessHeap(), 0, mixdev[x].lines);
501 HeapFree(GetProcessHeap(), 0, mixdev[x].controls);
503 cards = 0;
506 static mixer* MIX_GetMix(UINT wDevID)
508 mixer *mmixer;
510 if (wDevID < 0 || wDevID >= cards)
512 WARN("Invalid mixer id: %d\n", wDevID);
513 return NULL;
516 mmixer = &mixdev[wDevID];
517 return mmixer;
520 /* Since alsa doesn't tell what exactly changed, just assume all affected controls changed */
521 static int elem_callback(snd_mixer_elem_t *elem, unsigned int type)
523 mixer *mmixer = snd_mixer_elem_get_callback_private(elem);
524 int x;
525 BOOL captchanged = 0;
527 if (type != SND_CTL_EVENT_MASK_VALUE)
528 return 0;
530 assert(mmixer);
532 EnterCriticalSection(&elem_crst);
534 if (!mmixer->callback)
535 goto out;
537 for (x=0; x<mmixer->chans; ++x)
539 const int ofs = CONTROLSPERLINE*x;
540 if (elem != mmixer->lines[x].elem)
541 continue;
543 if (mmixer->lines[x].capt)
544 ++captchanged;
546 TRACE("Found changed control %s\n", debugstr_w(mmixer->lines[x].name));
547 mmixer->callback(mmixer->hmx, MM_MIXM_LINE_CHANGE, mmixer->callbackpriv, x, 0);
548 mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs, 0);
550 if (mmixer->controls[ofs+OFS_MUTE].enabled)
551 mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, ofs+OFS_MUTE, 0);
553 if (captchanged)
554 mmixer->callback(mmixer->hmx, MM_MIXM_CONTROL_CHANGE, mmixer->callbackpriv, CONTROLSPERLINE+OFS_MUX, 0);
556 out:
557 LeaveCriticalSection(&elem_crst);
559 return 0;
562 static DWORD WINAPI ALSA_MixerPollThread(LPVOID lParam)
564 struct pollfd *pfds = NULL;
565 int x, y, err, mcnt, count = 1;
567 TRACE("%p\n", lParam);
569 for (x = 0; x < cards; ++x)
570 count += snd_mixer_poll_descriptors_count(mixdev[x].mix);
572 TRACE("Counted %d descriptors\n", count);
573 pfds = HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct pollfd));
575 if (!pfds)
577 WARN("Out of memory\n");
578 goto die;
581 pfds[0].fd = msg_pipe[0];
582 pfds[0].events = POLLIN;
584 y = 1;
585 for (x = 0; x < cards; ++x)
586 y += snd_mixer_poll_descriptors(mixdev[x].mix, &pfds[y], count - y);
588 while ((err = poll(pfds, (unsigned int) count, -1)) >= 0 || errno == EINTR || errno == EAGAIN)
590 if (pfds[0].revents & POLLIN)
591 break;
593 mcnt = 1;
594 for (x = y = 0; x < cards; ++x)
596 int j, max = snd_mixer_poll_descriptors_count(mixdev[x].mix);
597 for (j = 0; j < max; ++j)
598 if (pfds[mcnt+j].revents)
600 y += snd_mixer_handle_events(mixdev[x].mix);
601 break;
603 mcnt += max;
605 if (y)
606 TRACE("Handled %d events\n", y);
609 die:
610 TRACE("Shutting down\n");
611 if (pfds)
612 HeapFree(GetProcessHeap(), 0, pfds);
614 y = read(msg_pipe[0], &x, sizeof(x));
615 close(msg_pipe[1]);
616 close(msg_pipe[0]);
617 return 0;
620 static DWORD MIX_Open(UINT wDevID, LPMIXEROPENDESC desc, DWORD_PTR flags)
622 mixer *mmixer = MIX_GetMix(wDevID);
623 if (!mmixer)
624 return MMSYSERR_BADDEVICEID;
626 flags &= CALLBACK_TYPEMASK;
627 switch (flags)
629 case CALLBACK_NULL:
630 goto done;
632 case CALLBACK_FUNCTION:
633 break;
635 default:
636 FIXME("Unhandled callback type: %08lx\n", flags & CALLBACK_TYPEMASK);
637 return MIXERR_INVALVALUE;
640 mmixer->callback = (LPDRVCALLBACK)desc->dwCallback;
641 mmixer->callbackpriv = desc->dwInstance;
642 mmixer->hmx = (HDRVR)desc->hmx;
644 done:
645 if (InterlockedIncrement(&refcnt) == 1)
647 if (pipe(msg_pipe) >= 0)
649 thread = CreateThread(NULL, 0, ALSA_MixerPollThread, NULL, 0, NULL);
650 if (!thread)
652 close(msg_pipe[0]);
653 close(msg_pipe[1]);
654 msg_pipe[0] = msg_pipe[1] = -1;
657 else
658 msg_pipe[0] = msg_pipe[1] = -1;
661 return MMSYSERR_NOERROR;
664 static DWORD MIX_Close(UINT wDevID)
666 int x;
667 mixer *mmixer = MIX_GetMix(wDevID);
668 if (!mmixer)
669 return MMSYSERR_BADDEVICEID;
671 EnterCriticalSection(&elem_crst);
672 mmixer->callback = 0;
673 LeaveCriticalSection(&elem_crst);
675 if (!InterlockedDecrement(&refcnt))
677 if (write(msg_pipe[1], &x, sizeof(x)) > 0)
679 TRACE("Shutting down thread...\n");
680 WaitForSingleObject(thread, INFINITE);
681 TRACE("Done\n");
685 return MMSYSERR_NOERROR;
688 static DWORD MIX_GetDevCaps(UINT wDevID, LPMIXERCAPS2W caps, DWORD_PTR parm2)
690 mixer *mmixer = MIX_GetMix(wDevID);
691 MIXERCAPS2W capsW;
693 if (!caps)
694 return MMSYSERR_INVALPARAM;
696 if (!mmixer)
697 return MMSYSERR_BADDEVICEID;
699 memset(&capsW, 0, sizeof(MIXERCAPS2W));
701 capsW.wMid = WINE_MIXER_MANUF_ID;
702 capsW.wPid = WINE_MIXER_PRODUCT_ID;
703 capsW.vDriverVersion = WINE_MIXER_VERSION;
705 lstrcpynW(capsW.szPname, mmixer->mixername, sizeof(capsW.szPname)/sizeof(WCHAR));
706 capsW.cDestinations = mmixer->dests;
707 memcpy(caps, &capsW, min(parm2, sizeof(capsW)));
708 return MMSYSERR_NOERROR;
712 /* get amount of sources for dest */
713 static int getsrccntfromchan(mixer *mmixer, int dad)
715 int i, j=0;
717 for (i=0; i<mmixer->chans; ++i)
718 if (i != dad && mmixer->lines[i].dst == dad)
720 ++j;
722 if (!j)
723 FIXME("No src found for %i (%s)?\n", dad, debugstr_w(mmixer->lines[dad].name));
724 return j;
727 /* find lineid for source 'num' with dest 'dad' */
728 static int getsrclinefromchan(mixer *mmixer, int dad, int num)
730 int i, j=0;
731 for (i=0; i<mmixer->chans; ++i)
732 if (i != dad && mmixer->lines[i].dst == dad)
734 if (num == j)
735 return i;
736 ++j;
738 WARN("No src found for src %i from dest %i\n", num, dad);
739 return 0;
742 /* get the source number belonging to line */
743 static int getsrcfromline(mixer *mmixer, int line)
745 int i, j=0, dad = mmixer->lines[line].dst;
747 for (i=0; i<mmixer->chans; ++i)
748 if (i != dad && mmixer->lines[i].dst == dad)
750 if (line == i)
751 return j;
752 ++j;
754 WARN("No src found for line %i with dad %i\n", line, dad);
755 return 0;
758 /* Here we give info over the source/dest line given by dwSource+dwDest or dwDest, respectively
759 * It is also possible that a line is found by componenttype or target type, latter is not implemented yet
760 * Most important values returned in struct:
761 * dwLineID
762 * sz(Short)Name
763 * line control count
764 * amount of channels
766 static DWORD MIX_GetLineInfo(UINT wDevID, LPMIXERLINEW Ml, DWORD_PTR flags)
768 DWORD_PTR qf = flags & MIXER_GETLINEINFOF_QUERYMASK;
769 mixer *mmixer = MIX_GetMix(wDevID);
770 line *mline;
771 int idx, i;
773 if (!Ml)
775 WARN("No Ml\n");
776 return MMSYSERR_INVALPARAM;
779 if (!mmixer)
781 WARN("Device %u not found\n", wDevID);
782 return MMSYSERR_BADDEVICEID;
785 if (Ml->cbStruct != sizeof(*Ml))
787 WARN("invalid parameter: Ml->cbStruct = %d != %d\n", Ml->cbStruct, sizeof(*Ml));
788 return MMSYSERR_INVALPARAM;
791 Ml->fdwLine = MIXERLINE_LINEF_ACTIVE;
792 Ml->dwUser = 0;
794 switch (qf)
796 case MIXER_GETLINEINFOF_COMPONENTTYPE:
798 Ml->dwLineID = 0xFFFF;
799 for (idx = 0; idx < mmixer->chans; ++idx)
800 if (mmixer->lines[idx].component == Ml->dwComponentType)
802 Ml->dwLineID = idx;
803 break;
805 if (Ml->dwLineID == 0xFFFF)
806 return MMSYSERR_KEYNOTFOUND;
807 /* Now that we have lineid, fallback to lineid*/
810 case MIXER_GETLINEINFOF_LINEID:
811 if (Ml->dwLineID < 0 || Ml->dwLineID >= mmixer->chans)
812 return MIXERR_INVALLINE;
814 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", Ml->dwLineID);
815 Ml->dwDestination = mmixer->lines[Ml->dwLineID].dst;
817 if (Ml->dwDestination != Ml->dwLineID)
819 Ml->dwSource = getsrcfromline(mmixer, Ml->dwLineID);
820 Ml->cConnections = 1;
822 else
824 Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
825 Ml->dwSource = 0xFFFFFFFF;
827 TRACE("Connections %d, source %d\n", Ml->cConnections, Ml->dwSource);
828 break;
830 case MIXER_GETLINEINFOF_DESTINATION:
831 if (Ml->dwDestination < 0 || Ml->dwDestination >= mmixer->dests)
833 WARN("dest %d out of bounds\n", Ml->dwDestination);
834 return MIXERR_INVALLINE;
837 Ml->dwLineID = Ml->dwDestination;
838 Ml->cConnections = getsrccntfromchan(mmixer, Ml->dwLineID);
839 Ml->dwSource = 0xFFFFFFFF;
840 break;
842 case MIXER_GETLINEINFOF_SOURCE:
843 if (Ml->dwDestination < 0 || Ml->dwDestination >= mmixer->dests)
845 WARN("dest %d for source out of bounds\n", Ml->dwDestination);
846 return MIXERR_INVALLINE;
849 if (Ml->dwSource < 0 || Ml->dwSource >= getsrccntfromchan(mmixer, Ml->dwDestination))
851 WARN("src %d out of bounds\n", Ml->dwSource);
852 return MIXERR_INVALLINE;
855 Ml->dwLineID = getsrclinefromchan(mmixer, Ml->dwDestination, Ml->dwSource);
856 Ml->cConnections = 1;
857 break;
859 case MIXER_GETLINEINFOF_TARGETTYPE:
860 FIXME("TODO: TARGETTYPE, stub\n");
861 return MMSYSERR_INVALPARAM;
863 default:
864 FIXME("Unknown query flag: %08lx\n", qf);
865 return MMSYSERR_INVALPARAM;
868 if (Ml->dwLineID >= mmixer->dests)
869 Ml->fdwLine |= MIXERLINE_LINEF_SOURCE;
871 mline = &mmixer->lines[Ml->dwLineID];
872 Ml->dwComponentType = mline->component;
873 Ml->cChannels = mmixer->lines[Ml->dwLineID].chans;
874 Ml->cControls = 0;
876 for (i=CONTROLSPERLINE*Ml->dwLineID;i<CONTROLSPERLINE*(Ml->dwLineID+1); ++i)
877 if (mmixer->controls[i].enabled)
878 ++(Ml->cControls);
880 lstrcpynW(Ml->szShortName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szShortName)/sizeof(WCHAR));
881 lstrcpynW(Ml->szName, mmixer->lines[Ml->dwLineID].name, sizeof(Ml->szName)/sizeof(WCHAR));
882 if (mline->capt)
883 Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
884 else
885 Ml->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
886 Ml->Target.dwDeviceID = 0xFFFFFFFF;
887 Ml->Target.wMid = WINE_MIXER_MANUF_ID;
888 Ml->Target.wPid = WINE_MIXER_PRODUCT_ID;
889 Ml->Target.vDriverVersion = WINE_MIXER_VERSION;
890 lstrcpynW(Ml->Target.szPname, mmixer->mixername, sizeof(Ml->Target.szPname)/sizeof(WCHAR));
891 return MMSYSERR_NOERROR;
894 #endif /*HAVE_ALSA*/
896 /**************************************************************************
897 * mxdMessage (WINEALSA.3)
899 DWORD WINAPI ALSA_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
900 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
902 #ifdef HAVE_ALSA
903 DWORD ret;
904 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
905 dwUser, dwParam1, dwParam2);
907 switch (wMsg)
909 case DRVM_INIT: ALSA_MixerInit(); ret = MMSYSERR_NOERROR; break;
910 case DRVM_EXIT: ALSA_MixerExit(); ret = MMSYSERR_NOERROR; break;
911 /* All taken care of by driver initialisation */
912 /* Unimplemented, and not needed */
913 case DRVM_ENABLE:
914 case DRVM_DISABLE:
915 ret = MMSYSERR_NOERROR; break;
917 case MXDM_OPEN:
918 ret = MIX_Open(wDevID, (LPMIXEROPENDESC) dwParam1, dwParam2); break;
920 case MXDM_CLOSE:
921 ret = MIX_Close(wDevID); break;
923 case MXDM_GETDEVCAPS:
924 ret = MIX_GetDevCaps(wDevID, (LPMIXERCAPS2W)dwParam1, dwParam2); break;
926 case MXDM_GETLINEINFO:
927 ret = MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2); break;
929 case MXDM_GETNUMDEVS:
930 ret = cards; break;
932 default:
933 WARN("unknown message %s!\n", getMessage(wMsg));
934 return MMSYSERR_NOTSUPPORTED;
937 TRACE("Returning %08X\n", ret);
938 return ret;
939 #else /*HAVE_ALSA*/
940 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
942 return MMSYSERR_NOTENABLED;
943 #endif /*HAVE_ALSA*/