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;