po: Update Korean resource.
[wine.git] / dlls / winecoreaudio.drv / mixer.c
blob1e9faef6a7e12bb5731de600f5a5e76bb6726573
1 /*
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
23 #include "config.h"
24 #include "wine/port.h"
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "mmddk.h"
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
65 char *name;
66 int direction;
67 int numChannels;
68 int componentType;
69 AudioDeviceID deviceID;
70 } MixerLine;
72 typedef struct tagMixerCtrl
74 DWORD dwLineID;
75 MIXERCONTROLW ctrl;
76 } MixerCtrl;
78 typedef struct tagCoreAudio_Mixer
80 MIXERCAPSW caps;
82 MixerCtrl *mixerCtrls;
83 MixerLine *lines;
84 DWORD numCtrl;
85 } CoreAudio_Mixer;
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;
96 switch (uMsg) {
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);
110 #undef MSG_TO_STR
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);
150 #undef TYPE_TO_STR
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);
179 #undef TYPE_TO_STR
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;
186 switch (dwType) {
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);
194 #undef TYPE_TO_STR
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 AudioObjectPropertyAddress propertyAddress;
218 propertyAddress.mSelector = kAudioDevicePropertyMute;
219 propertyAddress.mScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
220 propertyAddress.mElement = 0;
221 if (AudioObjectHasProperty(deviceID, &propertyAddress))
223 /* check if we can set it */
224 err = AudioObjectIsPropertySettable(deviceID, &propertyAddress, &writable);
225 if (err == noErr)
226 return writable;
228 return FALSE;
232 * Getters
234 static BOOL MIX_LineGetVolume(DWORD lineID, DWORD channels, Float32 *left, Float32 *right)
236 MixerLine *line = &mixer.lines[lineID];
237 UInt32 size = sizeof(Float32);
238 OSStatus err = noErr;
239 AudioObjectPropertyAddress address;
240 *left = *right = 0.0;
242 address.mSelector = kAudioDevicePropertyVolumeScalar;
243 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
244 address.mElement = 1;
245 err = AudioObjectGetPropertyData(line->deviceID, &address, 0, NULL, &size, left);
246 if (err != noErr)
247 return FALSE;
249 if (channels == 2)
251 size = sizeof(Float32);
252 address.mElement = 2;
253 err = AudioObjectGetPropertyData(line->deviceID, &address, 0, NULL, &size, right);
254 if (err != noErr)
255 return FALSE;
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);
266 UInt32 val = 0;
267 OSStatus err = noErr;
268 AudioObjectPropertyAddress address;
269 address.mSelector = kAudioDevicePropertyMute;
270 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
271 address.mElement = 0;
272 err = AudioObjectGetPropertyData(line->deviceID, &address, 0, NULL, &size, &val);
273 *muted = val;
275 return (err == noErr);
279 * Setters
281 static BOOL MIX_LineSetVolume(DWORD lineID, DWORD channels, Float32 left, Float32 right)
283 MixerLine *line = &mixer.lines[lineID];
284 UInt32 size = sizeof(Float32);
285 AudioObjectPropertyAddress address;
286 OSStatus err = noErr;
287 TRACE("lineID %d channels %d left %f right %f\n", lineID, channels, left, right);
289 address.mSelector = kAudioDevicePropertyVolumeScalar;
290 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
291 if (channels == 2)
293 address.mElement = 1;
294 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
295 if (err != noErr)
296 return FALSE;
298 address.mElement = 2;
299 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &right);
301 else
304 FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
305 address.mElement = 0;
306 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
308 right = left;
309 address.mElement = 1;
310 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
311 if (err != noErr)
312 return FALSE;
313 address.mElement = 2;
314 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &right);
316 return (err == noErr);
319 static BOOL MIX_LineSetMute(DWORD lineID, BOOL mute)
321 MixerLine *line = &mixer.lines[lineID];
322 UInt32 val = mute;
323 UInt32 size = sizeof(UInt32);
324 AudioObjectPropertyAddress address;
325 OSStatus err = noErr;
327 address.mSelector = kAudioDevicePropertyMute;
328 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
329 address.mElement = 0;
330 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, 0, size, &val);
331 return (err == noErr);
334 static void MIX_FillControls(void)
336 int i;
337 int ctrl = 0;
338 MixerLine *line;
339 for (i = 0; i < mixer.caps.cDestinations; i++)
341 line = &mixer.lines[i];
342 mixer.mixerCtrls[ctrl].dwLineID = i;
343 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
344 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
345 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
346 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
347 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
348 mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
349 ctrl++;
351 mixer.mixerCtrls[ctrl].dwLineID = i;
352 if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
353 mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
355 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
356 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
357 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
358 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
359 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
360 ctrl++;
362 assert(ctrl == mixer.numCtrl);
365 /**************************************************************************
366 * CoreAudio_MixerInit
368 LONG CoreAudio_MixerInit(void)
370 OSStatus status;
371 UInt32 propertySize;
372 AudioObjectPropertyAddress propertyAddress;
373 AudioDeviceID *deviceArray = NULL;
374 CFStringRef name;
375 int i;
376 int numLines;
378 AudioStreamBasicDescription streamDescription;
380 /* Find number of lines */
381 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
382 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
383 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
384 status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize);
385 if (status)
387 ERR("AudioObjectGetPropertyDataSize for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
388 return DRV_FAILURE;
391 numLines = propertySize / sizeof(AudioDeviceID);
393 mixer.mixerCtrls = NULL;
394 mixer.lines = NULL;
395 mixer.numCtrl = 0;
397 mixer.caps.cDestinations = numLines;
398 mixer.caps.wMid = 0xAA;
399 mixer.caps.wPid = 0x55;
400 mixer.caps.vDriverVersion = 0x0100;
402 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
404 mixer.caps.fdwSupport = 0; /* No bits defined yet */
406 mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
407 if (!mixer.lines)
408 goto error;
410 deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
412 propertySize = sizeof(AudioDeviceID) * numLines;
413 status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, deviceArray);
414 if (status)
416 ERR("AudioObjectGetPropertyData for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
417 goto error;
420 for (i = 0; i < numLines; i++)
422 MixerLine *line = &mixer.lines[i];
424 line->deviceID = deviceArray[i];
426 propertySize = sizeof(CFStringRef);
427 propertyAddress.mSelector = kAudioObjectPropertyName;
428 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
429 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
430 status = AudioObjectGetPropertyData(line->deviceID, &propertyAddress, 0, NULL, &propertySize, &name);
431 if (status) {
432 ERR("AudioObjectGetPropertyData for kAudioObjectPropertyName return %s\n", wine_dbgstr_fourcc(status));
433 goto error;
436 line->name = HeapAlloc(GetProcessHeap(), 0, CFStringGetLength(name) + 1);
437 if (!line->name)
438 goto error;
440 CFStringGetCString(name, line->name, CFStringGetLength(name) + 1, kCFStringEncodingUTF8);
442 line->componentType = DeviceComponentType(line->name);
444 /* check for directions */
445 /* Output ? */
446 propertySize = sizeof(UInt32);
447 propertyAddress.mSelector = kAudioDevicePropertyStreams;
448 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
449 status = AudioObjectGetPropertyDataSize(line->deviceID, &propertyAddress, 0, NULL, &propertySize);
450 if (status) {
451 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status));
452 goto error;
455 if ( (propertySize / sizeof(AudioStreamID)) != 0)
457 line->direction |= OutputDevice;
459 /* Check the number of channel for the stream */
460 propertySize = sizeof(streamDescription);
461 propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
462 status = AudioObjectGetPropertyData(line->deviceID, &propertyAddress, 0, NULL, &propertySize, &streamDescription);
463 if (status != noErr) {
464 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
465 goto error;
467 line->numChannels = streamDescription.mChannelsPerFrame;
469 else
471 /* Input ? */
472 propertySize = sizeof(UInt32);
473 propertyAddress.mScope = kAudioDevicePropertyScopeInput;
474 status = AudioObjectGetPropertyDataSize(line->deviceID, &propertyAddress, 0, NULL, &propertySize);
475 if (status) {
476 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status));
477 goto error;
479 if ( (propertySize / sizeof(AudioStreamID)) != 0)
481 line->direction |= InputDevice;
483 /* Check the number of channel for the stream */
484 propertySize = sizeof(streamDescription);
485 propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
486 status = AudioObjectGetPropertyData(line->deviceID, &propertyAddress, 0, NULL, &propertySize, &streamDescription);
487 if (status != noErr) {
488 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
489 goto error;
491 line->numChannels = streamDescription.mChannelsPerFrame;
495 mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
497 mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
498 if (!mixer.mixerCtrls)
499 goto error;
501 MIX_FillControls();
503 HeapFree(GetProcessHeap(), 0, deviceArray);
504 return DRV_SUCCESS;
506 error:
507 if (mixer.lines)
509 int i;
510 for (i = 0; i < mixer.caps.cDestinations; i++)
512 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
514 HeapFree(GetProcessHeap(), 0, mixer.lines);
516 HeapFree(GetProcessHeap(), 0, deviceArray);
517 if (mixer.mixerCtrls)
518 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
519 return DRV_FAILURE;
522 /**************************************************************************
523 * CoreAudio_MixerRelease
525 void CoreAudio_MixerRelease(void)
527 TRACE("()\n");
529 if (mixer.lines)
531 int i;
532 for (i = 0; i < mixer.caps.cDestinations; i++)
534 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
536 HeapFree(GetProcessHeap(), 0, mixer.lines);
538 if (mixer.mixerCtrls)
539 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
542 /**************************************************************************
543 * MIX_Open [internal]
545 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
547 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
548 if (lpMod == NULL) {
549 WARN("invalid parameter: lpMod == NULL\n");
550 return MMSYSERR_INVALPARAM;
553 if (wDevID >= numMixers) {
554 WARN("bad device ID: %04X\n", wDevID);
555 return MMSYSERR_BADDEVICEID;
557 return MMSYSERR_NOERROR;
560 /**************************************************************************
561 * MIX_GetNumDevs [internal]
563 static DWORD MIX_GetNumDevs(void)
565 TRACE("()\n");
566 return numMixers;
569 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
571 TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
573 if (lpCaps == NULL) {
574 WARN("Invalid Parameter\n");
575 return MMSYSERR_INVALPARAM;
578 if (wDevID >= numMixers) {
579 WARN("bad device ID : %d\n", wDevID);
580 return MMSYSERR_BADDEVICEID;
582 memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
583 return MMSYSERR_NOERROR;
586 /**************************************************************************
587 * MIX_GetLineInfo [internal]
589 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
591 int i;
592 DWORD ret = MMSYSERR_ERROR;
593 MixerLine *line = NULL;
595 TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
597 if (lpMl == NULL) {
598 WARN("invalid parameter: lpMl = NULL\n");
599 return MMSYSERR_INVALPARAM;
602 if (lpMl->cbStruct != sizeof(*lpMl)) {
603 WARN("invalid parameter: lpMl->cbStruct\n");
604 return MMSYSERR_INVALPARAM;
607 if (wDevID >= numMixers) {
608 WARN("bad device ID: %04X\n", wDevID);
609 return MMSYSERR_BADDEVICEID;
612 /* FIXME: set all the variables correctly... the lines below
613 * are very wrong...
615 lpMl->dwUser = 0;
617 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
619 case MIXER_GETLINEINFOF_DESTINATION:
620 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
621 if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
623 lpMl->dwLineID = lpMl->dwDestination;
624 line = &mixer.lines[lpMl->dwDestination];
626 else ret = MIXERR_INVALLINE;
627 break;
628 case MIXER_GETLINEINFOF_COMPONENTTYPE:
629 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
630 for (i = 0; i < mixer.caps.cDestinations; i++)
632 if (mixer.lines[i].componentType == lpMl->dwComponentType)
634 lpMl->dwDestination = lpMl->dwLineID = i;
635 line = &mixer.lines[i];
636 break;
639 if (line == NULL)
641 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
642 ret = MIXERR_INVALVALUE;
644 break;
645 case MIXER_GETLINEINFOF_SOURCE:
646 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
647 break;
648 case MIXER_GETLINEINFOF_LINEID:
649 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
650 if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
652 lpMl->dwDestination = lpMl->dwLineID;
653 line = &mixer.lines[lpMl->dwLineID];
655 else ret = MIXERR_INVALLINE;
656 break;
657 case MIXER_GETLINEINFOF_TARGETTYPE:
658 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
659 switch (lpMl->Target.dwType) {
660 case MIXERLINE_TARGETTYPE_UNDEFINED:
661 case MIXERLINE_TARGETTYPE_WAVEOUT:
662 case MIXERLINE_TARGETTYPE_WAVEIN:
663 case MIXERLINE_TARGETTYPE_MIDIOUT:
664 case MIXERLINE_TARGETTYPE_MIDIIN:
665 case MIXERLINE_TARGETTYPE_AUX:
666 default:
667 FIXME("Unhandled target type (%s)\n",
668 getTargetType(lpMl->Target.dwType));
669 return MMSYSERR_INVALPARAM;
671 break;
672 default:
673 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
674 break;
677 if (line)
679 lpMl->dwComponentType = line->componentType;
680 lpMl->cChannels = line->numChannels;
681 lpMl->cControls = ControlsPerLine;
683 /* FIXME check there with CoreAudio */
684 lpMl->cConnections = 1;
685 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
687 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
688 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
690 if ( IsInput(line->direction) )
691 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
692 else
693 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
695 lpMl->Target.dwDeviceID = line->deviceID;
696 lpMl->Target.wMid = mixer.caps.wMid;
697 lpMl->Target.wPid = mixer.caps.wPid;
698 lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
700 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
701 ret = MMSYSERR_NOERROR;
703 return ret;
706 /**************************************************************************
707 * MIX_GetLineControls [internal]
709 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
711 DWORD ret = MMSYSERR_NOTENABLED;
712 int ctrl = 0;
713 TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
715 if (lpMlc == NULL) {
716 WARN("invalid parameter: lpMlc == NULL\n");
717 return MMSYSERR_INVALPARAM;
720 if (lpMlc->cbStruct < sizeof(*lpMlc)) {
721 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
722 return MMSYSERR_INVALPARAM;
725 if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
726 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
727 return MMSYSERR_INVALPARAM;
730 if (wDevID >= numMixers) {
731 WARN("bad device ID: %04X\n", wDevID);
732 return MMSYSERR_BADDEVICEID;
735 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
737 case MIXER_GETLINECONTROLSF_ALL:
738 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
739 if (lpMlc->cControls != ControlsPerLine)
741 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
742 ret = MMSYSERR_INVALPARAM;
744 else
746 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
748 int i;
749 for (i = 0; i < lpMlc->cControls; i++)
751 lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
753 ret = MMSYSERR_NOERROR;
755 else ret = MIXERR_INVALLINE;
757 break;
758 case MIXER_GETLINECONTROLSF_ONEBYID:
759 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
760 if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
762 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
763 ret = MMSYSERR_NOERROR;
765 else ret = MIXERR_INVALVALUE;
766 break;
767 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
768 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
769 if ( (lpMlc->dwLineID < 0) || (lpMlc->dwLineID >= mixer.caps.cDestinations) )
771 ret = MIXERR_INVALLINE;
772 break;
774 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
776 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
777 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
778 ret = MMSYSERR_NOERROR;
780 else
781 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
783 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
784 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
785 ret = MMSYSERR_NOERROR;
787 break;
788 default:
789 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
790 ret = MMSYSERR_INVALPARAM;
793 return ret;
796 /**************************************************************************
797 * MIX_GetControlDetails [internal]
799 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
801 DWORD ret = MMSYSERR_NOTSUPPORTED;
802 DWORD dwControlType;
804 TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
806 if (lpmcd == NULL) {
807 TRACE("invalid parameter: lpmcd == NULL\n");
808 return MMSYSERR_INVALPARAM;
811 if (wDevID >= numMixers) {
812 WARN("bad device ID: %04X\n", wDevID);
813 return MMSYSERR_BADDEVICEID;
816 if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
818 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
819 return MMSYSERR_NOTSUPPORTED;
822 if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl )
824 WARN("bad control ID: %d\n", lpmcd->dwControlID);
825 return MIXERR_INVALVALUE;
828 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID);
830 dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
831 switch (dwControlType)
833 case MIXERCONTROL_CONTROLTYPE_VOLUME:
834 FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels);
836 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
837 Float32 left, right;
839 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
840 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
841 return MMSYSERR_INVALPARAM;
844 if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) )
846 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
848 switch (lpmcd->cChannels)
850 case 1:
851 /* mono... so R = L */
852 mcdu->dwValue = left * 65535;
853 TRACE("Reading RL = %d\n", mcdu->dwValue);
854 break;
855 case 2:
856 /* stereo, left is paDetails[0] */
857 mcdu->dwValue = left * 65535;
858 TRACE("Reading L = %d\n", mcdu->dwValue);
859 mcdu++;
860 mcdu->dwValue = right * 65535;
861 TRACE("Reading R = %d\n", mcdu->dwValue);
862 break;
863 default:
864 WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
865 return MMSYSERR_INVALPARAM;
867 TRACE("=> %08x\n", mcdu->dwValue);
868 ret = MMSYSERR_NOERROR;
871 break;
872 case MIXERCONTROL_CONTROLTYPE_MUTE:
873 case MIXERCONTROL_CONTROLTYPE_ONOFF:
874 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
876 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
877 BOOL muted;
878 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
879 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails);
880 return MMSYSERR_INVALPARAM;
882 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
884 if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) )
886 mcdb->fValue = muted;
887 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
888 ret = MMSYSERR_NOERROR;
891 break;
892 case MIXERCONTROL_CONTROLTYPE_MIXER:
893 case MIXERCONTROL_CONTROLTYPE_MUX:
894 default:
895 FIXME("controlType : %s\n", getControlType(dwControlType));
896 break;
898 return ret;
901 /**************************************************************************
902 * MIX_SetControlDetails [internal]
904 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
906 DWORD ret = MMSYSERR_NOTSUPPORTED;
907 DWORD dwControlType;
909 TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
911 if (lpmcd == NULL) {
912 TRACE("invalid parameter: lpmcd == NULL\n");
913 return MMSYSERR_INVALPARAM;
916 if (wDevID >= numMixers) {
917 WARN("bad device ID: %04X\n", wDevID);
918 return MMSYSERR_BADDEVICEID;
921 if ( (fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
923 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK);
924 return MMSYSERR_NOTSUPPORTED;
927 TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd->dwControlID);
928 dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
929 switch (dwControlType)
931 case MIXERCONTROL_CONTROLTYPE_VOLUME:
932 FIXME("controlType : %s\n", getControlType(dwControlType));
934 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
935 Float32 left, right = 0;
937 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
938 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
939 return MMSYSERR_INVALPARAM;
942 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
944 switch (lpmcd->cChannels)
946 case 1:
947 /* mono... so R = L */
948 TRACE("Setting RL to %d\n", mcdu->dwValue);
949 left = (Float32) mcdu->dwValue / 65535.0;
950 break;
951 case 2:
952 /* stereo, left is paDetails[0] */
953 TRACE("Setting L to %d\n", mcdu->dwValue);
954 left = (Float32) mcdu->dwValue / 65535.0;
955 mcdu++;
956 TRACE("Setting R to %d\n", mcdu->dwValue);
957 right = (Float32) mcdu->dwValue / 65535.0;
958 break;
959 default:
960 WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
961 return MMSYSERR_INVALPARAM;
963 if ( MIX_LineSetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, left, right) )
964 ret = MMSYSERR_NOERROR;
966 break;
967 case MIXERCONTROL_CONTROLTYPE_MUTE:
968 case MIXERCONTROL_CONTROLTYPE_ONOFF:
969 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
971 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
973 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
974 WARN("invalid parameter: cbDetails\n");
975 return MMSYSERR_INVALPARAM;
977 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
978 if ( MIX_LineSetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, mcdb->fValue) )
979 ret = MMSYSERR_NOERROR;
981 break;
982 case MIXERCONTROL_CONTROLTYPE_MIXER:
983 case MIXERCONTROL_CONTROLTYPE_MUX:
984 default:
985 FIXME("controlType : %s\n", getControlType(dwControlType));
986 ret = MMSYSERR_NOTSUPPORTED;
987 break;
989 return ret;
992 /**************************************************************************
993 * mxdMessage
995 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
996 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
998 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
999 dwUser, dwParam1, dwParam2);
1001 switch (wMsg)
1003 case DRVM_INIT:
1004 case DRVM_EXIT:
1005 case DRVM_ENABLE:
1006 case DRVM_DISABLE:
1007 /* FIXME: Pretend this is supported */
1008 return 0;
1009 case MXDM_OPEN:
1010 return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
1011 case MXDM_CLOSE:
1012 return MMSYSERR_NOERROR;
1013 case MXDM_GETNUMDEVS:
1014 return MIX_GetNumDevs();
1015 case MXDM_GETDEVCAPS:
1016 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
1017 case MXDM_GETLINEINFO:
1018 return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
1019 case MXDM_GETLINECONTROLS:
1020 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
1021 case MXDM_GETCONTROLDETAILS:
1022 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1023 case MXDM_SETCONTROLDETAILS:
1024 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1025 default:
1026 WARN("unknown message %d!\n", wMsg);
1027 return MMSYSERR_NOTSUPPORTED;
1031 #else
1033 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1034 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1036 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1037 return MMSYSERR_NOTENABLED;
1039 #endif /* HAVE_COREAUDIO_COREAUDIO_H */