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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 * + implement notification mechanism when state of mixer's controls
29 #include "wine/port.h"
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
45 #define NONAMELESSUNION
46 #define NONAMELESSSTRUCT
52 #include "wine/unicode.h"
53 #include "wine/debug.h"
55 WINE_DEFAULT_DEBUG_CHANNEL(mixer
);
59 #define MAX_MIXERDRV (6)
61 #define WINE_MIXER_MANUF_ID 0xAA
62 #define WINE_MIXER_PRODUCT_ID 0x55
63 #define WINE_MIXER_VERSION 0x0100
64 #define WINE_MIXER_NAME "WINE OSS Mixer"
66 #define WINE_CHN_MASK(_x) (1L << (_x))
67 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
68 /* Bass and Treble are no longer in the mask as Windows does not handle them */
69 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
70 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
71 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
72 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
73 WINE_CHN_MASK(SOUND_MIXER_CD) )
75 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
76 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
77 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
78 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
80 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
81 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
82 static const char * const MIX_Labels
[SOUND_MIXER_NRDEVICES
] = SOUND_DEVICE_LABELS
;
83 static const char * const MIX_Names
[SOUND_MIXER_NRDEVICES
] = SOUND_DEVICE_NAMES
;
95 int volume
[SOUND_MIXER_NRDEVICES
];
99 BOOL singleRecChannel
;
100 struct mixerCtrl
* ctrl
;
104 #define LINEID_DST 0xFFFF
105 #define LINEID_SPEAKER 0x0000
106 #define LINEID_RECORD 0x0001
108 static int MIX_NumMixers
;
109 static struct mixer MIX_Mixers
[MAX_MIXERDRV
];
111 /**************************************************************************
114 static const char * getMessage(UINT uMsg
)
117 #define MSG_TO_STR(x) case x: return #x;
119 MSG_TO_STR(DRVM_INIT
);
120 MSG_TO_STR(DRVM_EXIT
);
121 MSG_TO_STR(DRVM_ENABLE
);
122 MSG_TO_STR(DRVM_DISABLE
);
123 MSG_TO_STR(MXDM_GETDEVCAPS
);
124 MSG_TO_STR(MXDM_GETLINEINFO
);
125 MSG_TO_STR(MXDM_GETNUMDEVS
);
126 MSG_TO_STR(MXDM_OPEN
);
127 MSG_TO_STR(MXDM_CLOSE
);
128 MSG_TO_STR(MXDM_GETLINECONTROLS
);
129 MSG_TO_STR(MXDM_GETCONTROLDETAILS
);
130 MSG_TO_STR(MXDM_SETCONTROLDETAILS
);
133 sprintf(str
, "UNKNOWN(%08x)", uMsg
);
137 static const char * getIoctlCommand(int command
)
140 #define IOCTL_TO_STR(x) case x: return #x;
142 IOCTL_TO_STR(SOUND_MIXER_VOLUME
);
143 IOCTL_TO_STR(SOUND_MIXER_BASS
);
144 IOCTL_TO_STR(SOUND_MIXER_TREBLE
);
145 IOCTL_TO_STR(SOUND_MIXER_SYNTH
);
146 IOCTL_TO_STR(SOUND_MIXER_PCM
);
147 IOCTL_TO_STR(SOUND_MIXER_SPEAKER
);
148 IOCTL_TO_STR(SOUND_MIXER_LINE
);
149 IOCTL_TO_STR(SOUND_MIXER_MIC
);
150 IOCTL_TO_STR(SOUND_MIXER_CD
);
151 IOCTL_TO_STR(SOUND_MIXER_IMIX
);
152 IOCTL_TO_STR(SOUND_MIXER_ALTPCM
);
153 IOCTL_TO_STR(SOUND_MIXER_RECLEV
);
154 IOCTL_TO_STR(SOUND_MIXER_IGAIN
);
155 IOCTL_TO_STR(SOUND_MIXER_OGAIN
);
156 IOCTL_TO_STR(SOUND_MIXER_LINE1
);
157 IOCTL_TO_STR(SOUND_MIXER_LINE2
);
158 IOCTL_TO_STR(SOUND_MIXER_LINE3
);
159 IOCTL_TO_STR(SOUND_MIXER_DIGITAL1
);
160 IOCTL_TO_STR(SOUND_MIXER_DIGITAL2
);
161 IOCTL_TO_STR(SOUND_MIXER_DIGITAL3
);
162 #ifdef SOUND_MIXER_PHONEIN
163 IOCTL_TO_STR(SOUND_MIXER_PHONEIN
);
165 #ifdef SOUND_MIXER_PHONEOUT
166 IOCTL_TO_STR(SOUND_MIXER_PHONEOUT
);
168 IOCTL_TO_STR(SOUND_MIXER_VIDEO
);
169 IOCTL_TO_STR(SOUND_MIXER_RADIO
);
170 #ifdef SOUND_MIXER_MONITOR
171 IOCTL_TO_STR(SOUND_MIXER_MONITOR
);
175 sprintf(str
, "UNKNOWN(%08x)", command
);
179 static const char * getControlType(DWORD dwControlType
)
182 #define TYPE_TO_STR(x) case x: return #x;
183 switch (dwControlType
) {
184 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM
);
185 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER
);
186 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER
);
187 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER
);
188 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER
);
189 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN
);
190 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF
);
191 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE
);
192 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO
);
193 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS
);
194 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH
);
195 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST
);
196 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON
);
197 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS
);
198 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED
);
199 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED
);
200 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT
);
201 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER
);
202 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN
);
203 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN
);
204 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER
);
205 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME
);
206 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS
);
207 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE
);
208 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER
);
209 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT
);
210 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX
);
211 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT
);
212 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER
);
213 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME
);
214 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME
);
217 sprintf(str
, "UNKNOWN(%08x)", dwControlType
);
221 static const char * getComponentType(DWORD dwComponentType
)
224 #define TYPE_TO_STR(x) case x: return #x;
225 switch (dwComponentType
) {
226 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED
);
227 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL
);
228 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE
);
229 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR
);
230 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
);
231 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
);
232 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE
);
233 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN
);
234 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN
);
235 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
);
236 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL
);
237 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE
);
238 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
);
239 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
);
240 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
);
241 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE
);
242 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER
);
243 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
);
244 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
);
245 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG
);
248 sprintf(str
, "UNKNOWN(%08x)", dwComponentType
);
252 static const char * getTargetType(DWORD dwType
)
255 #define TYPE_TO_STR(x) case x: return #x;
257 TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED
);
258 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT
);
259 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN
);
260 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT
);
261 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN
);
262 TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX
);
265 sprintf(str
, "UNKNOWN(%08x)", dwType
);
269 static const WCHAR sz_short_volume
[] = {'V','o','l',0};
270 static const WCHAR sz_long_volume
[] = {'V','o','l','u','m','e',0};
271 static const WCHAR sz_shrtlng_mute
[] = {'M','u','t','e',0};
272 static const WCHAR sz_shrtlng_mixer
[] = {'M','i','x','e','r',0};
274 /**************************************************************************
275 * MIX_FillLineControls [internal]
277 static void MIX_FillLineControls(struct mixer
* mix
, int c
, DWORD lineID
,
280 struct mixerCtrl
* mc
= &mix
->ctrl
[c
];
283 TRACE("(%p, %d, %08x, %s)\n", mix
, c
, lineID
,
284 getControlType(dwControlType
));
286 mc
->dwLineID
= lineID
;
287 mc
->ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
288 mc
->ctrl
.dwControlID
= c
+ 1;
289 mc
->ctrl
.dwControlType
= dwControlType
;
291 switch (dwControlType
)
293 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
294 mc
->ctrl
.fdwControl
= 0;
295 mc
->ctrl
.cMultipleItems
= 0;
296 strcpyW(mc
->ctrl
.szShortName
, sz_short_volume
);
297 strcpyW(mc
->ctrl
.szName
, sz_long_volume
);
298 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
299 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
300 * [0, 100] is the range supported by OSS
301 * whatever the min and max values are they must match
302 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
304 mc
->ctrl
.Bounds
.s1
.dwMinimum
= 0;
305 mc
->ctrl
.Bounds
.s1
.dwMaximum
= 65535;
306 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
307 mc
->ctrl
.Metrics
.cSteps
= 656;
309 case MIXERCONTROL_CONTROLTYPE_MUTE
:
310 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
311 mc
->ctrl
.fdwControl
= 0;
312 mc
->ctrl
.cMultipleItems
= 0;
313 strcpyW(mc
->ctrl
.szShortName
, sz_shrtlng_mute
);
314 strcpyW(mc
->ctrl
.szName
, sz_shrtlng_mute
);
315 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
316 mc
->ctrl
.Bounds
.s1
.dwMinimum
= 0;
317 mc
->ctrl
.Bounds
.s1
.dwMaximum
= 1;
318 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
320 case MIXERCONTROL_CONTROLTYPE_MUX
:
321 case MIXERCONTROL_CONTROLTYPE_MIXER
:
322 mc
->ctrl
.fdwControl
= MIXERCONTROL_CONTROLF_MULTIPLE
;
323 mc
->ctrl
.cMultipleItems
= 0;
324 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
325 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
326 mc
->ctrl
.cMultipleItems
++;
327 strcpyW(mc
->ctrl
.szShortName
, sz_shrtlng_mixer
);
328 strcpyW(mc
->ctrl
.szName
, sz_shrtlng_mixer
);
329 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
330 mc
->ctrl
.Bounds
.s1
.dwMaximum
= mc
->ctrl
.cMultipleItems
- 1;
331 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
332 mc
->ctrl
.Metrics
.cSteps
= mc
->ctrl
.cMultipleItems
;
336 FIXME("Internal error: unknown type: %08x\n", dwControlType
);
338 TRACE("ctrl[%2d]: typ=%08x lin=%08x\n", c
+ 1, dwControlType
, lineID
);
341 /******************************************************************
346 static struct mixer
* MIX_Get(WORD wDevID
)
348 TRACE("(%04x)\n", wDevID
);
350 if (wDevID
>= MIX_NumMixers
|| MIX_Mixers
[wDevID
].dev_name
== NULL
)
353 return &MIX_Mixers
[wDevID
];
356 /**************************************************************************
357 * MIX_Open [internal]
359 static DWORD
MIX_Open(WORD wDevID
, LPMIXEROPENDESC lpMod
, DWORD flags
)
364 DWORD ret
= MMSYSERR_NOERROR
;
366 TRACE("(%04X, %p, %u);\n", wDevID
, lpMod
, flags
);
368 /* as we partly init the mixer with MIX_Open, we can allow null open decs
369 * EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM;
370 * anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer
371 * device before sending messages to it... it seems to be linked to all
372 * the equivalent of mixer identification
373 * (with a reference to a wave, midi.. handle
375 if ((mix
= MIX_Get(wDevID
)) == NULL
) {
376 WARN("bad device ID: %04X\n", wDevID
);
377 return MMSYSERR_BADDEVICEID
;
380 if ((mixer
= open(mix
->dev_name
, O_RDWR
)) < 0)
382 ERR("open(%s, O_RDWR) failed (%s)\n",
383 mix
->dev_name
, strerror(errno
));
385 if (errno
== ENODEV
|| errno
== ENXIO
)
387 /* no driver present */
389 return MMSYSERR_NODRIVER
;
391 return MMSYSERR_ERROR
;
394 if (ioctl(mixer
, SOUND_MIXER_READ_DEVMASK
, &mix
->devMask
) == -1)
396 ERR("ioctl(%s, SOUND_MIXER_DEVMASK) failed (%s)\n",
397 mix
->dev_name
, strerror(errno
));
398 ret
= MMSYSERR_ERROR
;
402 mix
->devMask
&= WINE_MIXER_MASK_SPEAKER
;
403 if (mix
->devMask
== 0)
406 ret
= MMSYSERR_NODRIVER
;
410 if (ioctl(mixer
, SOUND_MIXER_READ_STEREODEVS
, &mix
->stereoMask
) == -1)
412 ERR("ioctl(%s, SOUND_MIXER_STEREODEVS) failed (%s)\n",
413 mix
->dev_name
, strerror(errno
));
414 ret
= MMSYSERR_ERROR
;
417 mix
->stereoMask
&= WINE_MIXER_MASK_SPEAKER
;
419 if (ioctl(mixer
, SOUND_MIXER_READ_RECMASK
, &mix
->recMask
) == -1)
421 ERR("ioctl(%s, SOUND_MIXER_RECMASK) failed (%s)\n",
422 mix
->dev_name
, strerror(errno
));
423 ret
= MMSYSERR_ERROR
;
426 mix
->recMask
&= WINE_MIXER_MASK_RECORD
;
427 /* FIXME: we may need to support both rec lev & igain */
428 if (!WINE_CHN_SUPPORTS(mix
->recMask
, SOUND_MIXER_RECLEV
))
430 WARN("The sound card doesn't support rec level\n");
431 if (WINE_CHN_SUPPORTS(mix
->recMask
, SOUND_MIXER_IGAIN
))
432 WARN("but it does support IGain, please report\n");
434 if (ioctl(mixer
, SOUND_MIXER_READ_CAPS
, &caps
) == -1)
436 ERR("ioctl(%s, SOUND_MIXER_READ_CAPS) failed (%s)\n",
437 mix
->dev_name
, strerror(errno
));
438 ret
= MMSYSERR_ERROR
;
441 mix
->singleRecChannel
= caps
& SOUND_CAP_EXCL_INPUT
;
442 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
443 mix
->devMask
, mix
->recMask
, mix
->stereoMask
,
444 mix
->singleRecChannel
? "single" : "multiple");
445 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
449 mix
->numCtrl
= 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
450 /* FIXME: do we always have RECLEV on all cards ??? */
451 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
453 if (WINE_CHN_SUPPORTS(mix
->devMask
, i
))
454 mix
->numCtrl
+= 2; /* volume & mute */
455 if (WINE_CHN_SUPPORTS(mix
->recMask
, i
))
456 mix
->numCtrl
+= 2; /* volume & onoff */
459 if (!(mix
->ctrl
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
460 sizeof(mix
->ctrl
[0]) * mix
->numCtrl
)))
462 ret
= MMSYSERR_NOMEM
;
467 MIX_FillLineControls(mix
, j
++, MAKELONG(0, LINEID_DST
),
468 MIXERCONTROL_CONTROLTYPE_VOLUME
);
469 MIX_FillLineControls(mix
, j
++, MAKELONG(0, LINEID_DST
),
470 MIXERCONTROL_CONTROLTYPE_MUTE
);
471 MIX_FillLineControls(mix
, j
++, MAKELONG(1, LINEID_DST
),
472 mix
->singleRecChannel
?
473 MIXERCONTROL_CONTROLTYPE_MUX
:
474 MIXERCONTROL_CONTROLTYPE_MIXER
);
475 MIX_FillLineControls(mix
, j
++, MAKELONG(1, LINEID_DST
),
476 MIXERCONTROL_CONTROLTYPE_MUTE
/*EPP*/);
477 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
479 if (WINE_CHN_SUPPORTS(mix
->devMask
, i
))
481 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_SPEAKER
, i
),
482 MIXERCONTROL_CONTROLTYPE_VOLUME
);
483 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_SPEAKER
, i
),
484 MIXERCONTROL_CONTROLTYPE_MUTE
);
487 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
489 if (WINE_CHN_SUPPORTS(mix
->recMask
, i
))
491 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_RECORD
, i
),
492 MIXERCONTROL_CONTROLTYPE_VOLUME
);
493 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_RECORD
, i
),
494 MIXERCONTROL_CONTROLTYPE_MUTE
/*EPP*/);
497 assert(j
== mix
->numCtrl
);
503 /**************************************************************************
504 * MIX_GetVal [internal]
506 static BOOL
MIX_GetVal(struct mixer
* mix
, int chn
, int* val
)
511 TRACE("(%p, %s, %p\n", mix
, getIoctlCommand(chn
), val
);
513 if ((mixer
= open(mix
->dev_name
, O_RDWR
)) < 0) {
514 /* FIXME: ENXIO => no mixer installed */
515 WARN("mixer device not available !\n");
517 if (ioctl(mixer
, MIXER_READ(chn
), val
) >= 0) {
518 TRACE("Reading %04x for %s\n", *val
, getIoctlCommand(chn
));
521 ERR("ioctl(%s, MIXER_READ(%s)) failed (%s)\n",
522 mix
->dev_name
, getIoctlCommand(chn
), strerror(errno
));
529 /**************************************************************************
530 * MIX_SetVal [internal]
532 static BOOL
MIX_SetVal(struct mixer
* mix
, int chn
, int val
)
537 TRACE("(%p, %s, %x)\n", mix
, getIoctlCommand(chn
), val
);
539 if ((mixer
= open(mix
->dev_name
, O_RDWR
)) < 0) {
540 /* FIXME: ENXIO => no mixer installed */
541 WARN("mixer device not available !\n");
543 if (ioctl(mixer
, MIXER_WRITE(chn
), &val
) >= 0) {
544 TRACE("Set %s to %04x\n", getIoctlCommand(chn
), val
);
547 ERR("ioctl(%s, MIXER_WRITE(%s)) failed (%s)\n",
548 mix
->dev_name
, getIoctlCommand(chn
), strerror(errno
));
555 /******************************************************************
560 static BOOL
MIX_GetRecSrc(struct mixer
* mix
, unsigned* mask
)
565 TRACE("(%p, %p)\n", mix
, mask
);
567 if ((mixer
= open(mix
->dev_name
, O_RDWR
)) >= 0) {
568 if (ioctl(mixer
, SOUND_MIXER_READ_RECSRC
, &mask
) >= 0) {
571 ERR("ioctl(%s, SOUND_MIXER_READ_RECSRC) failed (%s)\n",
572 mix
->dev_name
, strerror(errno
));
579 /******************************************************************
584 static BOOL
MIX_SetRecSrc(struct mixer
* mix
, unsigned mask
)
589 TRACE("(%p, %08x)\n", mix
, mask
);
591 if ((mixer
= open(mix
->dev_name
, O_RDWR
)) >= 0) {
592 if (ioctl(mixer
, SOUND_MIXER_WRITE_RECSRC
, &mask
) >= 0) {
595 ERR("ioctl(%s, SOUND_MIXER_WRITE_RECSRC) failed (%s)\n",
596 mix
->dev_name
, strerror(errno
));
603 /**************************************************************************
604 * MIX_GetDevCaps [internal]
606 static DWORD
MIX_GetDevCaps(WORD wDevID
, LPMIXERCAPSW lpCaps
, DWORD dwSize
)
612 TRACE("(%04X, %p, %u);\n", wDevID
, lpCaps
, dwSize
);
614 if (lpCaps
== NULL
) {
615 WARN("invalid parameter: lpCaps == NULL\n");
616 return MMSYSERR_INVALPARAM
;
619 if ((mix
= MIX_Get(wDevID
)) == NULL
) {
620 WARN("bad device ID: %04X\n", wDevID
);
621 return MMSYSERR_BADDEVICEID
;
624 capsW
.wMid
= WINE_MIXER_MANUF_ID
;
625 capsW
.wPid
= WINE_MIXER_PRODUCT_ID
;
626 capsW
.vDriverVersion
= WINE_MIXER_VERSION
;
627 if (!(name
= mix
->name
)) name
= WINE_MIXER_NAME
;
628 MultiByteToWideChar(CP_ACP
, 0, name
, -1, capsW
.szPname
, sizeof(capsW
.szPname
) / sizeof(WCHAR
));
629 capsW
.cDestinations
= 2; /* speakers & record */
630 capsW
.fdwSupport
= 0; /* No bits defined yet */
632 memcpy(lpCaps
, &capsW
, min(dwSize
, sizeof(capsW
)));
634 return MMSYSERR_NOERROR
;
637 /**************************************************************************
638 * MIX_GetLineInfoDst [internal]
640 static DWORD
MIX_GetLineInfoDst(struct mixer
* mix
, LPMIXERLINEW lpMl
,
646 TRACE("(%p, %p, %08x)\n", mix
, lpMl
, dst
);
648 lpMl
->dwDestination
= dst
;
652 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
654 j
= SOUND_MIXER_VOLUME
;
655 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEOUT
;
658 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_WAVEIN
;
660 j
= SOUND_MIXER_RECLEV
;
661 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEIN
;
664 FIXME("shouldn't happen\n");
665 return MMSYSERR_ERROR
;
667 lpMl
->dwSource
= 0xFFFFFFFF;
668 MultiByteToWideChar(CP_ACP
, 0, MIX_Labels
[j
], -1, lpMl
->szShortName
, sizeof(lpMl
->szShortName
) / sizeof(WCHAR
));
669 MultiByteToWideChar(CP_ACP
, 0, MIX_Names
[j
], -1, lpMl
->szName
, sizeof(lpMl
->szName
) / sizeof(WCHAR
));
671 /* we have all connections found in the MIX_DevMask */
672 lpMl
->cConnections
= 0;
673 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
674 if (WINE_CHN_SUPPORTS(mask
, j
))
675 lpMl
->cConnections
++;
677 if (WINE_CHN_SUPPORTS(mix
->stereoMask
, lpMl
->dwLineID
))
679 lpMl
->dwLineID
= MAKELONG(dst
, LINEID_DST
);
681 for (j
= 0; j
< mix
->numCtrl
; j
++)
682 if (mix
->ctrl
[j
].dwLineID
== lpMl
->dwLineID
)
685 return MMSYSERR_NOERROR
;
688 /**************************************************************************
689 * MIX_GetLineInfoSrc [internal]
691 static DWORD
MIX_GetLineInfoSrc(struct mixer
* mix
, LPMIXERLINEW lpMl
,
692 DWORD idx
, DWORD dst
)
695 unsigned mask
= (dst
) ? mix
->recMask
: mix
->devMask
;
697 TRACE("(%p, %p, %d, %08x)\n", mix
, lpMl
, idx
, dst
);
699 MultiByteToWideChar(CP_ACP
, 0, MIX_Labels
[idx
], -1, lpMl
->szShortName
, sizeof(lpMl
->szShortName
) / sizeof(WCHAR
));
700 MultiByteToWideChar(CP_ACP
, 0, MIX_Names
[idx
], -1, lpMl
->szName
, sizeof(lpMl
->szName
) / sizeof(WCHAR
));
701 lpMl
->dwLineID
= MAKELONG(dst
, idx
);
702 lpMl
->dwDestination
= dst
;
703 lpMl
->cConnections
= 1;
705 for (i
= 0; i
< mix
->numCtrl
; i
++)
706 if (mix
->ctrl
[i
].dwLineID
== lpMl
->dwLineID
)
711 case SOUND_MIXER_SYNTH
:
712 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
;
713 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
714 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_MIDIOUT
;
717 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
;
718 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
719 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_UNDEFINED
;
721 case SOUND_MIXER_LINE
:
722 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_LINE
;
723 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
724 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_UNDEFINED
;
726 case SOUND_MIXER_MIC
:
727 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
;
728 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
729 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEIN
;
731 case SOUND_MIXER_PCM
:
732 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
;
733 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
734 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEOUT
;
736 case SOUND_MIXER_IMIX
:
737 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
;
738 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
739 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_UNDEFINED
;
742 WARN("Index %d not handled.\n", idx
);
743 return MIXERR_INVALLINE
;
746 if (dst
== 0 && WINE_CHN_SUPPORTS(mix
->stereoMask
, idx
))
748 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
750 if (WINE_CHN_SUPPORTS(mask
, j
))
757 return MMSYSERR_NOERROR
;
760 /******************************************************************
763 static BOOL
MIX_CheckLine(DWORD lineID
)
765 TRACE("(%08x)\n",lineID
);
767 return ((HIWORD(lineID
) < SOUND_MIXER_NRDEVICES
&& LOWORD(lineID
) < 2) ||
768 (HIWORD(lineID
) == LINEID_DST
&&
769 LOWORD(lineID
) < SOUND_MIXER_NRDEVICES
));
772 /**************************************************************************
773 * MIX_GetLineInfo [internal]
775 static DWORD
MIX_GetLineInfo(WORD wDevID
, LPMIXERLINEW lpMl
, DWORD fdwInfo
)
778 DWORD ret
= MMSYSERR_NOERROR
;
782 TRACE("(%04X, %p, %u);\n", wDevID
, lpMl
, fdwInfo
);
785 WARN("invalid parameter: lpMl = NULL\n");
786 return MMSYSERR_INVALPARAM
;
789 if (lpMl
->cbStruct
!= sizeof(*lpMl
)) {
790 WARN("invalid parameter: lpMl->cbStruct = %d != %d\n",
791 lpMl
->cbStruct
, sizeof(*lpMl
));
792 return MMSYSERR_INVALPARAM
;
795 if ((mix
= MIX_Get(wDevID
)) == NULL
) {
796 WARN("bad device ID: %04X\n", wDevID
);
797 return MMSYSERR_BADDEVICEID
;
800 /* FIXME: set all the variables correctly... the lines below
803 lpMl
->fdwLine
= MIXERLINE_LINEF_ACTIVE
;
806 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
)
808 case MIXER_GETLINEINFOF_DESTINATION
:
809 TRACE("MIXER_GETLINEINFOF_DESTINATION (%08x)\n", lpMl
->dwDestination
);
810 if (lpMl
->dwDestination
>= 2) {
811 WARN("invalid parameter: lpMl->dwDestination = %d >= 2\n",
812 lpMl
->dwDestination
);
813 return MMSYSERR_INVALPARAM
;
815 ret
= MIX_GetLineInfoDst(mix
, lpMl
, lpMl
->dwDestination
);
816 if (ret
!= MMSYSERR_NOERROR
) {
821 case MIXER_GETLINEINFOF_SOURCE
:
822 TRACE("MIXER_GETLINEINFOF_SOURCE (%08x), dst=%08x\n", lpMl
->dwSource
,
823 lpMl
->dwDestination
);
824 switch (lpMl
->dwDestination
)
826 case LINEID_SPEAKER
: mask
= mix
->devMask
; break;
827 case LINEID_RECORD
: mask
= mix
->recMask
; break;
829 WARN("invalid parameter\n");
830 return MMSYSERR_INVALPARAM
;
833 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
835 if (WINE_CHN_SUPPORTS(mask
, j
) && (i
-- == 0))
838 if (j
>= SOUND_MIXER_NRDEVICES
) {
839 WARN("invalid line\n");
840 return MIXERR_INVALLINE
;
842 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, j
, lpMl
->dwDestination
);
843 if (ret
!= MMSYSERR_NOERROR
) {
848 case MIXER_GETLINEINFOF_LINEID
:
849 TRACE("MIXER_GETLINEINFOF_LINEID (%08x)\n", lpMl
->dwLineID
);
851 if (!MIX_CheckLine(lpMl
->dwLineID
)) {
852 WARN("invalid line\n");
853 return MIXERR_INVALLINE
;
855 if (HIWORD(lpMl
->dwLineID
) == LINEID_DST
)
856 ret
= MIX_GetLineInfoDst(mix
, lpMl
, LOWORD(lpMl
->dwLineID
));
858 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, HIWORD(lpMl
->dwLineID
),
859 LOWORD(lpMl
->dwLineID
));
860 if (ret
!= MMSYSERR_NOERROR
) {
865 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
866 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE (%s)\n",
867 getComponentType(lpMl
->dwComponentType
));
868 switch (lpMl
->dwComponentType
)
870 case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
:
871 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
:
872 ret
= MIX_GetLineInfoDst(mix
, lpMl
, LINEID_SPEAKER
);
874 case MIXERLINE_COMPONENTTYPE_DST_LINE
:
875 case MIXERLINE_COMPONENTTYPE_DST_VOICEIN
:
876 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN
:
877 ret
= MIX_GetLineInfoDst(mix
, lpMl
, LINEID_RECORD
);
879 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
:
880 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_SYNTH
, 0);
882 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
:
883 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_CD
, 0);
885 case MIXERLINE_COMPONENTTYPE_SRC_LINE
:
886 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_LINE
, 0);
888 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
:
889 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_MIC
, 1);
891 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
:
892 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_PCM
, 0);
894 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
:
895 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_IMIX
, 1);
898 FIXME("Unhandled component type (%s)\n",
899 getComponentType(lpMl
->dwComponentType
));
900 return MMSYSERR_INVALPARAM
;
903 case MIXER_GETLINEINFOF_TARGETTYPE
:
904 FIXME("MIXER_GETLINEINFOF_TARGETTYPE not implemented yet.\n");
905 TRACE("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n",
906 getTargetType(lpMl
->Target
.dwType
));
907 switch (lpMl
->Target
.dwType
) {
908 case MIXERLINE_TARGETTYPE_UNDEFINED
:
909 case MIXERLINE_TARGETTYPE_WAVEOUT
:
910 case MIXERLINE_TARGETTYPE_WAVEIN
:
911 case MIXERLINE_TARGETTYPE_MIDIOUT
:
912 case MIXERLINE_TARGETTYPE_MIDIIN
:
913 case MIXERLINE_TARGETTYPE_AUX
:
915 FIXME("Unhandled target type (%s)\n",
916 getTargetType(lpMl
->Target
.dwType
));
917 return MMSYSERR_INVALPARAM
;
921 WARN("Unknown flag (%08lx)\n", fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
);
925 if ((fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
) != MIXER_GETLINEINFOF_TARGETTYPE
) {
927 lpMl
->Target
.dwDeviceID
= 0xFFFFFFFF;
928 lpMl
->Target
.wMid
= WINE_MIXER_MANUF_ID
;
929 lpMl
->Target
.wPid
= WINE_MIXER_PRODUCT_ID
;
930 lpMl
->Target
.vDriverVersion
= WINE_MIXER_VERSION
;
931 if (!(name
= mix
->name
)) name
= WINE_MIXER_NAME
;
932 MultiByteToWideChar(CP_ACP
, 0, name
, -1, lpMl
->Target
.szPname
, sizeof(lpMl
->Target
.szPname
) / sizeof(WCHAR
));
938 /******************************************************************
942 static BOOL
MIX_CheckControl(struct mixer
* mix
, DWORD ctrlID
)
944 TRACE("(%p, %08x)\n", mix
, ctrlID
);
946 return (ctrlID
>= 1 && ctrlID
<= mix
->numCtrl
);
949 /**************************************************************************
950 * MIX_GetLineControls [internal]
952 static DWORD
MIX_GetLineControls(WORD wDevID
, LPMIXERLINECONTROLSW lpMlc
,
955 DWORD dwRet
= MMSYSERR_NOERROR
;
958 TRACE("(%04X, %p, %u);\n", wDevID
, lpMlc
, flags
);
961 WARN("invalid parameter: lpMlc == NULL\n");
962 return MMSYSERR_INVALPARAM
;
965 if (lpMlc
->cbStruct
< sizeof(*lpMlc
)) {
966 WARN("invalid parameter: lpMlc->cbStruct = %d < %d\n",
967 lpMlc
->cbStruct
, sizeof(*lpMlc
));
968 return MMSYSERR_INVALPARAM
;
971 if (lpMlc
->cbmxctrl
< sizeof(MIXERCONTROLW
)) {
972 WARN("invalid parameter: lpMlc->cbmxctrl = %d < %d\n",
973 lpMlc
->cbmxctrl
, sizeof(MIXERCONTROLW
));
974 return MMSYSERR_INVALPARAM
;
977 if ((mix
= MIX_Get(wDevID
)) == NULL
) {
978 WARN("bad device ID: %04X\n", wDevID
);
979 return MMSYSERR_BADDEVICEID
;
982 switch (flags
& MIXER_GETLINECONTROLSF_QUERYMASK
)
984 case MIXER_GETLINECONTROLSF_ALL
:
988 TRACE("line=%08x MIXER_GETLINECONTROLSF_ALL (%d)\n",
989 lpMlc
->dwLineID
, lpMlc
->cControls
);
991 for (i
= j
= 0; i
< mix
->numCtrl
; i
++)
993 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
)
997 if (!j
|| lpMlc
->cControls
!= j
) {
998 WARN("invalid parameter\n");
999 dwRet
= MMSYSERR_INVALPARAM
;
1000 } else if (!MIX_CheckLine(lpMlc
->dwLineID
)) {
1001 WARN("invalid line\n");
1002 dwRet
= MIXERR_INVALLINE
;
1004 for (i
= j
= 0; i
< mix
->numCtrl
; i
++)
1006 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
)
1008 TRACE("[%d] => [%2d]: typ=%08x\n", j
, i
+ 1,
1009 mix
->ctrl
[i
].ctrl
.dwControlType
);
1010 lpMlc
->pamxctrl
[j
++] = mix
->ctrl
[i
].ctrl
;
1016 case MIXER_GETLINECONTROLSF_ONEBYID
:
1017 TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n",
1018 lpMlc
->dwLineID
, lpMlc
->u
.dwControlID
);
1020 if (!MIX_CheckControl(mix
, lpMlc
->u
.dwControlID
) ||
1021 mix
->ctrl
[lpMlc
->u
.dwControlID
- 1].dwLineID
!= lpMlc
->dwLineID
) {
1022 WARN("invalid parameter\n");
1023 dwRet
= MMSYSERR_INVALPARAM
;
1025 lpMlc
->pamxctrl
[0] = mix
->ctrl
[lpMlc
->u
.dwControlID
- 1].ctrl
;
1027 case MIXER_GETLINECONTROLSF_ONEBYTYPE
:
1028 TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n",
1029 lpMlc
->dwLineID
, getControlType(lpMlc
->u
.dwControlType
));
1030 if (!MIX_CheckLine(lpMlc
->dwLineID
)) {
1031 WARN("invalid line\n");
1032 dwRet
= MIXERR_INVALLINE
;
1035 DWORD ct
= lpMlc
->u
.dwControlType
& MIXERCONTROL_CT_CLASS_MASK
;
1036 for (i
= 0; i
< mix
->numCtrl
; i
++) {
1037 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
&&
1038 ct
== (mix
->ctrl
[i
].ctrl
.dwControlType
&
1039 MIXERCONTROL_CT_CLASS_MASK
)) {
1040 lpMlc
->pamxctrl
[0] = mix
->ctrl
[i
].ctrl
;
1045 if (i
== mix
->numCtrl
) {
1046 WARN("invalid parameter: control not found\n");
1047 dwRet
= MMSYSERR_INVALPARAM
;
1052 ERR("Unknown flag %08lx\n", flags
& MIXER_GETLINECONTROLSF_QUERYMASK
);
1053 dwRet
= MMSYSERR_INVALPARAM
;
1059 /**************************************************************************
1060 * MIX_GetControlDetails [internal]
1062 static DWORD
MIX_GetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
,
1065 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
1069 TRACE("(%04X, %p, %u);\n", wDevID
, lpmcd
, fdwDetails
);
1071 if (lpmcd
== NULL
) {
1072 WARN("invalid parameter: lpmcd == NULL\n");
1073 return MMSYSERR_INVALPARAM
;
1076 if ((mix
= MIX_Get(wDevID
)) == NULL
) {
1077 WARN("bad device ID: %04X\n", wDevID
);
1078 return MMSYSERR_BADDEVICEID
;
1081 switch (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
)
1083 case MIXER_GETCONTROLDETAILSF_VALUE
:
1084 TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd
->dwControlID
);
1085 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
1087 DWORD c
= lpmcd
->dwControlID
- 1;
1088 chnl
= HIWORD(mix
->ctrl
[c
].dwLineID
);
1089 if (chnl
== LINEID_DST
)
1090 chnl
= LOWORD(mix
->ctrl
[c
].dwLineID
) ? SOUND_MIXER_RECLEV
:
1092 switch (mix
->ctrl
[c
].ctrl
.dwControlType
)
1094 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
1096 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
1099 if (lpmcd
->cbDetails
!=
1100 sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
1101 WARN("invalid parameter: cbDetails != %d\n",
1102 sizeof(MIXERCONTROLDETAILS_UNSIGNED
));
1103 return MMSYSERR_INVALPARAM
;
1106 TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n",
1107 getControlType(mix
->ctrl
[c
].ctrl
.dwControlType
),
1110 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
1112 /* return value is 00RL (4 bytes)... */
1113 if ((val
= mix
->volume
[chnl
]) == -1 &&
1114 !MIX_GetVal(mix
, chnl
, &val
)) {
1115 WARN("invalid parameter\n");
1116 return MMSYSERR_INVALPARAM
;
1119 switch (lpmcd
->cChannels
)
1122 /* mono... so R = L */
1123 mcdu
->dwValue
= ((LOBYTE(LOWORD(val
)) * 65536.0) / 100.0) + 0.5;
1124 TRACE("Reading RL = %d\n", mcdu
->dwValue
);
1127 /* stereo, left is paDetails[0] */
1128 mcdu
->dwValue
= ((LOBYTE(LOWORD(val
)) * 65536.0) / 100.0) + 0.5;
1129 TRACE("Reading L = %d\n", mcdu
->dwValue
);
1131 mcdu
->dwValue
= ((HIBYTE(LOWORD(val
)) * 65536.0) / 100.0) + 0.5;
1132 TRACE("Reading R = %d\n", mcdu
->dwValue
);
1135 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
1136 return MMSYSERR_INVALPARAM
;
1138 TRACE("=> %08x\n", mcdu
->dwValue
);
1141 case MIXERCONTROL_CONTROLTYPE_MUTE
:
1142 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
1144 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
1146 if (lpmcd
->cbDetails
!=
1147 sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
1148 WARN("invalid parameter: cbDetails != %d\n",
1149 sizeof(MIXERCONTROLDETAILS_BOOLEAN
));
1150 return MMSYSERR_INVALPARAM
;
1153 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1154 getControlType(mix
->ctrl
[c
].ctrl
.dwControlType
),
1157 /* we mute both channels at the same time */
1158 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
1159 mcdb
->fValue
= (mix
->volume
[chnl
] != -1);
1160 TRACE("=> %s\n", mcdb
->fValue
? "on" : "off");
1163 case MIXERCONTROL_CONTROLTYPE_MIXER
:
1164 case MIXERCONTROL_CONTROLTYPE_MUX
:
1168 if (lpmcd
->cbDetails
!=
1169 sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
1170 WARN("invalid parameter: cbDetails != %d\n",
1171 sizeof(MIXERCONTROLDETAILS_BOOLEAN
));
1172 return MMSYSERR_INVALPARAM
;
1175 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1176 getControlType(mix
->ctrl
[c
].ctrl
.dwControlType
),
1179 if (!MIX_GetRecSrc(mix
, &mask
))
1181 /* FIXME: ENXIO => no mixer installed */
1182 WARN("mixer device not available !\n");
1183 ret
= MMSYSERR_ERROR
;
1187 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
1190 /* we mute both channels at the same time */
1191 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
1193 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
1195 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
1197 if (i
>= lpmcd
->u
.cMultipleItems
)
1198 return MMSYSERR_INVALPARAM
;
1199 mcdb
[i
++].fValue
= WINE_CHN_SUPPORTS(mask
, j
);
1206 WARN("%s Unsupported\n",
1207 getControlType(mix
->ctrl
[c
].ctrl
.dwControlType
));
1209 ret
= MMSYSERR_NOERROR
;
1213 WARN("invalid parameter\n");
1214 ret
= MMSYSERR_INVALPARAM
;
1217 case MIXER_GETCONTROLDETAILSF_LISTTEXT
:
1218 TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%08x)\n",
1219 lpmcd
->dwControlID
);
1221 ret
= MMSYSERR_INVALPARAM
;
1222 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
1224 DWORD c
= lpmcd
->dwControlID
- 1;
1226 if (mix
->ctrl
[c
].ctrl
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MUX
||
1227 mix
->ctrl
[c
].ctrl
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MIXER
)
1229 LPMIXERCONTROLDETAILS_LISTTEXTW mcdlt
;
1232 mcdlt
= (LPMIXERCONTROLDETAILS_LISTTEXTW
)lpmcd
->paDetails
;
1233 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
1235 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
1237 mcdlt
[i
].dwParam1
= MAKELONG(LINEID_RECORD
, j
);
1238 mcdlt
[i
].dwParam2
= 0;
1239 MultiByteToWideChar(CP_ACP
, 0, MIX_Names
[j
], -1,
1240 mcdlt
[i
].szName
, sizeof(mcdlt
[i
]) / sizeof(WCHAR
));
1244 if (i
!= lpmcd
->u
.cMultipleItems
) FIXME("bad count\n");
1245 ret
= MMSYSERR_NOERROR
;
1250 WARN("Unknown flag (%08lx)\n",
1251 fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
1256 /**************************************************************************
1257 * MIX_SetControlDetails [internal]
1259 static DWORD
MIX_SetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
,
1262 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
1267 TRACE("(%04X, %p, %u);\n", wDevID
, lpmcd
, fdwDetails
);
1269 if (lpmcd
== NULL
) {
1270 TRACE("invalid parameter: lpmcd == NULL\n");
1271 return MMSYSERR_INVALPARAM
;
1274 if ((mix
= MIX_Get(wDevID
)) == NULL
) {
1275 WARN("bad device ID: %04X\n", wDevID
);
1276 return MMSYSERR_BADDEVICEID
;
1279 switch (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
)
1281 case MIXER_GETCONTROLDETAILSF_VALUE
:
1282 TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd
->dwControlID
);
1283 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
1285 c
= lpmcd
->dwControlID
- 1;
1287 TRACE("dwLineID=%08x\n",mix
->ctrl
[c
].dwLineID
);
1289 chnl
= HIWORD(mix
->ctrl
[c
].dwLineID
);
1290 if (chnl
== LINEID_DST
)
1291 chnl
= LOWORD(mix
->ctrl
[c
].dwLineID
) ?
1292 SOUND_MIXER_RECLEV
: SOUND_MIXER_VOLUME
;
1294 switch (mix
->ctrl
[c
].ctrl
.dwControlType
)
1296 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
1298 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
1300 if (lpmcd
->cbDetails
!=
1301 sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
1302 WARN("invalid parameter: cbDetails != %d\n",
1303 sizeof(MIXERCONTROLDETAILS_UNSIGNED
));
1304 return MMSYSERR_INVALPARAM
;
1307 TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n",
1308 getControlType(mix
->ctrl
[c
].ctrl
.dwControlType
),
1311 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
1312 /* val should contain 00RL */
1313 switch (lpmcd
->cChannels
)
1316 /* mono... so R = L */
1317 TRACE("Setting RL to %d\n", mcdu
->dwValue
);
1318 val
= 0x101 * ((mcdu
->dwValue
* 100) >> 16);
1321 /* stereo, left is paDetails[0] */
1322 TRACE("Setting L to %d\n", mcdu
->dwValue
);
1323 val
= ((mcdu
->dwValue
* 100.0) / 65536.0) + 0.5;
1325 TRACE("Setting R to %d\n", mcdu
->dwValue
);
1326 val
+= (int)(((mcdu
->dwValue
* 100) / 65536.0) + 0.5) << 8;
1329 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
1330 return MMSYSERR_INVALPARAM
;
1333 if (mix
->volume
[chnl
] == -1)
1335 if (!MIX_SetVal(mix
, chnl
, val
)) {
1336 WARN("invalid parameter\n");
1337 return MMSYSERR_INVALPARAM
;
1342 mix
->volume
[chnl
] = val
;
1345 ret
= MMSYSERR_NOERROR
;
1347 case MIXERCONTROL_CONTROLTYPE_MUTE
:
1348 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
1350 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
1352 if (lpmcd
->cbDetails
!=
1353 sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
1354 WARN("invalid parameter: cbDetails != %d\n",
1355 sizeof(MIXERCONTROLDETAILS_BOOLEAN
));
1356 return MMSYSERR_INVALPARAM
;
1359 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1360 getControlType(mix
->ctrl
[c
].ctrl
.dwControlType
),
1363 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
1366 /* save the volume and then set it to 0 */
1367 if (!MIX_GetVal(mix
, chnl
, &mix
->volume
[chnl
]) ||
1368 !MIX_SetVal(mix
, chnl
, 0)) {
1369 WARN("invalid parameter\n");
1370 return MMSYSERR_INVALPARAM
;
1375 if (mix
->volume
[chnl
] == -1)
1377 ret
= MMSYSERR_NOERROR
;
1380 if (!MIX_SetVal(mix
, chnl
, mix
->volume
[chnl
])) {
1381 WARN("invalid parameter\n");
1382 return MMSYSERR_INVALPARAM
;
1384 mix
->volume
[chnl
] = -1;
1387 ret
= MMSYSERR_NOERROR
;
1389 case MIXERCONTROL_CONTROLTYPE_MIXER
:
1390 case MIXERCONTROL_CONTROLTYPE_MUX
:
1392 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
1396 if (lpmcd
->cbDetails
!=
1397 sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
1398 WARN("invalid parameter: cbDetails != %d\n",
1399 sizeof(MIXERCONTROLDETAILS_BOOLEAN
));
1400 return MMSYSERR_INVALPARAM
;
1403 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1404 getControlType(mix
->ctrl
[c
].ctrl
.dwControlType
),
1407 /* we mute both channels at the same time */
1408 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
1410 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
1412 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
) &&
1415 /* a mux can only select one line at a time... */
1416 if (mix
->singleRecChannel
&& mask
!= 0)
1419 return MMSYSERR_INVALPARAM
;
1421 mask
|= WINE_CHN_MASK(j
);
1424 if (i
!= lpmcd
->u
.cMultipleItems
)
1425 FIXME("bad count\n");
1426 TRACE("writing %04x as rec src\n", mask
);
1427 if (!MIX_SetRecSrc(mix
, mask
))
1428 ERR("Can't write new mixer settings\n");
1430 ret
= MMSYSERR_NOERROR
;
1437 WARN("Unknown SetControlDetails flag (%08lx)\n",
1438 fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
1443 /**************************************************************************
1444 * MIX_Init [internal]
1446 LRESULT
OSS_MixerInit(void)
1454 for (i
= 0; i
< MAX_MIXERDRV
; i
++) {
1458 sprintf(name
, "/dev/mixer");
1460 sprintf(name
, "/dev/mixer%d", i
);
1462 if ((mixer
= open(name
, O_RDWR
)) >= 0) {
1463 #ifdef SOUND_MIXER_INFO
1465 if (ioctl(mixer
, SOUND_MIXER_INFO
, &info
) >= 0) {
1466 MIX_Mixers
[MIX_NumMixers
].name
= HeapAlloc(GetProcessHeap(),0,strlen(info
.name
) + 1);
1467 strcpy(MIX_Mixers
[MIX_NumMixers
].name
, info
.name
);
1469 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
1470 * implement it properly, and there are probably similar issues
1471 * on other platforms, so we warn but try to go ahead.
1473 WARN("%s: cannot read SOUND_MIXER_INFO!\n", name
);
1478 MIX_Mixers
[MIX_NumMixers
].dev_name
= HeapAlloc(GetProcessHeap(),0,strlen(name
) + 1);
1479 strcpy(MIX_Mixers
[MIX_NumMixers
].dev_name
, name
);
1481 MIX_Open(MIX_NumMixers
- 1, NULL
, 0); /* FIXME */
1483 WARN("couldn't open %s\n", name
);
1487 if (MIX_NumMixers
== 0) {
1488 WARN("no driver\n");
1489 return MMSYSERR_NODRIVER
;
1492 return MMSYSERR_NOERROR
;
1495 /**************************************************************************
1496 * MIX_Exit [internal]
1498 LRESULT
OSS_MixerExit(void)
1504 for (i
= 0; i
< MIX_NumMixers
; i
++) {
1505 HeapFree(GetProcessHeap(),0,MIX_Mixers
[i
].name
);
1506 HeapFree(GetProcessHeap(),0,MIX_Mixers
[i
].dev_name
);
1509 return MMSYSERR_NOERROR
;
1512 /**************************************************************************
1513 * MIX_GetNumDevs [internal]
1515 static DWORD
MIX_GetNumDevs(void)
1519 return MIX_NumMixers
;
1522 #endif /* HAVE_OSS */
1524 /**************************************************************************
1525 * mxdMessage (WINEOSS.3)
1527 DWORD WINAPI
OSS_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
1528 DWORD dwParam1
, DWORD dwParam2
)
1531 TRACE("(%04X, %s, %08X, %08X, %08X);\n", wDevID
, getMessage(wMsg
),
1532 dwUser
, dwParam1
, dwParam2
);
1540 /* FIXME: Pretend this is supported */
1542 case MXDM_GETDEVCAPS
:
1543 return MIX_GetDevCaps(wDevID
, (LPMIXERCAPSW
)dwParam1
, dwParam2
);
1544 case MXDM_GETLINEINFO
:
1545 return MIX_GetLineInfo(wDevID
, (LPMIXERLINEW
)dwParam1
, dwParam2
);
1546 case MXDM_GETNUMDEVS
:
1547 return MIX_GetNumDevs();
1549 return MMSYSERR_NOERROR
;
1550 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1552 return MMSYSERR_NOERROR
;
1553 case MXDM_GETLINECONTROLS
:
1554 return MIX_GetLineControls(wDevID
, (LPMIXERLINECONTROLSW
)dwParam1
, dwParam2
);
1555 case MXDM_GETCONTROLDETAILS
:
1556 return MIX_GetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1557 case MXDM_SETCONTROLDETAILS
:
1558 return MIX_SetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1560 WARN("unknown message %d!\n", wMsg
);
1561 return MMSYSERR_NOTSUPPORTED
;
1564 TRACE("(%04X, %04X, %08X, %08X, %08X);\n", wDevID
, wMsg
,
1565 dwUser
, dwParam1
, dwParam2
);
1567 return MMSYSERR_NOTENABLED
;