po: Update Lithuanian translation.
[wine/multimedia.git] / dlls / winecoreaudio.drv / mixer.c
blob7a64aa4406f59b5544299ba7ebe72f0f0f70c15c
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 #include <CoreAudio/CoreAudio.h>
47 #include <CoreFoundation/CoreFoundation.h>
49 #define WINE_MIXER_NAME "CoreAudio Mixer"
51 #define InputDevice (1 << 0)
52 #define OutputDevice (1 << 1)
54 #define IsInput(dir) ((dir) & InputDevice)
55 #define IsOutput(dir) ((dir) & OutputDevice)
57 #define ControlsPerLine 2 /* number of control per line : volume & (mute | onoff) */
59 #define IDControlVolume 0
60 #define IDControlMute 1
62 typedef struct tagMixerLine
64 char *name;
65 int direction;
66 int numChannels;
67 int componentType;
68 AudioDeviceID deviceID;
69 } MixerLine;
71 typedef struct tagMixerCtrl
73 DWORD dwLineID;
74 MIXERCONTROLW ctrl;
75 } MixerCtrl;
77 typedef struct tagCoreAudio_Mixer
79 MIXERCAPSW caps;
81 MixerCtrl *mixerCtrls;
82 MixerLine *lines;
83 DWORD numCtrl;
84 } CoreAudio_Mixer;
86 static CoreAudio_Mixer mixer;
87 static int numMixers = 1;
89 /**************************************************************************
92 static const char * getMessage(UINT uMsg)
94 #define MSG_TO_STR(x) case x: return #x;
95 switch (uMsg) {
96 MSG_TO_STR(DRVM_INIT);
97 MSG_TO_STR(DRVM_EXIT);
98 MSG_TO_STR(DRVM_ENABLE);
99 MSG_TO_STR(DRVM_DISABLE);
100 MSG_TO_STR(MXDM_GETDEVCAPS);
101 MSG_TO_STR(MXDM_GETLINEINFO);
102 MSG_TO_STR(MXDM_GETNUMDEVS);
103 MSG_TO_STR(MXDM_OPEN);
104 MSG_TO_STR(MXDM_CLOSE);
105 MSG_TO_STR(MXDM_GETLINECONTROLS);
106 MSG_TO_STR(MXDM_GETCONTROLDETAILS);
107 MSG_TO_STR(MXDM_SETCONTROLDETAILS);
109 #undef MSG_TO_STR
110 return wine_dbg_sprintf("UNKNOWN(%08x)", uMsg);
113 static const char * getControlType(DWORD dwControlType)
115 #define TYPE_TO_STR(x) case x: return #x;
116 switch (dwControlType) {
117 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
118 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
119 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
120 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
121 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
122 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
123 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
124 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
125 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
126 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
127 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
128 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
129 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
130 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
131 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
132 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
133 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
134 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
135 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
136 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
137 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
138 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
139 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
140 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
141 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
142 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
143 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
144 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
145 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
146 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
147 TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
149 #undef TYPE_TO_STR
150 return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType);
153 static const char * getComponentType(DWORD dwComponentType)
155 #define TYPE_TO_STR(x) case x: return #x;
156 switch (dwComponentType) {
157 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED);
158 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL);
159 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE);
160 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR);
161 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
162 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES);
163 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE);
164 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
165 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN);
166 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED);
167 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL);
168 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE);
169 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);
170 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);
171 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
172 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE);
173 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER);
174 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
175 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY);
176 TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG);
178 #undef TYPE_TO_STR
179 return wine_dbg_sprintf("UNKNOWN(%08x)", dwComponentType);
182 static const char * getTargetType(DWORD dwType)
184 #define TYPE_TO_STR(x) case x: return #x;
185 switch (dwType) {
186 TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED);
187 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT);
188 TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN);
189 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT);
190 TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN);
191 TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX);
193 #undef TYPE_TO_STR
194 return wine_dbg_sprintf("UNKNOWN(%08x)", dwType);
197 /* FIXME is there a better way ? */
198 static DWORD DeviceComponentType(char *name)
200 if (strcmp(name, "Built-in Microphone") == 0)
201 return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
203 if (strcmp(name, "Built-in Line Input") == 0)
204 return MIXERLINE_COMPONENTTYPE_SRC_LINE;
206 if (strcmp(name, "Built-in Output") == 0)
207 return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
209 return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
212 static BOOL DeviceHasMute(AudioDeviceID deviceID, Boolean isInput)
214 Boolean writable = false;
215 OSStatus err = noErr;
216 AudioObjectPropertyAddress propertyAddress;
217 propertyAddress.mSelector = kAudioDevicePropertyMute;
218 propertyAddress.mScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
219 propertyAddress.mElement = 0;
220 if (AudioObjectHasProperty(deviceID, &propertyAddress))
222 /* check if we can set it */
223 err = AudioObjectIsPropertySettable(deviceID, &propertyAddress, &writable);
224 if (err == noErr)
225 return writable;
227 return FALSE;
231 * Getters
233 static BOOL MIX_LineGetVolume(DWORD lineID, DWORD channels, Float32 *left, Float32 *right)
235 MixerLine *line = &mixer.lines[lineID];
236 UInt32 size = sizeof(Float32);
237 OSStatus err = noErr;
238 AudioObjectPropertyAddress address;
239 *left = *right = 0.0;
241 address.mSelector = kAudioDevicePropertyVolumeScalar;
242 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
243 address.mElement = 1;
244 err = AudioObjectGetPropertyData(line->deviceID, &address, 0, NULL, &size, left);
245 if (err != noErr)
246 return FALSE;
248 if (channels == 2)
250 size = sizeof(Float32);
251 address.mElement = 2;
252 err = AudioObjectGetPropertyData(line->deviceID, &address, 0, NULL, &size, right);
253 if (err != noErr)
254 return FALSE;
257 TRACE("lineID %d channels %d return left %f right %f\n", lineID, channels, *left, *right);
258 return (err == noErr);
261 static BOOL MIX_LineGetMute(DWORD lineID, BOOL *muted)
263 MixerLine *line = &mixer.lines[lineID];
264 UInt32 size = sizeof(UInt32);
265 UInt32 val = 0;
266 OSStatus err = noErr;
267 AudioObjectPropertyAddress address;
268 address.mSelector = kAudioDevicePropertyMute;
269 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
270 address.mElement = 0;
271 err = AudioObjectGetPropertyData(line->deviceID, &address, 0, NULL, &size, &val);
272 *muted = val;
274 return (err == noErr);
278 * Setters
280 static BOOL MIX_LineSetVolume(DWORD lineID, DWORD channels, Float32 left, Float32 right)
282 MixerLine *line = &mixer.lines[lineID];
283 UInt32 size = sizeof(Float32);
284 AudioObjectPropertyAddress address;
285 OSStatus err = noErr;
286 TRACE("lineID %d channels %d left %f right %f\n", lineID, channels, left, right);
288 address.mSelector = kAudioDevicePropertyVolumeScalar;
289 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
290 if (channels == 2)
292 address.mElement = 1;
293 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
294 if (err != noErr)
295 return FALSE;
297 address.mElement = 2;
298 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &right);
300 else
303 FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
304 address.mElement = 0;
305 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
307 right = left;
308 address.mElement = 1;
309 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &left);
310 if (err != noErr)
311 return FALSE;
312 address.mElement = 2;
313 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, NULL, size, &right);
315 return (err == noErr);
318 static BOOL MIX_LineSetMute(DWORD lineID, BOOL mute)
320 MixerLine *line = &mixer.lines[lineID];
321 UInt32 val = mute;
322 UInt32 size = sizeof(UInt32);
323 AudioObjectPropertyAddress address;
324 OSStatus err = noErr;
326 address.mSelector = kAudioDevicePropertyMute;
327 address.mScope = IsInput(line->direction) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
328 address.mElement = 0;
329 err = AudioObjectSetPropertyData(line->deviceID, &address, 0, 0, size, &val);
330 return (err == noErr);
333 static void MIX_FillControls(void)
335 int i;
336 int ctrl = 0;
337 MixerLine *line;
338 for (i = 0; i < mixer.caps.cDestinations; i++)
340 line = &mixer.lines[i];
341 mixer.mixerCtrls[ctrl].dwLineID = i;
342 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
343 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
344 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
345 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
346 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
347 mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
348 ctrl++;
350 mixer.mixerCtrls[ctrl].dwLineID = i;
351 if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
352 mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
354 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
355 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
356 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
357 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
358 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
359 ctrl++;
361 assert(ctrl == mixer.numCtrl);
364 /**************************************************************************
365 * CoreAudio_MixerInit
367 LONG CoreAudio_MixerInit(void)
369 OSStatus status;
370 UInt32 propertySize;
371 AudioObjectPropertyAddress propertyAddress;
372 AudioDeviceID *deviceArray = NULL;
373 CFStringRef name;
374 int i;
375 int numLines;
377 AudioStreamBasicDescription streamDescription;
379 /* Find number of lines */
380 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
381 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
382 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
383 status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize);
384 if (status)
386 ERR("AudioObjectGetPropertyDataSize for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
387 return DRV_FAILURE;
390 numLines = propertySize / sizeof(AudioDeviceID);
392 mixer.mixerCtrls = NULL;
393 mixer.lines = NULL;
394 mixer.numCtrl = 0;
396 mixer.caps.cDestinations = numLines;
397 mixer.caps.wMid = 0xAA;
398 mixer.caps.wPid = 0x55;
399 mixer.caps.vDriverVersion = 0x0100;
401 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
403 mixer.caps.fdwSupport = 0; /* No bits defined yet */
405 mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
406 if (!mixer.lines)
407 goto error;
409 deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
411 propertySize = sizeof(AudioDeviceID) * numLines;
412 status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, deviceArray);
413 if (status)
415 ERR("AudioObjectGetPropertyData for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
416 goto error;
419 for (i = 0; i < numLines; i++)
421 MixerLine *line = &mixer.lines[i];
423 line->deviceID = deviceArray[i];
425 propertySize = sizeof(CFStringRef);
426 propertyAddress.mSelector = kAudioObjectPropertyName;
427 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
428 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
429 status = AudioObjectGetPropertyData(line->deviceID, &propertyAddress, 0, NULL, &propertySize, &name);
430 if (status) {
431 ERR("AudioObjectGetPropertyData for kAudioObjectPropertyName return %s\n", wine_dbgstr_fourcc(status));
432 goto error;
435 line->name = HeapAlloc(GetProcessHeap(), 0, CFStringGetLength(name) + 1);
436 if (!line->name)
437 goto error;
439 CFStringGetCString(name, line->name, CFStringGetLength(name) + 1, kCFStringEncodingUTF8);
441 line->componentType = DeviceComponentType(line->name);
443 /* check for directions */
444 /* Output ? */
445 propertySize = sizeof(UInt32);
446 propertyAddress.mSelector = kAudioDevicePropertyStreams;
447 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
448 status = AudioObjectGetPropertyDataSize(line->deviceID, &propertyAddress, 0, NULL, &propertySize);
449 if (status) {
450 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status));
451 goto error;
454 if ( (propertySize / sizeof(AudioStreamID)) != 0)
456 line->direction |= OutputDevice;
458 /* Check the number of channel for the stream */
459 propertySize = sizeof(streamDescription);
460 propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
461 status = AudioObjectGetPropertyData(line->deviceID, &propertyAddress, 0, NULL, &propertySize, &streamDescription);
462 if (status != noErr) {
463 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
464 goto error;
466 line->numChannels = streamDescription.mChannelsPerFrame;
468 else
470 /* Input ? */
471 propertySize = sizeof(UInt32);
472 propertyAddress.mScope = kAudioDevicePropertyScopeInput;
473 status = AudioObjectGetPropertyDataSize(line->deviceID, &propertyAddress, 0, NULL, &propertySize);
474 if (status) {
475 ERR("AudioObjectGetPropertyDataSize for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status));
476 goto error;
478 if ( (propertySize / sizeof(AudioStreamID)) != 0)
480 line->direction |= InputDevice;
482 /* Check the number of channel for the stream */
483 propertySize = sizeof(streamDescription);
484 propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
485 status = AudioObjectGetPropertyData(line->deviceID, &propertyAddress, 0, NULL, &propertySize, &streamDescription);
486 if (status != noErr) {
487 ERR("AudioObjectGetPropertyData for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
488 goto error;
490 line->numChannels = streamDescription.mChannelsPerFrame;
494 mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
496 mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
497 if (!mixer.mixerCtrls)
498 goto error;
500 MIX_FillControls();
502 HeapFree(GetProcessHeap(), 0, deviceArray);
503 return DRV_SUCCESS;
505 error:
506 if (mixer.lines)
508 int i;
509 for (i = 0; i < mixer.caps.cDestinations; i++)
511 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
513 HeapFree(GetProcessHeap(), 0, mixer.lines);
515 HeapFree(GetProcessHeap(), 0, deviceArray);
516 if (mixer.mixerCtrls)
517 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
518 return DRV_FAILURE;
521 /**************************************************************************
522 * CoreAudio_MixerRelease
524 void CoreAudio_MixerRelease(void)
526 TRACE("()\n");
528 if (mixer.lines)
530 int i;
531 for (i = 0; i < mixer.caps.cDestinations; i++)
533 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
535 HeapFree(GetProcessHeap(), 0, mixer.lines);
537 if (mixer.mixerCtrls)
538 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
541 /**************************************************************************
542 * MIX_Open [internal]
544 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
546 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
547 if (lpMod == NULL) {
548 WARN("invalid parameter: lpMod == NULL\n");
549 return MMSYSERR_INVALPARAM;
552 if (wDevID >= numMixers) {
553 WARN("bad device ID: %04X\n", wDevID);
554 return MMSYSERR_BADDEVICEID;
556 return MMSYSERR_NOERROR;
559 /**************************************************************************
560 * MIX_GetNumDevs [internal]
562 static DWORD MIX_GetNumDevs(void)
564 TRACE("()\n");
565 return numMixers;
568 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
570 TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
572 if (lpCaps == NULL) {
573 WARN("Invalid Parameter\n");
574 return MMSYSERR_INVALPARAM;
577 if (wDevID >= numMixers) {
578 WARN("bad device ID : %d\n", wDevID);
579 return MMSYSERR_BADDEVICEID;
581 memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
582 return MMSYSERR_NOERROR;
585 /**************************************************************************
586 * MIX_GetLineInfo [internal]
588 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
590 int i;
591 DWORD ret = MMSYSERR_ERROR;
592 MixerLine *line = NULL;
594 TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
596 if (lpMl == NULL) {
597 WARN("invalid parameter: lpMl = NULL\n");
598 return MMSYSERR_INVALPARAM;
601 if (lpMl->cbStruct != sizeof(*lpMl)) {
602 WARN("invalid parameter: lpMl->cbStruct\n");
603 return MMSYSERR_INVALPARAM;
606 if (wDevID >= numMixers) {
607 WARN("bad device ID: %04X\n", wDevID);
608 return MMSYSERR_BADDEVICEID;
611 /* FIXME: set all the variables correctly... the lines below
612 * are very wrong...
614 lpMl->dwUser = 0;
616 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
618 case MIXER_GETLINEINFOF_DESTINATION:
619 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
620 if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
622 lpMl->dwLineID = lpMl->dwDestination;
623 line = &mixer.lines[lpMl->dwDestination];
625 else ret = MIXERR_INVALLINE;
626 break;
627 case MIXER_GETLINEINFOF_COMPONENTTYPE:
628 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
629 for (i = 0; i < mixer.caps.cDestinations; i++)
631 if (mixer.lines[i].componentType == lpMl->dwComponentType)
633 lpMl->dwDestination = lpMl->dwLineID = i;
634 line = &mixer.lines[i];
635 break;
638 if (line == NULL)
640 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
641 ret = MIXERR_INVALVALUE;
643 break;
644 case MIXER_GETLINEINFOF_SOURCE:
645 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
646 break;
647 case MIXER_GETLINEINFOF_LINEID:
648 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
649 if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
651 lpMl->dwDestination = lpMl->dwLineID;
652 line = &mixer.lines[lpMl->dwLineID];
654 else ret = MIXERR_INVALLINE;
655 break;
656 case MIXER_GETLINEINFOF_TARGETTYPE:
657 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
658 switch (lpMl->Target.dwType) {
659 case MIXERLINE_TARGETTYPE_UNDEFINED:
660 case MIXERLINE_TARGETTYPE_WAVEOUT:
661 case MIXERLINE_TARGETTYPE_WAVEIN:
662 case MIXERLINE_TARGETTYPE_MIDIOUT:
663 case MIXERLINE_TARGETTYPE_MIDIIN:
664 case MIXERLINE_TARGETTYPE_AUX:
665 default:
666 FIXME("Unhandled target type (%s)\n",
667 getTargetType(lpMl->Target.dwType));
668 return MMSYSERR_INVALPARAM;
670 break;
671 default:
672 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
673 break;
676 if (line)
678 lpMl->dwComponentType = line->componentType;
679 lpMl->cChannels = line->numChannels;
680 lpMl->cControls = ControlsPerLine;
682 /* FIXME check there with CoreAudio */
683 lpMl->cConnections = 1;
684 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
686 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
687 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
689 if ( IsInput(line->direction) )
690 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
691 else
692 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
694 lpMl->Target.dwDeviceID = line->deviceID;
695 lpMl->Target.wMid = mixer.caps.wMid;
696 lpMl->Target.wPid = mixer.caps.wPid;
697 lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
699 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
700 ret = MMSYSERR_NOERROR;
702 return ret;
705 /**************************************************************************
706 * MIX_GetLineControls [internal]
708 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
710 DWORD ret = MMSYSERR_NOTENABLED;
711 int ctrl = 0;
712 TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
714 if (lpMlc == NULL) {
715 WARN("invalid parameter: lpMlc == NULL\n");
716 return MMSYSERR_INVALPARAM;
719 if (lpMlc->cbStruct < sizeof(*lpMlc)) {
720 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
721 return MMSYSERR_INVALPARAM;
724 if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
725 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
726 return MMSYSERR_INVALPARAM;
729 if (wDevID >= numMixers) {
730 WARN("bad device ID: %04X\n", wDevID);
731 return MMSYSERR_BADDEVICEID;
734 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
736 case MIXER_GETLINECONTROLSF_ALL:
737 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
738 if (lpMlc->cControls != ControlsPerLine)
740 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
741 ret = MMSYSERR_INVALPARAM;
743 else
745 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
747 int i;
748 for (i = 0; i < lpMlc->cControls; i++)
750 lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
752 ret = MMSYSERR_NOERROR;
754 else ret = MIXERR_INVALLINE;
756 break;
757 case MIXER_GETLINECONTROLSF_ONEBYID:
758 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
759 if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
761 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
762 ret = MMSYSERR_NOERROR;
764 else ret = MIXERR_INVALVALUE;
765 break;
766 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
767 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
768 if ( (lpMlc->dwLineID < 0) || (lpMlc->dwLineID >= mixer.caps.cDestinations) )
770 ret = MIXERR_INVALLINE;
771 break;
773 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
775 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
776 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
777 ret = MMSYSERR_NOERROR;
779 else
780 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
782 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
783 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
784 ret = MMSYSERR_NOERROR;
786 break;
787 default:
788 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
789 ret = MMSYSERR_INVALPARAM;
792 return ret;
795 /**************************************************************************
796 * MIX_GetControlDetails [internal]
798 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
800 DWORD ret = MMSYSERR_NOTSUPPORTED;
801 DWORD dwControlType;
803 TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
805 if (lpmcd == NULL) {
806 TRACE("invalid parameter: lpmcd == NULL\n");
807 return MMSYSERR_INVALPARAM;
810 if (wDevID >= numMixers) {
811 WARN("bad device ID: %04X\n", wDevID);
812 return MMSYSERR_BADDEVICEID;
815 if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
817 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
818 return MMSYSERR_NOTSUPPORTED;
821 if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl )
823 WARN("bad control ID: %d\n", lpmcd->dwControlID);
824 return MIXERR_INVALVALUE;
827 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID);
829 dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
830 switch (dwControlType)
832 case MIXERCONTROL_CONTROLTYPE_VOLUME:
833 FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels);
835 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
836 Float32 left, right;
838 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
839 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
840 return MMSYSERR_INVALPARAM;
843 if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) )
845 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
847 switch (lpmcd->cChannels)
849 case 1:
850 /* mono... so R = L */
851 mcdu->dwValue = left * 65535;
852 TRACE("Reading RL = %d\n", mcdu->dwValue);
853 break;
854 case 2:
855 /* stereo, left is paDetails[0] */
856 mcdu->dwValue = left * 65535;
857 TRACE("Reading L = %d\n", mcdu->dwValue);
858 mcdu++;
859 mcdu->dwValue = right * 65535;
860 TRACE("Reading R = %d\n", mcdu->dwValue);
861 break;
862 default:
863 WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
864 return MMSYSERR_INVALPARAM;
866 TRACE("=> %08x\n", mcdu->dwValue);
867 ret = MMSYSERR_NOERROR;
870 break;
871 case MIXERCONTROL_CONTROLTYPE_MUTE:
872 case MIXERCONTROL_CONTROLTYPE_ONOFF:
873 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
875 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
876 BOOL muted;
877 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
878 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails);
879 return MMSYSERR_INVALPARAM;
881 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
883 if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) )
885 mcdb->fValue = muted;
886 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
887 ret = MMSYSERR_NOERROR;
890 break;
891 case MIXERCONTROL_CONTROLTYPE_MIXER:
892 case MIXERCONTROL_CONTROLTYPE_MUX:
893 default:
894 FIXME("controlType : %s\n", getControlType(dwControlType));
895 break;
897 return ret;
900 /**************************************************************************
901 * MIX_SetControlDetails [internal]
903 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
905 DWORD ret = MMSYSERR_NOTSUPPORTED;
906 DWORD dwControlType;
908 TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
910 if (lpmcd == NULL) {
911 TRACE("invalid parameter: lpmcd == NULL\n");
912 return MMSYSERR_INVALPARAM;
915 if (wDevID >= numMixers) {
916 WARN("bad device ID: %04X\n", wDevID);
917 return MMSYSERR_BADDEVICEID;
920 if ( (fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
922 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK);
923 return MMSYSERR_NOTSUPPORTED;
926 TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd->dwControlID);
927 dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
928 switch (dwControlType)
930 case MIXERCONTROL_CONTROLTYPE_VOLUME:
931 FIXME("controlType : %s\n", getControlType(dwControlType));
933 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
934 Float32 left, right = 0;
936 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
937 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
938 return MMSYSERR_INVALPARAM;
941 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
943 switch (lpmcd->cChannels)
945 case 1:
946 /* mono... so R = L */
947 TRACE("Setting RL to %d\n", mcdu->dwValue);
948 left = (Float32) mcdu->dwValue / 65535.0;
949 break;
950 case 2:
951 /* stereo, left is paDetails[0] */
952 TRACE("Setting L to %d\n", mcdu->dwValue);
953 left = (Float32) mcdu->dwValue / 65535.0;
954 mcdu++;
955 TRACE("Setting R to %d\n", mcdu->dwValue);
956 right = (Float32) mcdu->dwValue / 65535.0;
957 break;
958 default:
959 WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
960 return MMSYSERR_INVALPARAM;
962 if ( MIX_LineSetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, left, right) )
963 ret = MMSYSERR_NOERROR;
965 break;
966 case MIXERCONTROL_CONTROLTYPE_MUTE:
967 case MIXERCONTROL_CONTROLTYPE_ONOFF:
968 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
970 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
972 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
973 WARN("invalid parameter: cbDetails\n");
974 return MMSYSERR_INVALPARAM;
976 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
977 if ( MIX_LineSetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, mcdb->fValue) )
978 ret = MMSYSERR_NOERROR;
980 break;
981 case MIXERCONTROL_CONTROLTYPE_MIXER:
982 case MIXERCONTROL_CONTROLTYPE_MUX:
983 default:
984 FIXME("controlType : %s\n", getControlType(dwControlType));
985 ret = MMSYSERR_NOTSUPPORTED;
986 break;
988 return ret;
991 /**************************************************************************
992 * mxdMessage
994 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
995 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
997 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
998 dwUser, dwParam1, dwParam2);
1000 switch (wMsg)
1002 case DRVM_INIT:
1003 case DRVM_EXIT:
1004 case DRVM_ENABLE:
1005 case DRVM_DISABLE:
1006 /* FIXME: Pretend this is supported */
1007 return 0;
1008 case MXDM_OPEN:
1009 return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
1010 case MXDM_CLOSE:
1011 return MMSYSERR_NOERROR;
1012 case MXDM_GETNUMDEVS:
1013 return MIX_GetNumDevs();
1014 case MXDM_GETDEVCAPS:
1015 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
1016 case MXDM_GETLINEINFO:
1017 return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
1018 case MXDM_GETLINECONTROLS:
1019 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
1020 case MXDM_GETCONTROLDETAILS:
1021 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1022 case MXDM_SETCONTROLDETAILS:
1023 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1024 default:
1025 WARN("unknown message %d!\n", wMsg);
1026 return MMSYSERR_NOTSUPPORTED;