1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
4 * Sample MIXER Wine Driver for Linux
6 * Copyright 1997 Marcus Meissner
7 * 1999,2001 Eric Pouech
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include <sys/ioctl.h>
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
43 #define WINE_MIXER_MANUF_ID 0xAA
44 #define WINE_MIXER_PRODUCT_ID 0x55
45 #define WINE_MIXER_VERSION 0x0100
46 #define WINE_MIXER_NAME "WINE OSS Mixer"
48 #define WINE_CHN_MASK(_x) (1L << (_x))
49 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
50 /* Bass and Treble are no longer in the mask as Windows does not handle them */
51 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
52 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
53 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
54 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
55 WINE_CHN_MASK(SOUND_MIXER_CD) )
57 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
58 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
59 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
60 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
62 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
63 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
64 static char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
65 static char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
76 int volume[SOUND_MIXER_NRDEVICES];
80 BOOL singleRecChannel;
81 struct mixerCtrl* ctrl;
85 #define LINEID_DST 0xFFFF
86 #define LINEID_SPEAKER 0x0000
87 #define LINEID_RECORD 0x0001
89 static int MIX_NumMixers;
90 static struct mixer MIX_Mixers[1];
92 /**************************************************************************
93 * MIX_FillLineControls [internal]
95 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
97 struct mixerCtrl* mc = &mix->ctrl[c];
100 mc->dwLineID = lineID;
101 mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
102 mc->ctrl.dwControlID = c + 1;
103 mc->ctrl.dwControlType = dwType;
107 case MIXERCONTROL_CONTROLTYPE_VOLUME:
108 mc->ctrl.fdwControl = 0;
109 mc->ctrl.cMultipleItems = 0;
110 lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
111 lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
112 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
113 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
114 * [0, 100] is the range supported by OSS
115 * whatever the min and max values are they must match
116 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
118 mc->ctrl.Bounds.s1.dwMinimum = 0;
119 mc->ctrl.Bounds.s1.dwMaximum = 65535;
120 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
122 case MIXERCONTROL_CONTROLTYPE_MUTE:
123 case MIXERCONTROL_CONTROLTYPE_ONOFF:
124 mc->ctrl.fdwControl = 0;
125 mc->ctrl.cMultipleItems = 0;
126 lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
127 lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
128 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
129 mc->ctrl.Bounds.s1.dwMinimum = 0;
130 mc->ctrl.Bounds.s1.dwMaximum = 1;
131 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
133 case MIXERCONTROL_CONTROLTYPE_MUX:
134 case MIXERCONTROL_CONTROLTYPE_MIXER:
135 mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
136 mc->ctrl.cMultipleItems = 0;
137 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
138 if (WINE_CHN_SUPPORTS(mix->recMask, j))
139 mc->ctrl.cMultipleItems++;
140 lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
141 lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
142 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
143 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
147 FIXME("Internal error: unknown type: %08lx\n", dwType);
149 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
152 /******************************************************************
157 static struct mixer* MIX_Get(WORD wDevID)
159 if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
160 return &MIX_Mixers[wDevID];
163 /**************************************************************************
164 * MIX_Open [internal]
166 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
171 DWORD ret = MMSYSERR_NOERROR;
173 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
175 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
176 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
177 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
178 * messages to it... it seems to be linked to all the equivalent of mixer identification
179 * (with a reference to a wave, midi.. handle
181 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
183 if ((mixer = open(mix->name, O_RDWR)) < 0)
185 if (errno == ENODEV || errno == ENXIO)
187 /* no driver present */
188 return MMSYSERR_NODRIVER;
190 return MMSYSERR_ERROR;
193 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
195 perror("ioctl mixer SOUND_MIXER_DEVMASK");
196 ret = MMSYSERR_ERROR;
199 mix->devMask &= WINE_MIXER_MASK_SPEAKER;
200 if (mix->devMask == 0)
202 ret = MMSYSERR_NODRIVER;
206 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
208 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
209 ret = MMSYSERR_ERROR;
212 mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
214 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
216 perror("ioctl mixer SOUND_MIXER_RECMASK");
217 ret = MMSYSERR_ERROR;
220 mix->recMask &= WINE_MIXER_MASK_RECORD;
221 /* FIXME: we may need to support both rec lev & igain */
222 if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
224 WARN("The sound card doesn't support rec level\n");
225 if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
226 WARN("but it does support IGain, please report\n");
228 if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
230 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
231 ret = MMSYSERR_ERROR;
234 mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
235 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
236 mix->devMask, mix->recMask, mix->stereoMask,
237 mix->singleRecChannel ? "single" : "multiple");
238 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
242 mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
243 /* FIXME: do we always have RECLEV on all cards ??? */
244 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
246 if (WINE_CHN_SUPPORTS(mix->devMask, i))
247 mix->numCtrl += 2; /* volume & mute */
248 if (WINE_CHN_SUPPORTS(mix->recMask, i))
249 mix->numCtrl += 2; /* volume & onoff */
252 if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
254 ret = MMSYSERR_NOMEM;
259 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
260 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
261 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
262 mix->singleRecChannel ?
263 MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
264 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
265 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
267 if (WINE_CHN_SUPPORTS(mix->devMask, i))
269 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
270 MIXERCONTROL_CONTROLTYPE_VOLUME);
271 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
272 MIXERCONTROL_CONTROLTYPE_MUTE);
275 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
277 if (WINE_CHN_SUPPORTS(mix->recMask, i))
279 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
280 MIXERCONTROL_CONTROLTYPE_VOLUME);
281 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
282 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
285 assert(j == mix->numCtrl);
291 /**************************************************************************
292 * MIX_GetVal [internal]
294 static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
299 if ((mixer = open(mix->name, O_RDWR)) < 0)
301 /* FIXME: ENXIO => no mixer installed */
302 WARN("mixer device not available !\n");
306 if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
308 TRACE("Reading volume %x on %d\n", *val, chn);
316 /**************************************************************************
317 * MIX_SetVal [internal]
319 static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
324 TRACE("Writing volume %x on %d\n", val, chn);
326 if ((mixer = open(mix->name, O_RDWR)) < 0)
328 /* FIXME: ENXIO => no mixer installed */
329 WARN("mixer device not available !\n");
333 if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
342 /******************************************************************
347 static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
352 if ((mixer = open(mix->name, O_RDWR)) >= 0)
354 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
360 /******************************************************************
365 static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
370 if ((mixer = open(mix->name, O_RDWR)) >= 0)
372 if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
374 ERR("Can't write new mixer settings\n");
383 /**************************************************************************
384 * MIX_GetDevCaps [internal]
386 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
390 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
392 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
393 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
395 lpCaps->wMid = WINE_MIXER_MANUF_ID;
396 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
397 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
398 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
400 lpCaps->cDestinations = 2; /* speakers & record */
401 lpCaps->fdwSupport = 0; /* No bits defined yet */
403 return MMSYSERR_NOERROR;
406 /**************************************************************************
407 * MIX_GetLineInfoDst [internal]
409 static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
414 lpMl->dwDestination = dst;
418 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
420 j = SOUND_MIXER_VOLUME;
423 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
425 j = SOUND_MIXER_RECLEV;
428 FIXME("shouldn't happen\n");
429 return MMSYSERR_ERROR;
431 lpMl->dwSource = 0xFFFFFFFF;
432 lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
433 lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
435 /* we have all connections found in the MIX_DevMask */
436 lpMl->cConnections = 0;
437 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
438 if (WINE_CHN_SUPPORTS(mask, j))
439 lpMl->cConnections++;
441 if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
443 lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
445 for (j = 0; j < mix->numCtrl; j++)
446 if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
449 return MMSYSERR_NOERROR;
452 /**************************************************************************
453 * MIX_GetLineInfoSrc [internal]
455 static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
458 unsigned mask = (dst) ? mix->recMask : mix->devMask;
460 strcpy(lpMl->szShortName, MIX_Labels[idx]);
461 strcpy(lpMl->szName, MIX_Names[idx]);
462 lpMl->dwLineID = MAKELONG(dst, idx);
463 lpMl->dwDestination = dst;
464 lpMl->cConnections = 1;
466 for (i = 0; i < mix->numCtrl; i++)
467 if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
472 case SOUND_MIXER_SYNTH:
473 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
474 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
477 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
478 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
480 case SOUND_MIXER_LINE:
481 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
482 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
484 case SOUND_MIXER_MIC:
485 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
486 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
488 case SOUND_MIXER_PCM:
489 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
490 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
492 case SOUND_MIXER_IMIX:
493 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
494 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
497 WARN("Index %ld not handled.\n", idx);
498 return MIXERR_INVALLINE;
501 if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
503 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
505 if (WINE_CHN_SUPPORTS(mask, j))
512 return MMSYSERR_NOERROR;
515 /******************************************************************
518 static BOOL MIX_CheckLine(DWORD lineID)
520 return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
521 (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
524 /**************************************************************************
525 * MIX_GetLineInfo [internal]
527 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
530 DWORD ret = MMSYSERR_NOERROR;
534 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
536 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
537 return MMSYSERR_INVALPARAM;
538 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
540 /* FIXME: set all the variables correctly... the lines below
543 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
546 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
548 case MIXER_GETLINEINFOF_DESTINATION:
549 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
550 if (lpMl->dwDestination >= 2)
551 return MMSYSERR_INVALPARAM;
552 if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
555 case MIXER_GETLINEINFOF_SOURCE:
556 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
557 switch (lpMl->dwDestination)
559 case 0: mask = mix->devMask; break;
560 case 1: mask = mix->recMask; break;
561 default: return MMSYSERR_INVALPARAM;
564 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
566 if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
569 if (j >= SOUND_MIXER_NRDEVICES)
570 return MIXERR_INVALLINE;
571 if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
574 case MIXER_GETLINEINFOF_LINEID:
575 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
577 if (!MIX_CheckLine(lpMl->dwLineID))
578 return MIXERR_INVALLINE;
579 if (HIWORD(lpMl->dwLineID) == LINEID_DST)
580 ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
582 ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
583 if (ret != MMSYSERR_NOERROR)
586 case MIXER_GETLINEINFOF_COMPONENTTYPE:
587 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
588 switch (lpMl->dwComponentType)
590 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
591 ret = MIX_GetLineInfoDst(mix, lpMl, 0);
593 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
594 ret = MIX_GetLineInfoDst(mix, lpMl, 1);
596 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
597 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
599 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
600 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
602 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
603 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
605 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
606 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
608 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
609 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
611 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
612 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
615 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
616 return MMSYSERR_INVALPARAM;
619 case MIXER_GETLINEINFOF_TARGETTYPE:
620 FIXME("_TARGETTYPE not implemented yet.\n");
623 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
627 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
628 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
629 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
630 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
631 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
632 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
637 /******************************************************************
641 static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
643 return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
646 /**************************************************************************
647 * MIX_GetLineControls [internal]
649 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
651 DWORD dwRet = MMSYSERR_NOERROR;
654 TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
656 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
657 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
658 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
659 return MMSYSERR_INVALPARAM;
660 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
662 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
664 case MIXER_GETLINECONTROLSF_ALL:
668 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
670 for (i = j = 0; i < mix->numCtrl; i++)
672 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
675 if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
676 else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
679 for (i = j = 0; i < mix->numCtrl; i++)
681 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
683 TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
684 lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
690 case MIXER_GETLINECONTROLSF_ONEBYID:
691 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
693 if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
694 mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
695 dwRet = MMSYSERR_INVALPARAM;
697 lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
699 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
700 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
701 if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
705 DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
707 for (i = 0; i < mix->numCtrl; i++)
709 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
710 ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
712 lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
716 if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
720 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
721 dwRet = MMSYSERR_INVALPARAM;
727 /**************************************************************************
728 * MIX_GetControlDetails [internal]
730 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
732 DWORD ret = MMSYSERR_NOTSUPPORTED;
736 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
738 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
739 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
741 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
743 case MIXER_GETCONTROLDETAILSF_VALUE:
744 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
745 if (MIX_CheckControl(mix, lpmcd->dwControlID))
747 c = lpmcd->dwControlID - 1;
748 chnl = HIWORD(mix->ctrl[c].dwLineID);
749 if (chnl == LINEID_DST)
750 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
751 switch (mix->ctrl[c].ctrl.dwControlType)
753 case MIXERCONTROL_CONTROLTYPE_VOLUME:
755 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
758 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
759 /* return value is 00RL (4 bytes)... */
760 if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
761 return MMSYSERR_INVALPARAM;
763 switch (lpmcd->cChannels)
766 /* mono... so R = L */
767 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
768 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
771 /* stereo, left is paDetails[0] */
772 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
773 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
774 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
775 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
778 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
779 return MMSYSERR_INVALPARAM;
781 TRACE("=> %08lx\n", mcdu->dwValue);
784 case MIXERCONTROL_CONTROLTYPE_MUTE:
785 case MIXERCONTROL_CONTROLTYPE_ONOFF:
787 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
789 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
790 /* we mute both channels at the same time */
791 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
792 mcdb->fValue = (mix->volume[chnl] != -1);
793 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
796 case MIXERCONTROL_CONTROLTYPE_MIXER:
797 case MIXERCONTROL_CONTROLTYPE_MUX:
801 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
802 if (!MIX_GetRecSrc(mix, &mask))
804 /* FIXME: ENXIO => no mixer installed */
805 WARN("mixer device not available !\n");
806 ret = MMSYSERR_ERROR;
810 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
813 /* we mute both channels at the same time */
814 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
816 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
818 if (WINE_CHN_SUPPORTS(mix->recMask, j))
820 if (i >= lpmcd->u.cMultipleItems)
821 return MMSYSERR_INVALPARAM;
822 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
829 WARN("Unsupported\n");
831 ret = MMSYSERR_NOERROR;
835 ret = MMSYSERR_INVALPARAM;
838 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
839 TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
841 ret = MMSYSERR_INVALPARAM;
842 if (MIX_CheckControl(mix, lpmcd->dwControlID))
844 int c = lpmcd->dwControlID - 1;
846 if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
847 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
849 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
852 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
853 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
855 if (WINE_CHN_SUPPORTS(mix->recMask, j))
857 mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
858 mcdlt[i].dwParam2 = 0;
859 strcpy(mcdlt[i].szName, MIX_Names[j]);
863 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
864 ret = MMSYSERR_NOERROR;
869 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
874 /**************************************************************************
875 * MIX_SetControlDetails [internal]
877 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
879 DWORD ret = MMSYSERR_NOTSUPPORTED;
884 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
886 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
887 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
889 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
891 case MIXER_GETCONTROLDETAILSF_VALUE:
892 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
893 if (MIX_CheckControl(mix, lpmcd->dwControlID))
895 c = lpmcd->dwControlID - 1;
896 chnl = HIWORD(mix->ctrl[c].dwLineID);
897 if (chnl == LINEID_DST)
898 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
900 switch (mix->ctrl[c].ctrl.dwControlType)
902 case MIXERCONTROL_CONTROLTYPE_VOLUME:
904 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
906 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
907 /* val should contain 00RL */
908 switch (lpmcd->cChannels)
911 /* mono... so R = L */
912 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
913 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
914 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
917 /* stereo, left is paDetails[0] */
918 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
919 TRACE("Setting L to %08ld\n", mcdu->dwValue);
920 val = ((mcdu->dwValue * 100) >> 16);
921 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
922 TRACE("Setting R to %08ld\n", mcdu->dwValue);
923 val += ((mcdu->dwValue * 100) >> 16) << 8;
926 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
927 return MMSYSERR_INVALPARAM;
930 if (mix->volume[chnl] == -1)
932 if (!MIX_SetVal(mix, chnl, val))
933 return MMSYSERR_INVALPARAM;
937 mix->volume[chnl] = val;
940 ret = MMSYSERR_NOERROR;
942 case MIXERCONTROL_CONTROLTYPE_MUTE:
943 case MIXERCONTROL_CONTROLTYPE_ONOFF:
945 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
947 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
948 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
951 if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
952 return MMSYSERR_INVALPARAM;
956 if (mix->volume[chnl] == -1)
958 ret = MMSYSERR_NOERROR;
961 if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
962 return MMSYSERR_INVALPARAM;
963 mix->volume[chnl] = -1;
966 ret = MMSYSERR_NOERROR;
968 case MIXERCONTROL_CONTROLTYPE_MIXER:
969 case MIXERCONTROL_CONTROLTYPE_MUX:
971 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
975 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
976 /* we mute both channels at the same time */
977 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
979 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
981 if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
983 /* a mux can only select one line at a time... */
984 if (mix->singleRecChannel && mask != 0)
987 return MMSYSERR_INVALPARAM;
989 mask |= WINE_CHN_MASK(j);
992 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
993 TRACE("writing %04x as rec src\n", mask);
994 if (!MIX_SetRecSrc(mix, mask))
995 ERR("Can't write new mixer settings\n");
997 ret = MMSYSERR_NOERROR;
1004 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1009 /**************************************************************************
1010 * MIX_Init [internal]
1012 static DWORD MIX_Init(void)
1016 #define MIXER_DEV "/dev/mixer"
1017 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1019 if (errno == ENODEV || errno == ENXIO)
1021 /* no driver present */
1022 return MMSYSERR_NODRIVER;
1025 return MMSYSERR_ERROR;
1029 MIX_Mixers[0].name = MIXER_DEV;
1030 MIX_Open(0, NULL, 0); /* FIXME */
1032 return MMSYSERR_NOERROR;
1035 /**************************************************************************
1036 * MIX_GetNumDevs [internal]
1038 static DWORD MIX_GetNumDevs(void)
1040 return MIX_NumMixers;
1043 #endif /* HAVE_OSS */
1045 /**************************************************************************
1046 * mixMessage (WINEOSS.3)
1048 DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1049 DWORD dwParam1, DWORD dwParam2)
1051 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1061 /* FIXME: Pretend this is supported */
1063 case MXDM_GETDEVCAPS:
1064 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
1065 case MXDM_GETLINEINFO:
1066 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
1067 case MXDM_GETNUMDEVS:
1068 return MIX_GetNumDevs();
1070 return MMSYSERR_NOERROR;
1071 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1073 return MMSYSERR_NOERROR;
1074 case MXDM_GETLINECONTROLS:
1075 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
1076 case MXDM_GETCONTROLDETAILS:
1077 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1078 case MXDM_SETCONTROLDETAILS:
1079 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1081 WARN("unknown message %d!\n", wMsg);
1082 return MMSYSERR_NOTSUPPORTED;
1085 return MMSYSERR_NOTENABLED;