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 
  34 extern "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 
  55 static 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
  61 struct 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)
  70 struct 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
  86 struct PortLine {
  87     AudioObjectPropertyScope scope;
  88     // if the device has several AudioStreams in the scope, streamID == 0
  89     AudioStreamID streamID;
  90 };
  91 
  92 struct 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 
 107 void RemoveChangeListeners(PortMixer *mixer);   // forward declaration
 108 
 109 OSStatus 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 
 188 const AudioObjectPropertyAddress changeListenersAddresses[] = {
 189     {kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
 190     {kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
 191     {kAudioDevicePropertyDeviceHasChanged, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}
 192 };
 193 
 194 void 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 
 203 void 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 
 216 INT32 PORT_GetPortMixerCount() {
 217     deviceCache.Refresh();
 218     int count = deviceCache.GetCount();
 219     TRACE1("<<PORT_GetPortMixerCount = %d\n", count);
 220     return count;
 221 }
 222 
 223 INT32 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 
 229 void* 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 
 270 void 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 
 293 INT32 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 
 302 INT32 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 
 359 INT32 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
 402 static 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
 413 static 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 
 450 void 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 
 648 bool 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 
 659 INT32 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 
 692 void 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
 720 static 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
 739 static 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 
 755 float 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 
 800 void 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