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 err
= AudioDeviceGetPropertyInfo(deviceID
, 0, isInput
, kAudioDevicePropertyMute
, NULL
, NULL
);
220 /* check if we can set it */
221 err
= AudioDeviceGetPropertyInfo(deviceID
, 0, isInput
, kAudioDevicePropertyMute
, NULL
, &writable
);
231 static BOOL
MIX_LineGetVolume(DWORD lineID
, DWORD channels
, Float32
*left
, Float32
*right
)
233 MixerLine
*line
= &mixer
.lines
[lineID
];
234 UInt32 size
= sizeof(Float32
);
235 OSStatus err
= noErr
;
236 *left
= *right
= 0.0;
238 err
= AudioDeviceGetProperty(line
->deviceID
, 1, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, &size
, left
);
244 size
= sizeof(Float32
);
245 err
= AudioDeviceGetProperty(line
->deviceID
, 2, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, &size
, right
);
250 TRACE("lineID %d channels %d return left %f right %f\n", lineID
, channels
, *left
, *right
);
251 return (err
== noErr
);
254 static BOOL
MIX_LineGetMute(DWORD lineID
, BOOL
*muted
)
256 MixerLine
*line
= &mixer
.lines
[lineID
];
257 UInt32 size
= sizeof(UInt32
);
259 OSStatus err
= noErr
;
260 err
= AudioDeviceGetProperty(line
->deviceID
, 0, IsInput(line
->direction
), kAudioDevicePropertyMute
, &size
, &val
);
263 return (err
== noErr
);
269 static BOOL
MIX_LineSetVolume(DWORD lineID
, DWORD channels
, Float32 left
, Float32 right
)
271 MixerLine
*line
= &mixer
.lines
[lineID
];
272 UInt32 size
= sizeof(Float32
);
273 OSStatus err
= noErr
;
274 TRACE("lineID %d channels %d left %f right %f\n", lineID
, channels
, left
, right
);
278 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 1, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &left
);
282 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 2, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &right
);
287 FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
288 err = AudioDeviceSetProperty(line->deviceID, NULL, 0, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
291 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 1, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &left
);
294 err
= AudioDeviceSetProperty(line
->deviceID
, NULL
, 2, IsInput(line
->direction
), kAudioDevicePropertyVolumeScalar
, size
, &right
);
296 return (err
== noErr
);
299 static BOOL
MIX_LineSetMute(DWORD lineID
, BOOL mute
)
301 MixerLine
*line
= &mixer
.lines
[lineID
];
303 UInt32 size
= sizeof(UInt32
);
304 OSStatus err
= noErr
;
306 err
= AudioDeviceSetProperty(line
->deviceID
, 0, 0, IsInput(line
->direction
), kAudioDevicePropertyMute
, size
, &val
);
307 return (err
== noErr
);
310 static void MIX_FillControls(void)
315 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
317 line
= &mixer
.lines
[i
];
318 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
319 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
320 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_VOLUME
;
321 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
322 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
323 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 65535;
324 mixer
.mixerCtrls
[ctrl
].ctrl
.Metrics
.cSteps
= 656;
327 mixer
.mixerCtrls
[ctrl
].dwLineID
= i
;
328 if ( !DeviceHasMute(line
->deviceID
, IsInput(line
->direction
)) )
329 mixer
.mixerCtrls
[ctrl
].ctrl
.fdwControl
|= MIXERCONTROL_CONTROLF_DISABLED
;
331 mixer
.mixerCtrls
[ctrl
].ctrl
.cbStruct
= sizeof(MIXERCONTROLW
);
332 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlType
= MIXERCONTROL_CONTROLTYPE_MUTE
;
333 mixer
.mixerCtrls
[ctrl
].ctrl
.dwControlID
= ctrl
;
334 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMinimum
= 0;
335 mixer
.mixerCtrls
[ctrl
].ctrl
.Bounds
.s1
.dwMaximum
= 1;
338 assert(ctrl
== mixer
.numCtrl
);
341 /**************************************************************************
342 * CoreAudio_MixerInit
344 LONG
CoreAudio_MixerInit(void)
348 AudioDeviceID
*deviceArray
= NULL
;
349 char name
[MAXPNAMELEN
];
353 AudioStreamBasicDescription streamDescription
;
355 /* Find number of lines */
356 status
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
, &propertySize
, NULL
);
359 ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status
));
363 numLines
= propertySize
/ sizeof(AudioDeviceID
);
365 mixer
.mixerCtrls
= NULL
;
369 mixer
.caps
.cDestinations
= numLines
;
370 mixer
.caps
.wMid
= 0xAA;
371 mixer
.caps
.wPid
= 0x55;
372 mixer
.caps
.vDriverVersion
= 0x0100;
374 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, mixer
.caps
.szPname
, sizeof(mixer
.caps
.szPname
) / sizeof(WCHAR
));
376 mixer
.caps
.fdwSupport
= 0; /* No bits defined yet */
378 mixer
.lines
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerLine
) * numLines
);
382 deviceArray
= HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID
) * numLines
);
384 propertySize
= sizeof(AudioDeviceID
) * numLines
;
385 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
, &propertySize
, deviceArray
);
388 ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status
));
392 for (i
= 0; i
< numLines
; i
++)
395 MixerLine
*line
= &mixer
.lines
[i
];
397 line
->deviceID
= deviceArray
[i
];
399 propertySize
= MAXPNAMELEN
;
400 status
= AudioDeviceGetProperty(line
->deviceID
, 0 , FALSE
, kAudioDevicePropertyDeviceName
, &propertySize
, name
);
402 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %s\n", wine_dbgstr_fourcc(status
));
406 line
->name
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, strlen(name
) + 1);
410 memcpy(line
->name
, name
, strlen(name
));
412 line
->componentType
= DeviceComponentType(line
->name
);
414 /* check for directions */
416 propertySize
= sizeof(UInt32
);
417 status
= AudioDeviceGetPropertyInfo(line
->deviceID
, 0, FALSE
, kAudioDevicePropertyStreams
, &propertySize
, &write
);
419 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %s\n", wine_dbgstr_fourcc(status
));
423 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
425 line
->direction
|= OutputDevice
;
427 /* Check the number of channel for the stream */
428 propertySize
= sizeof(streamDescription
);
429 status
= AudioDeviceGetProperty(line
->deviceID
, 0, FALSE
, kAudioDevicePropertyStreamFormat
, &propertySize
, &streamDescription
);
430 if (status
!= noErr
) {
431 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status
));
434 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
439 propertySize
= sizeof(UInt32
);
440 status
= AudioDeviceGetPropertyInfo(line
->deviceID
, 0, TRUE
, kAudioDevicePropertyStreams
, &propertySize
, &write
);
442 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status
));
445 if ( (propertySize
/ sizeof(AudioStreamID
)) != 0)
447 line
->direction
|= InputDevice
;
449 /* Check the number of channel for the stream */
450 propertySize
= sizeof(streamDescription
);
451 status
= AudioDeviceGetProperty(line
->deviceID
, 0, TRUE
, kAudioDevicePropertyStreamFormat
, &propertySize
, &streamDescription
);
452 if (status
!= noErr
) {
453 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status
));
456 line
->numChannels
= streamDescription
.mChannelsPerFrame
;
460 mixer
.numCtrl
+= ControlsPerLine
; /* volume & (mute | onoff) */
462 mixer
.mixerCtrls
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MixerCtrl
) * mixer
.numCtrl
);
463 if (!mixer
.mixerCtrls
)
468 HeapFree(GetProcessHeap(), 0, deviceArray
);
475 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
477 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
479 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
481 HeapFree(GetProcessHeap(), 0, deviceArray
);
482 if (mixer
.mixerCtrls
)
483 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
487 /**************************************************************************
488 * CoreAudio_MixerRelease
490 void CoreAudio_MixerRelease(void)
497 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
499 HeapFree(GetProcessHeap(), 0, mixer
.lines
[i
].name
);
501 HeapFree(GetProcessHeap(), 0, mixer
.lines
);
503 if (mixer
.mixerCtrls
)
504 HeapFree(GetProcessHeap(), 0, mixer
.mixerCtrls
);
507 /**************************************************************************
508 * MIX_Open [internal]
510 static DWORD
MIX_Open(WORD wDevID
, LPMIXEROPENDESC lpMod
, DWORD_PTR flags
)
512 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID
, lpMod
, flags
);
514 WARN("invalid parameter: lpMod == NULL\n");
515 return MMSYSERR_INVALPARAM
;
518 if (wDevID
>= numMixers
) {
519 WARN("bad device ID: %04X\n", wDevID
);
520 return MMSYSERR_BADDEVICEID
;
522 return MMSYSERR_NOERROR
;
525 /**************************************************************************
526 * MIX_GetNumDevs [internal]
528 static DWORD
MIX_GetNumDevs(void)
534 static DWORD
MIX_GetDevCaps(WORD wDevID
, LPMIXERCAPSW lpCaps
, DWORD_PTR dwSize
)
536 TRACE("wDevID=%d lpCaps=%p\n", wDevID
, lpCaps
);
538 if (lpCaps
== NULL
) {
539 WARN("Invalid Parameter\n");
540 return MMSYSERR_INVALPARAM
;
543 if (wDevID
>= numMixers
) {
544 WARN("bad device ID : %d\n", wDevID
);
545 return MMSYSERR_BADDEVICEID
;
547 memcpy(lpCaps
, &mixer
.caps
, min(dwSize
, sizeof(*lpCaps
)));
548 return MMSYSERR_NOERROR
;
551 /**************************************************************************
552 * MIX_GetLineInfo [internal]
554 static DWORD
MIX_GetLineInfo(WORD wDevID
, LPMIXERLINEW lpMl
, DWORD_PTR fdwInfo
)
557 DWORD ret
= MMSYSERR_ERROR
;
558 MixerLine
*line
= NULL
;
560 TRACE("%04X, %p, %08lx\n", wDevID
, lpMl
, fdwInfo
);
563 WARN("invalid parameter: lpMl = NULL\n");
564 return MMSYSERR_INVALPARAM
;
567 if (lpMl
->cbStruct
!= sizeof(*lpMl
)) {
568 WARN("invalid parameter: lpMl->cbStruct\n");
569 return MMSYSERR_INVALPARAM
;
572 if (wDevID
>= numMixers
) {
573 WARN("bad device ID: %04X\n", wDevID
);
574 return MMSYSERR_BADDEVICEID
;
577 /* FIXME: set all the variables correctly... the lines below
582 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
)
584 case MIXER_GETLINEINFOF_DESTINATION
:
585 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl
->dwDestination
);
586 if ( (lpMl
->dwDestination
>= 0) && (lpMl
->dwDestination
< mixer
.caps
.cDestinations
) )
588 lpMl
->dwLineID
= lpMl
->dwDestination
;
589 line
= &mixer
.lines
[lpMl
->dwDestination
];
591 else ret
= MIXERR_INVALLINE
;
593 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
594 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl
->dwComponentType
));
595 for (i
= 0; i
< mixer
.caps
.cDestinations
; i
++)
597 if (mixer
.lines
[i
].componentType
== lpMl
->dwComponentType
)
599 lpMl
->dwDestination
= lpMl
->dwLineID
= i
;
600 line
= &mixer
.lines
[i
];
606 WARN("can't find component type %s\n", getComponentType(lpMl
->dwComponentType
));
607 ret
= MIXERR_INVALVALUE
;
610 case MIXER_GETLINEINFOF_SOURCE
:
611 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl
->dwSource
, lpMl
->dwDestination
);
613 case MIXER_GETLINEINFOF_LINEID
:
614 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl
->dwLineID
);
615 if ( (lpMl
->dwLineID
>= 0) && (lpMl
->dwLineID
< mixer
.caps
.cDestinations
) )
617 lpMl
->dwDestination
= lpMl
->dwLineID
;
618 line
= &mixer
.lines
[lpMl
->dwLineID
];
620 else ret
= MIXERR_INVALLINE
;
622 case MIXER_GETLINEINFOF_TARGETTYPE
:
623 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl
->Target
.dwType
));
624 switch (lpMl
->Target
.dwType
) {
625 case MIXERLINE_TARGETTYPE_UNDEFINED
:
626 case MIXERLINE_TARGETTYPE_WAVEOUT
:
627 case MIXERLINE_TARGETTYPE_WAVEIN
:
628 case MIXERLINE_TARGETTYPE_MIDIOUT
:
629 case MIXERLINE_TARGETTYPE_MIDIIN
:
630 case MIXERLINE_TARGETTYPE_AUX
:
632 FIXME("Unhandled target type (%s)\n",
633 getTargetType(lpMl
->Target
.dwType
));
634 return MMSYSERR_INVALPARAM
;
638 WARN("Unknown flag (%08lx)\n", fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
);
644 lpMl
->dwComponentType
= line
->componentType
;
645 lpMl
->cChannels
= line
->numChannels
;
646 lpMl
->cControls
= ControlsPerLine
;
648 /* FIXME check there with CoreAudio */
649 lpMl
->cConnections
= 1;
650 lpMl
->fdwLine
= MIXERLINE_LINEF_ACTIVE
;
652 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szShortName
, sizeof(lpMl
->szShortName
) / sizeof(WCHAR
));
653 MultiByteToWideChar(CP_ACP
, 0, line
->name
, -1, lpMl
->szName
, sizeof(lpMl
->szName
) / sizeof(WCHAR
));
655 if ( IsInput(line
->direction
) )
656 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEIN
;
658 lpMl
->Target
.dwType
= MIXERLINE_TARGETTYPE_WAVEOUT
;
660 lpMl
->Target
.dwDeviceID
= line
->deviceID
;
661 lpMl
->Target
.wMid
= mixer
.caps
.wMid
;
662 lpMl
->Target
.wPid
= mixer
.caps
.wPid
;
663 lpMl
->Target
.vDriverVersion
= mixer
.caps
.vDriverVersion
;
665 MultiByteToWideChar(CP_ACP
, 0, WINE_MIXER_NAME
, -1, lpMl
->Target
.szPname
, sizeof(lpMl
->Target
.szPname
) / sizeof(WCHAR
));
666 ret
= MMSYSERR_NOERROR
;
671 /**************************************************************************
672 * MIX_GetLineControls [internal]
674 static DWORD
MIX_GetLineControls(WORD wDevID
, LPMIXERLINECONTROLSW lpMlc
, DWORD_PTR flags
)
676 DWORD ret
= MMSYSERR_NOTENABLED
;
678 TRACE("%04X, %p, %08lX\n", wDevID
, lpMlc
, flags
);
681 WARN("invalid parameter: lpMlc == NULL\n");
682 return MMSYSERR_INVALPARAM
;
685 if (lpMlc
->cbStruct
< sizeof(*lpMlc
)) {
686 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc
->cbStruct
);
687 return MMSYSERR_INVALPARAM
;
690 if (lpMlc
->cbmxctrl
< sizeof(MIXERCONTROLW
)) {
691 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc
->cbmxctrl
);
692 return MMSYSERR_INVALPARAM
;
695 if (wDevID
>= numMixers
) {
696 WARN("bad device ID: %04X\n", wDevID
);
697 return MMSYSERR_BADDEVICEID
;
700 switch (flags
& MIXER_GETLINECONTROLSF_QUERYMASK
)
702 case MIXER_GETLINECONTROLSF_ALL
:
703 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc
->dwLineID
, lpMlc
->cControls
);
704 if (lpMlc
->cControls
!= ControlsPerLine
)
706 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc
->cControls
);
707 ret
= MMSYSERR_INVALPARAM
;
711 if ( (lpMlc
->dwLineID
>= 0) && (lpMlc
->dwLineID
< mixer
.caps
.cDestinations
) )
714 for (i
= 0; i
< lpMlc
->cControls
; i
++)
716 lpMlc
->pamxctrl
[i
] = mixer
.mixerCtrls
[lpMlc
->dwLineID
* i
].ctrl
;
718 ret
= MMSYSERR_NOERROR
;
720 else ret
= MIXERR_INVALLINE
;
723 case MIXER_GETLINECONTROLSF_ONEBYID
:
724 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc
->dwLineID
, lpMlc
->u
.dwControlID
);
725 if ( lpMlc
->u
.dwControlID
>= 0 && lpMlc
->u
.dwControlID
< mixer
.numCtrl
)
727 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[lpMlc
->u
.dwControlID
].ctrl
;
728 ret
= MMSYSERR_NOERROR
;
730 else ret
= MIXERR_INVALVALUE
;
732 case MIXER_GETLINECONTROLSF_ONEBYTYPE
:
733 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc
->dwLineID
, getControlType(lpMlc
->u
.dwControlType
));
734 if ( (lpMlc
->dwLineID
< 0) || (lpMlc
->dwLineID
>= mixer
.caps
.cDestinations
) )
736 ret
= MIXERR_INVALLINE
;
739 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_VOLUME
)
741 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlVolume
;
742 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
743 ret
= MMSYSERR_NOERROR
;
746 if (lpMlc
->u
.dwControlType
== MIXERCONTROL_CONTROLTYPE_MUTE
)
748 ctrl
= (lpMlc
->dwLineID
* ControlsPerLine
) + IDControlMute
;
749 lpMlc
->pamxctrl
[0] = mixer
.mixerCtrls
[ctrl
].ctrl
;
750 ret
= MMSYSERR_NOERROR
;
754 ERR("Unknown flag %08lx\n", flags
& MIXER_GETLINECONTROLSF_QUERYMASK
);
755 ret
= MMSYSERR_INVALPARAM
;
761 /**************************************************************************
762 * MIX_GetControlDetails [internal]
764 static DWORD
MIX_GetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
766 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
769 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
772 TRACE("invalid parameter: lpmcd == NULL\n");
773 return MMSYSERR_INVALPARAM
;
776 if (wDevID
>= numMixers
) {
777 WARN("bad device ID: %04X\n", wDevID
);
778 return MMSYSERR_BADDEVICEID
;
781 if ( (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
783 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
);
784 return MMSYSERR_NOTSUPPORTED
;
787 if ( lpmcd
->dwControlID
< 0 || lpmcd
->dwControlID
>= mixer
.numCtrl
)
789 WARN("bad control ID: %d\n", lpmcd
->dwControlID
);
790 return MIXERR_INVALVALUE
;
793 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd
->dwControlID
);
795 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
796 switch (dwControlType
)
798 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
799 FIXME("controlType : %s channels %d\n", getControlType(dwControlType
), lpmcd
->cChannels
);
801 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
804 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
805 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
806 return MMSYSERR_INVALPARAM
;
809 if ( MIX_LineGetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, &left
, &right
) )
811 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
813 switch (lpmcd
->cChannels
)
816 /* mono... so R = L */
817 mcdu
->dwValue
= left
* 65535;
818 TRACE("Reading RL = %d\n", mcdu
->dwValue
);
821 /* stereo, left is paDetails[0] */
822 mcdu
->dwValue
= left
* 65535;
823 TRACE("Reading L = %d\n", mcdu
->dwValue
);
825 mcdu
->dwValue
= right
* 65535;
826 TRACE("Reading R = %d\n", mcdu
->dwValue
);
829 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
830 return MMSYSERR_INVALPARAM
;
832 TRACE("=> %08x\n", mcdu
->dwValue
);
833 ret
= MMSYSERR_NOERROR
;
837 case MIXERCONTROL_CONTROLTYPE_MUTE
:
838 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
839 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
841 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
843 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
844 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd
->cbDetails
);
845 return MMSYSERR_INVALPARAM
;
847 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
849 if ( MIX_LineGetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, &muted
) )
851 mcdb
->fValue
= muted
;
852 TRACE("=> %s\n", mcdb
->fValue
? "on" : "off");
853 ret
= MMSYSERR_NOERROR
;
857 case MIXERCONTROL_CONTROLTYPE_MIXER
:
858 case MIXERCONTROL_CONTROLTYPE_MUX
:
860 FIXME("controlType : %s\n", getControlType(dwControlType
));
866 /**************************************************************************
867 * MIX_SetControlDetails [internal]
869 static DWORD
MIX_SetControlDetails(WORD wDevID
, LPMIXERCONTROLDETAILS lpmcd
, DWORD_PTR fdwDetails
)
871 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
874 TRACE("%04X, %p, %08lx\n", wDevID
, lpmcd
, fdwDetails
);
877 TRACE("invalid parameter: lpmcd == NULL\n");
878 return MMSYSERR_INVALPARAM
;
881 if (wDevID
>= numMixers
) {
882 WARN("bad device ID: %04X\n", wDevID
);
883 return MMSYSERR_BADDEVICEID
;
886 if ( (fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
) != MIXER_GETCONTROLDETAILSF_VALUE
)
888 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails
& MIXER_SETCONTROLDETAILSF_QUERYMASK
);
889 return MMSYSERR_NOTSUPPORTED
;
892 TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd
->dwControlID
);
893 dwControlType
= mixer
.mixerCtrls
[lpmcd
->dwControlID
].ctrl
.dwControlType
;
894 switch (dwControlType
)
896 case MIXERCONTROL_CONTROLTYPE_VOLUME
:
897 FIXME("controlType : %s\n", getControlType(dwControlType
));
899 LPMIXERCONTROLDETAILS_UNSIGNED mcdu
;
900 Float32 left
, right
= 0;
902 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_UNSIGNED
)) {
903 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd
->cbDetails
);
904 return MMSYSERR_INVALPARAM
;
907 mcdu
= (LPMIXERCONTROLDETAILS_UNSIGNED
)lpmcd
->paDetails
;
909 switch (lpmcd
->cChannels
)
912 /* mono... so R = L */
913 TRACE("Setting RL to %d\n", mcdu
->dwValue
);
914 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
917 /* stereo, left is paDetails[0] */
918 TRACE("Setting L to %d\n", mcdu
->dwValue
);
919 left
= (Float32
) mcdu
->dwValue
/ 65535.0;
921 TRACE("Setting R to %d\n", mcdu
->dwValue
);
922 right
= (Float32
) mcdu
->dwValue
/ 65535.0;
925 WARN("Unsupported cChannels (%d)\n", lpmcd
->cChannels
);
926 return MMSYSERR_INVALPARAM
;
928 if ( MIX_LineSetVolume(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, lpmcd
->cChannels
, left
, right
) )
929 ret
= MMSYSERR_NOERROR
;
932 case MIXERCONTROL_CONTROLTYPE_MUTE
:
933 case MIXERCONTROL_CONTROLTYPE_ONOFF
:
934 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType
), lpmcd
->cChannels
);
936 LPMIXERCONTROLDETAILS_BOOLEAN mcdb
;
938 if (lpmcd
->cbDetails
!= sizeof(MIXERCONTROLDETAILS_BOOLEAN
)) {
939 WARN("invalid parameter: cbDetails\n");
940 return MMSYSERR_INVALPARAM
;
942 mcdb
= (LPMIXERCONTROLDETAILS_BOOLEAN
)lpmcd
->paDetails
;
943 if ( MIX_LineSetMute(mixer
.mixerCtrls
[lpmcd
->dwControlID
].dwLineID
, mcdb
->fValue
) )
944 ret
= MMSYSERR_NOERROR
;
947 case MIXERCONTROL_CONTROLTYPE_MIXER
:
948 case MIXERCONTROL_CONTROLTYPE_MUX
:
950 FIXME("controlType : %s\n", getControlType(dwControlType
));
951 ret
= MMSYSERR_NOTSUPPORTED
;
957 /**************************************************************************
960 DWORD WINAPI
CoreAudio_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
961 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
963 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID
, getMessage(wMsg
),
964 dwUser
, dwParam1
, dwParam2
);
972 /* FIXME: Pretend this is supported */
975 return MIX_Open(wDevID
, (LPMIXEROPENDESC
)dwParam1
, dwParam2
);
977 return MMSYSERR_NOERROR
;
978 case MXDM_GETNUMDEVS
:
979 return MIX_GetNumDevs();
980 case MXDM_GETDEVCAPS
:
981 return MIX_GetDevCaps(wDevID
, (LPMIXERCAPSW
)dwParam1
, dwParam2
);
982 case MXDM_GETLINEINFO
:
983 return MIX_GetLineInfo(wDevID
, (LPMIXERLINEW
)dwParam1
, dwParam2
);
984 case MXDM_GETLINECONTROLS
:
985 return MIX_GetLineControls(wDevID
, (LPMIXERLINECONTROLSW
)dwParam1
, dwParam2
);
986 case MXDM_GETCONTROLDETAILS
:
987 return MIX_GetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
988 case MXDM_SETCONTROLDETAILS
:
989 return MIX_SetControlDetails(wDevID
, (LPMIXERCONTROLDETAILS
)dwParam1
, dwParam2
);
991 WARN("unknown message %d!\n", wMsg
);
992 return MMSYSERR_NOTSUPPORTED
;
998 DWORD WINAPI
CoreAudio_mxdMessage(UINT wDevID
, UINT wMsg
, DWORD_PTR dwUser
,
999 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
1001 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1002 return MMSYSERR_NOTENABLED
;
1004 #endif /* HAVE_COREAUDIO_COREAUDIO_H */