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