1 /*
2  * Copyright (c) 1999, 2019, 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 ArrayList<Receiver> receiverList;
54 
55     private TransmitterList transmitterList;
56 
57     // lock to protect receiverList and transmitterList
58     // from simultaneous creation and destruction
59     // reduces possibility of deadlock, compared to
60     // synchronizing to the class instance
61     private final Object traRecLock = new Object();
62 
63     // DEVICE ATTRIBUTES
64 
65     private final MidiDevice.Info info;
66 
67     // DEVICE STATE
68 
69     private volatile boolean open;
70     private int openRefCount;
71 
72     /** List of Receivers and Transmitters that opened the device implicitely.
73      */
74     private List<Object> openKeepingObjects;
75 
76     /**
77      * This is the device handle returned from native code.
78      */
79     protected volatile long id;
80 
81     /**
82      * Constructs an AbstractMidiDevice with the specified info object.
83      * @param info the description of the device
84      */
85     /*
86      * The initial mode and only supported mode default to OMNI_ON_POLY.
87      */
AbstractMidiDevice(MidiDevice.Info info)88     protected AbstractMidiDevice(MidiDevice.Info info) {
89         this.info = info;
90         openRefCount = 0;
91     }
92 
93     // MIDI DEVICE METHODS
94 
95     @Override
getDeviceInfo()96     public final MidiDevice.Info getDeviceInfo() {
97         return info;
98     }
99 
100     /** Open the device from an application program.
101      * Setting the open reference count to -1 here prevents Transmitters and Receivers that
102      * opened the device implicitly from closing it. The only way to close the device after
103      * this call is a call to close().
104      */
105     @Override
open()106     public final void open() throws MidiUnavailableException {
107         synchronized(this) {
108             openRefCount = -1;
109             doOpen();
110         }
111     }
112 
113     /** Open the device implicitly.
114      * This method is intended to be used by AbstractReceiver
115      * and BasicTransmitter. Actually, it is called by getReceiverReferenceCounting() and
116      * getTransmitterReferenceCounting(). These, in turn, are called by MidiSytem on calls to
117      * getReceiver() and getTransmitter(). The former methods should pass the Receiver or
118      * Transmitter just created as the object parameter to this method. Storing references to
119      * these objects is necessary to be able to decide later (when it comes to closing) if
120      * R/T's are ones that opened the device implicitly.
121      *
122      * @object The Receiver or Transmitter instance that triggered this implicit open.
123      */
openInternal(Object object)124     private void openInternal(Object object) throws MidiUnavailableException {
125         synchronized(this) {
126             if (openRefCount != -1) {
127                 openRefCount++;
128                 getOpenKeepingObjects().add(object);
129             }
130             // double calls to doOpens() will be catched by the open flag.
131             doOpen();
132         }
133     }
134 
doOpen()135     private void doOpen() throws MidiUnavailableException {
136         synchronized(this) {
137             if (! isOpen()) {
138                 implOpen();
139                 open = true;
140             }
141         }
142     }
143 
144     @Override
close()145     public final void close() {
146         synchronized (this) {
147             doClose();
148             openRefCount = 0;
149         }
150     }
151 
152     /** Close the device for an object that implicitely opened it.
153      * This method is intended to be used by Transmitter.close() and Receiver.close().
154      * Those methods should pass this for the object parameter. Since Transmitters or Receivers
155      * do not know if their device has been opened implicitely because of them, they call this
156      * method in any case. This method now is able to seperate Receivers/Transmitters that opened
157      * the device implicitely from those that didn't by looking up the R/T in the
158      * openKeepingObjects list. Only if the R/T is contained there, the reference count is
159      * reduced.
160      *
161      * @param object The object that might have been opening the device implicitely (for now,
162      * this may be a Transmitter or receiver).
163      */
closeInternal(Object object)164     public final void closeInternal(Object object) {
165         synchronized(this) {
166             if (getOpenKeepingObjects().remove(object)) {
167                 if (openRefCount > 0) {
168                     openRefCount--;
169                     if (openRefCount == 0) {
170                         doClose();
171                     }
172                 }
173             }
174         }
175     }
176 
doClose()177     public final void doClose() {
178         synchronized(this) {
179             if (isOpen()) {
180                 implClose();
181                 open = false;
182             }
183         }
184     }
185 
186     @Override
isOpen()187     public final boolean isOpen() {
188         return open;
189     }
190 
implClose()191     protected void implClose() {
192         synchronized (traRecLock) {
193             if (receiverList != null) {
194                 // close all receivers
195                 for(int i = 0; i < receiverList.size(); i++) {
196                     receiverList.get(i).close();
197                 }
198                 receiverList.clear();
199             }
200             if (transmitterList != null) {
201                 // close all transmitters
202                 transmitterList.close();
203             }
204         }
205     }
206 
207     /**
208      * This implementation always returns -1.
209      * Devices that actually provide this should over-ride
210      * this method.
211      */
212     @Override
getMicrosecondPosition()213     public long getMicrosecondPosition() {
214         return -1;
215     }
216 
217     /** Return the maximum number of Receivers supported by this device.
218         Depending on the return value of hasReceivers(), this method returns either 0 or -1.
219         Subclasses should rather override hasReceivers() than override this method.
220      */
221     @Override
getMaxReceivers()222     public final int getMaxReceivers() {
223         if (hasReceivers()) {
224             return -1;
225         } else {
226             return 0;
227         }
228     }
229 
230     /** Return the maximum number of Transmitters supported by this device.
231         Depending on the return value of hasTransmitters(), this method returns either 0 or -1.
232         Subclasses should override hasTransmitters().
233      */
234     @Override
getMaxTransmitters()235     public final int getMaxTransmitters() {
236         if (hasTransmitters()) {
237             return -1;
238         } else {
239             return 0;
240         }
241     }
242 
243     /** Retrieve a Receiver for this device.
244         This method returns the value returned by createReceiver(), if it doesn't throw
245         an exception. Subclasses should rather override createReceiver() than override
246         this method.
247         If createReceiver returns a Receiver, it is added to the internal list
248         of Receivers (see getReceiversList)
249      */
250     @Override
getReceiver()251     public final Receiver getReceiver() throws MidiUnavailableException {
252         Receiver receiver;
253         synchronized (traRecLock) {
254             receiver = createReceiver(); // may throw MidiUnavailableException
255             getReceiverList().add(receiver);
256         }
257         return receiver;
258     }
259 
260     @Override
261     @SuppressWarnings("unchecked") // Cast of result of clone
getReceivers()262     public final List<Receiver> getReceivers() {
263         List<Receiver> recs;
264         synchronized (traRecLock) {
265             if (receiverList == null) {
266                 recs = Collections.unmodifiableList(new ArrayList<Receiver>(0));
267             } else {
268                 recs = Collections.unmodifiableList
269                     ((List<Receiver>) (receiverList.clone()));
270             }
271         }
272         return recs;
273     }
274 
275     /**
276      * This implementation uses createTransmitter, which may throw an exception.
277      * If a transmitter is returned in createTransmitter, it is added to the internal
278      * TransmitterList
279      */
280     @Override
getTransmitter()281     public final Transmitter getTransmitter() throws MidiUnavailableException {
282         Transmitter transmitter;
283         synchronized (traRecLock) {
284             transmitter = createTransmitter(); // may throw MidiUnavailableException
285             getTransmitterList().add(transmitter);
286         }
287         return transmitter;
288     }
289 
290     @Override
291     @SuppressWarnings("unchecked") // Cast of result of clone
getTransmitters()292     public final List<Transmitter> getTransmitters() {
293         List<Transmitter> tras;
294         synchronized (traRecLock) {
295             if (transmitterList == null
296                 || transmitterList.transmitters.size() == 0) {
297                 tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0));
298             } else {
299                 tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone()));
300             }
301         }
302         return tras;
303     }
304 
getId()305     final long getId() {
306         return id;
307     }
308 
309     // REFERENCE COUNTING
310 
311     /** Retrieve a Receiver and open the device implicitly.
312         This method is called by MidiSystem.getReceiver().
313      */
314     @Override
getReceiverReferenceCounting()315     public final Receiver getReceiverReferenceCounting()
316             throws MidiUnavailableException {
317         /* Keep this order of commands! If getReceiver() throws an exception,
318            openInternal() should not be called!
319         */
320         Receiver receiver;
321         synchronized (traRecLock) {
322             receiver = getReceiver();
323             AbstractMidiDevice.this.openInternal(receiver);
324         }
325         return receiver;
326     }
327 
328     /** Retrieve a Transmitter and open the device implicitly.
329         This method is called by MidiSystem.getTransmitter().
330      */
331     @Override
getTransmitterReferenceCounting()332     public final Transmitter getTransmitterReferenceCounting()
333             throws MidiUnavailableException {
334         /* Keep this order of commands! If getTransmitter() throws an exception,
335            openInternal() should not be called!
336         */
337         Transmitter transmitter;
338         synchronized (traRecLock) {
339             transmitter = getTransmitter();
340             AbstractMidiDevice.this.openInternal(transmitter);
341         }
342         return transmitter;
343     }
344 
345     /** Return the list of objects that have opened the device implicitely.
346      */
getOpenKeepingObjects()347     private synchronized List<Object> getOpenKeepingObjects() {
348         if (openKeepingObjects == null) {
349             openKeepingObjects = new ArrayList<>();
350         }
351         return openKeepingObjects;
352     }
353 
354     // RECEIVER HANDLING METHODS
355 
356     /** Return the internal list of Receivers, possibly creating it first.
357      */
getReceiverList()358     private List<Receiver> getReceiverList() {
359         synchronized (traRecLock) {
360             if (receiverList == null) {
361                 receiverList = new ArrayList<>();
362             }
363         }
364         return receiverList;
365     }
366 
367     /** Returns if this device supports Receivers.
368         Subclasses that use Receivers should override this method to
369         return true. They also should override createReceiver().
370 
371         @return true, if the device supports Receivers, false otherwise.
372     */
hasReceivers()373     protected boolean hasReceivers() {
374         return false;
375     }
376 
377     /** Create a Receiver object.
378         throwing an exception here means that Receivers aren't enabled.
379         Subclasses that use Receivers should override this method with
380         one that returns objects implementing Receiver.
381         Classes overriding this method should also override hasReceivers()
382         to return true.
383     */
createReceiver()384     protected Receiver createReceiver() throws MidiUnavailableException {
385         throw new MidiUnavailableException("MIDI IN receiver not available");
386     }
387 
388     // TRANSMITTER HANDLING
389 
390     /** Return the internal list of Transmitters, possibly creating it first.
391      */
getTransmitterList()392     final TransmitterList getTransmitterList() {
393         synchronized (traRecLock) {
394             if (transmitterList == null) {
395                 transmitterList = new TransmitterList();
396             }
397         }
398         return transmitterList;
399     }
400 
401     /** Returns if this device supports Transmitters.
402         Subclasses that use Transmitters should override this method to
403         return true. They also should override createTransmitter().
404 
405         @return true, if the device supports Transmitters, false otherwise.
406     */
hasTransmitters()407     protected boolean hasTransmitters() {
408         return false;
409     }
410 
411     /** Create a Transmitter object.
412         throwing an exception here means that Transmitters aren't enabled.
413         Subclasses that use Transmitters should override this method with
414         one that returns objects implementing Transmitters.
415         Classes overriding this method should also override hasTransmitters()
416         to return true.
417     */
createTransmitter()418     protected Transmitter createTransmitter() throws MidiUnavailableException {
419         throw new MidiUnavailableException("MIDI OUT transmitter not available");
420     }
421 
implOpen()422     protected abstract void implOpen() throws MidiUnavailableException;
423 
424     /**
425      * close this device if discarded by the garbage collector.
426      */
427     @Override
428     @SuppressWarnings("deprecation")
finalize()429     protected final void finalize() {
430         close();
431     }
432 
433     /** Base class for Receivers.
434         Subclasses that use Receivers must use this base class, since it
435         contains magic necessary to manage implicit closing the device.
436         This is necessary for Receivers retrieved via MidiSystem.getReceiver()
437         (which opens the device implicitely).
438      */
439     abstract class AbstractReceiver implements MidiDeviceReceiver {
440         private volatile boolean open = true;
441 
442 
443         /** Deliver a MidiMessage.
444             This method contains magic related to the closed state of a
445             Receiver. Therefore, subclasses should not override this method.
446             Instead, they should implement implSend().
447         */
448         @Override
send(final MidiMessage message, final long timeStamp)449         public final synchronized void send(final MidiMessage message,
450                                             final long timeStamp) {
451             if (!open) {
452                 throw new IllegalStateException("Receiver is not open");
453             }
454             implSend(message, timeStamp);
455         }
456 
implSend(MidiMessage message, long timeStamp)457         abstract void implSend(MidiMessage message, long timeStamp);
458 
459         /** Close the Receiver.
460          * Here, the call to the magic method closeInternal() takes place.
461          * Therefore, subclasses that override this method must call
462          * 'super.close()'.
463          */
464         @Override
close()465         public final void close() {
466             open = false;
467             synchronized (AbstractMidiDevice.this.traRecLock) {
468                 AbstractMidiDevice.this.getReceiverList().remove(this);
469             }
470             AbstractMidiDevice.this.closeInternal(this);
471         }
472 
473         @Override
getMidiDevice()474         public final MidiDevice getMidiDevice() {
475             return AbstractMidiDevice.this;
476         }
477 
isOpen()478         final boolean isOpen() {
479             return open;
480         }
481 
482         //$$fb is that a good idea?
483         //protected void finalize() {
484         //    close();
485         //}
486 
487     } // class AbstractReceiver
488 
489 
490     /**
491      * Transmitter base class.
492      * This class especially makes sure the device is closed if it
493      * has been opened implicitly by a call to MidiSystem.getTransmitter().
494      * The logic of doing so is actually in closeInternal().
495      *
496      * Also, it has some optimizations regarding sending to the Receivers,
497      * for known Receivers, and managing itself in the TransmitterList.
498      */
499     class BasicTransmitter implements MidiDeviceTransmitter {
500 
501         private Receiver receiver = null;
502         TransmitterList tlist = null;
503 
BasicTransmitter()504         protected BasicTransmitter() {
505         }
506 
setTransmitterList(TransmitterList tlist)507         private void setTransmitterList(TransmitterList tlist) {
508             this.tlist = tlist;
509         }
510 
511         @Override
setReceiver(Receiver receiver)512         public final void setReceiver(Receiver receiver) {
513             if (tlist != null && this.receiver != receiver) {
514                 tlist.receiverChanged(this, this.receiver, receiver);
515                 this.receiver = receiver;
516             }
517         }
518 
519         @Override
getReceiver()520         public final Receiver getReceiver() {
521             return receiver;
522         }
523 
524         /** Close the Transmitter.
525          * Here, the call to the magic method closeInternal() takes place.
526          * Therefore, subclasses that override this method must call
527          * 'super.close()'.
528          */
529         @Override
close()530         public final void close() {
531             AbstractMidiDevice.this.closeInternal(this);
532             if (tlist != null) {
533                 tlist.receiverChanged(this, this.receiver, null);
534                 tlist.remove(this);
535                 tlist = null;
536             }
537         }
538 
539         @Override
getMidiDevice()540         public final MidiDevice getMidiDevice() {
541             return AbstractMidiDevice.this;
542         }
543 
544     } // class BasicTransmitter
545 
546     /**
547      * a class to manage a list of transmitters.
548      */
549     final class TransmitterList {
550 
551         private final ArrayList<Transmitter> transmitters = new ArrayList<>();
552         private MidiOutDevice.MidiOutReceiver midiOutReceiver;
553 
554         // how many transmitters must be present for optimized
555         // handling
556         private int optimizedReceiverCount = 0;
557 
558 
add(Transmitter t)559         private void add(Transmitter t) {
560             synchronized(transmitters) {
561                 transmitters.add(t);
562             }
563             if (t instanceof BasicTransmitter) {
564                 ((BasicTransmitter) t).setTransmitterList(this);
565             }
566         }
567 
remove(Transmitter t)568         private void remove(Transmitter t) {
569             synchronized(transmitters) {
570                 int index = transmitters.indexOf(t);
571                 if (index >= 0) {
572                     transmitters.remove(index);
573                 }
574             }
575         }
576 
receiverChanged(BasicTransmitter t, Receiver oldR, Receiver newR)577         private void receiverChanged(BasicTransmitter t,
578                                      Receiver oldR,
579                                      Receiver newR) {
580             synchronized(transmitters) {
581                 // some optimization
582                 if (midiOutReceiver == oldR) {
583                     midiOutReceiver = null;
584                 }
585                 if (newR != null) {
586                     if ((newR instanceof MidiOutDevice.MidiOutReceiver)
587                         && (midiOutReceiver == null)) {
588                         midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR);
589                     }
590                 }
591                 optimizedReceiverCount =
592                       ((midiOutReceiver!=null)?1:0);
593             }
594             // more potential for optimization here
595         }
596 
597 
598         /** closes all transmitters and empties the list */
close()599         void close() {
600             synchronized (transmitters) {
601                 for(int i = 0; i < transmitters.size(); i++) {
602                     transmitters.get(i).close();
603                 }
604                 transmitters.clear();
605             }
606         }
607 
608 
609 
610         /**
611         * Send this message to all receivers
612         * status = packedMessage & 0xFF
613         * data1 = (packedMessage & 0xFF00) >> 8;
614         * data1 = (packedMessage & 0xFF0000) >> 16;
615         */
sendMessage(int packedMessage, long timeStamp)616         void sendMessage(int packedMessage, long timeStamp) {
617             try {
618                 synchronized(transmitters) {
619                     int size = transmitters.size();
620                     if (optimizedReceiverCount == size) {
621                         if (midiOutReceiver != null) {
622                             midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp);
623                         }
624                     } else {
625                         for (int i = 0; i < size; i++) {
626                             Receiver receiver = transmitters.get(i).getReceiver();
627                             if (receiver != null) {
628                                 if (optimizedReceiverCount > 0) {
629                                     if (receiver instanceof MidiOutDevice.MidiOutReceiver) {
630                                         ((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);
631                                     } else {
632                                         receiver.send(new FastShortMessage(packedMessage), timeStamp);
633                                     }
634                                 } else {
635                                     receiver.send(new FastShortMessage(packedMessage), timeStamp);
636                                 }
637                             }
638                         }
639                     }
640                 }
641             } catch (InvalidMidiDataException e) {
642                 // this happens when invalid data comes over the wire. Ignore it.
643             }
644         }
645 
sendMessage(byte[] data, long timeStamp)646         void sendMessage(byte[] data, long timeStamp) {
647             try {
648                 synchronized(transmitters) {
649                     int size = transmitters.size();
650                     for (int i = 0; i < size; i++) {
651                         Receiver receiver = transmitters.get(i).getReceiver();
652                         if (receiver != null) {
653                             //$$fb 2002-04-02: SysexMessages are mutable, so
654                             // an application could change the contents of this object,
655                             // or try to use the object later. So we can't get around object creation
656                             // But the array need not be unique for each FastSysexMessage object,
657                             // because it cannot be modified.
658                             receiver.send(new FastSysexMessage(data), timeStamp);
659                         }
660                     }
661                 }
662             } catch (InvalidMidiDataException e) {
663                 // this happens when invalid data comes over the wire. Ignore it.
664                 return;
665             }
666         }
667 
668         /**
669         * Send this message to all transmitters.
670         */
sendMessage(MidiMessage message, long timeStamp)671         void sendMessage(MidiMessage message, long timeStamp) {
672             if (message instanceof FastShortMessage) {
673                 sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);
674                 return;
675             }
676             synchronized(transmitters) {
677                 int size = transmitters.size();
678                 if (optimizedReceiverCount == size) {
679                     if (midiOutReceiver != null) {
680                         midiOutReceiver.send(message, timeStamp);
681                     }
682                 } else {
683                     for (int i = 0; i < size; i++) {
684                         Receiver receiver = transmitters.get(i).getReceiver();
685                         if (receiver != null) {
686                             //$$fb 2002-04-02: ShortMessages are mutable, so
687                             // an application could change the contents of this object,
688                             // or try to use the object later.
689                             // We violate this spec here, to avoid costly (and gc-intensive)
690                             // object creation for potentially hundred of messages per second.
691                             // The spec should be changed to allow Immutable MidiMessages
692                             // (i.e. throws InvalidStateException or so in setMessage)
693                             receiver.send(message, timeStamp);
694                         }
695                     }
696                 }
697             }
698         }
699     } // TransmitterList
700 }
701