ntoskrnl: Initialize structure size.
[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 /*
275  * Setters
276  */
277 static BOOL MIX_LineSetVolume(DWORD lineID, DWORD channels, Float32 left, Float32 right)
278 {
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);
283
284     if (channels == 2)
285     {
286         err = AudioDeviceSetProperty(line->deviceID, NULL, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
287         if (err != noErr)
288             return FALSE;
289
290         err = AudioDeviceSetProperty(line->deviceID, NULL, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &right);
291     }
292     else
293     {
294         /*
295             FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError
296             err = AudioDeviceSetProperty(line->deviceID, NULL, 0, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left);
297         */
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);
303     }
304     return (err == noErr);
305 }
306
307 static BOOL MIX_LineSetMute(DWORD lineID, BOOL mute)
308 {
309     MixerLine *line = &mixer.lines[lineID];
310     UInt32 val = mute;
311     UInt32 size = sizeof(UInt32);
312     OSStatus err = noErr;
313
314     err = AudioDeviceSetProperty(line->deviceID, 0, 0, IsInput(line->direction), kAudioDevicePropertyMute, size, &val);
315     return (err == noErr);
316 }
317
318 static void MIX_FillControls(void)
319 {
320     int i;
321     int ctrl = 0;
322     MixerLine *line;
323     for (i = 0; i < mixer.caps.cDestinations; i++)
324     {
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++;
334
335         mixer.mixerCtrls[ctrl].dwLineID = i;
336         if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
337             mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
338
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++;
345     }
346     assert(ctrl == mixer.numCtrl);
347 }
348
349 /**************************************************************************
350 *                               CoreAudio_MixerInit
351 */
352 LONG CoreAudio_MixerInit(void)
353 {
354     OSStatus status;
355     UInt32 propertySize;
356     AudioDeviceID *deviceArray = NULL;
357     char name[MAXPNAMELEN];
358     int i;
359     int numLines;
360
361     AudioStreamBasicDescription streamDescription;
362
363     /* Find number of lines */
364     status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
365     if (status)
366     {
367         ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
368         return DRV_FAILURE;
369     }
370
371     numLines = propertySize / sizeof(AudioDeviceID);
372
373     mixer.mixerCtrls = NULL;
374     mixer.lines = NULL;
375     mixer.numCtrl = 0;
376
377     mixer.caps.cDestinations = numLines;
378     mixer.caps.wMid = 0xAA;
379     mixer.caps.wPid = 0x55;
380     mixer.caps.vDriverVersion = 0x0100;
381
382     MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
383
384     mixer.caps.fdwSupport = 0; /* No bits defined yet */
385
386     mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
387     if (!mixer.lines)
388         goto error;
389
390     deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
391
392     propertySize = sizeof(AudioDeviceID) * numLines;
393     status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray);
394     if (status)
395     {
396         ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %s\n", wine_dbgstr_fourcc(status));
397         goto error;
398     }
399
400     for (i = 0; i < numLines; i++)
401     {
402         Boolean write;
403         MixerLine *line = &mixer.lines[i];
404
405         line->deviceID = deviceArray[i];
406
407         propertySize = MAXPNAMELEN;
408         status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
409         if (status) {
410             ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %s\n", wine_dbgstr_fourcc(status));
411             goto error;
412         }
413
414         line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1);
415         if (!line->name)
416             goto error;
417
418         memcpy(line->name, name, strlen(name));
419
420         line->componentType = DeviceComponentType(line->name);
421
422         /* check for directions */
423         /* Output ? */
424         propertySize = sizeof(UInt32);
425         status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write );
426         if (status) {
427             ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %s\n", wine_dbgstr_fourcc(status));
428             goto error;
429         }
430
431         if ( (propertySize / sizeof(AudioStreamID)) != 0)
432         {
433             line->direction |= OutputDevice;
434
435             /* Check the number of channel for the stream */
436             propertySize = sizeof(streamDescription);
437             status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
438             if (status != noErr) {
439                 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
440                 goto error;
441             }
442             line->numChannels = streamDescription.mChannelsPerFrame;
443         }
444         else
445         {
446             /* Input ? */
447             propertySize = sizeof(UInt32);
448             status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write );
449             if (status) {
450                 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %s\n", wine_dbgstr_fourcc(status));
451                 goto error;
452             }
453             if ( (propertySize / sizeof(AudioStreamID)) != 0)
454             {
455                 line->direction |= InputDevice;
456
457                 /* Check the number of channel for the stream */
458                 propertySize = sizeof(streamDescription);
459                 status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
460                 if (status != noErr) {
461                     ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %s\n", wine_dbgstr_fourcc(status));
462                     goto error;
463                 }
464                 line->numChannels = streamDescription.mChannelsPerFrame;
465             }
466         }
467
468         mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
469     }
470     mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
471     if (!mixer.mixerCtrls)
472         goto error;
473
474     MIX_FillControls();
475
476     HeapFree(GetProcessHeap(), 0, deviceArray);
477     return DRV_SUCCESS;
478
479 error:
480     if (mixer.lines)
481     {
482         int i;
483         for (i = 0; i < mixer.caps.cDestinations; i++)
484         {
485             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
486         }
487         HeapFree(GetProcessHeap(), 0, mixer.lines);
488     }
489     HeapFree(GetProcessHeap(), 0, deviceArray);
490     if (mixer.mixerCtrls)
491         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
492     return DRV_FAILURE;
493 }
494
495 /**************************************************************************
496 *                               CoreAudio_MixerRelease
497 */
498 void CoreAudio_MixerRelease(void)
499 {
500     TRACE("()\n");
501
502     if (mixer.lines)
503     {
504         int i;
505         for (i = 0; i < mixer.caps.cDestinations; i++)
506         {
507             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
508         }
509         HeapFree(GetProcessHeap(), 0, mixer.lines);
510     }
511     if (mixer.mixerCtrls)
512         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
513 }
514
515 /**************************************************************************
516 *                               MIX_Open                        [internal]
517 */
518 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
519 {
520     TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
521     if (lpMod == NULL) {
522         WARN("invalid parameter: lpMod == NULL\n");
523         return MMSYSERR_INVALPARAM;
524     }
525
526     if (wDevID >= numMixers) {
527         WARN("bad device ID: %04X\n", wDevID);
528         return MMSYSERR_BADDEVICEID;
529     }
530     return MMSYSERR_NOERROR;
531 }
532
533 /**************************************************************************
534 *                               MIX_GetNumDevs                  [internal]
535 */
536 static DWORD MIX_GetNumDevs(void)
537 {
538     TRACE("()\n");
539     return numMixers;
540 }
541
542 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
543 {
544     TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
545
546     if (lpCaps == NULL) {
547         WARN("Invalid Parameter\n");
548         return MMSYSERR_INVALPARAM;
549     }
550
551     if (wDevID >= numMixers) {
552         WARN("bad device ID : %d\n", wDevID);
553         return MMSYSERR_BADDEVICEID;
554     }
555     memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
556     return MMSYSERR_NOERROR;
557 }
558
559 /**************************************************************************
560 *                               MIX_GetLineInfo                 [internal]
561 */
562 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
563 {
564     int i;
565     DWORD ret = MMSYSERR_ERROR;
566     MixerLine *line = NULL;
567
568     TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
569
570     if (lpMl == NULL) {
571         WARN("invalid parameter: lpMl = NULL\n");
572         return MMSYSERR_INVALPARAM;
573     }
574
575     if (lpMl->cbStruct != sizeof(*lpMl)) {
576         WARN("invalid parameter: lpMl->cbStruct\n");
577         return MMSYSERR_INVALPARAM;
578     }
579
580     if (wDevID >= numMixers) {
581         WARN("bad device ID: %04X\n", wDevID);
582         return MMSYSERR_BADDEVICEID;
583     }
584
585     /* FIXME: set all the variables correctly... the lines below
586         * are very wrong...
587         */
588     lpMl->dwUser = 0;
589
590     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
591     {
592         case MIXER_GETLINEINFOF_DESTINATION:
593             TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
594             if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
595             {
596                 lpMl->dwLineID = lpMl->dwDestination;
597                 line = &mixer.lines[lpMl->dwDestination];
598             }
599             else ret = MIXERR_INVALLINE;
600             break;
601         case MIXER_GETLINEINFOF_COMPONENTTYPE:
602             TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
603             for (i = 0; i < mixer.caps.cDestinations; i++)
604             {
605                 if (mixer.lines[i].componentType == lpMl->dwComponentType)
606                 {
607                     lpMl->dwDestination = lpMl->dwLineID = i;
608                     line = &mixer.lines[i];
609                     break;
610                 }
611             }
612             if (line == NULL)
613             {
614                 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
615                 ret = MIXERR_INVALVALUE;
616             }
617             break;
618         case MIXER_GETLINEINFOF_SOURCE:
619             FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
620             break;
621         case MIXER_GETLINEINFOF_LINEID:
622             TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
623             if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
624             {
625                 lpMl->dwDestination = lpMl->dwLineID;
626                 line = &mixer.lines[lpMl->dwLineID];
627             }
628             else ret = MIXERR_INVALLINE;
629             break;
630         case MIXER_GETLINEINFOF_TARGETTYPE:
631             FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
632             switch (lpMl->Target.dwType) {
633                 case MIXERLINE_TARGETTYPE_UNDEFINED:
634                 case MIXERLINE_TARGETTYPE_WAVEOUT:
635                 case MIXERLINE_TARGETTYPE_WAVEIN:
636                 case MIXERLINE_TARGETTYPE_MIDIOUT:
637                 case MIXERLINE_TARGETTYPE_MIDIIN:
638                 case MIXERLINE_TARGETTYPE_AUX:
639                 default:
640                     FIXME("Unhandled target type (%s)\n",
641                           getTargetType(lpMl->Target.dwType));
642                     return MMSYSERR_INVALPARAM;
643             }
644                 break;
645         default:
646             WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
647             break;
648     }
649
650     if (line)
651     {
652         lpMl->dwComponentType = line->componentType;
653         lpMl->cChannels = line->numChannels;
654         lpMl->cControls = ControlsPerLine;
655
656         /* FIXME check there with CoreAudio */
657         lpMl->cConnections = 1;
658         lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
659
660         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
661         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
662
663         if ( IsInput(line->direction) )
664             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
665         else
666             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
667
668         lpMl->Target.dwDeviceID = line->deviceID;
669         lpMl->Target.wMid = mixer.caps.wMid;
670         lpMl->Target.wPid = mixer.caps.wPid;
671         lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
672
673         MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
674         ret = MMSYSERR_NOERROR;
675     }
676     return ret;
677 }
678
679 /**************************************************************************
680 *                               MIX_GetLineControls             [internal]
681 */
682 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
683 {
684     DWORD ret = MMSYSERR_NOTENABLED;
685     int ctrl = 0;
686     TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
687
688     if (lpMlc == NULL) {
689         WARN("invalid parameter: lpMlc == NULL\n");
690         return MMSYSERR_INVALPARAM;
691     }
692
693     if (lpMlc->cbStruct < sizeof(*lpMlc)) {
694         WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
695         return MMSYSERR_INVALPARAM;
696     }
697
698     if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
699         WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
700         return MMSYSERR_INVALPARAM;
701     }
702
703     if (wDevID >= numMixers) {
704         WARN("bad device ID: %04X\n", wDevID);
705         return MMSYSERR_BADDEVICEID;
706     }
707
708     switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
709     {
710         case MIXER_GETLINECONTROLSF_ALL:
711             FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
712             if (lpMlc->cControls != ControlsPerLine)
713             {
714                 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
715                 ret = MMSYSERR_INVALPARAM;
716             }
717             else
718             {
719                 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
720                 {
721                     int i;
722                     for (i = 0; i < lpMlc->cControls; i++)
723                     {
724                         lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
725                     }
726                     ret = MMSYSERR_NOERROR;
727                 }
728                 else ret = MIXERR_INVALLINE;
729             }
730             break;
731         case MIXER_GETLINECONTROLSF_ONEBYID:
732             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
733             if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
734             {
735                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
736                 ret = MMSYSERR_NOERROR;
737             }
738             else ret = MIXERR_INVALVALUE;
739             break;
740         case MIXER_GETLINECONTROLSF_ONEBYTYPE:
741             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
742             if ( (lpMlc->dwLineID < 0) || (lpMlc->dwLineID >= mixer.caps.cDestinations) )
743             {
744                 ret = MIXERR_INVALLINE;
745                 break;
746             }
747             if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
748             {
749                 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
750                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
751                 ret = MMSYSERR_NOERROR;
752             }
753             else
754                 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
755                 {
756                     ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
757                     lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
758                     ret = MMSYSERR_NOERROR;
759                 }
760                 break;
761         default:
762             ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
763             ret = MMSYSERR_INVALPARAM;
764     }
765
766     return ret;
767 }
768
769 /**************************************************************************
770  *                              MIX_GetControlDetails           [internal]
771  */
772 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
773 {
774     DWORD ret = MMSYSERR_NOTSUPPORTED;
775     DWORD dwControlType;
776
777     TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
778
779     if (lpmcd == NULL) {
780         TRACE("invalid parameter: lpmcd == NULL\n");
781         return MMSYSERR_INVALPARAM;
782     }
783
784     if (wDevID >= numMixers) {
785         WARN("bad device ID: %04X\n", wDevID);
786         return MMSYSERR_BADDEVICEID;
787     }
788
789     if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
790     {
791         WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
792         return MMSYSERR_NOTSUPPORTED;
793     }
794
795     if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl )
796     {
797         WARN("bad control ID: %d\n", lpmcd->dwControlID);
798         return MIXERR_INVALVALUE;
799     }
800
801     TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID);
802
803     dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
804     switch (dwControlType)
805     {
806         case MIXERCONTROL_CONTROLTYPE_VOLUME:
807             FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels);
808             {
809                 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
810                 Float32 left, right;
811
812                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
813                     WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
814                     return MMSYSERR_INVALPARAM;
815                 }
816
817                 if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) )
818                 {
819                     mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
820
821                     switch (lpmcd->cChannels)
822                     {
823                         case 1:
824                             /* mono... so R = L */
825                             mcdu->dwValue = left * 65535;
826                             TRACE("Reading RL = %d\n", mcdu->dwValue);
827                             break;
828                         case 2:
829                             /* stereo, left is paDetails[0] */
830                             mcdu->dwValue = left * 65535;
831                             TRACE("Reading L = %d\n", mcdu->dwValue);
832                             mcdu++;
833                             mcdu->dwValue = right * 65535;
834                             TRACE("Reading R = %d\n", mcdu->dwValue);
835                             break;
836                         default:
837                             WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
838                             return MMSYSERR_INVALPARAM;
839                     }
840                     TRACE("=> %08x\n", mcdu->dwValue);
841                     ret = MMSYSERR_NOERROR;
842                 }
843             }
844             break;
845         case MIXERCONTROL_CONTROLTYPE_MUTE:
846         case MIXERCONTROL_CONTROLTYPE_ONOFF:
847             FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
848             {
849                 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
850                 BOOL muted;
851                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
852                     WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails);
853                     return MMSYSERR_INVALPARAM;
854                 }
855                 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
856
857                 if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) )
858                 {
859                     mcdb->fValue = muted;
860                     TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
861                     ret = MMSYSERR_NOERROR;
862                 }
863             }
864             break;
865         case MIXERCONTROL_CONTROLTYPE_MIXER:
866         case MIXERCONTROL_CONTROLTYPE_MUX:
867         default:
868             FIXME("controlType : %s\n", getControlType(dwControlType));
869             break;
870     }
871     return ret;
872 }
873
874 /**************************************************************************
875 *                               MIX_SetControlDetails           [internal]
876 */
877 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails)
878 {
879     DWORD ret = MMSYSERR_NOTSUPPORTED;
880     DWORD dwControlType;
881
882     TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails);
883
884     if (lpmcd == NULL) {
885         TRACE("invalid parameter: lpmcd == NULL\n");
886         return MMSYSERR_INVALPARAM;
887     }
888
889     if (wDevID >= numMixers) {
890         WARN("bad device ID: %04X\n", wDevID);
891         return MMSYSERR_BADDEVICEID;
892     }
893
894     if ( (fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE )
895     {
896         WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK);
897         return MMSYSERR_NOTSUPPORTED;
898     }
899
900     TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd->dwControlID);
901     dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType;
902     switch (dwControlType)
903     {
904         case MIXERCONTROL_CONTROLTYPE_VOLUME:
905             FIXME("controlType : %s\n", getControlType(dwControlType));
906             {
907                 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
908                 Float32 left, right = 0;
909
910                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
911                     WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails);
912                     return MMSYSERR_INVALPARAM;
913                 }
914
915                 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
916
917                 switch (lpmcd->cChannels)
918                 {
919                     case 1:
920                         /* mono... so R = L */
921                         TRACE("Setting RL to %d\n", mcdu->dwValue);
922                         left = (Float32) mcdu->dwValue / 65535.0;
923                         break;
924                     case 2:
925                         /* stereo, left is paDetails[0] */
926                         TRACE("Setting L to %d\n", mcdu->dwValue);
927                         left = (Float32) mcdu->dwValue / 65535.0;
928                         mcdu++;
929                         TRACE("Setting R to %d\n", mcdu->dwValue);
930                         right = (Float32) mcdu->dwValue / 65535.0;
931                         break;
932                     default:
933                         WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
934                         return MMSYSERR_INVALPARAM;
935                 }
936                 if ( MIX_LineSetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, left, right) )
937                     ret = MMSYSERR_NOERROR;
938             }
939             break;
940         case MIXERCONTROL_CONTROLTYPE_MUTE:
941         case MIXERCONTROL_CONTROLTYPE_ONOFF:
942             TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels);
943             {
944                 LPMIXERCONTROLDETAILS_BOOLEAN   mcdb;
945
946                 if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
947                     WARN("invalid parameter: cbDetails\n");
948                     return MMSYSERR_INVALPARAM;
949                 }
950                 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
951                 if ( MIX_LineSetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, mcdb->fValue) )
952                     ret = MMSYSERR_NOERROR;
953             }
954             break;
955         case MIXERCONTROL_CONTROLTYPE_MIXER:
956         case MIXERCONTROL_CONTROLTYPE_MUX:
957         default:
958             FIXME("controlType : %s\n", getControlType(dwControlType));
959             ret = MMSYSERR_NOTSUPPORTED;
960             break;
961     }
962     return ret;
963 }
964
965 /**************************************************************************
966 *                               mxdMessage
967 */
968 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
969                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
970 {
971     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
972           dwUser, dwParam1, dwParam2);
973
974     switch (wMsg)
975     {
976         case DRVM_INIT:
977         case DRVM_EXIT:
978         case DRVM_ENABLE:
979         case DRVM_DISABLE:
980             /* FIXME: Pretend this is supported */
981             return 0;
982         case MXDM_OPEN:
983             return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
984         case MXDM_CLOSE:
985             return MMSYSERR_NOERROR;
986         case MXDM_GETNUMDEVS:
987             return MIX_GetNumDevs();
988         case MXDM_GETDEVCAPS:
989             return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
990         case MXDM_GETLINEINFO:
991             return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
992         case MXDM_GETLINECONTROLS:
993             return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
994         case MXDM_GETCONTROLDETAILS:
995             return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
996         case MXDM_SETCONTROLDETAILS:
997             return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
998         default:
999             WARN("unknown message %d!\n", wMsg);
1000             return MMSYSERR_NOTSUPPORTED;
1001     }
1002 }
1003
1004 #else
1005
1006 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1007                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1008 {
1009     TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1010     return MMSYSERR_NOTENABLED;
1011 }
1012 #endif /* HAVE_COREAUDIO_COREAUDIO_H */