1 /*
   2  * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package 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 {
 458                 Synthesizer synth = getSynthesizer();
 459                 if (synth instanceof ReferenceCountingDevice) {
 460                     rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting();
 461                 } else {
 462                     synth.open();
 463                     try {
 464                         rec = synth.getReceiver();
 465                     } finally {
 466                         // make sure that the synth is properly closed
 467                         if (rec == null) {
 468                             synth.close();
 469                         }
 470                     }
 471                 }
 472             } catch (MidiUnavailableException e) {
 473                 // something went wrong with synth
 474                 if (e instanceof MidiUnavailableException) {
 475                     mue = e;
 476                 }
 477             }
 478             if (rec == null) {
 479                 // then try to connect to the default Receiver
 480                 try {
 481                     rec = MidiSystem.getReceiver();
 482                 } catch (Exception e) {
 483                     // something went wrong. Nothing to do then!
 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                 }
1150             }
1151         }
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 }