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
36 #ifdef HAVE_SYS_IOCTL_H
37 # include <sys/ioctl.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(mmaux
);
52 #define WINE_MIXER_MANUF_ID 0xAA
53 #define WINE_MIXER_PRODUCT_ID 0x55
54 #define WINE_MIXER_VERSION 0x0100
55 #define WINE_MIXER_NAME "WINE OSS Mixer"
57 #define WINE_CHN_MASK(_x) (1L << (_x))
58 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
59 /* Bass and Treble are no longer in the mask as Windows does not handle them */
60 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
61 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
62 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
63 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
64 WINE_CHN_MASK(SOUND_MIXER_CD) )
66 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
67 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
68 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
69 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
71 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
72 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
73 static const char* MIX_Labels
[SOUND_MIXER_NRDEVICES
] = SOUND_DEVICE_LABELS
;
74 static const char* MIX_Names
[SOUND_MIXER_NRDEVICES
] = SOUND_DEVICE_NAMES
;
85 int volume
[SOUND_MIXER_NRDEVICES
];
89 BOOL singleRecChannel
;
90 struct mixerCtrl
* ctrl
;
94 #define LINEID_DST 0xFFFF
95 #define LINEID_SPEAKER 0x0000
96 #define LINEID_RECORD 0x0001
98 static int MIX_NumMixers
;
99 static struct mixer MIX_Mixers
[1];
101 /**************************************************************************
102 * MIX_FillLineControls [internal]
104 static void MIX_FillLineControls(struct mixer
* mix
, int c
, DWORD lineID
, DWORD dwType
)
106 struct mixerCtrl
* mc
= &mix
->ctrl
[c
];
109 mc
->dwLineID
= lineID
;
110 mc
->ctrl
.cbStruct
= sizeof(MIXERCONTROLA
);
111 mc
->ctrl
.dwControlID
= c
+ 1;
112 mc
->ctrl
.dwControlType
= dwType
;
116 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
117 mc
->ctrl
.fdwControl
= 0;
118 mc
->ctrl
.cMultipleItems
= 0;
119 lstrcpynA(mc
->ctrl
.szShortName
, "Vol", MIXER_SHORT_NAME_CHARS
);
120 lstrcpynA(mc
->ctrl
.szName
, "Volume", MIXER_LONG_NAME_CHARS
);
121 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
122 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
123 * [0, 100] is the range supported by OSS
124 * whatever the min and max values are they must match
125 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
127 mc
->ctrl
.Bounds
.s1
.dwMinimum
= 0;
128 mc
->ctrl
.Bounds
.s1
.dwMaximum
= 65535;
129 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
131 case MIXERCONTROL_CONTROLTYPE_MUTE
:
132 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
133 mc
->ctrl
.fdwControl
= 0;
134 mc
->ctrl
.cMultipleItems
= 0;
135 lstrcpynA(mc
->ctrl
.szShortName
, "Mute", MIXER_SHORT_NAME_CHARS
);
136 lstrcpynA(mc
->ctrl
.szName
, "Mute", MIXER_LONG_NAME_CHARS
);
137 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
138 mc
->ctrl
.Bounds
.s1
.dwMinimum
= 0;
139 mc
->ctrl
.Bounds
.s1
.dwMaximum
= 1;
140 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
142 case MIXERCONTROL_CONTROLTYPE_MUX
:
143 case MIXERCONTROL_CONTROLTYPE_MIXER
:
144 mc
->ctrl
.fdwControl
= MIXERCONTROL_CONTROLF_MULTIPLE
;
145 mc
->ctrl
.cMultipleItems
= 0;
146 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
147 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
148 mc
->ctrl
.cMultipleItems
++;
149 lstrcpynA(mc
->ctrl
.szShortName
, "Mixer", MIXER_SHORT_NAME_CHARS
);
150 lstrcpynA(mc
->ctrl
.szName
, "Mixer", MIXER_LONG_NAME_CHARS
);
151 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
152 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
156 FIXME("Internal error: unknown type: %08lx\n", dwType
);
158 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c
+ 1, dwType
, lineID
);
161 /******************************************************************
166 static struct mixer
* MIX_Get(WORD wDevID
)
168 if (wDevID
>= MIX_NumMixers
|| MIX_Mixers
[wDevID
].name
== NULL
) return NULL
;
169 return &MIX_Mixers
[wDevID
];
172 /**************************************************************************
173 * MIX_Open [internal]
175 static DWORD
MIX_Open(WORD wDevID
, LPMIXEROPENDESC lpMod
, DWORD flags
)
180 DWORD ret
= MMSYSERR_NOERROR
;
182 TRACE("(%04X, %p, %lu);\n", wDevID
, lpMod
, flags
);
184 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
185 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
186 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
187 * messages to it... it seems to be linked to all the equivalent of mixer identification
188 * (with a reference to a wave, midi.. handle
190 if (!(mix
= MIX_Get(wDevID
))) return MMSYSERR_BADDEVICEID
;
192 if ((mixer
= open(mix
->name
, O_RDWR
)) < 0)
194 if (errno
== ENODEV
|| errno
== ENXIO
)
196 /* no driver present */
197 return MMSYSERR_NODRIVER
;
199 return MMSYSERR_ERROR
;
202 if (ioctl(mixer
, SOUND_MIXER_READ_DEVMASK
, &mix
->devMask
) == -1)
204 perror("ioctl mixer SOUND_MIXER_DEVMASK");
205 ret
= MMSYSERR_ERROR
;
208 mix
->devMask
&= WINE_MIXER_MASK_SPEAKER
;
209 if (mix
->devMask
== 0)
211 ret
= MMSYSERR_NODRIVER
;
215 if (ioctl(mixer
, SOUND_MIXER_READ_STEREODEVS
, &mix
->stereoMask
) == -1)
217 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
218 ret
= MMSYSERR_ERROR
;
221 mix
->stereoMask
&= WINE_MIXER_MASK_SPEAKER
;
223 if (ioctl(mixer
, SOUND_MIXER_READ_RECMASK
, &mix
->recMask
) == -1)
225 perror("ioctl mixer SOUND_MIXER_RECMASK");
226 ret
= MMSYSERR_ERROR
;
229 mix
->recMask
&= WINE_MIXER_MASK_RECORD
;
230 /* FIXME: we may need to support both rec lev & igain */
231 if (!WINE_CHN_SUPPORTS(mix
->recMask
, SOUND_MIXER_RECLEV
))
233 WARN("The sound card doesn't support rec level\n");
234 if (WINE_CHN_SUPPORTS(mix
->recMask
, SOUND_MIXER_IGAIN
))
235 WARN("but it does support IGain, please report\n");
237 if (ioctl(mixer
, SOUND_MIXER_READ_CAPS
, &caps
) == -1)
239 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
240 ret
= MMSYSERR_ERROR
;
243 mix
->singleRecChannel
= caps
& SOUND_CAP_EXCL_INPUT
;
244 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
245 mix
->devMask
, mix
->recMask
, mix
->stereoMask
,
246 mix
->singleRecChannel
? "single" : "multiple");
247 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
251 mix
->numCtrl
= 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
252 /* FIXME: do we always have RECLEV on all cards ??? */
253 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
255 if (WINE_CHN_SUPPORTS(mix
->devMask
, i
))
256 mix
->numCtrl
+= 2; /* volume & mute */
257 if (WINE_CHN_SUPPORTS(mix
->recMask
, i
))
258 mix
->numCtrl
+= 2; /* volume & onoff */
261 if (!(mix
->ctrl
= HeapAlloc(GetProcessHeap(), 0, sizeof(mix
->ctrl
[0]) * mix
->numCtrl
)))
263 ret
= MMSYSERR_NOMEM
;
268 MIX_FillLineControls(mix
, j
++, MAKELONG(0, LINEID_DST
), MIXERCONTROL_CONTROLTYPE_VOLUME
);
269 MIX_FillLineControls(mix
, j
++, MAKELONG(0, LINEID_DST
), MIXERCONTROL_CONTROLTYPE_MUTE
);
270 MIX_FillLineControls(mix
, j
++, MAKELONG(1, LINEID_DST
),
271 mix
->singleRecChannel
?
272 MIXERCONTROL_CONTROLTYPE_MUX
: MIXERCONTROL_CONTROLTYPE_MIXER
);
273 MIX_FillLineControls(mix
, j
++, MAKELONG(1, LINEID_DST
), MIXERCONTROL_CONTROLTYPE_MUTE
/*EPP*/);
274 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
276 if (WINE_CHN_SUPPORTS(mix
->devMask
, i
))
278 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_SPEAKER
, i
),
279 MIXERCONTROL_CONTROLTYPE_VOLUME
);
280 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_SPEAKER
, i
),
281 MIXERCONTROL_CONTROLTYPE_MUTE
);
284 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
286 if (WINE_CHN_SUPPORTS(mix
->recMask
, i
))
288 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_RECORD
, i
),
289 MIXERCONTROL_CONTROLTYPE_VOLUME
);
290 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_RECORD
, i
),
291 MIXERCONTROL_CONTROLTYPE_MUTE
/*EPP*/);
294 assert(j
== mix
->numCtrl
);
300 /**************************************************************************
301 * MIX_GetVal [internal]
303 static BOOL
MIX_GetVal(struct mixer
* mix
, int chn
, int* val
)
308 if ((mixer
= open(mix
->name
, O_RDWR
)) < 0)
310 /* FIXME: ENXIO => no mixer installed */
311 WARN("mixer device not available !\n");
315 if (ioctl(mixer
, MIXER_READ(chn
), val
) >= 0)
317 TRACE("Reading volume %x on %d\n", *val
, chn
);
325 /**************************************************************************
326 * MIX_SetVal [internal]
328 static BOOL
MIX_SetVal(struct mixer
* mix
, int chn
, int val
)
333 TRACE("Writing volume %x on %d\n", val
, chn
);
335 if ((mixer
= open(mix
->name
, O_RDWR
)) < 0)
337 /* FIXME: ENXIO => no mixer installed */
338 WARN("mixer device not available !\n");
342 if (ioctl(mixer
, MIXER_WRITE(chn
), &val
) >= 0)
351 /******************************************************************
356 static BOOL
MIX_GetRecSrc(struct mixer
* mix
, unsigned* mask
)
361 if ((mixer
= open(mix
->name
, O_RDWR
)) >= 0)
363 if (ioctl(mixer
, SOUND_MIXER_READ_RECSRC
, &mask
) >= 0) ret
= TRUE
;
369 /******************************************************************
374 static BOOL
MIX_SetRecSrc(struct mixer
* mix
, unsigned mask
)
379 if ((mixer
= open(mix
->name
, O_RDWR
)) >= 0)
381 if (ioctl(mixer
, SOUND_MIXER_WRITE_RECSRC
, &mask
) < 0)
383 ERR("Can't write new mixer settings\n");
392 /**************************************************************************
393 * MIX_GetDevCaps [internal]
395 static DWORD
MIX_GetDevCaps(WORD wDevID
, LPMIXERCAPSA lpCaps
, DWORD dwSize
)
399 TRACE("(%04X, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
401 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
402 if (!(mix
= MIX_Get(wDevID
))) return MMSYSERR_BADDEVICEID
;
404 lpCaps
->wMid
= WINE_MIXER_MANUF_ID
;
405 lpCaps
->wPid
= WINE_MIXER_PRODUCT_ID
;
406 lpCaps
->vDriverVersion
= WINE_MIXER_VERSION
;
407 strcpy(lpCaps
->szPname
, WINE_MIXER_NAME
);
409 lpCaps
->cDestinations
= 2; /* speakers & record */
410 lpCaps
->fdwSupport
= 0; /* No bits defined yet */
412 return MMSYSERR_NOERROR
;
415 /**************************************************************************
416 * MIX_GetLineInfoDst [internal]
418 static DWORD
MIX_GetLineInfoDst(struct mixer
* mix
, LPMIXERLINEA lpMl
, DWORD dst
)
423 lpMl
->dwDestination
= dst
;
427 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
429 j
= SOUND_MIXER_VOLUME
;
432 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_WAVEIN
;
434 j
= SOUND_MIXER_RECLEV
;
437 FIXME("shouldn't happen\n");
438 return MMSYSERR_ERROR
;
440 lpMl
->dwSource
= 0xFFFFFFFF;
441 lstrcpynA(lpMl
->szShortName
, MIX_Labels
[j
], MIXER_SHORT_NAME_CHARS
);
442 lstrcpynA(lpMl
->szName
, MIX_Names
[j
], MIXER_LONG_NAME_CHARS
);
444 /* we have all connections found in the MIX_DevMask */
445 lpMl
->cConnections
= 0;
446 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
447 if (WINE_CHN_SUPPORTS(mask
, j
))
448 lpMl
->cConnections
++;
450 if (WINE_CHN_SUPPORTS(mix
->stereoMask
, lpMl
->dwLineID
))
452 lpMl
->dwLineID
= MAKELONG(dst
, LINEID_DST
);
454 for (j
= 0; j
< mix
->numCtrl
; j
++)
455 if (mix
->ctrl
[j
].dwLineID
== lpMl
->dwLineID
)
458 return MMSYSERR_NOERROR
;
461 /**************************************************************************
462 * MIX_GetLineInfoSrc [internal]
464 static DWORD
MIX_GetLineInfoSrc(struct mixer
* mix
, LPMIXERLINEA lpMl
, DWORD idx
, DWORD dst
)
467 unsigned mask
= (dst
) ? mix
->recMask
: mix
->devMask
;
469 strcpy(lpMl
->szShortName
, MIX_Labels
[idx
]);
470 strcpy(lpMl
->szName
, MIX_Names
[idx
]);
471 lpMl
->dwLineID
= MAKELONG(dst
, idx
);
472 lpMl
->dwDestination
= dst
;
473 lpMl
->cConnections
= 1;
475 for (i
= 0; i
< mix
->numCtrl
; i
++)
476 if (mix
->ctrl
[i
].dwLineID
== lpMl
->dwLineID
)
481 case SOUND_MIXER_SYNTH
:
482 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
;
483 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
486 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
;
487 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
489 case SOUND_MIXER_LINE
:
490 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_LINE
;
491 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
493 case SOUND_MIXER_MIC
:
494 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
;
495 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
497 case SOUND_MIXER_PCM
:
498 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
;
499 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
501 case SOUND_MIXER_IMIX
:
502 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
;
503 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
506 WARN("Index %ld not handled.\n", idx
);
507 return MIXERR_INVALLINE
;
510 if (dst
== 0 && WINE_CHN_SUPPORTS(mix
->stereoMask
, idx
))
512 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
514 if (WINE_CHN_SUPPORTS(mask
, j
))
521 return MMSYSERR_NOERROR
;
524 /******************************************************************
527 static BOOL
MIX_CheckLine(DWORD lineID
)
529 return ((HIWORD(lineID
) < SOUND_MIXER_NRDEVICES
&& LOWORD(lineID
) < 2) ||
530 (HIWORD(lineID
) == LINEID_DST
&& LOWORD(lineID
) < SOUND_MIXER_NRDEVICES
));
533 /**************************************************************************
534 * MIX_GetLineInfo [internal]
536 static DWORD
MIX_GetLineInfo(WORD wDevID
, LPMIXERLINEA lpMl
, DWORD fdwInfo
)
539 DWORD ret
= MMSYSERR_NOERROR
;
543 TRACE("(%04X, %p, %lu);\n", wDevID
, lpMl
, fdwInfo
);
545 if (lpMl
== NULL
|| lpMl
->cbStruct
!= sizeof(*lpMl
))
546 return MMSYSERR_INVALPARAM
;
547 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
549 /* FIXME: set all the variables correctly... the lines below
552 lpMl
->fdwLine
= MIXERLINE_LINEF_ACTIVE
;
555 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
)
557 case MIXER_GETLINEINFOF_DESTINATION
:
558 TRACE("DESTINATION (%08lx)\n", lpMl
->dwDestination
);
559 if (lpMl
->dwDestination
>= 2)
560 return MMSYSERR_INVALPARAM
;
561 if ((ret
= MIX_GetLineInfoDst(mix
, lpMl
, lpMl
->dwDestination
)) != MMSYSERR_NOERROR
)
564 case MIXER_GETLINEINFOF_SOURCE
:
565 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl
->dwSource
, lpMl
->dwDestination
);
566 switch (lpMl
->dwDestination
)
568 case 0: mask
= mix
->devMask
; break;
569 case 1: mask
= mix
->recMask
; break;
570 default: return MMSYSERR_INVALPARAM
;
573 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
575 if (WINE_CHN_SUPPORTS(mask
, j
) && (i
-- == 0))
578 if (j
>= SOUND_MIXER_NRDEVICES
)
579 return MIXERR_INVALLINE
;
580 if ((ret
= MIX_GetLineInfoSrc(mix
, lpMl
, j
, lpMl
->dwDestination
)) != MMSYSERR_NOERROR
)
583 case MIXER_GETLINEINFOF_LINEID
:
584 TRACE("LINEID (%08lx)\n", lpMl
->dwLineID
);
586 if (!MIX_CheckLine(lpMl
->dwLineID
))
587 return MIXERR_INVALLINE
;
588 if (HIWORD(lpMl
->dwLineID
) == LINEID_DST
)
589 ret
= MIX_GetLineInfoDst(mix
, lpMl
, LOWORD(lpMl
->dwLineID
));
591 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, HIWORD(lpMl
->dwLineID
), LOWORD(lpMl
->dwLineID
));
592 if (ret
!= MMSYSERR_NOERROR
)
595 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
596 TRACE("COMPONENT TYPE (%08lx)\n", lpMl
->dwComponentType
);
597 switch (lpMl
->dwComponentType
)
599 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
:
600 ret
= MIX_GetLineInfoDst(mix
, lpMl
, 0);
602 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN
:
603 ret
= MIX_GetLineInfoDst(mix
, lpMl
, 1);
605 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
:
606 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_SYNTH
, 0);
608 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
:
609 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_CD
, 0);
611 case MIXERLINE_COMPONENTTYPE_SRC_LINE
:
612 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_LINE
, 0);
614 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
:
615 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_MIC
, 1);
617 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
:
618 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_PCM
, 0);
620 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
:
621 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_IMIX
, 1);
624 FIXME("Unhandled component type (%08lx)\n", lpMl
->dwComponentType
);
625 return MMSYSERR_INVALPARAM
;
628 case MIXER_GETLINEINFOF_TARGETTYPE
:
629 FIXME("_TARGETTYPE not implemented yet.\n");
632 WARN("Unknown flag (%08lx)\n", fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
);
636 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_AUX
; /* FIXME */
637 lpMl
->Target
.dwDeviceID
= 0xFFFFFFFF;
638 lpMl
->Target
.wMid
= WINE_MIXER_MANUF_ID
;
639 lpMl
->Target
.wPid
= WINE_MIXER_PRODUCT_ID
;
640 lpMl
->Target
.vDriverVersion
= WINE_MIXER_VERSION
;
641 strcpy(lpMl
->Target
.szPname
, WINE_MIXER_NAME
);
646 /******************************************************************
650 static BOOL
MIX_CheckControl(struct mixer
* mix
, DWORD ctrlID
)
652 return (ctrlID
>= 1 && ctrlID
<= mix
->numCtrl
);
655 /**************************************************************************
656 * MIX_GetLineControls [internal]
658 static DWORD
MIX_GetLineControls(WORD wDevID
, LPMIXERLINECONTROLSA lpMlc
, DWORD flags
)
660 DWORD dwRet
= MMSYSERR_NOERROR
;
663 TRACE("(%04X, %p, %lu);\n", wDevID
, lpMlc
, flags
);
665 if (lpMlc
== NULL
) return MMSYSERR_INVALPARAM
;
666 if (lpMlc
->cbStruct
< sizeof(*lpMlc
) ||
667 lpMlc
->cbmxctrl
< sizeof(MIXERCONTROLA
))
668 return MMSYSERR_INVALPARAM
;
669 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
671 switch (flags
& MIXER_GETLINECONTROLSF_QUERYMASK
)
673 case MIXER_GETLINECONTROLSF_ALL
:
677 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc
->dwLineID
, lpMlc
->cControls
);
679 for (i
= j
= 0; i
< mix
->numCtrl
; i
++)
681 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
)
684 if (!j
|| lpMlc
->cControls
!= j
) dwRet
= MMSYSERR_INVALPARAM
;
685 else if (!MIX_CheckLine(lpMlc
->dwLineID
)) dwRet
= MIXERR_INVALLINE
;
688 for (i
= j
= 0; i
< mix
->numCtrl
; i
++)
690 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
)
692 TRACE("[%d] => [%2d]: typ=%08lx\n", j
, i
+ 1, mix
->ctrl
[i
].ctrl
.dwControlType
);
693 lpMlc
->pamxctrl
[j
++] = mix
->ctrl
[i
].ctrl
;
699 case MIXER_GETLINECONTROLSF_ONEBYID
:
700 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlID
);
702 if (!MIX_CheckControl(mix
, lpMlc
->u
.dwControlID
) ||
703 mix
->ctrl
[lpMlc
->u
.dwControlID
- 1].dwLineID
!= lpMlc
->dwLineID
)
704 dwRet
= MMSYSERR_INVALPARAM
;
706 lpMlc
->pamxctrl
[0] = mix
->ctrl
[lpMlc
->u
.dwControlID
- 1].ctrl
;
708 case MIXER_GETLINECONTROLSF_ONEBYTYPE
:
709 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlType
);
710 if (!MIX_CheckLine(lpMlc
->dwLineID
)) dwRet
= MIXERR_INVALLINE
;
714 DWORD ct
= lpMlc
->u
.dwControlType
& MIXERCONTROL_CT_CLASS_MASK
;
716 for (i
= 0; i
< mix
->numCtrl
; i
++)
718 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
&&
719 ct
== (mix
->ctrl
[i
].ctrl
.dwControlType
& MIXERCONTROL_CT_CLASS_MASK
))
721 lpMlc
->pamxctrl
[0] = mix
->ctrl
[i
].ctrl
;
725 if (i
== mix
->numCtrl
) dwRet
= MMSYSERR_INVALPARAM
;
729 ERR("Unknown flag %08lx\n", flags
& MIXER_GETLINECONTROLSF_QUERYMASK
);
730 dwRet
= MMSYSERR_INVALPARAM
;
736 /**************************************************************************
737 * MIX_GetControlDetails [internal]
739 static DWORD
MIX_GetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD fdwDetails
)
741 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
745 TRACE("(%04X, %p, %lu);\n", wDevID
, lpmcd
, fdwDetails
);
747 if (lpmcd
== NULL
) return MMSYSERR_INVALPARAM
;
748 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
750 switch (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
)
752 case MIXER_GETCONTROLDETAILSF_VALUE
:
753 TRACE("GCD VALUE (%08lx)\n", lpmcd
->dwControlID
);
754 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
756 c
= lpmcd
->dwControlID
- 1;
757 chnl
= HIWORD(mix
->ctrl
[c
].dwLineID
);
758 if (chnl
== LINEID_DST
)
759 chnl
= LOWORD(mix
->ctrl
[c
].dwLineID
) ? SOUND_MIXER_RECLEV
: SOUND_MIXER_VOLUME
;
760 switch (mix
->ctrl
[c
].ctrl
.dwControlType
)
762 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
764 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
767 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED
), lpmcd
->cbDetails
);
768 /* return value is 00RL (4 bytes)... */
769 if ((val
= mix
->volume
[chnl
]) == -1 && !MIX_GetVal(mix
, chnl
, &val
))
770 return MMSYSERR_INVALPARAM
;
772 switch (lpmcd
->cChannels
)
775 /* mono... so R = L */
776 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
777 mcdu
->dwValue
= (LOBYTE(LOWORD(val
)) * 65536L) / 100;
780 /* stereo, left is paDetails[0] */
781 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 0 * lpmcd
->cbDetails
);
782 mcdu
->dwValue
= (LOBYTE(LOWORD(val
)) * 65536L) / 100;
783 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 1 * lpmcd
->cbDetails
);
784 mcdu
->dwValue
= (HIBYTE(LOWORD(val
)) * 65536L) / 100;
787 WARN("Unknown cChannels (%ld)\n", lpmcd
->cChannels
);
788 return MMSYSERR_INVALPARAM
;
790 TRACE("=> %08lx\n", mcdu
->dwValue
);
793 case MIXERCONTROL_CONTROLTYPE_MUTE
:
794 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
796 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
798 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
799 /* we mute both channels at the same time */
800 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
801 mcdb
->fValue
= (mix
->volume
[chnl
] != -1);
802 TRACE("=> %s\n", mcdb
->fValue
? "on" : "off");
805 case MIXERCONTROL_CONTROLTYPE_MIXER
:
806 case MIXERCONTROL_CONTROLTYPE_MUX
:
810 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
811 if (!MIX_GetRecSrc(mix
, &mask
))
813 /* FIXME: ENXIO => no mixer installed */
814 WARN("mixer device not available !\n");
815 ret
= MMSYSERR_ERROR
;
819 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
822 /* we mute both channels at the same time */
823 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
825 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
827 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
829 if (i
>= lpmcd
->u
.cMultipleItems
)
830 return MMSYSERR_INVALPARAM
;
831 mcdb
[i
++].fValue
= WINE_CHN_SUPPORTS(mask
, j
);
838 WARN("Unsupported\n");
840 ret
= MMSYSERR_NOERROR
;
844 ret
= MMSYSERR_INVALPARAM
;
847 case MIXER_GETCONTROLDETAILSF_LISTTEXT
:
848 TRACE("LIST TEXT (%08lx)\n", lpmcd
->dwControlID
);
850 ret
= MMSYSERR_INVALPARAM
;
851 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
853 int c
= lpmcd
->dwControlID
- 1;
855 if (mix
->ctrl
[c
].ctrl
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MUX
||
856 mix
->ctrl
[c
].ctrl
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MIXER
)
858 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt
;
861 mcdlt
= (LPMIXERCONTROLDETAILS_LISTTEXTA
)lpmcd
->paDetails
;
862 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
864 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
866 mcdlt
[i
].dwParam1
= MAKELONG(LINEID_RECORD
, j
);
867 mcdlt
[i
].dwParam2
= 0;
868 strcpy(mcdlt
[i
].szName
, MIX_Names
[j
]);
872 if (i
!= lpmcd
->u
.cMultipleItems
) FIXME("bad count\n");
873 ret
= MMSYSERR_NOERROR
;
878 WARN("Unknown flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
883 /**************************************************************************
884 * MIX_SetControlDetails [internal]
886 static DWORD
MIX_SetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD fdwDetails
)
888 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
893 TRACE("(%04X, %p, %lu);\n", wDevID
, lpmcd
, fdwDetails
);
895 if (lpmcd
== NULL
) return MMSYSERR_INVALPARAM
;
896 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
898 switch (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
)
900 case MIXER_GETCONTROLDETAILSF_VALUE
:
901 TRACE("GCD VALUE (%08lx)\n", lpmcd
->dwControlID
);
902 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
904 c
= lpmcd
->dwControlID
- 1;
905 chnl
= HIWORD(mix
->ctrl
[c
].dwLineID
);
906 if (chnl
== LINEID_DST
)
907 chnl
= LOWORD(mix
->ctrl
[c
].dwLineID
) ? SOUND_MIXER_RECLEV
: SOUND_MIXER_VOLUME
;
909 switch (mix
->ctrl
[c
].ctrl
.dwControlType
)
911 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
913 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
915 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED
), lpmcd
->cbDetails
);
916 /* val should contain 00RL */
917 switch (lpmcd
->cChannels
)
920 /* mono... so R = L */
921 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
922 TRACE("Setting RL to %08ld\n", mcdu
->dwValue
);
923 val
= 0x101 * ((mcdu
->dwValue
* 100) >> 16);
926 /* stereo, left is paDetails[0] */
927 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 0 * lpmcd
->cbDetails
);
928 TRACE("Setting L to %08ld\n", mcdu
->dwValue
);
929 val
= ((mcdu
->dwValue
* 100) >> 16);
930 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 1 * lpmcd
->cbDetails
);
931 TRACE("Setting R to %08ld\n", mcdu
->dwValue
);
932 val
+= ((mcdu
->dwValue
* 100) >> 16) << 8;
935 WARN("Unknown cChannels (%ld)\n", lpmcd
->cChannels
);
936 return MMSYSERR_INVALPARAM
;
939 if (mix
->volume
[chnl
] == -1)
941 if (!MIX_SetVal(mix
, chnl
, val
))
942 return MMSYSERR_INVALPARAM
;
946 mix
->volume
[chnl
] = val
;
949 ret
= MMSYSERR_NOERROR
;
951 case MIXERCONTROL_CONTROLTYPE_MUTE
:
952 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
954 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
956 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
957 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
960 if (!MIX_GetVal(mix
, chnl
, &mix
->volume
[chnl
]) || !MIX_SetVal(mix
, chnl
, 0))
961 return MMSYSERR_INVALPARAM
;
965 if (mix
->volume
[chnl
] == -1)
967 ret
= MMSYSERR_NOERROR
;
970 if (!MIX_SetVal(mix
, chnl
, mix
->volume
[chnl
]))
971 return MMSYSERR_INVALPARAM
;
972 mix
->volume
[chnl
] = -1;
975 ret
= MMSYSERR_NOERROR
;
977 case MIXERCONTROL_CONTROLTYPE_MIXER
:
978 case MIXERCONTROL_CONTROLTYPE_MUX
:
980 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
984 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
985 /* we mute both channels at the same time */
986 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
988 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
990 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
) && mcdb
[i
++].fValue
)
992 /* a mux can only select one line at a time... */
993 if (mix
->singleRecChannel
&& mask
!= 0)
996 return MMSYSERR_INVALPARAM
;
998 mask
|= WINE_CHN_MASK(j
);
1001 if (i
!= lpmcd
->u
.cMultipleItems
) FIXME("bad count\n");
1002 TRACE("writing %04x as rec src\n", mask
);
1003 if (!MIX_SetRecSrc(mix
, mask
))
1004 ERR("Can't write new mixer settings\n");
1006 ret
= MMSYSERR_NOERROR
;
1013 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
1018 /**************************************************************************
1019 * MIX_Init [internal]
1021 static DWORD
MIX_Init(void)
1025 #define MIXER_DEV "/dev/mixer"
1026 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1028 if (errno
== ENODEV
|| errno
== ENXIO
)
1030 /* no driver present */
1031 return MMSYSERR_NODRIVER
;
1034 return MMSYSERR_ERROR
;
1038 MIX_Mixers
[0].name
= MIXER_DEV
;
1039 MIX_Open(0, NULL
, 0); /* FIXME */
1041 return MMSYSERR_NOERROR
;
1044 /**************************************************************************
1045 * MIX_GetNumDevs [internal]
1047 static DWORD
MIX_GetNumDevs(void)
1049 return MIX_NumMixers
;
1052 #endif /* HAVE_OSS */
1054 /**************************************************************************
1055 * mxdMessage (WINEOSS.3)
1057 DWORD WINAPI
OSS_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
1058 DWORD dwParam1
, DWORD dwParam2
)
1060 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1070 /* FIXME: Pretend this is supported */
1072 case MXDM_GETDEVCAPS
:
1073 return MIX_GetDevCaps(wDevID
, (LPMIXERCAPSA
)dwParam1
, dwParam2
);
1074 case MXDM_GETLINEINFO
:
1075 return MIX_GetLineInfo(wDevID
, (LPMIXERLINEA
)dwParam1
, dwParam2
);
1076 case MXDM_GETNUMDEVS
:
1077 return MIX_GetNumDevs();
1079 return MMSYSERR_NOERROR
;
1080 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1082 return MMSYSERR_NOERROR
;
1083 case MXDM_GETLINECONTROLS
:
1084 return MIX_GetLineControls(wDevID
, (LPMIXERLINECONTROLSA
)dwParam1
, dwParam2
);
1085 case MXDM_GETCONTROLDETAILS
:
1086 return MIX_GetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1087 case MXDM_SETCONTROLDETAILS
:
1088 return MIX_SetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1090 WARN("unknown message %d!\n", wMsg
);
1091 return MMSYSERR_NOTSUPPORTED
;
1094 return MMSYSERR_NOTENABLED
;