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 #ifdef HAVE_SYS_IOCTL_H
34 # include <sys/ioctl.h>
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
46 #define WINE_MIXER_MANUF_ID 0xAA
47 #define WINE_MIXER_PRODUCT_ID 0x55
48 #define WINE_MIXER_VERSION 0x0100
49 #define WINE_MIXER_NAME "WINE OSS Mixer"
51 #define WINE_CHN_MASK(_x) (1L << (_x))
52 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
53 /* Bass and Treble are no longer in the mask as Windows does not handle them */
54 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
55 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
56 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
57 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
58 WINE_CHN_MASK(SOUND_MIXER_CD) )
60 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
61 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
62 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
63 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
65 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
66 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
67 static char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
68 static char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
79 int volume[SOUND_MIXER_NRDEVICES];
83 BOOL singleRecChannel;
84 struct mixerCtrl* ctrl;
88 #define LINEID_DST 0xFFFF
89 #define LINEID_SPEAKER 0x0000
90 #define LINEID_RECORD 0x0001
92 static int MIX_NumMixers;
93 static struct mixer MIX_Mixers[1];
95 /**************************************************************************
96 * MIX_FillLineControls [internal]
98 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
100 struct mixerCtrl* mc = &mix->ctrl[c];
103 mc->dwLineID = lineID;
104 mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
105 mc->ctrl.dwControlID = c + 1;
106 mc->ctrl.dwControlType = dwType;
110 case MIXERCONTROL_CONTROLTYPE_VOLUME:
111 mc->ctrl.fdwControl = 0;
112 mc->ctrl.cMultipleItems = 0;
113 lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
114 lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
115 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
116 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
117 * [0, 100] is the range supported by OSS
118 * whatever the min and max values are they must match
119 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
121 mc->ctrl.Bounds.s1.dwMinimum = 0;
122 mc->ctrl.Bounds.s1.dwMaximum = 65535;
123 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
125 case MIXERCONTROL_CONTROLTYPE_MUTE:
126 case MIXERCONTROL_CONTROLTYPE_ONOFF:
127 mc->ctrl.fdwControl = 0;
128 mc->ctrl.cMultipleItems = 0;
129 lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
130 lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
131 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
132 mc->ctrl.Bounds.s1.dwMinimum = 0;
133 mc->ctrl.Bounds.s1.dwMaximum = 1;
134 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
136 case MIXERCONTROL_CONTROLTYPE_MUX:
137 case MIXERCONTROL_CONTROLTYPE_MIXER:
138 mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
139 mc->ctrl.cMultipleItems = 0;
140 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
141 if (WINE_CHN_SUPPORTS(mix->recMask, j))
142 mc->ctrl.cMultipleItems++;
143 lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
144 lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
145 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
146 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
150 FIXME("Internal error: unknown type: %08lx\n", dwType);
152 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
155 /******************************************************************
160 static struct mixer* MIX_Get(WORD wDevID)
162 if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
163 return &MIX_Mixers[wDevID];
166 /**************************************************************************
167 * MIX_Open [internal]
169 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
174 DWORD ret = MMSYSERR_NOERROR;
176 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
178 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
179 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
180 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
181 * messages to it... it seems to be linked to all the equivalent of mixer identification
182 * (with a reference to a wave, midi.. handle
184 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
186 if ((mixer = open(mix->name, O_RDWR)) < 0)
188 if (errno == ENODEV || errno == ENXIO)
190 /* no driver present */
191 return MMSYSERR_NODRIVER;
193 return MMSYSERR_ERROR;
196 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
198 perror("ioctl mixer SOUND_MIXER_DEVMASK");
199 ret = MMSYSERR_ERROR;
202 mix->devMask &= WINE_MIXER_MASK_SPEAKER;
203 if (mix->devMask == 0)
205 ret = MMSYSERR_NODRIVER;
209 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
211 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
212 ret = MMSYSERR_ERROR;
215 mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
217 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
219 perror("ioctl mixer SOUND_MIXER_RECMASK");
220 ret = MMSYSERR_ERROR;
223 mix->recMask &= WINE_MIXER_MASK_RECORD;
224 /* FIXME: we may need to support both rec lev & igain */
225 if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
227 WARN("The sound card doesn't support rec level\n");
228 if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
229 WARN("but it does support IGain, please report\n");
231 if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
233 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
234 ret = MMSYSERR_ERROR;
237 mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
238 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
239 mix->devMask, mix->recMask, mix->stereoMask,
240 mix->singleRecChannel ? "single" : "multiple");
241 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
245 mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
246 /* FIXME: do we always have RECLEV on all cards ??? */
247 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
249 if (WINE_CHN_SUPPORTS(mix->devMask, i))
250 mix->numCtrl += 2; /* volume & mute */
251 if (WINE_CHN_SUPPORTS(mix->recMask, i))
252 mix->numCtrl += 2; /* volume & onoff */
255 if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
257 ret = MMSYSERR_NOMEM;
262 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
263 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
264 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
265 mix->singleRecChannel ?
266 MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
267 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
268 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
270 if (WINE_CHN_SUPPORTS(mix->devMask, i))
272 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
273 MIXERCONTROL_CONTROLTYPE_VOLUME);
274 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
275 MIXERCONTROL_CONTROLTYPE_MUTE);
278 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
280 if (WINE_CHN_SUPPORTS(mix->recMask, i))
282 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
283 MIXERCONTROL_CONTROLTYPE_VOLUME);
284 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
285 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
288 assert(j == mix->numCtrl);
294 /**************************************************************************
295 * MIX_GetVal [internal]
297 static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
302 if ((mixer = open(mix->name, O_RDWR)) < 0)
304 /* FIXME: ENXIO => no mixer installed */
305 WARN("mixer device not available !\n");
309 if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
311 TRACE("Reading volume %x on %d\n", *val, chn);
319 /**************************************************************************
320 * MIX_SetVal [internal]
322 static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
327 TRACE("Writing volume %x on %d\n", val, chn);
329 if ((mixer = open(mix->name, O_RDWR)) < 0)
331 /* FIXME: ENXIO => no mixer installed */
332 WARN("mixer device not available !\n");
336 if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
345 /******************************************************************
350 static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
355 if ((mixer = open(mix->name, O_RDWR)) >= 0)
357 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
363 /******************************************************************
368 static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
373 if ((mixer = open(mix->name, O_RDWR)) >= 0)
375 if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
377 ERR("Can't write new mixer settings\n");
386 /**************************************************************************
387 * MIX_GetDevCaps [internal]
389 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
393 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
395 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
396 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
398 lpCaps->wMid = WINE_MIXER_MANUF_ID;
399 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
400 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
401 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
403 lpCaps->cDestinations = 2; /* speakers & record */
404 lpCaps->fdwSupport = 0; /* No bits defined yet */
406 return MMSYSERR_NOERROR;
409 /**************************************************************************
410 * MIX_GetLineInfoDst [internal]
412 static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
417 lpMl->dwDestination = dst;
421 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
423 j = SOUND_MIXER_VOLUME;
426 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
428 j = SOUND_MIXER_RECLEV;
431 FIXME("shouldn't happen\n");
432 return MMSYSERR_ERROR;
434 lpMl->dwSource = 0xFFFFFFFF;
435 lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
436 lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
438 /* we have all connections found in the MIX_DevMask */
439 lpMl->cConnections = 0;
440 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
441 if (WINE_CHN_SUPPORTS(mask, j))
442 lpMl->cConnections++;
444 if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
446 lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
448 for (j = 0; j < mix->numCtrl; j++)
449 if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
452 return MMSYSERR_NOERROR;
455 /**************************************************************************
456 * MIX_GetLineInfoSrc [internal]
458 static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
461 unsigned mask = (dst) ? mix->recMask : mix->devMask;
463 strcpy(lpMl->szShortName, MIX_Labels[idx]);
464 strcpy(lpMl->szName, MIX_Names[idx]);
465 lpMl->dwLineID = MAKELONG(dst, idx);
466 lpMl->dwDestination = dst;
467 lpMl->cConnections = 1;
469 for (i = 0; i < mix->numCtrl; i++)
470 if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
475 case SOUND_MIXER_SYNTH:
476 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
477 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
480 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
481 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
483 case SOUND_MIXER_LINE:
484 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
485 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
487 case SOUND_MIXER_MIC:
488 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
489 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
491 case SOUND_MIXER_PCM:
492 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
493 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
495 case SOUND_MIXER_IMIX:
496 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
497 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
500 WARN("Index %ld not handled.\n", idx);
501 return MIXERR_INVALLINE;
504 if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
506 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
508 if (WINE_CHN_SUPPORTS(mask, j))
515 return MMSYSERR_NOERROR;
518 /******************************************************************
521 static BOOL MIX_CheckLine(DWORD lineID)
523 return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
524 (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
527 /**************************************************************************
528 * MIX_GetLineInfo [internal]
530 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
533 DWORD ret = MMSYSERR_NOERROR;
537 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
539 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
540 return MMSYSERR_INVALPARAM;
541 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
543 /* FIXME: set all the variables correctly... the lines below
546 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
549 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
551 case MIXER_GETLINEINFOF_DESTINATION:
552 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
553 if (lpMl->dwDestination >= 2)
554 return MMSYSERR_INVALPARAM;
555 if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
558 case MIXER_GETLINEINFOF_SOURCE:
559 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
560 switch (lpMl->dwDestination)
562 case 0: mask = mix->devMask; break;
563 case 1: mask = mix->recMask; break;
564 default: return MMSYSERR_INVALPARAM;
567 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
569 if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
572 if (j >= SOUND_MIXER_NRDEVICES)
573 return MIXERR_INVALLINE;
574 if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
577 case MIXER_GETLINEINFOF_LINEID:
578 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
580 if (!MIX_CheckLine(lpMl->dwLineID))
581 return MIXERR_INVALLINE;
582 if (HIWORD(lpMl->dwLineID) == LINEID_DST)
583 ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
585 ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
586 if (ret != MMSYSERR_NOERROR)
589 case MIXER_GETLINEINFOF_COMPONENTTYPE:
590 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
591 switch (lpMl->dwComponentType)
593 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
594 ret = MIX_GetLineInfoDst(mix, lpMl, 0);
596 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
597 ret = MIX_GetLineInfoDst(mix, lpMl, 1);
599 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
600 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
602 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
603 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
605 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
606 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
608 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
609 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
611 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
612 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
614 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
615 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
618 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
619 return MMSYSERR_INVALPARAM;
622 case MIXER_GETLINEINFOF_TARGETTYPE:
623 FIXME("_TARGETTYPE not implemented yet.\n");
626 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
630 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
631 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
632 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
633 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
634 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
635 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
640 /******************************************************************
644 static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
646 return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
649 /**************************************************************************
650 * MIX_GetLineControls [internal]
652 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
654 DWORD dwRet = MMSYSERR_NOERROR;
657 TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
659 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
660 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
661 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
662 return MMSYSERR_INVALPARAM;
663 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
665 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
667 case MIXER_GETLINECONTROLSF_ALL:
671 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
673 for (i = j = 0; i < mix->numCtrl; i++)
675 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
678 if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
679 else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
682 for (i = j = 0; i < mix->numCtrl; i++)
684 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
686 TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
687 lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
693 case MIXER_GETLINECONTROLSF_ONEBYID:
694 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
696 if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
697 mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
698 dwRet = MMSYSERR_INVALPARAM;
700 lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
702 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
703 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
704 if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
708 DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
710 for (i = 0; i < mix->numCtrl; i++)
712 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
713 ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
715 lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
719 if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
723 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
724 dwRet = MMSYSERR_INVALPARAM;
730 /**************************************************************************
731 * MIX_GetControlDetails [internal]
733 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
735 DWORD ret = MMSYSERR_NOTSUPPORTED;
739 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
741 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
742 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
744 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
746 case MIXER_GETCONTROLDETAILSF_VALUE:
747 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
748 if (MIX_CheckControl(mix, lpmcd->dwControlID))
750 c = lpmcd->dwControlID - 1;
751 chnl = HIWORD(mix->ctrl[c].dwLineID);
752 if (chnl == LINEID_DST)
753 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
754 switch (mix->ctrl[c].ctrl.dwControlType)
756 case MIXERCONTROL_CONTROLTYPE_VOLUME:
758 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
761 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
762 /* return value is 00RL (4 bytes)... */
763 if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
764 return MMSYSERR_INVALPARAM;
766 switch (lpmcd->cChannels)
769 /* mono... so R = L */
770 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
771 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
774 /* stereo, left is paDetails[0] */
775 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
776 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
777 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
778 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
781 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
782 return MMSYSERR_INVALPARAM;
784 TRACE("=> %08lx\n", mcdu->dwValue);
787 case MIXERCONTROL_CONTROLTYPE_MUTE:
788 case MIXERCONTROL_CONTROLTYPE_ONOFF:
790 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
792 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
793 /* we mute both channels at the same time */
794 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
795 mcdb->fValue = (mix->volume[chnl] != -1);
796 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
799 case MIXERCONTROL_CONTROLTYPE_MIXER:
800 case MIXERCONTROL_CONTROLTYPE_MUX:
804 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
805 if (!MIX_GetRecSrc(mix, &mask))
807 /* FIXME: ENXIO => no mixer installed */
808 WARN("mixer device not available !\n");
809 ret = MMSYSERR_ERROR;
813 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
816 /* we mute both channels at the same time */
817 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
819 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
821 if (WINE_CHN_SUPPORTS(mix->recMask, j))
823 if (i >= lpmcd->u.cMultipleItems)
824 return MMSYSERR_INVALPARAM;
825 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
832 WARN("Unsupported\n");
834 ret = MMSYSERR_NOERROR;
838 ret = MMSYSERR_INVALPARAM;
841 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
842 TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
844 ret = MMSYSERR_INVALPARAM;
845 if (MIX_CheckControl(mix, lpmcd->dwControlID))
847 int c = lpmcd->dwControlID - 1;
849 if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
850 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
852 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
855 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
856 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
858 if (WINE_CHN_SUPPORTS(mix->recMask, j))
860 mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
861 mcdlt[i].dwParam2 = 0;
862 strcpy(mcdlt[i].szName, MIX_Names[j]);
866 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
867 ret = MMSYSERR_NOERROR;
872 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
877 /**************************************************************************
878 * MIX_SetControlDetails [internal]
880 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
882 DWORD ret = MMSYSERR_NOTSUPPORTED;
887 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
889 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
890 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
892 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
894 case MIXER_GETCONTROLDETAILSF_VALUE:
895 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
896 if (MIX_CheckControl(mix, lpmcd->dwControlID))
898 c = lpmcd->dwControlID - 1;
899 chnl = HIWORD(mix->ctrl[c].dwLineID);
900 if (chnl == LINEID_DST)
901 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
903 switch (mix->ctrl[c].ctrl.dwControlType)
905 case MIXERCONTROL_CONTROLTYPE_VOLUME:
907 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
909 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
910 /* val should contain 00RL */
911 switch (lpmcd->cChannels)
914 /* mono... so R = L */
915 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
916 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
917 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
920 /* stereo, left is paDetails[0] */
921 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
922 TRACE("Setting L to %08ld\n", mcdu->dwValue);
923 val = ((mcdu->dwValue * 100) >> 16);
924 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
925 TRACE("Setting R to %08ld\n", mcdu->dwValue);
926 val += ((mcdu->dwValue * 100) >> 16) << 8;
929 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
930 return MMSYSERR_INVALPARAM;
933 if (mix->volume[chnl] == -1)
935 if (!MIX_SetVal(mix, chnl, val))
936 return MMSYSERR_INVALPARAM;
940 mix->volume[chnl] = val;
943 ret = MMSYSERR_NOERROR;
945 case MIXERCONTROL_CONTROLTYPE_MUTE:
946 case MIXERCONTROL_CONTROLTYPE_ONOFF:
948 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
950 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
951 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
954 if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
955 return MMSYSERR_INVALPARAM;
959 if (mix->volume[chnl] == -1)
961 ret = MMSYSERR_NOERROR;
964 if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
965 return MMSYSERR_INVALPARAM;
966 mix->volume[chnl] = -1;
969 ret = MMSYSERR_NOERROR;
971 case MIXERCONTROL_CONTROLTYPE_MIXER:
972 case MIXERCONTROL_CONTROLTYPE_MUX:
974 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
978 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
979 /* we mute both channels at the same time */
980 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
982 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
984 if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
986 /* a mux can only select one line at a time... */
987 if (mix->singleRecChannel && mask != 0)
990 return MMSYSERR_INVALPARAM;
992 mask |= WINE_CHN_MASK(j);
995 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
996 TRACE("writing %04x as rec src\n", mask);
997 if (!MIX_SetRecSrc(mix, mask))
998 ERR("Can't write new mixer settings\n");
1000 ret = MMSYSERR_NOERROR;
1007 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1012 /**************************************************************************
1013 * MIX_Init [internal]
1015 static DWORD MIX_Init(void)
1019 #define MIXER_DEV "/dev/mixer"
1020 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1022 if (errno == ENODEV || errno == ENXIO)
1024 /* no driver present */
1025 return MMSYSERR_NODRIVER;
1028 return MMSYSERR_ERROR;
1032 MIX_Mixers[0].name = MIXER_DEV;
1033 MIX_Open(0, NULL, 0); /* FIXME */
1035 return MMSYSERR_NOERROR;
1038 /**************************************************************************
1039 * MIX_GetNumDevs [internal]
1041 static DWORD MIX_GetNumDevs(void)
1043 return MIX_NumMixers;
1046 #endif /* HAVE_OSS */
1048 /**************************************************************************
1049 * mixMessage (WINEOSS.3)
1051 DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1052 DWORD dwParam1, DWORD dwParam2)
1054 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1064 /* FIXME: Pretend this is supported */
1066 case MXDM_GETDEVCAPS:
1067 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
1068 case MXDM_GETLINEINFO:
1069 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
1070 case MXDM_GETNUMDEVS:
1071 return MIX_GetNumDevs();
1073 return MMSYSERR_NOERROR;
1074 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1076 return MMSYSERR_NOERROR;
1077 case MXDM_GETLINECONTROLS:
1078 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
1079 case MXDM_GETCONTROLDETAILS:
1080 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1081 case MXDM_SETCONTROLDETAILS:
1082 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1084 WARN("unknown message %d!\n", wMsg);
1085 return MMSYSERR_NOTSUPPORTED;
1088 return MMSYSERR_NOTENABLED;