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