winecoreaudio: Initial implementation of MIX_GetLineControls.
[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 static void MIX_FillControls(void)
237 {
238     int i;
239     int ctrl = 0;
240     MixerLine *line;
241     for (i = 0; i < mixer.caps.cDestinations; i++)
242     {
243         line = &mixer.lines[i];
244         mixer.mixerCtrls[ctrl].dwLineID = i;
245         mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
246         mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
247         mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
248         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
249         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535;
250         mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656;
251         ctrl++;
252
253         mixer.mixerCtrls[ctrl].dwLineID = i;
254         if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) )
255             mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED;
256
257         mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW);
258         mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
259         mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl;
260         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0;
261         mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1;
262         ctrl++;
263     }
264     assert(ctrl == mixer.numCtrl);
265 }
266
267 /**************************************************************************
268 *                               CoreAudio_MixerInit
269 */
270 LONG CoreAudio_MixerInit(void)
271 {
272     OSStatus status;
273     UInt32 propertySize;
274     AudioDeviceID *deviceArray = NULL;
275     char name[MAXPNAMELEN];
276     int i;
277     int numLines;
278
279     AudioStreamBasicDescription streamDescription;
280
281     /* Find number of lines */
282     status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL);
283     if (status)
284     {
285         ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
286             (char) (status >> 16),
287             (char) (status >> 8),
288             (char) status);
289         return 0;
290     }
291
292     numLines = propertySize / sizeof(AudioDeviceID);
293
294     mixer.mixerCtrls = NULL;
295     mixer.lines = NULL;
296     mixer.numCtrl = 0;
297
298     mixer.caps.cDestinations = numLines;
299     mixer.caps.wMid = 0xAA;
300     mixer.caps.wPid = 0x55;
301     mixer.caps.vDriverVersion = 0x0100;
302
303     MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR));
304
305     mixer.caps.fdwSupport = 0; /* No bits defined yet */
306
307     mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines);
308     if (!mixer.lines)
309         goto error;
310
311     deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines);
312
313     propertySize = sizeof(AudioDeviceID) * numLines;
314     status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray);
315     if (status)
316     {
317         ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24),
318             (char) (status >> 16),
319             (char) (status >> 8),
320             (char) status);
321         goto error;
322     }
323
324     for (i = 0; i < numLines; i++)
325     {
326         Boolean write;
327         MixerLine *line = &mixer.lines[i];
328
329         line->deviceID = deviceArray[i];
330
331         propertySize = MAXPNAMELEN;
332         status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name);
333         if (status) {
334             ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24),
335                 (char) (status >> 16),
336                 (char) (status >> 8),
337                 (char) status);
338             goto error;
339         }
340
341         line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1);
342         if (!line->name)
343             goto error;
344
345         memcpy(line->name, name, strlen(name));
346
347         line->componentType = DeviceComponentType(line->name);
348
349         /* check for directions */
350         /* Output ? */
351         propertySize = sizeof(UInt32);
352         status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write );
353         if (status) {
354             ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %c%c%c%c\n", (char) (status >> 24),
355                 (char) (status >> 16),
356                 (char) (status >> 8),
357                 (char) status);
358             goto error;
359         }
360
361         if ( (propertySize / sizeof(AudioStreamID)) != 0)
362         {
363             line->direction |= OutputDevice;
364
365             /* Check the number of channel for the stream */
366             propertySize = sizeof(streamDescription);
367             status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
368             if (status != noErr) {
369                 ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
370                     (char) (status >> 16),
371                     (char) (status >> 8),
372                     (char) status);
373                 goto error;
374             }
375             line->numChannels = streamDescription.mChannelsPerFrame;
376         }
377         else
378         {
379             /* Input ? */
380             propertySize = sizeof(UInt32);
381             status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write );
382             if (status) {
383                 ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %c%c%c%c\n", (char) (status >> 24),
384                     (char) (status >> 16),
385                     (char) (status >> 8),
386                     (char) status);
387                 goto error;
388             }
389             if ( (propertySize / sizeof(AudioStreamID)) != 0)
390             {
391                 line->direction |= InputDevice;
392
393                 /* Check the number of channel for the stream */
394                 propertySize = sizeof(streamDescription);
395                 status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription);
396                 if (status != noErr) {
397                     ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24),
398                         (char) (status >> 16),
399                         (char) (status >> 8),
400                         (char) status);
401                     goto error;
402                 }
403                 line->numChannels = streamDescription.mChannelsPerFrame;
404             }
405         }
406
407         mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */
408     }
409     mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl);
410     if (!mixer.mixerCtrls)
411         goto error;
412
413     MIX_FillControls();
414
415     HeapFree(GetProcessHeap(), 0, deviceArray);
416     return 1;
417
418 error:
419     if (mixer.lines)
420     {
421         int i;
422         for (i = 0; i < mixer.caps.cDestinations; i++)
423         {
424             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
425         }
426         HeapFree(GetProcessHeap(), 0, mixer.lines);
427     }
428     HeapFree(GetProcessHeap(), 0, deviceArray);
429     if (mixer.mixerCtrls)
430         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
431     return 0;
432 }
433
434 /**************************************************************************
435 *                               CoreAudio_MixerRelease
436 */
437 void CoreAudio_MixerRelease(void)
438 {
439     TRACE("()\n");
440
441     if (mixer.lines)
442     {
443         int i;
444         for (i = 0; i < mixer.caps.cDestinations; i++)
445         {
446             HeapFree(GetProcessHeap(), 0, mixer.lines[i].name);
447         }
448         HeapFree(GetProcessHeap(), 0, mixer.lines);
449     }
450     if (mixer.mixerCtrls)
451         HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls);
452 }
453
454 /**************************************************************************
455 *                               MIX_Open                        [internal]
456 */
457 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags)
458 {
459     TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags);
460     if (lpMod == NULL) {
461         WARN("invalid parameter: lpMod == NULL\n");
462         return MMSYSERR_INVALPARAM;
463     }
464
465     if (wDevID >= numMixers) {
466         WARN("bad device ID: %04X\n", wDevID);
467         return MMSYSERR_BADDEVICEID;
468     }
469     return MMSYSERR_NOERROR;
470 }
471
472 /**************************************************************************
473 *                               MIX_GetNumDevs                  [internal]
474 */
475 static DWORD MIX_GetNumDevs(void)
476 {
477     TRACE("()\n");
478     return numMixers;
479 }
480
481 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize)
482 {
483     TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps);
484
485     if (lpCaps == NULL) {
486         WARN("Invalid Parameter\n");
487         return MMSYSERR_INVALPARAM;
488     }
489
490     if (wDevID >= numMixers) {
491         WARN("bad device ID : %d\n", wDevID);
492         return MMSYSERR_BADDEVICEID;
493     }
494     memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps)));
495     return MMSYSERR_NOERROR;
496 }
497
498 /**************************************************************************
499 *                               MIX_GetLineInfo                 [internal]
500 */
501 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo)
502 {
503     int i;
504     DWORD ret = MMSYSERR_ERROR;
505     MixerLine *line = NULL;
506
507     TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo);
508
509     if (lpMl == NULL) {
510         WARN("invalid parameter: lpMl = NULL\n");
511         return MMSYSERR_INVALPARAM;
512     }
513
514     if (lpMl->cbStruct != sizeof(*lpMl)) {
515         WARN("invalid parameter: lpMl->cbStruct\n");
516         return MMSYSERR_INVALPARAM;
517     }
518
519     if (wDevID >= numMixers) {
520         WARN("bad device ID: %04X\n", wDevID);
521         return MMSYSERR_BADDEVICEID;
522     }
523
524     /* FIXME: set all the variables correctly... the lines below
525         * are very wrong...
526         */
527     lpMl->dwUser = 0;
528
529     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
530     {
531         case MIXER_GETLINEINFOF_DESTINATION:
532             TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination);
533             if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) )
534             {
535                 lpMl->dwLineID = lpMl->dwDestination;
536                 line = &mixer.lines[lpMl->dwDestination];
537             }
538             else ret = MIXERR_INVALLINE;
539             break;
540         case MIXER_GETLINEINFOF_COMPONENTTYPE:
541             TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType));
542             for (i = 0; i < mixer.caps.cDestinations; i++)
543             {
544                 if (mixer.lines[i].componentType == lpMl->dwComponentType)
545                 {
546                     lpMl->dwDestination = lpMl->dwLineID = i;
547                     line = &mixer.lines[i];
548                     break;
549                 }
550             }
551             if (line == NULL)
552             {
553                 WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType));
554                 ret = MIXERR_INVALVALUE;
555             }
556             break;
557         case MIXER_GETLINEINFOF_SOURCE:
558             FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination);
559             break;
560         case MIXER_GETLINEINFOF_LINEID:
561             TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID);
562             if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) )
563             {
564                 lpMl->dwDestination = lpMl->dwLineID;
565                 line = &mixer.lines[lpMl->dwLineID];
566             }
567             else ret = MIXERR_INVALLINE;
568             break;
569         case MIXER_GETLINEINFOF_TARGETTYPE:
570             FIXME("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", getTargetType(lpMl->Target.dwType));
571             switch (lpMl->Target.dwType) {
572                 case MIXERLINE_TARGETTYPE_UNDEFINED:
573                 case MIXERLINE_TARGETTYPE_WAVEOUT:
574                 case MIXERLINE_TARGETTYPE_WAVEIN:
575                 case MIXERLINE_TARGETTYPE_MIDIOUT:
576                 case MIXERLINE_TARGETTYPE_MIDIIN:
577                 case MIXERLINE_TARGETTYPE_AUX:
578                 default:
579                     FIXME("Unhandled target type (%s)\n",
580                           getTargetType(lpMl->Target.dwType));
581                     return MMSYSERR_INVALPARAM;
582             }
583                 break;
584         default:
585             WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
586             break;
587     }
588
589     if (line)
590     {
591         lpMl->dwComponentType = line->componentType;
592         lpMl->cChannels = line->numChannels;
593         lpMl->cControls = ControlsPerLine;
594
595         /* FIXME check there with CoreAudio */
596         lpMl->cConnections = 1;
597         lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
598
599         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
600         MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR));
601
602         if ( IsInput(line->direction) )
603             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
604         else
605             lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
606
607         lpMl->Target.dwDeviceID = line->deviceID;
608         lpMl->Target.wMid = mixer.caps.wMid;
609         lpMl->Target.wPid = mixer.caps.wPid;
610         lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion;
611
612         MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
613         ret = MMSYSERR_NOERROR;
614     }
615     return ret;
616 }
617
618 /**************************************************************************
619 *                               MIX_GetLineControls             [internal]
620 */
621 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags)
622 {
623     DWORD ret = MMSYSERR_NOTENABLED;
624     int ctrl = 0;
625     TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags);
626
627     if (lpMlc == NULL) {
628         WARN("invalid parameter: lpMlc == NULL\n");
629         return MMSYSERR_INVALPARAM;
630     }
631
632     if (lpMlc->cbStruct < sizeof(*lpMlc)) {
633         WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct);
634         return MMSYSERR_INVALPARAM;
635     }
636
637     if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
638         WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl);
639         return MMSYSERR_INVALPARAM;
640     }
641
642     if (wDevID >= numMixers) {
643         WARN("bad device ID: %04X\n", wDevID);
644         return MMSYSERR_BADDEVICEID;
645     }
646
647     switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
648     {
649         case MIXER_GETLINECONTROLSF_ALL:
650             FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls);
651             if (lpMlc->cControls != ControlsPerLine)
652             {
653                 WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls);
654                 ret = MMSYSERR_INVALPARAM;
655             }
656             else
657             {
658                 if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) )
659                 {
660                     int i;
661                     for (i = 0; i < lpMlc->cControls; i++)
662                     {
663                         lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl;
664                     }
665                     ret = MMSYSERR_NOERROR;
666                 }
667                 else ret = MIXERR_INVALLINE;
668             }
669             break;
670         case MIXER_GETLINECONTROLSF_ONEBYID:
671             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
672             if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl )
673             {
674                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl;
675                 ret = MMSYSERR_NOERROR;
676             }
677             else ret = MIXERR_INVALVALUE;
678             break;
679         case MIXER_GETLINECONTROLSF_ONEBYTYPE:
680             TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
681             if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
682             {
683                 ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume;
684                 lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
685                 ret = MMSYSERR_NOERROR;
686             }
687             else
688                 if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
689                 {
690                     ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute;
691                     lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl;
692                     ret = MMSYSERR_NOERROR;
693                 }
694                 break;
695         default:
696             ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
697             ret = MMSYSERR_INVALPARAM;
698     }
699
700     return ret;
701 }
702
703 /**************************************************************************
704 *                               mxdMessage
705 */
706 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
707                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
708 {
709     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
710           dwUser, dwParam1, dwParam2);
711
712     switch (wMsg)
713     {
714         case DRVM_INIT:
715         case DRVM_EXIT:
716         case DRVM_ENABLE:
717         case DRVM_DISABLE:
718             /* FIXME: Pretend this is supported */
719             return 0;
720         case MXDM_OPEN:
721             return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
722         case MXDM_CLOSE:
723             return MMSYSERR_NOERROR;
724         case MXDM_GETNUMDEVS:
725             return MIX_GetNumDevs();
726         case MXDM_GETDEVCAPS:
727             return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
728         case MXDM_GETLINEINFO:
729             return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
730         case MXDM_GETLINECONTROLS:
731             return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
732         case MXDM_GETCONTROLDETAILS:
733         case MXDM_SETCONTROLDETAILS:
734         default:
735             WARN("unknown message %d!\n", wMsg);
736             return MMSYSERR_NOTSUPPORTED;
737     }
738 }
739
740 #else
741
742 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
743                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
744 {
745     TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
746     return MMSYSERR_NOTENABLED;
747 }
748 #endif /* HAVE_COREAUDIO_COREAUDIO_H */