4639N/A/*
4688N/A * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
4639N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4639N/A *
4639N/A * This code is free software; you can redistribute it and/or modify it
4639N/A * under the terms of the GNU General Public License version 2 only, as
4639N/A * published by the Free Software Foundation. Oracle designates this
4639N/A * particular file as subject to the "Classpath" exception as provided
4639N/A * by Oracle in the LICENSE file that accompanied this code.
4639N/A *
4639N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4639N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4639N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4639N/A * version 2 for more details (a copy is included in the LICENSE file that
4639N/A * accompanied this code).
4639N/A *
4639N/A * You should have received a copy of the GNU General Public License version
4639N/A * 2 along with this work; if not, write to the Free Software Foundation,
4639N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4639N/A *
4639N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4639N/A * or visit www.oracle.com if you need additional information or have any
4639N/A * questions.
4639N/A */
4639N/A
4639N/A//#define USE_ERROR
4639N/A//#define USE_TRACE
4639N/A
4639N/A#include <CoreAudio/CoreAudio.h>
4639N/A#include <IOKit/audio/IOAudioTypes.h>
4639N/A
4639N/A#include "PLATFORM_API_MacOSX_Utils.h"
4639N/A
4639N/Aextern "C" {
4639N/A#include "Ports.h"
4639N/A}
4639N/A
4639N/A#if USE_PORTS == TRUE
4639N/A
4688N/A/* If a device has the only AudioStream in the scope (input or output),
4688N/A * PortMixer provides a single Port, using the stream kAudioStreamPropertyTerminalType
4688N/A * property value to determine Port.Type (PORT_GetPortType function).
4688N/A * If the device has several (more than 1) AudioStreams, there are 2 ways to represent Ports:
4688N/A * 1. (HALLab-style) single Port which represents all device channels with
4688N/A * "master volume" and (if number of channel is 2) "master balance"; if AudioDevice
4688N/A * does not provide "master" controls, implement "virtual master" controls.
4688N/A * Port.Type is PORT_SRC_UNKNOWN or PORT_DST_UNKNOWN.
4688N/A * 2. provide a separate Port for every AudioStream (with appropriate Port.Type);
4688N/A *
4688N/A * AudioHardware.h claims that AudioStream objects share AudioControl objects with their owning AudioDevice.
4688N/A * In practice 10.7 OSX drivers (built-in devices, USB audio) implement AudioControl only for AudioDevice.
4688N/A * For now 1st way is implemented (2nd way can be better if AudioStreams provide AudioControls).
4639N/A */
4639N/A
4639N/Astatic DeviceList deviceCache;
4639N/A
4688N/A#define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
4688N/A
4639N/A
4688N/A// CoreAudio's AudioControl
4688N/Astruct AudioControl {
4688N/A AudioObjectID controlID;
4688N/A AudioClassID classID; // kAudioVolumeControlClassID etc.
4688N/A AudioObjectPropertyScope scope; // input, output
4688N/A AudioObjectPropertyElement channel; // master = 0, channels = 1 2 ...
4688N/A};
4639N/A
4688N/A// Controls for Java
4688N/A// PortMixer do all memory management (alloc/free audioControls)
4688N/Astruct PortControl {
4688N/A enum ControlType {
4688N/A Volume, // manages single or multiple volume AudioControl
4688N/A Mute, // manages single or multiple mute AudioControls
4688N/A Balance // "virtual" control, manages 2 volume AudioControls (only for stereo lines)
4688N/A };
4688N/A ControlType type;
4639N/A
4688N/A int controlCount;
4688N/A AudioControl **audioControls;
4688N/A
4688N/A PortControl *next; // to organize PortControl list
4688N/A};
4639N/A
4688N/A// represents line (port) for PortMixer
4688N/A// used for PORT_GetPortCount/PORT_GetPortType/PORT_GetPortName functions
4688N/Astruct PortLine {
4688N/A AudioObjectPropertyScope scope;
4688N/A // if the device has several AudioStreams in the scope, streamID == 0
4688N/A AudioStreamID streamID;
4639N/A};
4639N/A
4639N/Astruct PortMixer {
4639N/A AudioDeviceID deviceID;
4639N/A
4688N/A int portCount;
4688N/A PortLine ports[2]; // maximum 2 lines - 1 for input & 1 for output
4688N/A
4688N/A int deviceControlCount; // -1 means "not initialized"
4688N/A AudioControl *deviceControls;
4688N/A
4688N/A PortControl *portControls; // list of port controls
4688N/A
4688N/A bool listenersInstalled;
4688N/A};
4688N/A
4688N/A
4688N/Avoid RemoveChangeListeners(PortMixer *mixer); // forward declaration
4688N/A
4688N/AOSStatus ChangeListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses,
4688N/A const AudioObjectPropertyAddress inAddresses[], void *inClientData)
4688N/A{
4688N/A PortMixer *mixer = (PortMixer *)inClientData;
4688N/A
4688N/A OSStatus err = noErr;
4688N/A UInt32 size;
4688N/A
4688N/A bool invalid = false;
4639N/A
4688N/A for (UInt32 i = 0; i < inNumberAddresses; i++) {
4688N/A switch (inAddresses[i].mSelector) {
4688N/A case kAudioHardwarePropertyDevices:
4688N/A // check if the device has been removed
4688N/A err = GetAudioObjectPropertySize(kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioHardwarePropertyDevices, &size);
4688N/A if (err == noErr) {
4688N/A int count = size/sizeof(AudioDeviceID);
4688N/A AudioDeviceID devices[count];
4688N/A err = GetAudioObjectProperty(kAudioObjectSystemObject, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioHardwarePropertyDevices, count*sizeof(AudioDeviceID), devices, 1);
4688N/A if (err == noErr) {
4688N/A bool found = false;
4688N/A for (int j = 0; j < count; j++) {
4688N/A if (devices[j] == mixer->deviceID) {
4688N/A found = true;
4688N/A break;
4688N/A }
4688N/A }
4688N/A if (!found) {
4688N/A invalid = true;
4688N/A }
4688N/A }
4688N/A }
4688N/A break;
4688N/A case kAudioObjectPropertyOwnedObjects:
4688N/A case kAudioDevicePropertyDeviceHasChanged:
4688N/A // ensure all _used_ AudioControl are valid
4688N/A err = GetAudioObjectPropertySize(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioObjectPropertyOwnedObjects, &size);
4688N/A if (err == noErr) {
4688N/A int count = size / sizeof(AudioObjectID);
4688N/A AudioObjectID controlIDs[count];
4688N/A err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioObjectPropertyOwnedObjects, count * sizeof(AudioObjectID), &controlIDs, 1);
4688N/A if (err == noErr) {
4688N/A for (PortControl *ctrl = mixer->portControls; ctrl != NULL; ctrl = ctrl->next) {
4688N/A for (int i = 0; i < ctrl->controlCount; i++) {
4688N/A bool found = false;
4688N/A for (int j = 0; j < count; j++) {
4688N/A if (ctrl->audioControls[i]->controlID == controlIDs[j]) {
4688N/A found = true;
4688N/A break;
4688N/A }
4688N/A }
4688N/A if (!found) {
4688N/A invalid = true;
4688N/A break; // goto next control
4688N/A }
4688N/A }
4688N/A }
4688N/A }
4688N/A }
4688N/A }
4688N/A }
4639N/A
4688N/A if (invalid) {
4688N/A TRACE1("PortMixer (deviceID=0x%x) becomes invalid", (int)mixer->deviceID);
4688N/A // invalidate all controls
4688N/A for (int i=0; i<mixer->deviceControlCount; i++) {
4688N/A mixer->deviceControls[i].controlID = 0;
4688N/A }
4688N/A RemoveChangeListeners(mixer);
4688N/A }
4688N/A
4688N/A
4688N/A return noErr;
4688N/A}
4688N/A
4688N/Aconst AudioObjectPropertyAddress changeListenersAddresses[] = {
4688N/A {kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
4688N/A {kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
4688N/A {kAudioDevicePropertyDeviceHasChanged, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}
4639N/A};
4639N/A
4688N/Avoid AddChangeListeners(PortMixer *mixer) {
4688N/A if (!mixer->listenersInstalled) {
4688N/A for (size_t i=0; i<sizeof(changeListenersAddresses)/sizeof(changeListenersAddresses[0]); i++) {
4688N/A AudioObjectAddPropertyListener(mixer->deviceID, &changeListenersAddresses[i], ChangeListenerProc, mixer);
4688N/A }
4688N/A mixer->listenersInstalled = true;
4688N/A }
4688N/A}
4688N/A
4688N/Avoid RemoveChangeListeners(PortMixer *mixer) {
4688N/A if (mixer->listenersInstalled) {
4688N/A for (size_t i=0; i<sizeof(changeListenersAddresses)/sizeof(changeListenersAddresses[0]); i++) {
4688N/A AudioObjectRemovePropertyListener(mixer->deviceID, &changeListenersAddresses[i], ChangeListenerProc, mixer);
4688N/A }
4688N/A mixer->listenersInstalled = false;
4688N/A }
4688N/A}
4688N/A
4688N/A
4688N/A////////////////////////////////////////////////////////////////////////////////
4688N/A// functions from Port.h
4688N/A
4639N/AINT32 PORT_GetPortMixerCount() {
4639N/A deviceCache.Refresh();
4639N/A int count = deviceCache.GetCount();
4688N/A TRACE1("<<PORT_GetPortMixerCount = %d\n", count);
4639N/A return count;
4639N/A}
4639N/A
4639N/AINT32 PORT_GetPortMixerDescription(INT32 mixerIndex, PortMixerDescription* mixerDescription) {
4639N/A bool result = deviceCache.GetDeviceInfo(mixerIndex, NULL, PORT_STRING_LENGTH,
4639N/A mixerDescription->name, mixerDescription->vendor, mixerDescription->description, mixerDescription->version);
4639N/A return result ? TRUE : FALSE;
4639N/A}
4639N/A
4639N/Avoid* PORT_Open(INT32 mixerIndex) {
4688N/A TRACE1("\n>>PORT_Open (mixerIndex=%d)\n", (int)mixerIndex);
4639N/A PortMixer *mixer = (PortMixer *)calloc(1, sizeof(PortMixer));
4639N/A
4639N/A mixer->deviceID = deviceCache.GetDeviceID(mixerIndex);
4639N/A if (mixer->deviceID != 0) {
4688N/A mixer->deviceControlCount = -1; // not initialized
4688N/A // fill mixer->ports (and mixer->portCount)
4688N/A for (int i=0; i<2; i++) {
4688N/A OSStatus err;
4688N/A UInt32 size = 0;
4688N/A AudioObjectPropertyScope scope =
4688N/A (i == 0) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
4639N/A
4688N/A err = GetAudioObjectPropertySize(mixer->deviceID, scope, kAudioDevicePropertyStreams, &size);
4688N/A if (err || size == 0) {
4688N/A continue;
4688N/A }
4688N/A if (size / sizeof(AudioStreamID) == 1) {
4688N/A // the device has the only AudioStream
4688N/A AudioStreamID streamID;
4688N/A err = GetAudioObjectProperty(mixer->deviceID, scope, kAudioDevicePropertyStreams,
4688N/A sizeof(streamID), &streamID, 1);
4688N/A if (err) {
4688N/A continue;
4688N/A }
4688N/A mixer->ports[mixer->portCount].streamID = streamID;
4688N/A } else {
4688N/A // the device has several AudioStreams in the scope
4688N/A mixer->ports[mixer->portCount].streamID = 0;
4688N/A }
4688N/A mixer->ports[mixer->portCount].scope = scope;
4688N/A mixer->portCount++;
4639N/A }
4639N/A }
4639N/A
4688N/A TRACE2("<<PORT_Open (mixerIndex=%d) %p\n", mixerIndex, mixer);
4639N/A return mixer;
4639N/A}
4639N/A
4688N/A
4639N/Avoid PORT_Close(void* id) {
4688N/A TRACE1(">>PORT_Close %p\n", id);
4639N/A PortMixer *mixer = (PortMixer *)id;
4639N/A
4639N/A if (mixer) {
4688N/A RemoveChangeListeners(mixer);
4688N/A while (mixer->portControls != NULL) {
4688N/A PortControl *control2delete = mixer->portControls;
4688N/A mixer->portControls = control2delete->next;
4688N/A
4688N/A if (control2delete->audioControls != NULL) {
4688N/A free(control2delete->audioControls);
4688N/A }
4688N/A free(control2delete);
4688N/A }
4688N/A if (mixer->deviceControls) {
4688N/A free(mixer->deviceControls);
4688N/A }
4639N/A free(mixer);
4639N/A }
4688N/A TRACE1("<<PORT_Close %p\n", mixer);
4639N/A}
4639N/A
4639N/AINT32 PORT_GetPortCount(void* id) {
4639N/A PortMixer *mixer = (PortMixer *)id;
4688N/A
4688N/A int result = mixer->portCount;
4639N/A
4688N/A TRACE1("<<PORT_GetPortCount = %d\n", result);
4688N/A return result;
4639N/A}
4639N/A
4639N/AINT32 PORT_GetPortType(void* id, INT32 portIndex) {
4639N/A PortMixer *mixer = (PortMixer *)id;
4639N/A INT32 ret = 0;
4639N/A
4688N/A if (portIndex < 0 || portIndex >= mixer->portCount) {
4688N/A ERROR1("PORT_GetPortType: line (portIndex = %d) not found\n", portIndex);
4639N/A return 0;
4639N/A }
4639N/A
4688N/A AudioObjectPropertyScope scope = mixer->ports[portIndex].scope;
4688N/A AudioStreamID streamID = mixer->ports[portIndex].streamID;
4688N/A if (streamID != 0) {
4688N/A UInt32 terminalType;
4639N/A
4688N/A OSStatus err = GetAudioObjectProperty(streamID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioStreamPropertyTerminalType, sizeof(terminalType), &terminalType, 1);
4688N/A if (err) {
4688N/A OS_ERROR1(err, "PORT_GetPortType(kAudioStreamPropertyTerminalType), portIndex=%d", portIndex);
4688N/A return 0;
4688N/A }
4688N/A
4688N/A // Note that kAudioStreamPropertyTerminalType actually returns values from
4688N/A // IOAudioTypes.h, not the defined kAudioStreamTerminalType*.
4688N/A TRACE4("PORT_GetPortType (portIndex=%d), scope=%s, termType=0x%04x (%s)\n",
4688N/A (int)portIndex, FourCC2Str(scope), (int)terminalType, FourCC2Str(terminalType));
4639N/A switch (terminalType) {
4639N/A case INPUT_MICROPHONE:
4639N/A ret = PORT_SRC_MICROPHONE;
4639N/A break;
4688N/A
4639N/A case OUTPUT_SPEAKER:
4639N/A ret = PORT_DST_SPEAKER;
4639N/A break;
4639N/A case OUTPUT_HEADPHONES:
4639N/A ret = PORT_DST_HEADPHONE;
4639N/A break;
4688N/A
4688N/A case EXTERNAL_LINE_CONNECTOR:
4688N/A ret = scope == kAudioDevicePropertyScopeInput ? PORT_SRC_LINE_IN : PORT_DST_LINE_OUT;
4639N/A break;
4688N/A
4639N/A default:
4688N/A TRACE1(" unknown output terminal type %#x\n", terminalType);
4639N/A }
4688N/A } else {
4688N/A TRACE0(" PORT_GetPortType: multiple streams\n");
4639N/A }
4639N/A
4688N/A if (ret == 0) {
4688N/A // if the type not detected, return "common type"
4688N/A ret = scope == kAudioDevicePropertyScopeInput ? PORT_SRC_UNKNOWN : PORT_DST_UNKNOWN;
4688N/A }
4688N/A
4688N/A TRACE2("<<PORT_GetPortType (portIndex=%d) = %d\n", portIndex, ret);
4639N/A return ret;
4639N/A}
4639N/A
4639N/AINT32 PORT_GetPortName(void* id, INT32 portIndex, char* name, INT32 len) {
4639N/A PortMixer *mixer = (PortMixer *)id;
4688N/A
4688N/A name[0] = 0; // for safety
4639N/A
4688N/A if (portIndex < 0 || portIndex >= mixer->portCount) {
4688N/A ERROR1("PORT_GetPortName: line (portIndex = %d) not found\n", portIndex);
4688N/A return FALSE;
4688N/A }
4639N/A
4688N/A AudioStreamID streamID = mixer->ports[portIndex].streamID;
4688N/A CFStringRef cfname = NULL;
4688N/A if (streamID != 0) {
4688N/A OSStatus err = GetAudioObjectProperty(streamID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioObjectPropertyName, sizeof(cfname), &cfname, 1);
4688N/A if (err && err != kAudioHardwareUnknownPropertyError) {
4688N/A OS_ERROR1(err, "PORT_GetPortName(stream name), portIndex=%d", portIndex);
4688N/A return FALSE;
4688N/A }
4639N/A }
4639N/A
4639N/A if (!cfname) {
4639N/A // use the device's name if the stream has no name (usually the case)
4688N/A // or the device has several AudioStreams
4688N/A OSStatus err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioObjectPropertyName, sizeof(cfname), &cfname, 1);
4639N/A if (err) {
4639N/A OS_ERROR1(err, "PORT_GetPortName(device name), portIndex=%d", portIndex);
4639N/A return FALSE;
4639N/A }
4639N/A }
4639N/A
4639N/A if (cfname) {
4639N/A CFStringGetCString(cfname, name, len, kCFStringEncodingUTF8);
4639N/A CFRelease(cfname);
4639N/A }
4639N/A
4688N/A TRACE2("<<PORT_GetPortName (portIndex = %d) = %s\n", portIndex, name);
4639N/A return TRUE;
4639N/A}
4639N/A
4639N/A
4688N/A// counts number of valid (non-NULL) elements in the array of AudioControls
4688N/Astatic int ValidControlCount(AudioControl **arr, int offset, int len) {
4688N/A int result = 0;
4688N/A int end = offset + len;
4688N/A for (int i=offset; i<end; i++) {
4688N/A if (arr[i] != NULL)
4688N/A result++;
4688N/A }
4688N/A return result;
4639N/A}
4639N/A
4688N/A// returns java control
4688N/Astatic void* CreatePortControl(PortMixer *mixer, PortControlCreator *creator, PortControl::ControlType type,
4688N/A AudioControl **audioControls, int offset, int len) {
4688N/A void *jControl = NULL;
4688N/A PortControl *control = (PortControl *)calloc(1, sizeof(PortControl));
4688N/A float precision = 0.01;
4688N/A
4688N/A control->type = type;
4688N/A control->controlCount = len;
4688N/A control->audioControls = (AudioControl **)malloc(len * sizeof(AudioControl *));
4688N/A memcpy(control->audioControls, audioControls + offset, len * sizeof(AudioControl *));
4688N/A
4688N/A switch (control->type) {
4688N/A case PortControl::Volume:
4688N/A jControl = creator->newFloatControl(creator, control, CONTROL_TYPE_VOLUME, 0, 1, precision, "");
4688N/A break;
4688N/A case PortControl::Mute:
4688N/A jControl = creator->newBooleanControl(creator, control, CONTROL_TYPE_MUTE);
4688N/A break;
4688N/A case PortControl::Balance:
4688N/A jControl = creator->newFloatControl(creator, control, CONTROL_TYPE_BALANCE, -1, 1, precision, "");
4688N/A break;
4688N/A };
4688N/A
4688N/A if (jControl == NULL) {
4688N/A ERROR0("CreatePortControl: javaControl was not created\n");
4688N/A free(control->audioControls);
4688N/A free(control);
4688N/A return NULL;
4688N/A }
4688N/A
4688N/A // add the control to mixer control list;
4688N/A control->next = mixer->portControls;
4688N/A mixer->portControls = control;
4688N/A
4688N/A return jControl;
4639N/A}
4639N/A
4639N/Avoid PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
4639N/A PortMixer *mixer = (PortMixer *)id;
4639N/A
4688N/A TRACE1(">>PORT_GetControls (portIndex = %d)\n", portIndex);
4639N/A
4688N/A if (portIndex < 0 || portIndex >= mixer->portCount) {
4688N/A ERROR1("<<PORT_GetControls: line (portIndex = %d) not found\n", portIndex);
4688N/A return;
4688N/A }
4639N/A
4688N/A PortLine *port = &(mixer->ports[portIndex]);
4639N/A
4688N/A if (mixer->deviceControlCount < 0) { // not initialized
4688N/A OSStatus err;
4688N/A UInt32 size;
4688N/A // deviceControlCount is overestimated
4639N/A // because we don't actually filter by if the owned objects are controls
4639N/A err = GetAudioObjectPropertySize(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioObjectPropertyOwnedObjects, &size);
4639N/A
4688N/A if (err) {
4688N/A OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject size", portIndex);
4688N/A } else {
4688N/A mixer->deviceControlCount = size / sizeof(AudioObjectID);
4688N/A TRACE1(" PORT_GetControls: detected %d owned objects\n", mixer->deviceControlCount);
4639N/A
4688N/A AudioObjectID controlIDs[mixer->deviceControlCount];
4688N/A
4688N/A err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioObjectPropertyOwnedObjects, sizeof(controlIDs), controlIDs, 1);
4639N/A
4688N/A if (err) {
4688N/A OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject values", portIndex);
4688N/A } else {
4688N/A mixer->deviceControls = (AudioControl *)calloc(mixer->deviceControlCount, sizeof(AudioControl));
4639N/A
4688N/A for (int i = 0; i < mixer->deviceControlCount; i++) {
4688N/A AudioControl *control = &mixer->deviceControls[i];
4639N/A
4688N/A control->controlID = controlIDs[i];
4639N/A
4688N/A OSStatus err1 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioObjectPropertyClass, sizeof(control->classID), &control->classID, 1);
4688N/A OSStatus err2 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioControlPropertyScope, sizeof(control->scope), &control->scope, 1);
4688N/A OSStatus err3 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
4688N/A kAudioControlPropertyElement, sizeof(control->channel), &control->channel, 1);
4688N/A if (err1 || err2 || err3) { // not a control or other error
4639N/A control->classID = 0;
4639N/A continue;
4639N/A }
4639N/A
4688N/A TRACE4("- control 0x%x, class='%s', scope='%s', channel=%d\n",
4688N/A control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
4639N/A }
4639N/A }
4639N/A }
4639N/A }
4639N/A
4688N/A if (mixer->deviceControlCount <= 0) {
4688N/A TRACE1("<<PORT_GetControls (portIndex = %d): no owned AudioControls\n", portIndex);
4688N/A return;
4639N/A }
4639N/A
4688N/A int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0);
4688N/A
4688N/A // collect volume and mute controls
4688N/A AudioControl* volumeControls[totalChannels+1]; // 0 - for master channel
4688N/A memset(&volumeControls, 0, sizeof(AudioControl *) * (totalChannels+1));
4688N/A AudioControl* muteControls[totalChannels+1]; // 0 - for master channel
4688N/A memset(&muteControls, 0, sizeof(AudioControl *) * (totalChannels+1));
4688N/A
4688N/A for (int i=0; i<mixer->deviceControlCount; i++) {
4688N/A AudioControl *control = &mixer->deviceControls[i];
4688N/A if (control->classID == 0 || control->scope != port->scope || control->channel > (unsigned)totalChannels) {
4688N/A continue;
4688N/A }
4688N/A if (control->classID == kAudioVolumeControlClassID) {
4688N/A if (volumeControls[control->channel] == NULL) {
4688N/A volumeControls[control->channel] = control;
4688N/A } else {
4688N/A ERROR4("WARNING: duplicate VOLUME control 0x%x, class='%s', scope='%s', channel=%d\n",
4688N/A control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
4688N/A }
4688N/A } else if (control->classID == kAudioMuteControlClassID) {
4688N/A if (muteControls[control->channel] == NULL) {
4688N/A muteControls[control->channel] = control;
4688N/A } else {
4688N/A ERROR4("WARNING: duplicate MUTE control 0x%x, class='%s', scope='%s', channel=%d\n",
4688N/A control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
4688N/A }
4688N/A } else {
4688N/A#ifdef USE_ERROR
4688N/A if (control->classID != 0) {
4688N/A ERROR4("WARNING: unhandled control 0x%x, class='%s', scope='%s', channel=%d\n",
4688N/A control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
4688N/A }
4688N/A#endif
4688N/A }
4688N/A }
4688N/A
4688N/A ////////////////////////////////////////////////////////
4688N/A // create java control hierarchy
4688N/A
4688N/A void *masterVolume = NULL, *masterMute = NULL, *masterBalance = NULL;
4688N/A // volumeControls[0] and muteControls[0] - master volume/mute
4688N/A // volumeControls[n] and muteControls[n] (n=1..totalChannels) - corresponding channel controls
4688N/A if (volumeControls[0] != NULL) { // "master volume" AudioControl
4688N/A masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 0, 1);
4688N/A } else {
4688N/A if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
4688N/A // every channel has volume control => create virtual master volume
4688N/A masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 1, totalChannels);
4688N/A } else {
4688N/A TRACE2(" PORT_GetControls (master volume): totalChannels = %d, valid volume controls = %d\n",
4688N/A totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
4688N/A }
4688N/A }
4688N/A
4688N/A if (muteControls[0] != NULL) { // "master mute"
4688N/A masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 0, 1);
4688N/A } else {
4688N/A if (ValidControlCount(muteControls, 1, totalChannels) == totalChannels) {
4688N/A // every channel has mute control => create virtual master mute control
4688N/A masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 1, totalChannels);
4688N/A } else {
4688N/A TRACE2(" PORT_GetControls (master mute): totalChannels = %d, valid volume controls = %d\n",
4688N/A totalChannels, ValidControlCount(muteControls, 1, totalChannels));
4688N/A }
4639N/A }
4639N/A
4688N/A // virtual balance
4688N/A if (totalChannels == 2) {
4688N/A if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
4688N/A masterBalance = CreatePortControl(mixer, creator, PortControl::Balance, volumeControls, 1, totalChannels);
4688N/A } else {
4688N/A TRACE2(" PORT_GetControls (naster balance): totalChannels = %d, valid volume controls = %d\n",
4688N/A totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
4688N/A }
4688N/A }
4639N/A
4688N/A // add "master" controls
4688N/A if (masterVolume != NULL) {
4688N/A creator->addControl(creator, masterVolume);
4688N/A }
4688N/A if (masterBalance != NULL) {
4688N/A creator->addControl(creator, masterBalance);
4688N/A }
4688N/A if (masterMute != NULL) {
4688N/A creator->addControl(creator, masterMute);
4639N/A }
4639N/A
4688N/A // don't add per-channel controls for mono & stereo - they are handled by "master" controls
4688N/A // TODO: this should be reviewed to handle controls other than mute & volume
4688N/A if (totalChannels > 2) {
4688N/A // add separate compound control for each channel (containing volume and mute)
4688N/A // (ensure that we have controls)
4688N/A if (ValidControlCount(volumeControls, 1, totalChannels) > 0 || ValidControlCount(muteControls, 1, totalChannels) > 0) {
4688N/A for (int ch=1; ch<=totalChannels; ch++) {
4688N/A // get the channel name
4688N/A char *channelName;
4688N/A CFStringRef cfname = NULL;
4688N/A const AudioObjectPropertyAddress address = {kAudioObjectPropertyElementName, port->scope, ch};
4688N/A UInt32 size = sizeof(cfname);
4688N/A OSStatus err = AudioObjectGetPropertyData(mixer->deviceID, &address, 0, NULL, &size, &cfname);
4688N/A if (err == noErr) {
4688N/A CFIndex length = CFStringGetLength(cfname) + 1;
4688N/A channelName = (char *)malloc(length);
4688N/A CFStringGetCString(cfname, channelName, length, kCFStringEncodingUTF8);
4688N/A CFRelease(cfname);
4688N/A } else {
4688N/A channelName = (char *)malloc(16);
4688N/A sprintf(channelName, "Ch %d", ch);
4688N/A }
4639N/A
4688N/A void* jControls[2];
4688N/A int controlCount = 0;
4688N/A if (volumeControls[ch] != NULL) {
4688N/A jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, ch, 1);
4688N/A }
4688N/A if (muteControls[ch] != NULL) {
4688N/A jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, ch, 1);
4688N/A }
4688N/A // TODO: add any extra controls for "other" controls for the channel
4688N/A
4688N/A void *compoundControl = creator->newCompoundControl(creator, channelName, jControls, controlCount);
4688N/A creator->addControl(creator, compoundControl);
4688N/A
4688N/A free(channelName);
4688N/A }
4639N/A }
4639N/A }
4639N/A
4688N/A AddChangeListeners(mixer);
4688N/A
4688N/A TRACE1("<<PORT_GetControls (portIndex = %d)\n", portIndex);
4688N/A}
4639N/A
4688N/Abool TestPortControlValidity(PortControl *control) {
4688N/A for (int i=0; i<control->controlCount; i++) {
4688N/A if (control->audioControls[i]->controlID == 0)
4688N/A return false;
4639N/A }
4688N/A return true;
4688N/A}
4639N/A
4688N/A
4688N/A#define DEFAULT_MUTE_VALUE 0
4639N/A
4639N/AINT32 PORT_GetIntValue(void* controlIDV) {
4639N/A PortControl *control = (PortControl *)controlIDV;
4688N/A INT32 result = 0;
4639N/A
4688N/A switch (control->type) {
4688N/A case PortControl::Mute:
4688N/A if (!TestPortControlValidity(control)) {
4688N/A return DEFAULT_MUTE_VALUE;
4688N/A }
4688N/A result = 1; // default is "muted", if some channel in unmuted, then "virtual mute" is also unmuted
4688N/A for (int i=0; i<control->controlCount; i++) {
4688N/A UInt32 value;
4688N/A OSStatus err = GetAudioObjectProperty(control->audioControls[i]->controlID,
4688N/A kAudioObjectPropertyScopeGlobal, kAudioBooleanControlPropertyValue, sizeof(value), &value, 1);
4688N/A if (err) {
4688N/A OS_ERROR3(err, "PORT_GetIntValue, control %d of %d (coltrolID = 0x%x)",
4688N/A i, control->controlCount, control->audioControls[i]->controlID);
4688N/A return DEFAULT_MUTE_VALUE;
4688N/A }
4688N/A if (value == 0) {
4688N/A result = 0;
4688N/A }
4688N/A }
4639N/A break;
4639N/A default:
4688N/A ERROR1("PORT_GetIntValue requested for non-Int control (control-type == %d)\n", control->type);
4639N/A return 0;
4639N/A }
4639N/A
4688N/A //TRACE1("<<PORT_GetIntValue = %d\n", result);
4688N/A return result;
4639N/A}
4639N/A
4639N/Avoid PORT_SetIntValue(void* controlIDV, INT32 value) {
4688N/A //TRACE1("> PORT_SetIntValue = %d\n", value);
4639N/A PortControl *control = (PortControl *)controlIDV;
4639N/A
4688N/A if (!TestPortControlValidity(control)) {
4639N/A return;
4639N/A }
4639N/A
4688N/A switch (control->type) {
4688N/A case PortControl::Mute:
4688N/A for (int i=0; i<control->controlCount; i++) {
4688N/A OSStatus err = SetAudioObjectProperty(control->audioControls[i]->controlID,
4688N/A kAudioObjectPropertyScopeGlobal, kAudioBooleanControlPropertyValue, sizeof(value), &value);
4688N/A if (err) {
4688N/A OS_ERROR3(err, "PORT_SetIntValue, control %d of %d (coltrolID = 0x%x)",
4688N/A i, control->controlCount, control->audioControls[i]->controlID);
4688N/A // don't return - try to set the rest of AudioControls
4688N/A }
4688N/A }
4688N/A break;
4688N/A default:
4688N/A ERROR1("PORT_SetIntValue requested for non-Int control (control-type == %d)\n", control->type);
4639N/A return;
4639N/A }
4639N/A}
4639N/A
4688N/A
4688N/A// gets volume value for all AudioControls of the PortControl
4688N/Astatic bool GetPortControlVolumes(PortControl *control, Float32 *volumes, Float32 *maxVolume) {
4688N/A *maxVolume = 0.0f;
4688N/A for (int i=0; i<control->controlCount; i++) {
4688N/A OSStatus err = GetAudioObjectProperty(control->audioControls[i]->controlID,
4688N/A kAudioObjectPropertyScopeGlobal, kAudioLevelControlPropertyScalarValue,
4688N/A sizeof(volumes[i]), &volumes[i], 1);
4688N/A if (err) {
4688N/A OS_ERROR3(err, "GetPortControlVolumes, control %d of %d (controlID = 0x%x)",
4688N/A i, control->controlCount, control->audioControls[i]->controlID);
4688N/A return false;
4688N/A }
4688N/A if (volumes[i] > *maxVolume) {
4688N/A *maxVolume = volumes[i];
4688N/A }
4688N/A }
4688N/A return true;
4688N/A}
4688N/A
4688N/A// sets volume value for all AudioControls of the PortControl
4688N/Astatic void SetPortControlVolumes(PortControl *control, Float32 *volumes) {
4688N/A for (int i=0; i<control->controlCount; i++) {
4688N/A OSStatus err = SetAudioObjectProperty(control->audioControls[i]->controlID,
4688N/A kAudioObjectPropertyScopeGlobal, kAudioLevelControlPropertyScalarValue,
4688N/A sizeof(volumes[i]), &volumes[i]);
4688N/A if (err) {
4688N/A OS_ERROR3(err, "SetPortControlVolumes , control %d of %d (coltrolID = 0x%x)",
4688N/A i, control->controlCount, control->audioControls[i]->controlID);
4688N/A // don't return - try to set the rest of AudioControls
4688N/A }
4688N/A }
4688N/A}
4688N/A
4688N/A#define DEFAULT_VOLUME_VALUE 1.0f
4688N/A#define DEFAULT_BALANCE_VALUE 0.0f
4688N/A
4639N/Afloat PORT_GetFloatValue(void* controlIDV) {
4639N/A PortControl *control = (PortControl *)controlIDV;
4688N/A Float32 result = 0;
4688N/A
4688N/A Float32 subVolumes[control->controlCount];
4688N/A Float32 maxVolume;
4688N/A
4688N/A switch (control->type) {
4688N/A case PortControl::Volume:
4688N/A if (!TestPortControlValidity(control)) {
4688N/A return DEFAULT_VOLUME_VALUE;
4688N/A }
4639N/A
4688N/A if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
4688N/A return DEFAULT_VOLUME_VALUE;
4688N/A }
4688N/A result = maxVolume;
4688N/A break;
4688N/A case PortControl::Balance:
4688N/A if (!TestPortControlValidity(control)) {
4688N/A return DEFAULT_BALANCE_VALUE;
4688N/A }
4688N/A
4688N/A // balance control always has 2 volume controls
4688N/A if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
4688N/A return DEFAULT_VOLUME_VALUE;
4688N/A }
4688N/A // calculate balance value
4688N/A if (subVolumes[0] > subVolumes[1]) {
4688N/A result = -1.0f + (subVolumes[1] / subVolumes[0]);
4688N/A } else if (subVolumes[1] > subVolumes[0]) {
4688N/A result = 1.0f - (subVolumes[0] / subVolumes[1]);
4688N/A } else {
4688N/A result = 0.0f;
4639N/A }
4639N/A break;
4639N/A default:
4688N/A ERROR1("GetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
4639N/A return 0;
4639N/A }
4639N/A
4688N/A TRACE1("<<PORT_GetFloatValue = %f\n", result);
4688N/A return result;
4639N/A}
4639N/A
4639N/Avoid PORT_SetFloatValue(void* controlIDV, float value) {
4639N/A TRACE1("> PORT_SetFloatValue = %f\n", value);
4639N/A PortControl *control = (PortControl *)controlIDV;
4688N/A
4688N/A if (!TestPortControlValidity(control)) {
4688N/A return;
4688N/A }
4688N/A
4688N/A Float32 subVolumes[control->controlCount];
4688N/A Float32 maxVolume;
4639N/A
4688N/A switch (control->type) {
4688N/A case PortControl::Volume:
4688N/A if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
4688N/A return;
4688N/A }
4688N/A // update the values
4688N/A if (maxVolume > 0.001) {
4688N/A float multiplicator = value/maxVolume;
4688N/A for (int i=0; i<control->controlCount; i++)
4688N/A subVolumes[i] *= multiplicator;
4688N/A } else {
4688N/A // volume for all channels == 0, so set all channels to "value"
4688N/A for (int i=0; i<control->controlCount; i++)
4688N/A subVolumes[i] = value;
4688N/A }
4688N/A // set new values
4688N/A SetPortControlVolumes(control, subVolumes);
4688N/A break;
4688N/A case PortControl::Balance:
4688N/A // balance control always has 2 volume controls
4688N/A if (!GetPortControlVolumes(control, subVolumes, &maxVolume)) {
4688N/A return;
4688N/A }
4688N/A // calculate new values
4688N/A if (value < 0.0f) {
4688N/A subVolumes[0] = maxVolume;
4688N/A subVolumes[1] = maxVolume * (value + 1.0f);
4688N/A } else {
4688N/A // this case also handles value == 0
4688N/A subVolumes[0] = maxVolume * (1.0f - value);
4688N/A subVolumes[1] = maxVolume;
4688N/A }
4688N/A // set new values
4688N/A SetPortControlVolumes(control, subVolumes);
4639N/A break;
4639N/A default:
4688N/A ERROR1("PORT_SetFloatValue requested for non-Float control (control-type == %d)\n", control->type);
4639N/A return;
4639N/A }
4639N/A}
4639N/A
4639N/A#endif // USE_PORTS