2 * Sample MIXER Wine Driver for Mac OS X (based on OSS mixer)
4 * Copyright 1997 Marcus Meissner
5 * 1999,2001 Eric Pouech
6 * 2006,2007 Emmanuel Maillard
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/port.h"
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
40 #include "coreaudio.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(mixer
);
46 #include <CoreAudio/CoreAudio.h>
47 #include <CoreFoundation/CoreFoundation.h>
49 #define WINE_MIXER_NAME "CoreAudio Mixer"
51 #define InputDevice (1 << 0)
52 #define OutputDevice (1 << 1)
54 #define IsInput(dir) ((dir) & InputDevice)
55 #define IsOutput(dir) ((dir) & OutputDevice)
57 #define ControlsPerLine 2 /* number of control per line : volume & (mute | onoff) */
59 #define IDControlVolume 0
60 #define IDControlMute 1
62 typedef struct tagMixerLine
68 AudioDeviceID deviceID
;
71 typedef struct tagMixerCtrl
77 typedef struct tagCoreAudio_Mixer
81 MixerCtrl
*mixerCtrls
;
86 static CoreAudio_Mixer mixer
;
87 static int numMixers
= 1;
89 /**************************************************************************
92 static const char * getMessage(UINT uMsg
)
94 #define MSG_TO_STR(x) case x: return #x;
96 MSG_TO_STR(DRVM_INIT
);
97 MSG_TO_STR(DRVM_EXIT
);
98 MSG_TO_STR(DRVM_ENABLE
);
99 MSG_TO_STR(DRVM_DISABLE
);
100 MSG_TO_STR(MXDM_GETDEVCAPS
);
101 MSG_TO_STR(MXDM_GETLINEINFO
);
102 MSG_TO_STR(MXDM_GETNUMDEVS
);
103 MSG_TO_STR(MXDM_OPEN
);
104 MSG_TO_STR(MXDM_CLOSE
);
105 MSG_TO_STR(MXDM_GETLINECONTROLS
);
106 MSG_TO_STR(MXDM_GETCONTROLDETAILS
);
107 MSG_TO_STR(MXDM_SETCONTROLDETAILS
);
110 return wine_dbg_sprintf("UNKNOWN(%08x)", uMsg
);
113 static const char * getControlType(DWORD dwControlType
)
115 #define TYPE_TO_STR(x) case x: return #x;
116 switch (dwControlType
) {
117 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM
);
118 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER
);
119 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER
);
120 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER
);
121 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER
);
122 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN
);
123 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF
);
124 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE
);
125 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO
);
126 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS
);
127 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH
);
128 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST
);
129 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON
);
130 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS
);
131 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED
);
132 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED
);
133 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT
);
134 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER
);
135 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN
);
136 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN
);
137 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER
);
138 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME
);
139 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS
);
140 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE
);
141 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER
);
142 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT
);
143 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX
);
144 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT
);
145 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER
);
146 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME
);
147 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME
);
150 return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType
);
153 static const char * getComponentType(DWORD dwComponentType
)
155 #define TYPE_TO_STR(x) case x: return #x;
156 switch (dwComponentType
) {
157 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED
);
158 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL
);
159 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE
);
160 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR
);
161 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
);
162 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
);
163 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE
);
164 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN
);
165 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN
);
166 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
);
167 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL
);
168 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE
);
169 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
);
170 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
);
171 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
);
172 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE
);
173 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER
);
174 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
);
175 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
);
176 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG
);
179 return wine_dbg_sprintf("UNKNOWN(%08x)", dwComponentType
);
182 static const char * getTargetType(DWORD dwType
)
184 #define TYPE_TO_STR(x) case x: return #x;
186 TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED
);
187 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT
);
188 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN
);
189 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT
);
190 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN
);
191 TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX
);
194 return wine_dbg_sprintf("UNKNOWN(%08x)", dwType
);
197 /* FIXME is there a better way ? */
198 static DWORD
DeviceComponentType(char *name
)
200 if (strcmp(name
, "Built-in Microphone") == 0)
201 return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
;
203 if (strcmp(name
, "Built-in Line Input") == 0)
204 return MIXERLINE_COMPONENTTYPE_SRC_LINE
;
206 if (strcmp(name
, "Built-in Output") == 0)
207 return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
209 return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
;
212 static BOOL
DeviceHasMute(AudioDeviceID deviceID
, Boolean isInput
)
214 Boolean writable
= false;
215 OSStatus err
= noErr
;
216 AudioObjectPropertyAddress propertyAddress
;
217 propertyAddress
.mSelector
= kAudioDevicePropertyMute
;
218 propertyAddress
.mScope
= isInput
? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
219 propertyAddress
.mElement
= 0;
220 if (AudioObjectHasProperty(deviceID
, &propertyAddress
))
222 /* check if we can set it */
223 err
= AudioObjectIsPropertySettable(deviceID
, &propertyAddress
, &writable
);
233 static BOOL
MIX_LineGetVolume(DWORD lineID
, DWORD channels
, Float32
*left
, Float32
*right
)
235 MixerLine
*line
= &mixer
.lines
[lineID
];
236 UInt32 size
= sizeof(Float32
);
237 OSStatus err
= noErr
;
238 AudioObjectPropertyAddress address
;
239 *left
= *right
= 0.0;
241 address
.mSelector
= kAudioDevicePropertyVolumeScalar
;
242 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
243 address
.mElement
= 1;
244 err
= AudioObjectGetPropertyData(line
->deviceID
, &address
, 0, NULL
, &size
, left
);
250 size
= sizeof(Float32
);
251 address
.mElement
= 2;
252 err
= AudioObjectGetPropertyData(line
->deviceID
, &address
, 0, NULL
, &size
, right
);
257 TRACE("lineID %d channels %d return left %f right %f\n", lineID
, channels
, *left
, *right
);
258 return (err
== noErr
);
261 static BOOL
MIX_LineGetMute(DWORD lineID
, BOOL
*muted
)
263 MixerLine
*line
= &mixer
.lines
[lineID
];
264 UInt32 size
= sizeof(UInt32
);
266 OSStatus err
= noErr
;
267 AudioObjectPropertyAddress address
;
268 address
.mSelector
= kAudioDevicePropertyMute
;
269 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
270 address
.mElement
= 0;
271 err
= AudioObjectGetPropertyData(line
->deviceID
, &address
, 0, NULL
, &size
, &val
);
274 return (err
== noErr
);
280 static BOOL
MIX_LineSetVolume(DWORD lineID
, DWORD channels
, Float32 left
, Float32 right
)
282 MixerLine
*line
= &mixer
.lines
[lineID
];
283 UInt32 size
= sizeof(Float32
);
284 AudioObjectPropertyAddress address
;
285 OSStatus err
= noErr
;
286 TRACE("lineID %d channels %d left %f right %f\n", lineID
, channels
, left
, right
);
288 address
.mSelector
= kAudioDevicePropertyVolumeScalar
;
289 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
292 address
.mElement
= 1;
293 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &left
);
297 address
.mElement
= 2;
298 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &right
);
303 FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
304 address.mElement = 0;
305 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
308 address
.mElement
= 1;
309 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &left
);
312 address
.mElement
= 2;
313 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &right
);
315 return (err
== noErr
);
318 static BOOL
MIX_LineSetMute(DWORD lineID
, BOOL mute
)
320 MixerLine
*line
= &mixer
.lines
[lineID
];
322 UInt32 size
= sizeof(UInt32
);
323 AudioObjectPropertyAddress address
;
324 OSStatus err
= noErr
;
326 address
.mSelector
= kAudioDevicePropertyMute
;
327 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
328 address
.mElement
= 0;
329 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, 0, size
, &val
);
330 return (err
== noErr
);
333 static void MIX_FillControls(void)
338 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
340 line
= &mixer
.lines
[i
];
341 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
342 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
343 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_VOLUME
;
344 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
345 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
346 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 65535;
347 mixer
.mixerCtrls
[ctrl
].ctrl
.Metrics
.cSteps
= 656;
350 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
351 if ( !DeviceHasMute(line
->deviceID
, IsInput(line
->direction
)) )
352 mixer
.mixerCtrls
[ctrl
].ctrl
.fdwControl
|= MIXERCONTROL_CONTROLF_DISABLED
;
354 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
355 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_MUTE
;
356 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
357 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
358 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 1;
361 assert(ctrl
== mixer
.numCtrl
);
364 /**************************************************************************
365 * CoreAudio_MixerInit
367 LONG
CoreAudio_MixerInit(void)
371 AudioObjectPropertyAddress propertyAddress
;
372 AudioDeviceID
*deviceArray
= NULL
;
377 AudioStreamBasicDescription streamDescription
;
379 /* Find number of lines */
380 propertyAddress
.mSelector
= kAudioHardwarePropertyDevices
;
381 propertyAddress
.mScope
= kAudioObjectPropertyScopeGlobal
;
382 propertyAddress
.mElement
= kAudioObjectPropertyElementMaster
;
383 status
= AudioObjectGetPropertyDataSize(kAudioObjectSystemObject
, &propertyAddress
, 0, NULL
, &propertySize
);
386 ERR("AudioObjectGetPropertyDataSize for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status
));
390 numLines
= propertySize
/ sizeof(AudioDeviceID
);
392 mixer
.mixerCtrls
= NULL
;
396 mixer
.caps
.cDestinations
= numLines
;
397 mixer
.caps
.wMid
= 0xAA;
398 mixer
.caps
.wPid
= 0x55;
399 mixer
.caps
.vDriverVersion
= 0x0100;
401 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, mixer
.caps
.szPname
, sizeof(mixer
.caps
.szPname
) / sizeof(WCHAR
));
403 mixer
.caps
.fdwSupport
= 0; /* No bits defined yet */
405 mixer
.lines
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerLine
) * numLines
);
409 deviceArray
= HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID
) * numLines
);
411 propertySize
= sizeof(AudioDeviceID
) * numLines
;
412 status
= AudioObjectGetPropertyData(kAudioObjectSystemObject
, &propertyAddress
, 0, NULL
, &propertySize
, deviceArray
);
415 ERR("AudioObjectGetPropertyData for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status
));
419 for (i
= 0; i
< numLines
; i
++)
421 MixerLine
*line
= &mixer
.lines
[i
];
423 line
->deviceID
= deviceArray
[i
];
425 propertySize
= sizeof(CFStringRef
);
426 propertyAddress
.mSelector
= kAudioObjectPropertyName
;
427 propertyAddress
.mScope
= kAudioObjectPropertyScopeGlobal
;
428 propertyAddress
.mElement
= kAudioObjectPropertyElementMaster
;
429 status
= AudioObjectGetPropertyData(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
, &name
);
431 ERR("AudioObjectGetPropertyData for kAudioObjectPropertyName return %s\n", wine_dbgstr_fourcc(status
));
435 line
->name
= HeapAlloc(GetProcessHeap(), 0, CFStringGetLength(name
) + 1);
439 CFStringGetCString(name
, line
->name
, CFStringGetLength(name
) + 1, kCFStringEncodingUTF8
);
441 line
->componentType
= DeviceComponentType(line
->name
);
443 /* check for directions */
445 propertySize
= sizeof(UInt32
);
446 propertyAddress
.mSelector
= kAudioDevicePropertyStreams
;
447 propertyAddress
.mScope
= kAudioDevicePropertyScopeOutput
;
448 status
= AudioObjectGetPropertyDataSize(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
);
450 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status
));
454 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
456 line
->direction
|= OutputDevice
;
458 /* Check the number of channel for the stream */
459 propertySize
= sizeof(streamDescription
);
460 propertyAddress
.mSelector
= kAudioDevicePropertyStreamFormat
;
461 status
= AudioObjectGetPropertyData(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
, &streamDescription
);
462 if (status
!= noErr
) {
463 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status
));
466 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
471 propertySize
= sizeof(UInt32
);
472 propertyAddress
.mScope
= kAudioDevicePropertyScopeInput
;
473 status
= AudioObjectGetPropertyDataSize(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
);
475 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status
));
478 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
480 line
->direction
|= InputDevice
;
482 /* Check the number of channel for the stream */
483 propertySize
= sizeof(streamDescription
);
484 propertyAddress
.mSelector
= kAudioDevicePropertyStreamFormat
;
485 status
= AudioObjectGetPropertyData(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
, &streamDescription
);
486 if (status
!= noErr
) {
487 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status
));
490 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
494 mixer
.numCtrl
+= ControlsPerLine
; /* volume & (mute | onoff) */
496 mixer
.mixerCtrls
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerCtrl
) * mixer
.numCtrl
);
497 if (!mixer
.mixerCtrls
)
502 HeapFree(GetProcessHeap(), 0, deviceArray
);
509 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
511 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
513 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
515 HeapFree(GetProcessHeap(), 0, deviceArray
);
516 if (mixer
.mixerCtrls
)
517 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
521 /**************************************************************************
522 * CoreAudio_MixerRelease
524 void CoreAudio_MixerRelease(void)
531 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
533 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
535 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
537 if (mixer
.mixerCtrls
)
538 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
541 /**************************************************************************
542 * MIX_Open [internal]
544 static DWORD
MIX_Open(WORD wDevID
, LPMIXEROPENDESC lpMod
, DWORD_PTR flags
)
546 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID
, lpMod
, flags
);
548 WARN("invalid parameter: lpMod == NULL\n");
549 return MMSYSERR_INVALPARAM
;
552 if (wDevID
>= numMixers
) {
553 WARN("bad device ID: %04X\n", wDevID
);
554 return MMSYSERR_BADDEVICEID
;
556 return MMSYSERR_NOERROR
;
559 /**************************************************************************
560 * MIX_GetNumDevs [internal]
562 static DWORD
MIX_GetNumDevs(void)
568 static DWORD
MIX_GetDevCaps(WORD wDevID
, LPMIXERCAPSW lpCaps
, DWORD_PTR dwSize
)
570 TRACE("wDevID=%d lpCaps=%p\n", wDevID
, lpCaps
);
572 if (lpCaps
== NULL
) {
573 WARN("Invalid Parameter\n");
574 return MMSYSERR_INVALPARAM
;
577 if (wDevID
>= numMixers
) {
578 WARN("bad device ID : %d\n", wDevID
);
579 return MMSYSERR_BADDEVICEID
;
581 memcpy(lpCaps
, &mixer
.caps
, min(dwSize
, sizeof(*lpCaps
)));
582 return MMSYSERR_NOERROR
;
585 /**************************************************************************
586 * MIX_GetLineInfo [internal]
588 static DWORD
MIX_GetLineInfo(WORD wDevID
, LPMIXERLINEW lpMl
, DWORD_PTR fdwInfo
)
591 DWORD ret
= MMSYSERR_ERROR
;
592 MixerLine
*line
= NULL
;
594 TRACE("%04X, %p, %08lx\n", wDevID
, lpMl
, fdwInfo
);
597 WARN("invalid parameter: lpMl = NULL\n");
598 return MMSYSERR_INVALPARAM
;
601 if (lpMl
->cbStruct
!= sizeof(*lpMl
)) {
602 WARN("invalid parameter: lpMl->cbStruct\n");
603 return MMSYSERR_INVALPARAM
;
606 if (wDevID
>= numMixers
) {
607 WARN("bad device ID: %04X\n", wDevID
);
608 return MMSYSERR_BADDEVICEID
;
611 /* FIXME: set all the variables correctly... the lines below
616 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
)
618 case MIXER_GETLINEINFOF_DESTINATION
:
619 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl
->dwDestination
);
620 if ( (lpMl
->dwDestination
>= 0) && (lpMl
->dwDestination
< mixer
.caps
.cDestinations
) )
622 lpMl
->dwLineID
= lpMl
->dwDestination
;
623 line
= &mixer
.lines
[lpMl
->dwDestination
];
625 else ret
= MIXERR_INVALLINE
;
627 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
628 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl
->dwComponentType
));
629 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
631 if (mixer
.lines
[i
].componentType
== lpMl
->dwComponentType
)
633 lpMl
->dwDestination
= lpMl
->dwLineID
= i
;
634 line
= &mixer
.lines
[i
];
640 WARN("can't find component type %s\n", getComponentType(lpMl
->dwComponentType
));
641 ret
= MIXERR_INVALVALUE
;
644 case MIXER_GETLINEINFOF_SOURCE
:
645 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl
->dwSource
, lpMl
->dwDestination
);
647 case MIXER_GETLINEINFOF_LINEID
:
648 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl
->dwLineID
);
649 if ( (lpMl
->dwLineID
>= 0) && (lpMl
->dwLineID
< mixer
.caps
.cDestinations
) )
651 lpMl
->dwDestination
= lpMl
->dwLineID
;
652 line
= &mixer
.lines
[lpMl
->dwLineID
];
654 else ret
= MIXERR_INVALLINE
;
656 case MIXER_GETLINEINFOF_TARGETTYPE
:
657 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl
->Target
.dwType
));
658 switch (lpMl
->Target
.dwType
) {
659 case MIXERLINE_TARGETTYPE_UNDEFINED
:
660 case MIXERLINE_TARGETTYPE_WAVEOUT
:
661 case MIXERLINE_TARGETTYPE_WAVEIN
:
662 case MIXERLINE_TARGETTYPE_MIDIOUT
:
663 case MIXERLINE_TARGETTYPE_MIDIIN
:
664 case MIXERLINE_TARGETTYPE_AUX
:
666 FIXME("Unhandled target type (%s)\n",
667 getTargetType(lpMl
->Target
.dwType
));
668 return MMSYSERR_INVALPARAM
;
672 WARN("Unknown flag (%08lx)\n", fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
);
678 lpMl
->dwComponentType
= line
->componentType
;
679 lpMl
->cChannels
= line
->numChannels
;
680 lpMl
->cControls
= ControlsPerLine
;
682 /* FIXME check there with CoreAudio */
683 lpMl
->cConnections
= 1;
684 lpMl
->fdwLine
= MIXERLINE_LINEF_ACTIVE
;
686 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szShortName
, sizeof(lpMl
->szShortName
) / sizeof(WCHAR
));
687 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szName
, sizeof(lpMl
->szName
) / sizeof(WCHAR
));
689 if ( IsInput(line
->direction
) )
690 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEIN
;
692 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEOUT
;
694 lpMl
->Target
.dwDeviceID
= line
->deviceID
;
695 lpMl
->Target
.wMid
= mixer
.caps
.wMid
;
696 lpMl
->Target
.wPid
= mixer
.caps
.wPid
;
697 lpMl
->Target
.vDriverVersion
= mixer
.caps
.vDriverVersion
;
699 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, lpMl
->Target
.szPname
, sizeof(lpMl
->Target
.szPname
) / sizeof(WCHAR
));
700 ret
= MMSYSERR_NOERROR
;
705 /**************************************************************************
706 * MIX_GetLineControls [internal]
708 static DWORD
MIX_GetLineControls(WORD wDevID
, LPMIXERLINECONTROLSW lpMlc
, DWORD_PTR flags
)
710 DWORD ret
= MMSYSERR_NOTENABLED
;
712 TRACE("%04X, %p, %08lX\n", wDevID
, lpMlc
, flags
);
715 WARN("invalid parameter: lpMlc == NULL\n");
716 return MMSYSERR_INVALPARAM
;
719 if (lpMlc
->cbStruct
< sizeof(*lpMlc
)) {
720 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc
->cbStruct
);
721 return MMSYSERR_INVALPARAM
;
724 if (lpMlc
->cbmxctrl
< sizeof(MIXERCONTROLW
)) {
725 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc
->cbmxctrl
);
726 return MMSYSERR_INVALPARAM
;
729 if (wDevID
>= numMixers
) {
730 WARN("bad device ID: %04X\n", wDevID
);
731 return MMSYSERR_BADDEVICEID
;
734 switch (flags
& MIXER_GETLINECONTROLSF_QUERYMASK
)
736 case MIXER_GETLINECONTROLSF_ALL
:
737 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc
->dwLineID
, lpMlc
->cControls
);
738 if (lpMlc
->cControls
!= ControlsPerLine
)
740 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc
->cControls
);
741 ret
= MMSYSERR_INVALPARAM
;
745 if ( (lpMlc
->dwLineID
>= 0) && (lpMlc
->dwLineID
< mixer
.caps
.cDestinations
) )
748 for (i
= 0; i
< lpMlc
->cControls
; i
++)
750 lpMlc
->pamxctrl
[i
] = mixer
.mixerCtrls
[lpMlc
->dwLineID
* i
].ctrl
;
752 ret
= MMSYSERR_NOERROR
;
754 else ret
= MIXERR_INVALLINE
;
757 case MIXER_GETLINECONTROLSF_ONEBYID
:
758 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlID
);
759 if ( lpMlc
->u
.dwControlID
>= 0 && lpMlc
->u
.dwControlID
< mixer
.numCtrl
)
761 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[lpMlc
->u
.dwControlID
].ctrl
;
762 ret
= MMSYSERR_NOERROR
;
764 else ret
= MIXERR_INVALVALUE
;
766 case MIXER_GETLINECONTROLSF_ONEBYTYPE
:
767 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc
->dwLineID
, getControlType(lpMlc
->u
.dwControlType
));
768 if ( (lpMlc
->dwLineID
< 0) || (lpMlc
->dwLineID
>= mixer
.caps
.cDestinations
) )
770 ret
= MIXERR_INVALLINE
;
773 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_VOLUME
)
775 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlVolume
;
776 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
777 ret
= MMSYSERR_NOERROR
;
780 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MUTE
)
782 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlMute
;
783 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
784 ret
= MMSYSERR_NOERROR
;
788 ERR("Unknown flag %08lx\n", flags
& MIXER_GETLINECONTROLSF_QUERYMASK
);
789 ret
= MMSYSERR_INVALPARAM
;
795 /**************************************************************************
796 * MIX_GetControlDetails [internal]
798 static DWORD
MIX_GetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
800 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
803 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
806 TRACE("invalid parameter: lpmcd == NULL\n");
807 return MMSYSERR_INVALPARAM
;
810 if (wDevID
>= numMixers
) {
811 WARN("bad device ID: %04X\n", wDevID
);
812 return MMSYSERR_BADDEVICEID
;
815 if ( (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
817 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
818 return MMSYSERR_NOTSUPPORTED
;
821 if ( lpmcd
->dwControlID
< 0 || lpmcd
->dwControlID
>= mixer
.numCtrl
)
823 WARN("bad control ID: %d\n", lpmcd
->dwControlID
);
824 return MIXERR_INVALVALUE
;
827 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd
->dwControlID
);
829 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
830 switch (dwControlType
)
832 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
833 FIXME("controlType : %s channels %d\n", getControlType(dwControlType
), lpmcd
->cChannels
);
835 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
838 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
839 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
840 return MMSYSERR_INVALPARAM
;
843 if ( MIX_LineGetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, &left
, &right
) )
845 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
847 switch (lpmcd
->cChannels
)
850 /* mono... so R = L */
851 mcdu
->dwValue
= left
* 65535;
852 TRACE("Reading RL = %d\n", mcdu
->dwValue
);
855 /* stereo, left is paDetails[0] */
856 mcdu
->dwValue
= left
* 65535;
857 TRACE("Reading L = %d\n", mcdu
->dwValue
);
859 mcdu
->dwValue
= right
* 65535;
860 TRACE("Reading R = %d\n", mcdu
->dwValue
);
863 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
864 return MMSYSERR_INVALPARAM
;
866 TRACE("=> %08x\n", mcdu
->dwValue
);
867 ret
= MMSYSERR_NOERROR
;
871 case MIXERCONTROL_CONTROLTYPE_MUTE
:
872 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
873 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
875 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
877 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
878 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd
->cbDetails
);
879 return MMSYSERR_INVALPARAM
;
881 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
883 if ( MIX_LineGetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, &muted
) )
885 mcdb
->fValue
= muted
;
886 TRACE("=> %s\n", mcdb
->fValue
? "on" : "off");
887 ret
= MMSYSERR_NOERROR
;
891 case MIXERCONTROL_CONTROLTYPE_MIXER
:
892 case MIXERCONTROL_CONTROLTYPE_MUX
:
894 FIXME("controlType : %s\n", getControlType(dwControlType
));
900 /**************************************************************************
901 * MIX_SetControlDetails [internal]
903 static DWORD
MIX_SetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
905 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
908 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
911 TRACE("invalid parameter: lpmcd == NULL\n");
912 return MMSYSERR_INVALPARAM
;
915 if (wDevID
>= numMixers
) {
916 WARN("bad device ID: %04X\n", wDevID
);
917 return MMSYSERR_BADDEVICEID
;
920 if ( (fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
922 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
);
923 return MMSYSERR_NOTSUPPORTED
;
926 TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd
->dwControlID
);
927 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
928 switch (dwControlType
)
930 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
931 FIXME("controlType : %s\n", getControlType(dwControlType
));
933 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
934 Float32 left
, right
= 0;
936 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
937 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
938 return MMSYSERR_INVALPARAM
;
941 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
943 switch (lpmcd
->cChannels
)
946 /* mono... so R = L */
947 TRACE("Setting RL to %d\n", mcdu
->dwValue
);
948 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
951 /* stereo, left is paDetails[0] */
952 TRACE("Setting L to %d\n", mcdu
->dwValue
);
953 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
955 TRACE("Setting R to %d\n", mcdu
->dwValue
);
956 right
= (Float32
) mcdu
->dwValue
/ 65535.0;
959 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
960 return MMSYSERR_INVALPARAM
;
962 if ( MIX_LineSetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, left
, right
) )
963 ret
= MMSYSERR_NOERROR
;
966 case MIXERCONTROL_CONTROLTYPE_MUTE
:
967 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
968 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
970 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
972 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
973 WARN("invalid parameter: cbDetails\n");
974 return MMSYSERR_INVALPARAM
;
976 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
977 if ( MIX_LineSetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, mcdb
->fValue
) )
978 ret
= MMSYSERR_NOERROR
;
981 case MIXERCONTROL_CONTROLTYPE_MIXER
:
982 case MIXERCONTROL_CONTROLTYPE_MUX
:
984 FIXME("controlType : %s\n", getControlType(dwControlType
));
985 ret
= MMSYSERR_NOTSUPPORTED
;
991 /**************************************************************************
994 DWORD WINAPI
CoreAudio_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
995 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
997 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID
, getMessage(wMsg
),
998 dwUser
, dwParam1
, dwParam2
);
1006 /* FIXME: Pretend this is supported */
1009 return MIX_Open(wDevID
, (LPMIXEROPENDESC
)dwParam1
, dwParam2
);
1011 return MMSYSERR_NOERROR
;
1012 case MXDM_GETNUMDEVS
:
1013 return MIX_GetNumDevs();
1014 case MXDM_GETDEVCAPS
:
1015 return MIX_GetDevCaps(wDevID
, (LPMIXERCAPSW
)dwParam1
, dwParam2
);
1016 case MXDM_GETLINEINFO
:
1017 return MIX_GetLineInfo(wDevID
, (LPMIXERLINEW
)dwParam1
, dwParam2
);
1018 case MXDM_GETLINECONTROLS
:
1019 return MIX_GetLineControls(wDevID
, (LPMIXERLINECONTROLSW
)dwParam1
, dwParam2
);
1020 case MXDM_GETCONTROLDETAILS
:
1021 return MIX_GetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1022 case MXDM_SETCONTROLDETAILS
:
1023 return MIX_SetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1025 WARN("unknown message %d!\n", wMsg
);
1026 return MMSYSERR_NOTSUPPORTED
;