< prev index next >

src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDevice.java

Print this page




   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.media.sound;
  27 
  28 import java.util.ArrayList;
  29 import java.util.List;
  30 import java.util.Collections;

  31 
  32 import javax.sound.midi.*;







  33 
  34 
  35 /**
  36  * Abstract AbstractMidiDevice class representing functionality shared by
  37  * MidiInDevice and MidiOutDevice objects.
  38  *
  39  * @author David Rivas
  40  * @author Kara Kytle
  41  * @author Matthias Pfisterer
  42  * @author Florian Bomers
  43  */
  44 abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice {
  45 
  46     // STATIC VARIABLES
  47     private static final boolean TRACE_TRANSMITTER = false;
  48 
  49     // INSTANCE VARIABLES
  50 
  51     private ArrayList<Receiver> receiverList;
  52 
  53     private TransmitterList transmitterList;
  54 
  55     // lock to protect receiverList and transmitterList
  56     // from simultaneous creation and destruction
  57     // reduces possibility of deadlock, compared to
  58     // synchronizing to the class instance
  59     private final Object traRecLock = new Object();
  60 
  61     // DEVICE ATTRIBUTES
  62 
  63     private final MidiDevice.Info info;
  64 
  65 
  66     // DEVICE STATE
  67 
  68     private volatile boolean open;
  69     private int openRefCount;
  70 
  71     /** List of Receivers and Transmitters that opened the device implicitely.
  72      */
  73     private List<Object> openKeepingObjects;
  74 
  75     /**
  76      * This is the device handle returned from native code
  77      */
  78     protected volatile long id;
  79 
  80 
  81 
  82     // CONSTRUCTOR
  83 
  84 
  85     /**
  86      * Constructs an AbstractMidiDevice with the specified info object.
  87      * @param info the description of the device
  88      */
  89     /*
  90      * The initial mode and only supported mode default to OMNI_ON_POLY.
  91      */
  92     protected AbstractMidiDevice(MidiDevice.Info info) {
  93 
  94         if(Printer.trace) Printer.trace(">> AbstractMidiDevice CONSTRUCTOR");
  95 
  96         this.info = info;
  97         openRefCount = 0;
  98 
  99         if(Printer.trace) Printer.trace("<< AbstractMidiDevice CONSTRUCTOR completed");
 100     }
 101 
 102 
 103     // MIDI DEVICE METHODS
 104 

 105     public final MidiDevice.Info getDeviceInfo() {
 106         return info;
 107     }
 108 
 109     /** Open the device from an application program.
 110      * Setting the open reference count to -1 here prevents Transmitters and Receivers that
 111      * opened the device implicitly from closing it. The only way to close the device after
 112      * this call is a call to close().
 113      */

 114     public final void open() throws MidiUnavailableException {
 115         if (Printer.trace) Printer.trace("> AbstractMidiDevice: open()");
 116         synchronized(this) {
 117             openRefCount = -1;
 118             doOpen();
 119         }
 120         if (Printer.trace) Printer.trace("< AbstractMidiDevice: open() completed");
 121     }
 122 
 123 
 124 
 125     /** Open the device implicitly.
 126      * This method is intended to be used by AbstractReceiver
 127      * and BasicTransmitter. Actually, it is called by getReceiverReferenceCounting() and
 128      * getTransmitterReferenceCounting(). These, in turn, are called by MidiSytem on calls to
 129      * getReceiver() and getTransmitter(). The former methods should pass the Receiver or
 130      * Transmitter just created as the object parameter to this method. Storing references to
 131      * these objects is necessary to be able to decide later (when it comes to closing) if
 132      * R/T's are ones that opened the device implicitly.
 133      *
 134      * @object The Receiver or Transmitter instance that triggered this implicit open.
 135      */
 136     private void openInternal(Object object) throws MidiUnavailableException {
 137         if (Printer.trace) Printer.trace("> AbstractMidiDevice: openInternal()");
 138         synchronized(this) {
 139             if (openRefCount != -1) {
 140                 openRefCount++;
 141                 getOpenKeepingObjects().add(object);
 142             }
 143             // double calls to doOpens() will be catched by the open flag.
 144             doOpen();
 145         }
 146         if (Printer.trace) Printer.trace("< AbstractMidiDevice: openInternal() completed");
 147     }
 148 
 149 
 150     private void doOpen() throws MidiUnavailableException {
 151         if (Printer.trace) Printer.trace("> AbstractMidiDevice: doOpen()");
 152         synchronized(this) {
 153             if (! isOpen()) {
 154                 implOpen();
 155                 open = true;
 156             }
 157         }
 158         if (Printer.trace) Printer.trace("< AbstractMidiDevice: doOpen() completed");
 159     }
 160 
 161 
 162     public final void close() {
 163         if (Printer.trace) Printer.trace("> AbstractMidiDevice: close()");
 164         synchronized (this) {
 165             doClose();
 166             openRefCount = 0;
 167         }
 168         if (Printer.trace) Printer.trace("< AbstractMidiDevice: close() completed");
 169     }
 170 
 171 
 172     /** Close the device for an object that implicitely opened it.
 173      * This method is intended to be used by Transmitter.close() and Receiver.close().
 174      * Those methods should pass this for the object parameter. Since Transmitters or Receivers
 175      * do not know if their device has been opened implicitely because of them, they call this
 176      * method in any case. This method now is able to seperate Receivers/Transmitters that opened
 177      * the device implicitely from those that didn't by looking up the R/T in the
 178      * openKeepingObjects list. Only if the R/T is contained there, the reference count is
 179      * reduced.
 180      *
 181      * @param object The object that might have been opening the device implicitely (for now,
 182      * this may be a Transmitter or receiver).
 183      */
 184     public final void closeInternal(Object object) {
 185         if (Printer.trace) Printer.trace("> AbstractMidiDevice: closeInternal()");
 186         synchronized(this) {
 187             if (getOpenKeepingObjects().remove(object)) {
 188                 if (openRefCount > 0) {
 189                     openRefCount--;
 190                     if (openRefCount == 0) {
 191                         doClose();
 192                     }
 193                 }
 194             }
 195         }
 196         if (Printer.trace) Printer.trace("< AbstractMidiDevice: closeInternal() completed");
 197     }
 198 
 199 
 200     public final void doClose() {
 201         if (Printer.trace) Printer.trace("> AbstractMidiDevice: doClose()");
 202         synchronized(this) {
 203             if (isOpen()) {
 204                 implClose();
 205                 open = false;
 206             }
 207         }
 208         if (Printer.trace) Printer.trace("< AbstractMidiDevice: doClose() completed");
 209     }
 210 
 211 
 212     public final boolean isOpen() {
 213         return open;
 214     }
 215 
 216 
 217     protected void implClose() {
 218         synchronized (traRecLock) {
 219             if (receiverList != null) {
 220                 // close all receivers
 221                 for(int i = 0; i < receiverList.size(); i++) {
 222                     receiverList.get(i).close();
 223                 }
 224                 receiverList.clear();
 225             }
 226             if (transmitterList != null) {
 227                 // close all transmitters
 228                 transmitterList.close();
 229             }
 230         }
 231     }
 232 
 233 
 234     /**
 235      * This implementation always returns -1.
 236      * Devices that actually provide this should over-ride
 237      * this method.
 238      */

 239     public long getMicrosecondPosition() {
 240         return -1;
 241     }
 242 
 243 
 244     /** Return the maximum number of Receivers supported by this device.
 245         Depending on the return value of hasReceivers(), this method returns either 0 or -1.
 246         Subclasses should rather override hasReceivers() than override this method.
 247      */

 248     public final int getMaxReceivers() {
 249         if (hasReceivers()) {
 250             return -1;
 251         } else {
 252             return 0;
 253         }
 254     }
 255 
 256 
 257     /** Return the maximum number of Transmitters supported by this device.
 258         Depending on the return value of hasTransmitters(), this method returns either 0 or -1.
 259         Subclasses should override hasTransmitters().
 260      */

 261     public final int getMaxTransmitters() {
 262         if (hasTransmitters()) {
 263             return -1;
 264         } else {
 265             return 0;
 266         }
 267     }
 268 
 269 
 270     /** Retrieve a Receiver for this device.
 271         This method returns the value returned by createReceiver(), if it doesn't throw
 272         an exception. Subclasses should rather override createReceiver() than override
 273         this method.
 274         If createReceiver returns a Receiver, it is added to the internal list
 275         of Receivers (see getReceiversList)
 276      */

 277     public final Receiver getReceiver() throws MidiUnavailableException {
 278         Receiver receiver;
 279         synchronized (traRecLock) {
 280             receiver = createReceiver(); // may throw MidiUnavailableException
 281             getReceiverList().add(receiver);
 282         }
 283         return receiver;
 284     }
 285 
 286 
 287     @SuppressWarnings("unchecked") // Cast of result of clone
 288     public final List<Receiver> getReceivers() {
 289         List<Receiver> recs;
 290         synchronized (traRecLock) {
 291             if (receiverList == null) {
 292                 recs = Collections.unmodifiableList(new ArrayList<Receiver>(0));
 293             } else {
 294                 recs = Collections.unmodifiableList
 295                     ((List<Receiver>) (receiverList.clone()));
 296             }
 297         }
 298         return recs;
 299     }
 300 
 301 
 302     /**
 303      * This implementation uses createTransmitter, which may throw an exception.
 304      * If a transmitter is returned in createTransmitter, it is added to the internal
 305      * TransmitterList
 306      */

 307     public final Transmitter getTransmitter() throws MidiUnavailableException {
 308         Transmitter transmitter;
 309         synchronized (traRecLock) {
 310             transmitter = createTransmitter(); // may throw MidiUnavailableException
 311             getTransmitterList().add(transmitter);
 312         }
 313         return transmitter;
 314     }
 315 
 316 
 317     @SuppressWarnings("unchecked") // Cast of result of clone
 318     public final List<Transmitter> getTransmitters() {
 319         List<Transmitter> tras;
 320         synchronized (traRecLock) {
 321             if (transmitterList == null
 322                 || transmitterList.transmitters.size() == 0) {
 323                 tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0));
 324             } else {
 325                 tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone()));
 326             }
 327         }
 328         return tras;
 329     }
 330 
 331 
 332     // HELPER METHODS
 333 
 334     final long getId() {
 335         return id;
 336     }
 337 
 338 
 339     // REFERENCE COUNTING
 340 
 341     /** Retrieve a Receiver and open the device implicitly.
 342         This method is called by MidiSystem.getReceiver().
 343      */

 344     public final Receiver getReceiverReferenceCounting()
 345             throws MidiUnavailableException {
 346         /* Keep this order of commands! If getReceiver() throws an exception,
 347            openInternal() should not be called!
 348         */
 349         Receiver receiver;
 350         synchronized (traRecLock) {
 351             receiver = getReceiver();
 352             AbstractMidiDevice.this.openInternal(receiver);
 353         }
 354         return receiver;
 355     }
 356 
 357 
 358     /** Retrieve a Transmitter and open the device implicitly.
 359         This method is called by MidiSystem.getTransmitter().
 360      */

 361     public final Transmitter getTransmitterReferenceCounting()
 362             throws MidiUnavailableException {
 363         /* Keep this order of commands! If getTransmitter() throws an exception,
 364            openInternal() should not be called!
 365         */
 366         Transmitter transmitter;
 367         synchronized (traRecLock) {
 368             transmitter = getTransmitter();
 369             AbstractMidiDevice.this.openInternal(transmitter);
 370         }
 371         return transmitter;
 372     }
 373 
 374 
 375     /** Return the list of objects that have opened the device implicitely.
 376      */
 377     private synchronized List<Object> getOpenKeepingObjects() {
 378         if (openKeepingObjects == null) {
 379             openKeepingObjects = new ArrayList<>();
 380         }
 381         return openKeepingObjects;
 382     }
 383 
 384 
 385 
 386     // RECEIVER HANDLING METHODS
 387 
 388 
 389     /** Return the internal list of Receivers, possibly creating it first.
 390      */
 391     private List<Receiver> getReceiverList() {
 392         synchronized (traRecLock) {
 393             if (receiverList == null) {
 394                 receiverList = new ArrayList<Receiver>();
 395             }
 396         }
 397         return receiverList;
 398     }
 399 
 400 
 401     /** Returns if this device supports Receivers.
 402         Subclasses that use Receivers should override this method to
 403         return true. They also should override createReceiver().
 404 
 405         @return true, if the device supports Receivers, false otherwise.
 406     */
 407     protected boolean hasReceivers() {
 408         return false;
 409     }
 410 
 411 
 412     /** Create a Receiver object.
 413         throwing an exception here means that Receivers aren't enabled.
 414         Subclasses that use Receivers should override this method with
 415         one that returns objects implementing Receiver.
 416         Classes overriding this method should also override hasReceivers()
 417         to return true.
 418     */
 419     protected Receiver createReceiver() throws MidiUnavailableException {
 420         throw new MidiUnavailableException("MIDI IN receiver not available");
 421     }
 422 
 423 
 424 
 425     // TRANSMITTER HANDLING
 426 
 427     /** Return the internal list of Transmitters, possibly creating it first.
 428      */
 429     final TransmitterList getTransmitterList() {
 430         synchronized (traRecLock) {
 431             if (transmitterList == null) {
 432                 transmitterList = new TransmitterList();
 433             }
 434         }
 435         return transmitterList;
 436     }
 437 
 438 
 439     /** Returns if this device supports Transmitters.
 440         Subclasses that use Transmitters should override this method to
 441         return true. They also should override createTransmitter().
 442 
 443         @return true, if the device supports Transmitters, false otherwise.
 444     */
 445     protected boolean hasTransmitters() {
 446         return false;
 447     }
 448 
 449 
 450     /** Create a Transmitter object.
 451         throwing an exception here means that Transmitters aren't enabled.
 452         Subclasses that use Transmitters should override this method with
 453         one that returns objects implementing Transmitters.
 454         Classes overriding this method should also override hasTransmitters()
 455         to return true.
 456     */
 457     protected Transmitter createTransmitter() throws MidiUnavailableException {
 458         throw new MidiUnavailableException("MIDI OUT transmitter not available");
 459     }
 460 
 461     // ABSTRACT METHODS
 462 
 463     protected abstract void implOpen() throws MidiUnavailableException;
 464 
 465 
 466     /**
 467      * close this device if discarded by the garbage collector
 468      */

 469     protected final void finalize() {
 470         close();
 471     }
 472 
 473     // INNER CLASSES
 474 
 475     /** Base class for Receivers.
 476         Subclasses that use Receivers must use this base class, since it
 477         contains magic necessary to manage implicit closing the device.
 478         This is necessary for Receivers retrieved via MidiSystem.getReceiver()
 479         (which opens the device implicitely).
 480      */
 481     abstract class AbstractReceiver implements MidiDeviceReceiver {
 482         private volatile boolean open = true;
 483 
 484 
 485         /** Deliver a MidiMessage.
 486             This method contains magic related to the closed state of a
 487             Receiver. Therefore, subclasses should not override this method.
 488             Instead, they should implement implSend().
 489         */
 490         @Override
 491         public final synchronized void send(final MidiMessage message,
 492                                             final long timeStamp) {
 493             if (!open) {
 494                 throw new IllegalStateException("Receiver is not open");


 533      * Transmitter base class.
 534      * This class especially makes sure the device is closed if it
 535      * has been opened implicitly by a call to MidiSystem.getTransmitter().
 536      * The logic of doing so is actually in closeInternal().
 537      *
 538      * Also, it has some optimizations regarding sending to the Receivers,
 539      * for known Receivers, and managing itself in the TransmitterList.
 540      */
 541     class BasicTransmitter implements MidiDeviceTransmitter {
 542 
 543         private Receiver receiver = null;
 544         TransmitterList tlist = null;
 545 
 546         protected BasicTransmitter() {
 547         }
 548 
 549         private void setTransmitterList(TransmitterList tlist) {
 550             this.tlist = tlist;
 551         }
 552 

 553         public final void setReceiver(Receiver receiver) {
 554             if (tlist != null && this.receiver != receiver) {
 555                 if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver);
 556                 tlist.receiverChanged(this, this.receiver, receiver);
 557                 this.receiver = receiver;
 558             }
 559         }
 560 

 561         public final Receiver getReceiver() {
 562             return receiver;
 563         }
 564 
 565 
 566         /** Close the Transmitter.
 567          * Here, the call to the magic method closeInternal() takes place.
 568          * Therefore, subclasses that override this method must call
 569          * 'super.close()'.
 570          */

 571         public final void close() {
 572             AbstractMidiDevice.this.closeInternal(this);
 573             if (tlist != null) {
 574                 tlist.receiverChanged(this, this.receiver, null);
 575                 tlist.remove(this);
 576                 tlist = null;
 577             }
 578         }
 579 

 580         public final MidiDevice getMidiDevice() {
 581             return AbstractMidiDevice.this;
 582         }
 583 
 584     } // class BasicTransmitter
 585 
 586 
 587     /**
 588      * a class to manage a list of transmitters
 589      */
 590     final class TransmitterList {
 591 
 592         private final ArrayList<Transmitter> transmitters = new ArrayList<Transmitter>();
 593         private MidiOutDevice.MidiOutReceiver midiOutReceiver;
 594 
 595         // how many transmitters must be present for optimized
 596         // handling
 597         private int optimizedReceiverCount = 0;
 598 
 599 
 600         private void add(Transmitter t) {
 601             synchronized(transmitters) {
 602                 transmitters.add(t);
 603             }
 604             if (t instanceof BasicTransmitter) {
 605                 ((BasicTransmitter) t).setTransmitterList(this);
 606             }
 607             if (Printer.debug) Printer.debug("--added transmitter "+t);
 608         }
 609 
 610         private void remove(Transmitter t) {
 611             synchronized(transmitters) {
 612                 int index = transmitters.indexOf(t);


 695                     int size = transmitters.size();
 696                     if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers");
 697                     for (int i = 0; i < size; i++) {
 698                         Receiver receiver = transmitters.get(i).getReceiver();
 699                         if (receiver != null) {
 700                             //$$fb 2002-04-02: SysexMessages are mutable, so
 701                             // an application could change the contents of this object,
 702                             // or try to use the object later. So we can't get around object creation
 703                             // But the array need not be unique for each FastSysexMessage object,
 704                             // because it cannot be modified.
 705                             receiver.send(new FastSysexMessage(data), timeStamp);
 706                         }
 707                     }
 708                 }
 709             } catch (InvalidMidiDataException e) {
 710                 // this happens when invalid data comes over the wire. Ignore it.
 711                 return;
 712             }
 713         }
 714 
 715 
 716         /**
 717         * Send this message to all transmitters
 718         */
 719         void sendMessage(MidiMessage message, long timeStamp) {
 720             if (message instanceof FastShortMessage) {
 721                 sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);
 722                 return;
 723             }
 724             synchronized(transmitters) {
 725                 int size = transmitters.size();
 726                 if (optimizedReceiverCount == size) {
 727                     if (midiOutReceiver != null) {
 728                         if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");
 729                         midiOutReceiver.send(message, timeStamp);
 730                     }
 731                 } else {
 732                     if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");
 733                     for (int i = 0; i < size; i++) {
 734                         Receiver receiver = transmitters.get(i).getReceiver();
 735                         if (receiver != null) {
 736                             //$$fb 2002-04-02: ShortMessages are mutable, so
 737                             // an application could change the contents of this object,
 738                             // or try to use the object later.
 739                             // We violate this spec here, to avoid costly (and gc-intensive)
 740                             // object creation for potentially hundred of messages per second.
 741                             // The spec should be changed to allow Immutable MidiMessages
 742                             // (i.e. throws InvalidStateException or so in setMessage)
 743                             receiver.send(message, timeStamp);
 744                         }
 745                     }
 746                 }
 747             }
 748         }
 749 
 750 
 751     } // TransmitterList
 752 
 753 }


   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.media.sound;
  27 
  28 import java.util.ArrayList;

  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javax.sound.midi.InvalidMidiDataException;
  33 import javax.sound.midi.MidiDevice;
  34 import javax.sound.midi.MidiDeviceReceiver;
  35 import javax.sound.midi.MidiDeviceTransmitter;
  36 import javax.sound.midi.MidiMessage;
  37 import javax.sound.midi.MidiUnavailableException;
  38 import javax.sound.midi.Receiver;
  39 import javax.sound.midi.Transmitter;
  40 
  41 
  42 /**
  43  * Abstract AbstractMidiDevice class representing functionality shared by
  44  * MidiInDevice and MidiOutDevice objects.
  45  *
  46  * @author David Rivas
  47  * @author Kara Kytle
  48  * @author Matthias Pfisterer
  49  * @author Florian Bomers
  50  */
  51 abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice {
  52 

  53     private static final boolean TRACE_TRANSMITTER = false;
  54 


  55     private ArrayList<Receiver> receiverList;
  56 
  57     private TransmitterList transmitterList;
  58 
  59     // lock to protect receiverList and transmitterList
  60     // from simultaneous creation and destruction
  61     // reduces possibility of deadlock, compared to
  62     // synchronizing to the class instance
  63     private final Object traRecLock = new Object();
  64 
  65     // DEVICE ATTRIBUTES
  66 
  67     private final MidiDevice.Info info;
  68 

  69     // DEVICE STATE
  70 
  71     private volatile boolean open;
  72     private int openRefCount;
  73 
  74     /** List of Receivers and Transmitters that opened the device implicitely.
  75      */
  76     private List<Object> openKeepingObjects;
  77 
  78     /**
  79      * This is the device handle returned from native code.
  80      */
  81     protected volatile long id;
  82 





  83     /**
  84      * Constructs an AbstractMidiDevice with the specified info object.
  85      * @param info the description of the device
  86      */
  87     /*
  88      * The initial mode and only supported mode default to OMNI_ON_POLY.
  89      */
  90     protected AbstractMidiDevice(MidiDevice.Info info) {
  91 
  92         if(Printer.trace) Printer.trace(">> AbstractMidiDevice CONSTRUCTOR");
  93 
  94         this.info = info;
  95         openRefCount = 0;
  96 
  97         if(Printer.trace) Printer.trace("<< AbstractMidiDevice CONSTRUCTOR completed");
  98     }
  99 

 100     // MIDI DEVICE METHODS
 101 
 102     @Override
 103     public final MidiDevice.Info getDeviceInfo() {
 104         return info;
 105     }
 106 
 107     /** Open the device from an application program.
 108      * Setting the open reference count to -1 here prevents Transmitters and Receivers that
 109      * opened the device implicitly from closing it. The only way to close the device after
 110      * this call is a call to close().
 111      */
 112     @Override
 113     public final void open() throws MidiUnavailableException {
 114         if (Printer.trace) Printer.trace("> AbstractMidiDevice: open()");
 115         synchronized(this) {
 116             openRefCount = -1;
 117             doOpen();
 118         }
 119         if (Printer.trace) Printer.trace("< AbstractMidiDevice: open() completed");
 120     }
 121 


 122     /** Open the device implicitly.
 123      * This method is intended to be used by AbstractReceiver
 124      * and BasicTransmitter. Actually, it is called by getReceiverReferenceCounting() and
 125      * getTransmitterReferenceCounting(). These, in turn, are called by MidiSytem on calls to
 126      * getReceiver() and getTransmitter(). The former methods should pass the Receiver or
 127      * Transmitter just created as the object parameter to this method. Storing references to
 128      * these objects is necessary to be able to decide later (when it comes to closing) if
 129      * R/T's are ones that opened the device implicitly.
 130      *
 131      * @object The Receiver or Transmitter instance that triggered this implicit open.
 132      */
 133     private void openInternal(Object object) throws MidiUnavailableException {
 134         if (Printer.trace) Printer.trace("> AbstractMidiDevice: openInternal()");
 135         synchronized(this) {
 136             if (openRefCount != -1) {
 137                 openRefCount++;
 138                 getOpenKeepingObjects().add(object);
 139             }
 140             // double calls to doOpens() will be catched by the open flag.
 141             doOpen();
 142         }
 143         if (Printer.trace) Printer.trace("< AbstractMidiDevice: openInternal() completed");
 144     }
 145 

 146     private void doOpen() throws MidiUnavailableException {
 147         if (Printer.trace) Printer.trace("> AbstractMidiDevice: doOpen()");
 148         synchronized(this) {
 149             if (! isOpen()) {
 150                 implOpen();
 151                 open = true;
 152             }
 153         }
 154         if (Printer.trace) Printer.trace("< AbstractMidiDevice: doOpen() completed");
 155     }
 156 
 157     @Override
 158     public final void close() {
 159         if (Printer.trace) Printer.trace("> AbstractMidiDevice: close()");
 160         synchronized (this) {
 161             doClose();
 162             openRefCount = 0;
 163         }
 164         if (Printer.trace) Printer.trace("< AbstractMidiDevice: close() completed");
 165     }
 166 

 167     /** Close the device for an object that implicitely opened it.
 168      * This method is intended to be used by Transmitter.close() and Receiver.close().
 169      * Those methods should pass this for the object parameter. Since Transmitters or Receivers
 170      * do not know if their device has been opened implicitely because of them, they call this
 171      * method in any case. This method now is able to seperate Receivers/Transmitters that opened
 172      * the device implicitely from those that didn't by looking up the R/T in the
 173      * openKeepingObjects list. Only if the R/T is contained there, the reference count is
 174      * reduced.
 175      *
 176      * @param object The object that might have been opening the device implicitely (for now,
 177      * this may be a Transmitter or receiver).
 178      */
 179     public final void closeInternal(Object object) {
 180         if (Printer.trace) Printer.trace("> AbstractMidiDevice: closeInternal()");
 181         synchronized(this) {
 182             if (getOpenKeepingObjects().remove(object)) {
 183                 if (openRefCount > 0) {
 184                     openRefCount--;
 185                     if (openRefCount == 0) {
 186                         doClose();
 187                     }
 188                 }
 189             }
 190         }
 191         if (Printer.trace) Printer.trace("< AbstractMidiDevice: closeInternal() completed");
 192     }
 193 

 194     public final void doClose() {
 195         if (Printer.trace) Printer.trace("> AbstractMidiDevice: doClose()");
 196         synchronized(this) {
 197             if (isOpen()) {
 198                 implClose();
 199                 open = false;
 200             }
 201         }
 202         if (Printer.trace) Printer.trace("< AbstractMidiDevice: doClose() completed");
 203     }
 204 
 205     @Override
 206     public final boolean isOpen() {
 207         return open;
 208     }
 209 

 210     protected void implClose() {
 211         synchronized (traRecLock) {
 212             if (receiverList != null) {
 213                 // close all receivers
 214                 for(int i = 0; i < receiverList.size(); i++) {
 215                     receiverList.get(i).close();
 216                 }
 217                 receiverList.clear();
 218             }
 219             if (transmitterList != null) {
 220                 // close all transmitters
 221                 transmitterList.close();
 222             }
 223         }
 224     }
 225 

 226     /**
 227      * This implementation always returns -1.
 228      * Devices that actually provide this should over-ride
 229      * this method.
 230      */
 231     @Override
 232     public long getMicrosecondPosition() {
 233         return -1;
 234     }
 235 

 236     /** Return the maximum number of Receivers supported by this device.
 237         Depending on the return value of hasReceivers(), this method returns either 0 or -1.
 238         Subclasses should rather override hasReceivers() than override this method.
 239      */
 240     @Override
 241     public final int getMaxReceivers() {
 242         if (hasReceivers()) {
 243             return -1;
 244         } else {
 245             return 0;
 246         }
 247     }
 248 

 249     /** Return the maximum number of Transmitters supported by this device.
 250         Depending on the return value of hasTransmitters(), this method returns either 0 or -1.
 251         Subclasses should override hasTransmitters().
 252      */
 253     @Override
 254     public final int getMaxTransmitters() {
 255         if (hasTransmitters()) {
 256             return -1;
 257         } else {
 258             return 0;
 259         }
 260     }
 261 

 262     /** Retrieve a Receiver for this device.
 263         This method returns the value returned by createReceiver(), if it doesn't throw
 264         an exception. Subclasses should rather override createReceiver() than override
 265         this method.
 266         If createReceiver returns a Receiver, it is added to the internal list
 267         of Receivers (see getReceiversList)
 268      */
 269     @Override
 270     public final Receiver getReceiver() throws MidiUnavailableException {
 271         Receiver receiver;
 272         synchronized (traRecLock) {
 273             receiver = createReceiver(); // may throw MidiUnavailableException
 274             getReceiverList().add(receiver);
 275         }
 276         return receiver;
 277     }
 278 
 279     @Override
 280     @SuppressWarnings("unchecked") // Cast of result of clone
 281     public final List<Receiver> getReceivers() {
 282         List<Receiver> recs;
 283         synchronized (traRecLock) {
 284             if (receiverList == null) {
 285                 recs = Collections.unmodifiableList(new ArrayList<Receiver>(0));
 286             } else {
 287                 recs = Collections.unmodifiableList
 288                     ((List<Receiver>) (receiverList.clone()));
 289             }
 290         }
 291         return recs;
 292     }
 293 

 294     /**
 295      * This implementation uses createTransmitter, which may throw an exception.
 296      * If a transmitter is returned in createTransmitter, it is added to the internal
 297      * TransmitterList
 298      */
 299     @Override
 300     public final Transmitter getTransmitter() throws MidiUnavailableException {
 301         Transmitter transmitter;
 302         synchronized (traRecLock) {
 303             transmitter = createTransmitter(); // may throw MidiUnavailableException
 304             getTransmitterList().add(transmitter);
 305         }
 306         return transmitter;
 307     }
 308 
 309     @Override
 310     @SuppressWarnings("unchecked") // Cast of result of clone
 311     public final List<Transmitter> getTransmitters() {
 312         List<Transmitter> tras;
 313         synchronized (traRecLock) {
 314             if (transmitterList == null
 315                 || transmitterList.transmitters.size() == 0) {
 316                 tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0));
 317             } else {
 318                 tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone()));
 319             }
 320         }
 321         return tras;
 322     }
 323 



 324     final long getId() {
 325         return id;
 326     }
 327 

 328     // REFERENCE COUNTING
 329 
 330     /** Retrieve a Receiver and open the device implicitly.
 331         This method is called by MidiSystem.getReceiver().
 332      */
 333     @Override
 334     public final Receiver getReceiverReferenceCounting()
 335             throws MidiUnavailableException {
 336         /* Keep this order of commands! If getReceiver() throws an exception,
 337            openInternal() should not be called!
 338         */
 339         Receiver receiver;
 340         synchronized (traRecLock) {
 341             receiver = getReceiver();
 342             AbstractMidiDevice.this.openInternal(receiver);
 343         }
 344         return receiver;
 345     }
 346 

 347     /** Retrieve a Transmitter and open the device implicitly.
 348         This method is called by MidiSystem.getTransmitter().
 349      */
 350     @Override
 351     public final Transmitter getTransmitterReferenceCounting()
 352             throws MidiUnavailableException {
 353         /* Keep this order of commands! If getTransmitter() throws an exception,
 354            openInternal() should not be called!
 355         */
 356         Transmitter transmitter;
 357         synchronized (traRecLock) {
 358             transmitter = getTransmitter();
 359             AbstractMidiDevice.this.openInternal(transmitter);
 360         }
 361         return transmitter;
 362     }
 363 

 364     /** Return the list of objects that have opened the device implicitely.
 365      */
 366     private synchronized List<Object> getOpenKeepingObjects() {
 367         if (openKeepingObjects == null) {
 368             openKeepingObjects = new ArrayList<>();
 369         }
 370         return openKeepingObjects;
 371     }
 372 


 373     // RECEIVER HANDLING METHODS
 374 

 375     /** Return the internal list of Receivers, possibly creating it first.
 376      */
 377     private List<Receiver> getReceiverList() {
 378         synchronized (traRecLock) {
 379             if (receiverList == null) {
 380                 receiverList = new ArrayList<>();
 381             }
 382         }
 383         return receiverList;
 384     }
 385 

 386     /** Returns if this device supports Receivers.
 387         Subclasses that use Receivers should override this method to
 388         return true. They also should override createReceiver().
 389 
 390         @return true, if the device supports Receivers, false otherwise.
 391     */
 392     protected boolean hasReceivers() {
 393         return false;
 394     }
 395 

 396     /** Create a Receiver object.
 397         throwing an exception here means that Receivers aren't enabled.
 398         Subclasses that use Receivers should override this method with
 399         one that returns objects implementing Receiver.
 400         Classes overriding this method should also override hasReceivers()
 401         to return true.
 402     */
 403     protected Receiver createReceiver() throws MidiUnavailableException {
 404         throw new MidiUnavailableException("MIDI IN receiver not available");
 405     }
 406 


 407     // TRANSMITTER HANDLING
 408 
 409     /** Return the internal list of Transmitters, possibly creating it first.
 410      */
 411     final TransmitterList getTransmitterList() {
 412         synchronized (traRecLock) {
 413             if (transmitterList == null) {
 414                 transmitterList = new TransmitterList();
 415             }
 416         }
 417         return transmitterList;
 418     }
 419 

 420     /** Returns if this device supports Transmitters.
 421         Subclasses that use Transmitters should override this method to
 422         return true. They also should override createTransmitter().
 423 
 424         @return true, if the device supports Transmitters, false otherwise.
 425     */
 426     protected boolean hasTransmitters() {
 427         return false;
 428     }
 429 

 430     /** Create a Transmitter object.
 431         throwing an exception here means that Transmitters aren't enabled.
 432         Subclasses that use Transmitters should override this method with
 433         one that returns objects implementing Transmitters.
 434         Classes overriding this method should also override hasTransmitters()
 435         to return true.
 436     */
 437     protected Transmitter createTransmitter() throws MidiUnavailableException {
 438         throw new MidiUnavailableException("MIDI OUT transmitter not available");
 439     }
 440 


 441     protected abstract void implOpen() throws MidiUnavailableException;
 442 

 443     /**
 444      * close this device if discarded by the garbage collector.
 445      */
 446     @Override
 447     protected final void finalize() {
 448         close();
 449     }
 450 


 451     /** Base class for Receivers.
 452         Subclasses that use Receivers must use this base class, since it
 453         contains magic necessary to manage implicit closing the device.
 454         This is necessary for Receivers retrieved via MidiSystem.getReceiver()
 455         (which opens the device implicitely).
 456      */
 457     abstract class AbstractReceiver implements MidiDeviceReceiver {
 458         private volatile boolean open = true;
 459 
 460 
 461         /** Deliver a MidiMessage.
 462             This method contains magic related to the closed state of a
 463             Receiver. Therefore, subclasses should not override this method.
 464             Instead, they should implement implSend().
 465         */
 466         @Override
 467         public final synchronized void send(final MidiMessage message,
 468                                             final long timeStamp) {
 469             if (!open) {
 470                 throw new IllegalStateException("Receiver is not open");


 509      * Transmitter base class.
 510      * This class especially makes sure the device is closed if it
 511      * has been opened implicitly by a call to MidiSystem.getTransmitter().
 512      * The logic of doing so is actually in closeInternal().
 513      *
 514      * Also, it has some optimizations regarding sending to the Receivers,
 515      * for known Receivers, and managing itself in the TransmitterList.
 516      */
 517     class BasicTransmitter implements MidiDeviceTransmitter {
 518 
 519         private Receiver receiver = null;
 520         TransmitterList tlist = null;
 521 
 522         protected BasicTransmitter() {
 523         }
 524 
 525         private void setTransmitterList(TransmitterList tlist) {
 526             this.tlist = tlist;
 527         }
 528 
 529         @Override
 530         public final void setReceiver(Receiver receiver) {
 531             if (tlist != null && this.receiver != receiver) {
 532                 if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver);
 533                 tlist.receiverChanged(this, this.receiver, receiver);
 534                 this.receiver = receiver;
 535             }
 536         }
 537 
 538         @Override
 539         public final Receiver getReceiver() {
 540             return receiver;
 541         }
 542 

 543         /** Close the Transmitter.
 544          * Here, the call to the magic method closeInternal() takes place.
 545          * Therefore, subclasses that override this method must call
 546          * 'super.close()'.
 547          */
 548         @Override
 549         public final void close() {
 550             AbstractMidiDevice.this.closeInternal(this);
 551             if (tlist != null) {
 552                 tlist.receiverChanged(this, this.receiver, null);
 553                 tlist.remove(this);
 554                 tlist = null;
 555             }
 556         }
 557 
 558         @Override
 559         public final MidiDevice getMidiDevice() {
 560             return AbstractMidiDevice.this;
 561         }
 562 
 563     } // class BasicTransmitter
 564 

 565     /**
 566      * a class to manage a list of transmitters.
 567      */
 568     final class TransmitterList {
 569 
 570         private final ArrayList<Transmitter> transmitters = new ArrayList<>();
 571         private MidiOutDevice.MidiOutReceiver midiOutReceiver;
 572 
 573         // how many transmitters must be present for optimized
 574         // handling
 575         private int optimizedReceiverCount = 0;
 576 
 577 
 578         private void add(Transmitter t) {
 579             synchronized(transmitters) {
 580                 transmitters.add(t);
 581             }
 582             if (t instanceof BasicTransmitter) {
 583                 ((BasicTransmitter) t).setTransmitterList(this);
 584             }
 585             if (Printer.debug) Printer.debug("--added transmitter "+t);
 586         }
 587 
 588         private void remove(Transmitter t) {
 589             synchronized(transmitters) {
 590                 int index = transmitters.indexOf(t);


 673                     int size = transmitters.size();
 674                     if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers");
 675                     for (int i = 0; i < size; i++) {
 676                         Receiver receiver = transmitters.get(i).getReceiver();
 677                         if (receiver != null) {
 678                             //$$fb 2002-04-02: SysexMessages are mutable, so
 679                             // an application could change the contents of this object,
 680                             // or try to use the object later. So we can't get around object creation
 681                             // But the array need not be unique for each FastSysexMessage object,
 682                             // because it cannot be modified.
 683                             receiver.send(new FastSysexMessage(data), timeStamp);
 684                         }
 685                     }
 686                 }
 687             } catch (InvalidMidiDataException e) {
 688                 // this happens when invalid data comes over the wire. Ignore it.
 689                 return;
 690             }
 691         }
 692 

 693         /**
 694         * Send this message to all transmitters.
 695         */
 696         void sendMessage(MidiMessage message, long timeStamp) {
 697             if (message instanceof FastShortMessage) {
 698                 sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);
 699                 return;
 700             }
 701             synchronized(transmitters) {
 702                 int size = transmitters.size();
 703                 if (optimizedReceiverCount == size) {
 704                     if (midiOutReceiver != null) {
 705                         if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");
 706                         midiOutReceiver.send(message, timeStamp);
 707                     }
 708                 } else {
 709                     if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");
 710                     for (int i = 0; i < size; i++) {
 711                         Receiver receiver = transmitters.get(i).getReceiver();
 712                         if (receiver != null) {
 713                             //$$fb 2002-04-02: ShortMessages are mutable, so
 714                             // an application could change the contents of this object,
 715                             // or try to use the object later.
 716                             // We violate this spec here, to avoid costly (and gc-intensive)
 717                             // object creation for potentially hundred of messages per second.
 718                             // The spec should be changed to allow Immutable MidiMessages
 719                             // (i.e. throws InvalidStateException or so in setMessage)
 720                             receiver.send(message, timeStamp);
 721                         }
 722                     }
 723                 }
 724             }
 725         }


 726     } // TransmitterList

 727 }
< prev index next >