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 #if defined(HAVE_COREAUDIO_COREAUDIO_H)
47 #include <CoreAudio/CoreAudio.h>
48 #include <CoreFoundation/CoreFoundation.h>
50 #define WINE_MIXER_NAME "CoreAudio Mixer"
52 #define InputDevice (1 << 0)
53 #define OutputDevice (1 << 1)
55 #define IsInput(dir) ((dir) & InputDevice)
56 #define IsOutput(dir) ((dir) & OutputDevice)
58 #define ControlsPerLine 2 /* number of control per line : volume & (mute | onoff) */
60 #define IDControlVolume 0
61 #define IDControlMute 1
63 typedef struct tagMixerLine
69 AudioDeviceID deviceID
;
72 typedef struct tagMixerCtrl
78 typedef struct tagCoreAudio_Mixer
82 MixerCtrl
*mixerCtrls
;
87 static CoreAudio_Mixer mixer
;
88 static int numMixers
= 1;
90 /**************************************************************************
93 static const char * getMessage(UINT uMsg
)
95 #define MSG_TO_STR(x) case x: return #x;
97 MSG_TO_STR(DRVM_INIT
);
98 MSG_TO_STR(DRVM_EXIT
);
99 MSG_TO_STR(DRVM_ENABLE
);
100 MSG_TO_STR(DRVM_DISABLE
);
101 MSG_TO_STR(MXDM_GETDEVCAPS
);
102 MSG_TO_STR(MXDM_GETLINEINFO
);
103 MSG_TO_STR(MXDM_GETNUMDEVS
);
104 MSG_TO_STR(MXDM_OPEN
);
105 MSG_TO_STR(MXDM_CLOSE
);
106 MSG_TO_STR(MXDM_GETLINECONTROLS
);
107 MSG_TO_STR(MXDM_GETCONTROLDETAILS
);
108 MSG_TO_STR(MXDM_SETCONTROLDETAILS
);
111 return wine_dbg_sprintf("UNKNOWN(%08x)", uMsg
);
114 static const char * getControlType(DWORD dwControlType
)
116 #define TYPE_TO_STR(x) case x: return #x;
117 switch (dwControlType
) {
118 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM
);
119 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER
);
120 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER
);
121 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER
);
122 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER
);
123 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN
);
124 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF
);
125 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE
);
126 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO
);
127 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS
);
128 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH
);
129 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST
);
130 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON
);
131 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS
);
132 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED
);
133 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED
);
134 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT
);
135 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER
);
136 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN
);
137 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN
);
138 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER
);
139 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME
);
140 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS
);
141 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE
);
142 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER
);
143 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT
);
144 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX
);
145 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT
);
146 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER
);
147 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME
);
148 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME
);
151 return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType
);
154 static const char * getComponentType(DWORD dwComponentType
)
156 #define TYPE_TO_STR(x) case x: return #x;
157 switch (dwComponentType
) {
158 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED
);
159 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL
);
160 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE
);
161 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR
);
162 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
);
163 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
);
164 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE
);
165 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN
);
166 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN
);
167 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
);
168 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL
);
169 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE
);
170 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
);
171 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
);
172 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
);
173 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE
);
174 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER
);
175 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
);
176 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
);
177 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG
);
180 return wine_dbg_sprintf("UNKNOWN(%08x)", dwComponentType
);
183 static const char * getTargetType(DWORD dwType
)
185 #define TYPE_TO_STR(x) case x: return #x;
187 TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED
);
188 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT
);
189 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN
);
190 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT
);
191 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN
);
192 TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX
);
195 return wine_dbg_sprintf("UNKNOWN(%08x)", dwType
);
198 /* FIXME is there a better way ? */
199 static DWORD
DeviceComponentType(char *name
)
201 if (strcmp(name
, "Built-in Microphone") == 0)
202 return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
;
204 if (strcmp(name
, "Built-in Line Input") == 0)
205 return MIXERLINE_COMPONENTTYPE_SRC_LINE
;
207 if (strcmp(name
, "Built-in Output") == 0)
208 return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
210 return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
;
213 static BOOL
DeviceHasMute(AudioDeviceID deviceID
, Boolean isInput
)
215 Boolean writable
= false;
216 OSStatus err
= noErr
;
217 AudioObjectPropertyAddress propertyAddress
;
218 propertyAddress
.mSelector
= kAudioDevicePropertyMute
;
219 propertyAddress
.mScope
= isInput
? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
220 propertyAddress
.mElement
= 0;
221 if (AudioObjectHasProperty(deviceID
, &propertyAddress
))
223 /* check if we can set it */
224 err
= AudioObjectIsPropertySettable(deviceID
, &propertyAddress
, &writable
);
234 static BOOL
MIX_LineGetVolume(DWORD lineID
, DWORD channels
, Float32
*left
, Float32
*right
)
236 MixerLine
*line
= &mixer
.lines
[lineID
];
237 UInt32 size
= sizeof(Float32
);
238 OSStatus err
= noErr
;
239 AudioObjectPropertyAddress address
;
240 *left
= *right
= 0.0;
242 address
.mSelector
= kAudioDevicePropertyVolumeScalar
;
243 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
244 address
.mElement
= 1;
245 err
= AudioObjectGetPropertyData(line
->deviceID
, &address
, 0, NULL
, &size
, left
);
251 size
= sizeof(Float32
);
252 address
.mElement
= 2;
253 err
= AudioObjectGetPropertyData(line
->deviceID
, &address
, 0, NULL
, &size
, right
);
258 TRACE("lineID %d channels %d return left %f right %f\n", lineID
, channels
, *left
, *right
);
259 return (err
== noErr
);
262 static BOOL
MIX_LineGetMute(DWORD lineID
, BOOL
*muted
)
264 MixerLine
*line
= &mixer
.lines
[lineID
];
265 UInt32 size
= sizeof(UInt32
);
267 OSStatus err
= noErr
;
268 AudioObjectPropertyAddress address
;
269 address
.mSelector
= kAudioDevicePropertyMute
;
270 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
271 address
.mElement
= 0;
272 err
= AudioObjectGetPropertyData(line
->deviceID
, &address
, 0, NULL
, &size
, &val
);
275 return (err
== noErr
);
281 static BOOL
MIX_LineSetVolume(DWORD lineID
, DWORD channels
, Float32 left
, Float32 right
)
283 MixerLine
*line
= &mixer
.lines
[lineID
];
284 UInt32 size
= sizeof(Float32
);
285 AudioObjectPropertyAddress address
;
286 OSStatus err
= noErr
;
287 TRACE("lineID %d channels %d left %f right %f\n", lineID
, channels
, left
, right
);
289 address
.mSelector
= kAudioDevicePropertyVolumeScalar
;
290 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
293 address
.mElement
= 1;
294 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &left
);
298 address
.mElement
= 2;
299 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &right
);
304 FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
305 address.mElement = 0;
306 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
309 address
.mElement
= 1;
310 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &left
);
313 address
.mElement
= 2;
314 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, NULL
, size
, &right
);
316 return (err
== noErr
);
319 static BOOL
MIX_LineSetMute(DWORD lineID
, BOOL mute
)
321 MixerLine
*line
= &mixer
.lines
[lineID
];
323 UInt32 size
= sizeof(UInt32
);
324 AudioObjectPropertyAddress address
;
325 OSStatus err
= noErr
;
327 address
.mSelector
= kAudioDevicePropertyMute
;
328 address
.mScope
= IsInput(line
->direction
) ? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
329 address
.mElement
= 0;
330 err
= AudioObjectSetPropertyData(line
->deviceID
, &address
, 0, 0, size
, &val
);
331 return (err
== noErr
);
334 static void MIX_FillControls(void)
339 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
341 line
= &mixer
.lines
[i
];
342 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
343 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
344 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_VOLUME
;
345 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
346 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
347 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 65535;
348 mixer
.mixerCtrls
[ctrl
].ctrl
.Metrics
.cSteps
= 656;
351 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
352 if ( !DeviceHasMute(line
->deviceID
, IsInput(line
->direction
)) )
353 mixer
.mixerCtrls
[ctrl
].ctrl
.fdwControl
|= MIXERCONTROL_CONTROLF_DISABLED
;
355 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
356 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_MUTE
;
357 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
358 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
359 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 1;
362 assert(ctrl
== mixer
.numCtrl
);
365 /**************************************************************************
366 * CoreAudio_MixerInit
368 LONG
CoreAudio_MixerInit(void)
372 AudioObjectPropertyAddress propertyAddress
;
373 AudioDeviceID
*deviceArray
= NULL
;
378 AudioStreamBasicDescription streamDescription
;
380 /* Find number of lines */
381 propertyAddress
.mSelector
= kAudioHardwarePropertyDevices
;
382 propertyAddress
.mScope
= kAudioObjectPropertyScopeGlobal
;
383 propertyAddress
.mElement
= kAudioObjectPropertyElementMaster
;
384 status
= AudioObjectGetPropertyDataSize(kAudioObjectSystemObject
, &propertyAddress
, 0, NULL
, &propertySize
);
387 ERR("AudioObjectGetPropertyDataSize for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status
));
391 numLines
= propertySize
/ sizeof(AudioDeviceID
);
393 mixer
.mixerCtrls
= NULL
;
397 mixer
.caps
.cDestinations
= numLines
;
398 mixer
.caps
.wMid
= 0xAA;
399 mixer
.caps
.wPid
= 0x55;
400 mixer
.caps
.vDriverVersion
= 0x0100;
402 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, mixer
.caps
.szPname
, sizeof(mixer
.caps
.szPname
) / sizeof(WCHAR
));
404 mixer
.caps
.fdwSupport
= 0; /* No bits defined yet */
406 mixer
.lines
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerLine
) * numLines
);
410 deviceArray
= HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID
) * numLines
);
412 propertySize
= sizeof(AudioDeviceID
) * numLines
;
413 status
= AudioObjectGetPropertyData(kAudioObjectSystemObject
, &propertyAddress
, 0, NULL
, &propertySize
, deviceArray
);
416 ERR("AudioObjectGetPropertyData for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status
));
420 for (i
= 0; i
< numLines
; i
++)
422 MixerLine
*line
= &mixer
.lines
[i
];
424 line
->deviceID
= deviceArray
[i
];
426 propertySize
= sizeof(CFStringRef
);
427 propertyAddress
.mSelector
= kAudioObjectPropertyName
;
428 propertyAddress
.mScope
= kAudioObjectPropertyScopeGlobal
;
429 propertyAddress
.mElement
= kAudioObjectPropertyElementMaster
;
430 status
= AudioObjectGetPropertyData(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
, &name
);
432 ERR("AudioObjectGetPropertyData for kAudioObjectPropertyName return %s\n", wine_dbgstr_fourcc(status
));
436 line
->name
= HeapAlloc(GetProcessHeap(), 0, CFStringGetLength(name
) + 1);
440 CFStringGetCString(name
, line
->name
, CFStringGetLength(name
) + 1, kCFStringEncodingUTF8
);
442 line
->componentType
= DeviceComponentType(line
->name
);
444 /* check for directions */
446 propertySize
= sizeof(UInt32
);
447 propertyAddress
.mSelector
= kAudioDevicePropertyStreams
;
448 propertyAddress
.mScope
= kAudioDevicePropertyScopeOutput
;
449 status
= AudioObjectGetPropertyDataSize(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
);
451 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status
));
455 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
457 line
->direction
|= OutputDevice
;
459 /* Check the number of channel for the stream */
460 propertySize
= sizeof(streamDescription
);
461 propertyAddress
.mSelector
= kAudioDevicePropertyStreamFormat
;
462 status
= AudioObjectGetPropertyData(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
, &streamDescription
);
463 if (status
!= noErr
) {
464 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status
));
467 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
472 propertySize
= sizeof(UInt32
);
473 propertyAddress
.mScope
= kAudioDevicePropertyScopeInput
;
474 status
= AudioObjectGetPropertyDataSize(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
);
476 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status
));
479 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
481 line
->direction
|= InputDevice
;
483 /* Check the number of channel for the stream */
484 propertySize
= sizeof(streamDescription
);
485 propertyAddress
.mSelector
= kAudioDevicePropertyStreamFormat
;
486 status
= AudioObjectGetPropertyData(line
->deviceID
, &propertyAddress
, 0, NULL
, &propertySize
, &streamDescription
);
487 if (status
!= noErr
) {
488 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status
));
491 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
495 mixer
.numCtrl
+= ControlsPerLine
; /* volume & (mute | onoff) */
497 mixer
.mixerCtrls
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerCtrl
) * mixer
.numCtrl
);
498 if (!mixer
.mixerCtrls
)
503 HeapFree(GetProcessHeap(), 0, deviceArray
);
510 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
512 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
514 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
516 HeapFree(GetProcessHeap(), 0, deviceArray
);
517 if (mixer
.mixerCtrls
)
518 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
522 /**************************************************************************
523 * CoreAudio_MixerRelease
525 void CoreAudio_MixerRelease(void)
532 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
534 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
536 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
538 if (mixer
.mixerCtrls
)
539 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
542 /**************************************************************************
543 * MIX_Open [internal]
545 static DWORD
MIX_Open(WORD wDevID
, LPMIXEROPENDESC lpMod
, DWORD_PTR flags
)
547 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID
, lpMod
, flags
);
549 WARN("invalid parameter: lpMod == NULL\n");
550 return MMSYSERR_INVALPARAM
;
553 if (wDevID
>= numMixers
) {
554 WARN("bad device ID: %04X\n", wDevID
);
555 return MMSYSERR_BADDEVICEID
;
557 return MMSYSERR_NOERROR
;
560 /**************************************************************************
561 * MIX_GetNumDevs [internal]
563 static DWORD
MIX_GetNumDevs(void)
569 static DWORD
MIX_GetDevCaps(WORD wDevID
, LPMIXERCAPSW lpCaps
, DWORD_PTR dwSize
)
571 TRACE("wDevID=%d lpCaps=%p\n", wDevID
, lpCaps
);
573 if (lpCaps
== NULL
) {
574 WARN("Invalid Parameter\n");
575 return MMSYSERR_INVALPARAM
;
578 if (wDevID
>= numMixers
) {
579 WARN("bad device ID : %d\n", wDevID
);
580 return MMSYSERR_BADDEVICEID
;
582 memcpy(lpCaps
, &mixer
.caps
, min(dwSize
, sizeof(*lpCaps
)));
583 return MMSYSERR_NOERROR
;
586 /**************************************************************************
587 * MIX_GetLineInfo [internal]
589 static DWORD
MIX_GetLineInfo(WORD wDevID
, LPMIXERLINEW lpMl
, DWORD_PTR fdwInfo
)
592 DWORD ret
= MMSYSERR_ERROR
;
593 MixerLine
*line
= NULL
;
595 TRACE("%04X, %p, %08lx\n", wDevID
, lpMl
, fdwInfo
);
598 WARN("invalid parameter: lpMl = NULL\n");
599 return MMSYSERR_INVALPARAM
;
602 if (lpMl
->cbStruct
!= sizeof(*lpMl
)) {
603 WARN("invalid parameter: lpMl->cbStruct\n");
604 return MMSYSERR_INVALPARAM
;
607 if (wDevID
>= numMixers
) {
608 WARN("bad device ID: %04X\n", wDevID
);
609 return MMSYSERR_BADDEVICEID
;
612 /* FIXME: set all the variables correctly... the lines below
617 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
)
619 case MIXER_GETLINEINFOF_DESTINATION
:
620 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl
->dwDestination
);
621 if ( (lpMl
->dwDestination
>= 0) && (lpMl
->dwDestination
< mixer
.caps
.cDestinations
) )
623 lpMl
->dwLineID
= lpMl
->dwDestination
;
624 line
= &mixer
.lines
[lpMl
->dwDestination
];
626 else ret
= MIXERR_INVALLINE
;
628 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
629 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl
->dwComponentType
));
630 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
632 if (mixer
.lines
[i
].componentType
== lpMl
->dwComponentType
)
634 lpMl
->dwDestination
= lpMl
->dwLineID
= i
;
635 line
= &mixer
.lines
[i
];
641 WARN("can't find component type %s\n", getComponentType(lpMl
->dwComponentType
));
642 ret
= MIXERR_INVALVALUE
;
645 case MIXER_GETLINEINFOF_SOURCE
:
646 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl
->dwSource
, lpMl
->dwDestination
);
648 case MIXER_GETLINEINFOF_LINEID
:
649 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl
->dwLineID
);
650 if ( (lpMl
->dwLineID
>= 0) && (lpMl
->dwLineID
< mixer
.caps
.cDestinations
) )
652 lpMl
->dwDestination
= lpMl
->dwLineID
;
653 line
= &mixer
.lines
[lpMl
->dwLineID
];
655 else ret
= MIXERR_INVALLINE
;
657 case MIXER_GETLINEINFOF_TARGETTYPE
:
658 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl
->Target
.dwType
));
659 switch (lpMl
->Target
.dwType
) {
660 case MIXERLINE_TARGETTYPE_UNDEFINED
:
661 case MIXERLINE_TARGETTYPE_WAVEOUT
:
662 case MIXERLINE_TARGETTYPE_WAVEIN
:
663 case MIXERLINE_TARGETTYPE_MIDIOUT
:
664 case MIXERLINE_TARGETTYPE_MIDIIN
:
665 case MIXERLINE_TARGETTYPE_AUX
:
667 FIXME("Unhandled target type (%s)\n",
668 getTargetType(lpMl
->Target
.dwType
));
669 return MMSYSERR_INVALPARAM
;
673 WARN("Unknown flag (%08lx)\n", fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
);
679 lpMl
->dwComponentType
= line
->componentType
;
680 lpMl
->cChannels
= line
->numChannels
;
681 lpMl
->cControls
= ControlsPerLine
;
683 /* FIXME check there with CoreAudio */
684 lpMl
->cConnections
= 1;
685 lpMl
->fdwLine
= MIXERLINE_LINEF_ACTIVE
;
687 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szShortName
, sizeof(lpMl
->szShortName
) / sizeof(WCHAR
));
688 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szName
, sizeof(lpMl
->szName
) / sizeof(WCHAR
));
690 if ( IsInput(line
->direction
) )
691 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEIN
;
693 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEOUT
;
695 lpMl
->Target
.dwDeviceID
= line
->deviceID
;
696 lpMl
->Target
.wMid
= mixer
.caps
.wMid
;
697 lpMl
->Target
.wPid
= mixer
.caps
.wPid
;
698 lpMl
->Target
.vDriverVersion
= mixer
.caps
.vDriverVersion
;
700 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, lpMl
->Target
.szPname
, sizeof(lpMl
->Target
.szPname
) / sizeof(WCHAR
));
701 ret
= MMSYSERR_NOERROR
;
706 /**************************************************************************
707 * MIX_GetLineControls [internal]
709 static DWORD
MIX_GetLineControls(WORD wDevID
, LPMIXERLINECONTROLSW lpMlc
, DWORD_PTR flags
)
711 DWORD ret
= MMSYSERR_NOTENABLED
;
713 TRACE("%04X, %p, %08lX\n", wDevID
, lpMlc
, flags
);
716 WARN("invalid parameter: lpMlc == NULL\n");
717 return MMSYSERR_INVALPARAM
;
720 if (lpMlc
->cbStruct
< sizeof(*lpMlc
)) {
721 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc
->cbStruct
);
722 return MMSYSERR_INVALPARAM
;
725 if (lpMlc
->cbmxctrl
< sizeof(MIXERCONTROLW
)) {
726 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc
->cbmxctrl
);
727 return MMSYSERR_INVALPARAM
;
730 if (wDevID
>= numMixers
) {
731 WARN("bad device ID: %04X\n", wDevID
);
732 return MMSYSERR_BADDEVICEID
;
735 switch (flags
& MIXER_GETLINECONTROLSF_QUERYMASK
)
737 case MIXER_GETLINECONTROLSF_ALL
:
738 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc
->dwLineID
, lpMlc
->cControls
);
739 if (lpMlc
->cControls
!= ControlsPerLine
)
741 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc
->cControls
);
742 ret
= MMSYSERR_INVALPARAM
;
746 if ( (lpMlc
->dwLineID
>= 0) && (lpMlc
->dwLineID
< mixer
.caps
.cDestinations
) )
749 for (i
= 0; i
< lpMlc
->cControls
; i
++)
751 lpMlc
->pamxctrl
[i
] = mixer
.mixerCtrls
[lpMlc
->dwLineID
* i
].ctrl
;
753 ret
= MMSYSERR_NOERROR
;
755 else ret
= MIXERR_INVALLINE
;
758 case MIXER_GETLINECONTROLSF_ONEBYID
:
759 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlID
);
760 if ( lpMlc
->u
.dwControlID
>= 0 && lpMlc
->u
.dwControlID
< mixer
.numCtrl
)
762 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[lpMlc
->u
.dwControlID
].ctrl
;
763 ret
= MMSYSERR_NOERROR
;
765 else ret
= MIXERR_INVALVALUE
;
767 case MIXER_GETLINECONTROLSF_ONEBYTYPE
:
768 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc
->dwLineID
, getControlType(lpMlc
->u
.dwControlType
));
769 if ( (lpMlc
->dwLineID
< 0) || (lpMlc
->dwLineID
>= mixer
.caps
.cDestinations
) )
771 ret
= MIXERR_INVALLINE
;
774 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_VOLUME
)
776 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlVolume
;
777 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
778 ret
= MMSYSERR_NOERROR
;
781 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MUTE
)
783 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlMute
;
784 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
785 ret
= MMSYSERR_NOERROR
;
789 ERR("Unknown flag %08lx\n", flags
& MIXER_GETLINECONTROLSF_QUERYMASK
);
790 ret
= MMSYSERR_INVALPARAM
;
796 /**************************************************************************
797 * MIX_GetControlDetails [internal]
799 static DWORD
MIX_GetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
801 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
804 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
807 TRACE("invalid parameter: lpmcd == NULL\n");
808 return MMSYSERR_INVALPARAM
;
811 if (wDevID
>= numMixers
) {
812 WARN("bad device ID: %04X\n", wDevID
);
813 return MMSYSERR_BADDEVICEID
;
816 if ( (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
818 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
819 return MMSYSERR_NOTSUPPORTED
;
822 if ( lpmcd
->dwControlID
< 0 || lpmcd
->dwControlID
>= mixer
.numCtrl
)
824 WARN("bad control ID: %d\n", lpmcd
->dwControlID
);
825 return MIXERR_INVALVALUE
;
828 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd
->dwControlID
);
830 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
831 switch (dwControlType
)
833 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
834 FIXME("controlType : %s channels %d\n", getControlType(dwControlType
), lpmcd
->cChannels
);
836 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
839 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
840 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
841 return MMSYSERR_INVALPARAM
;
844 if ( MIX_LineGetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, &left
, &right
) )
846 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
848 switch (lpmcd
->cChannels
)
851 /* mono... so R = L */
852 mcdu
->dwValue
= left
* 65535;
853 TRACE("Reading RL = %d\n", mcdu
->dwValue
);
856 /* stereo, left is paDetails[0] */
857 mcdu
->dwValue
= left
* 65535;
858 TRACE("Reading L = %d\n", mcdu
->dwValue
);
860 mcdu
->dwValue
= right
* 65535;
861 TRACE("Reading R = %d\n", mcdu
->dwValue
);
864 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
865 return MMSYSERR_INVALPARAM
;
867 TRACE("=> %08x\n", mcdu
->dwValue
);
868 ret
= MMSYSERR_NOERROR
;
872 case MIXERCONTROL_CONTROLTYPE_MUTE
:
873 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
874 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
876 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
878 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
879 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd
->cbDetails
);
880 return MMSYSERR_INVALPARAM
;
882 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
884 if ( MIX_LineGetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, &muted
) )
886 mcdb
->fValue
= muted
;
887 TRACE("=> %s\n", mcdb
->fValue
? "on" : "off");
888 ret
= MMSYSERR_NOERROR
;
892 case MIXERCONTROL_CONTROLTYPE_MIXER
:
893 case MIXERCONTROL_CONTROLTYPE_MUX
:
895 FIXME("controlType : %s\n", getControlType(dwControlType
));
901 /**************************************************************************
902 * MIX_SetControlDetails [internal]
904 static DWORD
MIX_SetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
906 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
909 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
912 TRACE("invalid parameter: lpmcd == NULL\n");
913 return MMSYSERR_INVALPARAM
;
916 if (wDevID
>= numMixers
) {
917 WARN("bad device ID: %04X\n", wDevID
);
918 return MMSYSERR_BADDEVICEID
;
921 if ( (fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
923 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
);
924 return MMSYSERR_NOTSUPPORTED
;
927 TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd
->dwControlID
);
928 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
929 switch (dwControlType
)
931 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
932 FIXME("controlType : %s\n", getControlType(dwControlType
));
934 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
935 Float32 left
, right
= 0;
937 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
938 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
939 return MMSYSERR_INVALPARAM
;
942 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
944 switch (lpmcd
->cChannels
)
947 /* mono... so R = L */
948 TRACE("Setting RL to %d\n", mcdu
->dwValue
);
949 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
952 /* stereo, left is paDetails[0] */
953 TRACE("Setting L to %d\n", mcdu
->dwValue
);
954 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
956 TRACE("Setting R to %d\n", mcdu
->dwValue
);
957 right
= (Float32
) mcdu
->dwValue
/ 65535.0;
960 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
961 return MMSYSERR_INVALPARAM
;
963 if ( MIX_LineSetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, left
, right
) )
964 ret
= MMSYSERR_NOERROR
;
967 case MIXERCONTROL_CONTROLTYPE_MUTE
:
968 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
969 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
971 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
973 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
974 WARN("invalid parameter: cbDetails\n");
975 return MMSYSERR_INVALPARAM
;
977 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
978 if ( MIX_LineSetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, mcdb
->fValue
) )
979 ret
= MMSYSERR_NOERROR
;
982 case MIXERCONTROL_CONTROLTYPE_MIXER
:
983 case MIXERCONTROL_CONTROLTYPE_MUX
:
985 FIXME("controlType : %s\n", getControlType(dwControlType
));
986 ret
= MMSYSERR_NOTSUPPORTED
;
992 /**************************************************************************
995 DWORD WINAPI
CoreAudio_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
996 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
998 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID
, getMessage(wMsg
),
999 dwUser
, dwParam1
, dwParam2
);
1007 /* FIXME: Pretend this is supported */
1010 return MIX_Open(wDevID
, (LPMIXEROPENDESC
)dwParam1
, dwParam2
);
1012 return MMSYSERR_NOERROR
;
1013 case MXDM_GETNUMDEVS
:
1014 return MIX_GetNumDevs();
1015 case MXDM_GETDEVCAPS
:
1016 return MIX_GetDevCaps(wDevID
, (LPMIXERCAPSW
)dwParam1
, dwParam2
);
1017 case MXDM_GETLINEINFO
:
1018 return MIX_GetLineInfo(wDevID
, (LPMIXERLINEW
)dwParam1
, dwParam2
);
1019 case MXDM_GETLINECONTROLS
:
1020 return MIX_GetLineControls(wDevID
, (LPMIXERLINECONTROLSW
)dwParam1
, dwParam2
);
1021 case MXDM_GETCONTROLDETAILS
:
1022 return MIX_GetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1023 case MXDM_SETCONTROLDETAILS
:
1024 return MIX_SetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1026 WARN("unknown message %d!\n", wMsg
);
1027 return MMSYSERR_NOTSUPPORTED
;
1033 DWORD WINAPI
CoreAudio_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
1034 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
1036 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1037 return MMSYSERR_NOTENABLED
;
1039 #endif /* HAVE_COREAUDIO_COREAUDIO_H */