src/share/classes/javax/sound/midi/MidiSystem.java

Print this page




   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 javax.sound.midi;
  27 
  28 import java.io.FileInputStream;
  29 import java.io.File;

  30 import java.io.InputStream;
  31 import java.io.OutputStream;
  32 import java.io.IOException;
  33 
  34 import java.util.ArrayList;
  35 import java.util.HashSet;
  36 import java.util.Iterator;
  37 import java.util.List;

  38 import java.util.Set;
  39 
  40 import java.net.URL;
  41 
  42 import javax.sound.midi.spi.MidiFileWriter;
  43 import javax.sound.midi.spi.MidiFileReader;

  44 import javax.sound.midi.spi.SoundbankReader;
  45 import javax.sound.midi.spi.MidiDeviceProvider;
  46 
  47 import com.sun.media.sound.JDK13Services;
  48 import com.sun.media.sound.ReferenceCountingDevice;
  49 import com.sun.media.sound.AutoConnectSequencer;

  50 import com.sun.media.sound.MidiDeviceReceiverEnvelope;
  51 import com.sun.media.sound.MidiDeviceTransmitterEnvelope;
  52 
  53 
  54 /**
  55  * The <code>MidiSystem</code> class provides access to the installed MIDI
  56  * system resources, including devices such as synthesizers, sequencers, and
  57  * MIDI input and output ports.  A typical simple MIDI application might
  58  * begin by invoking one or more <code>MidiSystem</code> methods to learn
  59  * what devices are installed and to obtain the ones needed in that
  60  * application.
  61  * <p>
  62  * The class also has methods for reading files, streams, and  URLs that
  63  * contain standard MIDI file data or soundbanks.  You can query the
  64  * <code>MidiSystem</code> for the format of a specified MIDI file.
  65  * <p>
  66  * You cannot instantiate a <code>MidiSystem</code>; all the methods are
  67  * static.
  68  *
  69  * <p>Properties can be used to specify default MIDI devices.
  70  * Both system properties and a properties file are considered.
  71  * The <code>sound.properties</code> properties file is read from
  72  * an implementation-specific location (typically it is the <code>lib</code>
  73  * directory in the Java installation directory).
  74  * If a property exists both as a system property and in the
  75  * properties file, the system property takes precedence. If none is
  76  * specified, a suitable default is chosen among the available devices.
  77  * The syntax of the properties file is specified in
  78  * {@link java.util.Properties#load(InputStream) Properties.load}. The
  79  * following table lists the available property keys and which methods
  80  * consider them:
  81  *
  82  * <table border=0>
  83  *  <caption>MIDI System Property Keys</caption>
  84  *  <tr>
  85  *   <th>Property Key</th>
  86  *   <th>Interface</th>
  87  *   <th>Affected Method</th>
  88  *  </tr>
  89  *  <tr>
  90  *   <td><code>javax.sound.midi.Receiver</code></td>
  91  *   <td>{@link Receiver}</td>
  92  *   <td>{@link #getReceiver}</td>
  93  *  </tr>
  94  *  <tr>
  95  *   <td><code>javax.sound.midi.Sequencer</code></td>
  96  *   <td>{@link Sequencer}</td>
  97  *   <td>{@link #getSequencer}</td>
  98  *  </tr>
  99  *  <tr>
 100  *   <td><code>javax.sound.midi.Synthesizer</code></td>
 101  *   <td>{@link Synthesizer}</td>
 102  *   <td>{@link #getSynthesizer}</td>
 103  *  </tr>
 104  *  <tr>
 105  *   <td><code>javax.sound.midi.Transmitter</code></td>
 106  *   <td>{@link Transmitter}</td>
 107  *   <td>{@link #getTransmitter}</td>
 108  *  </tr>
 109  * </table>
 110  *
 111  * The property value consists of the provider class name
 112  * and the device name, separated by the hash mark (&quot;#&quot;).
 113  * The provider class name is the fully-qualified
 114  * name of a concrete {@link javax.sound.midi.spi.MidiDeviceProvider
 115  * MIDI device provider} class. The device name is matched against
 116  * the <code>String</code> returned by the <code>getName</code>
 117  * method of <code>MidiDevice.Info</code>.
 118  * Either the class name, or the device name may be omitted.
 119  * If only the class name is specified, the trailing hash mark
 120  * is optional.
 121  *
 122  * <p>If the provider class is specified, and it can be
 123  * successfully retrieved from the installed providers,
 124  * the list of
 125  * <code>MidiDevice.Info</code> objects is retrieved
 126  * from the provider. Otherwise, or when these devices
 127  * do not provide a subsequent match, the list is retrieved
 128  * from {@link #getMidiDeviceInfo} to contain
 129  * all available <code>MidiDevice.Info</code> objects.
 130  *
 131  * <p>If a device name is specified, the resulting list of
 132  * <code>MidiDevice.Info</code> objects is searched:
 133  * the first one with a matching name, and whose
 134  * <code>MidiDevice</code> implements the
 135  * respective interface, will be returned.
 136  * If no matching <code>MidiDevice.Info</code> object
 137  * is found, or the device name is not specified,
 138  * the first suitable device from the resulting
 139  * list will be returned. For Sequencer and Synthesizer,
 140  * a device is suitable if it implements the respective
 141  * interface; whereas for Receiver and Transmitter, a device is
 142  * suitable if it
 143  * implements neither Sequencer nor Synthesizer and provides
 144  * at least one Receiver or Transmitter, respectively.
 145  *
 146  * For example, the property <code>javax.sound.midi.Receiver</code>
 147  * with a value
 148  * <code>&quot;com.sun.media.sound.MidiProvider#SunMIDI1&quot;</code>
 149  * will have the following consequences when
 150  * <code>getReceiver</code> is called:
 151  * if the class <code>com.sun.media.sound.MidiProvider</code> exists
 152  * in the list of installed MIDI device providers,
 153  * the first <code>Receiver</code> device with name
 154  * <code>&quot;SunMIDI1&quot;</code> will be returned. If it cannot
 155  * be found, the first <code>Receiver</code> from that provider
 156  * will be returned, regardless of name.
 157  * If there is none, the first <code>Receiver</code> with name
 158  * <code>&quot;SunMIDI1&quot;</code> in the list of all devices
 159  * (as returned by <code>getMidiDeviceInfo</code>) will be returned,
 160  * or, if not found, the first <code>Receiver</code> that can
 161  * be found in the list of all devices is returned.
 162  * If that fails, too, a <code>MidiUnavailableException</code>
 163  * is thrown.
 164  *
 165  * @author Kara Kytle
 166  * @author Florian Bomers
 167  * @author Matthias Pfisterer
 168  */
 169 public class MidiSystem {
 170 
 171     /**
 172      * Private no-args constructor for ensuring against instantiation.
 173      */
 174     private MidiSystem() {
 175     }
 176 
 177 
 178     /**
 179      * Obtains an array of information objects representing
 180      * the set of all MIDI devices available on the system.
 181      * A returned information object can then be used to obtain the
 182      * corresponding device object, by invoking
 183      * {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}.
 184      *
 185      * @return an array of <code>MidiDevice.Info</code> objects, one
 186      * for each installed MIDI device.  If no such devices are installed,
 187      * an array of length 0 is returned.
 188      */
 189     public static MidiDevice.Info[] getMidiDeviceInfo() {
 190         List<MidiDevice.Info> allInfos = new ArrayList<>();
 191         List<MidiDeviceProvider> providers = getMidiDeviceProviders();
 192 
 193         for(int i = 0; i < providers.size(); i++) {
 194             MidiDeviceProvider provider = providers.get(i);
 195             MidiDevice.Info[] tmpinfo = provider.getDeviceInfo();
 196             for (int j = 0; j < tmpinfo.length; j++) {
 197                 allInfos.add( tmpinfo[j] );
 198             }
 199         }
 200         MidiDevice.Info[] infosArray = allInfos.toArray(new MidiDevice.Info[0]);
 201         return infosArray;
 202     }
 203 
 204 
 205     /**
 206      * Obtains the requested MIDI device.
 207      *
 208      * @param info a device information object representing the desired device.
 209      * @return the requested device
 210      * @throws MidiUnavailableException if the requested device is not available
 211      * due to resource restrictions
 212      * @throws IllegalArgumentException if the info object does not represent
 213      * a MIDI device installed on the system
 214      * @see #getMidiDeviceInfo
 215      */
 216     public static MidiDevice getMidiDevice(MidiDevice.Info info) throws MidiUnavailableException {
 217         List<MidiDeviceProvider> providers = getMidiDeviceProviders();
 218 
 219         for(int i = 0; i < providers.size(); i++) {
 220             MidiDeviceProvider provider = providers.get(i);
 221             if (provider.isDeviceSupported(info)) {
 222                 MidiDevice device = provider.getDevice(info);
 223                 return device;
 224             }
 225         }
 226         throw new IllegalArgumentException("Requested device not installed: " + info);
 227     }
 228 
 229 
 230     /**
 231      * Obtains a MIDI receiver from an external MIDI port
 232      * or other default device.
 233      * The returned receiver always implements
 234      * the {@code MidiDeviceReceiver} interface.
 235      *
 236      * <p>If the system property
 237      * <code>javax.sound.midi.Receiver</code>
 238      * is defined or it is defined in the file &quot;sound.properties&quot;,
 239      * it is used to identify the device that provides the default receiver.
 240      * For details, refer to the {@link MidiSystem class description}.
 241      *
 242      * If a suitable MIDI port is not available, the Receiver is
 243      * retrieved from an installed synthesizer.
 244      *
 245      * <p>If a native receiver provided by the default device does not implement
 246      * the {@code MidiDeviceReceiver} interface, it will be wrapped in a
 247      * wrapper class that implements the {@code MidiDeviceReceiver} interface.
 248      * The corresponding {@code Receiver} method calls will be forwarded
 249      * to the native receiver.
 250      *
 251      * <p>If this method returns successfully, the {@link
 252      * javax.sound.midi.MidiDevice MidiDevice} the
 253      * <code>Receiver</code> belongs to is opened implicitly, if it is
 254      * not already open. It is possible to close an implicitly opened
 255      * device by calling {@link javax.sound.midi.Receiver#close close}
 256      * on the returned <code>Receiver</code>. All open <code>Receiver</code>
 257      * instances have to be closed in order to release system resources
 258      * hold by the <code>MidiDevice</code>. For a
 259      * detailed description of open/close behaviour see the class
 260      * description of {@link javax.sound.midi.MidiDevice MidiDevice}.
 261      *
 262      *
 263      * @return the default MIDI receiver
 264      * @throws MidiUnavailableException if the default receiver is not
 265      *         available due to resource restrictions,
 266      *         or no device providing receivers is installed in the system
 267      */
 268     public static Receiver getReceiver() throws MidiUnavailableException {
 269         // may throw MidiUnavailableException
 270         MidiDevice device = getDefaultDeviceWrapper(Receiver.class);
 271         Receiver receiver;
 272         if (device instanceof ReferenceCountingDevice) {
 273             receiver = ((ReferenceCountingDevice) device).getReceiverReferenceCounting();
 274         } else {
 275             receiver = device.getReceiver();
 276         }
 277         if (!(receiver instanceof MidiDeviceReceiver)) {
 278             receiver = new MidiDeviceReceiverEnvelope(device, receiver);
 279         }
 280         return receiver;
 281     }
 282 
 283 
 284     /**
 285      * Obtains a MIDI transmitter from an external MIDI port
 286      * or other default source.
 287      * The returned transmitter always implements
 288      * the {@code MidiDeviceTransmitter} interface.
 289      *
 290      * <p>If the system property
 291      * <code>javax.sound.midi.Transmitter</code>
 292      * is defined or it is defined in the file &quot;sound.properties&quot;,
 293      * it is used to identify the device that provides the default transmitter.
 294      * For details, refer to the {@link MidiSystem class description}.
 295      *
 296      * <p>If a native transmitter provided by the default device does not implement
 297      * the {@code MidiDeviceTransmitter} interface, it will be wrapped in a
 298      * wrapper class that implements the {@code MidiDeviceTransmitter} interface.
 299      * The corresponding {@code Transmitter} method calls will be forwarded
 300      * to the native transmitter.
 301      *
 302      * <p>If this method returns successfully, the {@link
 303      * javax.sound.midi.MidiDevice MidiDevice} the
 304      * <code>Transmitter</code> belongs to is opened implicitly, if it
 305      * is not already open. It is possible to close an implicitly
 306      * opened device by calling {@link
 307      * javax.sound.midi.Transmitter#close close} on the returned
 308      * <code>Transmitter</code>. All open <code>Transmitter</code>
 309      * instances have to be closed in order to release system resources
 310      * hold by the <code>MidiDevice</code>. For a detailed description
 311      * of open/close behaviour see the class description of {@link
 312      * javax.sound.midi.MidiDevice MidiDevice}.
 313      *
 314      * @return the default MIDI transmitter
 315      * @throws MidiUnavailableException if the default transmitter is not
 316      *         available due to resource restrictions,
 317      *         or no device providing transmitters is installed in the system
 318      */
 319     public static Transmitter getTransmitter() throws MidiUnavailableException {
 320         // may throw MidiUnavailableException
 321         MidiDevice device = getDefaultDeviceWrapper(Transmitter.class);
 322         Transmitter transmitter;
 323         if (device instanceof ReferenceCountingDevice) {
 324             transmitter = ((ReferenceCountingDevice) device).getTransmitterReferenceCounting();
 325         } else {
 326             transmitter = device.getTransmitter();
 327         }
 328         if (!(transmitter instanceof MidiDeviceTransmitter)) {
 329             transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter);
 330         }
 331         return transmitter;
 332     }
 333 
 334 
 335     /**
 336      * Obtains the default synthesizer.
 337      *
 338      * <p>If the system property
 339      * <code>javax.sound.midi.Synthesizer</code>
 340      * is defined or it is defined in the file &quot;sound.properties&quot;,
 341      * it is used to identify the default synthesizer.
 342      * For details, refer to the {@link MidiSystem class description}.
 343      *
 344      * @return the default synthesizer
 345      * @throws MidiUnavailableException if the synthesizer is not
 346      *         available due to resource restrictions,
 347      *         or no synthesizer is installed in the system
 348      */
 349     public static Synthesizer getSynthesizer() throws MidiUnavailableException {
 350         // may throw MidiUnavailableException
 351         return (Synthesizer) getDefaultDeviceWrapper(Synthesizer.class);
 352     }
 353 
 354 
 355     /**
 356      * Obtains the default <code>Sequencer</code>, connected to
 357      * a default device.
 358      * The returned <code>Sequencer</code> instance is
 359      * connected to the default <code>Synthesizer</code>,
 360      * as returned by {@link #getSynthesizer}.
 361      * If there is no <code>Synthesizer</code>
 362      * available, or the default <code>Synthesizer</code>
 363      * cannot be opened, the <code>sequencer</code> is connected
 364      * to the default <code>Receiver</code>, as returned
 365      * by {@link #getReceiver}.
 366      * The connection is made by retrieving a <code>Transmitter</code>
 367      * instance from the <code>Sequencer</code> and setting its
 368      * <code>Receiver</code>.
 369      * Closing and re-opening the sequencer will restore the
 370      * connection to the default device.
 371      *
 372      * <p>This method is equivalent to calling
 373      * <code>getSequencer(true)</code>.
 374      *
 375      * <p>If the system property
 376      * <code>javax.sound.midi.Sequencer</code>
 377      * is defined or it is defined in the file &quot;sound.properties&quot;,
 378      * it is used to identify the default sequencer.
 379      * For details, refer to the {@link MidiSystem class description}.
 380      *
 381      * @return the default sequencer, connected to a default Receiver
 382      * @throws MidiUnavailableException if the sequencer is not
 383      *         available due to resource restrictions,
 384      *         or there is no <code>Receiver</code> available by any
 385      *         installed <code>MidiDevice</code>,
 386      *         or no sequencer is installed in the system.
 387      * @see #getSequencer(boolean)
 388      * @see #getSynthesizer
 389      * @see #getReceiver
 390      */
 391     public static Sequencer getSequencer() throws MidiUnavailableException {
 392         return getSequencer(true);
 393     }
 394 
 395 
 396 
 397     /**
 398      * Obtains the default <code>Sequencer</code>, optionally
 399      * connected to a default device.
 400      *
 401      * <p>If <code>connected</code> is true, the returned
 402      * <code>Sequencer</code> instance is
 403      * connected to the default <code>Synthesizer</code>,
 404      * as returned by {@link #getSynthesizer}.
 405      * If there is no <code>Synthesizer</code>
 406      * available, or the default <code>Synthesizer</code>
 407      * cannot be opened, the <code>sequencer</code> is connected
 408      * to the default <code>Receiver</code>, as returned
 409      * by {@link #getReceiver}.
 410      * The connection is made by retrieving a <code>Transmitter</code>
 411      * instance from the <code>Sequencer</code> and setting its
 412      * <code>Receiver</code>.
 413      * Closing and re-opening the sequencer will restore the
 414      * connection to the default device.










 415      *
 416      * <p>If <code>connected</code> is false, the returned
 417      * <code>Sequencer</code> instance is not connected, it
 418      * has no open <code>Transmitters</code>. In order to
 419      * play the sequencer on a MIDI device, or a <code>Synthesizer</code>,
 420      * it is necessary to get a <code>Transmitter</code> and set its
 421      * <code>Receiver</code>.
 422      *
 423      * <p>If the system property
 424      * <code>javax.sound.midi.Sequencer</code>
 425      * is defined or it is defined in the file "sound.properties",
 426      * it is used to identify the default sequencer.
 427      * For details, refer to the {@link MidiSystem class description}.
 428      *
 429      * @param connected whether or not the returned {@code Sequencer}
 430      * is connected to the default {@code Synthesizer}
 431      * @return the default sequencer
 432      * @throws MidiUnavailableException if the sequencer is not
 433      *         available due to resource restrictions,
 434      *         or no sequencer is installed in the system,
 435      *         or if <code>connected</code> is true, and there is
 436      *         no <code>Receiver</code> available by any installed
 437      *         <code>MidiDevice</code>
 438      * @see #getSynthesizer
 439      * @see #getReceiver
 440      * @since 1.5
 441      */
 442     public static Sequencer getSequencer(boolean connected)
 443         throws MidiUnavailableException {
 444         Sequencer seq = (Sequencer) getDefaultDeviceWrapper(Sequencer.class);
 445 
 446         if (connected) {
 447             // IMPORTANT: this code needs to be synch'ed with
 448             //            all AutoConnectSequencer instances,
 449             //            (e.g. RealTimeSequencer) because the
 450             //            same algorithm for synth retrieval
 451             //            needs to be used!
 452 
 453             Receiver rec = null;
 454             MidiUnavailableException mue = null;
 455 
 456             // first try to connect to the default synthesizer
 457             try {


 484                     if (e instanceof MidiUnavailableException) {
 485                         mue = (MidiUnavailableException) e;
 486                     }
 487                 }
 488             }
 489             if (rec != null) {
 490                 seq.getTransmitter().setReceiver(rec);
 491                 if (seq instanceof AutoConnectSequencer) {
 492                     ((AutoConnectSequencer) seq).setAutoConnect(rec);
 493                 }
 494             } else {
 495                 if (mue != null) {
 496                     throw mue;
 497                 }
 498                 throw new MidiUnavailableException("no receiver available");
 499             }
 500         }
 501         return seq;
 502     }
 503 
 504 
 505 
 506 
 507     /**
 508      * Constructs a MIDI sound bank by reading it from the specified stream.
 509      * The stream must point to
 510      * a valid MIDI soundbank file.  In general, MIDI soundbank providers may
 511      * need to read some data from the stream before determining whether they
 512      * support it.  These parsers must
 513      * be able to mark the stream, read enough data to determine whether they
 514      * support the stream, and, if not, reset the stream's read pointer to
 515      * its original position.  If the input stream does not support this,
 516      * this method may fail with an IOException.
 517      * @param stream the source of the sound bank data.
 518      * @return the sound bank
 519      * @throws InvalidMidiDataException if the stream does not point to
 520      * valid MIDI soundbank data recognized by the system
 521      * @throws IOException if an I/O error occurred when loading the soundbank
 522      * @see InputStream#markSupported
 523      * @see InputStream#mark
 524      */
 525     public static Soundbank getSoundbank(InputStream stream)
 526         throws InvalidMidiDataException, IOException {
 527 
 528         SoundbankReader sp = null;
 529         Soundbank s = null;
 530 
 531         List<SoundbankReader> providers = getSoundbankReaders();
 532 
 533         for(int i = 0; i < providers.size(); i++) {
 534             sp = providers.get(i);
 535             s = sp.getSoundbank(stream);
 536 
 537             if( s!= null) {
 538                 return s;
 539             }
 540         }
 541         throw new InvalidMidiDataException("cannot get soundbank from stream");
 542 
 543     }
 544 
 545 
 546     /**
 547      * Constructs a <code>Soundbank</code> by reading it from the specified URL.
 548      * The URL must point to a valid MIDI soundbank file.
 549      *
 550      * @param url the source of the sound bank data
 551      * @return the sound bank
 552      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 553      * soundbank data recognized by the system
 554      * @throws IOException if an I/O error occurred when loading the soundbank
 555      */
 556     public static Soundbank getSoundbank(URL url)
 557         throws InvalidMidiDataException, IOException {
 558 
 559         SoundbankReader sp = null;
 560         Soundbank s = null;
 561 
 562         List<SoundbankReader> providers = getSoundbankReaders();
 563 
 564         for(int i = 0; i < providers.size(); i++) {
 565             sp = providers.get(i);
 566             s = sp.getSoundbank(url);
 567 
 568             if( s!= null) {
 569                 return s;
 570             }
 571         }
 572         throw new InvalidMidiDataException("cannot get soundbank from stream");
 573 
 574     }
 575 
 576 
 577     /**
 578      * Constructs a <code>Soundbank</code> by reading it from the specified
 579      * <code>File</code>.
 580      * The <code>File</code> must point to a valid MIDI soundbank file.
 581      *
 582      * @param file the source of the sound bank data
 583      * @return the sound bank
 584      * @throws InvalidMidiDataException if the <code>File</code> does not
 585      * point to valid MIDI soundbank data recognized by the system
 586      * @throws IOException if an I/O error occurred when loading the soundbank
 587      */
 588     public static Soundbank getSoundbank(File file)
 589         throws InvalidMidiDataException, IOException {
 590 
 591         SoundbankReader sp = null;
 592         Soundbank s = null;
 593 
 594         List<SoundbankReader> providers = getSoundbankReaders();
 595 
 596         for(int i = 0; i < providers.size(); i++) {
 597             sp = providers.get(i);
 598             s = sp.getSoundbank(file);
 599 
 600             if( s!= null) {
 601                 return s;
 602             }
 603         }
 604         throw new InvalidMidiDataException("cannot get soundbank from stream");
 605     }
 606 
 607 
 608 
 609     /**
 610      * Obtains the MIDI file format of the data in the specified input stream.
 611      * The stream must point to valid MIDI file data for a file type recognized
 612      * by the system.
 613      * <p>
 614      * This method and/or the code it invokes may need to read some data from
 615      * the stream to determine whether its data format is supported.  The
 616      * implementation may therefore
 617      * need to mark the stream, read enough data to determine whether it is in
 618      * a supported format, and reset the stream's read pointer to its original
 619      * position.  If the input stream does not permit this set of operations,
 620      * this method may fail with an <code>IOException</code>.
 621      * <p>
 622      * This operation can only succeed for files of a type which can be parsed
 623      * by an installed file reader.  It may fail with an InvalidMidiDataException
 624      * even for valid files if no compatible file reader is installed.  It
 625      * will also fail with an InvalidMidiDataException if a compatible file reader
 626      * is installed, but encounters errors while determining the file format.
 627      *
 628      * @param stream the input stream from which file format information
 629      * should be extracted
 630      * @return an <code>MidiFileFormat</code> object describing the MIDI file
 631      * format
 632      * @throws InvalidMidiDataException if the stream does not point to valid
 633      * MIDI file data recognized by the system
 634      * @throws IOException if an I/O exception occurs while accessing the
 635      * stream
 636      * @see #getMidiFileFormat(URL)
 637      * @see #getMidiFileFormat(File)
 638      * @see InputStream#markSupported
 639      * @see InputStream#mark
 640      */
 641     public static MidiFileFormat getMidiFileFormat(InputStream stream)
 642         throws InvalidMidiDataException, IOException {
 643 
 644         List<MidiFileReader> providers = getMidiFileReaders();
 645         MidiFileFormat format = null;
 646 
 647         for(int i = 0; i < providers.size(); i++) {
 648             MidiFileReader reader =  providers.get(i);
 649             try {
 650                 format = reader.getMidiFileFormat( stream ); // throws IOException
 651                 break;
 652             } catch (InvalidMidiDataException e) {
 653                 continue;
 654             }
 655         }
 656 
 657         if( format==null ) {
 658             throw new InvalidMidiDataException("input stream is not a supported file type");
 659         } else {
 660             return format;
 661         }
 662     }
 663 
 664 
 665     /**
 666      * Obtains the MIDI file format of the data in the specified URL.  The URL
 667      * must point to valid MIDI file data for a file type recognized
 668      * by the system.
 669      * <p>
 670      * This operation can only succeed for files of a type which can be parsed
 671      * by an installed file reader.  It may fail with an InvalidMidiDataException
 672      * even for valid files if no compatible file reader is installed.  It
 673      * will also fail with an InvalidMidiDataException if a compatible file reader
 674      * is installed, but encounters errors while determining the file format.

 675      *
 676      * @param url the URL from which file format information should be
 677      * extracted
 678      * @return a <code>MidiFileFormat</code> object describing the MIDI file
 679      * format
 680      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 681      * file data recognized by the system
 682      * @throws IOException if an I/O exception occurs while accessing the URL
 683      *
 684      * @see #getMidiFileFormat(InputStream)
 685      * @see #getMidiFileFormat(File)
 686      */
 687     public static MidiFileFormat getMidiFileFormat(URL url)
 688         throws InvalidMidiDataException, IOException {
 689 
 690         List<MidiFileReader> providers = getMidiFileReaders();
 691         MidiFileFormat format = null;
 692 
 693         for(int i = 0; i < providers.size(); i++) {
 694             MidiFileReader reader = providers.get(i);
 695             try {
 696                 format = reader.getMidiFileFormat( url ); // throws IOException
 697                 break;
 698             } catch (InvalidMidiDataException e) {
 699                 continue;
 700             }
 701         }
 702 
 703         if( format==null ) {
 704             throw new InvalidMidiDataException("url is not a supported file type");
 705         } else {
 706             return format;
 707         }
 708     }
 709 
 710 
 711     /**
 712      * Obtains the MIDI file format of the specified <code>File</code>.  The
 713      * <code>File</code> must point to valid MIDI file data for a file type
 714      * recognized by the system.
 715      * <p>
 716      * This operation can only succeed for files of a type which can be parsed
 717      * by an installed file reader.  It may fail with an InvalidMidiDataException
 718      * even for valid files if no compatible file reader is installed.  It
 719      * will also fail with an InvalidMidiDataException if a compatible file reader
 720      * is installed, but encounters errors while determining the file format.
 721      *
 722      * @param file the <code>File</code> from which file format information
 723      * should be extracted
 724      * @return a <code>MidiFileFormat</code> object describing the MIDI file
 725      * format
 726      * @throws InvalidMidiDataException if the <code>File</code> does not point
 727      *  to valid MIDI file data recognized by the system
 728      * @throws IOException if an I/O exception occurs while accessing the file
 729      *
 730      * @see #getMidiFileFormat(InputStream)
 731      * @see #getMidiFileFormat(URL)
 732      */
 733     public static MidiFileFormat getMidiFileFormat(File file)
 734         throws InvalidMidiDataException, IOException {
 735 
 736         List<MidiFileReader> providers = getMidiFileReaders();
 737         MidiFileFormat format = null;
 738 
 739         for(int i = 0; i < providers.size(); i++) {
 740             MidiFileReader reader = providers.get(i);
 741             try {
 742                 format = reader.getMidiFileFormat( file ); // throws IOException
 743                 break;
 744             } catch (InvalidMidiDataException e) {
 745                 continue;
 746             }
 747         }
 748 
 749         if( format==null ) {
 750             throw new InvalidMidiDataException("file is not a supported file type");
 751         } else {
 752             return format;
 753         }
 754     }
 755 
 756 
 757     /**
 758      * Obtains a MIDI sequence from the specified input stream.  The stream must
 759      * point to valid MIDI file data for a file type recognized
 760      * by the system.
 761      * <p>
 762      * This method and/or the code it invokes may need to read some data
 763      * from the stream to determine whether
 764      * its data format is supported.  The implementation may therefore
 765      * need to mark the stream, read enough data to determine whether it is in
 766      * a supported format, and reset the stream's read pointer to its original
 767      * position.  If the input stream does not permit this set of operations,
 768      * this method may fail with an <code>IOException</code>.
 769      * <p>
 770      * This operation can only succeed for files of a type which can be parsed
 771      * by an installed file reader.  It may fail with an InvalidMidiDataException
 772      * even for valid files if no compatible file reader is installed.  It
 773      * will also fail with an InvalidMidiDataException if a compatible file reader
 774      * is installed, but encounters errors while constructing the <code>Sequence</code>

 775      * object from the file data.
 776      *
 777      * @param stream the input stream from which the <code>Sequence</code>
 778      * should be constructed
 779      * @return a <code>Sequence</code> object based on the MIDI file data
 780      * contained in the input stream
 781      * @throws InvalidMidiDataException if the stream does not point to
 782      * valid MIDI file data recognized by the system
 783      * @throws IOException if an I/O exception occurs while accessing the
 784      * stream
 785      * @see InputStream#markSupported
 786      * @see InputStream#mark
 787      */
 788     public static Sequence getSequence(InputStream stream)
 789         throws InvalidMidiDataException, IOException {
 790 
 791         List<MidiFileReader> providers = getMidiFileReaders();
 792         Sequence sequence = null;
 793 
 794         for(int i = 0; i < providers.size(); i++) {
 795             MidiFileReader reader = providers.get(i);
 796             try {
 797                 sequence = reader.getSequence( stream ); // throws IOException
 798                 break;
 799             } catch (InvalidMidiDataException e) {
 800                 continue;
 801             }
 802         }
 803 
 804         if( sequence==null ) {
 805             throw new InvalidMidiDataException("could not get sequence from input stream");
 806         } else {
 807             return sequence;
 808         }
 809     }
 810 
 811 
 812     /**
 813      * Obtains a MIDI sequence from the specified URL.  The URL must
 814      * point to valid MIDI file data for a file type recognized
 815      * by the system.
 816      * <p>
 817      * This operation can only succeed for files of a type which can be parsed
 818      * by an installed file reader.  It may fail with an InvalidMidiDataException
 819      * even for valid files if no compatible file reader is installed.  It
 820      * will also fail with an InvalidMidiDataException if a compatible file reader
 821      * is installed, but encounters errors while constructing the <code>Sequence</code>

 822      * object from the file data.
 823      *
 824      * @param url the URL from which the <code>Sequence</code> should be
 825      * constructed
 826      * @return a <code>Sequence</code> object based on the MIDI file data
 827      * pointed to by the URL
 828      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 829      * file data recognized by the system
 830      * @throws IOException if an I/O exception occurs while accessing the URL
 831      */
 832     public static Sequence getSequence(URL url)
 833         throws InvalidMidiDataException, IOException {
 834 
 835         List<MidiFileReader> providers = getMidiFileReaders();
 836         Sequence sequence = null;
 837 
 838         for(int i = 0; i < providers.size(); i++) {
 839             MidiFileReader reader = providers.get(i);
 840             try {
 841                 sequence = reader.getSequence( url ); // throws IOException
 842                 break;
 843             } catch (InvalidMidiDataException e) {
 844                 continue;
 845             }
 846         }
 847 
 848         if( sequence==null ) {
 849             throw new InvalidMidiDataException("could not get sequence from URL");
 850         } else {
 851             return sequence;
 852         }
 853     }
 854 
 855 
 856     /**
 857      * Obtains a MIDI sequence from the specified <code>File</code>.
 858      * The <code>File</code> must point to valid MIDI file data
 859      * for a file type recognized by the system.
 860      * <p>
 861      * This operation can only succeed for files of a type which can be parsed
 862      * by an installed file reader.  It may fail with an InvalidMidiDataException
 863      * even for valid files if no compatible file reader is installed.  It
 864      * will also fail with an InvalidMidiDataException if a compatible file reader
 865      * is installed, but encounters errors while constructing the <code>Sequence</code>

 866      * object from the file data.
 867      *
 868      * @param file the <code>File</code> from which the <code>Sequence</code>
 869      * should be constructed
 870      * @return a <code>Sequence</code> object based on the MIDI file data
 871      * pointed to by the File
 872      * @throws InvalidMidiDataException if the File does not point to valid MIDI
 873      * file data recognized by the system
 874      * @throws IOException if an I/O exception occurs
 875      */
 876     public static Sequence getSequence(File file)
 877         throws InvalidMidiDataException, IOException {
 878 
 879         List<MidiFileReader> providers = getMidiFileReaders();
 880         Sequence sequence = null;
 881 
 882         for(int i = 0; i < providers.size(); i++) {
 883             MidiFileReader reader = providers.get(i);
 884             try {
 885                 sequence = reader.getSequence( file ); // throws IOException
 886                 break;
 887             } catch (InvalidMidiDataException e) {
 888                 continue;
 889             }
 890         }
 891 
 892         if( sequence==null ) {
 893             throw new InvalidMidiDataException("could not get sequence from file");
 894         } else {
 895             return sequence;
 896         }
 897     }
 898 
 899 
 900     /**
 901      * Obtains the set of MIDI file types for which file writing support is
 902      * provided by the system.
 903      * @return array of unique file types.  If no file types are supported,
 904      * an array of length 0 is returned.

 905      */
 906     public static int[] getMidiFileTypes() {
 907 
 908         List<MidiFileWriter> providers = getMidiFileWriters();
 909         Set<Integer> allTypes = new HashSet<>();
 910 
 911         // gather from all the providers
 912 
 913         for (int i = 0; i < providers.size(); i++ ) {
 914             MidiFileWriter writer = providers.get(i);
 915             int[] types = writer.getMidiFileTypes();
 916             for (int j = 0; j < types.length; j++ ) {
 917                 allTypes.add(types[j]);
 918             }
 919         }
 920         int resultTypes[] = new int[allTypes.size()];
 921         int index = 0;
 922         Iterator<Integer> iterator = allTypes.iterator();
 923         while (iterator.hasNext()) {
 924             Integer integer = iterator.next();
 925             resultTypes[index++] = integer.intValue();
 926         }
 927         return resultTypes;
 928     }
 929 
 930 
 931     /**
 932      * Indicates whether file writing support for the specified MIDI file type
 933      * is provided by the system.

 934      * @param fileType the file type for which write capabilities are queried
 935      * @return <code>true</code> if the file type is supported,
 936      * otherwise <code>false</code>
 937      */
 938     public static boolean isFileTypeSupported(int fileType) {
 939 
 940         List<MidiFileWriter> providers = getMidiFileWriters();
 941 
 942         for (int i = 0; i < providers.size(); i++ ) {
 943             MidiFileWriter writer = providers.get(i);
 944             if( writer.isFileTypeSupported(fileType)) {
 945                 return true;
 946             }
 947         }
 948         return false;
 949     }
 950 
 951 
 952     /**
 953      * Obtains the set of MIDI file types that the system can write from the
 954      * sequence specified.
 955      * @param sequence the sequence for which MIDI file type support
 956      * is queried
 957      * @return the set of unique supported file types.  If no file types are supported,
 958      * returns an array of length 0.
 959      */
 960     public static int[] getMidiFileTypes(Sequence sequence) {
 961 
 962         List<MidiFileWriter> providers = getMidiFileWriters();
 963         Set<Integer> allTypes = new HashSet<>();
 964 
 965         // gather from all the providers
 966 
 967         for (int i = 0; i < providers.size(); i++ ) {
 968             MidiFileWriter writer = providers.get(i);
 969             int[] types = writer.getMidiFileTypes(sequence);
 970             for (int j = 0; j < types.length; j++ ) {
 971                 allTypes.add(types[j]);
 972             }
 973         }
 974         int resultTypes[] = new int[allTypes.size()];
 975         int index = 0;
 976         Iterator<Integer> iterator = allTypes.iterator();
 977         while (iterator.hasNext()) {
 978             Integer integer = iterator.next();
 979             resultTypes[index++] = integer.intValue();
 980         }
 981         return resultTypes;
 982     }
 983 
 984 
 985     /**
 986      * Indicates whether a MIDI file of the file type specified can be written
 987      * from the sequence indicated.
 988      * @param fileType the file type for which write capabilities
 989      * are queried
 990      * @param sequence the sequence for which file writing support is queried
 991      * @return <code>true</code> if the file type is supported for this
 992      * sequence, otherwise <code>false</code>
 993      */
 994     public static boolean isFileTypeSupported(int fileType, Sequence sequence) {
 995 
 996         List<MidiFileWriter> providers = getMidiFileWriters();
 997 
 998         for (int i = 0; i < providers.size(); i++ ) {
 999             MidiFileWriter writer = providers.get(i);
1000             if( writer.isFileTypeSupported(fileType,sequence)) {
1001                 return true;
1002             }
1003         }
1004         return false;
1005     }
1006 
1007 
1008     /**
1009      * Writes a stream of bytes representing a file of the MIDI file type
1010      * indicated to the output stream provided.

1011      * @param in sequence containing MIDI data to be written to the file
1012      * @param fileType the file type of the file to be written to the output stream

1013      * @param out stream to which the file data should be written
1014      * @return the number of bytes written to the output stream
1015      * @throws IOException if an I/O exception occurs
1016      * @throws IllegalArgumentException if the file format is not supported by
1017      * the system
1018      * @see #isFileTypeSupported(int, Sequence)
1019      * @see     #getMidiFileTypes(Sequence)
1020      */
1021     public static int write(Sequence in, int fileType, OutputStream out) throws IOException {
1022 
1023         List<MidiFileWriter> providers = getMidiFileWriters();
1024         //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
1025         int bytesWritten = -2;
1026 
1027         for (int i = 0; i < providers.size(); i++ ) {
1028             MidiFileWriter writer = providers.get(i);
1029             if( writer.isFileTypeSupported( fileType, in ) ) {
1030 
1031                 bytesWritten = writer.write(in, fileType, out);
1032                 break;
1033             }
1034         }
1035         if (bytesWritten == -2) {
1036             throw new IllegalArgumentException("MIDI file type is not supported");
1037         }
1038         return bytesWritten;
1039     }
1040 
1041 
1042     /**
1043      * Writes a stream of bytes representing a file of the MIDI file type
1044      * indicated to the external file provided.

1045      * @param in sequence containing MIDI data to be written to the file
1046      * @param type the file type of the file to be written to the output stream
1047      * @param out external file to which the file data should be written
1048      * @return the number of bytes written to the file
1049      * @throws IOException if an I/O exception occurs
1050      * @throws IllegalArgumentException if the file type is not supported by
1051      * the system
1052      * @see #isFileTypeSupported(int, Sequence)
1053      * @see     #getMidiFileTypes(Sequence)
1054      */
1055     public static int write(Sequence in, int type, File out) throws IOException {
1056 
1057         List<MidiFileWriter> providers = getMidiFileWriters();
1058         //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
1059         int bytesWritten = -2;
1060 
1061         for (int i = 0; i < providers.size(); i++ ) {
1062             MidiFileWriter writer = providers.get(i);
1063             if( writer.isFileTypeSupported( type, in ) ) {
1064 
1065                 bytesWritten = writer.write(in, type, out);
1066                 break;
1067             }
1068         }
1069         if (bytesWritten == -2) {
1070             throw new IllegalArgumentException("MIDI file type is not supported");
1071         }
1072         return bytesWritten;
1073     }
1074 
1075 
1076 
1077     // HELPER METHODS
1078     @SuppressWarnings("unchecked")
1079     private static List<MidiDeviceProvider> getMidiDeviceProviders() {
1080         return (List<MidiDeviceProvider>) getProviders(MidiDeviceProvider.class);
1081     }
1082 
1083     @SuppressWarnings("unchecked")
1084     private static List<SoundbankReader> getSoundbankReaders() {
1085         return (List<SoundbankReader>) getProviders(SoundbankReader.class);
1086     }
1087 
1088     @SuppressWarnings("unchecked")
1089     private static List<MidiFileWriter> getMidiFileWriters() {
1090         return (List<MidiFileWriter>) getProviders(MidiFileWriter.class);
1091     }
1092 
1093     @SuppressWarnings("unchecked")
1094     private static List<MidiFileReader> getMidiFileReaders() {
1095         return (List<MidiFileReader>) getProviders(MidiFileReader.class);
1096     }
1097 
1098 
1099     /** Attempts to locate and return a default MidiDevice of the specified
1100      * type.
1101      *
1102      * This method wraps {@link #getDefaultDevice}. It catches the
1103      * <code>IllegalArgumentException</code> thrown by
1104      * <code>getDefaultDevice</code> and instead throws a
1105      * <code>MidiUnavailableException</code>, with the catched
1106      * exception chained.
1107      *
1108      * @param deviceClass The requested device type, one of Synthesizer.class,
1109      * Sequencer.class, Receiver.class or Transmitter.class.
1110      * @throws  MidiUnavalableException on failure.
1111      */
1112     private static MidiDevice getDefaultDeviceWrapper(Class<?> deviceClass)
1113         throws MidiUnavailableException{
1114         try {
1115             return getDefaultDevice(deviceClass);
1116         } catch (IllegalArgumentException iae) {
1117             MidiUnavailableException mae = new MidiUnavailableException();
1118             mae.initCause(iae);
1119             throw mae;
1120         }
1121     }
1122 
1123 
1124     /** Attempts to locate and return a default MidiDevice of the specified
1125      * type.
1126      *
1127      * @param deviceClass The requested device type, one of Synthesizer.class,
1128      * Sequencer.class, Receiver.class or Transmitter.class.
1129      * @throws  IllegalArgumentException on failure.
1130      */
1131     private static MidiDevice getDefaultDevice(Class<?> deviceClass) {
1132         List<MidiDeviceProvider> providers = getMidiDeviceProviders();
1133         String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass);
1134         String instanceName = JDK13Services.getDefaultInstanceName(deviceClass);
1135         MidiDevice device;
1136 
1137         if (providerClassName != null) {
1138             MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers);
1139             if (defaultProvider != null) {
1140                 if (instanceName != null) {
1141                     device = getNamedDevice(instanceName, defaultProvider, deviceClass);
1142                     if (device != null) {
1143                         return device;
1144                     }
1145                 }
1146                 device = getFirstDevice(defaultProvider, deviceClass);
1147                 if (device != null) {
1148                     return device;
1149                 }


1152 
1153         /* Provider class not specified or cannot be found, or
1154            provider class specified, and no appropriate device available or
1155            provider class and instance specified and instance cannot be found or is not appropriate */
1156         if (instanceName != null) {
1157             device = getNamedDevice(instanceName, providers, deviceClass);
1158             if (device != null) {
1159                 return device;
1160             }
1161         }
1162 
1163         /* No default are specified, or if something is specified, everything
1164            failed. */
1165         device = getFirstDevice(providers, deviceClass);
1166         if (device != null) {
1167             return device;
1168         }
1169         throw new IllegalArgumentException("Requested device not installed");
1170     }
1171 
1172 
1173 
1174     /** Return a MidiDeviceProcider of a given class from the list of
1175         MidiDeviceProviders.
1176 
1177         @param providerClassName The class name of the provider to be returned.
1178         @param provider The list of MidiDeviceProviders that is searched.
1179         @return A MidiDeviceProvider of the requested class, or null if none
1180         is found.
1181     */
1182     private static MidiDeviceProvider getNamedProvider(String providerClassName,
1183                                                        List<MidiDeviceProvider> providers) {
1184         for(int i = 0; i < providers.size(); i++) {
1185             MidiDeviceProvider provider = providers.get(i);
1186             if (provider.getClass().getName().equals(providerClassName)) {
1187                 return provider;
1188             }
1189         }
1190         return null;
1191     }
1192 
1193 
1194     /** Return a MidiDevice with a given name from a given MidiDeviceProvider.
1195         @param deviceName The name of the MidiDevice to be returned.
1196         @param provider The MidiDeviceProvider to check for MidiDevices.
1197         @param deviceClass The requested device type, one of Synthesizer.class,
1198         Sequencer.class, Receiver.class or Transmitter.class.
1199 
1200         @return A MidiDevice matching the requirements, or null if none is found.
1201     */
1202     private static MidiDevice getNamedDevice(String deviceName,
1203                                              MidiDeviceProvider provider,
1204                                              Class<?> deviceClass) {
1205         MidiDevice device;
1206         // try to get MIDI port
1207         device = getNamedDevice(deviceName, provider, deviceClass,
1208                                  false, false);
1209         if (device != null) {
1210             return device;
1211         }
1212 
1213         if (deviceClass == Receiver.class) {
1214             // try to get Synthesizer
1215             device = getNamedDevice(deviceName, provider, deviceClass,
1216                                      true, false);
1217             if (device != null) {
1218                 return device;
1219             }
1220         }
1221 
1222         return null;
1223     }
1224 
1225 
1226     /** Return a MidiDevice with a given name from a given MidiDeviceProvider.
1227       @param deviceName The name of the MidiDevice to be returned.
1228       @param provider The MidiDeviceProvider to check for MidiDevices.
1229       @param deviceClass The requested device type, one of Synthesizer.class,
1230       Sequencer.class, Receiver.class or Transmitter.class.
1231 
1232       @return A MidiDevice matching the requirements, or null if none is found.
1233      */
1234     private static MidiDevice getNamedDevice(String deviceName,
1235                                              MidiDeviceProvider provider,
1236                                              Class<?> deviceClass,
1237                                              boolean allowSynthesizer,
1238                                              boolean allowSequencer) {
1239         MidiDevice.Info[] infos = provider.getDeviceInfo();
1240         for (int i = 0; i < infos.length; i++) {
1241             if (infos[i].getName().equals(deviceName)) {
1242                 MidiDevice device = provider.getDevice(infos[i]);
1243                 if (isAppropriateDevice(device, deviceClass,
1244                                         allowSynthesizer, allowSequencer)) {
1245                     return device;
1246                 }
1247             }
1248         }
1249         return null;
1250     }
1251 
1252 
1253     /** Return a MidiDevice with a given name from a list of
1254         MidiDeviceProviders.
1255         @param deviceName The name of the MidiDevice to be returned.
1256         @param providers The List of MidiDeviceProviders to check for
1257         MidiDevices.
1258         @param deviceClass The requested device type, one of Synthesizer.class,
1259         Sequencer.class, Receiver.class or Transmitter.class.
1260         @return A Mixer matching the requirements, or null if none is found.
1261     */
1262     private static MidiDevice getNamedDevice(String deviceName,
1263                                              List<MidiDeviceProvider> providers,
1264                                              Class<?> deviceClass) {
1265         MidiDevice device;
1266         // try to get MIDI port
1267         device = getNamedDevice(deviceName, providers, deviceClass,
1268                                  false, false);
1269         if (device != null) {
1270             return device;
1271         }
1272 
1273         if (deviceClass == Receiver.class) {
1274             // try to get Synthesizer
1275             device = getNamedDevice(deviceName, providers, deviceClass,
1276                                      true, false);
1277             if (device != null) {
1278                 return device;
1279             }
1280         }
1281 
1282         return null;
1283     }
1284 
1285 
1286     /** Return a MidiDevice with a given name from a list of
1287         MidiDeviceProviders.
1288         @param deviceName The name of the MidiDevice to be returned.
1289         @param providers The List of MidiDeviceProviders to check for
1290         MidiDevices.
1291         @param deviceClass The requested device type, one of Synthesizer.class,
1292         Sequencer.class, Receiver.class or Transmitter.class.
1293         @return A Mixer matching the requirements, or null if none is found.
1294      */
1295     private static MidiDevice getNamedDevice(String deviceName,
1296                                              List<MidiDeviceProvider> providers,
1297                                              Class<?> deviceClass,
1298                                              boolean allowSynthesizer,
1299                                              boolean allowSequencer) {
1300         for(int i = 0; i < providers.size(); i++) {
1301             MidiDeviceProvider provider = providers.get(i);
1302             MidiDevice device = getNamedDevice(deviceName, provider,
1303                                                deviceClass,
1304                                                allowSynthesizer,
1305                                                allowSequencer);
1306             if (device != null) {
1307                 return device;
1308             }
1309         }
1310         return null;
1311     }
1312 
1313 
1314     /** From a given MidiDeviceProvider, return the first appropriate device.
1315         @param provider The MidiDeviceProvider to check for MidiDevices.
1316         @param deviceClass The requested device type, one of Synthesizer.class,
1317         Sequencer.class, Receiver.class or Transmitter.class.
1318         @return A MidiDevice is considered appropriate, or null if no
1319         appropriate device is found.

1320     */
1321     private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
1322                                              Class<?> deviceClass) {
1323         MidiDevice device;
1324         // try to get MIDI port
1325         device = getFirstDevice(provider, deviceClass,
1326                                 false, false);
1327         if (device != null) {
1328             return device;
1329         }
1330 
1331         if (deviceClass == Receiver.class) {
1332             // try to get Synthesizer
1333             device = getFirstDevice(provider, deviceClass,
1334                                     true, false);
1335             if (device != null) {
1336                 return device;
1337             }
1338         }
1339 
1340         return null;
1341     }
1342 
1343 
1344     /** From a given MidiDeviceProvider, return the first appropriate device.
1345         @param provider The MidiDeviceProvider to check for MidiDevices.
1346         @param deviceClass The requested device type, one of Synthesizer.class,
1347         Sequencer.class, Receiver.class or Transmitter.class.
1348         @return A MidiDevice is considered appropriate, or null if no
1349         appropriate device is found.

1350      */
1351     private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
1352                                              Class<?> deviceClass,
1353                                              boolean allowSynthesizer,
1354                                              boolean allowSequencer) {
1355         MidiDevice.Info[] infos = provider.getDeviceInfo();
1356         for (int j = 0; j < infos.length; j++) {
1357             MidiDevice device = provider.getDevice(infos[j]);
1358             if (isAppropriateDevice(device, deviceClass,
1359                                     allowSynthesizer, allowSequencer)) {
1360                 return device;
1361             }
1362         }
1363         return null;
1364     }
1365 
1366 
1367     /** From a List of MidiDeviceProviders, return the first appropriate
1368         MidiDevice.
1369         @param providers The List of MidiDeviceProviders to search.
1370         @param deviceClass The requested device type, one of Synthesizer.class,
1371         Sequencer.class, Receiver.class or Transmitter.class.
1372         @return A MidiDevice that is considered appropriate, or null
1373         if none is found.

1374     */
1375     private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,
1376                                              Class<?> deviceClass) {
1377         MidiDevice device;
1378         // try to get MIDI port
1379         device = getFirstDevice(providers, deviceClass,
1380                                 false, false);
1381         if (device != null) {
1382             return device;
1383         }
1384 
1385         if (deviceClass == Receiver.class) {
1386             // try to get Synthesizer
1387             device = getFirstDevice(providers, deviceClass,
1388                                     true, false);
1389             if (device != null) {
1390                 return device;
1391             }
1392         }
1393 
1394         return null;
1395     }
1396 
1397 
1398     /** From a List of MidiDeviceProviders, return the first appropriate
1399         MidiDevice.
1400         @param providers The List of MidiDeviceProviders to search.
1401         @param deviceClass The requested device type, one of Synthesizer.class,
1402         Sequencer.class, Receiver.class or Transmitter.class.
1403         @return A MidiDevice that is considered appropriate, or null
1404         if none is found.

1405      */
1406     private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,
1407                                              Class<?> deviceClass,
1408                                              boolean allowSynthesizer,
1409                                              boolean allowSequencer) {
1410         for(int i = 0; i < providers.size(); i++) {
1411             MidiDeviceProvider provider = providers.get(i);
1412             MidiDevice device = getFirstDevice(provider, deviceClass,
1413                                                allowSynthesizer,
1414                                                allowSequencer);
1415             if (device != null) {
1416                 return device;
1417             }
1418         }
1419         return null;
1420     }
1421 
1422 
1423     /** Checks if a MidiDevice is appropriate.
1424         If deviceClass is Synthesizer or Sequencer, a device implementing
1425         the respective interface is considered appropriate. If deviceClass
1426         is Receiver or Transmitter, a device is considered appropriate if
1427         it implements neither Synthesizer nor Transmitter, and if it can
1428         provide at least one Receiver or Transmitter, respectively.
1429 
1430         @param device the MidiDevice to test
1431         @param allowSynthesizer if true, Synthesizers are considered
1432         appropriate. Otherwise only pure MidiDevices are considered
1433         appropriate (unless allowSequencer is true). This flag only has an
1434         effect for deviceClass Receiver and Transmitter. For other device
1435         classes (Sequencer and Synthesizer), this flag has no effect.
1436         @param allowSequencer if true, Sequencers are considered
1437         appropriate. Otherwise only pure MidiDevices are considered
1438         appropriate (unless allowSynthesizer is true). This flag only has an
1439         effect for deviceClass Receiver and Transmitter. For other device
1440         classes (Sequencer and Synthesizer), this flag has no effect.
1441         @return true if the device is considered appropriate according to the
1442         rules given above, false otherwise.

1443     */
1444     private static boolean isAppropriateDevice(MidiDevice device,
1445                                                Class<?> deviceClass,
1446                                                boolean allowSynthesizer,
1447                                                boolean allowSequencer) {
1448         if (deviceClass.isInstance(device)) {
1449            // This clause is for deviceClass being either Synthesizer
1450             // or Sequencer.
1451             return true;
1452         } else {
1453             // Now the case that deviceClass is Transmitter or
1454             // Receiver. If neither allowSynthesizer nor allowSequencer is
1455             // true, we require device instances to be
1456             // neither Synthesizer nor Sequencer, since we only want
1457             // devices representing MIDI ports.
1458             // Otherwise, the respective type is accepted, too
1459             if ( (! (device instanceof Sequencer) &&
1460                   ! (device instanceof Synthesizer) ) ||
1461                  ((device instanceof Sequencer) && allowSequencer) ||
1462                  ((device instanceof Synthesizer) && allowSynthesizer)) {
1463                 // And of cource, the device has to be able to provide
1464                 // Receivers or Transmitters.
1465                 if ((deviceClass == Receiver.class &&
1466                      device.getMaxReceivers() != 0) ||
1467                     (deviceClass == Transmitter.class &&
1468                      device.getMaxTransmitters() != 0)) {
1469                     return true;
1470                 }
1471             }
1472         }
1473         return false;
1474     }
1475 
1476 
1477     /**
1478      * Obtains the set of services currently installed on the system
1479      * using the SPI mechanism in 1.3.
1480      * @return a List of instances of providers for the requested service.
1481      * If no providers are available, a List of length 0 will be returned.

1482      */
1483      private static List<?> getProviders(Class<?> providerClass) {
1484          return JDK13Services.getProviders(providerClass);
1485     }
1486 }


   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 javax.sound.midi;
  27 

  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.OutputStream;
  32 import java.net.URL;

  33 import java.util.ArrayList;
  34 import java.util.HashSet;
  35 import java.util.Iterator;
  36 import java.util.List;
  37 import java.util.Properties;
  38 import java.util.Set;
  39 
  40 import javax.sound.midi.spi.MidiDeviceProvider;


  41 import javax.sound.midi.spi.MidiFileReader;
  42 import javax.sound.midi.spi.MidiFileWriter;
  43 import javax.sound.midi.spi.SoundbankReader;

  44 


  45 import com.sun.media.sound.AutoConnectSequencer;
  46 import com.sun.media.sound.JDK13Services;
  47 import com.sun.media.sound.MidiDeviceReceiverEnvelope;
  48 import com.sun.media.sound.MidiDeviceTransmitterEnvelope;
  49 import com.sun.media.sound.ReferenceCountingDevice;
  50 
  51 /**
  52  * The {@code MidiSystem} class provides access to the installed MIDI system
  53  * resources, including devices such as synthesizers, sequencers, and MIDI input
  54  * and output ports. A typical simple MIDI application might begin by invoking
  55  * one or more {@code MidiSystem} methods to learn what devices are installed
  56  * and to obtain the ones needed in that application.
  57  * <p>
  58  * The class also has methods for reading files, streams, and  URLs that contain
  59  * standard MIDI file data or soundbanks. You can query the {@code MidiSystem}
  60  * for the format of a specified MIDI file.
  61  * <p>
  62  * You cannot instantiate a {@code MidiSystem}; all the methods are static.
  63  * <p>
  64  * Properties can be used to specify default MIDI devices. Both system
  65  * properties and a properties file are considered. The "sound.properties"
  66  * properties file is read from an implementation-specific location (typically
  67  * it is the {@code lib} directory in the Java installation directory). If a
  68  * property exists both as a system property and in the properties file, the
  69  * system property takes precedence. If none is specified, a suitable default is
  70  * chosen among the available devices. The syntax of the properties file is
  71  * specified in {@link Properties#load(InputStream) Properties.load}. The
  72  * following table lists the available property keys and which methods consider
  73  * them:




  74  *
  75  * <table border=0>
  76  *  <caption>MIDI System Property Keys</caption>
  77  *  <tr>
  78  *   <th>Property Key</th>
  79  *   <th>Interface</th>
  80  *   <th>Affected Method</th>
  81  *  </tr>
  82  *  <tr>
  83  *   <td>{@code javax.sound.midi.Receiver}</td>
  84  *   <td>{@link Receiver}</td>
  85  *   <td>{@link #getReceiver}</td>
  86  *  </tr>
  87  *  <tr>
  88  *   <td>{@code javax.sound.midi.Sequencer}</td>
  89  *   <td>{@link Sequencer}</td>
  90  *   <td>{@link #getSequencer}</td>
  91  *  </tr>
  92  *  <tr>
  93  *   <td>{@code javax.sound.midi.Synthesizer}</td>
  94  *   <td>{@link Synthesizer}</td>
  95  *   <td>{@link #getSynthesizer}</td>
  96  *  </tr>
  97  *  <tr>
  98  *   <td>{@code javax.sound.midi.Transmitter}</td>
  99  *   <td>{@link Transmitter}</td>
 100  *   <td>{@link #getTransmitter}</td>
 101  *  </tr>
 102  * </table>
 103  *
 104  * The property value consists of the provider class name and the device name,
 105  * separated by the hash mark (&quot;#&quot;). The provider class name is the
 106  * fully-qualified name of a concrete
 107  * {@link MidiDeviceProvider MIDI device provider} class. The device name is
 108  * matched against the {@code String} returned by the {@code getName} method of
 109  * {@code MidiDevice.Info}. Either the class name, or the device name may be
 110  * omitted. If only the class name is specified, the trailing hash mark is
 111  * optional.
 112  * <p>
 113  * If the provider class is specified, and it can be successfully retrieved from
 114  * the installed providers, the list of {@code MidiDevice.Info} objects is
 115  * retrieved from the provider. Otherwise, or when these devices do not provide
 116  * a subsequent match, the list is retrieved from {@link #getMidiDeviceInfo} to
 117  * contain all available {@code MidiDevice.Info} objects.
 118  * <p>
 119  * If a device name is specified, the resulting list of {@code MidiDevice.Info}
 120  * objects is searched: the first one with a matching name, and whose
 121  * {@code MidiDevice} implements the respective interface, will be returned. If
 122  * no matching {@code MidiDevice.Info} object is found, or the device name is
 123  * not specified, the first suitable device from the resulting list will be
 124  * returned. For Sequencer and Synthesizer, a device is suitable if it
 125  * implements the respective interface; whereas for Receiver and Transmitter, a
 126  * device is suitable if it implements neither Sequencer nor Synthesizer and
 127  * provides at least one Receiver or Transmitter, respectively.
 128  * <p>
 129  * For example, the property {@code javax.sound.midi.Receiver} with a value
 130  * {@code "com.sun.media.sound.MidiProvider#SunMIDI1"} will have the following
 131  * consequences when {@code getReceiver} is called: if the class
 132  * {@code com.sun.media.sound.MidiProvider} exists in the list of installed MIDI
 133  * device providers, the first {@code Receiver} device with name
 134  * {@code "SunMIDI1"} will be returned. If it cannot be found, the first
 135  * {@code Receiver} from that provider will be returned, regardless of name. If
 136  * there is none, the first {@code Receiver} with name {@code "SunMIDI1"} in the
 137  * list of all devices (as returned by {@code getMidiDeviceInfo}) will be
 138  * returned, or, if not found, the first {@code Receiver} that can be found in
 139  * the list of all devices is returned. If that fails, too, a
 140  * {@code MidiUnavailableException} is thrown.
















 141  *
 142  * @author Kara Kytle
 143  * @author Florian Bomers
 144  * @author Matthias Pfisterer
 145  */
 146 public class MidiSystem {
 147 
 148     /**
 149      * Private no-args constructor for ensuring against instantiation.
 150      */
 151     private MidiSystem() {
 152     }
 153 

 154     /**
 155      * Obtains an array of information objects representing the set of all MIDI
 156      * devices available on the system. A returned information object can then
 157      * be used to obtain the corresponding device object, by invoking

 158      * {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}.
 159      *
 160      * @return an array of {@code MidiDevice.Info} objects, one for each
 161      *         installed MIDI device. If no such devices are installed, an array
 162      *         of length 0 is returned.
 163      */
 164     public static MidiDevice.Info[] getMidiDeviceInfo() {
 165         List<MidiDevice.Info> allInfos = new ArrayList<>();
 166         List<MidiDeviceProvider> providers = getMidiDeviceProviders();
 167 
 168         for(int i = 0; i < providers.size(); i++) {
 169             MidiDeviceProvider provider = providers.get(i);
 170             MidiDevice.Info[] tmpinfo = provider.getDeviceInfo();
 171             for (int j = 0; j < tmpinfo.length; j++) {
 172                 allInfos.add( tmpinfo[j] );
 173             }
 174         }
 175         MidiDevice.Info[] infosArray = allInfos.toArray(new MidiDevice.Info[0]);
 176         return infosArray;
 177     }
 178 

 179     /**
 180      * Obtains the requested MIDI device.
 181      *
 182      * @param  info a device information object representing the desired device
 183      * @return the requested device
 184      * @throws MidiUnavailableException if the requested device is not available
 185      *         due to resource restrictions
 186      * @throws IllegalArgumentException if the info object does not represent a
 187      *         MIDI device installed on the system
 188      * @see #getMidiDeviceInfo
 189      */
 190     public static MidiDevice getMidiDevice(MidiDevice.Info info) throws MidiUnavailableException {
 191         List<MidiDeviceProvider> providers = getMidiDeviceProviders();
 192 
 193         for(int i = 0; i < providers.size(); i++) {
 194             MidiDeviceProvider provider = providers.get(i);
 195             if (provider.isDeviceSupported(info)) {
 196                 MidiDevice device = provider.getDevice(info);
 197                 return device;
 198             }
 199         }
 200         throw new IllegalArgumentException("Requested device not installed: " + info);
 201     }
 202 

 203     /**
 204      * Obtains a MIDI receiver from an external MIDI port or other default
 205      * device. The returned receiver always implements the
 206      * {@code MidiDeviceReceiver} interface.
 207      * <p>
 208      * If the system property {@code javax.sound.midi.Receiver} is defined or it
 209      * is defined in the file "sound.properties", it is used to identify the
 210      * device that provides the default receiver. For details, refer to the
 211      * {@link MidiSystem class description}.
 212      * <p>
 213      * If a suitable MIDI port is not available, the Receiver is retrieved from
 214      * an installed synthesizer.
 215      * <p>
 216      * If a native receiver provided by the default device does not implement
 217      * the {@code MidiDeviceReceiver} interface, it will be wrapped in a wrapper
 218      * class that implements the {@code MidiDeviceReceiver} interface. The
 219      * corresponding {@code Receiver} method calls will be forwarded to the
 220      * native receiver.
 221      * <p>
 222      * If this method returns successfully, the {@link MidiDevice MidiDevice}
 223      * the {@code Receiver} belongs to is opened implicitly, if it is not
 224      * already open. It is possible to close an implicitly opened device by
 225      * calling {@link Receiver#close close} on the returned {@code Receiver}.
 226      * All open {@code Receiver} instances have to be closed in order to release
 227      * system resources hold by the {@code MidiDevice}. For a detailed
 228      * description of open/close behaviour see the class description of
 229      * {@link MidiDevice MidiDevice}.





 230      *
 231      * @return the default MIDI receiver
 232      * @throws MidiUnavailableException if the default receiver is not available
 233      *         due to resource restrictions, or no device providing receivers is
 234      *         installed in the system
 235      */
 236     public static Receiver getReceiver() throws MidiUnavailableException {
 237         // may throw MidiUnavailableException
 238         MidiDevice device = getDefaultDeviceWrapper(Receiver.class);
 239         Receiver receiver;
 240         if (device instanceof ReferenceCountingDevice) {
 241             receiver = ((ReferenceCountingDevice) device).getReceiverReferenceCounting();
 242         } else {
 243             receiver = device.getReceiver();
 244         }
 245         if (!(receiver instanceof MidiDeviceReceiver)) {
 246             receiver = new MidiDeviceReceiverEnvelope(device, receiver);
 247         }
 248         return receiver;
 249     }
 250 

 251     /**
 252      * Obtains a MIDI transmitter from an external MIDI port or other default
 253      * source. The returned transmitter always implements the
 254      * {@code MidiDeviceTransmitter} interface.
 255      * <p>
 256      * If the system property {@code javax.sound.midi.Transmitter} is defined or
 257      * it is defined in the file "sound.properties", it is used to identify the
 258      * device that provides the default transmitter. For details, refer to the
 259      * {@link MidiSystem class description}.
 260      * <p>
 261      * If a native transmitter provided by the default device does not implement


 262      * the {@code MidiDeviceTransmitter} interface, it will be wrapped in a
 263      * wrapper class that implements the {@code MidiDeviceTransmitter}
 264      * interface. The corresponding {@code Transmitter} method calls will be
 265      * forwarded to the native transmitter.
 266      * <p>
 267      * If this method returns successfully, the {@link MidiDevice MidiDevice}
 268      * the {@code Transmitter} belongs to is opened implicitly, if it is not
 269      * already open. It is possible to close an implicitly opened device by
 270      * calling {@link Transmitter#close close} on the returned
 271      * {@code Transmitter}. All open {@code Transmitter} instances have to be
 272      * closed in order to release system resources hold by the
 273      * {@code MidiDevice}. For a detailed description of open/close behaviour
 274      * see the class description of {@link MidiDevice MidiDevice}.



 275      *
 276      * @return the default MIDI transmitter
 277      * @throws MidiUnavailableException if the default transmitter is not
 278      *         available due to resource restrictions, or no device providing
 279      *         transmitters is installed in the system
 280      */
 281     public static Transmitter getTransmitter() throws MidiUnavailableException {
 282         // may throw MidiUnavailableException
 283         MidiDevice device = getDefaultDeviceWrapper(Transmitter.class);
 284         Transmitter transmitter;
 285         if (device instanceof ReferenceCountingDevice) {
 286             transmitter = ((ReferenceCountingDevice) device).getTransmitterReferenceCounting();
 287         } else {
 288             transmitter = device.getTransmitter();
 289         }
 290         if (!(transmitter instanceof MidiDeviceTransmitter)) {
 291             transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter);
 292         }
 293         return transmitter;
 294     }
 295 

 296     /**
 297      * Obtains the default synthesizer.
 298      * <p>
 299      * If the system property {@code javax.sound.midi.Synthesizer} is defined or
 300      * it is defined in the file "sound.properties", it is used to identify the
 301      * default synthesizer. For details, refer to the
 302      * {@link MidiSystem class description}.

 303      *
 304      * @return the default synthesizer
 305      * @throws MidiUnavailableException if the synthesizer is not available due
 306      *         to resource restrictions, or no synthesizer is installed in the
 307      *         system
 308      */
 309     public static Synthesizer getSynthesizer() throws MidiUnavailableException {
 310         // may throw MidiUnavailableException
 311         return (Synthesizer) getDefaultDeviceWrapper(Synthesizer.class);
 312     }
 313 

 314     /**
 315      * Obtains the default {@code Sequencer}, connected to a default device. The
 316      * returned {@code Sequencer} instance is connected to the default
 317      * {@code Synthesizer}, as returned by {@link #getSynthesizer}. If there is
 318      * no {@code Synthesizer} available, or the default {@code Synthesizer}
 319      * cannot be opened, the {@code sequencer} is connected to the default
 320      * {@code Receiver}, as returned by {@link #getReceiver}. The connection is
 321      * made by retrieving a {@code Transmitter} instance from the
 322      * {@code Sequencer} and setting its {@code Receiver}. Closing and
 323      * re-opening the sequencer will restore the connection to the default
 324      * device.
 325      * <p>
 326      * This method is equivalent to calling {@code getSequencer(true)}.
 327      * <p>
 328      * If the system property {@code javax.sound.midi.Sequencer} is defined or
 329      * it is defined in the file "sound.properties", it is used to identify the
 330      * default sequencer. For details, refer to the
 331      * {@link MidiSystem class description}.







 332      *
 333      * @return the default sequencer, connected to a default Receiver
 334      * @throws MidiUnavailableException if the sequencer is not available due to
 335      *         resource restrictions, or there is no {@code Receiver} available
 336      *         by any installed {@code MidiDevice}, or no sequencer is installed
 337      *         in the system

 338      * @see #getSequencer(boolean)
 339      * @see #getSynthesizer
 340      * @see #getReceiver
 341      */
 342     public static Sequencer getSequencer() throws MidiUnavailableException {
 343         return getSequencer(true);
 344     }
 345 


 346     /**
 347      * Obtains the default {@code Sequencer}, optionally connected to a default
 348      * device.
 349      * <p>
 350      * If {@code connected} is true, the returned {@code Sequencer} instance is
 351      * connected to the default {@code Synthesizer}, as returned by
 352      * {@link #getSynthesizer}. If there is no {@code Synthesizer} available, or
 353      * the default {@code Synthesizer} cannot be opened, the {@code sequencer}
 354      * is connected to the default {@code Receiver}, as returned by
 355      * {@link #getReceiver}. The connection is made by retrieving a
 356      * {@code Transmitter} instance from the {@code Sequencer} and setting its
 357      * {@code Receiver}. Closing and re-opening the sequencer will restore the





 358      * connection to the default device.
 359      * <p>
 360      * If {@code connected} is false, the returned {@code Sequencer} instance is
 361      * not connected, it has no open {@code Transmitters}. In order to play the
 362      * sequencer on a MIDI device, or a {@code Synthesizer}, it is necessary to
 363      * get a {@code Transmitter} and set its {@code Receiver}.
 364      * <p>
 365      * If the system property {@code javax.sound.midi.Sequencer} is defined or
 366      * it is defined in the file "sound.properties", it is used to identify the
 367      * default sequencer. For details, refer to the
 368      * {@link MidiSystem class description}.
 369      *
 370      * @param  connected whether or not the returned {@code Sequencer} is
 371      *         connected to the default {@code Synthesizer}













 372      * @return the default sequencer
 373      * @throws MidiUnavailableException if the sequencer is not available due to
 374      *         resource restrictions, or no sequencer is installed in the
 375      *         system, or if {@code connected} is true, and there is no
 376      *         {@code Receiver} available by any installed {@code MidiDevice}


 377      * @see #getSynthesizer
 378      * @see #getReceiver
 379      * @since 1.5
 380      */
 381     public static Sequencer getSequencer(boolean connected)
 382         throws MidiUnavailableException {
 383         Sequencer seq = (Sequencer) getDefaultDeviceWrapper(Sequencer.class);
 384 
 385         if (connected) {
 386             // IMPORTANT: this code needs to be synch'ed with
 387             //            all AutoConnectSequencer instances,
 388             //            (e.g. RealTimeSequencer) because the
 389             //            same algorithm for synth retrieval
 390             //            needs to be used!
 391 
 392             Receiver rec = null;
 393             MidiUnavailableException mue = null;
 394 
 395             // first try to connect to the default synthesizer
 396             try {


 423                     if (e instanceof MidiUnavailableException) {
 424                         mue = (MidiUnavailableException) e;
 425                     }
 426                 }
 427             }
 428             if (rec != null) {
 429                 seq.getTransmitter().setReceiver(rec);
 430                 if (seq instanceof AutoConnectSequencer) {
 431                     ((AutoConnectSequencer) seq).setAutoConnect(rec);
 432                 }
 433             } else {
 434                 if (mue != null) {
 435                     throw mue;
 436                 }
 437                 throw new MidiUnavailableException("no receiver available");
 438             }
 439         }
 440         return seq;
 441     }
 442 



 443     /**
 444      * Constructs a MIDI sound bank by reading it from the specified stream. The
 445      * stream must point to a valid MIDI soundbank file. In general, MIDI
 446      * soundbank providers may need to read some data from the stream before
 447      * determining whether they support it. These parsers must be able to mark
 448      * the stream, read enough data to determine whether they support the
 449      * stream, and, if not, reset the stream's read pointer to its original
 450      * position. If the input stream does not support this, this method may fail
 451      * with an {@code IOException}.
 452      *
 453      * @param  stream the source of the sound bank data
 454      * @return the sound bank
 455      * @throws InvalidMidiDataException if the stream does not point to valid
 456      *         MIDI soundbank data recognized by the system
 457      * @throws IOException if an I/O error occurred when loading the soundbank
 458      * @see InputStream#markSupported
 459      * @see InputStream#mark
 460      */
 461     public static Soundbank getSoundbank(InputStream stream)
 462         throws InvalidMidiDataException, IOException {
 463 
 464         SoundbankReader sp = null;
 465         Soundbank s = null;
 466 
 467         List<SoundbankReader> providers = getSoundbankReaders();
 468 
 469         for(int i = 0; i < providers.size(); i++) {
 470             sp = providers.get(i);
 471             s = sp.getSoundbank(stream);
 472 
 473             if( s!= null) {
 474                 return s;
 475             }
 476         }
 477         throw new InvalidMidiDataException("cannot get soundbank from stream");
 478 
 479     }
 480 

 481     /**
 482      * Constructs a {@code Soundbank} by reading it from the specified URL. The
 483      * URL must point to a valid MIDI soundbank file.
 484      *
 485      * @param  url the source of the sound bank data
 486      * @return the sound bank
 487      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 488      *         soundbank data recognized by the system
 489      * @throws IOException if an I/O error occurred when loading the soundbank
 490      */
 491     public static Soundbank getSoundbank(URL url)
 492         throws InvalidMidiDataException, IOException {
 493 
 494         SoundbankReader sp = null;
 495         Soundbank s = null;
 496 
 497         List<SoundbankReader> providers = getSoundbankReaders();
 498 
 499         for(int i = 0; i < providers.size(); i++) {
 500             sp = providers.get(i);
 501             s = sp.getSoundbank(url);
 502 
 503             if( s!= null) {
 504                 return s;
 505             }
 506         }
 507         throw new InvalidMidiDataException("cannot get soundbank from stream");
 508 
 509     }
 510 

 511     /**
 512      * Constructs a {@code Soundbank} by reading it from the specified
 513      * {@code File}. The {@code File} must point to a valid MIDI soundbank file.

 514      *
 515      * @param  file the source of the sound bank data
 516      * @return the sound bank
 517      * @throws InvalidMidiDataException if the {@code File} does not point to
 518      *         valid MIDI soundbank data recognized by the system
 519      * @throws IOException if an I/O error occurred when loading the soundbank
 520      */
 521     public static Soundbank getSoundbank(File file)
 522         throws InvalidMidiDataException, IOException {
 523 
 524         SoundbankReader sp = null;
 525         Soundbank s = null;
 526 
 527         List<SoundbankReader> providers = getSoundbankReaders();
 528 
 529         for(int i = 0; i < providers.size(); i++) {
 530             sp = providers.get(i);
 531             s = sp.getSoundbank(file);
 532 
 533             if( s!= null) {
 534                 return s;
 535             }
 536         }
 537         throw new InvalidMidiDataException("cannot get soundbank from stream");
 538     }
 539 


 540     /**
 541      * Obtains the MIDI file format of the data in the specified input stream.
 542      * The stream must point to valid MIDI file data for a file type recognized
 543      * by the system.
 544      * <p>
 545      * This method and/or the code it invokes may need to read some data from
 546      * the stream to determine whether its data format is supported. The
 547      * implementation may therefore need to mark the stream, read enough data to
 548      * determine whether it is in a supported format, and reset the stream's
 549      * read pointer to its original position. If the input stream does not
 550      * permit this set of operations, this method may fail with an
 551      * {@code IOException}.
 552      * <p>
 553      * This operation can only succeed for files of a type which can be parsed
 554      * by an installed file reader. It may fail with an
 555      * {@code InvalidMidiDataException} even for valid files if no compatible
 556      * file reader is installed. It will also fail with an
 557      * {@code InvalidMidiDataException} if a compatible file reader is
 558      * installed, but encounters errors while determining the file format.
 559      *
 560      * @param  stream the input stream from which file format information should
 561      *         be extracted
 562      * @return an {@code MidiFileFormat} object describing the MIDI file format
 563      * @throws InvalidMidiDataException if the stream does not point to valid
 564      *         MIDI file data recognized by the system
 565      * @throws IOException if an I/O exception occurs while accessing the
 566      *         stream
 567      * @see #getMidiFileFormat(URL)
 568      * @see #getMidiFileFormat(File)
 569      * @see InputStream#markSupported
 570      * @see InputStream#mark
 571      */
 572     public static MidiFileFormat getMidiFileFormat(InputStream stream)
 573         throws InvalidMidiDataException, IOException {
 574 
 575         List<MidiFileReader> providers = getMidiFileReaders();
 576         MidiFileFormat format = null;
 577 
 578         for(int i = 0; i < providers.size(); i++) {
 579             MidiFileReader reader =  providers.get(i);
 580             try {
 581                 format = reader.getMidiFileFormat( stream ); // throws IOException
 582                 break;
 583             } catch (InvalidMidiDataException e) {
 584                 continue;
 585             }
 586         }
 587 
 588         if( format==null ) {
 589             throw new InvalidMidiDataException("input stream is not a supported file type");
 590         } else {
 591             return format;
 592         }
 593     }
 594 

 595     /**
 596      * Obtains the MIDI file format of the data in the specified URL. The URL
 597      * must point to valid MIDI file data for a file type recognized by the
 598      * system.
 599      * <p>
 600      * This operation can only succeed for files of a type which can be parsed
 601      * by an installed file reader. It may fail with an
 602      * {@code InvalidMidiDataException} even for valid files if no compatible
 603      * file reader is installed. It will also fail with an
 604      * {@code InvalidMidiDataException} if a compatible file reader is
 605      * installed, but encounters errors while determining the file format.
 606      *
 607      * @param  url the URL from which file format information should be
 608      *         extracted
 609      * @return a {@code MidiFileFormat} object describing the MIDI file format

 610      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 611      *         file data recognized by the system
 612      * @throws IOException if an I/O exception occurs while accessing the URL

 613      * @see #getMidiFileFormat(InputStream)
 614      * @see #getMidiFileFormat(File)
 615      */
 616     public static MidiFileFormat getMidiFileFormat(URL url)
 617         throws InvalidMidiDataException, IOException {
 618 
 619         List<MidiFileReader> providers = getMidiFileReaders();
 620         MidiFileFormat format = null;
 621 
 622         for(int i = 0; i < providers.size(); i++) {
 623             MidiFileReader reader = providers.get(i);
 624             try {
 625                 format = reader.getMidiFileFormat( url ); // throws IOException
 626                 break;
 627             } catch (InvalidMidiDataException e) {
 628                 continue;
 629             }
 630         }
 631 
 632         if( format==null ) {
 633             throw new InvalidMidiDataException("url is not a supported file type");
 634         } else {
 635             return format;
 636         }
 637     }
 638 

 639     /**
 640      * Obtains the MIDI file format of the specified {@code File}. The
 641      * {@code File} must point to valid MIDI file data for a file type
 642      * recognized by the system.
 643      * <p>
 644      * This operation can only succeed for files of a type which can be parsed
 645      * by an installed file reader. It may fail with an
 646      * {@code InvalidMidiDataException} even for valid files if no compatible
 647      * file reader is installed. It will also fail with an
 648      * {@code InvalidMidiDataException} if a compatible file reader is
 649      * installed, but encounters errors while determining the file format.
 650      *
 651      * @param  file the {@code File} from which file format information should
 652      *         be extracted
 653      * @return a {@code MidiFileFormat} object describing the MIDI file format
 654      * @throws InvalidMidiDataException if the {@code File} does not point to
 655      *         valid MIDI file data recognized by the system
 656      * @throws IOException if an I/O exception occurs while accessing the file

 657      * @see #getMidiFileFormat(InputStream)
 658      * @see #getMidiFileFormat(URL)
 659      */
 660     public static MidiFileFormat getMidiFileFormat(File file)
 661         throws InvalidMidiDataException, IOException {
 662 
 663         List<MidiFileReader> providers = getMidiFileReaders();
 664         MidiFileFormat format = null;
 665 
 666         for(int i = 0; i < providers.size(); i++) {
 667             MidiFileReader reader = providers.get(i);
 668             try {
 669                 format = reader.getMidiFileFormat( file ); // throws IOException
 670                 break;
 671             } catch (InvalidMidiDataException e) {
 672                 continue;
 673             }
 674         }
 675 
 676         if( format==null ) {
 677             throw new InvalidMidiDataException("file is not a supported file type");
 678         } else {
 679             return format;
 680         }
 681     }
 682 

 683     /**
 684      * Obtains a MIDI sequence from the specified input stream. The stream must
 685      * point to valid MIDI file data for a file type recognized by the system.

 686      * <p>
 687      * This method and/or the code it invokes may need to read some data from
 688      * the stream to determine whether its data format is supported. The
 689      * implementation may therefore need to mark the stream, read enough data to
 690      * determine whether it is in a supported format, and reset the stream's
 691      * read pointer to its original position. If the input stream does not
 692      * permit this set of operations, this method may fail with an
 693      * {@code IOException}.
 694      * <p>
 695      * This operation can only succeed for files of a type which can be parsed
 696      * by an installed file reader. It may fail with an
 697      * {@code InvalidMidiDataException} even for valid files if no compatible
 698      * file reader is installed. It will also fail with an
 699      * {@code InvalidMidiDataException} if a compatible file reader is
 700      * installed, but encounters errors while constructing the {@code Sequence}
 701      * object from the file data.
 702      *
 703      * @param  stream the input stream from which the {@code Sequence} should be
 704      *         constructed
 705      * @return a {@code Sequence} object based on the MIDI file data contained
 706      *         in the input stream
 707      * @throws InvalidMidiDataException if the stream does not point to valid
 708      *         MIDI file data recognized by the system
 709      * @throws IOException if an I/O exception occurs while accessing the stream

 710      * @see InputStream#markSupported
 711      * @see InputStream#mark
 712      */
 713     public static Sequence getSequence(InputStream stream)
 714         throws InvalidMidiDataException, IOException {
 715 
 716         List<MidiFileReader> providers = getMidiFileReaders();
 717         Sequence sequence = null;
 718 
 719         for(int i = 0; i < providers.size(); i++) {
 720             MidiFileReader reader = providers.get(i);
 721             try {
 722                 sequence = reader.getSequence( stream ); // throws IOException
 723                 break;
 724             } catch (InvalidMidiDataException e) {
 725                 continue;
 726             }
 727         }
 728 
 729         if( sequence==null ) {
 730             throw new InvalidMidiDataException("could not get sequence from input stream");
 731         } else {
 732             return sequence;
 733         }
 734     }
 735 

 736     /**
 737      * Obtains a MIDI sequence from the specified URL. The URL must point to
 738      * valid MIDI file data for a file type recognized by the system.

 739      * <p>
 740      * This operation can only succeed for files of a type which can be parsed
 741      * by an installed file reader. It may fail with an
 742      * {@code InvalidMidiDataException} even for valid files if no compatible
 743      * file reader is installed. It will also fail with an
 744      * {@code InvalidMidiDataException} if a compatible file reader is
 745      * installed, but encounters errors while constructing the {@code Sequence}
 746      * object from the file data.
 747      *
 748      * @param  url the URL from which the {@code Sequence} should be constructed
 749      * @return a {@code Sequence} object based on the MIDI file data pointed to
 750      *         by the URL

 751      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 752      *         file data recognized by the system
 753      * @throws IOException if an I/O exception occurs while accessing the URL
 754      */
 755     public static Sequence getSequence(URL url)
 756         throws InvalidMidiDataException, IOException {
 757 
 758         List<MidiFileReader> providers = getMidiFileReaders();
 759         Sequence sequence = null;
 760 
 761         for(int i = 0; i < providers.size(); i++) {
 762             MidiFileReader reader = providers.get(i);
 763             try {
 764                 sequence = reader.getSequence( url ); // throws IOException
 765                 break;
 766             } catch (InvalidMidiDataException e) {
 767                 continue;
 768             }
 769         }
 770 
 771         if( sequence==null ) {
 772             throw new InvalidMidiDataException("could not get sequence from URL");
 773         } else {
 774             return sequence;
 775         }
 776     }
 777 

 778     /**
 779      * Obtains a MIDI sequence from the specified {@code File}. The {@code File}
 780      * must point to valid MIDI file data for a file type recognized by the
 781      * system.
 782      * <p>
 783      * This operation can only succeed for files of a type which can be parsed
 784      * by an installed file reader. It may fail with an
 785      * {@code InvalidMidiDataException} even for valid files if no compatible
 786      * file reader is installed. It will also fail with an
 787      * {@code InvalidMidiDataException} if a compatible file reader is
 788      * installed, but encounters errors while constructing the {@code Sequence}
 789      * object from the file data.
 790      *
 791      * @param  file the {@code File} from which the {@code Sequence} should be
 792      *         constructed
 793      * @return a {@code Sequence} object based on the MIDI file data pointed to
 794      *         by the File
 795      * @throws InvalidMidiDataException if the File does not point to valid MIDI
 796      *         file data recognized by the system
 797      * @throws IOException if an I/O exception occurs
 798      */
 799     public static Sequence getSequence(File file)
 800         throws InvalidMidiDataException, IOException {
 801 
 802         List<MidiFileReader> providers = getMidiFileReaders();
 803         Sequence sequence = null;
 804 
 805         for(int i = 0; i < providers.size(); i++) {
 806             MidiFileReader reader = providers.get(i);
 807             try {
 808                 sequence = reader.getSequence( file ); // throws IOException
 809                 break;
 810             } catch (InvalidMidiDataException e) {
 811                 continue;
 812             }
 813         }
 814 
 815         if( sequence==null ) {
 816             throw new InvalidMidiDataException("could not get sequence from file");
 817         } else {
 818             return sequence;
 819         }
 820     }
 821 

 822     /**
 823      * Obtains the set of MIDI file types for which file writing support is
 824      * provided by the system.
 825      *
 826      * @return array of unique file types. If no file types are supported, an
 827      *         array of length 0 is returned.
 828      */
 829     public static int[] getMidiFileTypes() {
 830 
 831         List<MidiFileWriter> providers = getMidiFileWriters();
 832         Set<Integer> allTypes = new HashSet<>();
 833 
 834         // gather from all the providers
 835 
 836         for (int i = 0; i < providers.size(); i++ ) {
 837             MidiFileWriter writer = providers.get(i);
 838             int[] types = writer.getMidiFileTypes();
 839             for (int j = 0; j < types.length; j++ ) {
 840                 allTypes.add(types[j]);
 841             }
 842         }
 843         int resultTypes[] = new int[allTypes.size()];
 844         int index = 0;
 845         Iterator<Integer> iterator = allTypes.iterator();
 846         while (iterator.hasNext()) {
 847             Integer integer = iterator.next();
 848             resultTypes[index++] = integer.intValue();
 849         }
 850         return resultTypes;
 851     }
 852 

 853     /**
 854      * Indicates whether file writing support for the specified MIDI file type
 855      * is provided by the system.
 856      *
 857      * @param  fileType the file type for which write capabilities are queried
 858      * @return {@code true} if the file type is supported, otherwise
 859      *         {@code false}
 860      */
 861     public static boolean isFileTypeSupported(int fileType) {
 862 
 863         List<MidiFileWriter> providers = getMidiFileWriters();
 864 
 865         for (int i = 0; i < providers.size(); i++ ) {
 866             MidiFileWriter writer = providers.get(i);
 867             if( writer.isFileTypeSupported(fileType)) {
 868                 return true;
 869             }
 870         }
 871         return false;
 872     }
 873 

 874     /**
 875      * Obtains the set of MIDI file types that the system can write from the
 876      * sequence specified.
 877      *
 878      * @param  sequence the sequence for which MIDI file type support is queried
 879      * @return the set of unique supported file types. If no file types are
 880      *         supported, returns an array of length 0.
 881      */
 882     public static int[] getMidiFileTypes(Sequence sequence) {
 883 
 884         List<MidiFileWriter> providers = getMidiFileWriters();
 885         Set<Integer> allTypes = new HashSet<>();
 886 
 887         // gather from all the providers
 888 
 889         for (int i = 0; i < providers.size(); i++ ) {
 890             MidiFileWriter writer = providers.get(i);
 891             int[] types = writer.getMidiFileTypes(sequence);
 892             for (int j = 0; j < types.length; j++ ) {
 893                 allTypes.add(types[j]);
 894             }
 895         }
 896         int resultTypes[] = new int[allTypes.size()];
 897         int index = 0;
 898         Iterator<Integer> iterator = allTypes.iterator();
 899         while (iterator.hasNext()) {
 900             Integer integer = iterator.next();
 901             resultTypes[index++] = integer.intValue();
 902         }
 903         return resultTypes;
 904     }
 905 

 906     /**
 907      * Indicates whether a MIDI file of the file type specified can be written
 908      * from the sequence indicated.
 909      *
 910      * @param  fileType the file type for which write capabilities are queried
 911      * @param  sequence the sequence for which file writing support is queried
 912      * @return {@code true} if the file type is supported for this sequence,
 913      *         otherwise {@code false}
 914      */
 915     public static boolean isFileTypeSupported(int fileType, Sequence sequence) {
 916 
 917         List<MidiFileWriter> providers = getMidiFileWriters();
 918 
 919         for (int i = 0; i < providers.size(); i++ ) {
 920             MidiFileWriter writer = providers.get(i);
 921             if( writer.isFileTypeSupported(fileType,sequence)) {
 922                 return true;
 923             }
 924         }
 925         return false;
 926     }
 927 

 928     /**
 929      * Writes a stream of bytes representing a file of the MIDI file type
 930      * indicated to the output stream provided.
 931      *
 932      * @param  in sequence containing MIDI data to be written to the file
 933      * @param  fileType the file type of the file to be written to the output
 934      *         stream
 935      * @param  out stream to which the file data should be written
 936      * @return the number of bytes written to the output stream
 937      * @throws IOException if an I/O exception occurs
 938      * @throws IllegalArgumentException if the file format is not supported by
 939      *         the system
 940      * @see #isFileTypeSupported(int, Sequence)
 941      * @see #getMidiFileTypes(Sequence)
 942      */
 943     public static int write(Sequence in, int fileType, OutputStream out) throws IOException {
 944 
 945         List<MidiFileWriter> providers = getMidiFileWriters();
 946         //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
 947         int bytesWritten = -2;
 948 
 949         for (int i = 0; i < providers.size(); i++ ) {
 950             MidiFileWriter writer = providers.get(i);
 951             if( writer.isFileTypeSupported( fileType, in ) ) {
 952 
 953                 bytesWritten = writer.write(in, fileType, out);
 954                 break;
 955             }
 956         }
 957         if (bytesWritten == -2) {
 958             throw new IllegalArgumentException("MIDI file type is not supported");
 959         }
 960         return bytesWritten;
 961     }
 962 

 963     /**
 964      * Writes a stream of bytes representing a file of the MIDI file type
 965      * indicated to the external file provided.
 966      *
 967      * @param  in sequence containing MIDI data to be written to the file
 968      * @param  type the file type of the file to be written to the output stream
 969      * @param  out external file to which the file data should be written
 970      * @return the number of bytes written to the file
 971      * @throws IOException if an I/O exception occurs
 972      * @throws IllegalArgumentException if the file type is not supported by the
 973      *         system
 974      * @see #isFileTypeSupported(int, Sequence)
 975      * @see #getMidiFileTypes(Sequence)
 976      */
 977     public static int write(Sequence in, int type, File out) throws IOException {
 978 
 979         List<MidiFileWriter> providers = getMidiFileWriters();
 980         //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
 981         int bytesWritten = -2;
 982 
 983         for (int i = 0; i < providers.size(); i++ ) {
 984             MidiFileWriter writer = providers.get(i);
 985             if( writer.isFileTypeSupported( type, in ) ) {
 986 
 987                 bytesWritten = writer.write(in, type, out);
 988                 break;
 989             }
 990         }
 991         if (bytesWritten == -2) {
 992             throw new IllegalArgumentException("MIDI file type is not supported");
 993         }
 994         return bytesWritten;
 995     }
 996 


 997     // HELPER METHODS
 998     @SuppressWarnings("unchecked")
 999     private static List<MidiDeviceProvider> getMidiDeviceProviders() {
1000         return (List<MidiDeviceProvider>) getProviders(MidiDeviceProvider.class);
1001     }
1002 
1003     @SuppressWarnings("unchecked")
1004     private static List<SoundbankReader> getSoundbankReaders() {
1005         return (List<SoundbankReader>) getProviders(SoundbankReader.class);
1006     }
1007 
1008     @SuppressWarnings("unchecked")
1009     private static List<MidiFileWriter> getMidiFileWriters() {
1010         return (List<MidiFileWriter>) getProviders(MidiFileWriter.class);
1011     }
1012 
1013     @SuppressWarnings("unchecked")
1014     private static List<MidiFileReader> getMidiFileReaders() {
1015         return (List<MidiFileReader>) getProviders(MidiFileReader.class);
1016     }
1017 
1018     /**
1019      * Attempts to locate and return a default MidiDevice of the specified type.


1020      * This method wraps {@link #getDefaultDevice}. It catches the
1021      * {@code IllegalArgumentException} thrown by {@code getDefaultDevice} and
1022      * instead throws a {@code MidiUnavailableException}, with the catched

1023      * exception chained.
1024      *
1025      * @param  deviceClass The requested device type, one of Synthesizer.class,
1026      *         Sequencer.class, Receiver.class or Transmitter.class
1027      * @throws MidiUnavailableException on failure
1028      */
1029     private static MidiDevice getDefaultDeviceWrapper(Class<?> deviceClass)
1030         throws MidiUnavailableException{
1031         try {
1032             return getDefaultDevice(deviceClass);
1033         } catch (IllegalArgumentException iae) {
1034             MidiUnavailableException mae = new MidiUnavailableException();
1035             mae.initCause(iae);
1036             throw mae;
1037         }
1038     }
1039 
1040     /**
1041      * Attempts to locate and return a default MidiDevice of the specified type.

1042      *
1043      * @param  deviceClass The requested device type, one of Synthesizer.class,
1044      *         Sequencer.class, Receiver.class or Transmitter.class
1045      * @throws IllegalArgumentException on failure
1046      */
1047     private static MidiDevice getDefaultDevice(Class<?> deviceClass) {
1048         List<MidiDeviceProvider> providers = getMidiDeviceProviders();
1049         String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass);
1050         String instanceName = JDK13Services.getDefaultInstanceName(deviceClass);
1051         MidiDevice device;
1052 
1053         if (providerClassName != null) {
1054             MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers);
1055             if (defaultProvider != null) {
1056                 if (instanceName != null) {
1057                     device = getNamedDevice(instanceName, defaultProvider, deviceClass);
1058                     if (device != null) {
1059                         return device;
1060                     }
1061                 }
1062                 device = getFirstDevice(defaultProvider, deviceClass);
1063                 if (device != null) {
1064                     return device;
1065                 }


1068 
1069         /* Provider class not specified or cannot be found, or
1070            provider class specified, and no appropriate device available or
1071            provider class and instance specified and instance cannot be found or is not appropriate */
1072         if (instanceName != null) {
1073             device = getNamedDevice(instanceName, providers, deviceClass);
1074             if (device != null) {
1075                 return device;
1076             }
1077         }
1078 
1079         /* No default are specified, or if something is specified, everything
1080            failed. */
1081         device = getFirstDevice(providers, deviceClass);
1082         if (device != null) {
1083             return device;
1084         }
1085         throw new IllegalArgumentException("Requested device not installed");
1086     }
1087 
1088     /**
1089      * Return a MidiDeviceProvider of a given class from the list of
1090      * MidiDeviceProviders.
1091      *
1092      * @param  providerClassName The class name of the provider to be returned
1093      * @param  providers The list of MidiDeviceProviders that is searched
1094      * @return A MidiDeviceProvider of the requested class, or null if none is
1095      *         found

1096      */
1097     private static MidiDeviceProvider getNamedProvider(String providerClassName,
1098                                                        List<MidiDeviceProvider> providers) {
1099         for(int i = 0; i < providers.size(); i++) {
1100             MidiDeviceProvider provider = providers.get(i);
1101             if (provider.getClass().getName().equals(providerClassName)) {
1102                 return provider;
1103             }
1104         }
1105         return null;
1106     }
1107 
1108     /**
1109      * Return a MidiDevice with a given name from a given MidiDeviceProvider.
1110      *
1111      * @param  deviceName The name of the MidiDevice to be returned
1112      * @param  provider The MidiDeviceProvider to check for MidiDevices
1113      * @param  deviceClass The requested device type, one of Synthesizer.class,
1114      *         Sequencer.class, Receiver.class or Transmitter.class
1115      * @return A MidiDevice matching the requirements, or null if none is found
1116      */
1117     private static MidiDevice getNamedDevice(String deviceName,
1118                                              MidiDeviceProvider provider,
1119                                              Class<?> deviceClass) {
1120         MidiDevice device;
1121         // try to get MIDI port
1122         device = getNamedDevice(deviceName, provider, deviceClass,
1123                                  false, false);
1124         if (device != null) {
1125             return device;
1126         }
1127 
1128         if (deviceClass == Receiver.class) {
1129             // try to get Synthesizer
1130             device = getNamedDevice(deviceName, provider, deviceClass,
1131                                      true, false);
1132             if (device != null) {
1133                 return device;
1134             }
1135         }
1136 
1137         return null;
1138     }
1139 
1140     /**
1141      * Return a MidiDevice with a given name from a given MidiDeviceProvider.
1142      *
1143      * @param  deviceName The name of the MidiDevice to be returned
1144      * @param  provider The MidiDeviceProvider to check for MidiDevices
1145      * @param  deviceClass The requested device type, one of Synthesizer.class,
1146      *         Sequencer.class, Receiver.class or Transmitter.class
1147      * @return A MidiDevice matching the requirements, or null if none is found
1148      */
1149     private static MidiDevice getNamedDevice(String deviceName,
1150                                              MidiDeviceProvider provider,
1151                                              Class<?> deviceClass,
1152                                              boolean allowSynthesizer,
1153                                              boolean allowSequencer) {
1154         MidiDevice.Info[] infos = provider.getDeviceInfo();
1155         for (int i = 0; i < infos.length; i++) {
1156             if (infos[i].getName().equals(deviceName)) {
1157                 MidiDevice device = provider.getDevice(infos[i]);
1158                 if (isAppropriateDevice(device, deviceClass,
1159                                         allowSynthesizer, allowSequencer)) {
1160                     return device;
1161                 }
1162             }
1163         }
1164         return null;
1165     }
1166 
1167     /**
1168      * Return a MidiDevice with a given name from a list of MidiDeviceProviders.
1169      *
1170      * @param  deviceName The name of the MidiDevice to be returned
1171      * @param  providers The List of MidiDeviceProviders to check for
1172      *         MidiDevices
1173      * @param  deviceClass The requested device type, one of Synthesizer.class,
1174      *         Sequencer.class, Receiver.class or Transmitter.class
1175      * @return A Mixer matching the requirements, or null if none is found
1176      */
1177     private static MidiDevice getNamedDevice(String deviceName,
1178                                              List<MidiDeviceProvider> providers,
1179                                              Class<?> deviceClass) {
1180         MidiDevice device;
1181         // try to get MIDI port
1182         device = getNamedDevice(deviceName, providers, deviceClass,
1183                                  false, false);
1184         if (device != null) {
1185             return device;
1186         }
1187 
1188         if (deviceClass == Receiver.class) {
1189             // try to get Synthesizer
1190             device = getNamedDevice(deviceName, providers, deviceClass,
1191                                      true, false);
1192             if (device != null) {
1193                 return device;
1194             }
1195         }
1196 
1197         return null;
1198     }
1199 
1200     /**
1201      * Return a MidiDevice with a given name from a list of MidiDeviceProviders.
1202      *
1203      * @param  deviceName The name of the MidiDevice to be returned
1204      * @param  providers The List of MidiDeviceProviders to check for
1205      *         MidiDevices
1206      * @param  deviceClass The requested device type, one of Synthesizer.class,
1207      *         Sequencer.class, Receiver.class or Transmitter.class
1208      * @return A Mixer matching the requirements, or null if none is found
1209      */
1210     private static MidiDevice getNamedDevice(String deviceName,
1211                                              List<MidiDeviceProvider> providers,
1212                                              Class<?> deviceClass,
1213                                              boolean allowSynthesizer,
1214                                              boolean allowSequencer) {
1215         for(int i = 0; i < providers.size(); i++) {
1216             MidiDeviceProvider provider = providers.get(i);
1217             MidiDevice device = getNamedDevice(deviceName, provider,
1218                                                deviceClass,
1219                                                allowSynthesizer,
1220                                                allowSequencer);
1221             if (device != null) {
1222                 return device;
1223             }
1224         }
1225         return null;
1226     }
1227 
1228     /**
1229      * From a given MidiDeviceProvider, return the first appropriate device.
1230      *
1231      * @param  provider The MidiDeviceProvider to check for MidiDevices
1232      * @param  deviceClass The requested device type, one of Synthesizer.class,
1233      *         Sequencer.class, Receiver.class or Transmitter.class
1234      * @return A MidiDevice is considered appropriate, or null if no appropriate
1235      *         device is found
1236      */
1237     private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
1238                                              Class<?> deviceClass) {
1239         MidiDevice device;
1240         // try to get MIDI port
1241         device = getFirstDevice(provider, deviceClass,
1242                                 false, false);
1243         if (device != null) {
1244             return device;
1245         }
1246 
1247         if (deviceClass == Receiver.class) {
1248             // try to get Synthesizer
1249             device = getFirstDevice(provider, deviceClass,
1250                                     true, false);
1251             if (device != null) {
1252                 return device;
1253             }
1254         }
1255 
1256         return null;
1257     }
1258 
1259     /**
1260      * From a given MidiDeviceProvider, return the first appropriate device.
1261      *
1262      * @param  provider The MidiDeviceProvider to check for MidiDevices
1263      * @param  deviceClass The requested device type, one of Synthesizer.class,
1264      *         Sequencer.class, Receiver.class or Transmitter.class
1265      * @return A MidiDevice is considered appropriate, or null if no appropriate
1266      *         device is found
1267      */
1268     private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
1269                                              Class<?> deviceClass,
1270                                              boolean allowSynthesizer,
1271                                              boolean allowSequencer) {
1272         MidiDevice.Info[] infos = provider.getDeviceInfo();
1273         for (int j = 0; j < infos.length; j++) {
1274             MidiDevice device = provider.getDevice(infos[j]);
1275             if (isAppropriateDevice(device, deviceClass,
1276                                     allowSynthesizer, allowSequencer)) {
1277                 return device;
1278             }
1279         }
1280         return null;
1281     }
1282 
1283     /**
1284      * From a List of MidiDeviceProviders, return the first appropriate
1285      * MidiDevice.
1286      *
1287      * @param  providers The List of MidiDeviceProviders to search
1288      * @param  deviceClass The requested device type, one of Synthesizer.class,
1289      *         Sequencer.class, Receiver.class or Transmitter.class
1290      * @return A MidiDevice that is considered appropriate, or null if none is
1291      *         found
1292      */
1293     private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,
1294                                              Class<?> deviceClass) {
1295         MidiDevice device;
1296         // try to get MIDI port
1297         device = getFirstDevice(providers, deviceClass,
1298                                 false, false);
1299         if (device != null) {
1300             return device;
1301         }
1302 
1303         if (deviceClass == Receiver.class) {
1304             // try to get Synthesizer
1305             device = getFirstDevice(providers, deviceClass,
1306                                     true, false);
1307             if (device != null) {
1308                 return device;
1309             }
1310         }
1311 
1312         return null;
1313     }
1314 
1315     /**
1316      * From a List of MidiDeviceProviders, return the first appropriate
1317      * MidiDevice.
1318      *
1319      * @param  providers The List of MidiDeviceProviders to search
1320      * @param  deviceClass The requested device type, one of Synthesizer.class,
1321      *         Sequencer.class, Receiver.class or Transmitter.class
1322      * @return A MidiDevice that is considered appropriate, or null if none is
1323      *         found
1324      */
1325     private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,
1326                                              Class<?> deviceClass,
1327                                              boolean allowSynthesizer,
1328                                              boolean allowSequencer) {
1329         for(int i = 0; i < providers.size(); i++) {
1330             MidiDeviceProvider provider = providers.get(i);
1331             MidiDevice device = getFirstDevice(provider, deviceClass,
1332                                                allowSynthesizer,
1333                                                allowSequencer);
1334             if (device != null) {
1335                 return device;
1336             }
1337         }
1338         return null;
1339     }
1340 
1341     /**
1342      * Checks if a MidiDevice is appropriate. If deviceClass is Synthesizer or
1343      * Sequencer, a device implementing the respective interface is considered
1344      * appropriate. If deviceClass is Receiver or Transmitter, a device is
1345      * considered appropriate if it implements neither Synthesizer nor
1346      * Transmitter, and if it can provide at least one Receiver or Transmitter,
1347      * respectively.
1348      *
1349      * @param  device the MidiDevice to test
1350      * @param  allowSynthesizer if true, Synthesizers are considered
1351      *         appropriate. Otherwise only pure MidiDevices are considered
1352      *         appropriate (unless allowSequencer is true). This flag only has
1353      *         an effect for deviceClass Receiver and Transmitter. For other
1354      *         device classes (Sequencer and Synthesizer), this flag has no
1355      *         effect.
1356      * @param  allowSequencer if true, Sequencers are considered appropriate.
1357      *         Otherwise only pure MidiDevices are considered appropriate
1358      *         (unless allowSynthesizer is true). This flag only has an effect
1359      *         for deviceClass Receiver and Transmitter. For other device
1360      *         classes (Sequencer and Synthesizer), this flag has no effect.
1361      * @return true if the device is considered appropriate according to the
1362      *         rules given above, false otherwise
1363      */
1364     private static boolean isAppropriateDevice(MidiDevice device,
1365                                                Class<?> deviceClass,
1366                                                boolean allowSynthesizer,
1367                                                boolean allowSequencer) {
1368         if (deviceClass.isInstance(device)) {
1369            // This clause is for deviceClass being either Synthesizer
1370             // or Sequencer.
1371             return true;
1372         } else {
1373             // Now the case that deviceClass is Transmitter or
1374             // Receiver. If neither allowSynthesizer nor allowSequencer is
1375             // true, we require device instances to be
1376             // neither Synthesizer nor Sequencer, since we only want
1377             // devices representing MIDI ports.
1378             // Otherwise, the respective type is accepted, too
1379             if ( (! (device instanceof Sequencer) &&
1380                   ! (device instanceof Synthesizer) ) ||
1381                  ((device instanceof Sequencer) && allowSequencer) ||
1382                  ((device instanceof Synthesizer) && allowSynthesizer)) {
1383                 // And of cource, the device has to be able to provide
1384                 // Receivers or Transmitters.
1385                 if ((deviceClass == Receiver.class &&
1386                      device.getMaxReceivers() != 0) ||
1387                     (deviceClass == Transmitter.class &&
1388                      device.getMaxTransmitters() != 0)) {
1389                     return true;
1390                 }
1391             }
1392         }
1393         return false;
1394     }
1395 

1396     /**
1397      * Obtains the set of services currently installed on the system using the
1398      * SPI mechanism in 1.3.
1399      *
1400      * @return a List of instances of providers for the requested service. If no
1401      *         providers are available, a List of length 0 will be returned.
1402      */
1403      private static List<?> getProviders(Class<?> providerClass) {
1404          return JDK13Services.getProviders(providerClass);
1405     }
1406 }