1 /*
   2  * Copyright (c) 2003, 2018, 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     if (control == NULL) {
 418         return NULL;
 419     }
 420     float precision = 0.01;
 421 
 422     control->type = type;
 423     control->controlCount = len;
 424     control->audioControls = (AudioControl **)malloc(len * sizeof(AudioControl *));
 425     if (control->audioControls == NULL) {
 426         free(control);
 427         return NULL;
 428     }
 429     memcpy(control->audioControls, audioControls + offset, len * sizeof(AudioControl *));
 430 
 431     switch (control->type) {
 432     case PortControl::Volume:
 433         jControl = creator->newFloatControl(creator, control, CONTROL_TYPE_VOLUME, 0, 1, precision, "");
 434         break;
 435     case PortControl::Mute:
 436         jControl = creator->newBooleanControl(creator, control, CONTROL_TYPE_MUTE);
 437         break;
 438     case PortControl::Balance:
 439         jControl = creator->newFloatControl(creator, control, CONTROL_TYPE_BALANCE, -1, 1, precision, "");
 440         break;
 441     };
 442 
 443     if (jControl == NULL) {
 444         ERROR0("CreatePortControl: javaControl was not created\n");
 445         free(control->audioControls);
 446         free(control);
 447         return NULL;
 448     }
 449 
 450     // add the control to mixer control list;
 451     control->next = mixer->portControls;
 452     mixer->portControls = control;
 453 
 454     return jControl;
 455 }
 456 
 457 void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
 458     PortMixer *mixer = (PortMixer *)id;
 459 
 460     TRACE1(">>PORT_GetControls (portIndex = %d)\n", portIndex);
 461 
 462     if (portIndex < 0 || portIndex >= mixer->portCount) {
 463         ERROR1("<<PORT_GetControls: line (portIndex = %d) not found\n", portIndex);
 464         return;
 465     }
 466 
 467     PortLine *port = &(mixer->ports[portIndex]);
 468 
 469     if (mixer->deviceControlCount < 0) {    // not initialized
 470         OSStatus err;
 471         UInt32 size;
 472         // deviceControlCount is overestimated
 473         // because we don't actually filter by if the owned objects are controls
 474         err = GetAudioObjectPropertySize(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
 475             kAudioObjectPropertyOwnedObjects, &size);
 476 
 477         if (err) {
 478             OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject size", portIndex);
 479         } else {
 480             mixer->deviceControlCount = size / sizeof(AudioObjectID);
 481             TRACE1("  PORT_GetControls: detected %d owned objects\n", mixer->deviceControlCount);
 482 
 483             AudioObjectID controlIDs[mixer->deviceControlCount];
 484 
 485             err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
 486                 kAudioObjectPropertyOwnedObjects, sizeof(controlIDs), controlIDs, 1);
 487 
 488             if (err) {
 489                 OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject values", portIndex);
 490             } else {
 491                 mixer->deviceControls = (AudioControl *)calloc(mixer->deviceControlCount, sizeof(AudioControl));
 492                 if (mixer->deviceControls == NULL) {
 493                     return;
 494                 }
 495 
 496                 for (int i = 0; i < mixer->deviceControlCount; i++) {
 497                     AudioControl *control = &mixer->deviceControls[i];
 498 
 499                     control->controlID = controlIDs[i];
 500 
 501                     OSStatus err1 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
 502                         kAudioObjectPropertyClass, sizeof(control->classID), &control->classID, 1);
 503                     OSStatus err2 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
 504                         kAudioControlPropertyScope, sizeof(control->scope), &control->scope, 1);
 505                     OSStatus err3 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
 506                         kAudioControlPropertyElement, sizeof(control->channel), &control->channel, 1);
 507                     if (err1 || err2 || err3) { // not a control or other error
 508                         control->classID = 0;
 509                         continue;
 510                     }
 511 
 512                     TRACE4("- control 0x%x, class='%s', scope='%s', channel=%d\n",
 513                         control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
 514                 }
 515             }
 516         }
 517     }
 518 
 519     if (mixer->deviceControlCount <= 0) {
 520         TRACE1("<<PORT_GetControls (portIndex = %d): no owned AudioControls\n", portIndex);
 521         return;
 522     }
 523 
 524     int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0);
 525 
 526     // collect volume and mute controls
 527     AudioControl* volumeControls[totalChannels+1];  // 0 - for master channel
 528     memset(&volumeControls, 0, sizeof(AudioControl *) * (totalChannels+1));
 529     AudioControl* muteControls[totalChannels+1];  // 0 - for master channel
 530     memset(&muteControls, 0, sizeof(AudioControl *) * (totalChannels+1));
 531 
 532     for (int i=0; i<mixer->deviceControlCount; i++) {
 533         AudioControl *control = &mixer->deviceControls[i];
 534         if (control->classID == 0 || control->scope != port->scope || control->channel > (unsigned)totalChannels) {
 535             continue;
 536         }
 537         if (control->classID == kAudioVolumeControlClassID) {
 538             if (volumeControls[control->channel] == NULL) {
 539                 volumeControls[control->channel] = control;
 540             } else {
 541                 ERROR4("WARNING: duplicate VOLUME control 0x%x, class='%s', scope='%s', channel=%d\n",
 542                     control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
 543             }
 544         } else if (control->classID == kAudioMuteControlClassID) {
 545             if (muteControls[control->channel] == NULL) {
 546                 muteControls[control->channel] = control;
 547             } else {
 548                 ERROR4("WARNING: duplicate MUTE control 0x%x, class='%s', scope='%s', channel=%d\n",
 549                     control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
 550             }
 551         } else {
 552 #ifdef USE_ERROR
 553             if (control->classID != 0) {
 554                 ERROR4("WARNING: unhandled control 0x%x, class='%s', scope='%s', channel=%d\n",
 555                     control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
 556             }
 557 #endif
 558         }
 559     }
 560 
 561     ////////////////////////////////////////////////////////
 562     // create java control hierarchy
 563 
 564     void *masterVolume = NULL, *masterMute = NULL, *masterBalance = NULL;
 565     // volumeControls[0] and muteControls[0] - master volume/mute
 566     // volumeControls[n] and muteControls[n] (n=1..totalChannels) - corresponding channel controls
 567     if (volumeControls[0] != NULL) {    // "master volume" AudioControl
 568         masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 0, 1);
 569     } else {
 570         if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
 571             // every channel has volume control => create virtual master volume
 572             masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 1, totalChannels);
 573         } else {
 574             TRACE2("  PORT_GetControls (master volume): totalChannels = %d, valid volume controls = %d\n",
 575                 totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
 576         }
 577     }
 578 
 579     if (muteControls[0] != NULL) {      // "master mute"
 580         masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 0, 1);
 581     } else {
 582         if (ValidControlCount(muteControls, 1, totalChannels) == totalChannels) {
 583             // every channel has mute control => create virtual master mute control
 584             masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 1, totalChannels);
 585         } else {
 586             TRACE2("  PORT_GetControls (master mute): totalChannels = %d, valid volume controls = %d\n",
 587                 totalChannels, ValidControlCount(muteControls, 1, totalChannels));
 588         }
 589     }
 590 
 591     // virtual balance
 592     if (totalChannels == 2) {
 593         if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
 594             masterBalance = CreatePortControl(mixer, creator, PortControl::Balance, volumeControls, 1, totalChannels);
 595         } else {
 596             TRACE2("  PORT_GetControls (naster balance): totalChannels = %d, valid volume controls = %d\n",
 597                 totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
 598         }
 599     }
 600 
 601     // add "master" controls
 602     if (masterVolume != NULL) {
 603         creator->addControl(creator, masterVolume);
 604     }
 605     if (masterBalance != NULL) {
 606         creator->addControl(creator, masterBalance);
 607     }
 608     if (masterMute != NULL) {
 609         creator->addControl(creator, masterMute);
 610     }
 611 
 612     // don't add per-channel controls for mono & stereo - they are handled by "master" controls
 613     // TODO: this should be reviewed to handle controls other than mute & volume
 614     if (totalChannels > 2) {
 615         // add separate compound control for each channel (containing volume and mute)
 616         // (ensure that we have controls)
 617         if (ValidControlCount(volumeControls, 1, totalChannels) > 0 || ValidControlCount(muteControls, 1, totalChannels) > 0) {
 618             for (int ch=1; ch<=totalChannels; ch++) {
 619                 // get the channel name
 620                 char *channelName;
 621                 CFStringRef cfname = NULL;
 622                 const AudioObjectPropertyAddress address = {kAudioObjectPropertyElementName, port->scope, (unsigned)ch};
 623                 UInt32 size = sizeof(cfname);
 624                 OSStatus err = AudioObjectGetPropertyData(mixer->deviceID, &address, 0, NULL, &size, &cfname);
 625                 if (err == noErr) {
 626                     CFIndex length = CFStringGetLength(cfname) + 1;
 627                     channelName = (char *)malloc(length);
 628                     if (channelName == NULL) {
 629                         return;
 630                     }
 631                     CFStringGetCString(cfname, channelName, length, kCFStringEncodingUTF8);
 632                     CFRelease(cfname);
 633                 } else {
 634                     channelName = (char *)malloc(16);
 635                     if (channelName == NULL) {
 636                         return;
 637                     }
 638                     sprintf(channelName, "Ch %d", ch);
 639                 }
 640 
 641                 void* jControls[2];
 642                 int controlCount = 0;
 643                 if (volumeControls[ch] != NULL) {
 644                     jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, ch, 1);
 645                 }
 646                 if (muteControls[ch] != NULL) {
 647                     jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, ch, 1);
 648                 }
 649                 // TODO: add any extra controls for "other" controls for the channel
 650 
 651                 void *compoundControl = creator->newCompoundControl(creator, channelName, jControls, controlCount);
 652                 creator->addControl(creator, compoundControl);
 653 
 654                 free(channelName);
 655             }
 656         }
 657     }
 658 
 659     AddChangeListeners(mixer);
 660 
 661     TRACE1("<<PORT_GetControls (portIndex = %d)\n", portIndex);
 662 }
 663 
 664 bool TestPortControlValidity(PortControl *control) {
 665     for (int i=0; i<control->controlCount; i++) {
 666         if (control->audioControls[i]->controlID == 0)
 667             return false;
 668     }
 669     return true;
 670 }
 671 
 672 
 673 #define DEFAULT_MUTE_VALUE 0
 674 
 675 INT32 PORT_GetIntValue(void* controlIDV) {
 676     PortControl *control = (PortControl *)controlIDV;
 677     INT32 result = 0;
 678 
 679     switch (control->type) {
 680     case PortControl::Mute:
 681         if (!TestPortControlValidity(control)) {
 682             return DEFAULT_MUTE_VALUE;
 683         }
 684         result = 1; // default is "muted", if some channel in unmuted, then "virtual mute" is also unmuted
 685         for (int i=0; i<control->controlCount; i++) {
 686             UInt32 value;
 687             OSStatus err = GetAudioObjectProperty(control->audioControls[i]->controlID,
 688                 kAudioObjectPropertyScopeGlobal, kAudioBooleanControlPropertyValue, sizeof(value), &value, 1);
 689             if (err) {
 690                 OS_ERROR3(err, "PORT_GetIntValue, control %d of %d (coltrolID = 0x%x)",
 691                     i, control->controlCount, control->audioControls[i]->controlID);
 692                 return DEFAULT_MUTE_VALUE;
 693             }
 694             if (value == 0) {
 695                 result = 0;
 696             }
 697         }
 698         break;
 699     default:
 700         ERROR1("PORT_GetIntValue requested for non-Int control (control-type == %d)\n", control->type);
 701         return 0;
 702     }
 703 
 704     //TRACE1("<<PORT_GetIntValue = %d\n", result);
 705     return result;
 706 }
 707 
 708 void PORT_SetIntValue(void* controlIDV, INT32 value) {
 709     //TRACE1("> PORT_SetIntValue = %d\n", value);
 710     PortControl *control = (PortControl *)controlIDV;
 711 
 712     if (!TestPortControlValidity(control)) {
 713         return;
 714     }
 715 
 716     switch (control->type) {
 717     case PortControl::Mute:
 718         for (int i=0; i<control->controlCount; i++) {
 719             OSStatus err = SetAudioObjectProperty(control->audioControls[i]->controlID,
 720                 kAudioObjectPropertyScopeGlobal, kAudioBooleanControlPropertyValue, sizeof(value), &value);
 721             if (err) {
 722                 OS_ERROR3(err, "PORT_SetIntValue, control %d of %d (coltrolID = 0x%x)",
 723                     i, control->controlCount, control->audioControls[i]->controlID);
 724                 // don't return - try to set the rest of AudioControls
 725             }
 726         }
 727         break;
 728     default:
 729         ERROR1("PORT_SetIntValue requested for non-Int control (control-type == %d)\n", control->type);
 730         return;
 731     }
 732 }
 733 
 734 
 735 // gets volume value for all AudioControls of the PortControl
 736 static bool GetPortControlVolumes(PortControl *control, Float32 *volumes, Float32 *maxVolume) {
 737     *maxVolume = 0.0f;
 738     for (int i=0; i<control->controlCount; i++) {
 739         OSStatus err = GetAudioObjectProperty(control->audioControls[i]->controlID,
 740             kAudioObjectPropertyScopeGlobal, kAudioLevelControlPropertyScalarValue,
 741             sizeof(volumes[i]), &volumes[i], 1);
 742         if (err) {
 743             OS_ERROR3(err, "GetPortControlVolumes, control %d of %d (controlID = 0x%x)",
 744                 i, control->controlCount, control->audioControls[i]->controlID);
 745             return false;
 746         }
 747         if (volumes[i] > *maxVolume) {
 748             *maxVolume = volumes[i];
 749         }
 750     }
 751     return true;
 752 }
 753 
 754 // sets volume value for all AudioControls of the PortControl
 755 static void SetPortControlVolumes(PortControl *control, Float32 *volumes) {
 756     for (int i=0; i<control->controlCount; i++) {
 757         OSStatus err = SetAudioObjectProperty(control->audioControls[i]->controlID,
 758             kAudioObjectPropertyScopeGlobal, kAudioLevelControlPropertyScalarValue,
 759             sizeof(volumes[i]), &volumes[i]);
 760         if (err) {
 761             OS_ERROR3(err, "SetPortControlVolumes , control %d of %d (coltrolID = 0x%x)",
 762                 i, control->controlCount, control->audioControls[i]->controlID);
 763             // don't return - try to set the rest of AudioControls
 764         }
 765     }
 766 }
 767 
 768 #define DEFAULT_VOLUME_VALUE    1.0f
 769 #define DEFAULT_BALANCE_VALUE   0.0f
 770 
 771 float PORT_GetFloatValue(void* controlIDV) {
 772     PortControl *control = (PortControl *)controlIDV;
 773     Float32 result = 0;
 774 
 775     Float32 subVolumes[control->controlCount];
 776     Float32 maxVolume;
 777 
 778     switch (control->type) {
 779     case PortControl::Volume:
 780         if (!TestPortControlValidity(control)) {
 781             return DEFAULT_VOLUME_VALUE;
 782         }
 783 
 784         if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
 785             return DEFAULT_VOLUME_VALUE;
 786         }
 787         result = maxVolume;
 788         break;
 789     case PortControl::Balance:
 790         if (!TestPortControlValidity(control)) {
 791             return DEFAULT_BALANCE_VALUE;
 792         }
 793 
 794         // balance control always has 2 volume controls
 795         if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
 796             return DEFAULT_VOLUME_VALUE;
 797         }
 798         // calculate balance value
 799         if (subVolumes[0] > subVolumes[1]) {
 800             result = -1.0f + (subVolumes[1] / subVolumes[0]);
 801         } else if (subVolumes[1] > subVolumes[0]) {
 802             result = 1.0f - (subVolumes[0] / subVolumes[1]);
 803         } else {
 804             result = 0.0f;
 805         }
 806         break;
 807     default:
 808         ERROR1("GetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
 809         return 0;
 810     }
 811 
 812     TRACE1("<<PORT_GetFloatValue = %f\n", result);
 813     return result;
 814 }
 815 
 816 void PORT_SetFloatValue(void* controlIDV, float value) {
 817     TRACE1("> PORT_SetFloatValue = %f\n", value);
 818     PortControl *control = (PortControl *)controlIDV;
 819 
 820     if (!TestPortControlValidity(control)) {
 821         return;
 822     }
 823 
 824     Float32 subVolumes[control->controlCount];
 825     Float32 maxVolume;
 826 
 827     switch (control->type) {
 828     case PortControl::Volume:
 829         if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
 830             return;
 831         }
 832         // update the values
 833         if (maxVolume > 0.001) {
 834             float multiplicator = value/maxVolume;
 835             for (int i=0; i<control->controlCount; i++)
 836                 subVolumes[i] *= multiplicator;
 837         } else {
 838             // volume for all channels == 0, so set all channels to "value"
 839             for (int i=0; i<control->controlCount; i++)
 840                 subVolumes[i] = value;
 841         }
 842         // set new values
 843         SetPortControlVolumes(control, subVolumes);
 844         break;
 845     case PortControl::Balance:
 846         // balance control always has 2 volume controls
 847         if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
 848             return;
 849         }
 850         // calculate new values
 851         if (value < 0.0f) {
 852             subVolumes[0] = maxVolume;
 853             subVolumes[1] = maxVolume * (value + 1.0f);
 854         } else {
 855             // this case also handles value == 0
 856             subVolumes[0] = maxVolume * (1.0f - value);
 857             subVolumes[1] = maxVolume;
 858         }
 859         // set new values
 860         SetPortControlVolumes(control, subVolumes);
 861         break;
 862     default:
 863         ERROR1("PORT_SetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
 864         return;
 865     }
 866 }
 867 
 868 #endif // USE_PORTS