winecoreaudio.drv: Remove superfluous casts of zero.
[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 #define MSG_TO_STR(x) case x: return #x;
96     switch (uMsg) {
97         MSG_TO_STR(DRVM_INIT);
98         MSG_TO_STR(DRVM_EXIT);
99         MSG_TO_STR(DRVM_ENABLE);
100         MSG_TO_STR(DRVM_DISABLE);
101         MSG_TO_STR(MXDM_GETDEVCAPS);
102         MSG_TO_STR(MXDM_GETLINEINFO);
103         MSG_TO_STR(MXDM_GETNUMDEVS);
104         MSG_TO_STR(MXDM_OPEN);
105         MSG_TO_STR(MXDM_CLOSE);
106         MSG_TO_STR(MXDM_GETLINECONTROLS);
107         MSG_TO_STR(MXDM_GETCONTROLDETAILS);
108         MSG_TO_STR(MXDM_SETCONTROLDETAILS);
109     }
110 #undef MSG_TO_STR
111     return wine_dbg_sprintf("UNKNOWN(%08x)", uMsg);
112 }
113
114 static const char * getControlType(DWORD dwControlType)
115 {
116 #define TYPE_TO_STR(x) case x: return #x;
117     switch (dwControlType) {
118         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
119         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
120         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
121         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
122         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
123         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
124         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
125         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
126         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
127         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
128         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
129         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
130         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
131         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
132         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
133         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
134         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
135         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
136         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
137         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
138         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
139         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
140         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
141         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
142         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
143         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
144         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
145         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
146         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
147         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
148         TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
149     }
150 #undef TYPE_TO_STR
151     return wine_dbg_sprintf("UNKNOWN(%08x)", dwControlType);
152 }
153
154 static const char * getComponentType(DWORD dwComponentType)
155 {
156 #define TYPE_TO_STR(x) case x: return #x;
157     switch (dwComponentType) {
158         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED);
159         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL);
160         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE);
161         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR);
162         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
163         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES);
164         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE);
165         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
166         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN);
167         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED);
168         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL);
169         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE);
170         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);
171         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);
172         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
173         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE);
174         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER);
175         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
176         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY);
177         TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG);
178     }
179 #undef TYPE_TO_STR
180     return wine_dbg_sprintf("UNKNOWN(%08x)", dwComponentType);
181 }
182
183 static const char * getTargetType(DWORD dwType)
184 {
185 #define TYPE_TO_STR(x) case x: return #x;
186     switch (dwType) {
187         TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED);
188         TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT);
189         TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN);
190         TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT);
191         TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN);
192         TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX);
193     }
194 #undef TYPE_TO_STR
195     return wine_dbg_sprintf("UNKNOWN(%08x)", dwType);
196 }
197
198 /* FIXME is there a better way ? */
199 static DWORD DeviceComponentType(char *name)
200 {
201     if (strcmp(name, "Built-in Microphone") == 0)
202         return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
203
204     if (strcmp(name, "Built-in Line Input") == 0)
205         return MIXERLINE_COMPONENTTYPE_SRC_LINE;
206
207     if (strcmp(name, "Built-in Output") == 0)
208         return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
209
210     return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
211 }
212
213 static BOOL DeviceHasMute(AudioDeviceID deviceID, Boolean isInput)
214 {
215     Boolean writable = false;
216     OSStatus err = noErr;
217     err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, NULL);
218     if (err == noErr)
219     {
220         /* check if we can set it */
221         err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, &writable);
222         if (err == noErr)
223             return writable;
224     }
225     return FALSE;
226 }
227
228 /*
229  * Getters
230  */
231 static BOOL MIX_LineGetVolume(DWORD lineID, DWORD channels, Float32 *left, Float32 *right)
232 {
233     MixerLine *line = &mixer.lines[lineID];
234     UInt32 size = sizeof(Float32);
235     OSStatus err = noErr;
236     *left = *right = 0.0;
237
238     err = AudioDeviceGetProperty(line->deviceID, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, left);
239     if (err != noErr)
240         return FALSE;
241
242     if (channels == 2)
243     {
244         size = sizeof(Float32);
245         err = AudioDeviceGetProperty(line->deviceID, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, right);
246         if (err != noErr)
247             return FALSE;
248     }
249
250     TRACE("lineID %d channels %d return left %f right %f\n", lineID, channels, *left, *right);
251     return (err == noErr);
252 }
253
254 static BOOL MIX_LineGetMute(DWORD lineID, BOOL *muted)
255 {
256     MixerLine *line = &mixer.lines[lineID];
257     UInt32 size = sizeof(UInt32);
258     UInt32 val = 0;
259     OSStatus err = noErr;
260     err = AudioDeviceGetProperty(line->deviceID, 0, IsInput(line->direction), kAudioDevicePropertyMute, &size, &val);
261     *muted = val;
262
263     return (err == noErr);
264 }
265
266 /*
267  * Setters
268  */
269 static BOOL MIX_LineSetVolume(DWORD lineID, DWORD channels, Float32 left, Float32 right)
270 {
271     MixerLine *line = &mixer.lines[lineID];
272     UInt32 size = sizeof(Float32);
273     OSStatus err = noErr;
274     TRACE("lineID %d channels %d left %f right %f\n", lineID, channels, left, right);
275
276     if (channels == 2)
277     {
278         err = AudioDeviceSetProperty(line->deviceID, NULL, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
279         if (err != noErr)
280             return FALSE;
281
282         err = AudioDeviceSetProperty(line->deviceID, NULL, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &right);
283     }
284     else
285     {
286         /*
287             FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
288             err = AudioDeviceSetProperty(line->deviceID, NULL, 0, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
289         */
290         right = left;
291         err = AudioDeviceSetProperty(line->deviceID, NULL, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
292         if (err != noErr)
293             return FALSE;
294         err = AudioDeviceSetProperty(line->deviceID, NULL, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &right);
295     }
296     return (err == noErr);
297 }
298
299 static BOOL MIX_LineSetMute(DWORD lineID, BOOL mute)
300 {
301     MixerLine *line = &mixer.lines[lineID];
302     UInt32 val = mute;
303     UInt32 size = sizeof(UInt32);
304     OSStatus err = noErr;
305
306     err = AudioDeviceSetProperty(line->deviceID, 0, 0, IsInput(line->direction), kAudioDevicePropertyMute, size, &val);
307     return (err == noErr);
308 }
309
310 static void MIX_FillControls(void)
311 {
312     int i;
313     int ctrl = 0;
314     MixerLine *line;
315     for (i = 0; i < mixer.caps.cDestinations; i++)
316     {
317         line = &mixer.lines[i];
318         mixer.mixerCtrls[ctrl].dwLineID = i;
319         mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
320         mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
321         mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
322         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
323         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
324         mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
325         ctrl++;
326
327         mixer.mixerCtrls[ctrl].dwLineID = i;
328         if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
329             mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
330
331         mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
332         mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
333         mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
334         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
335         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
336         ctrl++;
337     }
338     assert(ctrl == mixer.numCtrl);
339 }
340
341 /**************************************************************************
342 *                               CoreAudio_MixerInit
343 */
344 LONG CoreAudio_MixerInit(void)
345 {
346     OSStatus status;
347     UInt32 propertySize;
348     AudioDeviceID *deviceArray = NULL;
349     char name[MAXPNAMELEN];
350     int i;
351     int numLines;
352
353     AudioStreamBasicDescription streamDescription;
354
355     /* Find number of lines */
356     status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
357     if (status)
358     {
359         ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
360         return DRV_FAILURE;
361     }
362
363     numLines = propertySize / sizeof(AudioDeviceID);
364
365     mixer.mixerCtrls = NULL;
366     mixer.lines = NULL;
367     mixer.numCtrl = 0;
368
369     mixer.caps.cDestinations = numLines;
370     mixer.caps.wMid = 0xAA;
371     mixer.caps.wPid = 0x55;
372     mixer.caps.vDriverVersion = 0x0100;
373
374     MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
375
376     mixer.caps.fdwSupport = 0; /* No bits defined yet */
377
378     mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
379     if (!mixer.lines)
380         goto error;
381
382     deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
383
384     propertySize = sizeof(AudioDeviceID) * numLines;
385     status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray);
386     if (status)
387     {
388         ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
389         goto error;
390     }
391
392     for (i = 0; i < numLines; i++)
393     {
394         Boolean write;
395         MixerLine *line = &mixer.lines[i];
396
397         line->deviceID = deviceArray[i];
398
399         propertySize = MAXPNAMELEN;
400         status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
401         if (status) {
402             ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %s\n", wine_dbgstr_fourcc(status));
403             goto error;
404         }
405
406         line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1);
407         if (!line->name)
408             goto error;
409
410         memcpy(line->name, name, strlen(name));
411
412         line->componentType = DeviceComponentType(line->name);
413
414         /* check for directions */
415         /* Output ? */
416         propertySize = sizeof(UInt32);
417         status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write );
418         if (status) {
419             ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %s\n", wine_dbgstr_fourcc(status));
420             goto error;
421         }
422
423         if ( (propertySize / sizeof(AudioStreamID)) != 0)
424         {
425             line->direction |= OutputDevice;
426
427             /* Check the number of channel for the stream */
428             propertySize = sizeof(streamDescription);
429             status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
430             if (status != noErr) {
431                 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
432                 goto error;
433             }
434             line->numChannels = streamDescription.mChannelsPerFrame;
435         }
436         else
437         {
438             /* Input ? */
439             propertySize = sizeof(UInt32);
440             status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write );
441             if (status) {
442                 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status));
443                 goto error;
444             }
445             if ( (propertySize / sizeof(AudioStreamID)) != 0)
446             {
447                 line->direction |= InputDevice;
448
449                 /* Check the number of channel for the stream */
450                 propertySize = sizeof(streamDescription);
451                 status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
452                 if (status != noErr) {
453                     ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
454                     goto error;
455                 }
456                 line->numChannels = streamDescription.mChannelsPerFrame;
457             }
458         }
459
460         mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
461     }
462     mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
463     if (!mixer.mixerCtrls)
464         goto error;
465
466     MIX_FillControls();
467
468     HeapFree(GetProcessHeap(), 0, deviceArray);
469     return DRV_SUCCESS;
470
471 error:
472     if (mixer.lines)
473     {
474         int i;
475         for (i = 0; i < mixer.caps.cDestinations; i++)
476         {
477             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
478         }
479         HeapFree(GetProcessHeap(), 0, mixer.lines);
480     }
481     HeapFree(GetProcessHeap(), 0, deviceArray);
482     if (mixer.mixerCtrls)
483         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
484     return DRV_FAILURE;
485 }
486
487 /**************************************************************************
488 *                               CoreAudio_MixerRelease
489 */
490 void CoreAudio_MixerRelease(void)
491 {
492     TRACE("()\n");
493
494     if (mixer.lines)
495     {
496         int i;
497         for (i = 0; i < mixer.caps.cDestinations; i++)
498         {
499             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
500         }
501         HeapFree(GetProcessHeap(), 0, mixer.lines);
502     }
503     if (mixer.mixerCtrls)
504         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
505 }
506
507 /**************************************************************************
508 *                               MIX_Open                        [internal]
509 */
510 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
511 {
512     TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
513     if (lpMod == NULL) {
514         WARN("invalid parameter: lpMod == NULL\n");
515         return MMSYSERR_INVALPARAM;
516     }
517
518     if (wDevID >= numMixers) {
519         WARN("bad device ID: %04X\n", wDevID);
520         return MMSYSERR_BADDEVICEID;
521     }
522     return MMSYSERR_NOERROR;
523 }
524
525 /**************************************************************************
526 *                               MIX_GetNumDevs                  [internal]
527 */
528 static DWORD MIX_GetNumDevs(void)
529 {
530     TRACE("()\n");
531     return numMixers;
532 }
533
534 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
535 {
536     TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
537
538     if (lpCaps == NULL) {
539         WARN("Invalid Parameter\n");
540         return MMSYSERR_INVALPARAM;
541     }
542
543     if (wDevID >= numMixers) {
544         WARN("bad device ID : %d\n", wDevID);
545         return MMSYSERR_BADDEVICEID;
546     }
547     memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
548     return MMSYSERR_NOERROR;
549 }
550
551 /**************************************************************************
552 *                               MIX_GetLineInfo                 [internal]
553 */
554 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
555 {
556     int i;
557     DWORD ret = MMSYSERR_ERROR;
558     MixerLine *line = NULL;
559
560     TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
561
562     if (lpMl == NULL) {
563         WARN("invalid parameter: lpMl = NULL\n");
564         return MMSYSERR_INVALPARAM;
565     }
566
567     if (lpMl->cbStruct != sizeof(*lpMl)) {
568         WARN("invalid parameter: lpMl->cbStruct\n");
569         return MMSYSERR_INVALPARAM;
570     }
571
572     if (wDevID >= numMixers) {
573         WARN("bad device ID: %04X\n", wDevID);
574         return MMSYSERR_BADDEVICEID;
575     }
576
577     /* FIXME: set all the variables correctly... the lines below
578         * are very wrong...
579         */
580     lpMl->dwUser = 0;
581
582     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
583     {
584         case MIXER_GETLINEINFOF_DESTINATION:
585             TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
586             if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
587             {
588                 lpMl->dwLineID = lpMl->dwDestination;
589                 line = &mixer.lines[lpMl->dwDestination];
590             }
591             else ret = MIXERR_INVALLINE;
592             break;
593         case MIXER_GETLINEINFOF_COMPONENTTYPE:
594             TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
595             for (i = 0; i < mixer.caps.cDestinations; i++)
596             {
597                 if (mixer.lines[i].componentType == lpMl->dwComponentType)
598                 {
599                     lpMl->dwDestination = lpMl->dwLineID = i;
600                     line = &mixer.lines[i];
601                     break;
602                 }
603             }
604             if (line == NULL)
605             {
606                 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
607                 ret = MIXERR_INVALVALUE;
608             }
609             break;
610         case MIXER_GETLINEINFOF_SOURCE:
611             FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
612             break;
613         case MIXER_GETLINEINFOF_LINEID:
614             TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
615             if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
616             {
617                 lpMl->dwDestination = lpMl->dwLineID;
618                 line = &mixer.lines[lpMl->dwLineID];
619             }
620             else ret = MIXERR_INVALLINE;
621             break;
622         case MIXER_GETLINEINFOF_TARGETTYPE:
623             FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
624             switch (lpMl->Target.dwType) {
625                 case MIXERLINE_TARGETTYPE_UNDEFINED:
626                 case MIXERLINE_TARGETTYPE_WAVEOUT:
627                 case MIXERLINE_TARGETTYPE_WAVEIN:
628                 case MIXERLINE_TARGETTYPE_MIDIOUT:
629                 case MIXERLINE_TARGETTYPE_MIDIIN:
630                 case MIXERLINE_TARGETTYPE_AUX:
631                 default:
632                     FIXME("Unhandled target type (%s)\n",
633                           getTargetType(lpMl->Target.dwType));
634                     return MMSYSERR_INVALPARAM;
635             }
636                 break;
637         default:
638             WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
639             break;
640     }
641
642     if (line)
643     {
644         lpMl->dwComponentType = line->componentType;
645         lpMl->cChannels = line->numChannels;
646         lpMl->cControls = ControlsPerLine;
647
648         /* FIXME check there with CoreAudio */
649         lpMl->cConnections = 1;
650         lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
651
652         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
653         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
654
655         if ( IsInput(line->direction) )
656             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
657         else
658             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
659
660         lpMl->Target.dwDeviceID = line->deviceID;
661         lpMl->Target.wMid = mixer.caps.wMid;
662         lpMl->Target.wPid = mixer.caps.wPid;
663         lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
664
665         MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
666         ret = MMSYSERR_NOERROR;
667     }
668     return ret;
669 }
670
671 /**************************************************************************
672 *                               MIX_GetLineControls             [internal]
673 */
674 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
675 {
676     DWORD ret = MMSYSERR_NOTENABLED;
677     int ctrl = 0;
678     TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
679
680     if (lpMlc == NULL) {
681         WARN("invalid parameter: lpMlc == NULL\n");
682         return MMSYSERR_INVALPARAM;
683     }
684
685     if (lpMlc->cbStruct < sizeof(*lpMlc)) {
686         WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
687         return MMSYSERR_INVALPARAM;
688     }
689
690     if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
691         WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
692         return MMSYSERR_INVALPARAM;
693     }
694
695     if (wDevID >= numMixers) {
696         WARN("bad device ID: %04X\n", wDevID);
697         return MMSYSERR_BADDEVICEID;
698     }
699
700     switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
701     {
702         case MIXER_GETLINECONTROLSF_ALL:
703             FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
704             if (lpMlc->cControls != ControlsPerLine)
705             {
706                 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
707                 ret = MMSYSERR_INVALPARAM;
708             }
709             else
710             {
711                 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
712                 {
713                     int i;
714                     for (i = 0; i < lpMlc->cControls; i++)
715                     {
716                         lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
717                     }
718                     ret = MMSYSERR_NOERROR;
719                 }
720                 else ret = MIXERR_INVALLINE;
721             }
722             break;
723         case MIXER_GETLINECONTROLSF_ONEBYID:
724             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
725             if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
726             {
727                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
728                 ret = MMSYSERR_NOERROR;
729             }
730             else ret = MIXERR_INVALVALUE;
731             break;
732         case MIXER_GETLINECONTROLSF_ONEBYTYPE:
733             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
734             if ( (lpMlc->dwLineID < 0) || (lpMlc->dwLineID >= mixer.caps.cDestinations) )
735             {
736                 ret = MIXERR_INVALLINE;
737                 break;
738             }
739             if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
740             {
741                 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
742                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
743                 ret = MMSYSERR_NOERROR;
744             }
745             else
746                 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
747                 {
748                     ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
749                     lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
750                     ret = MMSYSERR_NOERROR;
751                 }
752                 break;
753         default:
754             ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
755             ret = MMSYSERR_INVALPARAM;
756     }
757
758     return ret;
759 }
760
761 /**************************************************************************
762  *                              MIX_GetControlDetails           [internal]
763  */
764 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
765 {
766     DWORD ret = MMSYSERR_NOTSUPPORTED;
767     DWORD dwControlType;
768
769     TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
770
771     if (lpmcd == NULL) {
772         TRACE("invalid parameter: lpmcd == NULL\n");
773         return MMSYSERR_INVALPARAM;
774     }
775
776     if (wDevID >= numMixers) {
777         WARN("bad device ID: %04X\n", wDevID);
778         return MMSYSERR_BADDEVICEID;
779     }
780
781     if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
782     {
783         WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
784         return MMSYSERR_NOTSUPPORTED;
785     }
786
787     if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl )
788     {
789         WARN("bad control ID: %d\n", lpmcd->dwControlID);
790         return MIXERR_INVALVALUE;
791     }
792
793     TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID);
794
795     dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
796     switch (dwControlType)
797     {
798         case MIXERCONTROL_CONTROLTYPE_VOLUME:
799             FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels);
800             {
801                 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
802                 Float32 left, right;
803
804                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
805                     WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
806                     return MMSYSERR_INVALPARAM;
807                 }
808
809                 if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) )
810                 {
811                     mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
812
813                     switch (lpmcd->cChannels)
814                     {
815                         case 1:
816                             /* mono... so R = L */
817                             mcdu->dwValue = left * 65535;
818                             TRACE("Reading RL = %d\n", mcdu->dwValue);
819                             break;
820                         case 2:
821                             /* stereo, left is paDetails[0] */
822                             mcdu->dwValue = left * 65535;
823                             TRACE("Reading L = %d\n", mcdu->dwValue);
824                             mcdu++;
825                             mcdu->dwValue = right * 65535;
826                             TRACE("Reading R = %d\n", mcdu->dwValue);
827                             break;
828                         default:
829                             WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
830                             return MMSYSERR_INVALPARAM;
831                     }
832                     TRACE("=> %08x\n", mcdu->dwValue);
833                     ret = MMSYSERR_NOERROR;
834                 }
835             }
836             break;
837         case MIXERCONTROL_CONTROLTYPE_MUTE:
838         case MIXERCONTROL_CONTROLTYPE_ONOFF:
839             FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
840             {
841                 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
842                 BOOL muted;
843                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
844                     WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails);
845                     return MMSYSERR_INVALPARAM;
846                 }
847                 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
848
849                 if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) )
850                 {
851                     mcdb->fValue = muted;
852                     TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
853                     ret = MMSYSERR_NOERROR;
854                 }
855             }
856             break;
857         case MIXERCONTROL_CONTROLTYPE_MIXER:
858         case MIXERCONTROL_CONTROLTYPE_MUX:
859         default:
860             FIXME("controlType : %s\n", getControlType(dwControlType));
861             break;
862     }
863     return ret;
864 }
865
866 /**************************************************************************
867 *                               MIX_SetControlDetails           [internal]
868 */
869 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
870 {
871     DWORD ret = MMSYSERR_NOTSUPPORTED;
872     DWORD dwControlType;
873
874     TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
875
876     if (lpmcd == NULL) {
877         TRACE("invalid parameter: lpmcd == NULL\n");
878         return MMSYSERR_INVALPARAM;
879     }
880
881     if (wDevID >= numMixers) {
882         WARN("bad device ID: %04X\n", wDevID);
883         return MMSYSERR_BADDEVICEID;
884     }
885
886     if ( (fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
887     {
888         WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK);
889         return MMSYSERR_NOTSUPPORTED;
890     }
891
892     TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd->dwControlID);
893     dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
894     switch (dwControlType)
895     {
896         case MIXERCONTROL_CONTROLTYPE_VOLUME:
897             FIXME("controlType : %s\n", getControlType(dwControlType));
898             {
899                 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
900                 Float32 left, right = 0;
901
902                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
903                     WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
904                     return MMSYSERR_INVALPARAM;
905                 }
906
907                 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
908
909                 switch (lpmcd->cChannels)
910                 {
911                     case 1:
912                         /* mono... so R = L */
913                         TRACE("Setting RL to %d\n", mcdu->dwValue);
914                         left = (Float32) mcdu->dwValue / 65535.0;
915                         break;
916                     case 2:
917                         /* stereo, left is paDetails[0] */
918                         TRACE("Setting L to %d\n", mcdu->dwValue);
919                         left = (Float32) mcdu->dwValue / 65535.0;
920                         mcdu++;
921                         TRACE("Setting R to %d\n", mcdu->dwValue);
922                         right = (Float32) mcdu->dwValue / 65535.0;
923                         break;
924                     default:
925                         WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
926                         return MMSYSERR_INVALPARAM;
927                 }
928                 if ( MIX_LineSetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, left, right) )
929                     ret = MMSYSERR_NOERROR;
930             }
931             break;
932         case MIXERCONTROL_CONTROLTYPE_MUTE:
933         case MIXERCONTROL_CONTROLTYPE_ONOFF:
934             TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
935             {
936                 LPMIXERCONTROLDETAILS_BOOLEAN   mcdb;
937
938                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
939                     WARN("invalid parameter: cbDetails\n");
940                     return MMSYSERR_INVALPARAM;
941                 }
942                 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
943                 if ( MIX_LineSetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, mcdb->fValue) )
944                     ret = MMSYSERR_NOERROR;
945             }
946             break;
947         case MIXERCONTROL_CONTROLTYPE_MIXER:
948         case MIXERCONTROL_CONTROLTYPE_MUX:
949         default:
950             FIXME("controlType : %s\n", getControlType(dwControlType));
951             ret = MMSYSERR_NOTSUPPORTED;
952             break;
953     }
954     return ret;
955 }
956
957 /**************************************************************************
958 *                               mxdMessage
959 */
960 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
961                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
962 {
963     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
964           dwUser, dwParam1, dwParam2);
965
966     switch (wMsg)
967     {
968         case DRVM_INIT:
969         case DRVM_EXIT:
970         case DRVM_ENABLE:
971         case DRVM_DISABLE:
972             /* FIXME: Pretend this is supported */
973             return 0;
974         case MXDM_OPEN:
975             return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
976         case MXDM_CLOSE:
977             return MMSYSERR_NOERROR;
978         case MXDM_GETNUMDEVS:
979             return MIX_GetNumDevs();
980         case MXDM_GETDEVCAPS:
981             return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
982         case MXDM_GETLINEINFO:
983             return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
984         case MXDM_GETLINECONTROLS:
985             return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
986         case MXDM_GETCONTROLDETAILS:
987             return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
988         case MXDM_SETCONTROLDETAILS:
989             return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
990         default:
991             WARN("unknown message %d!\n", wMsg);
992             return MMSYSERR_NOTSUPPORTED;
993     }
994 }
995
996 #else
997
998 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
999                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1000 {
1001     TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1002     return MMSYSERR_NOTENABLED;
1003 }
1004 #endif /* HAVE_COREAUDIO_COREAUDIO_H */