winecoreaudio: Initial implementation of MIX_GetControlDetails.
[wine] / dlls / winecoreaudio.drv / mixer.c
1 /*
2  * Sample MIXER Wine Driver for Mac OS X (based on OSS mixer)
3  *
4  * Copyright    1997 Marcus Meissner
5  *              1999,2001 Eric Pouech
6  *              2006,2007 Emmanuel Maillard
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
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
33
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"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(mixer);
45
46 #if defined(HAVE_COREAUDIO_COREAUDIO_H)
47 #include <CoreAudio/CoreAudio.h>
48 #include <CoreFoundation/CoreFoundation.h>
49
50 #define WINE_MIXER_NAME "CoreAudio Mixer"
51
52 #define InputDevice (1 << 0)
53 #define OutputDevice (1 << 1)
54
55 #define IsInput(dir) ((dir) & InputDevice)
56 #define IsOutput(dir) ((dir) & OutputDevice)
57
58 #define ControlsPerLine 2 /* number of control per line : volume & (mute | onoff) */
59
60 #define IDControlVolume 0
61 #define IDControlMute 1
62
63 typedef struct tagMixerLine
64 {
65     char *name;
66     int direction;
67     int numChannels;
68     int componentType;
69     AudioDeviceID deviceID;
70 } MixerLine;
71
72 typedef struct tagMixerCtrl
73 {
74     DWORD dwLineID;
75     MIXERCONTROLW ctrl;
76 } MixerCtrl;
77
78 typedef struct tagCoreAudio_Mixer
79 {
80     MIXERCAPSW caps;
81
82     MixerCtrl *mixerCtrls;
83     MixerLine *lines;
84     DWORD numCtrl;
85 } CoreAudio_Mixer;
86
87 static CoreAudio_Mixer mixer;
88 static int numMixers = 1;
89
90 /**************************************************************************
91 */
92
93 static const char * getMessage(UINT uMsg)
94 {
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);
110     }
111 #undef MSG_TO_STR
112         sprintf(str, "UNKNOWN(%08x)", uMsg);
113     return str;
114 }
115
116 static const char * getControlType(DWORD dwControlType)
117 {
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);
152     }
153 #undef TYPE_TO_STR
154         sprintf(str, "UNKNOWN(%08x)", dwControlType);
155     return str;
156 }
157
158 static const char * getComponentType(DWORD dwComponentType)
159 {
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);
183     }
184 #undef TYPE_TO_STR
185         sprintf(str, "UNKNOWN(%08x)", dwComponentType);
186     return str;
187 }
188
189 static const char * getTargetType(DWORD dwType)
190 {
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);
200     }
201 #undef TYPE_TO_STR
202         sprintf(str, "UNKNOWN(%08x)", dwType);
203     return str;
204 }
205
206 /* FIXME is there a better way ? */
207 static DWORD DeviceComponentType(char *name)
208 {
209     if (strcmp(name, "Built-in Microphone") == 0)
210         return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
211
212     if (strcmp(name, "Built-in Line Input") == 0)
213         return MIXERLINE_COMPONENTTYPE_SRC_LINE;
214
215     if (strcmp(name, "Built-in Output") == 0)
216         return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
217
218     return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
219 }
220
221 static BOOL DeviceHasMute(AudioDeviceID deviceID, Boolean isInput)
222 {
223     Boolean writable = false;
224     OSStatus err = noErr;
225     err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, NULL);
226     if (err == noErr)
227     {
228         /* check if we can set it */
229         err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, &writable);
230         if (err == noErr)
231             return writable;
232     }
233     return FALSE;
234 }
235
236 /*
237  * Getters
238  */
239 static BOOL MIX_LineGetVolume(DWORD lineID, DWORD channels, Float32 *left, Float32 *right)
240 {
241     MixerLine *line = &mixer.lines[lineID];
242     UInt32 size = sizeof(Float32);
243     OSStatus err = noErr;
244     *left = *right = 0.0;
245
246     err = AudioDeviceGetProperty(line->deviceID, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, left);
247     if (err != noErr)
248         return FALSE;
249
250     if (channels == 2)
251     {
252         size = sizeof(Float32);
253         err = AudioDeviceGetProperty(line->deviceID, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, right);
254         if (err != noErr)
255             return FALSE;
256     }
257
258     TRACE("lineID %d channels %d return left %f right %f\n", lineID, channels, *left, *right);
259     return (err == noErr);
260 }
261
262 static BOOL MIX_LineGetMute(DWORD lineID, BOOL *muted)
263 {
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;
270
271     return (err == noErr);
272 }
273
274 static void MIX_FillControls(void)
275 {
276     int i;
277     int ctrl = 0;
278     MixerLine *line;
279     for (i = 0; i < mixer.caps.cDestinations; i++)
280     {
281         line = &mixer.lines[i];
282         mixer.mixerCtrls[ctrl].dwLineID = i;
283         mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
284         mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
285         mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
286         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
287         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
288         mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
289         ctrl++;
290
291         mixer.mixerCtrls[ctrl].dwLineID = i;
292         if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
293             mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
294
295         mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
296         mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
297         mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
298         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
299         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
300         ctrl++;
301     }
302     assert(ctrl == mixer.numCtrl);
303 }
304
305 /**************************************************************************
306 *                               CoreAudio_MixerInit
307 */
308 LONG CoreAudio_MixerInit(void)
309 {
310     OSStatus status;
311     UInt32 propertySize;
312     AudioDeviceID *deviceArray = NULL;
313     char name[MAXPNAMELEN];
314     int i;
315     int numLines;
316
317     AudioStreamBasicDescription streamDescription;
318
319     /* Find number of lines */
320     status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
321     if (status)
322     {
323         ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
324             (char) (status >> 16),
325             (char) (status >> 8),
326             (char) status);
327         return 0;
328     }
329
330     numLines = propertySize / sizeof(AudioDeviceID);
331
332     mixer.mixerCtrls = NULL;
333     mixer.lines = NULL;
334     mixer.numCtrl = 0;
335
336     mixer.caps.cDestinations = numLines;
337     mixer.caps.wMid = 0xAA;
338     mixer.caps.wPid = 0x55;
339     mixer.caps.vDriverVersion = 0x0100;
340
341     MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
342
343     mixer.caps.fdwSupport = 0; /* No bits defined yet */
344
345     mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
346     if (!mixer.lines)
347         goto error;
348
349     deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
350
351     propertySize = sizeof(AudioDeviceID) * numLines;
352     status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray);
353     if (status)
354     {
355         ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
356             (char) (status >> 16),
357             (char) (status >> 8),
358             (char) status);
359         goto error;
360     }
361
362     for (i = 0; i < numLines; i++)
363     {
364         Boolean write;
365         MixerLine *line = &mixer.lines[i];
366
367         line->deviceID = deviceArray[i];
368
369         propertySize = MAXPNAMELEN;
370         status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
371         if (status) {
372             ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
373                 (char) (status >> 16),
374                 (char) (status >> 8),
375                 (char) status);
376             goto error;
377         }
378
379         line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1);
380         if (!line->name)
381             goto error;
382
383         memcpy(line->name, name, strlen(name));
384
385         line->componentType = DeviceComponentType(line->name);
386
387         /* check for directions */
388         /* Output ? */
389         propertySize = sizeof(UInt32);
390         status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write );
391         if (status) {
392             ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %c%c%c%c\n", (char) (status >> 24),
393                 (char) (status >> 16),
394                 (char) (status >> 8),
395                 (char) status);
396             goto error;
397         }
398
399         if ( (propertySize / sizeof(AudioStreamID)) != 0)
400         {
401             line->direction |= OutputDevice;
402
403             /* Check the number of channel for the stream */
404             propertySize = sizeof(streamDescription);
405             status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
406             if (status != noErr) {
407                 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
408                     (char) (status >> 16),
409                     (char) (status >> 8),
410                     (char) status);
411                 goto error;
412             }
413             line->numChannels = streamDescription.mChannelsPerFrame;
414         }
415         else
416         {
417             /* Input ? */
418             propertySize = sizeof(UInt32);
419             status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write );
420             if (status) {
421                 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %c%c%c%c\n", (char) (status >> 24),
422                     (char) (status >> 16),
423                     (char) (status >> 8),
424                     (char) status);
425                 goto error;
426             }
427             if ( (propertySize / sizeof(AudioStreamID)) != 0)
428             {
429                 line->direction |= InputDevice;
430
431                 /* Check the number of channel for the stream */
432                 propertySize = sizeof(streamDescription);
433                 status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
434                 if (status != noErr) {
435                     ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
436                         (char) (status >> 16),
437                         (char) (status >> 8),
438                         (char) status);
439                     goto error;
440                 }
441                 line->numChannels = streamDescription.mChannelsPerFrame;
442             }
443         }
444
445         mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
446     }
447     mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
448     if (!mixer.mixerCtrls)
449         goto error;
450
451     MIX_FillControls();
452
453     HeapFree(GetProcessHeap(), 0, deviceArray);
454     return 1;
455
456 error:
457     if (mixer.lines)
458     {
459         int i;
460         for (i = 0; i < mixer.caps.cDestinations; i++)
461         {
462             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
463         }
464         HeapFree(GetProcessHeap(), 0, mixer.lines);
465     }
466     HeapFree(GetProcessHeap(), 0, deviceArray);
467     if (mixer.mixerCtrls)
468         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
469     return 0;
470 }
471
472 /**************************************************************************
473 *                               CoreAudio_MixerRelease
474 */
475 void CoreAudio_MixerRelease(void)
476 {
477     TRACE("()\n");
478
479     if (mixer.lines)
480     {
481         int i;
482         for (i = 0; i < mixer.caps.cDestinations; i++)
483         {
484             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
485         }
486         HeapFree(GetProcessHeap(), 0, mixer.lines);
487     }
488     if (mixer.mixerCtrls)
489         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
490 }
491
492 /**************************************************************************
493 *                               MIX_Open                        [internal]
494 */
495 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
496 {
497     TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
498     if (lpMod == NULL) {
499         WARN("invalid parameter: lpMod == NULL\n");
500         return MMSYSERR_INVALPARAM;
501     }
502
503     if (wDevID >= numMixers) {
504         WARN("bad device ID: %04X\n", wDevID);
505         return MMSYSERR_BADDEVICEID;
506     }
507     return MMSYSERR_NOERROR;
508 }
509
510 /**************************************************************************
511 *                               MIX_GetNumDevs                  [internal]
512 */
513 static DWORD MIX_GetNumDevs(void)
514 {
515     TRACE("()\n");
516     return numMixers;
517 }
518
519 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
520 {
521     TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
522
523     if (lpCaps == NULL) {
524         WARN("Invalid Parameter\n");
525         return MMSYSERR_INVALPARAM;
526     }
527
528     if (wDevID >= numMixers) {
529         WARN("bad device ID : %d\n", wDevID);
530         return MMSYSERR_BADDEVICEID;
531     }
532     memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
533     return MMSYSERR_NOERROR;
534 }
535
536 /**************************************************************************
537 *                               MIX_GetLineInfo                 [internal]
538 */
539 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
540 {
541     int i;
542     DWORD ret = MMSYSERR_ERROR;
543     MixerLine *line = NULL;
544
545     TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
546
547     if (lpMl == NULL) {
548         WARN("invalid parameter: lpMl = NULL\n");
549         return MMSYSERR_INVALPARAM;
550     }
551
552     if (lpMl->cbStruct != sizeof(*lpMl)) {
553         WARN("invalid parameter: lpMl->cbStruct\n");
554         return MMSYSERR_INVALPARAM;
555     }
556
557     if (wDevID >= numMixers) {
558         WARN("bad device ID: %04X\n", wDevID);
559         return MMSYSERR_BADDEVICEID;
560     }
561
562     /* FIXME: set all the variables correctly... the lines below
563         * are very wrong...
564         */
565     lpMl->dwUser = 0;
566
567     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
568     {
569         case MIXER_GETLINEINFOF_DESTINATION:
570             TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
571             if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
572             {
573                 lpMl->dwLineID = lpMl->dwDestination;
574                 line = &mixer.lines[lpMl->dwDestination];
575             }
576             else ret = MIXERR_INVALLINE;
577             break;
578         case MIXER_GETLINEINFOF_COMPONENTTYPE:
579             TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
580             for (i = 0; i < mixer.caps.cDestinations; i++)
581             {
582                 if (mixer.lines[i].componentType == lpMl->dwComponentType)
583                 {
584                     lpMl->dwDestination = lpMl->dwLineID = i;
585                     line = &mixer.lines[i];
586                     break;
587                 }
588             }
589             if (line == NULL)
590             {
591                 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
592                 ret = MIXERR_INVALVALUE;
593             }
594             break;
595         case MIXER_GETLINEINFOF_SOURCE:
596             FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
597             break;
598         case MIXER_GETLINEINFOF_LINEID:
599             TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
600             if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
601             {
602                 lpMl->dwDestination = lpMl->dwLineID;
603                 line = &mixer.lines[lpMl->dwLineID];
604             }
605             else ret = MIXERR_INVALLINE;
606             break;
607         case MIXER_GETLINEINFOF_TARGETTYPE:
608             FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
609             switch (lpMl->Target.dwType) {
610                 case MIXERLINE_TARGETTYPE_UNDEFINED:
611                 case MIXERLINE_TARGETTYPE_WAVEOUT:
612                 case MIXERLINE_TARGETTYPE_WAVEIN:
613                 case MIXERLINE_TARGETTYPE_MIDIOUT:
614                 case MIXERLINE_TARGETTYPE_MIDIIN:
615                 case MIXERLINE_TARGETTYPE_AUX:
616                 default:
617                     FIXME("Unhandled target type (%s)\n",
618                           getTargetType(lpMl->Target.dwType));
619                     return MMSYSERR_INVALPARAM;
620             }
621                 break;
622         default:
623             WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
624             break;
625     }
626
627     if (line)
628     {
629         lpMl->dwComponentType = line->componentType;
630         lpMl->cChannels = line->numChannels;
631         lpMl->cControls = ControlsPerLine;
632
633         /* FIXME check there with CoreAudio */
634         lpMl->cConnections = 1;
635         lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
636
637         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
638         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
639
640         if ( IsInput(line->direction) )
641             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
642         else
643             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
644
645         lpMl->Target.dwDeviceID = line->deviceID;
646         lpMl->Target.wMid = mixer.caps.wMid;
647         lpMl->Target.wPid = mixer.caps.wPid;
648         lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
649
650         MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
651         ret = MMSYSERR_NOERROR;
652     }
653     return ret;
654 }
655
656 /**************************************************************************
657 *                               MIX_GetLineControls             [internal]
658 */
659 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
660 {
661     DWORD ret = MMSYSERR_NOTENABLED;
662     int ctrl = 0;
663     TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
664
665     if (lpMlc == NULL) {
666         WARN("invalid parameter: lpMlc == NULL\n");
667         return MMSYSERR_INVALPARAM;
668     }
669
670     if (lpMlc->cbStruct < sizeof(*lpMlc)) {
671         WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
672         return MMSYSERR_INVALPARAM;
673     }
674
675     if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
676         WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
677         return MMSYSERR_INVALPARAM;
678     }
679
680     if (wDevID >= numMixers) {
681         WARN("bad device ID: %04X\n", wDevID);
682         return MMSYSERR_BADDEVICEID;
683     }
684
685     switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
686     {
687         case MIXER_GETLINECONTROLSF_ALL:
688             FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
689             if (lpMlc->cControls != ControlsPerLine)
690             {
691                 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
692                 ret = MMSYSERR_INVALPARAM;
693             }
694             else
695             {
696                 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
697                 {
698                     int i;
699                     for (i = 0; i < lpMlc->cControls; i++)
700                     {
701                         lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
702                     }
703                     ret = MMSYSERR_NOERROR;
704                 }
705                 else ret = MIXERR_INVALLINE;
706             }
707             break;
708         case MIXER_GETLINECONTROLSF_ONEBYID:
709             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
710             if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
711             {
712                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
713                 ret = MMSYSERR_NOERROR;
714             }
715             else ret = MIXERR_INVALVALUE;
716             break;
717         case MIXER_GETLINECONTROLSF_ONEBYTYPE:
718             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
719             if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
720             {
721                 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
722                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
723                 ret = MMSYSERR_NOERROR;
724             }
725             else
726                 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
727                 {
728                     ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
729                     lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
730                     ret = MMSYSERR_NOERROR;
731                 }
732                 break;
733         default:
734             ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
735             ret = MMSYSERR_INVALPARAM;
736     }
737
738     return ret;
739 }
740
741 /**************************************************************************
742  *                              MIX_GetControlDetails           [internal]
743  */
744 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
745 {
746     DWORD ret = MMSYSERR_NOTSUPPORTED;
747     DWORD dwControlType;
748
749     TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
750
751     if (lpmcd == NULL) {
752         TRACE("invalid parameter: lpmcd == NULL\n");
753         return MMSYSERR_INVALPARAM;
754     }
755
756     if (wDevID >= numMixers) {
757         WARN("bad device ID: %04X\n", wDevID);
758         return MMSYSERR_BADDEVICEID;
759     }
760
761     if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
762     {
763         WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
764         return MMSYSERR_NOTSUPPORTED;
765     }
766
767     if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl )
768     {
769         WARN("bad control ID: %d\n", lpmcd->dwControlID);
770         return MIXERR_INVALVALUE;
771     }
772
773     TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID);
774
775     dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
776     switch (dwControlType)
777     {
778         case MIXERCONTROL_CONTROLTYPE_VOLUME:
779             FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels);
780             {
781                 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
782                 Float32 left, right;
783
784                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
785                     WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
786                     return MMSYSERR_INVALPARAM;
787                 }
788
789                 if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) )
790                 {
791                     mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
792
793                     switch (lpmcd->cChannels)
794                     {
795                         case 1:
796                             /* mono... so R = L */
797                             mcdu->dwValue = left * 65535;
798                             TRACE("Reading RL = %d\n", mcdu->dwValue);
799                             break;
800                         case 2:
801                             /* stereo, left is paDetails[0] */
802                             mcdu->dwValue = left * 65535;
803                             TRACE("Reading L = %d\n", mcdu->dwValue);
804                             mcdu++;
805                             mcdu->dwValue = right * 65535;
806                             TRACE("Reading R = %d\n", mcdu->dwValue);
807                             break;
808                         default:
809                             WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
810                             return MMSYSERR_INVALPARAM;
811                     }
812                     TRACE("=> %08x\n", mcdu->dwValue);
813                     ret = MMSYSERR_NOERROR;
814                 }
815             }
816             break;
817         case MIXERCONTROL_CONTROLTYPE_MUTE:
818         case MIXERCONTROL_CONTROLTYPE_ONOFF:
819             FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
820             {
821                 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
822                 BOOL muted;
823                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
824                     WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails);
825                     return MMSYSERR_INVALPARAM;
826                 }
827                 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
828
829                 if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) )
830                 {
831                     mcdb->fValue = muted;
832                     TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
833                     ret = MMSYSERR_NOERROR;
834                 }
835             }
836             break;
837         case MIXERCONTROL_CONTROLTYPE_MIXER:
838         case MIXERCONTROL_CONTROLTYPE_MUX:
839         default:
840             FIXME("controlType : %s\n", getControlType(dwControlType));
841             break;
842     }
843     return ret;
844 }
845
846 /**************************************************************************
847 *                               mxdMessage
848 */
849 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
850                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
851 {
852     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
853           dwUser, dwParam1, dwParam2);
854
855     switch (wMsg)
856     {
857         case DRVM_INIT:
858         case DRVM_EXIT:
859         case DRVM_ENABLE:
860         case DRVM_DISABLE:
861             /* FIXME: Pretend this is supported */
862             return 0;
863         case MXDM_OPEN:
864             return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
865         case MXDM_CLOSE:
866             return MMSYSERR_NOERROR;
867         case MXDM_GETNUMDEVS:
868             return MIX_GetNumDevs();
869         case MXDM_GETDEVCAPS:
870             return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
871         case MXDM_GETLINEINFO:
872             return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
873         case MXDM_GETLINECONTROLS:
874             return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
875         case MXDM_GETCONTROLDETAILS:
876             return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
877         case MXDM_SETCONTROLDETAILS:
878         default:
879             WARN("unknown message %d!\n", wMsg);
880             return MMSYSERR_NOTSUPPORTED;
881     }
882 }
883
884 #else
885
886 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
887                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
888 {
889     TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
890     return MMSYSERR_NOTENABLED;
891 }
892 #endif /* HAVE_COREAUDIO_COREAUDIO_H */