1 /*
2  * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
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      */
AbstractMidiDevice(MidiDevice.Info info)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
getDeviceInfo()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
open()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      */
openInternal(Object object)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 
doOpen()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
close()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      */
closeInternal(Object object)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 
doClose()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
isOpen()206     public final boolean isOpen() {
207         return open;
208     }
209 
implClose()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
getMicrosecondPosition()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
getMaxReceivers()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
getMaxTransmitters()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
getReceiver()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
getReceivers()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
getTransmitter()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
getTransmitters()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 
getId()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
getReceiverReferenceCounting()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
getTransmitterReferenceCounting()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      */
getOpenKeepingObjects()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      */
getReceiverList()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     */
hasReceivers()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     */
createReceiver()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      */
getTransmitterList()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     */
hasTransmitters()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     */
createTransmitter()437     protected Transmitter createTransmitter() throws MidiUnavailableException {
438         throw new MidiUnavailableException("MIDI OUT transmitter not available");
439     }
440 
implOpen()441     protected abstract void implOpen() throws MidiUnavailableException;
442 
443     /**
444      * close this device if discarded by the garbage collector.
445      */
446     @Override
447     @SuppressWarnings("deprecation")
finalize()448     protected final void finalize() {
449         close();
450     }
451 
452     /** Base class for Receivers.
453         Subclasses that use Receivers must use this base class, since it
454         contains magic necessary to manage implicit closing the device.
455         This is necessary for Receivers retrieved via MidiSystem.getReceiver()
456         (which opens the device implicitely).
457      */
458     abstract class AbstractReceiver implements MidiDeviceReceiver {
459         private volatile boolean open = true;
460 
461 
462         /** Deliver a MidiMessage.
463             This method contains magic related to the closed state of a
464             Receiver. Therefore, subclasses should not override this method.
465             Instead, they should implement implSend().
466         */
467         @Override
send(final MidiMessage message, final long timeStamp)468         public final synchronized void send(final MidiMessage message,
469                                             final long timeStamp) {
470             if (!open) {
471                 throw new IllegalStateException("Receiver is not open");
472             }
473             implSend(message, timeStamp);
474         }
475 
implSend(MidiMessage message, long timeStamp)476         abstract void implSend(MidiMessage message, long timeStamp);
477 
478         /** Close the Receiver.
479          * Here, the call to the magic method closeInternal() takes place.
480          * Therefore, subclasses that override this method must call
481          * 'super.close()'.
482          */
483         @Override
close()484         public final void close() {
485             open = false;
486             synchronized (AbstractMidiDevice.this.traRecLock) {
487                 AbstractMidiDevice.this.getReceiverList().remove(this);
488             }
489             AbstractMidiDevice.this.closeInternal(this);
490         }
491 
492         @Override
getMidiDevice()493         public final MidiDevice getMidiDevice() {
494             return AbstractMidiDevice.this;
495         }
496 
isOpen()497         final boolean isOpen() {
498             return open;
499         }
500 
501         //$$fb is that a good idea?
502         //protected void finalize() {
503         //    close();
504         //}
505 
506     } // class AbstractReceiver
507 
508 
509     /**
510      * Transmitter base class.
511      * This class especially makes sure the device is closed if it
512      * has been opened implicitly by a call to MidiSystem.getTransmitter().
513      * The logic of doing so is actually in closeInternal().
514      *
515      * Also, it has some optimizations regarding sending to the Receivers,
516      * for known Receivers, and managing itself in the TransmitterList.
517      */
518     class BasicTransmitter implements MidiDeviceTransmitter {
519 
520         private Receiver receiver = null;
521         TransmitterList tlist = null;
522 
BasicTransmitter()523         protected BasicTransmitter() {
524         }
525 
setTransmitterList(TransmitterList tlist)526         private void setTransmitterList(TransmitterList tlist) {
527             this.tlist = tlist;
528         }
529 
530         @Override
setReceiver(Receiver receiver)531         public final void setReceiver(Receiver receiver) {
532             if (tlist != null && this.receiver != receiver) {
533                 if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver);
534                 tlist.receiverChanged(this, this.receiver, receiver);
535                 this.receiver = receiver;
536             }
537         }
538 
539         @Override
getReceiver()540         public final Receiver getReceiver() {
541             return receiver;
542         }
543 
544         /** Close the Transmitter.
545          * Here, the call to the magic method closeInternal() takes place.
546          * Therefore, subclasses that override this method must call
547          * 'super.close()'.
548          */
549         @Override
close()550         public final void close() {
551             AbstractMidiDevice.this.closeInternal(this);
552             if (tlist != null) {
553                 tlist.receiverChanged(this, this.receiver, null);
554                 tlist.remove(this);
555                 tlist = null;
556             }
557         }
558 
559         @Override
getMidiDevice()560         public final MidiDevice getMidiDevice() {
561             return AbstractMidiDevice.this;
562         }
563 
564     } // class BasicTransmitter
565 
566     /**
567      * a class to manage a list of transmitters.
568      */
569     final class TransmitterList {
570 
571         private final ArrayList<Transmitter> transmitters = new ArrayList<>();
572         private MidiOutDevice.MidiOutReceiver midiOutReceiver;
573 
574         // how many transmitters must be present for optimized
575         // handling
576         private int optimizedReceiverCount = 0;
577 
578 
add(Transmitter t)579         private void add(Transmitter t) {
580             synchronized(transmitters) {
581                 transmitters.add(t);
582             }
583             if (t instanceof BasicTransmitter) {
584                 ((BasicTransmitter) t).setTransmitterList(this);
585             }
586             if (Printer.debug) Printer.debug("--added transmitter "+t);
587         }
588 
remove(Transmitter t)589         private void remove(Transmitter t) {
590             synchronized(transmitters) {
591                 int index = transmitters.indexOf(t);
592                 if (index >= 0) {
593                     transmitters.remove(index);
594                     if (Printer.debug) Printer.debug("--removed transmitter "+t);
595                 }
596             }
597         }
598 
receiverChanged(BasicTransmitter t, Receiver oldR, Receiver newR)599         private void receiverChanged(BasicTransmitter t,
600                                      Receiver oldR,
601                                      Receiver newR) {
602             synchronized(transmitters) {
603                 // some optimization
604                 if (midiOutReceiver == oldR) {
605                     midiOutReceiver = null;
606                 }
607                 if (newR != null) {
608                     if ((newR instanceof MidiOutDevice.MidiOutReceiver)
609                         && (midiOutReceiver == null)) {
610                         midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR);
611                     }
612                 }
613                 optimizedReceiverCount =
614                       ((midiOutReceiver!=null)?1:0);
615             }
616             // more potential for optimization here
617         }
618 
619 
620         /** closes all transmitters and empties the list */
close()621         void close() {
622             synchronized (transmitters) {
623                 for(int i = 0; i < transmitters.size(); i++) {
624                     transmitters.get(i).close();
625                 }
626                 transmitters.clear();
627             }
628             if (Printer.trace) Printer.trace("TransmitterList.close() succeeded");
629         }
630 
631 
632 
633         /**
634         * Send this message to all receivers
635         * status = packedMessage & 0xFF
636         * data1 = (packedMessage & 0xFF00) >> 8;
637         * data1 = (packedMessage & 0xFF0000) >> 16;
638         */
sendMessage(int packedMessage, long timeStamp)639         void sendMessage(int packedMessage, long timeStamp) {
640             try {
641                 synchronized(transmitters) {
642                     int size = transmitters.size();
643                     if (optimizedReceiverCount == size) {
644                         if (midiOutReceiver != null) {
645                             if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver");
646                             midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp);
647                         }
648                     } else {
649                         if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers");
650                         for (int i = 0; i < size; i++) {
651                             Receiver receiver = transmitters.get(i).getReceiver();
652                             if (receiver != null) {
653                                 if (optimizedReceiverCount > 0) {
654                                     if (receiver instanceof MidiOutDevice.MidiOutReceiver) {
655                                         ((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);
656                                     } else {
657                                         receiver.send(new FastShortMessage(packedMessage), timeStamp);
658                                     }
659                                 } else {
660                                     receiver.send(new FastShortMessage(packedMessage), timeStamp);
661                                 }
662                             }
663                         }
664                     }
665                 }
666             } catch (InvalidMidiDataException e) {
667                 // this happens when invalid data comes over the wire. Ignore it.
668             }
669         }
670 
sendMessage(byte[] data, long timeStamp)671         void sendMessage(byte[] data, long timeStamp) {
672             try {
673                 synchronized(transmitters) {
674                     int size = transmitters.size();
675                     if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers");
676                     for (int i = 0; i < size; i++) {
677                         Receiver receiver = transmitters.get(i).getReceiver();
678                         if (receiver != null) {
679                             //$$fb 2002-04-02: SysexMessages are mutable, so
680                             // an application could change the contents of this object,
681                             // or try to use the object later. So we can't get around object creation
682                             // But the array need not be unique for each FastSysexMessage object,
683                             // because it cannot be modified.
684                             receiver.send(new FastSysexMessage(data), timeStamp);
685                         }
686                     }
687                 }
688             } catch (InvalidMidiDataException e) {
689                 // this happens when invalid data comes over the wire. Ignore it.
690                 return;
691             }
692         }
693 
694         /**
695         * Send this message to all transmitters.
696         */
sendMessage(MidiMessage message, long timeStamp)697         void sendMessage(MidiMessage message, long timeStamp) {
698             if (message instanceof FastShortMessage) {
699                 sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);
700                 return;
701             }
702             synchronized(transmitters) {
703                 int size = transmitters.size();
704                 if (optimizedReceiverCount == size) {
705                     if (midiOutReceiver != null) {
706                         if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");
707                         midiOutReceiver.send(message, timeStamp);
708                     }
709                 } else {
710                     if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");
711                     for (int i = 0; i < size; i++) {
712                         Receiver receiver = transmitters.get(i).getReceiver();
713                         if (receiver != null) {
714                             //$$fb 2002-04-02: ShortMessages are mutable, so
715                             // an application could change the contents of this object,
716                             // or try to use the object later.
717                             // We violate this spec here, to avoid costly (and gc-intensive)
718                             // object creation for potentially hundred of messages per second.
719                             // The spec should be changed to allow Immutable MidiMessages
720                             // (i.e. throws InvalidStateException or so in setMessage)
721                             receiver.send(message, timeStamp);
722                         }
723                     }
724                 }
725             }
726         }
727     } // TransmitterList
728 }
729