557 lines
16 KiB
Java
557 lines
16 KiB
Java
/*
|
|
* Copyright (c) 1999, 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.Mixer;
|
|
import javax.sound.sampled.Line;
|
|
import javax.sound.sampled.LineUnavailableException;
|
|
|
|
/**
|
|
* Abstract Mixer. Implements Mixer (with abstract methods) and specifies
|
|
* some other common methods for use by our implementation.
|
|
*
|
|
* @author Kara Kytle
|
|
*/
|
|
//$$fb 2002-07-26: let AbstractMixer be an AbstractLine and NOT an AbstractDataLine!
|
|
abstract class AbstractMixer extends AbstractLine implements Mixer {
|
|
|
|
// STATIC VARIABLES
|
|
protected static final int PCM = 0;
|
|
protected static final int ULAW = 1;
|
|
protected static final int ALAW = 2;
|
|
|
|
|
|
// IMMUTABLE PROPERTIES
|
|
|
|
/**
|
|
* Info object describing this mixer.
|
|
*/
|
|
private final Mixer.Info mixerInfo;
|
|
|
|
/**
|
|
* source lines provided by this mixer
|
|
*/
|
|
protected Line.Info[] sourceLineInfo;
|
|
|
|
/**
|
|
* target lines provided by this mixer
|
|
*/
|
|
protected Line.Info[] targetLineInfo;
|
|
|
|
/**
|
|
* if any line of this mixer is started
|
|
*/
|
|
private boolean started = false;
|
|
|
|
/**
|
|
* if this mixer had been opened manually with open()
|
|
* If it was, then it won't be closed automatically,
|
|
* only when close() is called manually.
|
|
*/
|
|
private boolean manuallyOpened = false;
|
|
|
|
|
|
/**
|
|
* Supported formats for the mixer.
|
|
*/
|
|
//$$fb DELETE
|
|
//protected Vector formats = new Vector();
|
|
|
|
|
|
// STATE VARIABLES
|
|
|
|
|
|
/**
|
|
* Source lines (ports) currently open
|
|
*/
|
|
private final Vector sourceLines = new Vector();
|
|
|
|
|
|
/**
|
|
* Target lines currently open.
|
|
*/
|
|
private final Vector targetLines = new Vector();
|
|
|
|
|
|
/**
|
|
* Constructs a new AbstractMixer.
|
|
* @param mixer the mixer with which this line is associated
|
|
* @param controls set of supported controls
|
|
*/
|
|
protected AbstractMixer(Mixer.Info mixerInfo,
|
|
Control[] controls,
|
|
Line.Info[] sourceLineInfo,
|
|
Line.Info[] targetLineInfo) {
|
|
|
|
// Line.Info, AbstractMixer, Control[]
|
|
super(new Line.Info(Mixer.class), null, controls);
|
|
|
|
// setup the line part
|
|
this.mixer = this;
|
|
if (controls == null) {
|
|
controls = new Control[0];
|
|
}
|
|
|
|
// setup the mixer part
|
|
this.mixerInfo = mixerInfo;
|
|
this.sourceLineInfo = sourceLineInfo;
|
|
this.targetLineInfo = targetLineInfo;
|
|
}
|
|
|
|
|
|
// MIXER METHODS
|
|
|
|
|
|
public final Mixer.Info getMixerInfo() {
|
|
return mixerInfo;
|
|
}
|
|
|
|
|
|
public final Line.Info[] getSourceLineInfo() {
|
|
Line.Info[] localArray = new Line.Info[sourceLineInfo.length];
|
|
System.arraycopy(sourceLineInfo, 0, localArray, 0, sourceLineInfo.length);
|
|
return localArray;
|
|
}
|
|
|
|
|
|
public final Line.Info[] getTargetLineInfo() {
|
|
|
|
Line.Info[] localArray = new Line.Info[targetLineInfo.length];
|
|
System.arraycopy(targetLineInfo, 0, localArray, 0, targetLineInfo.length);
|
|
return localArray;
|
|
}
|
|
|
|
|
|
public final Line.Info[] getSourceLineInfo(Line.Info info) {
|
|
|
|
int i;
|
|
Vector vec = new Vector();
|
|
|
|
for (i = 0; i < sourceLineInfo.length; i++) {
|
|
|
|
if (info.matches(sourceLineInfo[i])) {
|
|
vec.addElement(sourceLineInfo[i]);
|
|
}
|
|
}
|
|
|
|
Line.Info[] returnedArray = new Line.Info[vec.size()];
|
|
for (i = 0; i < returnedArray.length; i++) {
|
|
returnedArray[i] = (Line.Info)vec.elementAt(i);
|
|
}
|
|
|
|
return returnedArray;
|
|
}
|
|
|
|
|
|
public final Line.Info[] getTargetLineInfo(Line.Info info) {
|
|
|
|
int i;
|
|
Vector vec = new Vector();
|
|
|
|
for (i = 0; i < targetLineInfo.length; i++) {
|
|
|
|
if (info.matches(targetLineInfo[i])) {
|
|
vec.addElement(targetLineInfo[i]);
|
|
}
|
|
}
|
|
|
|
Line.Info[] returnedArray = new Line.Info[vec.size()];
|
|
for (i = 0; i < returnedArray.length; i++) {
|
|
returnedArray[i] = (Line.Info)vec.elementAt(i);
|
|
}
|
|
|
|
return returnedArray;
|
|
}
|
|
|
|
|
|
public final boolean isLineSupported(Line.Info info) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < sourceLineInfo.length; i++) {
|
|
|
|
if (info.matches(sourceLineInfo[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < targetLineInfo.length; i++) {
|
|
|
|
if (info.matches(targetLineInfo[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
public abstract Line getLine(Line.Info info) throws LineUnavailableException;
|
|
|
|
public abstract int getMaxLines(Line.Info info);
|
|
|
|
protected abstract void implOpen() throws LineUnavailableException;
|
|
protected abstract void implStart();
|
|
protected abstract void implStop();
|
|
protected abstract void implClose();
|
|
|
|
|
|
public final Line[] getSourceLines() {
|
|
|
|
Line[] localLines;
|
|
|
|
synchronized(sourceLines) {
|
|
|
|
localLines = new Line[sourceLines.size()];
|
|
|
|
for (int i = 0; i < localLines.length; i++) {
|
|
localLines[i] = (Line)sourceLines.elementAt(i);
|
|
}
|
|
}
|
|
|
|
return localLines;
|
|
}
|
|
|
|
|
|
public final Line[] getTargetLines() {
|
|
|
|
Line[] localLines;
|
|
|
|
synchronized(targetLines) {
|
|
|
|
localLines = new Line[targetLines.size()];
|
|
|
|
for (int i = 0; i < localLines.length; i++) {
|
|
localLines[i] = (Line)targetLines.elementAt(i);
|
|
}
|
|
}
|
|
|
|
return localLines;
|
|
}
|
|
|
|
|
|
/**
|
|
* Default implementation always throws an exception.
|
|
*/
|
|
public final void synchronize(Line[] lines, boolean maintainSync) {
|
|
throw new IllegalArgumentException("Synchronization not supported by this mixer.");
|
|
}
|
|
|
|
|
|
/**
|
|
* Default implementation always throws an exception.
|
|
*/
|
|
public final void unsynchronize(Line[] lines) {
|
|
throw new IllegalArgumentException("Synchronization not supported by this mixer.");
|
|
}
|
|
|
|
|
|
/**
|
|
* Default implementation always returns false.
|
|
*/
|
|
public final boolean isSynchronizationSupported(Line[] lines,
|
|
boolean maintainSync) {
|
|
return false;
|
|
}
|
|
|
|
|
|
// OVERRIDES OF ABSTRACT DATA LINE METHODS
|
|
|
|
/**
|
|
* This implementation tries to open the mixer with its current format and buffer size settings.
|
|
*/
|
|
public final synchronized void open() throws LineUnavailableException {
|
|
open(true);
|
|
}
|
|
|
|
/**
|
|
* This implementation tries to open the mixer with its current format and buffer size settings.
|
|
*/
|
|
final synchronized void open(boolean manual) throws LineUnavailableException {
|
|
if (Printer.trace) Printer.trace(">> AbstractMixer: open()");
|
|
if (!isOpen()) {
|
|
implOpen();
|
|
// if the mixer is not currently open, set open to true and send event
|
|
setOpen(true);
|
|
if (manual) {
|
|
manuallyOpened = true;
|
|
}
|
|
}
|
|
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: open() succeeded");
|
|
}
|
|
|
|
|
|
// METHOD FOR INTERNAL IMPLEMENTATION USE
|
|
|
|
|
|
/**
|
|
* The default implementation of this method just determines whether
|
|
* this line is a source or target line, calls open(no-arg) on the
|
|
* mixer, and adds the line to the appropriate vector.
|
|
* The mixer may be opened at a format different than the line's
|
|
* format if it is a DataLine.
|
|
*/
|
|
final synchronized void open(Line line) throws LineUnavailableException {
|
|
|
|
if (Printer.trace) Printer.trace(">> AbstractMixer: open(line = " + line + ")");
|
|
|
|
// $$kk: 06.11.99: ignore ourselves for now
|
|
if (this.equals(line)) {
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: open(" + line + ") nothing done");
|
|
return;
|
|
}
|
|
|
|
// source line?
|
|
if (isSourceLine(line.getLineInfo())) {
|
|
if (! sourceLines.contains(line) ) {
|
|
// call the no-arg open method for the mixer; it should open at its
|
|
// default format if it is not open yet
|
|
open(false);
|
|
|
|
// we opened successfully! add the line to the list
|
|
sourceLines.addElement(line);
|
|
}
|
|
} else {
|
|
// target line?
|
|
if(isTargetLine(line.getLineInfo())) {
|
|
if (! targetLines.contains(line) ) {
|
|
// call the no-arg open method for the mixer; it should open at its
|
|
// default format if it is not open yet
|
|
open(false);
|
|
|
|
// we opened successfully! add the line to the list
|
|
targetLines.addElement(line);
|
|
}
|
|
} else {
|
|
if (Printer.err) Printer.err("Unknown line received for AbstractMixer.open(Line): " + line);
|
|
}
|
|
}
|
|
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: open(" + line + ") completed");
|
|
}
|
|
|
|
|
|
/**
|
|
* Removes this line from the list of open source lines and
|
|
* open target lines, if it exists in either.
|
|
* If the list is now empty, closes the mixer.
|
|
*/
|
|
final synchronized void close(Line line) {
|
|
|
|
if (Printer.trace) Printer.trace(">> AbstractMixer: close(" + line + ")");
|
|
|
|
// $$kk: 06.11.99: ignore ourselves for now
|
|
if (this.equals(line)) {
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: close(" + line + ") nothing done");
|
|
return;
|
|
}
|
|
|
|
sourceLines.removeElement(line);
|
|
targetLines.removeElement(line);
|
|
|
|
if (Printer.debug) Printer.debug("AbstractMixer: close(line): sourceLines.size() now: " + sourceLines.size());
|
|
if (Printer.debug) Printer.debug("AbstractMixer: close(line): targetLines.size() now: " + targetLines.size());
|
|
|
|
|
|
if (sourceLines.isEmpty() && targetLines.isEmpty() && !manuallyOpened) {
|
|
if (Printer.trace) Printer.trace("AbstractMixer: close(" + line + "): need to close the mixer");
|
|
close();
|
|
}
|
|
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: close(" + line + ") succeeded");
|
|
}
|
|
|
|
|
|
/**
|
|
* Close all lines and then close this mixer.
|
|
*/
|
|
public final synchronized void close() {
|
|
if (Printer.trace) Printer.trace(">> AbstractMixer: close()");
|
|
if (isOpen()) {
|
|
// close all source lines
|
|
Line[] localLines = getSourceLines();
|
|
for (int i = 0; i<localLines.length; i++) {
|
|
localLines[i].close();
|
|
}
|
|
|
|
// close all target lines
|
|
localLines = getTargetLines();
|
|
for (int i = 0; i<localLines.length; i++) {
|
|
localLines[i].close();
|
|
}
|
|
|
|
implClose();
|
|
|
|
// set the open state to false and send events
|
|
setOpen(false);
|
|
}
|
|
manuallyOpened = false;
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: close() succeeded");
|
|
}
|
|
|
|
/**
|
|
* Starts the mixer.
|
|
*/
|
|
final synchronized void start(Line line) {
|
|
|
|
if (Printer.trace) Printer.trace(">> AbstractMixer: start(" + line + ")");
|
|
|
|
// $$kk: 06.11.99: ignore ourselves for now
|
|
if (this.equals(line)) {
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: start(" + line + ") nothing done");
|
|
return;
|
|
}
|
|
|
|
// we just start the mixer regardless of anything else here.
|
|
if (!started) {
|
|
if (Printer.debug) Printer.debug("AbstractMixer: start(line): starting the mixer");
|
|
implStart();
|
|
started = true;
|
|
}
|
|
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: start(" + line + ") succeeded");
|
|
}
|
|
|
|
|
|
/**
|
|
* Stops the mixer if this was the last running line.
|
|
*/
|
|
final synchronized void stop(Line line) {
|
|
|
|
if (Printer.trace) Printer.trace(">> AbstractMixer: stop(" + line + ")");
|
|
|
|
// $$kk: 06.11.99: ignore ourselves for now
|
|
if (this.equals(line)) {
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") nothing done");
|
|
return;
|
|
}
|
|
|
|
Vector localSourceLines = (Vector)sourceLines.clone();
|
|
for (int i = 0; i < localSourceLines.size(); i++) {
|
|
|
|
// if any other open line is running, return
|
|
|
|
// this covers clips and source data lines
|
|
if (localSourceLines.elementAt(i) instanceof AbstractDataLine) {
|
|
AbstractDataLine sourceLine = (AbstractDataLine)localSourceLines.elementAt(i);
|
|
if ( sourceLine.isStartedRunning() && (!sourceLine.equals(line)) ) {
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") found running sourceLine: " + sourceLine);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector localTargetLines = (Vector)targetLines.clone();
|
|
for (int i = 0; i < localTargetLines.size(); i++) {
|
|
|
|
// if any other open line is running, return
|
|
// this covers target data lines
|
|
if (localTargetLines.elementAt(i) instanceof AbstractDataLine) {
|
|
AbstractDataLine targetLine = (AbstractDataLine)localTargetLines.elementAt(i);
|
|
if ( targetLine.isStartedRunning() && (!targetLine.equals(line)) ) {
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") found running targetLine: " + targetLine);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// otherwise, stop
|
|
if (Printer.debug) Printer.debug("AbstractMixer: stop(line): stopping the mixer");
|
|
started = false;
|
|
implStop();
|
|
|
|
if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") succeeded");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Determines whether this is a source line for this mixer.
|
|
* Right now this just checks whether it's supported, but should
|
|
* check whether it actually belongs to this mixer....
|
|
*/
|
|
final boolean isSourceLine(Line.Info info) {
|
|
|
|
for (int i = 0; i < sourceLineInfo.length; i++) {
|
|
if (info.matches(sourceLineInfo[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Determines whether this is a target line for this mixer.
|
|
* Right now this just checks whether it's supported, but should
|
|
* check whether it actually belongs to this mixer....
|
|
*/
|
|
final boolean isTargetLine(Line.Info info) {
|
|
|
|
for (int i = 0; i < targetLineInfo.length; i++) {
|
|
if (info.matches(targetLineInfo[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the first complete Line.Info object it finds that
|
|
* matches the one specified, or null if no matching Line.Info
|
|
* object is found.
|
|
*/
|
|
final Line.Info getLineInfo(Line.Info info) {
|
|
if (info == null) {
|
|
return null;
|
|
}
|
|
// $$kk: 05.31.99: need to change this so that
|
|
// the format and buffer size get set in the
|
|
// returned info object for data lines??
|
|
for (int i = 0; i < sourceLineInfo.length; i++) {
|
|
if (info.matches(sourceLineInfo[i])) {
|
|
return sourceLineInfo[i];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < targetLineInfo.length; i++) {
|
|
if (info.matches(targetLineInfo[i])) {
|
|
return targetLineInfo[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|