1 /* 2 * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.media.sound; 27 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 32 import javax.sound.midi.InvalidMidiDataException; 33 import javax.sound.midi.MidiDevice; 34 import javax.sound.midi.MidiDeviceReceiver; 35 import javax.sound.midi.MidiDeviceTransmitter; 36 import javax.sound.midi.MidiMessage; 37 import javax.sound.midi.MidiUnavailableException; 38 import javax.sound.midi.Receiver; 39 import javax.sound.midi.Transmitter; 40 41 42 /** 43 * Abstract AbstractMidiDevice class representing functionality shared by 44 * MidiInDevice and MidiOutDevice objects. 45 * 46 * @author David Rivas 47 * @author Kara Kytle 48 * @author Matthias Pfisterer 49 * @author Florian Bomers 50 */ 51 abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice { 52 53 private 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"); 471 } 472 implSend(message, timeStamp); 473 } 474 475 abstract void implSend(MidiMessage message, long timeStamp); 476 477 /** Close the Receiver. 478 * Here, the call to the magic method closeInternal() takes place. 479 * Therefore, subclasses that override this method must call 480 * 'super.close()'. 481 */ 482 @Override 483 public final void close() { 484 open = false; 485 synchronized (AbstractMidiDevice.this.traRecLock) { 486 AbstractMidiDevice.this.getReceiverList().remove(this); 487 } 488 AbstractMidiDevice.this.closeInternal(this); 489 } 490 491 @Override 492 public final MidiDevice getMidiDevice() { 493 return AbstractMidiDevice.this; 494 } 495 496 final boolean isOpen() { 497 return open; 498 } 499 500 //$$fb is that a good idea? 501 //protected void finalize() { 502 // close(); 503 //} 504 505 } // class AbstractReceiver 506 507 508 /** 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); 591 if (index >= 0) { 592 transmitters.remove(index); 593 if (Printer.debug) Printer.debug("--removed transmitter "+t); 594 } 595 } 596 } 597 598 private void receiverChanged(BasicTransmitter t, 599 Receiver oldR, 600 Receiver newR) { 601 synchronized(transmitters) { 602 // some optimization 603 if (midiOutReceiver == oldR) { 604 midiOutReceiver = null; 605 } 606 if (newR != null) { 607 if ((newR instanceof MidiOutDevice.MidiOutReceiver) 608 && (midiOutReceiver == null)) { 609 midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR); 610 } 611 } 612 optimizedReceiverCount = 613 ((midiOutReceiver!=null)?1:0); 614 } 615 // more potential for optimization here 616 } 617 618 619 /** closes all transmitters and empties the list */ 620 void close() { 621 synchronized (transmitters) { 622 for(int i = 0; i < transmitters.size(); i++) { 623 transmitters.get(i).close(); 624 } 625 transmitters.clear(); 626 } 627 if (Printer.trace) Printer.trace("TransmitterList.close() succeeded"); 628 } 629 630 631 632 /** 633 * Send this message to all receivers 634 * status = packedMessage & 0xFF 635 * data1 = (packedMessage & 0xFF00) >> 8; 636 * data1 = (packedMessage & 0xFF0000) >> 16; 637 */ 638 void sendMessage(int packedMessage, long timeStamp) { 639 try { 640 synchronized(transmitters) { 641 int size = transmitters.size(); 642 if (optimizedReceiverCount == size) { 643 if (midiOutReceiver != null) { 644 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver"); 645 midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp); 646 } 647 } else { 648 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers"); 649 for (int i = 0; i < size; i++) { 650 Receiver receiver = transmitters.get(i).getReceiver(); 651 if (receiver != null) { 652 if (optimizedReceiverCount > 0) { 653 if (receiver instanceof MidiOutDevice.MidiOutReceiver) { 654 ((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp); 655 } else { 656 receiver.send(new FastShortMessage(packedMessage), timeStamp); 657 } 658 } else { 659 receiver.send(new FastShortMessage(packedMessage), timeStamp); 660 } 661 } 662 } 663 } 664 } 665 } catch (InvalidMidiDataException e) { 666 // this happens when invalid data comes over the wire. Ignore it. 667 } 668 } 669 670 void sendMessage(byte[] data, long timeStamp) { 671 try { 672 synchronized(transmitters) { 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 }