Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java
38924 views
/*1* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.media.sound;2627import java.util.ArrayList;28import java.util.List;29import java.util.Collections;3031import javax.sound.midi.*;323334/**35* Abstract AbstractMidiDevice class representing functionality shared by36* MidiInDevice and MidiOutDevice objects.37*38* @author David Rivas39* @author Kara Kytle40* @author Matthias Pfisterer41* @author Florian Bomers42*/43abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice {4445// STATIC VARIABLES46private static final boolean TRACE_TRANSMITTER = false;4748// INSTANCE VARIABLES4950private ArrayList<Receiver> receiverList;5152private TransmitterList transmitterList;5354// lock to protect receiverList and transmitterList55// from simultaneous creation and destruction56// reduces possibility of deadlock, compared to57// synchronizing to the class instance58private final Object traRecLock = new Object();5960// DEVICE ATTRIBUTES6162private final MidiDevice.Info info;636465// DEVICE STATE6667private volatile boolean open;68private int openRefCount;6970/** List of Receivers and Transmitters that opened the device implicitely.71*/72private List openKeepingObjects;7374/**75* This is the device handle returned from native code76*/77protected volatile long id;78798081// CONSTRUCTOR828384/**85* Constructs an AbstractMidiDevice with the specified info object.86* @param info the description of the device87*/88/*89* The initial mode and and only supported mode default to OMNI_ON_POLY.90*/91protected AbstractMidiDevice(MidiDevice.Info info) {9293if(Printer.trace) Printer.trace(">> AbstractMidiDevice CONSTRUCTOR");9495this.info = info;96openRefCount = 0;9798if(Printer.trace) Printer.trace("<< AbstractMidiDevice CONSTRUCTOR completed");99}100101102// MIDI DEVICE METHODS103104public final MidiDevice.Info getDeviceInfo() {105return info;106}107108/** Open the device from an application program.109* Setting the open reference count to -1 here prevents Transmitters and Receivers that110* opened the the device implicitly from closing it. The only way to close the device after111* this call is a call to close().112*/113public final void open() throws MidiUnavailableException {114if (Printer.trace) Printer.trace("> AbstractMidiDevice: open()");115synchronized(this) {116openRefCount = -1;117doOpen();118}119if (Printer.trace) Printer.trace("< AbstractMidiDevice: open() completed");120}121122123124/** Open the device implicitly.125* This method is intended to be used by AbstractReceiver126* and BasicTransmitter. Actually, it is called by getReceiverReferenceCounting() and127* getTransmitterReferenceCounting(). These, in turn, are called by MidiSytem on calls to128* getReceiver() and getTransmitter(). The former methods should pass the Receiver or129* Transmitter just created as the object parameter to this method. Storing references to130* these objects is necessary to be able to decide later (when it comes to closing) if131* R/T's are ones that opened the device implicitly.132*133* @object The Receiver or Transmitter instance that triggered this implicit open.134*/135private void openInternal(Object object) throws MidiUnavailableException {136if (Printer.trace) Printer.trace("> AbstractMidiDevice: openInternal()");137synchronized(this) {138if (openRefCount != -1) {139openRefCount++;140getOpenKeepingObjects().add(object);141}142// double calls to doOpens() will be catched by the open flag.143doOpen();144}145if (Printer.trace) Printer.trace("< AbstractMidiDevice: openInternal() completed");146}147148149private void doOpen() throws MidiUnavailableException {150if (Printer.trace) Printer.trace("> AbstractMidiDevice: doOpen()");151synchronized(this) {152if (! isOpen()) {153implOpen();154open = true;155}156}157if (Printer.trace) Printer.trace("< AbstractMidiDevice: doOpen() completed");158}159160161public final void close() {162if (Printer.trace) Printer.trace("> AbstractMidiDevice: close()");163synchronized (this) {164doClose();165openRefCount = 0;166}167if (Printer.trace) Printer.trace("< AbstractMidiDevice: close() completed");168}169170171/** Close the device for an object that implicitely opened it.172* This method is intended to be used by Transmitter.close() and Receiver.close().173* Those methods should pass this for the object parameter. Since Transmitters or Receivers174* do not know if their device has been opened implicitely because of them, they call this175* method in any case. This method now is able to seperate Receivers/Transmitters that opened176* the device implicitely from those that didn't by looking up the R/T in the177* openKeepingObjects list. Only if the R/T is contained there, the reference count is178* reduced.179*180* @param object The object that might have been opening the device implicitely (for now,181* this may be a Transmitter or receiver).182*/183public final void closeInternal(Object object) {184if (Printer.trace) Printer.trace("> AbstractMidiDevice: closeInternal()");185synchronized(this) {186if (getOpenKeepingObjects().remove(object)) {187if (openRefCount > 0) {188openRefCount--;189if (openRefCount == 0) {190doClose();191}192}193}194}195if (Printer.trace) Printer.trace("< AbstractMidiDevice: closeInternal() completed");196}197198199public final void doClose() {200if (Printer.trace) Printer.trace("> AbstractMidiDevice: doClose()");201synchronized(this) {202if (isOpen()) {203implClose();204open = false;205}206}207if (Printer.trace) Printer.trace("< AbstractMidiDevice: doClose() completed");208}209210211public final boolean isOpen() {212return open;213}214215216protected void implClose() {217synchronized (traRecLock) {218if (receiverList != null) {219// close all receivers220for(int i = 0; i < receiverList.size(); i++) {221receiverList.get(i).close();222}223receiverList.clear();224}225if (transmitterList != null) {226// close all transmitters227transmitterList.close();228}229}230}231232233/**234* This implementation always returns -1.235* Devices that actually provide this should over-ride236* this method.237*/238public long getMicrosecondPosition() {239return -1;240}241242243/** Return the maximum number of Receivers supported by this device.244Depending on the return value of hasReceivers(), this method returns either 0 or -1.245Subclasses should rather override hasReceivers() than override this method.246*/247public final int getMaxReceivers() {248if (hasReceivers()) {249return -1;250} else {251return 0;252}253}254255256/** Return the maximum number of Transmitters supported by this device.257Depending on the return value of hasTransmitters(), this method returns either 0 or -1.258Subclasses should override hasTransmitters().259*/260public final int getMaxTransmitters() {261if (hasTransmitters()) {262return -1;263} else {264return 0;265}266}267268269/** Retrieve a Receiver for this device.270This method returns the value returned by createReceiver(), if it doesn't throw271an exception. Subclasses should rather override createReceiver() than override272this method.273If createReceiver returns a Receiver, it is added to the internal list274of Receivers (see getReceiversList)275*/276public final Receiver getReceiver() throws MidiUnavailableException {277Receiver receiver;278synchronized (traRecLock) {279receiver = createReceiver(); // may throw MidiUnavailableException280getReceiverList().add(receiver);281}282return receiver;283}284285286public final List<Receiver> getReceivers() {287List<Receiver> recs;288synchronized (traRecLock) {289if (receiverList == null) {290recs = Collections.unmodifiableList(new ArrayList<Receiver>(0));291} else {292recs = Collections.unmodifiableList293((List<Receiver>) (receiverList.clone()));294}295}296return recs;297}298299300/**301* This implementation uses createTransmitter, which may throw an exception.302* If a transmitter is returned in createTransmitter, it is added to the internal303* TransmitterList304*/305public final Transmitter getTransmitter() throws MidiUnavailableException {306Transmitter transmitter;307synchronized (traRecLock) {308transmitter = createTransmitter(); // may throw MidiUnavailableException309getTransmitterList().add(transmitter);310}311return transmitter;312}313314315public final List<Transmitter> getTransmitters() {316List<Transmitter> tras;317synchronized (traRecLock) {318if (transmitterList == null319|| transmitterList.transmitters.size() == 0) {320tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0));321} else {322tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone()));323}324}325return tras;326}327328329// HELPER METHODS330331final long getId() {332return id;333}334335336// REFERENCE COUNTING337338/** Retrieve a Receiver and open the device implicitly.339This method is called by MidiSystem.getReceiver().340*/341public final Receiver getReceiverReferenceCounting()342throws MidiUnavailableException {343/* Keep this order of commands! If getReceiver() throws an exception,344openInternal() should not be called!345*/346Receiver receiver;347synchronized (traRecLock) {348receiver = getReceiver();349AbstractMidiDevice.this.openInternal(receiver);350}351return receiver;352}353354355/** Retrieve a Transmitter and open the device implicitly.356This method is called by MidiSystem.getTransmitter().357*/358public final Transmitter getTransmitterReferenceCounting()359throws MidiUnavailableException {360/* Keep this order of commands! If getTransmitter() throws an exception,361openInternal() should not be called!362*/363Transmitter transmitter;364synchronized (traRecLock) {365transmitter = getTransmitter();366AbstractMidiDevice.this.openInternal(transmitter);367}368return transmitter;369}370371372/** Return the list of objects that have opened the device implicitely.373*/374private synchronized List getOpenKeepingObjects() {375if (openKeepingObjects == null) {376openKeepingObjects = new ArrayList();377}378return openKeepingObjects;379}380381382383// RECEIVER HANDLING METHODS384385386/** Return the internal list of Receivers, possibly creating it first.387*/388private List<Receiver> getReceiverList() {389synchronized (traRecLock) {390if (receiverList == null) {391receiverList = new ArrayList<Receiver>();392}393}394return receiverList;395}396397398/** Returns if this device supports Receivers.399Subclasses that use Receivers should override this method to400return true. They also should override createReceiver().401402@return true, if the device supports Receivers, false otherwise.403*/404protected boolean hasReceivers() {405return false;406}407408409/** Create a Receiver object.410throwing an exception here means that Receivers aren't enabled.411Subclasses that use Receivers should override this method with412one that returns objects implementing Receiver.413Classes overriding this method should also override hasReceivers()414to return true.415*/416protected Receiver createReceiver() throws MidiUnavailableException {417throw new MidiUnavailableException("MIDI IN receiver not available");418}419420421422// TRANSMITTER HANDLING423424/** Return the internal list of Transmitters, possibly creating it first.425*/426final TransmitterList getTransmitterList() {427synchronized (traRecLock) {428if (transmitterList == null) {429transmitterList = new TransmitterList();430}431}432return transmitterList;433}434435436/** Returns if this device supports Transmitters.437Subclasses that use Transmitters should override this method to438return true. They also should override createTransmitter().439440@return true, if the device supports Transmitters, false otherwise.441*/442protected boolean hasTransmitters() {443return false;444}445446447/** Create a Transmitter object.448throwing an exception here means that Transmitters aren't enabled.449Subclasses that use Transmitters should override this method with450one that returns objects implementing Transmitters.451Classes overriding this method should also override hasTransmitters()452to return true.453*/454protected Transmitter createTransmitter() throws MidiUnavailableException {455throw new MidiUnavailableException("MIDI OUT transmitter not available");456}457458// ABSTRACT METHODS459460protected abstract void implOpen() throws MidiUnavailableException;461462463/**464* close this device if discarded by the garbage collector465*/466protected final void finalize() {467close();468}469470// INNER CLASSES471472/** Base class for Receivers.473Subclasses that use Receivers must use this base class, since it474contains magic necessary to manage implicit closing the device.475This is necessary for Receivers retrieved via MidiSystem.getReceiver()476(which opens the device implicitely).477*/478abstract class AbstractReceiver implements MidiDeviceReceiver {479private volatile boolean open = true;480481482/** Deliver a MidiMessage.483This method contains magic related to the closed state of a484Receiver. Therefore, subclasses should not override this method.485Instead, they should implement implSend().486*/487@Override488public final synchronized void send(final MidiMessage message,489final long timeStamp) {490if (!open) {491throw new IllegalStateException("Receiver is not open");492}493implSend(message, timeStamp);494}495496abstract void implSend(MidiMessage message, long timeStamp);497498/** Close the Receiver.499* Here, the call to the magic method closeInternal() takes place.500* Therefore, subclasses that override this method must call501* 'super.close()'.502*/503@Override504public final void close() {505open = false;506synchronized (AbstractMidiDevice.this.traRecLock) {507AbstractMidiDevice.this.getReceiverList().remove(this);508}509AbstractMidiDevice.this.closeInternal(this);510}511512@Override513public final MidiDevice getMidiDevice() {514return AbstractMidiDevice.this;515}516517final boolean isOpen() {518return open;519}520521//$$fb is that a good idea?522//protected void finalize() {523// close();524//}525526} // class AbstractReceiver527528529/**530* Transmitter base class.531* This class especially makes sure the device is closed if it532* has been opened implicitly by a call to MidiSystem.getTransmitter().533* The logic of doing so is actually in closeInternal().534*535* Also, it has some optimizations regarding sending to the Receivers,536* for known Receivers, and managing itself in the TransmitterList.537*/538class BasicTransmitter implements MidiDeviceTransmitter {539540private Receiver receiver = null;541TransmitterList tlist = null;542543protected BasicTransmitter() {544}545546private void setTransmitterList(TransmitterList tlist) {547this.tlist = tlist;548}549550public final void setReceiver(Receiver receiver) {551if (tlist != null && this.receiver != receiver) {552if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver);553tlist.receiverChanged(this, this.receiver, receiver);554this.receiver = receiver;555}556}557558public final Receiver getReceiver() {559return receiver;560}561562563/** Close the Transmitter.564* Here, the call to the magic method closeInternal() takes place.565* Therefore, subclasses that override this method must call566* 'super.close()'.567*/568public final void close() {569AbstractMidiDevice.this.closeInternal(this);570if (tlist != null) {571tlist.receiverChanged(this, this.receiver, null);572tlist.remove(this);573tlist = null;574}575}576577public final MidiDevice getMidiDevice() {578return AbstractMidiDevice.this;579}580581} // class BasicTransmitter582583584/**585* a class to manage a list of transmitters586*/587final class TransmitterList {588589private final ArrayList<Transmitter> transmitters = new ArrayList<Transmitter>();590private MidiOutDevice.MidiOutReceiver midiOutReceiver;591592// how many transmitters must be present for optimized593// handling594private int optimizedReceiverCount = 0;595596597private void add(Transmitter t) {598synchronized(transmitters) {599transmitters.add(t);600}601if (t instanceof BasicTransmitter) {602((BasicTransmitter) t).setTransmitterList(this);603}604if (Printer.debug) Printer.debug("--added transmitter "+t);605}606607private void remove(Transmitter t) {608synchronized(transmitters) {609int index = transmitters.indexOf(t);610if (index >= 0) {611transmitters.remove(index);612if (Printer.debug) Printer.debug("--removed transmitter "+t);613}614}615}616617private void receiverChanged(BasicTransmitter t,618Receiver oldR,619Receiver newR) {620synchronized(transmitters) {621// some optimization622if (midiOutReceiver == oldR) {623midiOutReceiver = null;624}625if (newR != null) {626if ((newR instanceof MidiOutDevice.MidiOutReceiver)627&& (midiOutReceiver == null)) {628midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR);629}630}631optimizedReceiverCount =632((midiOutReceiver!=null)?1:0);633}634// more potential for optimization here635}636637638/** closes all transmitters and empties the list */639void close() {640synchronized (transmitters) {641for(int i = 0; i < transmitters.size(); i++) {642transmitters.get(i).close();643}644transmitters.clear();645}646if (Printer.trace) Printer.trace("TransmitterList.close() succeeded");647}648649650651/**652* Send this message to all receivers653* status = packedMessage & 0xFF654* data1 = (packedMessage & 0xFF00) >> 8;655* data1 = (packedMessage & 0xFF0000) >> 16;656*/657void sendMessage(int packedMessage, long timeStamp) {658try {659synchronized(transmitters) {660int size = transmitters.size();661if (optimizedReceiverCount == size) {662if (midiOutReceiver != null) {663if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver");664midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp);665}666} else {667if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers");668for (int i = 0; i < size; i++) {669Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();670if (receiver != null) {671if (optimizedReceiverCount > 0) {672if (receiver instanceof MidiOutDevice.MidiOutReceiver) {673((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);674} else {675receiver.send(new FastShortMessage(packedMessage), timeStamp);676}677} else {678receiver.send(new FastShortMessage(packedMessage), timeStamp);679}680}681}682}683}684} catch (InvalidMidiDataException e) {685// this happens when invalid data comes over the wire. Ignore it.686}687}688689void sendMessage(byte[] data, long timeStamp) {690try {691synchronized(transmitters) {692int size = transmitters.size();693if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers");694for (int i = 0; i < size; i++) {695Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();696if (receiver != null) {697//$$fb 2002-04-02: SysexMessages are mutable, so698// an application could change the contents of this object,699// or try to use the object later. So we can't get around object creation700// But the array need not be unique for each FastSysexMessage object,701// because it cannot be modified.702receiver.send(new FastSysexMessage(data), timeStamp);703}704}705}706} catch (InvalidMidiDataException e) {707// this happens when invalid data comes over the wire. Ignore it.708return;709}710}711712713/**714* Send this message to all transmitters715*/716void sendMessage(MidiMessage message, long timeStamp) {717if (message instanceof FastShortMessage) {718sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);719return;720}721synchronized(transmitters) {722int size = transmitters.size();723if (optimizedReceiverCount == size) {724if (midiOutReceiver != null) {725if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");726midiOutReceiver.send(message, timeStamp);727}728} else {729if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");730for (int i = 0; i < size; i++) {731Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();732if (receiver != null) {733//$$fb 2002-04-02: ShortMessages are mutable, so734// an application could change the contents of this object,735// or try to use the object later.736// We violate this spec here, to avoid costly (and gc-intensive)737// object creation for potentially hundred of messages per second.738// The spec should be changed to allow Immutable MidiMessages739// (i.e. throws InvalidStateException or so in setMessage)740receiver.send(message, timeStamp);741}742}743}744}745}746747748} // TransmitterList749750}751752753