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
)
96 #define MSG_TO_STR(x) case x: return #x;
98 MSG_TO_STR(DRVM_INIT
);
99 MSG_TO_STR(DRVM_EXIT
);
100 MSG_TO_STR(DRVM_ENABLE
);
101 MSG_TO_STR(DRVM_DISABLE
);
102 MSG_TO_STR(MXDM_GETDEVCAPS
);
103 MSG_TO_STR(MXDM_GETLINEINFO
);
104 MSG_TO_STR(MXDM_GETNUMDEVS
);
105 MSG_TO_STR(MXDM_OPEN
);
106 MSG_TO_STR(MXDM_CLOSE
);
107 MSG_TO_STR(MXDM_GETLINECONTROLS
);
108 MSG_TO_STR(MXDM_GETCONTROLDETAILS
);
109 MSG_TO_STR(MXDM_SETCONTROLDETAILS
);
112 sprintf(str
, "UNKNOWN(%08x)", uMsg
);
116 static const char * getControlType(DWORD dwControlType
)
119 #define TYPE_TO_STR(x) case x: return #x;
120 switch (dwControlType
) {
121 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM
);
122 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER
);
123 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER
);
124 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER
);
125 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER
);
126 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN
);
127 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF
);
128 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE
);
129 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO
);
130 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS
);
131 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH
);
132 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST
);
133 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON
);
134 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS
);
135 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED
);
136 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED
);
137 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT
);
138 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER
);
139 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN
);
140 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN
);
141 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER
);
142 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME
);
143 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS
);
144 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE
);
145 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER
);
146 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT
);
147 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX
);
148 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT
);
149 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER
);
150 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME
);
151 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME
);
154 sprintf(str
, "UNKNOWN(%08x)", dwControlType
);
158 static const char * getComponentType(DWORD dwComponentType
)
161 #define TYPE_TO_STR(x) case x: return #x;
162 switch (dwComponentType
) {
163 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED
);
164 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL
);
165 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE
);
166 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR
);
167 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
);
168 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
);
169 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE
);
170 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN
);
171 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN
);
172 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
);
173 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL
);
174 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE
);
175 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
);
176 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
);
177 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
);
178 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE
);
179 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER
);
180 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
);
181 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
);
182 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG
);
185 sprintf(str
, "UNKNOWN(%08x)", dwComponentType
);
189 static const char * getTargetType(DWORD dwType
)
192 #define TYPE_TO_STR(x) case x: return #x;
194 TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED
);
195 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT
);
196 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN
);
197 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT
);
198 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN
);
199 TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX
);
202 sprintf(str
, "UNKNOWN(%08x)", dwType
);
206 /* FIXME is there a better way ? */
207 static DWORD
DeviceComponentType(char *name
)
209 if (strcmp(name
, "Built-in Microphone") == 0)
210 return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
;
212 if (strcmp(name
, "Built-in Line Input") == 0)
213 return MIXERLINE_COMPONENTTYPE_SRC_LINE
;
215 if (strcmp(name
, "Built-in Output") == 0)
216 return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
218 return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
;
221 static BOOL
DeviceHasMute(AudioDeviceID deviceID
, Boolean isInput
)
223 Boolean writable
= false;
224 OSStatus err
= noErr
;
225 err
= AudioDeviceGetPropertyInfo(deviceID
, 0, isInput
, kAudioDevicePropertyMute
, NULL
, NULL
);
228 /* check if we can set it */
229 err
= AudioDeviceGetPropertyInfo(deviceID
, 0, isInput
, kAudioDevicePropertyMute
, NULL
, &writable
);
239 static BOOL
MIX_LineGetVolume(DWORD lineID
, DWORD channels
, Float32
*left
, Float32
*right
)
241 MixerLine
*line
= &mixer
.lines
[lineID
];
242 UInt32 size
= sizeof(Float32
);
243 OSStatus err
= noErr
;
244 *left
= *right
= 0.0;
246 err
= AudioDeviceGetProperty(line
->deviceID
, 1, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, &size
, left
);
252 size
= sizeof(Float32
);
253 err
= AudioDeviceGetProperty(line
->deviceID
, 2, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, &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 err
= AudioDeviceGetProperty(line
->deviceID
, 0, IsInput(line
->direction
), kAudioDevicePropertyMute
, &size
, &val
);
271 return (err
== noErr
);
277 static BOOL
MIX_LineSetVolume(DWORD lineID
, DWORD channels
, Float32 left
, Float32 right
)
279 MixerLine
*line
= &mixer
.lines
[lineID
];
280 UInt32 size
= sizeof(Float32
);
281 OSStatus err
= noErr
;
282 TRACE("lineID %d channels %d left %f right %f\n", lineID
, channels
, left
, right
);
286 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 1, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &left
);
290 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 2, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &right
);
295 FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
296 err = AudioDeviceSetProperty(line->deviceID, NULL, 0, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
299 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 1, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &left
);
302 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 2, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &right
);
304 return (err
== noErr
);
307 static BOOL
MIX_LineSetMute(DWORD lineID
, BOOL mute
)
309 MixerLine
*line
= &mixer
.lines
[lineID
];
311 UInt32 size
= sizeof(UInt32
);
312 OSStatus err
= noErr
;
314 err
= AudioDeviceSetProperty(line
->deviceID
, 0, 0, IsInput(line
->direction
), kAudioDevicePropertyMute
, size
, &val
);
315 return (err
== noErr
);
318 static void MIX_FillControls(void)
323 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
325 line
= &mixer
.lines
[i
];
326 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
327 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
328 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_VOLUME
;
329 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
330 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
331 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 65535;
332 mixer
.mixerCtrls
[ctrl
].ctrl
.Metrics
.cSteps
= 656;
335 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
336 if ( !DeviceHasMute(line
->deviceID
, IsInput(line
->direction
)) )
337 mixer
.mixerCtrls
[ctrl
].ctrl
.fdwControl
|= MIXERCONTROL_CONTROLF_DISABLED
;
339 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
340 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_MUTE
;
341 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
342 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
343 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 1;
346 assert(ctrl
== mixer
.numCtrl
);
349 /**************************************************************************
350 * CoreAudio_MixerInit
352 LONG
CoreAudio_MixerInit(void)
356 AudioDeviceID
*deviceArray
= NULL
;
357 char name
[MAXPNAMELEN
];
361 AudioStreamBasicDescription streamDescription
;
363 /* Find number of lines */
364 status
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
, &propertySize
, NULL
);
367 ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status
>> 24),
368 (char) (status
>> 16),
369 (char) (status
>> 8),
374 numLines
= propertySize
/ sizeof(AudioDeviceID
);
376 mixer
.mixerCtrls
= NULL
;
380 mixer
.caps
.cDestinations
= numLines
;
381 mixer
.caps
.wMid
= 0xAA;
382 mixer
.caps
.wPid
= 0x55;
383 mixer
.caps
.vDriverVersion
= 0x0100;
385 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, mixer
.caps
.szPname
, sizeof(mixer
.caps
.szPname
) / sizeof(WCHAR
));
387 mixer
.caps
.fdwSupport
= 0; /* No bits defined yet */
389 mixer
.lines
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerLine
) * numLines
);
393 deviceArray
= HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID
) * numLines
);
395 propertySize
= sizeof(AudioDeviceID
) * numLines
;
396 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
, &propertySize
, deviceArray
);
399 ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status
>> 24),
400 (char) (status
>> 16),
401 (char) (status
>> 8),
406 for (i
= 0; i
< numLines
; i
++)
409 MixerLine
*line
= &mixer
.lines
[i
];
411 line
->deviceID
= deviceArray
[i
];
413 propertySize
= MAXPNAMELEN
;
414 status
= AudioDeviceGetProperty(line
->deviceID
, 0 , FALSE
, kAudioDevicePropertyDeviceName
, &propertySize
, name
);
416 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status
>> 24),
417 (char) (status
>> 16),
418 (char) (status
>> 8),
423 line
->name
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, strlen(name
) + 1);
427 memcpy(line
->name
, name
, strlen(name
));
429 line
->componentType
= DeviceComponentType(line
->name
);
431 /* check for directions */
433 propertySize
= sizeof(UInt32
);
434 status
= AudioDeviceGetPropertyInfo(line
->deviceID
, 0, FALSE
, kAudioDevicePropertyStreams
, &propertySize
, &write
);
436 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %c%c%c%c\n", (char) (status
>> 24),
437 (char) (status
>> 16),
438 (char) (status
>> 8),
443 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
445 line
->direction
|= OutputDevice
;
447 /* Check the number of channel for the stream */
448 propertySize
= sizeof(streamDescription
);
449 status
= AudioDeviceGetProperty(line
->deviceID
, 0, FALSE
, kAudioDevicePropertyStreamFormat
, &propertySize
, &streamDescription
);
450 if (status
!= noErr
) {
451 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status
>> 24),
452 (char) (status
>> 16),
453 (char) (status
>> 8),
457 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
462 propertySize
= sizeof(UInt32
);
463 status
= AudioDeviceGetPropertyInfo(line
->deviceID
, 0, TRUE
, kAudioDevicePropertyStreams
, &propertySize
, &write
);
465 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %c%c%c%c\n", (char) (status
>> 24),
466 (char) (status
>> 16),
467 (char) (status
>> 8),
471 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
473 line
->direction
|= InputDevice
;
475 /* Check the number of channel for the stream */
476 propertySize
= sizeof(streamDescription
);
477 status
= AudioDeviceGetProperty(line
->deviceID
, 0, TRUE
, kAudioDevicePropertyStreamFormat
, &propertySize
, &streamDescription
);
478 if (status
!= noErr
) {
479 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status
>> 24),
480 (char) (status
>> 16),
481 (char) (status
>> 8),
485 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
489 mixer
.numCtrl
+= ControlsPerLine
; /* volume & (mute | onoff) */
491 mixer
.mixerCtrls
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerCtrl
) * mixer
.numCtrl
);
492 if (!mixer
.mixerCtrls
)
497 HeapFree(GetProcessHeap(), 0, deviceArray
);
504 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
506 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
508 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
510 HeapFree(GetProcessHeap(), 0, deviceArray
);
511 if (mixer
.mixerCtrls
)
512 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
516 /**************************************************************************
517 * CoreAudio_MixerRelease
519 void CoreAudio_MixerRelease(void)
526 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
528 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
530 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
532 if (mixer
.mixerCtrls
)
533 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
536 /**************************************************************************
537 * MIX_Open [internal]
539 static DWORD
MIX_Open(WORD wDevID
, LPMIXEROPENDESC lpMod
, DWORD_PTR flags
)
541 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID
, lpMod
, flags
);
543 WARN("invalid parameter: lpMod == NULL\n");
544 return MMSYSERR_INVALPARAM
;
547 if (wDevID
>= numMixers
) {
548 WARN("bad device ID: %04X\n", wDevID
);
549 return MMSYSERR_BADDEVICEID
;
551 return MMSYSERR_NOERROR
;
554 /**************************************************************************
555 * MIX_GetNumDevs [internal]
557 static DWORD
MIX_GetNumDevs(void)
563 static DWORD
MIX_GetDevCaps(WORD wDevID
, LPMIXERCAPSW lpCaps
, DWORD_PTR dwSize
)
565 TRACE("wDevID=%d lpCaps=%p\n", wDevID
, lpCaps
);
567 if (lpCaps
== NULL
) {
568 WARN("Invalid Parameter\n");
569 return MMSYSERR_INVALPARAM
;
572 if (wDevID
>= numMixers
) {
573 WARN("bad device ID : %d\n", wDevID
);
574 return MMSYSERR_BADDEVICEID
;
576 memcpy(lpCaps
, &mixer
.caps
, min(dwSize
, sizeof(*lpCaps
)));
577 return MMSYSERR_NOERROR
;
580 /**************************************************************************
581 * MIX_GetLineInfo [internal]
583 static DWORD
MIX_GetLineInfo(WORD wDevID
, LPMIXERLINEW lpMl
, DWORD_PTR fdwInfo
)
586 DWORD ret
= MMSYSERR_ERROR
;
587 MixerLine
*line
= NULL
;
589 TRACE("%04X, %p, %08lx\n", wDevID
, lpMl
, fdwInfo
);
592 WARN("invalid parameter: lpMl = NULL\n");
593 return MMSYSERR_INVALPARAM
;
596 if (lpMl
->cbStruct
!= sizeof(*lpMl
)) {
597 WARN("invalid parameter: lpMl->cbStruct\n");
598 return MMSYSERR_INVALPARAM
;
601 if (wDevID
>= numMixers
) {
602 WARN("bad device ID: %04X\n", wDevID
);
603 return MMSYSERR_BADDEVICEID
;
606 /* FIXME: set all the variables correctly... the lines below
611 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
)
613 case MIXER_GETLINEINFOF_DESTINATION
:
614 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl
->dwDestination
);
615 if ( (lpMl
->dwDestination
>= 0) && (lpMl
->dwDestination
< mixer
.caps
.cDestinations
) )
617 lpMl
->dwLineID
= lpMl
->dwDestination
;
618 line
= &mixer
.lines
[lpMl
->dwDestination
];
620 else ret
= MIXERR_INVALLINE
;
622 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
623 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl
->dwComponentType
));
624 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
626 if (mixer
.lines
[i
].componentType
== lpMl
->dwComponentType
)
628 lpMl
->dwDestination
= lpMl
->dwLineID
= i
;
629 line
= &mixer
.lines
[i
];
635 WARN("can't find component type %s\n", getComponentType(lpMl
->dwComponentType
));
636 ret
= MIXERR_INVALVALUE
;
639 case MIXER_GETLINEINFOF_SOURCE
:
640 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl
->dwSource
, lpMl
->dwDestination
);
642 case MIXER_GETLINEINFOF_LINEID
:
643 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl
->dwLineID
);
644 if ( (lpMl
->dwLineID
>= 0) && (lpMl
->dwLineID
< mixer
.caps
.cDestinations
) )
646 lpMl
->dwDestination
= lpMl
->dwLineID
;
647 line
= &mixer
.lines
[lpMl
->dwLineID
];
649 else ret
= MIXERR_INVALLINE
;
651 case MIXER_GETLINEINFOF_TARGETTYPE
:
652 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl
->Target
.dwType
));
653 switch (lpMl
->Target
.dwType
) {
654 case MIXERLINE_TARGETTYPE_UNDEFINED
:
655 case MIXERLINE_TARGETTYPE_WAVEOUT
:
656 case MIXERLINE_TARGETTYPE_WAVEIN
:
657 case MIXERLINE_TARGETTYPE_MIDIOUT
:
658 case MIXERLINE_TARGETTYPE_MIDIIN
:
659 case MIXERLINE_TARGETTYPE_AUX
:
661 FIXME("Unhandled target type (%s)\n",
662 getTargetType(lpMl
->Target
.dwType
));
663 return MMSYSERR_INVALPARAM
;
667 WARN("Unknown flag (%08lx)\n", fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
);
673 lpMl
->dwComponentType
= line
->componentType
;
674 lpMl
->cChannels
= line
->numChannels
;
675 lpMl
->cControls
= ControlsPerLine
;
677 /* FIXME check there with CoreAudio */
678 lpMl
->cConnections
= 1;
679 lpMl
->fdwLine
= MIXERLINE_LINEF_ACTIVE
;
681 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szShortName
, sizeof(lpMl
->szShortName
) / sizeof(WCHAR
));
682 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szName
, sizeof(lpMl
->szName
) / sizeof(WCHAR
));
684 if ( IsInput(line
->direction
) )
685 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEIN
;
687 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEOUT
;
689 lpMl
->Target
.dwDeviceID
= line
->deviceID
;
690 lpMl
->Target
.wMid
= mixer
.caps
.wMid
;
691 lpMl
->Target
.wPid
= mixer
.caps
.wPid
;
692 lpMl
->Target
.vDriverVersion
= mixer
.caps
.vDriverVersion
;
694 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, lpMl
->Target
.szPname
, sizeof(lpMl
->Target
.szPname
) / sizeof(WCHAR
));
695 ret
= MMSYSERR_NOERROR
;
700 /**************************************************************************
701 * MIX_GetLineControls [internal]
703 static DWORD
MIX_GetLineControls(WORD wDevID
, LPMIXERLINECONTROLSW lpMlc
, DWORD_PTR flags
)
705 DWORD ret
= MMSYSERR_NOTENABLED
;
707 TRACE("%04X, %p, %08lX\n", wDevID
, lpMlc
, flags
);
710 WARN("invalid parameter: lpMlc == NULL\n");
711 return MMSYSERR_INVALPARAM
;
714 if (lpMlc
->cbStruct
< sizeof(*lpMlc
)) {
715 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc
->cbStruct
);
716 return MMSYSERR_INVALPARAM
;
719 if (lpMlc
->cbmxctrl
< sizeof(MIXERCONTROLW
)) {
720 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc
->cbmxctrl
);
721 return MMSYSERR_INVALPARAM
;
724 if (wDevID
>= numMixers
) {
725 WARN("bad device ID: %04X\n", wDevID
);
726 return MMSYSERR_BADDEVICEID
;
729 switch (flags
& MIXER_GETLINECONTROLSF_QUERYMASK
)
731 case MIXER_GETLINECONTROLSF_ALL
:
732 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc
->dwLineID
, lpMlc
->cControls
);
733 if (lpMlc
->cControls
!= ControlsPerLine
)
735 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc
->cControls
);
736 ret
= MMSYSERR_INVALPARAM
;
740 if ( (lpMlc
->dwLineID
>= 0) && (lpMlc
->dwLineID
< mixer
.caps
.cDestinations
) )
743 for (i
= 0; i
< lpMlc
->cControls
; i
++)
745 lpMlc
->pamxctrl
[i
] = mixer
.mixerCtrls
[lpMlc
->dwLineID
* i
].ctrl
;
747 ret
= MMSYSERR_NOERROR
;
749 else ret
= MIXERR_INVALLINE
;
752 case MIXER_GETLINECONTROLSF_ONEBYID
:
753 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlID
);
754 if ( lpMlc
->u
.dwControlID
>= 0 && lpMlc
->u
.dwControlID
< mixer
.numCtrl
)
756 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[lpMlc
->u
.dwControlID
].ctrl
;
757 ret
= MMSYSERR_NOERROR
;
759 else ret
= MIXERR_INVALVALUE
;
761 case MIXER_GETLINECONTROLSF_ONEBYTYPE
:
762 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc
->dwLineID
, getControlType(lpMlc
->u
.dwControlType
));
763 if ( (lpMlc
->dwLineID
< 0) || (lpMlc
->dwLineID
>= mixer
.caps
.cDestinations
) )
765 ret
= MIXERR_INVALLINE
;
768 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_VOLUME
)
770 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlVolume
;
771 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
772 ret
= MMSYSERR_NOERROR
;
775 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MUTE
)
777 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlMute
;
778 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
779 ret
= MMSYSERR_NOERROR
;
783 ERR("Unknown flag %08lx\n", flags
& MIXER_GETLINECONTROLSF_QUERYMASK
);
784 ret
= MMSYSERR_INVALPARAM
;
790 /**************************************************************************
791 * MIX_GetControlDetails [internal]
793 static DWORD
MIX_GetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
795 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
798 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
801 TRACE("invalid parameter: lpmcd == NULL\n");
802 return MMSYSERR_INVALPARAM
;
805 if (wDevID
>= numMixers
) {
806 WARN("bad device ID: %04X\n", wDevID
);
807 return MMSYSERR_BADDEVICEID
;
810 if ( (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
812 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
813 return MMSYSERR_NOTSUPPORTED
;
816 if ( lpmcd
->dwControlID
< 0 || lpmcd
->dwControlID
>= mixer
.numCtrl
)
818 WARN("bad control ID: %d\n", lpmcd
->dwControlID
);
819 return MIXERR_INVALVALUE
;
822 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd
->dwControlID
);
824 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
825 switch (dwControlType
)
827 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
828 FIXME("controlType : %s channels %d\n", getControlType(dwControlType
), lpmcd
->cChannels
);
830 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
833 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
834 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
835 return MMSYSERR_INVALPARAM
;
838 if ( MIX_LineGetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, &left
, &right
) )
840 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
842 switch (lpmcd
->cChannels
)
845 /* mono... so R = L */
846 mcdu
->dwValue
= left
* 65535;
847 TRACE("Reading RL = %d\n", mcdu
->dwValue
);
850 /* stereo, left is paDetails[0] */
851 mcdu
->dwValue
= left
* 65535;
852 TRACE("Reading L = %d\n", mcdu
->dwValue
);
854 mcdu
->dwValue
= right
* 65535;
855 TRACE("Reading R = %d\n", mcdu
->dwValue
);
858 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
859 return MMSYSERR_INVALPARAM
;
861 TRACE("=> %08x\n", mcdu
->dwValue
);
862 ret
= MMSYSERR_NOERROR
;
866 case MIXERCONTROL_CONTROLTYPE_MUTE
:
867 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
868 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
870 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
872 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
873 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd
->cbDetails
);
874 return MMSYSERR_INVALPARAM
;
876 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
878 if ( MIX_LineGetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, &muted
) )
880 mcdb
->fValue
= muted
;
881 TRACE("=> %s\n", mcdb
->fValue
? "on" : "off");
882 ret
= MMSYSERR_NOERROR
;
886 case MIXERCONTROL_CONTROLTYPE_MIXER
:
887 case MIXERCONTROL_CONTROLTYPE_MUX
:
889 FIXME("controlType : %s\n", getControlType(dwControlType
));
895 /**************************************************************************
896 * MIX_SetControlDetails [internal]
898 static DWORD
MIX_SetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
900 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
903 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
906 TRACE("invalid parameter: lpmcd == NULL\n");
907 return MMSYSERR_INVALPARAM
;
910 if (wDevID
>= numMixers
) {
911 WARN("bad device ID: %04X\n", wDevID
);
912 return MMSYSERR_BADDEVICEID
;
915 if ( (fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
917 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
);
918 return MMSYSERR_NOTSUPPORTED
;
921 TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd
->dwControlID
);
922 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
923 switch (dwControlType
)
925 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
926 FIXME("controlType : %s\n", getControlType(dwControlType
));
928 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
929 Float32 left
, right
= 0;
931 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
932 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
933 return MMSYSERR_INVALPARAM
;
936 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
938 switch (lpmcd
->cChannels
)
941 /* mono... so R = L */
942 TRACE("Setting RL to %d\n", mcdu
->dwValue
);
943 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
946 /* stereo, left is paDetails[0] */
947 TRACE("Setting L to %d\n", mcdu
->dwValue
);
948 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
950 TRACE("Setting R to %d\n", mcdu
->dwValue
);
951 right
= (Float32
) mcdu
->dwValue
/ 65535.0;
954 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
955 return MMSYSERR_INVALPARAM
;
957 if ( MIX_LineSetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, left
, right
) )
958 ret
= MMSYSERR_NOERROR
;
961 case MIXERCONTROL_CONTROLTYPE_MUTE
:
962 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
963 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
965 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
967 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
968 WARN("invalid parameter: cbDetails\n");
969 return MMSYSERR_INVALPARAM
;
971 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
972 if ( MIX_LineSetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, mcdb
->fValue
) )
973 ret
= MMSYSERR_NOERROR
;
976 case MIXERCONTROL_CONTROLTYPE_MIXER
:
977 case MIXERCONTROL_CONTROLTYPE_MUX
:
979 FIXME("controlType : %s\n", getControlType(dwControlType
));
980 ret
= MMSYSERR_NOTSUPPORTED
;
986 /**************************************************************************
989 DWORD WINAPI
CoreAudio_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
990 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
992 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID
, getMessage(wMsg
),
993 dwUser
, dwParam1
, dwParam2
);
1001 /* FIXME: Pretend this is supported */
1004 return MIX_Open(wDevID
, (LPMIXEROPENDESC
)dwParam1
, dwParam2
);
1006 return MMSYSERR_NOERROR
;
1007 case MXDM_GETNUMDEVS
:
1008 return MIX_GetNumDevs();
1009 case MXDM_GETDEVCAPS
:
1010 return MIX_GetDevCaps(wDevID
, (LPMIXERCAPSW
)dwParam1
, dwParam2
);
1011 case MXDM_GETLINEINFO
:
1012 return MIX_GetLineInfo(wDevID
, (LPMIXERLINEW
)dwParam1
, dwParam2
);
1013 case MXDM_GETLINECONTROLS
:
1014 return MIX_GetLineControls(wDevID
, (LPMIXERLINECONTROLSW
)dwParam1
, dwParam2
);
1015 case MXDM_GETCONTROLDETAILS
:
1016 return MIX_GetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1017 case MXDM_SETCONTROLDETAILS
:
1018 return MIX_SetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
1020 WARN("unknown message %d!\n", wMsg
);
1021 return MMSYSERR_NOTSUPPORTED
;
1027 DWORD WINAPI
CoreAudio_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
1028 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
1030 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1031 return MMSYSERR_NOTENABLED
;
1033 #endif /* HAVE_COREAUDIO_COREAUDIO_H */