winecoreaudio: Implement MIX_Open and MIX_GetNumDevs.
[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 /**************************************************************************
482 *                               mxdMessage
483 */
484 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
485                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
486 {
487     TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
488           dwUser, dwParam1, dwParam2);
489
490     switch (wMsg)
491     {
492         case DRVM_INIT:
493         case DRVM_EXIT:
494         case DRVM_ENABLE:
495         case DRVM_DISABLE:
496             /* FIXME: Pretend this is supported */
497             return 0;
498         case MXDM_OPEN:
499             return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
500         case MXDM_CLOSE:
501             return MMSYSERR_NOERROR;
502         case MXDM_GETNUMDEVS:
503             return MIX_GetNumDevs();
504         case MXDM_GETDEVCAPS:
505         case MXDM_GETLINEINFO:
506         case MXDM_GETLINECONTROLS:
507         case MXDM_GETCONTROLDETAILS:
508         case MXDM_SETCONTROLDETAILS:
509         default:
510             WARN("unknown message %d!\n", wMsg);
511             return MMSYSERR_NOTSUPPORTED;
512     }
513 }
514
515 #else
516
517 DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
518                                   DWORD_PTR dwParam1, DWORD_PTR dwParam2)
519 {
520     TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
521     return MMSYSERR_NOTENABLED;
522 }
523 #endif /* HAVE_COREAUDIO_COREAUDIO_H */