1 /* 2 * Copyright (c) 1999, 2013, 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.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 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 protected long id = 0; 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 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 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 public final List<Receiver> getReceivers() { 288 List<Receiver> recs; 289 synchronized (traRecLock) { 290 if (receiverList == null) { 291 recs = Collections.unmodifiableList(new ArrayList<Receiver>(0)); 292 } else { 293 recs = Collections.unmodifiableList 294 ((List<Receiver>) (receiverList.clone())); 295 } 296 } 297 return recs; 298 } 299 300 301 /** 302 * This implementation uses createTransmitter, which may throw an exception. 303 * If a transmitter is returned in createTransmitter, it is added to the internal 304 * TransmitterList 305 */ 306 public final Transmitter getTransmitter() throws MidiUnavailableException { 307 Transmitter transmitter; 308 synchronized (traRecLock) { 309 transmitter = createTransmitter(); // may throw MidiUnavailableException 310 getTransmitterList().add(transmitter); 311 } 312 return transmitter; 313 } 314 315 316 public final List<Transmitter> getTransmitters() { 317 List<Transmitter> tras; 318 synchronized (traRecLock) { 319 if (transmitterList == null 320 || transmitterList.transmitters.size() == 0) { 321 tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0)); 322 } else { 323 tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone())); 324 } 325 } 326 return tras; 327 } 328 329 330 // HELPER METHODS 331 332 final long getId() { 333 return id; 334 } 335 336 337 // REFERENCE COUNTING 338 339 /** Retrieve a Receiver and open the device implicitly. 340 This method is called by MidiSystem.getReceiver(). 341 */ 342 public final Receiver getReceiverReferenceCounting() 343 throws MidiUnavailableException { 344 /* Keep this order of commands! If getReceiver() throws an exception, 345 openInternal() should not be called! 346 */ 347 Receiver receiver; 348 synchronized (traRecLock) { 349 receiver = getReceiver(); 350 AbstractMidiDevice.this.openInternal(receiver); 351 } 352 return receiver; 353 } 354 355 356 /** Retrieve a Transmitter and open the device implicitly. 357 This method is called by MidiSystem.getTransmitter(). 358 */ 359 public final Transmitter getTransmitterReferenceCounting() 360 throws MidiUnavailableException { 361 /* Keep this order of commands! If getTransmitter() throws an exception, 362 openInternal() should not be called! 363 */ 364 Transmitter transmitter; 365 synchronized (traRecLock) { 366 transmitter = getTransmitter(); 367 AbstractMidiDevice.this.openInternal(transmitter); 368 } 369 return transmitter; 370 } 371 372 373 /** Return the list of objects that have opened the device implicitely. 374 */ 375 private synchronized List getOpenKeepingObjects() { 376 if (openKeepingObjects == null) { 377 openKeepingObjects = new ArrayList(); 378 } 379 return openKeepingObjects; 380 } 381 382 383 384 // RECEIVER HANDLING METHODS 385 386 387 /** Return the internal list of Receivers, possibly creating it first. 388 */ 389 private List<Receiver> getReceiverList() { 390 synchronized (traRecLock) { 391 if (receiverList == null) { 392 receiverList = new ArrayList<Receiver>(); 393 } 394 } 395 return receiverList; 396 } 397 398 399 /** Returns if this device supports Receivers. 400 Subclasses that use Receivers should override this method to 401 return true. They also should override createReceiver(). 402 403 @return true, if the device supports Receivers, false otherwise. 404 */ 405 protected boolean hasReceivers() { 406 return false; 407 } 408 409 410 /** Create a Receiver object. 411 throwing an exception here means that Receivers aren't enabled. 412 Subclasses that use Receivers should override this method with 413 one that returns objects implementing Receiver. 414 Classes overriding this method should also override hasReceivers() 415 to return true. 416 */ 417 protected Receiver createReceiver() throws MidiUnavailableException { 418 throw new MidiUnavailableException("MIDI IN receiver not available"); 419 } 420 421 422 423 // TRANSMITTER HANDLING 424 425 /** Return the internal list of Transmitters, possibly creating it first. 426 */ 427 final TransmitterList getTransmitterList() { 428 synchronized (traRecLock) { 429 if (transmitterList == null) { 430 transmitterList = new TransmitterList(); 431 } 432 } 433 return transmitterList; 434 } 435 436 437 /** Returns if this device supports Transmitters. 438 Subclasses that use Transmitters should override this method to 439 return true. They also should override createTransmitter(). 440 441 @return true, if the device supports Transmitters, false otherwise. 442 */ 443 protected boolean hasTransmitters() { 444 return false; 445 } 446 447 448 /** Create a Transmitter object. 449 throwing an exception here means that Transmitters aren't enabled. 450 Subclasses that use Transmitters should override this method with 451 one that returns objects implementing Transmitters. 452 Classes overriding this method should also override hasTransmitters() 453 to return true. 454 */ 455 protected Transmitter createTransmitter() throws MidiUnavailableException { 456 throw new MidiUnavailableException("MIDI OUT transmitter not available"); 457 } 458 459 // ABSTRACT METHODS 460 461 protected abstract void implOpen() throws MidiUnavailableException; 462 463 464 /** 465 * close this device if discarded by the garbage collector 466 */ 467 protected final void finalize() { 468 close(); 469 } 470 471 // INNER CLASSES 472 473 /** Base class for Receivers. 474 Subclasses that use Receivers must use this base class, since it 475 contains magic necessary to manage implicit closing the device. 476 This is necessary for Receivers retrieved via MidiSystem.getReceiver() 477 (which opens the device implicitely). 478 */ 479 abstract class AbstractReceiver implements MidiDeviceReceiver { 480 private boolean open = true; 481 482 483 /** Deliver a MidiMessage. 484 This method contains magic related to the closed state of a 485 Receiver. Therefore, subclasses should not override this method. 486 Instead, they should implement implSend(). 487 */ 488 @Override 489 public final synchronized void send(final MidiMessage message, 490 final long timeStamp) { 491 if (!open) { 492 throw new IllegalStateException("Receiver is not open"); 493 } 494 implSend(message, timeStamp); 495 } 496 497 abstract void implSend(MidiMessage message, long timeStamp); 498 499 /** Close the Receiver. 500 * Here, the call to the magic method closeInternal() takes place. 501 * Therefore, subclasses that override this method must call 502 * 'super.close()'. 503 */ 504 @Override 505 public final void close() { 506 open = false; 507 synchronized (AbstractMidiDevice.this.traRecLock) { 508 AbstractMidiDevice.this.getReceiverList().remove(this); 509 } 510 AbstractMidiDevice.this.closeInternal(this); 511 } 512 513 @Override 514 public final MidiDevice getMidiDevice() { 515 return AbstractMidiDevice.this; 516 } 517 518 final 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 class BasicTransmitter implements MidiDeviceTransmitter { 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 final 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 final 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 final 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 public final MidiDevice getMidiDevice() { 579 return AbstractMidiDevice.this; 580 } 581 582 } // class BasicTransmitter 583 584 585 /** 586 * a class to manage a list of transmitters 587 */ 588 final class TransmitterList { 589 590 private final ArrayList<Transmitter> transmitters = new ArrayList<Transmitter>(); 591 private MidiOutDevice.MidiOutReceiver midiOutReceiver; 592 593 // how many transmitters must be present for optimized 594 // handling 595 private int optimizedReceiverCount = 0; 596 597 598 private void add(Transmitter t) { 599 synchronized(transmitters) { 600 transmitters.add(t); 601 } 602 if (t instanceof BasicTransmitter) { 603 ((BasicTransmitter) t).setTransmitterList(this); 604 } 605 if (Printer.debug) Printer.debug("--added transmitter "+t); 606 } 607 608 private void remove(Transmitter t) { 609 synchronized(transmitters) { 610 int index = transmitters.indexOf(t); 611 if (index >= 0) { 612 transmitters.remove(index); 613 if (Printer.debug) Printer.debug("--removed transmitter "+t); 614 } 615 } 616 } 617 618 private void receiverChanged(BasicTransmitter t, 619 Receiver oldR, 620 Receiver newR) { 621 synchronized(transmitters) { 622 // some optimization 623 if (midiOutReceiver == oldR) { 624 midiOutReceiver = null; 625 } 626 if (newR != null) { 627 if ((newR instanceof MidiOutDevice.MidiOutReceiver) 628 && (midiOutReceiver == null)) { 629 midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR); 630 } 631 } 632 optimizedReceiverCount = 633 ((midiOutReceiver!=null)?1:0); 634 } 635 // more potential for optimization here 636 } 637 638 639 /** closes all transmitters and empties the list */ 640 void close() { 641 synchronized (transmitters) { 642 for(int i = 0; i < transmitters.size(); i++) { 643 transmitters.get(i).close(); 644 } 645 transmitters.clear(); 646 } 647 if (Printer.trace) Printer.trace("TransmitterList.close() succeeded"); 648 } 649 650 651 652 /** 653 * Send this message to all receivers 654 * status = packedMessage & 0xFF 655 * data1 = (packedMessage & 0xFF00) >> 8; 656 * data1 = (packedMessage & 0xFF0000) >> 16; 657 */ 658 void sendMessage(int packedMessage, long timeStamp) { 659 try { 660 synchronized(transmitters) { 661 int size = transmitters.size(); 662 if (optimizedReceiverCount == size) { 663 if (midiOutReceiver != null) { 664 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver"); 665 midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp); 666 } 667 } else { 668 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers"); 669 for (int i = 0; i < size; i++) { 670 Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver(); 671 if (receiver != null) { 672 if (optimizedReceiverCount > 0) { 673 if (receiver instanceof MidiOutDevice.MidiOutReceiver) { 674 ((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp); 675 } else { 676 receiver.send(new FastShortMessage(packedMessage), timeStamp); 677 } 678 } else { 679 receiver.send(new FastShortMessage(packedMessage), timeStamp); 680 } 681 } 682 } 683 } 684 } 685 } catch (InvalidMidiDataException e) { 686 // this happens when invalid data comes over the wire. Ignore it. 687 } 688 } 689 690 void sendMessage(byte[] data, long timeStamp) { 691 try { 692 synchronized(transmitters) { 693 int size = transmitters.size(); 694 if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers"); 695 for (int i = 0; i < size; i++) { 696 Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver(); 697 if (receiver != null) { 698 //$$fb 2002-04-02: SysexMessages are mutable, so 699 // an application could change the contents of this object, 700 // or try to use the object later. So we can't get around object creation 701 // But the array need not be unique for each FastSysexMessage object, 702 // because it cannot be modified. 703 receiver.send(new FastSysexMessage(data), timeStamp); 704 } 705 } 706 } 707 } catch (InvalidMidiDataException e) { 708 // this happens when invalid data comes over the wire. Ignore it. 709 return; 710 } 711 } 712 713 714 /** 715 * Send this message to all transmitters 716 */ 717 void sendMessage(MidiMessage message, long timeStamp) { 718 if (message instanceof FastShortMessage) { 719 sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp); 720 return; 721 } 722 synchronized(transmitters) { 723 int size = transmitters.size(); 724 if (optimizedReceiverCount == size) { 725 if (midiOutReceiver != null) { 726 if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver"); 727 midiOutReceiver.send(message, timeStamp); 728 } 729 } else { 730 if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers"); 731 for (int i = 0; i < size; i++) { 732 Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver(); 733 if (receiver != null) { 734 //$$fb 2002-04-02: ShortMessages are mutable, so 735 // an application could change the contents of this object, 736 // or try to use the object later. 737 // We violate this spec here, to avoid costly (and gc-intensive) 738 // object creation for potentially hundred of messages per second. 739 // The spec should be changed to allow Immutable MidiMessages 740 // (i.e. throws InvalidStateException or so in setMessage) 741 receiver.send(message, timeStamp); 742 } 743 } 744 } 745 } 746 } 747 748 749 } // TransmitterList 750 751 }