1/*
2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26//#define USE_ERROR
27//#define USE_TRACE
28
29#include <CoreAudio/CoreAudio.h>
30#include <IOKit/audio/IOAudioTypes.h>
31
32#include "PLATFORM_API_MacOSX_Utils.h"
33
34extern "C" {
35#include "Ports.h"
36}
37
38#if USE_PORTS == TRUE
39
40/* If a device has the only AudioStream in the scope (input or output),
41 * PortMixer provides a single Port, using the stream kAudioStreamPropertyTerminalType
42 * property value to determine Port.Type (PORT_GetPortType function).
43 * If the device has several (more than 1) AudioStreams, there are 2 ways to represent Ports:
44 * 1. (HALLab-style) single Port which represents all device channels with
45 *    "master volume" and (if number of channel is 2) "master balance"; if AudioDevice
46 *    does not provide "master" controls, implement "virtual master" controls.
47 *    Port.Type is PORT_SRC_UNKNOWN or PORT_DST_UNKNOWN.
48 * 2. provide a separate Port for every AudioStream (with appropriate Port.Type);
49 *
50 * AudioHardware.h claims that AudioStream objects share AudioControl objects with their owning AudioDevice.
51 * In practice 10.7 OSX drivers (built-in devices, USB audio) implement AudioControl only for AudioDevice.
52 * For now 1st way is implemented (2nd way can be better if AudioStreams provide AudioControls).
53 */
54
55static DeviceList deviceCache;
56
57#define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
58
59
60// CoreAudio's AudioControl
61struct AudioControl {
62    AudioObjectID controlID;
63    AudioClassID classID;               // kAudioVolumeControlClassID etc.
64    AudioObjectPropertyScope scope;     // input, output
65    AudioObjectPropertyElement channel; // master = 0, channels = 1 2 ...
66};
67
68// Controls for Java
69// PortMixer do all memory management (alloc/free audioControls)
70struct PortControl {
71    enum ControlType {
72        Volume,     // manages single or multiple volume AudioControl
73        Mute,       // manages single or multiple mute AudioControls
74        Balance     // "virtual" control, manages 2 volume AudioControls (only for stereo lines)
75    };
76    ControlType type;
77
78    int controlCount;
79    AudioControl **audioControls;
80
81    PortControl *next;  // to organize PortControl list
82};
83
84// represents line (port) for PortMixer
85// used for PORT_GetPortCount/PORT_GetPortType/PORT_GetPortName functions
86struct PortLine {
87    AudioObjectPropertyScope scope;
88    // if the device has several AudioStreams in the scope, streamID == 0
89    AudioStreamID streamID;
90};
91
92struct PortMixer {
93    AudioDeviceID deviceID;
94
95    int portCount;
96    PortLine ports[2]; // maximum 2 lines - 1 for input & 1 for output
97
98    int deviceControlCount; // -1 means "not initialized"
99    AudioControl *deviceControls;
100
101    PortControl *portControls;  // list of port controls
102
103    bool listenersInstalled;
104};
105
106
107void RemoveChangeListeners(PortMixer *mixer);   // forward declaration
108
109OSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses,
110        const AudioObjectPropertyAddress inAddresses[], void *inClientData)
111{
112    PortMixer *mixer = (PortMixer *)inClientData;
113
114    OSStatus err = noErr;
115    UInt32 size;
116
117    bool invalid = false;
118
119    for (UInt32 i = 0; i < inNumberAddresses; i++) {
120        switch (inAddresses[i].mSelector) {
121        case kAudioHardwarePropertyDevices:
122            // check if the device has been removed
123            err = GetAudioObjectPropertySize(kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal,
124                kAudioHardwarePropertyDevices, &size);
125            if (err == noErr) {
126                int count = size/sizeof(AudioDeviceID);
127                AudioDeviceID devices[count];
128                err = GetAudioObjectProperty(kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal,
129                    kAudioHardwarePropertyDevices, count*sizeof(AudioDeviceID), devices, 1);
130                if (err == noErr) {
131                    bool found = false;
132                    for (int j = 0; j < count; j++) {
133                        if (devices[j] == mixer->deviceID) {
134                            found = true;
135                            break;
136                        }
137                    }
138                    if (!found) {
139                        invalid = true;
140                    }
141                }
142            }
143            break;
144        case kAudioObjectPropertyOwnedObjects:
145        case kAudioDevicePropertyDeviceHasChanged:
146            // ensure all _used_ AudioControl are valid
147            err = GetAudioObjectPropertySize(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
148                kAudioObjectPropertyOwnedObjects, &size);
149            if (err == noErr) {
150                int count = size / sizeof(AudioObjectID);
151                AudioObjectID controlIDs[count];
152                err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
153                    kAudioObjectPropertyOwnedObjects, count * sizeof(AudioObjectID), &controlIDs, 1);
154                if (err == noErr) {
155                    for (PortControl *ctrl = mixer->portControls; ctrl != NULL; ctrl = ctrl->next) {
156                        for (int i = 0; i < ctrl->controlCount; i++) {
157                            bool found = false;
158                            for (int j = 0; j < count; j++) {
159                                if (ctrl->audioControls[i]->controlID == controlIDs[j]) {
160                                    found = true;
161                                    break;
162                                }
163                            }
164                            if (!found) {
165                                invalid = true;
166                                break;  // goto next control
167                            }
168                        }
169                    }
170                }
171            }
172        }
173    }
174
175    if (invalid) {
176        TRACE1("PortMixer (deviceID=0x%x) becomes invalid", (int)mixer->deviceID);
177        // invalidate all controls
178        for (int i=0; i<mixer->deviceControlCount; i++) {
179            mixer->deviceControls[i].controlID = 0;
180        }
181        RemoveChangeListeners(mixer);
182    }
183
184
185    return noErr;
186}
187
188const AudioObjectPropertyAddress changeListenersAddresses[] = {
189    {kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
190    {kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
191    {kAudioDevicePropertyDeviceHasChanged, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}
192};
193
194void AddChangeListeners(PortMixer *mixer) {
195    if (!mixer->listenersInstalled) {
196        for (size_t i=0; i<sizeof(changeListenersAddresses)/sizeof(changeListenersAddresses[0]); i++) {
197            AudioObjectAddPropertyListener(mixer->deviceID, &changeListenersAddresses[i], ChangeListenerProc, mixer);
198        }
199        mixer->listenersInstalled = true;
200    }
201}
202
203void RemoveChangeListeners(PortMixer *mixer) {
204    if (mixer->listenersInstalled) {
205        for (size_t i=0; i<sizeof(changeListenersAddresses)/sizeof(changeListenersAddresses[0]); i++) {
206            AudioObjectRemovePropertyListener(mixer->deviceID, &changeListenersAddresses[i], ChangeListenerProc, mixer);
207        }
208        mixer->listenersInstalled = false;
209    }
210}
211
212
213////////////////////////////////////////////////////////////////////////////////
214// functions from Port.h
215
216INT32 PORT_GetPortMixerCount() {
217    deviceCache.Refresh();
218    int count = deviceCache.GetCount();
219    TRACE1("<<PORT_GetPortMixerCount = %d\n", count);
220    return count;
221}
222
223INT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* mixerDescription) {
224    bool result = deviceCache.GetDeviceInfo(mixerIndex, NULL, PORT_STRING_LENGTH,
225            mixerDescription->name, mixerDescription->vendor, mixerDescription->description, mixerDescription->version);
226    return result ? TRUE : FALSE;
227}
228
229void* PORT_Open(INT32 mixerIndex) {
230    TRACE1("\n>>PORT_Open (mixerIndex=%d)\n", (int)mixerIndex);
231    PortMixer *mixer = (PortMixer *)calloc(1, sizeof(PortMixer));
232
233    mixer->deviceID = deviceCache.GetDeviceID(mixerIndex);
234    if (mixer->deviceID != 0) {
235        mixer->deviceControlCount = -1; // not initialized
236        // fill mixer->ports (and mixer->portCount)
237        for (int i=0; i<2; i++) {
238            OSStatus err;
239            UInt32 size = 0;
240            AudioObjectPropertyScope scope =
241                (i == 0) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
242
243            err = GetAudioObjectPropertySize(mixer->deviceID, scope, kAudioDevicePropertyStreams, &size);
244            if (err || size == 0) {
245                continue;
246            }
247            if (size / sizeof(AudioStreamID) == 1) {
248                // the device has the only AudioStream
249                AudioStreamID streamID;
250                err = GetAudioObjectProperty(mixer->deviceID, scope, kAudioDevicePropertyStreams,
251                    sizeof(streamID), &streamID, 1);
252                if (err) {
253                    continue;
254                }
255                mixer->ports[mixer->portCount].streamID = streamID;
256            } else {
257                // the device has several AudioStreams in the scope
258                mixer->ports[mixer->portCount].streamID = 0;
259            }
260            mixer->ports[mixer->portCount].scope = scope;
261            mixer->portCount++;
262        }
263    }
264
265    TRACE2("<<PORT_Open (mixerIndex=%d) %p\n", mixerIndex, mixer);
266    return mixer;
267}
268
269
270void PORT_Close(void* id) {
271    TRACE1(">>PORT_Close %p\n", id);
272    PortMixer *mixer = (PortMixer *)id;
273
274    if (mixer) {
275        RemoveChangeListeners(mixer);
276        while (mixer->portControls != NULL) {
277            PortControl *control2delete = mixer->portControls;
278            mixer->portControls = control2delete->next;
279
280            if (control2delete->audioControls != NULL) {
281                free(control2delete->audioControls);
282            }
283            free(control2delete);
284        }
285        if (mixer->deviceControls) {
286            free(mixer->deviceControls);
287        }
288        free(mixer);
289    }
290    TRACE1("<<PORT_Close %p\n", mixer);
291}
292
293INT32 PORT_GetPortCount(void* id) {
294    PortMixer *mixer = (PortMixer *)id;
295
296    int result = mixer->portCount;
297
298    TRACE1("<<PORT_GetPortCount = %d\n", result);
299    return result;
300}
301
302INT32 PORT_GetPortType(void* id, INT32 portIndex) {
303    PortMixer *mixer = (PortMixer *)id;
304    INT32 ret = 0;
305
306    if (portIndex < 0 || portIndex >= mixer->portCount) {
307        ERROR1("PORT_GetPortType: line (portIndex = %d) not found\n", portIndex);
308        return 0;
309    }
310
311    AudioObjectPropertyScope scope = mixer->ports[portIndex].scope;
312    AudioStreamID streamID = mixer->ports[portIndex].streamID;
313    if (streamID != 0) {
314        UInt32 terminalType;
315
316        OSStatus err = GetAudioObjectProperty(streamID, kAudioObjectPropertyScopeGlobal,
317            kAudioStreamPropertyTerminalType, sizeof(terminalType), &terminalType, 1);
318        if (err) {
319            OS_ERROR1(err, "PORT_GetPortType(kAudioStreamPropertyTerminalType), portIndex=%d", portIndex);
320            return 0;
321        }
322
323        // Note that kAudioStreamPropertyTerminalType actually returns values from
324        // IOAudioTypes.h, not the defined kAudioStreamTerminalType*.
325        TRACE4("PORT_GetPortType (portIndex=%d), scope=%s, termType=0x%04x (%s)\n",
326            (int)portIndex, FourCC2Str(scope), (int)terminalType, FourCC2Str(terminalType));
327        switch (terminalType) {
328        case INPUT_MICROPHONE:
329            ret = PORT_SRC_MICROPHONE;
330            break;
331
332        case OUTPUT_SPEAKER:
333            ret = PORT_DST_SPEAKER;
334            break;
335        case OUTPUT_HEADPHONES:
336            ret = PORT_DST_HEADPHONE;
337            break;
338
339        case EXTERNAL_LINE_CONNECTOR:
340            ret = scope == kAudioDevicePropertyScopeInput ? PORT_SRC_LINE_IN : PORT_DST_LINE_OUT;
341            break;
342
343        default:
344            TRACE1("  unknown output terminal type %#x\n", terminalType);
345        }
346    } else {
347        TRACE0("  PORT_GetPortType: multiple streams\n");
348    }
349
350    if (ret == 0) {
351        // if the type not detected, return "common type"
352        ret = scope == kAudioDevicePropertyScopeInput ? PORT_SRC_UNKNOWN : PORT_DST_UNKNOWN;
353    }
354
355    TRACE2("<<PORT_GetPortType (portIndex=%d) = %d\n", portIndex, ret);
356    return ret;
357}
358
359INT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {
360    PortMixer *mixer = (PortMixer *)id;
361
362    name[0] = 0;    // for safety
363
364    if (portIndex < 0 || portIndex >= mixer->portCount) {
365        ERROR1("PORT_GetPortName: line (portIndex = %d) not found\n", portIndex);
366        return FALSE;
367    }
368
369    AudioStreamID streamID = mixer->ports[portIndex].streamID;
370    CFStringRef cfname = NULL;
371    if (streamID != 0) {
372        OSStatus err = GetAudioObjectProperty(streamID, kAudioObjectPropertyScopeGlobal,
373            kAudioObjectPropertyName, sizeof(cfname), &cfname, 1);
374        if (err && err != kAudioHardwareUnknownPropertyError) {
375            OS_ERROR1(err, "PORT_GetPortName(stream name), portIndex=%d", portIndex);
376            return FALSE;
377        }
378    }
379
380    if (!cfname) {
381        // use the device's name if the stream has no name (usually the case)
382        // or the device has several AudioStreams
383        OSStatus err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
384            kAudioObjectPropertyName, sizeof(cfname), &cfname, 1);
385        if (err) {
386            OS_ERROR1(err, "PORT_GetPortName(device name), portIndex=%d", portIndex);
387            return FALSE;
388        }
389    }
390
391    if (cfname) {
392        CFStringGetCString(cfname, name, len, kCFStringEncodingUTF8);
393        CFRelease(cfname);
394    }
395
396    TRACE2("<<PORT_GetPortName (portIndex = %d) = %s\n", portIndex, name);
397    return TRUE;
398}
399
400
401// counts number of valid (non-NULL) elements in the array of AudioControls
402static int ValidControlCount(AudioControl **arr, int offset, int len) {
403    int result = 0;
404    int end = offset + len;
405    for (int i=offset; i<end; i++) {
406        if (arr[i] != NULL)
407            result++;
408    }
409    return result;
410}
411
412// returns java control
413static void* CreatePortControl(PortMixer *mixer, PortControlCreator *creator, PortControl::ControlType type,
414                               AudioControl **audioControls, int offset, int len) {
415    void *jControl = NULL;
416    PortControl *control = (PortControl *)calloc(1, sizeof(PortControl));
417    float precision = 0.01;
418
419    control->type = type;
420    control->controlCount = len;
421    control->audioControls = (AudioControl **)malloc(len * sizeof(AudioControl *));
422    memcpy(control->audioControls, audioControls + offset, len * sizeof(AudioControl *));
423
424    switch (control->type) {
425    case PortControl::Volume:
426        jControl = creator->newFloatControl(creator, control, CONTROL_TYPE_VOLUME, 0, 1, precision, "");
427        break;
428    case PortControl::Mute:
429        jControl = creator->newBooleanControl(creator, control, CONTROL_TYPE_MUTE);
430        break;
431    case PortControl::Balance:
432        jControl = creator->newFloatControl(creator, control, CONTROL_TYPE_BALANCE, -1, 1, precision, "");
433        break;
434    };
435
436    if (jControl == NULL) {
437        ERROR0("CreatePortControl: javaControl was not created\n");
438        free(control->audioControls);
439        free(control);
440        return NULL;
441    }
442
443    // add the control to mixer control list;
444    control->next = mixer->portControls;
445    mixer->portControls = control;
446
447    return jControl;
448}
449
450void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
451    PortMixer *mixer = (PortMixer *)id;
452
453    TRACE1(">>PORT_GetControls (portIndex = %d)\n", portIndex);
454
455    if (portIndex < 0 || portIndex >= mixer->portCount) {
456        ERROR1("<<PORT_GetControls: line (portIndex = %d) not found\n", portIndex);
457        return;
458    }
459
460    PortLine *port = &(mixer->ports[portIndex]);
461
462    if (mixer->deviceControlCount < 0) {    // not initialized
463        OSStatus err;
464        UInt32 size;
465        // deviceControlCount is overestimated
466        // because we don't actually filter by if the owned objects are controls
467        err = GetAudioObjectPropertySize(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
468            kAudioObjectPropertyOwnedObjects, &size);
469
470        if (err) {
471            OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject size", portIndex);
472        } else {
473            mixer->deviceControlCount = size / sizeof(AudioObjectID);
474            TRACE1("  PORT_GetControls: detected %d owned objects\n", mixer->deviceControlCount);
475
476            AudioObjectID controlIDs[mixer->deviceControlCount];
477
478            err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
479                kAudioObjectPropertyOwnedObjects, sizeof(controlIDs), controlIDs, 1);
480
481            if (err) {
482                OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject values", portIndex);
483            } else {
484                mixer->deviceControls = (AudioControl *)calloc(mixer->deviceControlCount, sizeof(AudioControl));
485
486                for (int i = 0; i < mixer->deviceControlCount; i++) {
487                    AudioControl *control = &mixer->deviceControls[i];
488
489                    control->controlID = controlIDs[i];
490
491                    OSStatus err1 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
492                        kAudioObjectPropertyClass, sizeof(control->classID), &control->classID, 1);
493                    OSStatus err2 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
494                        kAudioControlPropertyScope, sizeof(control->scope), &control->scope, 1);
495                    OSStatus err3 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
496                        kAudioControlPropertyElement, sizeof(control->channel), &control->channel, 1);
497                    if (err1 || err2 || err3) { // not a control or other error
498                        control->classID = 0;
499                        continue;
500                    }
501
502                    TRACE4("- control 0x%x, class='%s', scope='%s', channel=%d\n",
503                        control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
504                }
505            }
506        }
507    }
508
509    if (mixer->deviceControlCount <= 0) {
510        TRACE1("<<PORT_GetControls (portIndex = %d): no owned AudioControls\n", portIndex);
511        return;
512    }
513
514    int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0);
515
516    // collect volume and mute controls
517    AudioControl* volumeControls[totalChannels+1];  // 0 - for master channel
518    memset(&volumeControls, 0, sizeof(AudioControl *) * (totalChannels+1));
519    AudioControl* muteControls[totalChannels+1];  // 0 - for master channel
520    memset(&muteControls, 0, sizeof(AudioControl *) * (totalChannels+1));
521
522    for (int i=0; i<mixer->deviceControlCount; i++) {
523        AudioControl *control = &mixer->deviceControls[i];
524        if (control->classID == 0 || control->scope != port->scope || control->channel > (unsigned)totalChannels) {
525            continue;
526        }
527        if (control->classID == kAudioVolumeControlClassID) {
528            if (volumeControls[control->channel] == NULL) {
529                volumeControls[control->channel] = control;
530            } else {
531                ERROR4("WARNING: duplicate VOLUME control 0x%x, class='%s', scope='%s', channel=%d\n",
532                    control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
533            }
534        } else if (control->classID == kAudioMuteControlClassID) {
535            if (muteControls[control->channel] == NULL) {
536                muteControls[control->channel] = control;
537            } else {
538                ERROR4("WARNING: duplicate MUTE control 0x%x, class='%s', scope='%s', channel=%d\n",
539                    control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
540            }
541        } else {
542#ifdef USE_ERROR
543            if (control->classID != 0) {
544                ERROR4("WARNING: unhandled control 0x%x, class='%s', scope='%s', channel=%d\n",
545                    control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
546            }
547#endif
548        }
549    }
550
551    ////////////////////////////////////////////////////////
552    // create java control hierarchy
553
554    void *masterVolume = NULL, *masterMute = NULL, *masterBalance = NULL;
555    // volumeControls[0] and muteControls[0] - master volume/mute
556    // volumeControls[n] and muteControls[n] (n=1..totalChannels) - corresponding channel controls
557    if (volumeControls[0] != NULL) {    // "master volume" AudioControl
558        masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 0, 1);
559    } else {
560        if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
561            // every channel has volume control => create virtual master volume
562            masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 1, totalChannels);
563        } else {
564            TRACE2("  PORT_GetControls (master volume): totalChannels = %d, valid volume controls = %d\n",
565                totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
566        }
567    }
568
569    if (muteControls[0] != NULL) {      // "master mute"
570        masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 0, 1);
571    } else {
572        if (ValidControlCount(muteControls, 1, totalChannels) == totalChannels) {
573            // every channel has mute control => create virtual master mute control
574            masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 1, totalChannels);
575        } else {
576            TRACE2("  PORT_GetControls (master mute): totalChannels = %d, valid volume controls = %d\n",
577                totalChannels, ValidControlCount(muteControls, 1, totalChannels));
578        }
579    }
580
581    // virtual balance
582    if (totalChannels == 2) {
583        if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
584            masterBalance = CreatePortControl(mixer, creator, PortControl::Balance, volumeControls, 1, totalChannels);
585        } else {
586            TRACE2("  PORT_GetControls (naster balance): totalChannels = %d, valid volume controls = %d\n",
587                totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
588        }
589    }
590
591    // add "master" controls
592    if (masterVolume != NULL) {
593        creator->addControl(creator, masterVolume);
594    }
595    if (masterBalance != NULL) {
596        creator->addControl(creator, masterBalance);
597    }
598    if (masterMute != NULL) {
599        creator->addControl(creator, masterMute);
600    }
601
602    // don't add per-channel controls for mono & stereo - they are handled by "master" controls
603    // TODO: this should be reviewed to handle controls other than mute & volume
604    if (totalChannels > 2) {
605        // add separate compound control for each channel (containing volume and mute)
606        // (ensure that we have controls)
607        if (ValidControlCount(volumeControls, 1, totalChannels) > 0 || ValidControlCount(muteControls, 1, totalChannels) > 0) {
608            for (int ch=1; ch<=totalChannels; ch++) {
609                // get the channel name
610                char *channelName;
611                CFStringRef cfname = NULL;
612                const AudioObjectPropertyAddress address = {kAudioObjectPropertyElementName, port->scope, ch};
613                UInt32 size = sizeof(cfname);
614                OSStatus err = AudioObjectGetPropertyData(mixer->deviceID, &address, 0, NULL, &size, &cfname);
615                if (err == noErr) {
616                    CFIndex length = CFStringGetLength(cfname) + 1;
617                    channelName = (char *)malloc(length);
618                    CFStringGetCString(cfname, channelName, length, kCFStringEncodingUTF8);
619                    CFRelease(cfname);
620                } else {
621                    channelName = (char *)malloc(16);
622                    sprintf(channelName, "Ch %d", ch);
623                }
624
625                void* jControls[2];
626                int controlCount = 0;
627                if (volumeControls[ch] != NULL) {
628                    jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, ch, 1);
629                }
630                if (muteControls[ch] != NULL) {
631                    jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, ch, 1);
632                }
633                // TODO: add any extra controls for "other" controls for the channel
634
635                void *compoundControl = creator->newCompoundControl(creator, channelName, jControls, controlCount);
636                creator->addControl(creator, compoundControl);
637
638                free(channelName);
639            }
640        }
641    }
642
643    AddChangeListeners(mixer);
644
645    TRACE1("<<PORT_GetControls (portIndex = %d)\n", portIndex);
646}
647
648bool TestPortControlValidity(PortControl *control) {
649    for (int i=0; i<control->controlCount; i++) {
650        if (control->audioControls[i]->controlID == 0)
651            return false;
652    }
653    return true;
654}
655
656
657#define DEFAULT_MUTE_VALUE 0
658
659INT32 PORT_GetIntValue(void* controlIDV) {
660    PortControl *control = (PortControl *)controlIDV;
661    INT32 result = 0;
662
663    switch (control->type) {
664    case PortControl::Mute:
665        if (!TestPortControlValidity(control)) {
666            return DEFAULT_MUTE_VALUE;
667        }
668        result = 1; // default is "muted", if some channel in unmuted, then "virtual mute" is also unmuted
669        for (int i=0; i<control->controlCount; i++) {
670            UInt32 value;
671            OSStatus err = GetAudioObjectProperty(control->audioControls[i]->controlID,
672                kAudioObjectPropertyScopeGlobal, kAudioBooleanControlPropertyValue, sizeof(value), &value, 1);
673            if (err) {
674                OS_ERROR3(err, "PORT_GetIntValue, control %d of %d (coltrolID = 0x%x)",
675                    i, control->controlCount, control->audioControls[i]->controlID);
676                return DEFAULT_MUTE_VALUE;
677            }
678            if (value == 0) {
679                result = 0;
680            }
681        }
682        break;
683    default:
684        ERROR1("PORT_GetIntValue requested for non-Int control (control-type == %d)\n", control->type);
685        return 0;
686    }
687
688    //TRACE1("<<PORT_GetIntValue = %d\n", result);
689    return result;
690}
691
692void PORT_SetIntValue(void* controlIDV, INT32 value) {
693    //TRACE1("> PORT_SetIntValue = %d\n", value);
694    PortControl *control = (PortControl *)controlIDV;
695
696    if (!TestPortControlValidity(control)) {
697        return;
698    }
699
700    switch (control->type) {
701    case PortControl::Mute:
702        for (int i=0; i<control->controlCount; i++) {
703            OSStatus err = SetAudioObjectProperty(control->audioControls[i]->controlID,
704                kAudioObjectPropertyScopeGlobal, kAudioBooleanControlPropertyValue, sizeof(value), &value);
705            if (err) {
706                OS_ERROR3(err, "PORT_SetIntValue, control %d of %d (coltrolID = 0x%x)",
707                    i, control->controlCount, control->audioControls[i]->controlID);
708                // don't return - try to set the rest of AudioControls
709            }
710        }
711        break;
712    default:
713        ERROR1("PORT_SetIntValue requested for non-Int control (control-type == %d)\n", control->type);
714        return;
715    }
716}
717
718
719// gets volume value for all AudioControls of the PortControl
720static bool GetPortControlVolumes(PortControl *control, Float32 *volumes, Float32 *maxVolume) {
721    *maxVolume = 0.0f;
722    for (int i=0; i<control->controlCount; i++) {
723        OSStatus err = GetAudioObjectProperty(control->audioControls[i]->controlID,
724            kAudioObjectPropertyScopeGlobal, kAudioLevelControlPropertyScalarValue,
725            sizeof(volumes[i]), &volumes[i], 1);
726        if (err) {
727            OS_ERROR3(err, "GetPortControlVolumes, control %d of %d (controlID = 0x%x)",
728                i, control->controlCount, control->audioControls[i]->controlID);
729            return false;
730        }
731        if (volumes[i] > *maxVolume) {
732            *maxVolume = volumes[i];
733        }
734    }
735    return true;
736}
737
738// sets volume value for all AudioControls of the PortControl
739static void SetPortControlVolumes(PortControl *control, Float32 *volumes) {
740    for (int i=0; i<control->controlCount; i++) {
741        OSStatus err = SetAudioObjectProperty(control->audioControls[i]->controlID,
742            kAudioObjectPropertyScopeGlobal, kAudioLevelControlPropertyScalarValue,
743            sizeof(volumes[i]), &volumes[i]);
744        if (err) {
745            OS_ERROR3(err, "SetPortControlVolumes , control %d of %d (coltrolID = 0x%x)",
746                i, control->controlCount, control->audioControls[i]->controlID);
747            // don't return - try to set the rest of AudioControls
748        }
749    }
750}
751
752#define DEFAULT_VOLUME_VALUE    1.0f
753#define DEFAULT_BALANCE_VALUE   0.0f
754
755float PORT_GetFloatValue(void* controlIDV) {
756    PortControl *control = (PortControl *)controlIDV;
757    Float32 result = 0;
758
759    Float32 subVolumes[control->controlCount];
760    Float32 maxVolume;
761
762    switch (control->type) {
763    case PortControl::Volume:
764        if (!TestPortControlValidity(control)) {
765            return DEFAULT_VOLUME_VALUE;
766        }
767
768        if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
769            return DEFAULT_VOLUME_VALUE;
770        }
771        result = maxVolume;
772        break;
773    case PortControl::Balance:
774        if (!TestPortControlValidity(control)) {
775            return DEFAULT_BALANCE_VALUE;
776        }
777
778        // balance control always has 2 volume controls
779        if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
780            return DEFAULT_VOLUME_VALUE;
781        }
782        // calculate balance value
783        if (subVolumes[0] > subVolumes[1]) {
784            result = -1.0f + (subVolumes[1] / subVolumes[0]);
785        } else if (subVolumes[1] > subVolumes[0]) {
786            result = 1.0f - (subVolumes[0] / subVolumes[1]);
787        } else {
788            result = 0.0f;
789        }
790        break;
791    default:
792        ERROR1("GetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
793        return 0;
794    }
795
796    TRACE1("<<PORT_GetFloatValue = %f\n", result);
797    return result;
798}
799
800void PORT_SetFloatValue(void* controlIDV, float value) {
801    TRACE1("> PORT_SetFloatValue = %f\n", value);
802    PortControl *control = (PortControl *)controlIDV;
803
804    if (!TestPortControlValidity(control)) {
805        return;
806    }
807
808    Float32 subVolumes[control->controlCount];
809    Float32 maxVolume;
810
811    switch (control->type) {
812    case PortControl::Volume:
813        if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
814            return;
815        }
816        // update the values
817        if (maxVolume > 0.001) {
818            float multiplicator = value/maxVolume;
819            for (int i=0; i<control->controlCount; i++)
820                subVolumes[i] *= multiplicator;
821        } else {
822            // volume for all channels == 0, so set all channels to "value"
823            for (int i=0; i<control->controlCount; i++)
824                subVolumes[i] = value;
825        }
826        // set new values
827        SetPortControlVolumes(control, subVolumes);
828        break;
829    case PortControl::Balance:
830        // balance control always has 2 volume controls
831        if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
832            return;
833        }
834        // calculate new values
835        if (value < 0.0f) {
836            subVolumes[0] = maxVolume;
837            subVolumes[1] = maxVolume * (value + 1.0f);
838        } else {
839            // this case also handles value == 0
840            subVolumes[0] = maxVolume * (1.0f - value);
841            subVolumes[1] = maxVolume;
842        }
843        // set new values
844        SetPortControlVolumes(control, subVolumes);
845        break;
846    default:
847        ERROR1("PORT_SetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
848        return;
849    }
850}
851
852#endif // USE_PORTS
853