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
19 #include <sys/ioctl.h>
23 #include "debugtools.h"
25 DEFAULT_DEBUG_CHANNEL(mmaux
);
29 #define WINE_MIXER_MANUF_ID 0xAA
30 #define WINE_MIXER_PRODUCT_ID 0x55
31 #define WINE_MIXER_VERSION 0x0100
32 #define WINE_MIXER_NAME "WINE OSS Mixer"
34 #define WINE_CHN_MASK(_x) (1L << (_x))
35 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
36 /* Bass and Treble are no longer in the mask as Windows does not handle them */
37 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
38 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
39 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
40 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
41 WINE_CHN_MASK(SOUND_MIXER_CD) )
43 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
44 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
45 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
46 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
48 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
49 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
50 static char* MIX_Labels
[SOUND_MIXER_NRDEVICES
] = SOUND_DEVICE_LABELS
;
51 static char* MIX_Names
[SOUND_MIXER_NRDEVICES
] = SOUND_DEVICE_NAMES
;
62 int volume
[SOUND_MIXER_NRDEVICES
];
66 BOOL singleRecChannel
;
67 struct mixerCtrl
* ctrl
;
71 #define LINEID_DST 0xFFFF
72 #define LINEID_SPEAKER 0x0000
73 #define LINEID_RECORD 0x0001
75 static int MIX_NumMixers
;
76 static struct mixer MIX_Mixers
[1];
78 /**************************************************************************
79 * MIX_FillLineControls [internal]
81 static void MIX_FillLineControls(struct mixer
* mix
, int c
, DWORD lineID
, DWORD dwType
)
83 struct mixerCtrl
* mc
= &mix
->ctrl
[c
];
86 mc
->dwLineID
= lineID
;
87 mc
->ctrl
.cbStruct
= sizeof(MIXERCONTROLA
);
88 mc
->ctrl
.dwControlID
= c
+ 1;
89 mc
->ctrl
.dwControlType
= dwType
;
93 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
94 mc
->ctrl
.fdwControl
= 0;
95 mc
->ctrl
.cMultipleItems
= 0;
96 lstrcpynA(mc
->ctrl
.szShortName
, "Vol", MIXER_SHORT_NAME_CHARS
);
97 lstrcpynA(mc
->ctrl
.szName
, "Volume", MIXER_LONG_NAME_CHARS
);
98 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
99 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
100 * [0, 100] is the range supported by OSS
101 * whatever the min and max values are they must match
102 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
104 mc
->ctrl
.Bounds
.s1
.dwMinimum
= 0;
105 mc
->ctrl
.Bounds
.s1
.dwMaximum
= 65535;
106 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
108 case MIXERCONTROL_CONTROLTYPE_MUTE
:
109 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
110 mc
->ctrl
.fdwControl
= 0;
111 mc
->ctrl
.cMultipleItems
= 0;
112 lstrcpynA(mc
->ctrl
.szShortName
, "Mute", MIXER_SHORT_NAME_CHARS
);
113 lstrcpynA(mc
->ctrl
.szName
, "Mute", MIXER_LONG_NAME_CHARS
);
114 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
115 mc
->ctrl
.Bounds
.s1
.dwMinimum
= 0;
116 mc
->ctrl
.Bounds
.s1
.dwMaximum
= 1;
117 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
119 case MIXERCONTROL_CONTROLTYPE_MUX
:
120 case MIXERCONTROL_CONTROLTYPE_MIXER
:
121 mc
->ctrl
.fdwControl
= MIXERCONTROL_CONTROLF_MULTIPLE
;
122 mc
->ctrl
.cMultipleItems
= 0;
123 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
124 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
125 mc
->ctrl
.cMultipleItems
++;
126 lstrcpynA(mc
->ctrl
.szShortName
, "Mixer", MIXER_SHORT_NAME_CHARS
);
127 lstrcpynA(mc
->ctrl
.szName
, "Mixer", MIXER_LONG_NAME_CHARS
);
128 memset(&mc
->ctrl
.Bounds
, 0, sizeof(mc
->ctrl
.Bounds
));
129 memset(&mc
->ctrl
.Metrics
, 0, sizeof(mc
->ctrl
.Metrics
));
133 FIXME("Internal error: unknown type: %08lx\n", dwType
);
135 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c
+ 1, dwType
, lineID
);
138 /******************************************************************
143 static struct mixer
* MIX_Get(WORD wDevID
)
145 if (wDevID
>= MIX_NumMixers
|| MIX_Mixers
[wDevID
].name
== NULL
) return NULL
;
146 return &MIX_Mixers
[wDevID
];
149 /**************************************************************************
150 * MIX_Open [internal]
152 static DWORD
MIX_Open(WORD wDevID
, LPMIXEROPENDESC lpMod
, DWORD flags
)
157 DWORD ret
= MMSYSERR_NOERROR
;
159 TRACE("(%04X, %p, %lu);\n", wDevID
, lpMod
, flags
);
161 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
162 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
163 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
164 * messages to it... it seems to be linked to all the equivalent of mixer identification
165 * (with a reference to a wave, midi.. handle
167 if (!(mix
= MIX_Get(wDevID
))) return MMSYSERR_BADDEVICEID
;
169 if ((mixer
= open(mix
->name
, O_RDWR
)) < 0)
171 if (errno
== ENODEV
|| errno
== ENXIO
)
173 /* no driver present */
174 return MMSYSERR_NODRIVER
;
176 return MMSYSERR_ERROR
;
179 if (ioctl(mixer
, SOUND_MIXER_READ_DEVMASK
, &mix
->devMask
) == -1)
181 perror("ioctl mixer SOUND_MIXER_DEVMASK");
182 ret
= MMSYSERR_ERROR
;
185 mix
->devMask
&= WINE_MIXER_MASK_SPEAKER
;
186 if (mix
->devMask
== 0)
188 ret
= MMSYSERR_NODRIVER
;
192 if (ioctl(mixer
, SOUND_MIXER_READ_STEREODEVS
, &mix
->stereoMask
) == -1)
194 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
195 ret
= MMSYSERR_ERROR
;
198 mix
->stereoMask
&= WINE_MIXER_MASK_SPEAKER
;
200 if (ioctl(mixer
, SOUND_MIXER_READ_RECMASK
, &mix
->recMask
) == -1)
202 perror("ioctl mixer SOUND_MIXER_RECMASK");
203 ret
= MMSYSERR_ERROR
;
206 mix
->recMask
&= WINE_MIXER_MASK_RECORD
;
207 /* FIXME: we may need to support both rec lev & igain */
208 if (!WINE_CHN_SUPPORTS(mix
->recMask
, SOUND_MIXER_RECLEV
))
210 WARN("The sound card doesn't support rec level\n");
211 if (WINE_CHN_SUPPORTS(mix
->recMask
, SOUND_MIXER_IGAIN
))
212 WARN("but it does support IGain, please report\n");
214 if (ioctl(mixer
, SOUND_MIXER_READ_CAPS
, &caps
) == -1)
216 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
217 ret
= MMSYSERR_ERROR
;
220 mix
->singleRecChannel
= caps
& SOUND_CAP_EXCL_INPUT
;
221 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
222 mix
->devMask
, mix
->recMask
, mix
->stereoMask
,
223 mix
->singleRecChannel
? "single" : "multiple");
224 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
228 mix
->numCtrl
= 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
229 /* FIXME: do we always have RECLEV on all cards ??? */
230 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
232 if (WINE_CHN_SUPPORTS(mix
->devMask
, i
))
233 mix
->numCtrl
+= 2; /* volume & mute */
234 if (WINE_CHN_SUPPORTS(mix
->recMask
, i
))
235 mix
->numCtrl
+= 2; /* volume & onoff */
238 if (!(mix
->ctrl
= HeapAlloc(GetProcessHeap(), 0, sizeof(mix
->ctrl
[0]) * mix
->numCtrl
)))
240 ret
= MMSYSERR_NOMEM
;
245 MIX_FillLineControls(mix
, j
++, MAKELONG(0, LINEID_DST
), MIXERCONTROL_CONTROLTYPE_VOLUME
);
246 MIX_FillLineControls(mix
, j
++, MAKELONG(0, LINEID_DST
), MIXERCONTROL_CONTROLTYPE_MUTE
);
247 MIX_FillLineControls(mix
, j
++, MAKELONG(1, LINEID_DST
),
248 mix
->singleRecChannel
?
249 MIXERCONTROL_CONTROLTYPE_MUX
: MIXERCONTROL_CONTROLTYPE_MIXER
);
250 MIX_FillLineControls(mix
, j
++, MAKELONG(1, LINEID_DST
), MIXERCONTROL_CONTROLTYPE_MUTE
/*EPP*/);
251 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
253 if (WINE_CHN_SUPPORTS(mix
->devMask
, i
))
255 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_SPEAKER
, i
),
256 MIXERCONTROL_CONTROLTYPE_VOLUME
);
257 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_SPEAKER
, i
),
258 MIXERCONTROL_CONTROLTYPE_MUTE
);
261 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
263 if (WINE_CHN_SUPPORTS(mix
->recMask
, i
))
265 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_RECORD
, i
),
266 MIXERCONTROL_CONTROLTYPE_VOLUME
);
267 MIX_FillLineControls(mix
, j
++, MAKELONG(LINEID_RECORD
, i
),
268 MIXERCONTROL_CONTROLTYPE_MUTE
/*EPP*/);
271 assert(j
== mix
->numCtrl
);
277 /**************************************************************************
278 * MIX_GetVal [internal]
280 static BOOL
MIX_GetVal(struct mixer
* mix
, int chn
, int* val
)
285 if ((mixer
= open(mix
->name
, O_RDWR
)) < 0)
287 /* FIXME: ENXIO => no mixer installed */
288 WARN("mixer device not available !\n");
292 if (ioctl(mixer
, MIXER_READ(chn
), val
) >= 0)
294 TRACE("Reading volume %x on %d\n", *val
, chn
);
302 /**************************************************************************
303 * MIX_SetVal [internal]
305 static BOOL
MIX_SetVal(struct mixer
* mix
, int chn
, int val
)
310 TRACE("Writing volume %x on %d\n", val
, chn
);
312 if ((mixer
= open(mix
->name
, O_RDWR
)) < 0)
314 /* FIXME: ENXIO => no mixer installed */
315 WARN("mixer device not available !\n");
319 if (ioctl(mixer
, MIXER_WRITE(chn
), &val
) >= 0)
328 /******************************************************************
333 static BOOL
MIX_GetRecSrc(struct mixer
* mix
, unsigned* mask
)
338 if ((mixer
= open(mix
->name
, O_RDWR
)) >= 0)
340 if (ioctl(mixer
, SOUND_MIXER_READ_RECSRC
, &mask
) >= 0) ret
= TRUE
;
346 /******************************************************************
351 static BOOL
MIX_SetRecSrc(struct mixer
* mix
, unsigned mask
)
356 if ((mixer
= open(mix
->name
, O_RDWR
)) >= 0)
358 if (ioctl(mixer
, SOUND_MIXER_WRITE_RECSRC
, &mask
) < 0)
360 ERR("Can't write new mixer settings\n");
369 /**************************************************************************
370 * MIX_GetDevCaps [internal]
372 static DWORD
MIX_GetDevCaps(WORD wDevID
, LPMIXERCAPSA lpCaps
, DWORD dwSize
)
376 TRACE("(%04X, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
378 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
379 if (!(mix
= MIX_Get(wDevID
))) return MMSYSERR_BADDEVICEID
;
381 lpCaps
->wMid
= WINE_MIXER_MANUF_ID
;
382 lpCaps
->wPid
= WINE_MIXER_PRODUCT_ID
;
383 lpCaps
->vDriverVersion
= WINE_MIXER_VERSION
;
384 strcpy(lpCaps
->szPname
, WINE_MIXER_NAME
);
386 lpCaps
->cDestinations
= 2; /* speakers & record */
387 lpCaps
->fdwSupport
= 0; /* No bits defined yet */
389 return MMSYSERR_NOERROR
;
392 /**************************************************************************
393 * MIX_GetLineInfoDst [internal]
395 static DWORD
MIX_GetLineInfoDst(struct mixer
* mix
, LPMIXERLINEA lpMl
, DWORD dst
)
400 lpMl
->dwDestination
= dst
;
404 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
406 j
= SOUND_MIXER_VOLUME
;
409 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_WAVEIN
;
411 j
= SOUND_MIXER_RECLEV
;
414 FIXME("shouldn't happen\n");
415 return MMSYSERR_ERROR
;
417 lpMl
->dwSource
= 0xFFFFFFFF;
418 lstrcpynA(lpMl
->szShortName
, MIX_Labels
[j
], MIXER_SHORT_NAME_CHARS
);
419 lstrcpynA(lpMl
->szName
, MIX_Names
[j
], MIXER_LONG_NAME_CHARS
);
421 /* we have all connections found in the MIX_DevMask */
422 lpMl
->cConnections
= 0;
423 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
424 if (WINE_CHN_SUPPORTS(mask
, j
))
425 lpMl
->cConnections
++;
427 if (WINE_CHN_SUPPORTS(mix
->stereoMask
, lpMl
->dwLineID
))
429 lpMl
->dwLineID
= MAKELONG(dst
, LINEID_DST
);
431 for (j
= 0; j
< mix
->numCtrl
; j
++)
432 if (mix
->ctrl
[j
].dwLineID
== lpMl
->dwLineID
)
435 return MMSYSERR_NOERROR
;
438 /**************************************************************************
439 * MIX_GetLineInfoSrc [internal]
441 static DWORD
MIX_GetLineInfoSrc(struct mixer
* mix
, LPMIXERLINEA lpMl
, DWORD idx
, DWORD dst
)
444 unsigned mask
= (dst
) ? mix
->recMask
: mix
->devMask
;
446 strcpy(lpMl
->szShortName
, MIX_Labels
[idx
]);
447 strcpy(lpMl
->szName
, MIX_Names
[idx
]);
448 lpMl
->dwLineID
= MAKELONG(dst
, idx
);
449 lpMl
->dwDestination
= dst
;
450 lpMl
->cConnections
= 1;
452 for (i
= 0; i
< mix
->numCtrl
; i
++)
453 if (mix
->ctrl
[i
].dwLineID
== lpMl
->dwLineID
)
458 case SOUND_MIXER_SYNTH
:
459 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
;
460 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
463 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
;
464 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
466 case SOUND_MIXER_LINE
:
467 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_LINE
;
468 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
470 case SOUND_MIXER_MIC
:
471 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
;
472 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
474 case SOUND_MIXER_PCM
:
475 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
;
476 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
478 case SOUND_MIXER_IMIX
:
479 lpMl
->dwComponentType
= MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
;
480 lpMl
->fdwLine
|= MIXERLINE_LINEF_SOURCE
;
483 WARN("Index %ld not handled.\n", idx
);
484 return MIXERR_INVALLINE
;
487 if (dst
== 0 && WINE_CHN_SUPPORTS(mix
->stereoMask
, idx
))
489 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
491 if (WINE_CHN_SUPPORTS(mask
, j
))
498 return MMSYSERR_NOERROR
;
501 /******************************************************************
504 static BOOL
MIX_CheckLine(DWORD lineID
)
506 return ((HIWORD(lineID
) < SOUND_MIXER_NRDEVICES
&& LOWORD(lineID
) < 2) ||
507 (HIWORD(lineID
) == LINEID_DST
&& LOWORD(lineID
) < SOUND_MIXER_NRDEVICES
));
510 /**************************************************************************
511 * MIX_GetLineInfo [internal]
513 static DWORD
MIX_GetLineInfo(WORD wDevID
, LPMIXERLINEA lpMl
, DWORD fdwInfo
)
516 DWORD ret
= MMSYSERR_NOERROR
;
520 TRACE("(%04X, %p, %lu);\n", wDevID
, lpMl
, fdwInfo
);
522 if (lpMl
== NULL
|| lpMl
->cbStruct
!= sizeof(*lpMl
))
523 return MMSYSERR_INVALPARAM
;
524 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
526 /* FIXME: set all the variables correctly... the lines below
529 lpMl
->fdwLine
= MIXERLINE_LINEF_ACTIVE
;
532 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
)
534 case MIXER_GETLINEINFOF_DESTINATION
:
535 TRACE("DESTINATION (%08lx)\n", lpMl
->dwDestination
);
536 if (lpMl
->dwDestination
>= 2)
537 return MMSYSERR_INVALPARAM
;
538 if ((ret
= MIX_GetLineInfoDst(mix
, lpMl
, lpMl
->dwDestination
)) != MMSYSERR_NOERROR
)
541 case MIXER_GETLINEINFOF_SOURCE
:
542 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl
->dwSource
, lpMl
->dwDestination
);
543 switch (lpMl
->dwDestination
)
545 case 0: mask
= mix
->devMask
; break;
546 case 1: mask
= mix
->recMask
; break;
547 default: return MMSYSERR_INVALPARAM
;
550 for (j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
552 if (WINE_CHN_SUPPORTS(mask
, j
) && (i
-- == 0))
555 if (j
>= SOUND_MIXER_NRDEVICES
)
556 return MIXERR_INVALLINE
;
557 if ((ret
= MIX_GetLineInfoSrc(mix
, lpMl
, j
, lpMl
->dwDestination
)) != MMSYSERR_NOERROR
)
560 case MIXER_GETLINEINFOF_LINEID
:
561 TRACE("LINEID (%08lx)\n", lpMl
->dwLineID
);
563 if (!MIX_CheckLine(lpMl
->dwLineID
))
564 return MIXERR_INVALLINE
;
565 if (HIWORD(lpMl
->dwLineID
) == LINEID_DST
)
566 ret
= MIX_GetLineInfoDst(mix
, lpMl
, LOWORD(lpMl
->dwLineID
));
568 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, HIWORD(lpMl
->dwLineID
), LOWORD(lpMl
->dwLineID
));
569 if (ret
!= MMSYSERR_NOERROR
)
572 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
573 TRACE("COMPONENT TYPE (%08lx)\n", lpMl
->dwComponentType
);
574 switch (lpMl
->dwComponentType
)
576 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
:
577 ret
= MIX_GetLineInfoDst(mix
, lpMl
, 0);
579 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN
:
580 ret
= MIX_GetLineInfoDst(mix
, lpMl
, 1);
582 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
:
583 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_SYNTH
, 0);
585 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
:
586 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_CD
, 0);
588 case MIXERLINE_COMPONENTTYPE_SRC_LINE
:
589 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_LINE
, 0);
591 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
:
592 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_MIC
, 1);
594 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
:
595 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_PCM
, 0);
597 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
:
598 ret
= MIX_GetLineInfoSrc(mix
, lpMl
, SOUND_MIXER_IMIX
, 1);
601 FIXME("Unhandled component type (%08lx)\n", lpMl
->dwComponentType
);
602 return MMSYSERR_INVALPARAM
;
605 case MIXER_GETLINEINFOF_TARGETTYPE
:
606 FIXME("_TARGETTYPE not implemented yet.\n");
609 WARN("Unknown flag (%08lx)\n", fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
);
613 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_AUX
; /* FIXME */
614 lpMl
->Target
.dwDeviceID
= 0xFFFFFFFF;
615 lpMl
->Target
.wMid
= WINE_MIXER_MANUF_ID
;
616 lpMl
->Target
.wPid
= WINE_MIXER_PRODUCT_ID
;
617 lpMl
->Target
.vDriverVersion
= WINE_MIXER_VERSION
;
618 strcpy(lpMl
->Target
.szPname
, WINE_MIXER_NAME
);
623 /******************************************************************
627 static BOOL
MIX_CheckControl(struct mixer
* mix
, DWORD ctrlID
)
629 return (ctrlID
>= 1 && ctrlID
<= mix
->numCtrl
);
632 /**************************************************************************
633 * MIX_GetLineControls [internal]
635 static DWORD
MIX_GetLineControls(WORD wDevID
, LPMIXERLINECONTROLSA lpMlc
, DWORD flags
)
637 DWORD dwRet
= MMSYSERR_NOERROR
;
640 TRACE("(%04X, %p, %lu);\n", wDevID
, lpMlc
, flags
);
642 if (lpMlc
== NULL
) return MMSYSERR_INVALPARAM
;
643 if (lpMlc
->cbStruct
< sizeof(*lpMlc
) ||
644 lpMlc
->cbmxctrl
< sizeof(MIXERCONTROLA
))
645 return MMSYSERR_INVALPARAM
;
646 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
648 switch (flags
& MIXER_GETLINECONTROLSF_QUERYMASK
)
650 case MIXER_GETLINECONTROLSF_ALL
:
654 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc
->dwLineID
, lpMlc
->cControls
);
656 for (i
= j
= 0; i
< mix
->numCtrl
; i
++)
658 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
)
661 if (!j
|| lpMlc
->cControls
!= j
) dwRet
= MMSYSERR_INVALPARAM
;
662 else if (!MIX_CheckLine(lpMlc
->dwLineID
)) dwRet
= MIXERR_INVALLINE
;
665 for (i
= j
= 0; i
< mix
->numCtrl
; i
++)
667 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
)
669 TRACE("[%d] => [%2d]: typ=%08lx\n", j
, i
+ 1, mix
->ctrl
[i
].ctrl
.dwControlType
);
670 lpMlc
->pamxctrl
[j
++] = mix
->ctrl
[i
].ctrl
;
676 case MIXER_GETLINECONTROLSF_ONEBYID
:
677 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlID
);
679 if (!MIX_CheckControl(mix
, lpMlc
->u
.dwControlID
) ||
680 mix
->ctrl
[lpMlc
->u
.dwControlID
- 1].dwLineID
!= lpMlc
->dwLineID
)
681 dwRet
= MMSYSERR_INVALPARAM
;
683 lpMlc
->pamxctrl
[0] = mix
->ctrl
[lpMlc
->u
.dwControlID
- 1].ctrl
;
685 case MIXER_GETLINECONTROLSF_ONEBYTYPE
:
686 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlType
);
687 if (!MIX_CheckLine(lpMlc
->dwLineID
)) dwRet
= MIXERR_INVALLINE
;
691 DWORD ct
= lpMlc
->u
.dwControlType
& MIXERCONTROL_CT_CLASS_MASK
;
693 for (i
= 0; i
< mix
->numCtrl
; i
++)
695 if (mix
->ctrl
[i
].dwLineID
== lpMlc
->dwLineID
&&
696 ct
== (mix
->ctrl
[i
].ctrl
.dwControlType
& MIXERCONTROL_CT_CLASS_MASK
))
698 lpMlc
->pamxctrl
[0] = mix
->ctrl
[i
].ctrl
;
702 if (i
== mix
->numCtrl
) dwRet
= MMSYSERR_INVALPARAM
;
706 ERR("Unknown flag %08lx\n", flags
& MIXER_GETLINECONTROLSF_QUERYMASK
);
707 dwRet
= MMSYSERR_INVALPARAM
;
713 /**************************************************************************
714 * MIX_GetControlDetails [internal]
716 static DWORD
MIX_GetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD fdwDetails
)
718 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
722 TRACE("(%04X, %p, %lu);\n", wDevID
, lpmcd
, fdwDetails
);
724 if (lpmcd
== NULL
) return MMSYSERR_INVALPARAM
;
725 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
727 switch (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
)
729 case MIXER_GETCONTROLDETAILSF_VALUE
:
730 TRACE("GCD VALUE (%08lx)\n", lpmcd
->dwControlID
);
731 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
733 c
= lpmcd
->dwControlID
- 1;
734 chnl
= HIWORD(mix
->ctrl
[c
].dwLineID
);
735 if (chnl
== LINEID_DST
)
736 chnl
= LOWORD(mix
->ctrl
[c
].dwLineID
) ? SOUND_MIXER_RECLEV
: SOUND_MIXER_VOLUME
;
737 switch (mix
->ctrl
[c
].ctrl
.dwControlType
)
739 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
741 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
744 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED
), lpmcd
->cbDetails
);
745 /* return value is 00RL (4 bytes)... */
746 if ((val
= mix
->volume
[chnl
]) == -1 && !MIX_GetVal(mix
, chnl
, &val
))
747 return MMSYSERR_INVALPARAM
;
749 switch (lpmcd
->cChannels
)
752 /* mono... so R = L */
753 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
754 mcdu
->dwValue
= (LOBYTE(LOWORD(val
)) * 65536L) / 100;
757 /* stereo, left is paDetails[0] */
758 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 0 * lpmcd
->cbDetails
);
759 mcdu
->dwValue
= (LOBYTE(LOWORD(val
)) * 65536L) / 100;
760 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 1 * lpmcd
->cbDetails
);
761 mcdu
->dwValue
= (HIBYTE(LOWORD(val
)) * 65536L) / 100;
764 WARN("Unknown cChannels (%ld)\n", lpmcd
->cChannels
);
765 return MMSYSERR_INVALPARAM
;
767 TRACE("=> %08lx\n", mcdu
->dwValue
);
770 case MIXERCONTROL_CONTROLTYPE_MUTE
:
771 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
773 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
775 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
776 /* we mute both channels at the same time */
777 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
778 mcdb
->fValue
= (mix
->volume
[chnl
] != -1);
779 TRACE("=> %s\n", mcdb
->fValue
? "on" : "off");
782 case MIXERCONTROL_CONTROLTYPE_MIXER
:
783 case MIXERCONTROL_CONTROLTYPE_MUX
:
787 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
788 if (!MIX_GetRecSrc(mix
, &mask
))
790 /* FIXME: ENXIO => no mixer installed */
791 WARN("mixer device not available !\n");
792 ret
= MMSYSERR_ERROR
;
796 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
799 /* we mute both channels at the same time */
800 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
802 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
804 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
806 if (i
>= lpmcd
->u
.cMultipleItems
)
807 return MMSYSERR_INVALPARAM
;
808 mcdb
[i
++].fValue
= WINE_CHN_SUPPORTS(mask
, j
);
815 WARN("Unsupported\n");
817 ret
= MMSYSERR_NOERROR
;
821 ret
= MMSYSERR_INVALPARAM
;
824 case MIXER_GETCONTROLDETAILSF_LISTTEXT
:
825 TRACE("LIST TEXT (%08lx)\n", lpmcd
->dwControlID
);
827 ret
= MMSYSERR_INVALPARAM
;
828 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
830 int c
= lpmcd
->dwControlID
- 1;
832 if (mix
->ctrl
[c
].ctrl
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MUX
||
833 mix
->ctrl
[c
].ctrl
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MIXER
)
835 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt
;
838 mcdlt
= (LPMIXERCONTROLDETAILS_LISTTEXTA
)lpmcd
->paDetails
;
839 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
841 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
))
843 mcdlt
[i
].dwParam1
= MAKELONG(LINEID_RECORD
, j
);
844 mcdlt
[i
].dwParam2
= 0;
845 strcpy(mcdlt
[i
].szName
, MIX_Names
[j
]);
849 if (i
!= lpmcd
->u
.cMultipleItems
) FIXME("bad count\n");
850 ret
= MMSYSERR_NOERROR
;
855 WARN("Unknown flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
860 /**************************************************************************
861 * MIX_SetControlDetails [internal]
863 static DWORD
MIX_SetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD fdwDetails
)
865 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
870 TRACE("(%04X, %p, %lu);\n", wDevID
, lpmcd
, fdwDetails
);
872 if (lpmcd
== NULL
) return MMSYSERR_INVALPARAM
;
873 if ((mix
= MIX_Get(wDevID
)) == NULL
) return MMSYSERR_BADDEVICEID
;
875 switch (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
)
877 case MIXER_GETCONTROLDETAILSF_VALUE
:
878 TRACE("GCD VALUE (%08lx)\n", lpmcd
->dwControlID
);
879 if (MIX_CheckControl(mix
, lpmcd
->dwControlID
))
881 c
= lpmcd
->dwControlID
- 1;
882 chnl
= HIWORD(mix
->ctrl
[c
].dwLineID
);
883 if (chnl
== LINEID_DST
)
884 chnl
= LOWORD(mix
->ctrl
[c
].dwLineID
) ? SOUND_MIXER_RECLEV
: SOUND_MIXER_VOLUME
;
886 switch (mix
->ctrl
[c
].ctrl
.dwControlType
)
888 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
890 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
892 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED
), lpmcd
->cbDetails
);
893 /* val should contain 00RL */
894 switch (lpmcd
->cChannels
)
897 /* mono... so R = L */
898 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
899 TRACE("Setting RL to %08ld\n", mcdu
->dwValue
);
900 val
= 0x101 * ((mcdu
->dwValue
* 100) >> 16);
903 /* stereo, left is paDetails[0] */
904 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 0 * lpmcd
->cbDetails
);
905 TRACE("Setting L to %08ld\n", mcdu
->dwValue
);
906 val
= ((mcdu
->dwValue
* 100) >> 16);
907 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)((char*)lpmcd
->paDetails
+ 1 * lpmcd
->cbDetails
);
908 TRACE("Setting R to %08ld\n", mcdu
->dwValue
);
909 val
+= ((mcdu
->dwValue
* 100) >> 16) << 8;
912 WARN("Unknown cChannels (%ld)\n", lpmcd
->cChannels
);
913 return MMSYSERR_INVALPARAM
;
916 if (mix
->volume
[chnl
] == -1)
918 if (!MIX_SetVal(mix
, chnl
, val
))
919 return MMSYSERR_INVALPARAM
;
923 mix
->volume
[chnl
] = val
;
926 ret
= MMSYSERR_NOERROR
;
928 case MIXERCONTROL_CONTROLTYPE_MUTE
:
929 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
931 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
933 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
934 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
937 if (!MIX_GetVal(mix
, chnl
, &mix
->volume
[chnl
]) || !MIX_SetVal(mix
, chnl
, 0))
938 return MMSYSERR_INVALPARAM
;
942 if (mix
->volume
[chnl
] == -1)
944 ret
= MMSYSERR_NOERROR
;
947 if (!MIX_SetVal(mix
, chnl
, mix
->volume
[chnl
]))
948 return MMSYSERR_INVALPARAM
;
949 mix
->volume
[chnl
] = -1;
952 ret
= MMSYSERR_NOERROR
;
954 case MIXERCONTROL_CONTROLTYPE_MIXER
:
955 case MIXERCONTROL_CONTROLTYPE_MUX
:
957 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
961 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN
), lpmcd
->cbDetails
);
962 /* we mute both channels at the same time */
963 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
965 for (i
= j
= 0; j
< SOUND_MIXER_NRDEVICES
; j
++)
967 if (WINE_CHN_SUPPORTS(mix
->recMask
, j
) && mcdb
[i
++].fValue
)
969 /* a mux can only select one line at a time... */
970 if (mix
->singleRecChannel
&& mask
!= 0)
973 return MMSYSERR_INVALPARAM
;
975 mask
|= WINE_CHN_MASK(j
);
978 if (i
!= lpmcd
->u
.cMultipleItems
) FIXME("bad count\n");
979 TRACE("writing %04x as rec src\n", mask
);
980 if (!MIX_SetRecSrc(mix
, mask
))
981 ERR("Can't write new mixer settings\n");
983 ret
= MMSYSERR_NOERROR
;
990 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
995 /**************************************************************************
996 * MIX_Init [internal]
998 static DWORD
MIX_Init(void)
1002 #define MIXER_DEV "/dev/mixer"
1003 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1005 if (errno
== ENODEV
|| errno
== ENXIO
)
1007 /* no driver present */
1008 return MMSYSERR_NODRIVER
;
1011 return MMSYSERR_ERROR
;
1015 MIX_Mixers
[0].name
= MIXER_DEV
;
1016 MIX_Open(0, NULL
, 0); /* FIXME */
1018 return MMSYSERR_NOERROR
;
1021 /**************************************************************************
1022 * MIX_GetNumDevs [internal]
1024 static DWORD
MIX_GetNumDevs(void)
1026 return MIX_NumMixers
;
1029 #endif /* HAVE_OSS */
1031 /**************************************************************************
1032 * mixMessage (WINEOSS.3)
1034 DWORD WINAPI
OSS_mixMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
1035 DWORD dwParam1
, DWORD dwParam2
)
1037 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1047 /* FIXME: Pretend this is supported */
1049 case MXDM_GETDEVCAPS
:
1050 return MIX_GetDevCaps(wDevID
, (LPMIXERCAPSA
)dwParam1
, dwParam2
);
1051 case MXDM_GETLINEINFO
:
1052 return MIX_GetLineInfo(wDevID
, (LPMIXERLINEA
)dwParam1
, dwParam2
);
1053 case MXDM_GETNUMDEVS
:
1054 return MIX_GetNumDevs();
1056 return MMSYSERR_NOERROR
;
1057 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1059 return MMSYSERR_NOERROR
;
1060 case MXDM_GETLINECONTROLS
:
1061 return MIX_GetLineControls(wDevID
, (LPMIXERLINECONTROLSA
)dwParam1
, dwParam2
);
1062 case MXDM_GETCONTROLDETAILS
:
1063 return MIX_GetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1064 case MXDM_SETCONTROLDETAILS
:
1065 return MIX_SetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1067 WARN("unknown message %d!\n", wMsg
);
1068 return MMSYSERR_NOTSUPPORTED
;
1071 return MMSYSERR_NOTENABLED
;