1 /* 2 * Copyright (c) 1999, 2014, 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<Object> 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 @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 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"); 495 } 496 implSend(message, timeStamp); 497 } 498 499 abstract void implSend(MidiMessage message, long timeStamp); 500 501 /** Close the Receiver. 502 * Here, the call to the magic method closeInternal() takes place. 503 * Therefore, subclasses that override this method must call 504 * 'super.close()'. 505 */ 506 @Override 507 public final void close() { 508 open = false; 509 synchronized (AbstractMidiDevice.this.traRecLock) { 510 AbstractMidiDevice.this.getReceiverList().remove(this); 511 } 512 AbstractMidiDevice.this.closeInternal(this); 513 } 514 515 @Override 516 public final MidiDevice getMidiDevice() { 517 return AbstractMidiDevice.this; 518 } 519 520 final boolean isOpen() { 521 return open; 522 } 523 524 //$$fb is that a good idea? 525 //protected void finalize() { 526 // close(); 527 //} 528 529 } // class AbstractReceiver 530 531 532 /** 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); 613 if (index >= 0) { 614 transmitters.remove(index); 615 if (Printer.debug) Printer.debug("--removed transmitter "+t); 616 } 617 } 618 } 619 620 private void receiverChanged(BasicTransmitter t, 621 Receiver oldR, 622 Receiver newR) { 623 synchronized(transmitters) { 624 // some optimization 625 if (midiOutReceiver == oldR) { 626 midiOutReceiver = null; 627 } 628 if (newR != null) { 629 if ((newR instanceof MidiOutDevice.MidiOutReceiver) 630 && (midiOutReceiver == null)) { 631 midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR); 632 } 633 } 634 optimizedReceiverCount = 635 ((midiOutReceiver!=null)?1:0); 636 } 637 // more potential for optimization here 638 } 639 640 641 /** closes all transmitters and empties the list */ 642 void close() { 643 synchronized (transmitters) { 644 for(int i = 0; i < transmitters.size(); i++) { 645 transmitters.get(i).close(); 646 } 647 transmitters.clear(); 648 } 649 if (Printer.trace) Printer.trace("TransmitterList.close() succeeded"); 650 } 651 652 653 654 /** 655 * Send this message to all receivers 656 * status = packedMessage & 0xFF 657 * data1 = (packedMessage & 0xFF00) >> 8; 658 * data1 = (packedMessage & 0xFF0000) >> 16; 659 */ 660 void sendMessage(int packedMessage, long timeStamp) { 661 try { 662 synchronized(transmitters) { 663 int size = transmitters.size(); 664 if (optimizedReceiverCount == size) { 665 if (midiOutReceiver != null) { 666 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver"); 667 midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp); 668 } 669 } else { 670 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers"); 671 for (int i = 0; i < size; i++) { 672 Receiver receiver = transmitters.get(i).getReceiver(); 673 if (receiver != null) { 674 if (optimizedReceiverCount > 0) { 675 if (receiver instanceof MidiOutDevice.MidiOutReceiver) { 676 ((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp); 677 } else { 678 receiver.send(new FastShortMessage(packedMessage), timeStamp); 679 } 680 } else { 681 receiver.send(new FastShortMessage(packedMessage), timeStamp); 682 } 683 } 684 } 685 } 686 } 687 } catch (InvalidMidiDataException e) { 688 // this happens when invalid data comes over the wire. Ignore it. 689 } 690 } 691 692 void sendMessage(byte[] data, long timeStamp) { 693 try { 694 synchronized(transmitters) { 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 }