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