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);
236 static void MIX_FillControls(void)
241 for (i = 0; i < mixer.caps.cDestinations; i++)
243 line = &mixer.lines[i];
244 mixer.mixerCtrls[ctrl].dwLineID = i;
245 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
246 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
247 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
248 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
249 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
250 mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
253 mixer.mixerCtrls[ctrl].dwLineID = i;
254 if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
255 mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
257 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
258 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
259 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
260 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
261 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
264 assert(ctrl == mixer.numCtrl);
267 /**************************************************************************
268 * CoreAudio_MixerInit
270 LONG CoreAudio_MixerInit(void)
274 AudioDeviceID *deviceArray = NULL;
275 char name[MAXPNAMELEN];
279 AudioStreamBasicDescription streamDescription;
281 /* Find number of lines */
282 status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
285 ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
286 (char) (status >> 16),
287 (char) (status >> 8),
292 numLines = propertySize / sizeof(AudioDeviceID);
294 mixer.mixerCtrls = NULL;
298 mixer.caps.cDestinations = numLines;
299 mixer.caps.wMid = 0xAA;
300 mixer.caps.wPid = 0x55;
301 mixer.caps.vDriverVersion = 0x0100;
303 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
305 mixer.caps.fdwSupport = 0; /* No bits defined yet */
307 mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
311 deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
313 propertySize = sizeof(AudioDeviceID) * numLines;
314 status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray);
317 ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
318 (char) (status >> 16),
319 (char) (status >> 8),
324 for (i = 0; i < numLines; i++)
327 MixerLine *line = &mixer.lines[i];
329 line->deviceID = deviceArray[i];
331 propertySize = MAXPNAMELEN;
332 status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
334 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
335 (char) (status >> 16),
336 (char) (status >> 8),
341 line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1);
345 memcpy(line->name, name, strlen(name));
347 line->componentType = DeviceComponentType(line->name);
349 /* check for directions */
351 propertySize = sizeof(UInt32);
352 status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write );
354 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %c%c%c%c\n", (char) (status >> 24),
355 (char) (status >> 16),
356 (char) (status >> 8),
361 if ( (propertySize / sizeof(AudioStreamID)) != 0)
363 line->direction |= OutputDevice;
365 /* Check the number of channel for the stream */
366 propertySize = sizeof(streamDescription);
367 status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
368 if (status != noErr) {
369 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
370 (char) (status >> 16),
371 (char) (status >> 8),
375 line->numChannels = streamDescription.mChannelsPerFrame;
380 propertySize = sizeof(UInt32);
381 status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write );
383 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %c%c%c%c\n", (char) (status >> 24),
384 (char) (status >> 16),
385 (char) (status >> 8),
389 if ( (propertySize / sizeof(AudioStreamID)) != 0)
391 line->direction |= InputDevice;
393 /* Check the number of channel for the stream */
394 propertySize = sizeof(streamDescription);
395 status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
396 if (status != noErr) {
397 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
398 (char) (status >> 16),
399 (char) (status >> 8),
403 line->numChannels = streamDescription.mChannelsPerFrame;
407 mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
409 mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
410 if (!mixer.mixerCtrls)
415 HeapFree(GetProcessHeap(), 0, deviceArray);
422 for (i = 0; i < mixer.caps.cDestinations; i++)
424 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
426 HeapFree(GetProcessHeap(), 0, mixer.lines);
428 HeapFree(GetProcessHeap(), 0, deviceArray);
429 if (mixer.mixerCtrls)
430 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
434 /**************************************************************************
435 * CoreAudio_MixerRelease
437 void CoreAudio_MixerRelease(void)
444 for (i = 0; i < mixer.caps.cDestinations; i++)
446 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
448 HeapFree(GetProcessHeap(), 0, mixer.lines);
450 if (mixer.mixerCtrls)
451 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
454 /**************************************************************************
455 * MIX_Open [internal]
457 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
459 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
461 WARN("invalid parameter: lpMod == NULL\n");
462 return MMSYSERR_INVALPARAM;
465 if (wDevID >= numMixers) {
466 WARN("bad device ID: %04X\n", wDevID);
467 return MMSYSERR_BADDEVICEID;
469 return MMSYSERR_NOERROR;
472 /**************************************************************************
473 * MIX_GetNumDevs [internal]
475 static DWORD MIX_GetNumDevs(void)
481 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
483 TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
485 if (lpCaps == NULL) {
486 WARN("Invalid Parameter\n");
487 return MMSYSERR_INVALPARAM;
490 if (wDevID >= numMixers) {
491 WARN("bad device ID : %d\n", wDevID);
492 return MMSYSERR_BADDEVICEID;
494 memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
495 return MMSYSERR_NOERROR;
498 /**************************************************************************
499 * MIX_GetLineInfo [internal]
501 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
504 DWORD ret = MMSYSERR_ERROR;
505 MixerLine *line = NULL;
507 TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
510 WARN("invalid parameter: lpMl = NULL\n");
511 return MMSYSERR_INVALPARAM;
514 if (lpMl->cbStruct != sizeof(*lpMl)) {
515 WARN("invalid parameter: lpMl->cbStruct\n");
516 return MMSYSERR_INVALPARAM;
519 if (wDevID >= numMixers) {
520 WARN("bad device ID: %04X\n", wDevID);
521 return MMSYSERR_BADDEVICEID;
524 /* FIXME: set all the variables correctly... the lines below
529 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
531 case MIXER_GETLINEINFOF_DESTINATION:
532 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
533 if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
535 lpMl->dwLineID = lpMl->dwDestination;
536 line = &mixer.lines[lpMl->dwDestination];
538 else ret = MIXERR_INVALLINE;
540 case MIXER_GETLINEINFOF_COMPONENTTYPE:
541 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
542 for (i = 0; i < mixer.caps.cDestinations; i++)
544 if (mixer.lines[i].componentType == lpMl->dwComponentType)
546 lpMl->dwDestination = lpMl->dwLineID = i;
547 line = &mixer.lines[i];
553 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
554 ret = MIXERR_INVALVALUE;
557 case MIXER_GETLINEINFOF_SOURCE:
558 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
560 case MIXER_GETLINEINFOF_LINEID:
561 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
562 if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
564 lpMl->dwDestination = lpMl->dwLineID;
565 line = &mixer.lines[lpMl->dwLineID];
567 else ret = MIXERR_INVALLINE;
569 case MIXER_GETLINEINFOF_TARGETTYPE:
570 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
571 switch (lpMl->Target.dwType) {
572 case MIXERLINE_TARGETTYPE_UNDEFINED:
573 case MIXERLINE_TARGETTYPE_WAVEOUT:
574 case MIXERLINE_TARGETTYPE_WAVEIN:
575 case MIXERLINE_TARGETTYPE_MIDIOUT:
576 case MIXERLINE_TARGETTYPE_MIDIIN:
577 case MIXERLINE_TARGETTYPE_AUX:
579 FIXME("Unhandled target type (%s)\n",
580 getTargetType(lpMl->Target.dwType));
581 return MMSYSERR_INVALPARAM;
585 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
591 lpMl->dwComponentType = line->componentType;
592 lpMl->cChannels = line->numChannels;
593 lpMl->cControls = ControlsPerLine;
595 /* FIXME check there with CoreAudio */
596 lpMl->cConnections = 1;
597 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
599 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
600 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
602 if ( IsInput(line->direction) )
603 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
605 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
607 lpMl->Target.dwDeviceID = line->deviceID;
608 lpMl->Target.wMid = mixer.caps.wMid;
609 lpMl->Target.wPid = mixer.caps.wPid;
610 lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
612 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
613 ret = MMSYSERR_NOERROR;
618 /**************************************************************************
619 * MIX_GetLineControls [internal]
621 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
623 DWORD ret = MMSYSERR_NOTENABLED;
625 TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
628 WARN("invalid parameter: lpMlc == NULL\n");
629 return MMSYSERR_INVALPARAM;
632 if (lpMlc->cbStruct < sizeof(*lpMlc)) {
633 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
634 return MMSYSERR_INVALPARAM;
637 if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
638 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
639 return MMSYSERR_INVALPARAM;
642 if (wDevID >= numMixers) {
643 WARN("bad device ID: %04X\n", wDevID);
644 return MMSYSERR_BADDEVICEID;
647 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
649 case MIXER_GETLINECONTROLSF_ALL:
650 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
651 if (lpMlc->cControls != ControlsPerLine)
653 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
654 ret = MMSYSERR_INVALPARAM;
658 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
661 for (i = 0; i < lpMlc->cControls; i++)
663 lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
665 ret = MMSYSERR_NOERROR;
667 else ret = MIXERR_INVALLINE;
670 case MIXER_GETLINECONTROLSF_ONEBYID:
671 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
672 if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
674 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
675 ret = MMSYSERR_NOERROR;
677 else ret = MIXERR_INVALVALUE;
679 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
680 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
681 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
683 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
684 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
685 ret = MMSYSERR_NOERROR;
688 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
690 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
691 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
692 ret = MMSYSERR_NOERROR;
696 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
697 ret = MMSYSERR_INVALPARAM;
703 /**************************************************************************
706 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
707 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
709 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
710 dwUser, dwParam1, dwParam2);
718 /* FIXME: Pretend this is supported */
721 return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
723 return MMSYSERR_NOERROR;
724 case MXDM_GETNUMDEVS:
725 return MIX_GetNumDevs();
726 case MXDM_GETDEVCAPS:
727 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
728 case MXDM_GETLINEINFO:
729 return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
730 case MXDM_GETLINECONTROLS:
731 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
732 case MXDM_GETCONTROLDETAILS:
733 case MXDM_SETCONTROLDETAILS:
735 WARN("unknown message %d!\n", wMsg);
736 return MMSYSERR_NOTSUPPORTED;
742 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
743 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
745 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
746 return MMSYSERR_NOTENABLED;
748 #endif /* HAVE_COREAUDIO_COREAUDIO_H */