752 lines
26 KiB
Java
752 lines
26 KiB
Java
/*
|
|
* Copyright (c) 1999, 2016, 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.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Collections;
|
|
|
|
import javax.sound.midi.*;
|
|
|
|
|
|
/**
|
|
* Abstract AbstractMidiDevice class representing functionality shared by
|
|
* MidiInDevice and MidiOutDevice objects.
|
|
*
|
|
* @author David Rivas
|
|
* @author Kara Kytle
|
|
* @author Matthias Pfisterer
|
|
* @author Florian Bomers
|
|
*/
|
|
abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice {
|
|
|
|
// STATIC VARIABLES
|
|
private static final boolean TRACE_TRANSMITTER = false;
|
|
|
|
// INSTANCE VARIABLES
|
|
|
|
private ArrayList<Receiver> receiverList;
|
|
|
|
private TransmitterList transmitterList;
|
|
|
|
// lock to protect receiverList and transmitterList
|
|
// from simultaneous creation and destruction
|
|
// reduces possibility of deadlock, compared to
|
|
// synchronizing to the class instance
|
|
private final Object traRecLock = new Object();
|
|
|
|
// DEVICE ATTRIBUTES
|
|
|
|
private final MidiDevice.Info info;
|
|
|
|
|
|
// DEVICE STATE
|
|
|
|
private volatile boolean open;
|
|
private int openRefCount;
|
|
|
|
/** List of Receivers and Transmitters that opened the device implicitely.
|
|
*/
|
|
private List openKeepingObjects;
|
|
|
|
/**
|
|
* This is the device handle returned from native code
|
|
*/
|
|
protected volatile long id;
|
|
|
|
|
|
|
|
// CONSTRUCTOR
|
|
|
|
|
|
/**
|
|
* Constructs an AbstractMidiDevice with the specified info object.
|
|
* @param info the description of the device
|
|
*/
|
|
/*
|
|
* The initial mode and and only supported mode default to OMNI_ON_POLY.
|
|
*/
|
|
protected AbstractMidiDevice(MidiDevice.Info info) {
|
|
|
|
if(Printer.trace) Printer.trace(">> AbstractMidiDevice CONSTRUCTOR");
|
|
|
|
this.info = info;
|
|
openRefCount = 0;
|
|
|
|
if(Printer.trace) Printer.trace("<< AbstractMidiDevice CONSTRUCTOR completed");
|
|
}
|
|
|
|
|
|
// MIDI DEVICE METHODS
|
|
|
|
public final MidiDevice.Info getDeviceInfo() {
|
|
return info;
|
|
}
|
|
|
|
/** Open the device from an application program.
|
|
* Setting the open reference count to -1 here prevents Transmitters and Receivers that
|
|
* opened the the device implicitly from closing it. The only way to close the device after
|
|
* this call is a call to close().
|
|
*/
|
|
public final void open() throws MidiUnavailableException {
|
|
if (Printer.trace) Printer.trace("> AbstractMidiDevice: open()");
|
|
synchronized(this) {
|
|
openRefCount = -1;
|
|
doOpen();
|
|
}
|
|
if (Printer.trace) Printer.trace("< AbstractMidiDevice: open() completed");
|
|
}
|
|
|
|
|
|
|
|
/** Open the device implicitly.
|
|
* This method is intended to be used by AbstractReceiver
|
|
* and BasicTransmitter. Actually, it is called by getReceiverReferenceCounting() and
|
|
* getTransmitterReferenceCounting(). These, in turn, are called by MidiSytem on calls to
|
|
* getReceiver() and getTransmitter(). The former methods should pass the Receiver or
|
|
* Transmitter just created as the object parameter to this method. Storing references to
|
|
* these objects is necessary to be able to decide later (when it comes to closing) if
|
|
* R/T's are ones that opened the device implicitly.
|
|
*
|
|
* @object The Receiver or Transmitter instance that triggered this implicit open.
|
|
*/
|
|
private void openInternal(Object object) throws MidiUnavailableException {
|
|
if (Printer.trace) Printer.trace("> AbstractMidiDevice: openInternal()");
|
|
synchronized(this) {
|
|
if (openRefCount != -1) {
|
|
openRefCount++;
|
|
getOpenKeepingObjects().add(object);
|
|
}
|
|
// double calls to doOpens() will be catched by the open flag.
|
|
doOpen();
|
|
}
|
|
if (Printer.trace) Printer.trace("< AbstractMidiDevice: openInternal() completed");
|
|
}
|
|
|
|
|
|
private void doOpen() throws MidiUnavailableException {
|
|
if (Printer.trace) Printer.trace("> AbstractMidiDevice: doOpen()");
|
|
synchronized(this) {
|
|
if (! isOpen()) {
|
|
implOpen();
|
|
open = true;
|
|
}
|
|
}
|
|
if (Printer.trace) Printer.trace("< AbstractMidiDevice: doOpen() completed");
|
|
}
|
|
|
|
|
|
public final void close() {
|
|
if (Printer.trace) Printer.trace("> AbstractMidiDevice: close()");
|
|
synchronized (this) {
|
|
doClose();
|
|
openRefCount = 0;
|
|
}
|
|
if (Printer.trace) Printer.trace("< AbstractMidiDevice: close() completed");
|
|
}
|
|
|
|
|
|
/** Close the device for an object that implicitely opened it.
|
|
* This method is intended to be used by Transmitter.close() and Receiver.close().
|
|
* Those methods should pass this for the object parameter. Since Transmitters or Receivers
|
|
* do not know if their device has been opened implicitely because of them, they call this
|
|
* method in any case. This method now is able to seperate Receivers/Transmitters that opened
|
|
* the device implicitely from those that didn't by looking up the R/T in the
|
|
* openKeepingObjects list. Only if the R/T is contained there, the reference count is
|
|
* reduced.
|
|
*
|
|
* @param object The object that might have been opening the device implicitely (for now,
|
|
* this may be a Transmitter or receiver).
|
|
*/
|
|
public final void closeInternal(Object object) {
|
|
if (Printer.trace) Printer.trace("> AbstractMidiDevice: closeInternal()");
|
|
synchronized(this) {
|
|
if (getOpenKeepingObjects().remove(object)) {
|
|
if (openRefCount > 0) {
|
|
openRefCount--;
|
|
if (openRefCount == 0) {
|
|
doClose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Printer.trace) Printer.trace("< AbstractMidiDevice: closeInternal() completed");
|
|
}
|
|
|
|
|
|
public final void doClose() {
|
|
if (Printer.trace) Printer.trace("> AbstractMidiDevice: doClose()");
|
|
synchronized(this) {
|
|
if (isOpen()) {
|
|
implClose();
|
|
open = false;
|
|
}
|
|
}
|
|
if (Printer.trace) Printer.trace("< AbstractMidiDevice: doClose() completed");
|
|
}
|
|
|
|
|
|
public final boolean isOpen() {
|
|
return open;
|
|
}
|
|
|
|
|
|
protected void implClose() {
|
|
synchronized (traRecLock) {
|
|
if (receiverList != null) {
|
|
// close all receivers
|
|
for(int i = 0; i < receiverList.size(); i++) {
|
|
receiverList.get(i).close();
|
|
}
|
|
receiverList.clear();
|
|
}
|
|
if (transmitterList != null) {
|
|
// close all transmitters
|
|
transmitterList.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This implementation always returns -1.
|
|
* Devices that actually provide this should over-ride
|
|
* this method.
|
|
*/
|
|
public long getMicrosecondPosition() {
|
|
return -1;
|
|
}
|
|
|
|
|
|
/** Return the maximum number of Receivers supported by this device.
|
|
Depending on the return value of hasReceivers(), this method returns either 0 or -1.
|
|
Subclasses should rather override hasReceivers() than override this method.
|
|
*/
|
|
public final int getMaxReceivers() {
|
|
if (hasReceivers()) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/** Return the maximum number of Transmitters supported by this device.
|
|
Depending on the return value of hasTransmitters(), this method returns either 0 or -1.
|
|
Subclasses should override hasTransmitters().
|
|
*/
|
|
public final int getMaxTransmitters() {
|
|
if (hasTransmitters()) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/** Retrieve a Receiver for this device.
|
|
This method returns the value returned by createReceiver(), if it doesn't throw
|
|
an exception. Subclasses should rather override createReceiver() than override
|
|
this method.
|
|
If createReceiver returns a Receiver, it is added to the internal list
|
|
of Receivers (see getReceiversList)
|
|
*/
|
|
public final Receiver getReceiver() throws MidiUnavailableException {
|
|
Receiver receiver;
|
|
synchronized (traRecLock) {
|
|
receiver = createReceiver(); // may throw MidiUnavailableException
|
|
getReceiverList().add(receiver);
|
|
}
|
|
return receiver;
|
|
}
|
|
|
|
|
|
public final List<Receiver> getReceivers() {
|
|
List<Receiver> recs;
|
|
synchronized (traRecLock) {
|
|
if (receiverList == null) {
|
|
recs = Collections.unmodifiableList(new ArrayList<Receiver>(0));
|
|
} else {
|
|
recs = Collections.unmodifiableList
|
|
((List<Receiver>) (receiverList.clone()));
|
|
}
|
|
}
|
|
return recs;
|
|
}
|
|
|
|
|
|
/**
|
|
* This implementation uses createTransmitter, which may throw an exception.
|
|
* If a transmitter is returned in createTransmitter, it is added to the internal
|
|
* TransmitterList
|
|
*/
|
|
public final Transmitter getTransmitter() throws MidiUnavailableException {
|
|
Transmitter transmitter;
|
|
synchronized (traRecLock) {
|
|
transmitter = createTransmitter(); // may throw MidiUnavailableException
|
|
getTransmitterList().add(transmitter);
|
|
}
|
|
return transmitter;
|
|
}
|
|
|
|
|
|
public final List<Transmitter> getTransmitters() {
|
|
List<Transmitter> tras;
|
|
synchronized (traRecLock) {
|
|
if (transmitterList == null
|
|
|| transmitterList.transmitters.size() == 0) {
|
|
tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0));
|
|
} else {
|
|
tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone()));
|
|
}
|
|
}
|
|
return tras;
|
|
}
|
|
|
|
|
|
// HELPER METHODS
|
|
|
|
final long getId() {
|
|
return id;
|
|
}
|
|
|
|
|
|
// REFERENCE COUNTING
|
|
|
|
/** Retrieve a Receiver and open the device implicitly.
|
|
This method is called by MidiSystem.getReceiver().
|
|
*/
|
|
public final Receiver getReceiverReferenceCounting()
|
|
throws MidiUnavailableException {
|
|
/* Keep this order of commands! If getReceiver() throws an exception,
|
|
openInternal() should not be called!
|
|
*/
|
|
Receiver receiver;
|
|
synchronized (traRecLock) {
|
|
receiver = getReceiver();
|
|
AbstractMidiDevice.this.openInternal(receiver);
|
|
}
|
|
return receiver;
|
|
}
|
|
|
|
|
|
/** Retrieve a Transmitter and open the device implicitly.
|
|
This method is called by MidiSystem.getTransmitter().
|
|
*/
|
|
public final Transmitter getTransmitterReferenceCounting()
|
|
throws MidiUnavailableException {
|
|
/* Keep this order of commands! If getTransmitter() throws an exception,
|
|
openInternal() should not be called!
|
|
*/
|
|
Transmitter transmitter;
|
|
synchronized (traRecLock) {
|
|
transmitter = getTransmitter();
|
|
AbstractMidiDevice.this.openInternal(transmitter);
|
|
}
|
|
return transmitter;
|
|
}
|
|
|
|
|
|
/** Return the list of objects that have opened the device implicitely.
|
|
*/
|
|
private synchronized List getOpenKeepingObjects() {
|
|
if (openKeepingObjects == null) {
|
|
openKeepingObjects = new ArrayList();
|
|
}
|
|
return openKeepingObjects;
|
|
}
|
|
|
|
|
|
|
|
// RECEIVER HANDLING METHODS
|
|
|
|
|
|
/** Return the internal list of Receivers, possibly creating it first.
|
|
*/
|
|
private List<Receiver> getReceiverList() {
|
|
synchronized (traRecLock) {
|
|
if (receiverList == null) {
|
|
receiverList = new ArrayList<Receiver>();
|
|
}
|
|
}
|
|
return receiverList;
|
|
}
|
|
|
|
|
|
/** Returns if this device supports Receivers.
|
|
Subclasses that use Receivers should override this method to
|
|
return true. They also should override createReceiver().
|
|
|
|
@return true, if the device supports Receivers, false otherwise.
|
|
*/
|
|
protected boolean hasReceivers() {
|
|
return false;
|
|
}
|
|
|
|
|
|
/** Create a Receiver object.
|
|
throwing an exception here means that Receivers aren't enabled.
|
|
Subclasses that use Receivers should override this method with
|
|
one that returns objects implementing Receiver.
|
|
Classes overriding this method should also override hasReceivers()
|
|
to return true.
|
|
*/
|
|
protected Receiver createReceiver() throws MidiUnavailableException {
|
|
throw new MidiUnavailableException("MIDI IN receiver not available");
|
|
}
|
|
|
|
|
|
|
|
// TRANSMITTER HANDLING
|
|
|
|
/** Return the internal list of Transmitters, possibly creating it first.
|
|
*/
|
|
final TransmitterList getTransmitterList() {
|
|
synchronized (traRecLock) {
|
|
if (transmitterList == null) {
|
|
transmitterList = new TransmitterList();
|
|
}
|
|
}
|
|
return transmitterList;
|
|
}
|
|
|
|
|
|
/** Returns if this device supports Transmitters.
|
|
Subclasses that use Transmitters should override this method to
|
|
return true. They also should override createTransmitter().
|
|
|
|
@return true, if the device supports Transmitters, false otherwise.
|
|
*/
|
|
protected boolean hasTransmitters() {
|
|
return false;
|
|
}
|
|
|
|
|
|
/** Create a Transmitter object.
|
|
throwing an exception here means that Transmitters aren't enabled.
|
|
Subclasses that use Transmitters should override this method with
|
|
one that returns objects implementing Transmitters.
|
|
Classes overriding this method should also override hasTransmitters()
|
|
to return true.
|
|
*/
|
|
protected Transmitter createTransmitter() throws MidiUnavailableException {
|
|
throw new MidiUnavailableException("MIDI OUT transmitter not available");
|
|
}
|
|
|
|
// ABSTRACT METHODS
|
|
|
|
protected abstract void implOpen() throws MidiUnavailableException;
|
|
|
|
|
|
/**
|
|
* close this device if discarded by the garbage collector
|
|
*/
|
|
protected final void finalize() {
|
|
close();
|
|
}
|
|
|
|
// INNER CLASSES
|
|
|
|
/** Base class for Receivers.
|
|
Subclasses that use Receivers must use this base class, since it
|
|
contains magic necessary to manage implicit closing the device.
|
|
This is necessary for Receivers retrieved via MidiSystem.getReceiver()
|
|
(which opens the device implicitely).
|
|
*/
|
|
abstract class AbstractReceiver implements MidiDeviceReceiver {
|
|
private volatile boolean open = true;
|
|
|
|
|
|
/** Deliver a MidiMessage.
|
|
This method contains magic related to the closed state of a
|
|
Receiver. Therefore, subclasses should not override this method.
|
|
Instead, they should implement implSend().
|
|
*/
|
|
@Override
|
|
public final synchronized void send(final MidiMessage message,
|
|
final long timeStamp) {
|
|
if (!open) {
|
|
throw new IllegalStateException("Receiver is not open");
|
|
}
|
|
implSend(message, timeStamp);
|
|
}
|
|
|
|
abstract void implSend(MidiMessage message, long timeStamp);
|
|
|
|
/** Close the Receiver.
|
|
* Here, the call to the magic method closeInternal() takes place.
|
|
* Therefore, subclasses that override this method must call
|
|
* 'super.close()'.
|
|
*/
|
|
@Override
|
|
public final void close() {
|
|
open = false;
|
|
synchronized (AbstractMidiDevice.this.traRecLock) {
|
|
AbstractMidiDevice.this.getReceiverList().remove(this);
|
|
}
|
|
AbstractMidiDevice.this.closeInternal(this);
|
|
}
|
|
|
|
@Override
|
|
public final MidiDevice getMidiDevice() {
|
|
return AbstractMidiDevice.this;
|
|
}
|
|
|
|
final boolean isOpen() {
|
|
return open;
|
|
}
|
|
|
|
//$$fb is that a good idea?
|
|
//protected void finalize() {
|
|
// close();
|
|
//}
|
|
|
|
} // class AbstractReceiver
|
|
|
|
|
|
/**
|
|
* Transmitter base class.
|
|
* This class especially makes sure the device is closed if it
|
|
* has been opened implicitly by a call to MidiSystem.getTransmitter().
|
|
* The logic of doing so is actually in closeInternal().
|
|
*
|
|
* Also, it has some optimizations regarding sending to the Receivers,
|
|
* for known Receivers, and managing itself in the TransmitterList.
|
|
*/
|
|
class BasicTransmitter implements MidiDeviceTransmitter {
|
|
|
|
private Receiver receiver = null;
|
|
TransmitterList tlist = null;
|
|
|
|
protected BasicTransmitter() {
|
|
}
|
|
|
|
private void setTransmitterList(TransmitterList tlist) {
|
|
this.tlist = tlist;
|
|
}
|
|
|
|
public final void setReceiver(Receiver receiver) {
|
|
if (tlist != null && this.receiver != receiver) {
|
|
if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver);
|
|
tlist.receiverChanged(this, this.receiver, receiver);
|
|
this.receiver = receiver;
|
|
}
|
|
}
|
|
|
|
public final Receiver getReceiver() {
|
|
return receiver;
|
|
}
|
|
|
|
|
|
/** Close the Transmitter.
|
|
* Here, the call to the magic method closeInternal() takes place.
|
|
* Therefore, subclasses that override this method must call
|
|
* 'super.close()'.
|
|
*/
|
|
public final void close() {
|
|
AbstractMidiDevice.this.closeInternal(this);
|
|
if (tlist != null) {
|
|
tlist.receiverChanged(this, this.receiver, null);
|
|
tlist.remove(this);
|
|
tlist = null;
|
|
}
|
|
}
|
|
|
|
public final MidiDevice getMidiDevice() {
|
|
return AbstractMidiDevice.this;
|
|
}
|
|
|
|
} // class BasicTransmitter
|
|
|
|
|
|
/**
|
|
* a class to manage a list of transmitters
|
|
*/
|
|
final class TransmitterList {
|
|
|
|
private final ArrayList<Transmitter> transmitters = new ArrayList<Transmitter>();
|
|
private MidiOutDevice.MidiOutReceiver midiOutReceiver;
|
|
|
|
// how many transmitters must be present for optimized
|
|
// handling
|
|
private int optimizedReceiverCount = 0;
|
|
|
|
|
|
private void add(Transmitter t) {
|
|
synchronized(transmitters) {
|
|
transmitters.add(t);
|
|
}
|
|
if (t instanceof BasicTransmitter) {
|
|
((BasicTransmitter) t).setTransmitterList(this);
|
|
}
|
|
if (Printer.debug) Printer.debug("--added transmitter "+t);
|
|
}
|
|
|
|
private void remove(Transmitter t) {
|
|
synchronized(transmitters) {
|
|
int index = transmitters.indexOf(t);
|
|
if (index >= 0) {
|
|
transmitters.remove(index);
|
|
if (Printer.debug) Printer.debug("--removed transmitter "+t);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void receiverChanged(BasicTransmitter t,
|
|
Receiver oldR,
|
|
Receiver newR) {
|
|
synchronized(transmitters) {
|
|
// some optimization
|
|
if (midiOutReceiver == oldR) {
|
|
midiOutReceiver = null;
|
|
}
|
|
if (newR != null) {
|
|
if ((newR instanceof MidiOutDevice.MidiOutReceiver)
|
|
&& (midiOutReceiver == null)) {
|
|
midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR);
|
|
}
|
|
}
|
|
optimizedReceiverCount =
|
|
((midiOutReceiver!=null)?1:0);
|
|
}
|
|
// more potential for optimization here
|
|
}
|
|
|
|
|
|
/** closes all transmitters and empties the list */
|
|
void close() {
|
|
synchronized (transmitters) {
|
|
for(int i = 0; i < transmitters.size(); i++) {
|
|
transmitters.get(i).close();
|
|
}
|
|
transmitters.clear();
|
|
}
|
|
if (Printer.trace) Printer.trace("TransmitterList.close() succeeded");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Send this message to all receivers
|
|
* status = packedMessage & 0xFF
|
|
* data1 = (packedMessage & 0xFF00) >> 8;
|
|
* data1 = (packedMessage & 0xFF0000) >> 16;
|
|
*/
|
|
void sendMessage(int packedMessage, long timeStamp) {
|
|
try {
|
|
synchronized(transmitters) {
|
|
int size = transmitters.size();
|
|
if (optimizedReceiverCount == size) {
|
|
if (midiOutReceiver != null) {
|
|
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver");
|
|
midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp);
|
|
}
|
|
} else {
|
|
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers");
|
|
for (int i = 0; i < size; i++) {
|
|
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
|
|
if (receiver != null) {
|
|
if (optimizedReceiverCount > 0) {
|
|
if (receiver instanceof MidiOutDevice.MidiOutReceiver) {
|
|
((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);
|
|
} else {
|
|
receiver.send(new FastShortMessage(packedMessage), timeStamp);
|
|
}
|
|
} else {
|
|
receiver.send(new FastShortMessage(packedMessage), timeStamp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (InvalidMidiDataException e) {
|
|
// this happens when invalid data comes over the wire. Ignore it.
|
|
}
|
|
}
|
|
|
|
void sendMessage(byte[] data, long timeStamp) {
|
|
try {
|
|
synchronized(transmitters) {
|
|
int size = transmitters.size();
|
|
if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers");
|
|
for (int i = 0; i < size; i++) {
|
|
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
|
|
if (receiver != null) {
|
|
//$$fb 2002-04-02: SysexMessages are mutable, so
|
|
// an application could change the contents of this object,
|
|
// or try to use the object later. So we can't get around object creation
|
|
// But the array need not be unique for each FastSysexMessage object,
|
|
// because it cannot be modified.
|
|
receiver.send(new FastSysexMessage(data), timeStamp);
|
|
}
|
|
}
|
|
}
|
|
} catch (InvalidMidiDataException e) {
|
|
// this happens when invalid data comes over the wire. Ignore it.
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Send this message to all transmitters
|
|
*/
|
|
void sendMessage(MidiMessage message, long timeStamp) {
|
|
if (message instanceof FastShortMessage) {
|
|
sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);
|
|
return;
|
|
}
|
|
synchronized(transmitters) {
|
|
int size = transmitters.size();
|
|
if (optimizedReceiverCount == size) {
|
|
if (midiOutReceiver != null) {
|
|
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");
|
|
midiOutReceiver.send(message, timeStamp);
|
|
}
|
|
} else {
|
|
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");
|
|
for (int i = 0; i < size; i++) {
|
|
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
|
|
if (receiver != null) {
|
|
//$$fb 2002-04-02: ShortMessages are mutable, so
|
|
// an application could change the contents of this object,
|
|
// or try to use the object later.
|
|
// We violate this spec here, to avoid costly (and gc-intensive)
|
|
// object creation for potentially hundred of messages per second.
|
|
// The spec should be changed to allow Immutable MidiMessages
|
|
// (i.e. throws InvalidStateException or so in setMessage)
|
|
receiver.send(message, timeStamp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // TransmitterList
|
|
|
|
}
|