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 }
|