/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
//#define USE_ERROR
//#define USE_TRACE
#include <CoreAudio/CoreAudio.h>
#include "PLATFORM_API_MacOSX_Utils.h"
extern "C" {
#include "Ports.h"
}
/* If a device has the only AudioStream in the scope (input or output),
* PortMixer provides a single Port, using the stream kAudioStreamPropertyTerminalType
* property value to determine Port.Type (PORT_GetPortType function).
* If the device has several (more than 1) AudioStreams, there are 2 ways to represent Ports:
* 1. (HALLab-style) single Port which represents all device channels with
* "master volume" and (if number of channel is 2) "master balance"; if AudioDevice
* does not provide "master" controls, implement "virtual master" controls.
* Port.Type is PORT_SRC_UNKNOWN or PORT_DST_UNKNOWN.
* 2. provide a separate Port for every AudioStream (with appropriate Port.Type);
*
* AudioHardware.h claims that AudioStream objects share AudioControl objects with their owning AudioDevice.
* In practice 10.7 OSX drivers (built-in devices, USB audio) implement AudioControl only for AudioDevice.
* For now 1st way is implemented (2nd way can be better if AudioStreams provide AudioControls).
*/
// CoreAudio's AudioControl
struct AudioControl {
};
// Controls for Java
struct PortControl {
enum ControlType {
};
int controlCount;
};
// represents line (port) for PortMixer
// used for PORT_GetPortCount/PORT_GetPortType/PORT_GetPortName functions
struct PortLine {
// if the device has several AudioStreams in the scope, streamID == 0
};
struct PortMixer {
int portCount;
bool listenersInstalled;
};
{
bool invalid = false;
for (UInt32 i = 0; i < inNumberAddresses; i++) {
switch (inAddresses[i].mSelector) {
// check if the device has been removed
bool found = false;
for (int j = 0; j < count; j++) {
found = true;
break;
}
}
if (!found) {
invalid = true;
}
}
}
break;
// ensure all _used_ AudioControl are valid
for (int i = 0; i < ctrl->controlCount; i++) {
bool found = false;
for (int j = 0; j < count; j++) {
found = true;
break;
}
}
if (!found) {
invalid = true;
break; // goto next control
}
}
}
}
}
}
}
if (invalid) {
// invalidate all controls
for (int i=0; i<mixer->deviceControlCount; i++) {
}
}
return noErr;
}
{kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
{kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster},
{kAudioDevicePropertyDeviceHasChanged, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}
};
if (!mixer->listenersInstalled) {
AudioObjectAddPropertyListener(mixer->deviceID, &changeListenersAddresses[i], ChangeListenerProc, mixer);
}
mixer->listenersInstalled = true;
}
}
if (mixer->listenersInstalled) {
AudioObjectRemovePropertyListener(mixer->deviceID, &changeListenersAddresses[i], ChangeListenerProc, mixer);
}
mixer->listenersInstalled = false;
}
}
////////////////////////////////////////////////////////////////////////////////
// functions from Port.h
return count;
}
mixerDescription->name, mixerDescription->vendor, mixerDescription->description, mixerDescription->version);
}
// fill mixer->ports (and mixer->portCount)
for (int i=0; i<2; i++) {
continue;
}
// the device has the only AudioStream
if (err) {
continue;
}
} else {
// the device has several AudioStreams in the scope
}
}
}
return mixer;
}
if (mixer) {
}
}
if (mixer->deviceControls) {
}
}
}
return result;
}
return 0;
}
if (streamID != 0) {
if (err) {
return 0;
}
// Note that kAudioStreamPropertyTerminalType actually returns values from
// IOAudioTypes.h, not the defined kAudioStreamTerminalType*.
TRACE4("PORT_GetPortType (portIndex=%d), scope=%s, termType=0x%04x (%s)\n",
switch (terminalType) {
case INPUT_MICROPHONE:
break;
case OUTPUT_SPEAKER:
break;
case OUTPUT_HEADPHONES:
break;
case EXTERNAL_LINE_CONNECTOR:
break;
default:
}
} else {
TRACE0(" PORT_GetPortType: multiple streams\n");
}
if (ret == 0) {
// if the type not detected, return "common type"
}
return ret;
}
name[0] = 0; // for safety
return FALSE;
}
if (streamID != 0) {
return FALSE;
}
}
if (!cfname) {
// use the device's name if the stream has no name (usually the case)
// or the device has several AudioStreams
if (err) {
return FALSE;
}
}
if (cfname) {
}
return TRUE;
}
// counts number of valid (non-NULL) elements in the array of AudioControls
int result = 0;
result++;
}
return result;
}
// returns java control
static void* CreatePortControl(PortMixer *mixer, PortControlCreator *creator, PortControl::ControlType type,
case PortControl::Volume:
break;
case PortControl::Mute:
break;
case PortControl::Balance:
break;
};
ERROR0("CreatePortControl: javaControl was not created\n");
return NULL;
}
// add the control to mixer control list;
return jControl;
}
return;
}
// deviceControlCount is overestimated
// because we don't actually filter by if the owned objects are controls
if (err) {
} else {
if (err) {
} else {
for (int i = 0; i < mixer->deviceControlCount; i++) {
continue;
}
TRACE4("- control 0x%x, class='%s', scope='%s', channel=%d\n",
}
}
}
}
if (mixer->deviceControlCount <= 0) {
return;
}
int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0);
// collect volume and mute controls
for (int i=0; i<mixer->deviceControlCount; i++) {
if (control->classID == 0 || control->scope != port->scope || control->channel > (unsigned)totalChannels) {
continue;
}
} else {
ERROR4("WARNING: duplicate VOLUME control 0x%x, class='%s', scope='%s', channel=%d\n",
}
} else {
ERROR4("WARNING: duplicate MUTE control 0x%x, class='%s', scope='%s', channel=%d\n",
}
} else {
#ifdef USE_ERROR
ERROR4("WARNING: unhandled control 0x%x, class='%s', scope='%s', channel=%d\n",
}
#endif
}
}
////////////////////////////////////////////////////////
// create java control hierarchy
// volumeControls[n] and muteControls[n] (n=1..totalChannels) - corresponding channel controls
} else {
// every channel has volume control => create virtual master volume
masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 1, totalChannels);
} else {
TRACE2(" PORT_GetControls (master volume): totalChannels = %d, valid volume controls = %d\n",
}
}
} else {
// every channel has mute control => create virtual master mute control
} else {
TRACE2(" PORT_GetControls (master mute): totalChannels = %d, valid volume controls = %d\n",
}
}
// virtual balance
if (totalChannels == 2) {
masterBalance = CreatePortControl(mixer, creator, PortControl::Balance, volumeControls, 1, totalChannels);
} else {
TRACE2(" PORT_GetControls (naster balance): totalChannels = %d, valid volume controls = %d\n",
}
}
// add "master" controls
if (masterVolume != NULL) {
}
if (masterBalance != NULL) {
}
if (masterMute != NULL) {
}
// don't add per-channel controls for mono & stereo - they are handled by "master" controls
// TODO: this should be reviewed to handle controls other than mute & volume
if (totalChannels > 2) {
// add separate compound control for each channel (containing volume and mute)
// (ensure that we have controls)
if (ValidControlCount(volumeControls, 1, totalChannels) > 0 || ValidControlCount(muteControls, 1, totalChannels) > 0) {
// get the channel name
char *channelName;
} else {
}
int controlCount = 0;
jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, ch, 1);
}
jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, ch, 1);
}
// TODO: add any extra controls for "other" controls for the channel
}
}
}
}
for (int i=0; i<control->controlCount; i++) {
return false;
}
return true;
}
#define DEFAULT_MUTE_VALUE 0
case PortControl::Mute:
if (!TestPortControlValidity(control)) {
return DEFAULT_MUTE_VALUE;
}
for (int i=0; i<control->controlCount; i++) {
if (err) {
return DEFAULT_MUTE_VALUE;
}
if (value == 0) {
result = 0;
}
}
break;
default:
return 0;
}
//TRACE1("<<PORT_GetIntValue = %d\n", result);
return result;
}
//TRACE1("> PORT_SetIntValue = %d\n", value);
if (!TestPortControlValidity(control)) {
return;
}
case PortControl::Mute:
for (int i=0; i<control->controlCount; i++) {
if (err) {
// don't return - try to set the rest of AudioControls
}
}
break;
default:
return;
}
}
// gets volume value for all AudioControls of the PortControl
*maxVolume = 0.0f;
for (int i=0; i<control->controlCount; i++) {
if (err) {
return false;
}
}
}
return true;
}
// sets volume value for all AudioControls of the PortControl
for (int i=0; i<control->controlCount; i++) {
if (err) {
// don't return - try to set the rest of AudioControls
}
}
}
case PortControl::Volume:
if (!TestPortControlValidity(control)) {
return DEFAULT_VOLUME_VALUE;
}
return DEFAULT_VOLUME_VALUE;
}
break;
case PortControl::Balance:
if (!TestPortControlValidity(control)) {
return DEFAULT_BALANCE_VALUE;
}
// balance control always has 2 volume controls
return DEFAULT_VOLUME_VALUE;
}
// calculate balance value
} else {
result = 0.0f;
}
break;
default:
return 0;
}
return result;
}
if (!TestPortControlValidity(control)) {
return;
}
case PortControl::Volume:
return;
}
// update the values
if (maxVolume > 0.001) {
for (int i=0; i<control->controlCount; i++)
subVolumes[i] *= multiplicator;
} else {
// volume for all channels == 0, so set all channels to "value"
for (int i=0; i<control->controlCount; i++)
subVolumes[i] = value;
}
// set new values
break;
case PortControl::Balance:
// balance control always has 2 volume controls
return;
}
// calculate new values
if (value < 0.0f) {
subVolumes[0] = maxVolume;
} else {
// this case also handles value == 0
}
// set new values
break;
default:
return;
}
}
#endif // USE_PORTS