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
ChangeListenerProc(AudioObjectID inObjectID,UInt32 inNumberAddresses,const AudioObjectPropertyAddress inAddresses[],void * inClientData)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
AddChangeListeners(PortMixer * mixer)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
RemoveChangeListeners(PortMixer * mixer)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
PORT_GetPortMixerCount()216 INT32 PORT_GetPortMixerCount() {
217 deviceCache.Refresh();
218 int count = deviceCache.GetCount();
219 TRACE1("<<PORT_GetPortMixerCount = %d\n", count);
220 return count;
221 }
222
PORT_GetPortMixerDescription(INT32 mixerIndex,PortMixerDescription * mixerDescription)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
PORT_Open(INT32 mixerIndex)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
PORT_Close(void * id)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
PORT_GetPortCount(void * id)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
PORT_GetPortType(void * id,INT32 portIndex)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
PORT_GetPortName(void * id,INT32 portIndex,char * name,INT32 len)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
ValidControlCount(AudioControl ** arr,int offset,int len)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
CreatePortControl(PortMixer * mixer,PortControlCreator * creator,PortControl::ControlType type,AudioControl ** audioControls,int offset,int len)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
PORT_GetControls(void * id,INT32 portIndex,PortControlCreator * creator)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, 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
TestPortControlValidity(PortControl * control)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
PORT_GetIntValue(void * controlIDV)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
PORT_SetIntValue(void * controlIDV,INT32 value)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
GetPortControlVolumes(PortControl * control,Float32 * volumes,Float32 * maxVolume)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
SetPortControlVolumes(PortControl * control,Float32 * volumes)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
PORT_GetFloatValue(void * controlIDV)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
PORT_SetFloatValue(void * controlIDV,float value)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
869