506 lines
17 KiB
Java
506 lines
17 KiB
Java
/*
|
|
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* 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.
|
|
*/
|
|
|
|
package com.sun.media.sound;
|
|
|
|
import java.util.Vector;
|
|
|
|
import javax.sound.sampled.Control;
|
|
import javax.sound.sampled.Line;
|
|
import javax.sound.sampled.LineUnavailableException;
|
|
import javax.sound.sampled.Port;
|
|
import javax.sound.sampled.BooleanControl;
|
|
import javax.sound.sampled.CompoundControl;
|
|
import javax.sound.sampled.FloatControl;
|
|
|
|
|
|
/**
|
|
* A Mixer which only provides Ports.
|
|
*
|
|
* @author Florian Bomers
|
|
*/
|
|
final class PortMixer extends AbstractMixer {
|
|
|
|
// CONSTANTS
|
|
private static final int SRC_UNKNOWN = 0x01;
|
|
private static final int SRC_MICROPHONE = 0x02;
|
|
private static final int SRC_LINE_IN = 0x03;
|
|
private static final int SRC_COMPACT_DISC = 0x04;
|
|
private static final int SRC_MASK = 0xFF;
|
|
|
|
private static final int DST_UNKNOWN = 0x0100;
|
|
private static final int DST_SPEAKER = 0x0200;
|
|
private static final int DST_HEADPHONE = 0x0300;
|
|
private static final int DST_LINE_OUT = 0x0400;
|
|
private static final int DST_MASK = 0xFF00;
|
|
|
|
// INSTANCE VARIABLES
|
|
private Port.Info[] portInfos;
|
|
// cache of instantiated ports
|
|
private PortMixerPort[] ports;
|
|
|
|
// instance ID of the native implementation
|
|
private long id = 0;
|
|
|
|
// CONSTRUCTOR
|
|
PortMixer(PortMixerProvider.PortMixerInfo portMixerInfo) {
|
|
// pass in Line.Info, mixer, controls
|
|
super(portMixerInfo, // Mixer.Info
|
|
null, // Control[]
|
|
null, // Line.Info[] sourceLineInfo
|
|
null); // Line.Info[] targetLineInfo
|
|
|
|
if (Printer.trace) Printer.trace(">> PortMixer: constructor");
|
|
|
|
int count = 0;
|
|
int srcLineCount = 0;
|
|
int dstLineCount = 0;
|
|
|
|
try {
|
|
try {
|
|
id = nOpen(getMixerIndex());
|
|
if (id != 0) {
|
|
count = nGetPortCount(id);
|
|
if (count < 0) {
|
|
if (Printer.trace) Printer.trace("nGetPortCount() returned error code: " + count);
|
|
count = 0;
|
|
}
|
|
}
|
|
} catch (Exception e) {}
|
|
|
|
portInfos = new Port.Info[count];
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
int type = nGetPortType(id, i);
|
|
srcLineCount += ((type & SRC_MASK) != 0)?1:0;
|
|
dstLineCount += ((type & DST_MASK) != 0)?1:0;
|
|
portInfos[i] = getPortInfo(i, type);
|
|
}
|
|
} finally {
|
|
if (id != 0) {
|
|
nClose(id);
|
|
}
|
|
id = 0;
|
|
}
|
|
|
|
// fill sourceLineInfo and targetLineInfos with copies of the ones in portInfos
|
|
sourceLineInfo = new Port.Info[srcLineCount];
|
|
targetLineInfo = new Port.Info[dstLineCount];
|
|
|
|
srcLineCount = 0; dstLineCount = 0;
|
|
for (int i = 0; i < count; i++) {
|
|
if (portInfos[i].isSource()) {
|
|
sourceLineInfo[srcLineCount++] = portInfos[i];
|
|
} else {
|
|
targetLineInfo[dstLineCount++] = portInfos[i];
|
|
}
|
|
}
|
|
|
|
if (Printer.trace) Printer.trace("<< PortMixer: constructor completed");
|
|
}
|
|
|
|
|
|
// ABSTRACT MIXER: ABSTRACT METHOD IMPLEMENTATIONS
|
|
|
|
public Line getLine(Line.Info info) throws LineUnavailableException {
|
|
Line.Info fullInfo = getLineInfo(info);
|
|
|
|
if ((fullInfo != null) && (fullInfo instanceof Port.Info)) {
|
|
for (int i = 0; i < portInfos.length; i++) {
|
|
if (fullInfo.equals(portInfos[i])) {
|
|
return getPort(i);
|
|
}
|
|
}
|
|
}
|
|
throw new IllegalArgumentException("Line unsupported: " + info);
|
|
}
|
|
|
|
|
|
public int getMaxLines(Line.Info info) {
|
|
Line.Info fullInfo = getLineInfo(info);
|
|
|
|
// if it's not supported at all, return 0.
|
|
if (fullInfo == null) {
|
|
return 0;
|
|
}
|
|
|
|
if (fullInfo instanceof Port.Info) {
|
|
//return AudioSystem.NOT_SPECIFIED; // if several instances of PortMixerPort
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
protected void implOpen() throws LineUnavailableException {
|
|
if (Printer.trace) Printer.trace(">> PortMixer: implOpen (id="+id+")");
|
|
|
|
// open the mixer device
|
|
id = nOpen(getMixerIndex());
|
|
|
|
if (Printer.trace) Printer.trace("<< PortMixer: implOpen succeeded.");
|
|
}
|
|
|
|
protected void implClose() {
|
|
if (Printer.trace) Printer.trace(">> PortMixer: implClose");
|
|
|
|
// close the mixer device
|
|
long thisID = id;
|
|
id = 0;
|
|
nClose(thisID);
|
|
if (ports != null) {
|
|
for (int i = 0; i < ports.length; i++) {
|
|
if (ports[i] != null) {
|
|
ports[i].disposeControls();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Printer.trace) Printer.trace("<< PortMixer: implClose succeeded");
|
|
}
|
|
|
|
protected void implStart() {}
|
|
protected void implStop() {}
|
|
|
|
// IMPLEMENTATION HELPERS
|
|
|
|
private Port.Info getPortInfo(int portIndex, int type) {
|
|
switch (type) {
|
|
case SRC_UNKNOWN: return new PortInfo(nGetPortName(getID(), portIndex), true);
|
|
case SRC_MICROPHONE: return Port.Info.MICROPHONE;
|
|
case SRC_LINE_IN: return Port.Info.LINE_IN;
|
|
case SRC_COMPACT_DISC: return Port.Info.COMPACT_DISC;
|
|
|
|
case DST_UNKNOWN: return new PortInfo(nGetPortName(getID(), portIndex), false);
|
|
case DST_SPEAKER: return Port.Info.SPEAKER;
|
|
case DST_HEADPHONE: return Port.Info.HEADPHONE;
|
|
case DST_LINE_OUT: return Port.Info.LINE_OUT;
|
|
}
|
|
// should never happen...
|
|
if (Printer.debug) Printer.debug("unknown port type: "+type);
|
|
return null;
|
|
}
|
|
|
|
int getMixerIndex() {
|
|
return ((PortMixerProvider.PortMixerInfo) getMixerInfo()).getIndex();
|
|
}
|
|
|
|
Port getPort(int index) {
|
|
if (ports == null) {
|
|
ports = new PortMixerPort[portInfos.length];
|
|
}
|
|
if (ports[index] == null) {
|
|
ports[index] = new PortMixerPort((Port.Info)portInfos[index], this, index);
|
|
return ports[index];
|
|
}
|
|
// $$fb TODO: return (Port) (ports[index].clone());
|
|
return ports[index];
|
|
}
|
|
|
|
long getID() {
|
|
return id;
|
|
}
|
|
|
|
// INNER CLASSES
|
|
|
|
/**
|
|
* Private inner class representing a Port for the PortMixer.
|
|
*/
|
|
private static final class PortMixerPort extends AbstractLine
|
|
implements Port {
|
|
|
|
private final int portIndex;
|
|
private long id;
|
|
|
|
// CONSTRUCTOR
|
|
private PortMixerPort(Port.Info info,
|
|
PortMixer mixer,
|
|
int portIndex) {
|
|
super(info, mixer, null);
|
|
if (Printer.trace) Printer.trace("PortMixerPort CONSTRUCTOR: info: " + info);
|
|
this.portIndex = portIndex;
|
|
}
|
|
|
|
|
|
// ABSTRACT METHOD IMPLEMENTATIONS
|
|
|
|
// ABSTRACT LINE
|
|
|
|
void implOpen() throws LineUnavailableException {
|
|
if (Printer.trace) Printer.trace(">> PortMixerPort: implOpen().");
|
|
long newID = ((PortMixer) mixer).getID();
|
|
if ((id == 0) || (newID != id) || (controls.length == 0)) {
|
|
id = newID;
|
|
Vector vector = new Vector();
|
|
synchronized (vector) {
|
|
nGetControls(id, portIndex, vector);
|
|
controls = new Control[vector.size()];
|
|
for (int i = 0; i < controls.length; i++) {
|
|
controls[i] = (Control) vector.elementAt(i);
|
|
}
|
|
}
|
|
} else {
|
|
enableControls(controls, true);
|
|
}
|
|
if (Printer.trace) Printer.trace("<< PortMixerPort: implOpen() succeeded");
|
|
}
|
|
|
|
private void enableControls(Control[] controls, boolean enable) {
|
|
for (int i = 0; i < controls.length; i++) {
|
|
if (controls[i] instanceof BoolCtrl) {
|
|
((BoolCtrl) controls[i]).closed = !enable;
|
|
}
|
|
else if (controls[i] instanceof FloatCtrl) {
|
|
((FloatCtrl) controls[i]).closed = !enable;
|
|
}
|
|
else if (controls[i] instanceof CompoundControl) {
|
|
enableControls(((CompoundControl) controls[i]).getMemberControls(), enable);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void disposeControls() {
|
|
enableControls(controls, false);
|
|
controls = new Control[0];
|
|
}
|
|
|
|
|
|
void implClose() {
|
|
if (Printer.trace) Printer.trace(">> PortMixerPort: implClose()");
|
|
// get rid of controls
|
|
enableControls(controls, false);
|
|
if (Printer.trace) Printer.trace("<< PortMixerPort: implClose() succeeded");
|
|
}
|
|
|
|
// METHOD OVERRIDES
|
|
|
|
// this is very similar to open(AudioFormat, int) in AbstractDataLine...
|
|
public void open() throws LineUnavailableException {
|
|
synchronized (mixer) {
|
|
// if the line is not currently open, try to open it with this format and buffer size
|
|
if (!isOpen()) {
|
|
if (Printer.trace) Printer.trace("> PortMixerPort: open");
|
|
// reserve mixer resources for this line
|
|
mixer.open(this);
|
|
try {
|
|
// open the line. may throw LineUnavailableException.
|
|
implOpen();
|
|
|
|
// if we succeeded, set the open state to true and send events
|
|
setOpen(true);
|
|
} catch (LineUnavailableException e) {
|
|
// release mixer resources for this line and then throw the exception
|
|
mixer.close(this);
|
|
throw e;
|
|
}
|
|
if (Printer.trace) Printer.trace("< PortMixerPort: open succeeded");
|
|
}
|
|
}
|
|
}
|
|
|
|
// this is very similar to close() in AbstractDataLine...
|
|
public void close() {
|
|
synchronized (mixer) {
|
|
if (isOpen()) {
|
|
if (Printer.trace) Printer.trace("> PortMixerPort.close()");
|
|
|
|
// set the open state to false and send events
|
|
setOpen(false);
|
|
|
|
// close resources for this line
|
|
implClose();
|
|
|
|
// release mixer resources for this line
|
|
mixer.close(this);
|
|
if (Printer.trace) Printer.trace("< PortMixerPort.close() succeeded");
|
|
}
|
|
}
|
|
}
|
|
|
|
} // class PortMixerPort
|
|
|
|
/**
|
|
* Private inner class representing a BooleanControl for PortMixerPort
|
|
*/
|
|
private static final class BoolCtrl extends BooleanControl {
|
|
// the handle to the native control function
|
|
private final long controlID;
|
|
private boolean closed = false;
|
|
|
|
private static BooleanControl.Type createType(String name) {
|
|
if (name.equals("Mute")) {
|
|
return BooleanControl.Type.MUTE;
|
|
}
|
|
else if (name.equals("Select")) {
|
|
// $$fb add as new static type?
|
|
//return BooleanControl.Type.SELECT;
|
|
}
|
|
return new BCT(name);
|
|
}
|
|
|
|
|
|
private BoolCtrl(long controlID, String name) {
|
|
this(controlID, createType(name));
|
|
}
|
|
|
|
private BoolCtrl(long controlID, BooleanControl.Type typ) {
|
|
super(typ, false);
|
|
this.controlID = controlID;
|
|
}
|
|
|
|
public void setValue(boolean value) {
|
|
if (!closed) {
|
|
nControlSetIntValue(controlID, value?1:0);
|
|
}
|
|
}
|
|
|
|
public boolean getValue() {
|
|
if (!closed) {
|
|
// never use any cached values
|
|
return (nControlGetIntValue(controlID)!=0)?true:false;
|
|
}
|
|
// ??
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* inner class for custom types
|
|
*/
|
|
private static final class BCT extends BooleanControl.Type {
|
|
private BCT(String name) {
|
|
super(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Private inner class representing a CompoundControl for PortMixerPort
|
|
*/
|
|
private static final class CompCtrl extends CompoundControl {
|
|
private CompCtrl(String name, Control[] controls) {
|
|
super(new CCT(name), controls);
|
|
}
|
|
|
|
/**
|
|
* inner class for custom compound control types
|
|
*/
|
|
private static final class CCT extends CompoundControl.Type {
|
|
private CCT(String name) {
|
|
super(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Private inner class representing a BooleanControl for PortMixerPort
|
|
*/
|
|
private static final class FloatCtrl extends FloatControl {
|
|
// the handle to the native control function
|
|
private final long controlID;
|
|
private boolean closed = false;
|
|
|
|
// predefined float control types. See also Ports.h
|
|
private final static FloatControl.Type[] FLOAT_CONTROL_TYPES = {
|
|
null,
|
|
FloatControl.Type.BALANCE,
|
|
FloatControl.Type.MASTER_GAIN,
|
|
FloatControl.Type.PAN,
|
|
FloatControl.Type.VOLUME
|
|
};
|
|
|
|
private FloatCtrl(long controlID, String name,
|
|
float min, float max, float precision, String units) {
|
|
this(controlID, new FCT(name), min, max, precision, units);
|
|
}
|
|
|
|
private FloatCtrl(long controlID, int type,
|
|
float min, float max, float precision, String units) {
|
|
this(controlID, FLOAT_CONTROL_TYPES[type], min, max, precision, units);
|
|
}
|
|
|
|
private FloatCtrl(long controlID, FloatControl.Type typ,
|
|
float min, float max, float precision, String units) {
|
|
super(typ, min, max, precision, 1000, min, units);
|
|
this.controlID = controlID;
|
|
}
|
|
|
|
public void setValue(float value) {
|
|
if (!closed) {
|
|
nControlSetFloatValue(controlID, value);
|
|
}
|
|
}
|
|
|
|
public float getValue() {
|
|
if (!closed) {
|
|
// never use any cached values
|
|
return nControlGetFloatValue(controlID);
|
|
}
|
|
// ??
|
|
return getMinimum();
|
|
}
|
|
|
|
/**
|
|
* inner class for custom types
|
|
*/
|
|
private static final class FCT extends FloatControl.Type {
|
|
private FCT(String name) {
|
|
super(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Private inner class representing a port info
|
|
*/
|
|
private static final class PortInfo extends Port.Info {
|
|
private PortInfo(String name, boolean isSource) {
|
|
super(Port.class, name, isSource);
|
|
}
|
|
}
|
|
|
|
// open the mixer with the given index. Returns a handle ID
|
|
private static native long nOpen(int mixerIndex) throws LineUnavailableException;
|
|
private static native void nClose(long id);
|
|
|
|
// gets the number of ports for this mixer
|
|
private static native int nGetPortCount(long id);
|
|
|
|
// gets the type of the port with this index
|
|
private static native int nGetPortType(long id, int portIndex);
|
|
|
|
// gets the name of the port with this index
|
|
private static native String nGetPortName(long id, int portIndex);
|
|
|
|
// fills the vector with the controls for this port
|
|
private static native void nGetControls(long id, int portIndex, Vector vector);
|
|
|
|
// getters/setters for controls
|
|
private static native void nControlSetIntValue(long controlID, int value);
|
|
private static native int nControlGetIntValue(long controlID);
|
|
private static native void nControlSetFloatValue(long controlID, float value);
|
|
private static native float nControlGetFloatValue(long controlID);
|
|
|
|
}
|