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 /**************************************************************************
484 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
485 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
487 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
488 dwUser, dwParam1, dwParam2);
496 /* FIXME: Pretend this is supported */
499 return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
501 return MMSYSERR_NOERROR;
502 case MXDM_GETNUMDEVS:
503 return MIX_GetNumDevs();
504 case MXDM_GETDEVCAPS:
505 case MXDM_GETLINEINFO:
506 case MXDM_GETLINECONTROLS:
507 case MXDM_GETCONTROLDETAILS:
508 case MXDM_SETCONTROLDETAILS:
510 WARN("unknown message %d!\n", wMsg);
511 return MMSYSERR_NOTSUPPORTED;
517 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
518 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
520 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
521 return MMSYSERR_NOTENABLED;
523 #endif /* HAVE_COREAUDIO_COREAUDIO_H */