1 /* 2 * Copyright (c) 1999, 2010, 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 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 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 MidiDevice.Info getDeviceInfo() { 106 return info; 107 } 108 109 /** Open the device from an aplication 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 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 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 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 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 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 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 Receiver getReceiverReferenceCounting() throws MidiUnavailableException { 343 /* Keep this order of commands! If getReceiver() throws an exception, 344 openInternal() should not be called! 345 */ 346 Receiver receiver; 347 synchronized (traRecLock) { 348 receiver = getReceiver(); 349 AbstractMidiDevice.this.openInternal(receiver); 350 } 351 return receiver; 352 } 353 354 355 /** Retrieve a Transmitter and open the device implicitly. 356 This method is called by MidiSystem.getTransmitter(). 357 */ 358 public Transmitter getTransmitterReferenceCounting() throws MidiUnavailableException { 359 /* Keep this order of commands! If getTransmitter() throws an exception, 360 openInternal() should not be called! 361 */ 362 Transmitter transmitter; 363 synchronized (traRecLock) { 364 transmitter = getTransmitter(); 365 AbstractMidiDevice.this.openInternal(transmitter); 366 } 367 return transmitter; 368 } 369 370 371 /** Return the list of objects that have opened the device implicitely. 372 */ 373 private synchronized List getOpenKeepingObjects() { 374 if (openKeepingObjects == null) { 375 openKeepingObjects = new ArrayList(); 376 } 377 return openKeepingObjects; 378 } 379 380 381 382 // RECEIVER HANDLING METHODS 383 384 385 /** Return the internal list of Receivers, possibly creating it first. 386 */ 387 private List<Receiver> getReceiverList() { 388 synchronized (traRecLock) { 389 if (receiverList == null) { 390 receiverList = new ArrayList<Receiver>(); 391 } 392 } 393 return receiverList; 394 } 395 396 397 /** Returns if this device supports Receivers. 398 Subclasses that use Receivers should override this method to 399 return true. They also should override createReceiver(). 400 401 @return true, if the device supports Receivers, false otherwise. 402 */ 403 protected boolean hasReceivers() { 404 return false; 405 } 406 407 408 /** Create a Receiver object. 409 throwing an exception here means that Receivers aren't enabled. 410 Subclasses that use Receivers should override this method with 411 one that returns objects implementing Receiver. 412 Classes overriding this method should also override hasReceivers() 413 to return true. 414 */ 415 protected Receiver createReceiver() throws MidiUnavailableException { 416 throw new MidiUnavailableException("MIDI IN receiver not available"); 417 } 418 419 420 421 // TRANSMITTER HANDLING 422 423 /** Return the internal list of Transmitters, possibly creating it first. 424 */ 425 protected TransmitterList getTransmitterList() { 426 synchronized (traRecLock) { 427 if (transmitterList == null) { 428 transmitterList = new TransmitterList(); 429 } 430 } 431 return transmitterList; 432 } 433 434 435 /** Returns if this device supports Transmitters. 436 Subclasses that use Transmitters should override this method to 437 return true. They also should override createTransmitter(). 438 439 @return true, if the device supports Transmitters, false otherwise. 440 */ 441 protected boolean hasTransmitters() { 442 return false; 443 } 444 445 446 /** Create a Transmitter object. 447 throwing an exception here means that Transmitters aren't enabled. 448 Subclasses that use Transmitters should override this method with 449 one that returns objects implementing Transmitters. 450 Classes overriding this method should also override hasTransmitters() 451 to return true. 452 */ 453 protected Transmitter createTransmitter() throws MidiUnavailableException { 454 throw new MidiUnavailableException("MIDI OUT transmitter not available"); 455 } 456 457 // ABSTRACT METHODS 458 459 protected abstract void implOpen() throws MidiUnavailableException; 460 461 462 /** 463 * close this device if discarded by the garbage collector 464 */ 465 protected void finalize() { 466 close(); 467 } 468 469 // INNER CLASSES 470 471 /** Base class for Receivers. 472 Subclasses that use Receivers must use this base class, since it 473 contains magic necessary to manage implicit closing the device. 474 This is necessary for Receivers retrieved via MidiSystem.getReceiver() 475 (which opens the device implicitely). 476 */ 477 protected abstract class AbstractReceiver implements MidiDeviceReceiver { 478 private boolean open = true; 479 480 481 /** Deliver a MidiMessage. 482 This method contains magic related to the closed state of a 483 Receiver. Therefore, subclasses should not override this method. 484 Instead, they should implement implSend(). 485 */ 486 public synchronized void send(MidiMessage message, long timeStamp) { 487 if (open) { 488 implSend(message, timeStamp); 489 } else { 490 throw new IllegalStateException("Receiver is not open"); 491 } 492 } 493 494 495 protected abstract void implSend(MidiMessage message, long timeStamp); 496 497 498 /** Close the Receiver. 499 * Here, the call to the magic method closeInternal() takes place. 500 * Therefore, subclasses that override this method must call 501 * 'super.close()'. 502 */ 503 public void close() { 504 open = false; 505 synchronized (AbstractMidiDevice.this.traRecLock) { 506 AbstractMidiDevice.this.getReceiverList().remove(this); 507 } 508 AbstractMidiDevice.this.closeInternal(this); 509 } 510 511 public MidiDevice getMidiDevice() { 512 return AbstractMidiDevice.this; 513 } 514 515 protected boolean isOpen() { 516 return open; 517 } 518 519 //$$fb is that a good idea? 520 //protected void finalize() { 521 // close(); 522 //} 523 524 } // class AbstractReceiver 525 526 527 /** 528 * Transmitter base class. 529 * This class especially makes sure the device is closed if it 530 * has been opened implicitly by a call to MidiSystem.getTransmitter(). 531 * The logic of doing so is actually in closeInternal(). 532 * 533 * Also, it has some optimizations regarding sending to the Receivers, 534 * for known Receivers, and managing itself in the TransmitterList. 535 */ 536 protected class BasicTransmitter implements MidiDeviceTransmitter { 537 538 private Receiver receiver = null; 539 TransmitterList tlist = null; 540 541 protected BasicTransmitter() { 542 } 543 544 private void setTransmitterList(TransmitterList tlist) { 545 this.tlist = tlist; 546 } 547 548 public void setReceiver(Receiver receiver) { 549 if (tlist != null && this.receiver != receiver) { 550 if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver); 551 tlist.receiverChanged(this, this.receiver, receiver); 552 this.receiver = receiver; 553 } 554 } 555 556 public Receiver getReceiver() { 557 return receiver; 558 } 559 560 561 /** Close the Transmitter. 562 * Here, the call to the magic method closeInternal() takes place. 563 * Therefore, subclasses that override this method must call 564 * 'super.close()'. 565 */ 566 public void close() { 567 AbstractMidiDevice.this.closeInternal(this); 568 if (tlist != null) { 569 tlist.receiverChanged(this, this.receiver, null); 570 tlist.remove(this); 571 tlist = null; 572 } 573 } 574 575 public MidiDevice getMidiDevice() { 576 return AbstractMidiDevice.this; 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 590 // how many transmitters must be present for optimized 591 // handling 592 private int optimizedReceiverCount = 0; 593 594 595 private void add(Transmitter t) { 596 synchronized(transmitters) { 597 transmitters.add(t); 598 } 599 if (t instanceof BasicTransmitter) { 600 ((BasicTransmitter) t).setTransmitterList(this); 601 } 602 if (Printer.debug) Printer.debug("--added transmitter "+t); 603 } 604 605 private void remove(Transmitter t) { 606 synchronized(transmitters) { 607 int index = transmitters.indexOf(t); 608 if (index >= 0) { 609 transmitters.remove(index); 610 if (Printer.debug) Printer.debug("--removed transmitter "+t); 611 } 612 } 613 } 614 615 private void receiverChanged(BasicTransmitter t, 616 Receiver oldR, 617 Receiver newR) { 618 synchronized(transmitters) { 619 // some optimization 620 if (midiOutReceiver == oldR) { 621 midiOutReceiver = null; 622 } 623 if (newR != null) { 624 if ((newR instanceof MidiOutDevice.MidiOutReceiver) 625 && (midiOutReceiver == null)) { 626 midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR); 627 } 628 } 629 optimizedReceiverCount = 630 ((midiOutReceiver!=null)?1:0); 631 } 632 // more potential for optimization here 633 } 634 635 636 /** closes all transmitters and empties the list */ 637 void close() { 638 synchronized (transmitters) { 639 for(int i = 0; i < transmitters.size(); i++) { 640 transmitters.get(i).close(); 641 } 642 transmitters.clear(); 643 } 644 if (Printer.trace) Printer.trace("TransmitterList.close() succeeded"); 645 } 646 647 648 649 /** 650 * Send this message to all receivers 651 * status = packedMessage & 0xFF 652 * data1 = (packedMessage & 0xFF00) >> 8; 653 * data1 = (packedMessage & 0xFF0000) >> 16; 654 */ 655 void sendMessage(int packedMessage, long timeStamp) { 656 try { 657 synchronized(transmitters) { 658 int size = transmitters.size(); 659 if (optimizedReceiverCount == size) { 660 if (midiOutReceiver != null) { 661 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver"); 662 midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp); 663 } 664 } else { 665 if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers"); 666 for (int i = 0; i < size; i++) { 667 Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver(); 668 if (receiver != null) { 669 if (optimizedReceiverCount > 0) { 670 if (receiver instanceof MidiOutDevice.MidiOutReceiver) { 671 ((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp); 672 } else { 673 receiver.send(new FastShortMessage(packedMessage), timeStamp); 674 } 675 } else { 676 receiver.send(new FastShortMessage(packedMessage), timeStamp); 677 } 678 } 679 } 680 } 681 } 682 } catch (InvalidMidiDataException e) { 683 // this happens when invalid data comes over the wire. Ignore it. 684 } 685 } 686 687 void sendMessage(byte[] data, long timeStamp) { 688 try { 689 synchronized(transmitters) { 690 int size = transmitters.size(); 691 if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers"); 692 for (int i = 0; i < size; i++) { 693 Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver(); 694 if (receiver != null) { 695 //$$fb 2002-04-02: SysexMessages are mutable, so 696 // an application could change the contents of this object, 697 // or try to use the object later. So we can't get around object creation 698 // But the array need not be unique for each FastSysexMessage object, 699 // because it cannot be modified. 700 receiver.send(new FastSysexMessage(data), timeStamp); 701 } 702 } 703 } 704 } catch (InvalidMidiDataException e) { 705 // this happens when invalid data comes over the wire. Ignore it. 706 return; 707 } 708 } 709 710 711 /** 712 * Send this message to all transmitters 713 */ 714 void sendMessage(MidiMessage message, long timeStamp) { 715 if (message instanceof FastShortMessage) { 716 sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp); 717 return; 718 } 719 synchronized(transmitters) { 720 int size = transmitters.size(); 721 if (optimizedReceiverCount == size) { 722 if (midiOutReceiver != null) { 723 if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver"); 724 midiOutReceiver.send(message, timeStamp); 725 } 726 } else { 727 if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers"); 728 for (int i = 0; i < size; i++) { 729 Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver(); 730 if (receiver != null) { 731 //$$fb 2002-04-02: ShortMessages are mutable, so 732 // an application could change the contents of this object, 733 // or try to use the object later. 734 // We violate this spec here, to avoid costly (and gc-intensive) 735 // object creation for potentially hundred of messages per second. 736 // The spec should be changed to allow Immutable MidiMessages 737 // (i.e. throws InvalidStateException or so in setMessage) 738 receiver.send(message, timeStamp); 739 } 740 } 741 } 742 } 743 } 744 745 746 } // TransmitterList 747 748 }