coreaudio: Verify valid lineId in MIXER_GETLINECONTROLSF_ONEBYTYPE.
[wine/multimedia.git] / dlls / winecoreaudio.drv / mixer.c
blob66db8cb3742665f27196275a367c734a3cb4fffa
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 static char str[64];
96 #define MSG_TO_STR(x) case x: return #x;
97 switch (uMsg) {
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);
111 #undef MSG_TO_STR
112 sprintf(str, "UNKNOWN(%08x)", uMsg);
113 return str;
116 static const char * getControlType(DWORD dwControlType)
118 static char str[64];
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);
153 #undef TYPE_TO_STR
154 sprintf(str, "UNKNOWN(%08x)", dwControlType);
155 return str;
158 static const char * getComponentType(DWORD dwComponentType)
160 static char str[64];
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);
184 #undef TYPE_TO_STR
185 sprintf(str, "UNKNOWN(%08x)", dwComponentType);
186 return str;
189 static const char * getTargetType(DWORD dwType)
191 static char str[64];
192 #define TYPE_TO_STR(x) case x: return #x;
193 switch (dwType) {
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);
201 #undef TYPE_TO_STR
202 sprintf(str, "UNKNOWN(%08x)", dwType);
203 return str;
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);
226 if (err == noErr)
228 /* check if we can set it */
229 err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, &writable);
230 if (err == noErr)
231 return writable;
233 return FALSE;
237 * Getters
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);
247 if (err != noErr)
248 return FALSE;
250 if (channels == 2)
252 size = sizeof(Float32);
253 err = AudioDeviceGetProperty(line->deviceID, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &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 err = AudioDeviceGetProperty(line->deviceID, 0, IsInput(line->direction), kAudioDevicePropertyMute, &size, &val);
269 *muted = val;
271 return (err == noErr);
275 * Setters
277 static BOOL MIX_LineSetVolume(DWORD lineID, DWORD channels, Float32 left, Float32 right)
279 MixerLine *line = &mixer.lines[lineID];
280 UInt32 size = sizeof(Float32);
281 OSStatus err = noErr;
282 TRACE("lineID %d channels %d left %f right %f\n", lineID, channels, left, right);
284 if (channels == 2)
286 err = AudioDeviceSetProperty(line->deviceID, NULL, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
287 if (err != noErr)
288 return FALSE;
290 err = AudioDeviceSetProperty(line->deviceID, NULL, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &right);
292 else
295 FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
296 err = AudioDeviceSetProperty(line->deviceID, NULL, 0, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
298 right = left;
299 err = AudioDeviceSetProperty(line->deviceID, NULL, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
300 if (err != noErr)
301 return FALSE;
302 err = AudioDeviceSetProperty(line->deviceID, NULL, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &right);
304 return (err == noErr);
307 static BOOL MIX_LineSetMute(DWORD lineID, BOOL mute)
309 MixerLine *line = &mixer.lines[lineID];
310 UInt32 val = mute;
311 UInt32 size = sizeof(UInt32);
312 OSStatus err = noErr;
314 err = AudioDeviceSetProperty(line->deviceID, 0, 0, IsInput(line->direction), kAudioDevicePropertyMute, size, &val);
315 return (err == noErr);
318 static void MIX_FillControls(void)
320 int i;
321 int ctrl = 0;
322 MixerLine *line;
323 for (i = 0; i < mixer.caps.cDestinations; i++)
325 line = &mixer.lines[i];
326 mixer.mixerCtrls[ctrl].dwLineID = i;
327 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
328 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
329 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
330 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
331 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
332 mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
333 ctrl++;
335 mixer.mixerCtrls[ctrl].dwLineID = i;
336 if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
337 mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
339 mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
340 mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
341 mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
342 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
343 mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
344 ctrl++;
346 assert(ctrl == mixer.numCtrl);
349 /**************************************************************************
350 * CoreAudio_MixerInit
352 LONG CoreAudio_MixerInit(void)
354 OSStatus status;
355 UInt32 propertySize;
356 AudioDeviceID *deviceArray = NULL;
357 char name[MAXPNAMELEN];
358 int i;
359 int numLines;
361 AudioStreamBasicDescription streamDescription;
363 /* Find number of lines */
364 status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
365 if (status)
367 ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
368 (char) (status >> 16),
369 (char) (status >> 8),
370 (char) status);
371 return 0;
374 numLines = propertySize / sizeof(AudioDeviceID);
376 mixer.mixerCtrls = NULL;
377 mixer.lines = NULL;
378 mixer.numCtrl = 0;
380 mixer.caps.cDestinations = numLines;
381 mixer.caps.wMid = 0xAA;
382 mixer.caps.wPid = 0x55;
383 mixer.caps.vDriverVersion = 0x0100;
385 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
387 mixer.caps.fdwSupport = 0; /* No bits defined yet */
389 mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
390 if (!mixer.lines)
391 goto error;
393 deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
395 propertySize = sizeof(AudioDeviceID) * numLines;
396 status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray);
397 if (status)
399 ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
400 (char) (status >> 16),
401 (char) (status >> 8),
402 (char) status);
403 goto error;
406 for (i = 0; i < numLines; i++)
408 Boolean write;
409 MixerLine *line = &mixer.lines[i];
411 line->deviceID = deviceArray[i];
413 propertySize = MAXPNAMELEN;
414 status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
415 if (status) {
416 ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
417 (char) (status >> 16),
418 (char) (status >> 8),
419 (char) status);
420 goto error;
423 line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1);
424 if (!line->name)
425 goto error;
427 memcpy(line->name, name, strlen(name));
429 line->componentType = DeviceComponentType(line->name);
431 /* check for directions */
432 /* Output ? */
433 propertySize = sizeof(UInt32);
434 status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write );
435 if (status) {
436 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %c%c%c%c\n", (char) (status >> 24),
437 (char) (status >> 16),
438 (char) (status >> 8),
439 (char) status);
440 goto error;
443 if ( (propertySize / sizeof(AudioStreamID)) != 0)
445 line->direction |= OutputDevice;
447 /* Check the number of channel for the stream */
448 propertySize = sizeof(streamDescription);
449 status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
450 if (status != noErr) {
451 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
452 (char) (status >> 16),
453 (char) (status >> 8),
454 (char) status);
455 goto error;
457 line->numChannels = streamDescription.mChannelsPerFrame;
459 else
461 /* Input ? */
462 propertySize = sizeof(UInt32);
463 status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write );
464 if (status) {
465 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %c%c%c%c\n", (char) (status >> 24),
466 (char) (status >> 16),
467 (char) (status >> 8),
468 (char) status);
469 goto error;
471 if ( (propertySize / sizeof(AudioStreamID)) != 0)
473 line->direction |= InputDevice;
475 /* Check the number of channel for the stream */
476 propertySize = sizeof(streamDescription);
477 status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
478 if (status != noErr) {
479 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
480 (char) (status >> 16),
481 (char) (status >> 8),
482 (char) status);
483 goto error;
485 line->numChannels = streamDescription.mChannelsPerFrame;
489 mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
491 mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
492 if (!mixer.mixerCtrls)
493 goto error;
495 MIX_FillControls();
497 HeapFree(GetProcessHeap(), 0, deviceArray);
498 return 1;
500 error:
501 if (mixer.lines)
503 int i;
504 for (i = 0; i < mixer.caps.cDestinations; i++)
506 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
508 HeapFree(GetProcessHeap(), 0, mixer.lines);
510 HeapFree(GetProcessHeap(), 0, deviceArray);
511 if (mixer.mixerCtrls)
512 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
513 return 0;
516 /**************************************************************************
517 * CoreAudio_MixerRelease
519 void CoreAudio_MixerRelease(void)
521 TRACE("()\n");
523 if (mixer.lines)
525 int i;
526 for (i = 0; i < mixer.caps.cDestinations; i++)
528 HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
530 HeapFree(GetProcessHeap(), 0, mixer.lines);
532 if (mixer.mixerCtrls)
533 HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
536 /**************************************************************************
537 * MIX_Open [internal]
539 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
541 TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
542 if (lpMod == NULL) {
543 WARN("invalid parameter: lpMod == NULL\n");
544 return MMSYSERR_INVALPARAM;
547 if (wDevID >= numMixers) {
548 WARN("bad device ID: %04X\n", wDevID);
549 return MMSYSERR_BADDEVICEID;
551 return MMSYSERR_NOERROR;
554 /**************************************************************************
555 * MIX_GetNumDevs [internal]
557 static DWORD MIX_GetNumDevs(void)
559 TRACE("()\n");
560 return numMixers;
563 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
565 TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
567 if (lpCaps == NULL) {
568 WARN("Invalid Parameter\n");
569 return MMSYSERR_INVALPARAM;
572 if (wDevID >= numMixers) {
573 WARN("bad device ID : %d\n", wDevID);
574 return MMSYSERR_BADDEVICEID;
576 memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
577 return MMSYSERR_NOERROR;
580 /**************************************************************************
581 * MIX_GetLineInfo [internal]
583 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
585 int i;
586 DWORD ret = MMSYSERR_ERROR;
587 MixerLine *line = NULL;
589 TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
591 if (lpMl == NULL) {
592 WARN("invalid parameter: lpMl = NULL\n");
593 return MMSYSERR_INVALPARAM;
596 if (lpMl->cbStruct != sizeof(*lpMl)) {
597 WARN("invalid parameter: lpMl->cbStruct\n");
598 return MMSYSERR_INVALPARAM;
601 if (wDevID >= numMixers) {
602 WARN("bad device ID: %04X\n", wDevID);
603 return MMSYSERR_BADDEVICEID;
606 /* FIXME: set all the variables correctly... the lines below
607 * are very wrong...
609 lpMl->dwUser = 0;
611 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
613 case MIXER_GETLINEINFOF_DESTINATION:
614 TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
615 if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
617 lpMl->dwLineID = lpMl->dwDestination;
618 line = &mixer.lines[lpMl->dwDestination];
620 else ret = MIXERR_INVALLINE;
621 break;
622 case MIXER_GETLINEINFOF_COMPONENTTYPE:
623 TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
624 for (i = 0; i < mixer.caps.cDestinations; i++)
626 if (mixer.lines[i].componentType == lpMl->dwComponentType)
628 lpMl->dwDestination = lpMl->dwLineID = i;
629 line = &mixer.lines[i];
630 break;
633 if (line == NULL)
635 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
636 ret = MIXERR_INVALVALUE;
638 break;
639 case MIXER_GETLINEINFOF_SOURCE:
640 FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
641 break;
642 case MIXER_GETLINEINFOF_LINEID:
643 TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
644 if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
646 lpMl->dwDestination = lpMl->dwLineID;
647 line = &mixer.lines[lpMl->dwLineID];
649 else ret = MIXERR_INVALLINE;
650 break;
651 case MIXER_GETLINEINFOF_TARGETTYPE:
652 FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
653 switch (lpMl->Target.dwType) {
654 case MIXERLINE_TARGETTYPE_UNDEFINED:
655 case MIXERLINE_TARGETTYPE_WAVEOUT:
656 case MIXERLINE_TARGETTYPE_WAVEIN:
657 case MIXERLINE_TARGETTYPE_MIDIOUT:
658 case MIXERLINE_TARGETTYPE_MIDIIN:
659 case MIXERLINE_TARGETTYPE_AUX:
660 default:
661 FIXME("Unhandled target type (%s)\n",
662 getTargetType(lpMl->Target.dwType));
663 return MMSYSERR_INVALPARAM;
665 break;
666 default:
667 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
668 break;
671 if (line)
673 lpMl->dwComponentType = line->componentType;
674 lpMl->cChannels = line->numChannels;
675 lpMl->cControls = ControlsPerLine;
677 /* FIXME check there with CoreAudio */
678 lpMl->cConnections = 1;
679 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
681 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
682 MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
684 if ( IsInput(line->direction) )
685 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
686 else
687 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
689 lpMl->Target.dwDeviceID = line->deviceID;
690 lpMl->Target.wMid = mixer.caps.wMid;
691 lpMl->Target.wPid = mixer.caps.wPid;
692 lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
694 MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
695 ret = MMSYSERR_NOERROR;
697 return ret;
700 /**************************************************************************
701 * MIX_GetLineControls [internal]
703 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
705 DWORD ret = MMSYSERR_NOTENABLED;
706 int ctrl = 0;
707 TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
709 if (lpMlc == NULL) {
710 WARN("invalid parameter: lpMlc == NULL\n");
711 return MMSYSERR_INVALPARAM;
714 if (lpMlc->cbStruct < sizeof(*lpMlc)) {
715 WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
716 return MMSYSERR_INVALPARAM;
719 if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
720 WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
721 return MMSYSERR_INVALPARAM;
724 if (wDevID >= numMixers) {
725 WARN("bad device ID: %04X\n", wDevID);
726 return MMSYSERR_BADDEVICEID;
729 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
731 case MIXER_GETLINECONTROLSF_ALL:
732 FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
733 if (lpMlc->cControls != ControlsPerLine)
735 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
736 ret = MMSYSERR_INVALPARAM;
738 else
740 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
742 int i;
743 for (i = 0; i < lpMlc->cControls; i++)
745 lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
747 ret = MMSYSERR_NOERROR;
749 else ret = MIXERR_INVALLINE;
751 break;
752 case MIXER_GETLINECONTROLSF_ONEBYID:
753 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
754 if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
756 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
757 ret = MMSYSERR_NOERROR;
759 else ret = MIXERR_INVALVALUE;
760 break;
761 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
762 TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
763 if ( (lpMlc->dwLineID < 0) || (lpMlc->dwLineID >= mixer.caps.cDestinations) )
765 ret = MIXERR_INVALLINE;
766 break;
768 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
770 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
771 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
772 ret = MMSYSERR_NOERROR;
774 else
775 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
777 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
778 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
779 ret = MMSYSERR_NOERROR;
781 break;
782 default:
783 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
784 ret = MMSYSERR_INVALPARAM;
787 return ret;
790 /**************************************************************************
791 * MIX_GetControlDetails [internal]
793 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
795 DWORD ret = MMSYSERR_NOTSUPPORTED;
796 DWORD dwControlType;
798 TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
800 if (lpmcd == NULL) {
801 TRACE("invalid parameter: lpmcd == NULL\n");
802 return MMSYSERR_INVALPARAM;
805 if (wDevID >= numMixers) {
806 WARN("bad device ID: %04X\n", wDevID);
807 return MMSYSERR_BADDEVICEID;
810 if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
812 WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
813 return MMSYSERR_NOTSUPPORTED;
816 if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl )
818 WARN("bad control ID: %d\n", lpmcd->dwControlID);
819 return MIXERR_INVALVALUE;
822 TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID);
824 dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
825 switch (dwControlType)
827 case MIXERCONTROL_CONTROLTYPE_VOLUME:
828 FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels);
830 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
831 Float32 left, right;
833 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
834 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
835 return MMSYSERR_INVALPARAM;
838 if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) )
840 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
842 switch (lpmcd->cChannels)
844 case 1:
845 /* mono... so R = L */
846 mcdu->dwValue = left * 65535;
847 TRACE("Reading RL = %d\n", mcdu->dwValue);
848 break;
849 case 2:
850 /* stereo, left is paDetails[0] */
851 mcdu->dwValue = left * 65535;
852 TRACE("Reading L = %d\n", mcdu->dwValue);
853 mcdu++;
854 mcdu->dwValue = right * 65535;
855 TRACE("Reading R = %d\n", mcdu->dwValue);
856 break;
857 default:
858 WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
859 return MMSYSERR_INVALPARAM;
861 TRACE("=> %08x\n", mcdu->dwValue);
862 ret = MMSYSERR_NOERROR;
865 break;
866 case MIXERCONTROL_CONTROLTYPE_MUTE:
867 case MIXERCONTROL_CONTROLTYPE_ONOFF:
868 FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
870 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
871 BOOL muted;
872 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
873 WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails);
874 return MMSYSERR_INVALPARAM;
876 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
878 if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) )
880 mcdb->fValue = muted;
881 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
882 ret = MMSYSERR_NOERROR;
885 break;
886 case MIXERCONTROL_CONTROLTYPE_MIXER:
887 case MIXERCONTROL_CONTROLTYPE_MUX:
888 default:
889 FIXME("controlType : %s\n", getControlType(dwControlType));
890 break;
892 return ret;
895 /**************************************************************************
896 * MIX_SetControlDetails [internal]
898 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
900 DWORD ret = MMSYSERR_NOTSUPPORTED;
901 DWORD dwControlType;
903 TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
905 if (lpmcd == NULL) {
906 TRACE("invalid parameter: lpmcd == NULL\n");
907 return MMSYSERR_INVALPARAM;
910 if (wDevID >= numMixers) {
911 WARN("bad device ID: %04X\n", wDevID);
912 return MMSYSERR_BADDEVICEID;
915 if ( (fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
917 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK);
918 return MMSYSERR_NOTSUPPORTED;
921 TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd->dwControlID);
922 dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
923 switch (dwControlType)
925 case MIXERCONTROL_CONTROLTYPE_VOLUME:
926 FIXME("controlType : %s\n", getControlType(dwControlType));
928 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
929 Float32 left, right = 0;
931 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
932 WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
933 return MMSYSERR_INVALPARAM;
936 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
938 switch (lpmcd->cChannels)
940 case 1:
941 /* mono... so R = L */
942 TRACE("Setting RL to %d\n", mcdu->dwValue);
943 left = (Float32) mcdu->dwValue / 65535.0;
944 break;
945 case 2:
946 /* stereo, left is paDetails[0] */
947 TRACE("Setting L to %d\n", mcdu->dwValue);
948 left = (Float32) mcdu->dwValue / 65535.0;
949 mcdu++;
950 TRACE("Setting R to %d\n", mcdu->dwValue);
951 right = (Float32) mcdu->dwValue / 65535.0;
952 break;
953 default:
954 WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
955 return MMSYSERR_INVALPARAM;
957 if ( MIX_LineSetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, left, right) )
958 ret = MMSYSERR_NOERROR;
960 break;
961 case MIXERCONTROL_CONTROLTYPE_MUTE:
962 case MIXERCONTROL_CONTROLTYPE_ONOFF:
963 TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
965 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
967 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
968 WARN("invalid parameter: cbDetails\n");
969 return MMSYSERR_INVALPARAM;
971 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
972 if ( MIX_LineSetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, mcdb->fValue) )
973 ret = MMSYSERR_NOERROR;
975 break;
976 case MIXERCONTROL_CONTROLTYPE_MIXER:
977 case MIXERCONTROL_CONTROLTYPE_MUX:
978 default:
979 FIXME("controlType : %s\n", getControlType(dwControlType));
980 ret = MMSYSERR_NOTSUPPORTED;
981 break;
983 return ret;
986 /**************************************************************************
987 * mxdMessage
989 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
990 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
992 TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
993 dwUser, dwParam1, dwParam2);
995 switch (wMsg)
997 case DRVM_INIT:
998 case DRVM_EXIT:
999 case DRVM_ENABLE:
1000 case DRVM_DISABLE:
1001 /* FIXME: Pretend this is supported */
1002 return 0;
1003 case MXDM_OPEN:
1004 return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
1005 case MXDM_CLOSE:
1006 return MMSYSERR_NOERROR;
1007 case MXDM_GETNUMDEVS:
1008 return MIX_GetNumDevs();
1009 case MXDM_GETDEVCAPS:
1010 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
1011 case MXDM_GETLINEINFO:
1012 return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
1013 case MXDM_GETLINECONTROLS:
1014 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
1015 case MXDM_GETCONTROLDETAILS:
1016 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1017 case MXDM_SETCONTROLDETAILS:
1018 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1019 default:
1020 WARN("unknown message %d!\n", wMsg);
1021 return MMSYSERR_NOTSUPPORTED;
1025 #else
1027 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1028 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1030 TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1031 return MMSYSERR_NOTENABLED;
1033 #endif /* HAVE_COREAUDIO_COREAUDIO_H */