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
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.h>
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
48 #define WINE_MIXER_MANUF_ID 0xAA
49 #define WINE_MIXER_PRODUCT_ID 0x55
50 #define WINE_MIXER_VERSION 0x0100
51 #define WINE_MIXER_NAME "WINE OSS Mixer"
53 #define WINE_CHN_MASK(_x) (1L << (_x))
54 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
55 /* Bass and Treble are no longer in the mask as Windows does not handle them */
56 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
57 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
58 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
59 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
60 WINE_CHN_MASK(SOUND_MIXER_CD) )
62 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
63 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
64 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
65 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
67 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
68 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
69 static char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
70 static char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
81 int volume[SOUND_MIXER_NRDEVICES];
85 BOOL singleRecChannel;
86 struct mixerCtrl* ctrl;
90 #define LINEID_DST 0xFFFF
91 #define LINEID_SPEAKER 0x0000
92 #define LINEID_RECORD 0x0001
94 static int MIX_NumMixers;
95 static struct mixer MIX_Mixers[1];
97 /**************************************************************************
98 * MIX_FillLineControls [internal]
100 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
102 struct mixerCtrl* mc = &mix->ctrl[c];
105 mc->dwLineID = lineID;
106 mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
107 mc->ctrl.dwControlID = c + 1;
108 mc->ctrl.dwControlType = dwType;
112 case MIXERCONTROL_CONTROLTYPE_VOLUME:
113 mc->ctrl.fdwControl = 0;
114 mc->ctrl.cMultipleItems = 0;
115 lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
116 lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
117 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
118 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
119 * [0, 100] is the range supported by OSS
120 * whatever the min and max values are they must match
121 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
123 mc->ctrl.Bounds.s1.dwMinimum = 0;
124 mc->ctrl.Bounds.s1.dwMaximum = 65535;
125 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
127 case MIXERCONTROL_CONTROLTYPE_MUTE:
128 case MIXERCONTROL_CONTROLTYPE_ONOFF:
129 mc->ctrl.fdwControl = 0;
130 mc->ctrl.cMultipleItems = 0;
131 lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
132 lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
133 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
134 mc->ctrl.Bounds.s1.dwMinimum = 0;
135 mc->ctrl.Bounds.s1.dwMaximum = 1;
136 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
138 case MIXERCONTROL_CONTROLTYPE_MUX:
139 case MIXERCONTROL_CONTROLTYPE_MIXER:
140 mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
141 mc->ctrl.cMultipleItems = 0;
142 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
143 if (WINE_CHN_SUPPORTS(mix->recMask, j))
144 mc->ctrl.cMultipleItems++;
145 lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
146 lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
147 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
148 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
152 FIXME("Internal error: unknown type: %08lx\n", dwType);
154 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
157 /******************************************************************
162 static struct mixer* MIX_Get(WORD wDevID)
164 if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
165 return &MIX_Mixers[wDevID];
168 /**************************************************************************
169 * MIX_Open [internal]
171 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
176 DWORD ret = MMSYSERR_NOERROR;
178 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
180 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
181 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
182 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
183 * messages to it... it seems to be linked to all the equivalent of mixer identification
184 * (with a reference to a wave, midi.. handle
186 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
188 if ((mixer = open(mix->name, O_RDWR)) < 0)
190 if (errno == ENODEV || errno == ENXIO)
192 /* no driver present */
193 return MMSYSERR_NODRIVER;
195 return MMSYSERR_ERROR;
198 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
200 perror("ioctl mixer SOUND_MIXER_DEVMASK");
201 ret = MMSYSERR_ERROR;
204 mix->devMask &= WINE_MIXER_MASK_SPEAKER;
205 if (mix->devMask == 0)
207 ret = MMSYSERR_NODRIVER;
211 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
213 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
214 ret = MMSYSERR_ERROR;
217 mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
219 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
221 perror("ioctl mixer SOUND_MIXER_RECMASK");
222 ret = MMSYSERR_ERROR;
225 mix->recMask &= WINE_MIXER_MASK_RECORD;
226 /* FIXME: we may need to support both rec lev & igain */
227 if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
229 WARN("The sound card doesn't support rec level\n");
230 if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
231 WARN("but it does support IGain, please report\n");
233 if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
235 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
236 ret = MMSYSERR_ERROR;
239 mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
240 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
241 mix->devMask, mix->recMask, mix->stereoMask,
242 mix->singleRecChannel ? "single" : "multiple");
243 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
247 mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
248 /* FIXME: do we always have RECLEV on all cards ??? */
249 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
251 if (WINE_CHN_SUPPORTS(mix->devMask, i))
252 mix->numCtrl += 2; /* volume & mute */
253 if (WINE_CHN_SUPPORTS(mix->recMask, i))
254 mix->numCtrl += 2; /* volume & onoff */
257 if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
259 ret = MMSYSERR_NOMEM;
264 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
265 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
266 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
267 mix->singleRecChannel ?
268 MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
269 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
270 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
272 if (WINE_CHN_SUPPORTS(mix->devMask, i))
274 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
275 MIXERCONTROL_CONTROLTYPE_VOLUME);
276 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
277 MIXERCONTROL_CONTROLTYPE_MUTE);
280 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
282 if (WINE_CHN_SUPPORTS(mix->recMask, i))
284 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
285 MIXERCONTROL_CONTROLTYPE_VOLUME);
286 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
287 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
290 assert(j == mix->numCtrl);
296 /**************************************************************************
297 * MIX_GetVal [internal]
299 static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
304 if ((mixer = open(mix->name, O_RDWR)) < 0)
306 /* FIXME: ENXIO => no mixer installed */
307 WARN("mixer device not available !\n");
311 if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
313 TRACE("Reading volume %x on %d\n", *val, chn);
321 /**************************************************************************
322 * MIX_SetVal [internal]
324 static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
329 TRACE("Writing volume %x on %d\n", val, chn);
331 if ((mixer = open(mix->name, O_RDWR)) < 0)
333 /* FIXME: ENXIO => no mixer installed */
334 WARN("mixer device not available !\n");
338 if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
347 /******************************************************************
352 static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
357 if ((mixer = open(mix->name, O_RDWR)) >= 0)
359 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
365 /******************************************************************
370 static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
375 if ((mixer = open(mix->name, O_RDWR)) >= 0)
377 if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
379 ERR("Can't write new mixer settings\n");
388 /**************************************************************************
389 * MIX_GetDevCaps [internal]
391 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
395 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
397 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
398 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
400 lpCaps->wMid = WINE_MIXER_MANUF_ID;
401 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
402 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
403 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
405 lpCaps->cDestinations = 2; /* speakers & record */
406 lpCaps->fdwSupport = 0; /* No bits defined yet */
408 return MMSYSERR_NOERROR;
411 /**************************************************************************
412 * MIX_GetLineInfoDst [internal]
414 static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
419 lpMl->dwDestination = dst;
423 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
425 j = SOUND_MIXER_VOLUME;
428 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
430 j = SOUND_MIXER_RECLEV;
433 FIXME("shouldn't happen\n");
434 return MMSYSERR_ERROR;
436 lpMl->dwSource = 0xFFFFFFFF;
437 lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
438 lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
440 /* we have all connections found in the MIX_DevMask */
441 lpMl->cConnections = 0;
442 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
443 if (WINE_CHN_SUPPORTS(mask, j))
444 lpMl->cConnections++;
446 if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
448 lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
450 for (j = 0; j < mix->numCtrl; j++)
451 if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
454 return MMSYSERR_NOERROR;
457 /**************************************************************************
458 * MIX_GetLineInfoSrc [internal]
460 static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
463 unsigned mask = (dst) ? mix->recMask : mix->devMask;
465 strcpy(lpMl->szShortName, MIX_Labels[idx]);
466 strcpy(lpMl->szName, MIX_Names[idx]);
467 lpMl->dwLineID = MAKELONG(dst, idx);
468 lpMl->dwDestination = dst;
469 lpMl->cConnections = 1;
471 for (i = 0; i < mix->numCtrl; i++)
472 if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
477 case SOUND_MIXER_SYNTH:
478 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
479 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
482 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
483 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
485 case SOUND_MIXER_LINE:
486 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
487 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
489 case SOUND_MIXER_MIC:
490 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
491 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
493 case SOUND_MIXER_PCM:
494 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
495 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
497 case SOUND_MIXER_IMIX:
498 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
499 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
502 WARN("Index %ld not handled.\n", idx);
503 return MIXERR_INVALLINE;
506 if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
508 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
510 if (WINE_CHN_SUPPORTS(mask, j))
517 return MMSYSERR_NOERROR;
520 /******************************************************************
523 static BOOL MIX_CheckLine(DWORD lineID)
525 return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
526 (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
529 /**************************************************************************
530 * MIX_GetLineInfo [internal]
532 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
535 DWORD ret = MMSYSERR_NOERROR;
539 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
541 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
542 return MMSYSERR_INVALPARAM;
543 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
545 /* FIXME: set all the variables correctly... the lines below
548 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
551 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
553 case MIXER_GETLINEINFOF_DESTINATION:
554 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
555 if (lpMl->dwDestination >= 2)
556 return MMSYSERR_INVALPARAM;
557 if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
560 case MIXER_GETLINEINFOF_SOURCE:
561 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
562 switch (lpMl->dwDestination)
564 case 0: mask = mix->devMask; break;
565 case 1: mask = mix->recMask; break;
566 default: return MMSYSERR_INVALPARAM;
569 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
571 if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
574 if (j >= SOUND_MIXER_NRDEVICES)
575 return MIXERR_INVALLINE;
576 if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
579 case MIXER_GETLINEINFOF_LINEID:
580 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
582 if (!MIX_CheckLine(lpMl->dwLineID))
583 return MIXERR_INVALLINE;
584 if (HIWORD(lpMl->dwLineID) == LINEID_DST)
585 ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
587 ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
588 if (ret != MMSYSERR_NOERROR)
591 case MIXER_GETLINEINFOF_COMPONENTTYPE:
592 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
593 switch (lpMl->dwComponentType)
595 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
596 ret = MIX_GetLineInfoDst(mix, lpMl, 0);
598 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
599 ret = MIX_GetLineInfoDst(mix, lpMl, 1);
601 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
602 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
604 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
605 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
607 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
608 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
610 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
611 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
613 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
614 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
616 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
617 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
620 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
621 return MMSYSERR_INVALPARAM;
624 case MIXER_GETLINEINFOF_TARGETTYPE:
625 FIXME("_TARGETTYPE not implemented yet.\n");
628 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
632 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
633 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
634 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
635 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
636 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
637 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
642 /******************************************************************
646 static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
648 return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
651 /**************************************************************************
652 * MIX_GetLineControls [internal]
654 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
656 DWORD dwRet = MMSYSERR_NOERROR;
659 TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
661 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
662 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
663 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
664 return MMSYSERR_INVALPARAM;
665 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
667 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
669 case MIXER_GETLINECONTROLSF_ALL:
673 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
675 for (i = j = 0; i < mix->numCtrl; i++)
677 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
680 if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
681 else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
684 for (i = j = 0; i < mix->numCtrl; i++)
686 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
688 TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
689 lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
695 case MIXER_GETLINECONTROLSF_ONEBYID:
696 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
698 if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
699 mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
700 dwRet = MMSYSERR_INVALPARAM;
702 lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
704 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
705 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
706 if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
710 DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
712 for (i = 0; i < mix->numCtrl; i++)
714 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
715 ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
717 lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
721 if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
725 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
726 dwRet = MMSYSERR_INVALPARAM;
732 /**************************************************************************
733 * MIX_GetControlDetails [internal]
735 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
737 DWORD ret = MMSYSERR_NOTSUPPORTED;
741 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
743 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
744 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
746 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
748 case MIXER_GETCONTROLDETAILSF_VALUE:
749 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
750 if (MIX_CheckControl(mix, lpmcd->dwControlID))
752 c = lpmcd->dwControlID - 1;
753 chnl = HIWORD(mix->ctrl[c].dwLineID);
754 if (chnl == LINEID_DST)
755 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
756 switch (mix->ctrl[c].ctrl.dwControlType)
758 case MIXERCONTROL_CONTROLTYPE_VOLUME:
760 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
763 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
764 /* return value is 00RL (4 bytes)... */
765 if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
766 return MMSYSERR_INVALPARAM;
768 switch (lpmcd->cChannels)
771 /* mono... so R = L */
772 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
773 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
776 /* stereo, left is paDetails[0] */
777 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
778 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
779 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
780 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
783 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
784 return MMSYSERR_INVALPARAM;
786 TRACE("=> %08lx\n", mcdu->dwValue);
789 case MIXERCONTROL_CONTROLTYPE_MUTE:
790 case MIXERCONTROL_CONTROLTYPE_ONOFF:
792 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
794 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
795 /* we mute both channels at the same time */
796 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
797 mcdb->fValue = (mix->volume[chnl] != -1);
798 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
801 case MIXERCONTROL_CONTROLTYPE_MIXER:
802 case MIXERCONTROL_CONTROLTYPE_MUX:
806 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
807 if (!MIX_GetRecSrc(mix, &mask))
809 /* FIXME: ENXIO => no mixer installed */
810 WARN("mixer device not available !\n");
811 ret = MMSYSERR_ERROR;
815 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
818 /* we mute both channels at the same time */
819 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
821 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
823 if (WINE_CHN_SUPPORTS(mix->recMask, j))
825 if (i >= lpmcd->u.cMultipleItems)
826 return MMSYSERR_INVALPARAM;
827 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
834 WARN("Unsupported\n");
836 ret = MMSYSERR_NOERROR;
840 ret = MMSYSERR_INVALPARAM;
843 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
844 TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
846 ret = MMSYSERR_INVALPARAM;
847 if (MIX_CheckControl(mix, lpmcd->dwControlID))
849 int c = lpmcd->dwControlID - 1;
851 if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
852 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
854 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
857 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
858 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
860 if (WINE_CHN_SUPPORTS(mix->recMask, j))
862 mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
863 mcdlt[i].dwParam2 = 0;
864 strcpy(mcdlt[i].szName, MIX_Names[j]);
868 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
869 ret = MMSYSERR_NOERROR;
874 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
879 /**************************************************************************
880 * MIX_SetControlDetails [internal]
882 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
884 DWORD ret = MMSYSERR_NOTSUPPORTED;
889 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
891 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
892 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
894 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
896 case MIXER_GETCONTROLDETAILSF_VALUE:
897 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
898 if (MIX_CheckControl(mix, lpmcd->dwControlID))
900 c = lpmcd->dwControlID - 1;
901 chnl = HIWORD(mix->ctrl[c].dwLineID);
902 if (chnl == LINEID_DST)
903 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
905 switch (mix->ctrl[c].ctrl.dwControlType)
907 case MIXERCONTROL_CONTROLTYPE_VOLUME:
909 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
911 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
912 /* val should contain 00RL */
913 switch (lpmcd->cChannels)
916 /* mono... so R = L */
917 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
918 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
919 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
922 /* stereo, left is paDetails[0] */
923 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
924 TRACE("Setting L to %08ld\n", mcdu->dwValue);
925 val = ((mcdu->dwValue * 100) >> 16);
926 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
927 TRACE("Setting R to %08ld\n", mcdu->dwValue);
928 val += ((mcdu->dwValue * 100) >> 16) << 8;
931 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
932 return MMSYSERR_INVALPARAM;
935 if (mix->volume[chnl] == -1)
937 if (!MIX_SetVal(mix, chnl, val))
938 return MMSYSERR_INVALPARAM;
942 mix->volume[chnl] = val;
945 ret = MMSYSERR_NOERROR;
947 case MIXERCONTROL_CONTROLTYPE_MUTE:
948 case MIXERCONTROL_CONTROLTYPE_ONOFF:
950 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
952 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
953 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
956 if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
957 return MMSYSERR_INVALPARAM;
961 if (mix->volume[chnl] == -1)
963 ret = MMSYSERR_NOERROR;
966 if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
967 return MMSYSERR_INVALPARAM;
968 mix->volume[chnl] = -1;
971 ret = MMSYSERR_NOERROR;
973 case MIXERCONTROL_CONTROLTYPE_MIXER:
974 case MIXERCONTROL_CONTROLTYPE_MUX:
976 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
980 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
981 /* we mute both channels at the same time */
982 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
984 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
986 if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
988 /* a mux can only select one line at a time... */
989 if (mix->singleRecChannel && mask != 0)
992 return MMSYSERR_INVALPARAM;
994 mask |= WINE_CHN_MASK(j);
997 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
998 TRACE("writing %04x as rec src\n", mask);
999 if (!MIX_SetRecSrc(mix, mask))
1000 ERR("Can't write new mixer settings\n");
1002 ret = MMSYSERR_NOERROR;
1009 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1014 /**************************************************************************
1015 * MIX_Init [internal]
1017 static DWORD MIX_Init(void)
1021 #define MIXER_DEV "/dev/mixer"
1022 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1024 if (errno == ENODEV || errno == ENXIO)
1026 /* no driver present */
1027 return MMSYSERR_NODRIVER;
1030 return MMSYSERR_ERROR;
1034 MIX_Mixers[0].name = MIXER_DEV;
1035 MIX_Open(0, NULL, 0); /* FIXME */
1037 return MMSYSERR_NOERROR;
1040 /**************************************************************************
1041 * MIX_GetNumDevs [internal]
1043 static DWORD MIX_GetNumDevs(void)
1045 return MIX_NumMixers;
1048 #endif /* HAVE_OSS */
1050 /**************************************************************************
1051 * mxdMessage (WINEOSS.3)
1053 DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1054 DWORD dwParam1, DWORD dwParam2)
1056 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1066 /* FIXME: Pretend this is supported */
1068 case MXDM_GETDEVCAPS:
1069 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
1070 case MXDM_GETLINEINFO:
1071 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
1072 case MXDM_GETNUMDEVS:
1073 return MIX_GetNumDevs();
1075 return MMSYSERR_NOERROR;
1076 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1078 return MMSYSERR_NOERROR;
1079 case MXDM_GETLINECONTROLS:
1080 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
1081 case MXDM_GETCONTROLDETAILS:
1082 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1083 case MXDM_SETCONTROLDETAILS:
1084 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1086 WARN("unknown message %d!\n", wMsg);
1087 return MMSYSERR_NOTSUPPORTED;
1090 return MMSYSERR_NOTENABLED;