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 */