2 * Sample MIXER Wine Driver for Mac OS X (based on OSS mixer)
4 * Copyright 1997 Marcus Meissner
5 * 1999,2001 Eric Pouech
6 * 2006,2007 Emmanuel Maillard
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/port.h"
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
40 #include "coreaudio.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(mixer);
46 #if defined(HAVE_COREAUDIO_COREAUDIO_H)
47 #include <CoreAudio/CoreAudio.h>
48 #include <CoreFoundation/CoreFoundation.h>
50 #define WINE_MIXER_NAME "CoreAudio Mixer"
52 #define InputDevice (1 << 0)
53 #define OutputDevice (1 << 1)
55 #define IsInput(dir) ((dir) & InputDevice)
56 #define IsOutput(dir) ((dir) & OutputDevice)
58 #define ControlsPerLine 2 /* number of control per line : volume & (mute | onoff) */
60 #define IDControlVolume 0
61 #define IDControlMute 1
63 typedef struct tagMixerLine
69 AudioDeviceID deviceID;
72 typedef struct tagMixerCtrl
78 typedef struct tagCoreAudio_Mixer
82 MixerCtrl *mixerCtrls;
87 static CoreAudio_Mixer mixer;
88 static int numMixers = 1;
90 /**************************************************************************
93 static const char * getMessage(UINT uMsg)
96 #define MSG_TO_STR(x) case x: return #x;
98 MSG_TO_STR(DRVM_INIT);
99 MSG_TO_STR(DRVM_EXIT);
100 MSG_TO_STR(DRVM_ENABLE);
101 MSG_TO_STR(DRVM_DISABLE);
102 MSG_TO_STR(MXDM_GETDEVCAPS);
103 MSG_TO_STR(MXDM_GETLINEINFO);
104 MSG_TO_STR(MXDM_GETNUMDEVS);
105 MSG_TO_STR(MXDM_OPEN);
106 MSG_TO_STR(MXDM_CLOSE);
107 MSG_TO_STR(MXDM_GETLINECONTROLS);
108 MSG_TO_STR(MXDM_GETCONTROLDETAILS);
109 MSG_TO_STR(MXDM_SETCONTROLDETAILS);
112 sprintf(str, "UNKNOWN(%08x)", uMsg);
116 static const char * getControlType(DWORD dwControlType)
119 #define TYPE_TO_STR(x) case x: return #x;
120 switch (dwControlType) {
121 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
122 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
123 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
124 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
125 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
126 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
127 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
128 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
129 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
130 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
131 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
132 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
133 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
134 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
135 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
136 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
137 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
138 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
139 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
140 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
141 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
142 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
143 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
144 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
145 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
146 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
147 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
148 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
149 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
150 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
151 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
154 sprintf(str, "UNKNOWN(%08x)", dwControlType);
158 static const char * getComponentType(DWORD dwComponentType)
161 #define TYPE_TO_STR(x) case x: return #x;
162 switch (dwComponentType) {
163 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED);
164 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL);
165 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE);
166 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR);
167 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
168 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES);
169 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE);
170 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
171 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN);
172 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED);
173 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL);
174 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE);
175 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);
176 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);
177 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
178 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE);
179 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER);
180 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
181 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY);
182 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG);
185 sprintf(str, "UNKNOWN(%08x)", dwComponentType);
189 static const char * getTargetType(DWORD dwType)
192 #define TYPE_TO_STR(x) case x: return #x;
194 TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED);
195 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT);
196 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN);
197 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT);
198 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN);
199 TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX);
202 sprintf(str, "UNKNOWN(%08x)", dwType);
206 /* FIXME is there a better way ? */
207 static DWORD DeviceComponentType(char *name)
209 if (strcmp(name, "Built-in Microphone") == 0)
210 return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
212 if (strcmp(name, "Built-in Line Input") == 0)
213 return MIXERLINE_COMPONENTTYPE_SRC_LINE;
215 if (strcmp(name, "Built-in Output") == 0)
216 return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
218 return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
221 static BOOL DeviceHasMute(AudioDeviceID deviceID, Boolean isInput)
223 Boolean writable = false;
224 OSStatus err = noErr;
225 err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, NULL);
228 /* check if we can set it */
229 err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, &writable);
239 static BOOL MIX_LineGetVolume(DWORD lineID, DWORD channels, Float32 *left, Float32 *right)
241 MixerLine *line = &mixer.lines[lineID];
242 UInt32 size = sizeof(Float32);
243 OSStatus err = noErr;
244 *left = *right = 0.0;
246 err = AudioDeviceGetProperty(line->deviceID, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, left);
252 size = sizeof(Float32);
253 err = AudioDeviceGetProperty(line->deviceID, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, right);
258 TRACE("lineID %d channels %d return left %f right %f\n", lineID, channels, *left, *right);
259 return (err == noErr);
262 static BOOL MIX_LineGetMute(DWORD lineID, BOOL *muted)
264 MixerLine *line = &mixer.lines[lineID];
265 UInt32 size = sizeof(UInt32);
267 OSStatus err = noErr;
268 err = AudioDeviceGetProperty(line->deviceID, 0, IsInput(line->direction), kAudioDevicePropertyMute, &size, &val);
271 return (err == noErr);
274 static void MIX_FillControls(void)
279 for (i = 0; i < mixer.caps.cDestinations; i++)
281 line = &mixer.lines[i];
282 mixer.mixerCtrls[ctrl].dwLineID = i;
283 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
284 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
285 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
286 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
287 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
288 mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
291 mixer.mixerCtrls[ctrl].dwLineID = i;
292 if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
293 mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
295 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
296 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
297 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
298 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
299 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
302 assert(ctrl == mixer.numCtrl);
305 /**************************************************************************
306 * CoreAudio_MixerInit
308 LONG CoreAudio_MixerInit(void)
312 AudioDeviceID *deviceArray = NULL;
313 char name[MAXPNAMELEN];
317 AudioStreamBasicDescription streamDescription;
319 /* Find number of lines */
320 status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
323 ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
324 (char) (status >> 16),
325 (char) (status >> 8),
330 numLines = propertySize / sizeof(AudioDeviceID);
332 mixer.mixerCtrls = NULL;
336 mixer.caps.cDestinations = numLines;
337 mixer.caps.wMid = 0xAA;
338 mixer.caps.wPid = 0x55;
339 mixer.caps.vDriverVersion = 0x0100;
341 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
343 mixer.caps.fdwSupport = 0; /* No bits defined yet */
345 mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
349 deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
351 propertySize = sizeof(AudioDeviceID) * numLines;
352 status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray);
355 ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
356 (char) (status >> 16),
357 (char) (status >> 8),
362 for (i = 0; i < numLines; i++)
365 MixerLine *line = &mixer.lines[i];
367 line->deviceID = deviceArray[i];
369 propertySize = MAXPNAMELEN;
370 status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
372 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
373 (char) (status >> 16),
374 (char) (status >> 8),
379 line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1);
383 memcpy(line->name, name, strlen(name));
385 line->componentType = DeviceComponentType(line->name);
387 /* check for directions */
389 propertySize = sizeof(UInt32);
390 status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write );
392 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %c%c%c%c\n", (char) (status >> 24),
393 (char) (status >> 16),
394 (char) (status >> 8),
399 if ( (propertySize / sizeof(AudioStreamID)) != 0)
401 line->direction |= OutputDevice;
403 /* Check the number of channel for the stream */
404 propertySize = sizeof(streamDescription);
405 status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
406 if (status != noErr) {
407 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
408 (char) (status >> 16),
409 (char) (status >> 8),
413 line->numChannels = streamDescription.mChannelsPerFrame;
418 propertySize = sizeof(UInt32);
419 status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write );
421 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %c%c%c%c\n", (char) (status >> 24),
422 (char) (status >> 16),
423 (char) (status >> 8),
427 if ( (propertySize / sizeof(AudioStreamID)) != 0)
429 line->direction |= InputDevice;
431 /* Check the number of channel for the stream */
432 propertySize = sizeof(streamDescription);
433 status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
434 if (status != noErr) {
435 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
436 (char) (status >> 16),
437 (char) (status >> 8),
441 line->numChannels = streamDescription.mChannelsPerFrame;
445 mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
447 mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
448 if (!mixer.mixerCtrls)
453 HeapFree(GetProcessHeap(), 0, deviceArray);
460 for (i = 0; i < mixer.caps.cDestinations; i++)
462 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
464 HeapFree(GetProcessHeap(), 0, mixer.lines);
466 HeapFree(GetProcessHeap(), 0, deviceArray);
467 if (mixer.mixerCtrls)
468 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
472 /**************************************************************************
473 * CoreAudio_MixerRelease
475 void CoreAudio_MixerRelease(void)
482 for (i = 0; i < mixer.caps.cDestinations; i++)
484 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
486 HeapFree(GetProcessHeap(), 0, mixer.lines);
488 if (mixer.mixerCtrls)
489 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
492 /**************************************************************************
493 * MIX_Open [internal]
495 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
497 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
499 WARN("invalid parameter: lpMod == NULL\n");
500 return MMSYSERR_INVALPARAM;
503 if (wDevID >= numMixers) {
504 WARN("bad device ID: %04X\n", wDevID);
505 return MMSYSERR_BADDEVICEID;
507 return MMSYSERR_NOERROR;
510 /**************************************************************************
511 * MIX_GetNumDevs [internal]
513 static DWORD MIX_GetNumDevs(void)
519 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
521 TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
523 if (lpCaps == NULL) {
524 WARN("Invalid Parameter\n");
525 return MMSYSERR_INVALPARAM;
528 if (wDevID >= numMixers) {
529 WARN("bad device ID : %d\n", wDevID);
530 return MMSYSERR_BADDEVICEID;
532 memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
533 return MMSYSERR_NOERROR;
536 /**************************************************************************
537 * MIX_GetLineInfo [internal]
539 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
542 DWORD ret = MMSYSERR_ERROR;
543 MixerLine *line = NULL;
545 TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
548 WARN("invalid parameter: lpMl = NULL\n");
549 return MMSYSERR_INVALPARAM;
552 if (lpMl->cbStruct != sizeof(*lpMl)) {
553 WARN("invalid parameter: lpMl->cbStruct\n");
554 return MMSYSERR_INVALPARAM;
557 if (wDevID >= numMixers) {
558 WARN("bad device ID: %04X\n", wDevID);
559 return MMSYSERR_BADDEVICEID;
562 /* FIXME: set all the variables correctly... the lines below
567 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
569 case MIXER_GETLINEINFOF_DESTINATION:
570 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
571 if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
573 lpMl->dwLineID = lpMl->dwDestination;
574 line = &mixer.lines[lpMl->dwDestination];
576 else ret = MIXERR_INVALLINE;
578 case MIXER_GETLINEINFOF_COMPONENTTYPE:
579 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
580 for (i = 0; i < mixer.caps.cDestinations; i++)
582 if (mixer.lines[i].componentType == lpMl->dwComponentType)
584 lpMl->dwDestination = lpMl->dwLineID = i;
585 line = &mixer.lines[i];
591 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
592 ret = MIXERR_INVALVALUE;
595 case MIXER_GETLINEINFOF_SOURCE:
596 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
598 case MIXER_GETLINEINFOF_LINEID:
599 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
600 if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
602 lpMl->dwDestination = lpMl->dwLineID;
603 line = &mixer.lines[lpMl->dwLineID];
605 else ret = MIXERR_INVALLINE;
607 case MIXER_GETLINEINFOF_TARGETTYPE:
608 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
609 switch (lpMl->Target.dwType) {
610 case MIXERLINE_TARGETTYPE_UNDEFINED:
611 case MIXERLINE_TARGETTYPE_WAVEOUT:
612 case MIXERLINE_TARGETTYPE_WAVEIN:
613 case MIXERLINE_TARGETTYPE_MIDIOUT:
614 case MIXERLINE_TARGETTYPE_MIDIIN:
615 case MIXERLINE_TARGETTYPE_AUX:
617 FIXME("Unhandled target type (%s)\n",
618 getTargetType(lpMl->Target.dwType));
619 return MMSYSERR_INVALPARAM;
623 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
629 lpMl->dwComponentType = line->componentType;
630 lpMl->cChannels = line->numChannels;
631 lpMl->cControls = ControlsPerLine;
633 /* FIXME check there with CoreAudio */
634 lpMl->cConnections = 1;
635 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
637 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
638 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
640 if ( IsInput(line->direction) )
641 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
643 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
645 lpMl->Target.dwDeviceID = line->deviceID;
646 lpMl->Target.wMid = mixer.caps.wMid;
647 lpMl->Target.wPid = mixer.caps.wPid;
648 lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
650 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
651 ret = MMSYSERR_NOERROR;
656 /**************************************************************************
657 * MIX_GetLineControls [internal]
659 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
661 DWORD ret = MMSYSERR_NOTENABLED;
663 TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
666 WARN("invalid parameter: lpMlc == NULL\n");
667 return MMSYSERR_INVALPARAM;
670 if (lpMlc->cbStruct < sizeof(*lpMlc)) {
671 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
672 return MMSYSERR_INVALPARAM;
675 if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
676 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
677 return MMSYSERR_INVALPARAM;
680 if (wDevID >= numMixers) {
681 WARN("bad device ID: %04X\n", wDevID);
682 return MMSYSERR_BADDEVICEID;
685 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
687 case MIXER_GETLINECONTROLSF_ALL:
688 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
689 if (lpMlc->cControls != ControlsPerLine)
691 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
692 ret = MMSYSERR_INVALPARAM;
696 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
699 for (i = 0; i < lpMlc->cControls; i++)
701 lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
703 ret = MMSYSERR_NOERROR;
705 else ret = MIXERR_INVALLINE;
708 case MIXER_GETLINECONTROLSF_ONEBYID:
709 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
710 if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
712 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
713 ret = MMSYSERR_NOERROR;
715 else ret = MIXERR_INVALVALUE;
717 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
718 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
719 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
721 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
722 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
723 ret = MMSYSERR_NOERROR;
726 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
728 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
729 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
730 ret = MMSYSERR_NOERROR;
734 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
735 ret = MMSYSERR_INVALPARAM;
741 /**************************************************************************
742 * MIX_GetControlDetails [internal]
744 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
746 DWORD ret = MMSYSERR_NOTSUPPORTED;
749 TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
752 TRACE("invalid parameter: lpmcd == NULL\n");
753 return MMSYSERR_INVALPARAM;
756 if (wDevID >= numMixers) {
757 WARN("bad device ID: %04X\n", wDevID);
758 return MMSYSERR_BADDEVICEID;
761 if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
763 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
764 return MMSYSERR_NOTSUPPORTED;
767 if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl )
769 WARN("bad control ID: %d\n", lpmcd->dwControlID);
770 return MIXERR_INVALVALUE;
773 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID);
775 dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
776 switch (dwControlType)
778 case MIXERCONTROL_CONTROLTYPE_VOLUME:
779 FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels);
781 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
784 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
785 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
786 return MMSYSERR_INVALPARAM;
789 if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) )
791 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
793 switch (lpmcd->cChannels)
796 /* mono... so R = L */
797 mcdu->dwValue = left * 65535;
798 TRACE("Reading RL = %d\n", mcdu->dwValue);
801 /* stereo, left is paDetails[0] */
802 mcdu->dwValue = left * 65535;
803 TRACE("Reading L = %d\n", mcdu->dwValue);
805 mcdu->dwValue = right * 65535;
806 TRACE("Reading R = %d\n", mcdu->dwValue);
809 WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
810 return MMSYSERR_INVALPARAM;
812 TRACE("=> %08x\n", mcdu->dwValue);
813 ret = MMSYSERR_NOERROR;
817 case MIXERCONTROL_CONTROLTYPE_MUTE:
818 case MIXERCONTROL_CONTROLTYPE_ONOFF:
819 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
821 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
823 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
824 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails);
825 return MMSYSERR_INVALPARAM;
827 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
829 if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) )
831 mcdb->fValue = muted;
832 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
833 ret = MMSYSERR_NOERROR;
837 case MIXERCONTROL_CONTROLTYPE_MIXER:
838 case MIXERCONTROL_CONTROLTYPE_MUX:
840 FIXME("controlType : %s\n", getControlType(dwControlType));
846 /**************************************************************************
849 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
850 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
852 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
853 dwUser, dwParam1, dwParam2);
861 /* FIXME: Pretend this is supported */
864 return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
866 return MMSYSERR_NOERROR;
867 case MXDM_GETNUMDEVS:
868 return MIX_GetNumDevs();
869 case MXDM_GETDEVCAPS:
870 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
871 case MXDM_GETLINEINFO:
872 return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
873 case MXDM_GETLINECONTROLS:
874 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
875 case MXDM_GETCONTROLDETAILS:
876 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
877 case MXDM_SETCONTROLDETAILS:
879 WARN("unknown message %d!\n", wMsg);
880 return MMSYSERR_NOTSUPPORTED;
886 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
887 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
889 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
890 return MMSYSERR_NOTENABLED;
892 #endif /* HAVE_COREAUDIO_COREAUDIO_H */