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