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