1 /*
   2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package 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 properties file is &quot;lib/sound.properties&quot; in the JRE
  72  * directory. If a property exists both as a system property and in the
  73  * properties file, the system property takes precedence. If none is
  74  * specified, a suitable default is chosen among the available devices.
  75  * The syntax of the properties file is specified in
  76  * {@link java.util.Properties#load(InputStream) Properties.load}. The
  77  * following table lists the available property keys and which methods
  78  * consider them:
  79  *
  80  * <table border=0>
  81  *  <tr>
  82  *   <th>Property Key</th>
  83  *   <th>Interface</th>
  84  *   <th>Affected Method</th>
  85  *  </tr>
  86  *  <tr>
  87  *   <td><code>javax.sound.midi.Receiver</code></td>
  88  *   <td>{@link Receiver}</td>
  89  *   <td>{@link #getReceiver}</td>
  90  *  </tr>
  91  *  <tr>
  92  *   <td><code>javax.sound.midi.Sequencer</code></td>
  93  *   <td>{@link Sequencer}</td>
  94  *   <td>{@link #getSequencer}</td>
  95  *  </tr>
  96  *  <tr>
  97  *   <td><code>javax.sound.midi.Synthesizer</code></td>
  98  *   <td>{@link Synthesizer}</td>
  99  *   <td>{@link #getSynthesizer}</td>
 100  *  </tr>
 101  *  <tr>
 102  *   <td><code>javax.sound.midi.Transmitter</code></td>
 103  *   <td>{@link Transmitter}</td>
 104  *   <td>{@link #getTransmitter}</td>
 105  *  </tr>
 106  * </table>
 107  *
 108  * The property value consists of the provider class name
 109  * and the device name, separated by the hash mark (&quot;#&quot;).
 110  * The provider class name is the fully-qualified
 111  * name of a concrete {@link javax.sound.midi.spi.MidiDeviceProvider
 112  * MIDI device provider} class. The device name is matched against
 113  * the <code>String</code> returned by the <code>getName</code>
 114  * method of <code>MidiDevice.Info</code>.
 115  * Either the class name, or the device name may be omitted.
 116  * If only the class name is specified, the trailing hash mark
 117  * is optional.
 118  *
 119  * <p>If the provider class is specified, and it can be
 120  * successully retrieved from the installed providers,
 121  * the list of
 122  * <code>MidiDevice.Info</code> objects is retrieved
 123  * from the provider. Otherwise, or when these devices
 124  * do not provide a subsequent match, the list is retrieved
 125  * from {@link #getMidiDeviceInfo} to contain
 126  * all available <code>MidiDevice.Info</code> objects.
 127  *
 128  * <p>If a device name is specified, the resulting list of
 129  * <code>MidiDevice.Info</code> objects is searched:
 130  * the first one with a matching name, and whose
 131  * <code>MidiDevice</code> implements the
 132  * respective interface, will be returned.
 133  * If no matching <code>MidiDevice.Info</code> object
 134  * is found, or the device name is not specified,
 135  * the first suitable device from the resulting
 136  * list will be returned. For Sequencer and Synthesizer,
 137  * a device is suitable if it implements the respective
 138  * interface; whereas for Receiver and Transmitter, a device is
 139  * suitable if it
 140  * implements neither Sequencer nor Synthesizer and provides
 141  * at least one Receiver or Transmitter, respectively.
 142  *
 143  * For example, the property <code>javax.sound.midi.Receiver</code>
 144  * with a value
 145  * <code>&quot;com.sun.media.sound.MidiProvider#SunMIDI1&quot;</code>
 146  * will have the following consequences when
 147  * <code>getReceiver</code> is called:
 148  * if the class <code>com.sun.media.sound.MidiProvider</code> exists
 149  * in the list of installed MIDI device providers,
 150  * the first <code>Receiver</code> device with name
 151  * <code>&quot;SunMIDI1&quot;</code> will be returned. If it cannot
 152  * be found, the first <code>Receiver</code> from that provider
 153  * will be returned, regardless of name.
 154  * If there is none, the first <code>Receiver</code> with name
 155  * <code>&quot;SunMIDI1&quot;</code> in the list of all devices
 156  * (as returned by <code>getMidiDeviceInfo</code>) will be returned,
 157  * or, if not found, the first <code>Receiver</code> that can
 158  * be found in the list of all devices is returned.
 159  * If that fails, too, a <code>MidiUnavailableException</code>
 160  * is thrown.
 161  *
 162  * @author Kara Kytle
 163  * @author Florian Bomers
 164  * @author Matthias Pfisterer
 165  */
 166 public class MidiSystem {
 167 
 168     /**
 169      * Private no-args constructor for ensuring against instantiation.
 170      */
 171     private MidiSystem() {
 172     }
 173 
 174 
 175     /**
 176      * Obtains an array of information objects representing
 177      * the set of all MIDI devices available on the system.
 178      * A returned information object can then be used to obtain the
 179      * corresponding device object, by invoking
 180      * {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}.
 181      *
 182      * @return an array of <code>MidiDevice.Info</code> objects, one
 183      * for each installed MIDI device.  If no such devices are installed,
 184      * an array of length 0 is returned.
 185      */
 186     public static MidiDevice.Info[] getMidiDeviceInfo() {
 187         List allInfos = new ArrayList();
 188         List providers = getMidiDeviceProviders();
 189 
 190         for(int i = 0; i < providers.size(); i++) {
 191             MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);
 192             MidiDevice.Info[] tmpinfo = provider.getDeviceInfo();
 193             for (int j = 0; j < tmpinfo.length; j++) {
 194                 allInfos.add( tmpinfo[j] );
 195             }
 196         }
 197         MidiDevice.Info[] infosArray = (MidiDevice.Info[]) allInfos.toArray(new MidiDevice.Info[0]);
 198         return infosArray;
 199     }
 200 
 201 
 202     /**
 203      * Obtains the requested MIDI device.
 204      *
 205      * @param info a device information object representing the desired device.
 206      * @return the requested device
 207      * @throws MidiUnavailableException if the requested device is not available
 208      * due to resource restrictions
 209      * @throws IllegalArgumentException if the info object does not represent
 210      * a MIDI device installed on the system
 211      * @see #getMidiDeviceInfo
 212      */
 213     public static MidiDevice getMidiDevice(MidiDevice.Info info) throws MidiUnavailableException {
 214         List providers = getMidiDeviceProviders();
 215 
 216         for(int i = 0; i < providers.size(); i++) {
 217             MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);
 218             if (provider.isDeviceSupported(info)) {
 219                 MidiDevice device = provider.getDevice(info);
 220                 return device;
 221             }
 222         }
 223         throw new IllegalArgumentException("Requested device not installed: " + info);
 224     }
 225 
 226 
 227     /**
 228      * Obtains a MIDI receiver from an external MIDI port
 229      * or other default device.
 230      * The returned receiver always implements
 231      * the {@code MidiDeviceReceiver} interface.
 232      *
 233      * <p>If the system property
 234      * <code>javax.sound.midi.Receiver</code>
 235      * is defined or it is defined in the file &quot;sound.properties&quot;,
 236      * it is used to identify the device that provides the default receiver.
 237      * For details, refer to the {@link MidiSystem class description}.
 238      *
 239      * If a suitable MIDI port is not available, the Receiver is
 240      * retrieved from an installed synthesizer.
 241      *
 242      * <p>If this method returns successfully, the {@link
 243      * javax.sound.midi.MidiDevice MidiDevice} the
 244      * <code>Receiver</code> belongs to is opened implicitly, if it is
 245      * not already open. It is possible to close an implicitly opened
 246      * device by calling {@link javax.sound.midi.Receiver#close close}
 247      * on the returned <code>Receiver</code>. All open <code>Receiver</code>
 248      * instances have to be closed in order to release system resources
 249      * hold by the <code>MidiDevice</code>. For a
 250      * detailed description of open/close behaviour see the class
 251      * description of {@link javax.sound.midi.MidiDevice MidiDevice}.
 252      *
 253      *
 254      * @return the default MIDI receiver
 255      * @throws MidiUnavailableException if the default receiver is not
 256      *         available due to resource restrictions,
 257      *         or no device providing receivers is installed in the system
 258      */
 259     public static Receiver getReceiver() throws MidiUnavailableException {
 260         // may throw MidiUnavailableException
 261         MidiDevice device = getDefaultDeviceWrapper(Receiver.class);
 262         Receiver receiver;
 263         if (device instanceof ReferenceCountingDevice) {
 264             receiver = ((ReferenceCountingDevice) device).getReceiverReferenceCounting();
 265         } else {
 266             receiver = device.getReceiver();
 267         }
 268         if (!(receiver instanceof MidiDeviceReceiver)) {
 269             receiver = new MidiDeviceReceiverEnvelope(device, receiver);
 270         }
 271         return receiver;
 272     }
 273 
 274 
 275     /**
 276      * Obtains a MIDI transmitter from an external MIDI port
 277      * or other default source.
 278      * The returned transmitter always implements
 279      * the {@code MidiDeviceTransmitter} interface.
 280      *
 281      * <p>If the system property
 282      * <code>javax.sound.midi.Transmitter</code>
 283      * is defined or it is defined in the file &quot;sound.properties&quot;,
 284      * it is used to identify the device that provides the default transmitter.
 285      * For details, refer to the {@link MidiSystem class description}.
 286      *
 287      * If this method returns successfully, the {@link
 288      * javax.sound.midi.MidiDevice MidiDevice} the
 289      * <code>Transmitter</code> belongs to is opened implicitly, if it
 290      * is not already open. It is possible to close an implicitly
 291      * opened device by calling {@link
 292      * javax.sound.midi.Transmitter#close close} on the returned
 293      * <code>Transmitter</code>. All open <code>Transmitter</code>
 294      * instances have to be closed in order to release system resources
 295      * hold by the <code>MidiDevice</code>. For a detailed description
 296      * of open/close behaviour see the class description of {@link
 297      * javax.sound.midi.MidiDevice MidiDevice}.
 298      *
 299      * @return the default MIDI transmitter
 300      * @throws MidiUnavailableException if the default transmitter is not
 301      *         available due to resource restrictions,
 302      *         or no device providing transmitters is installed in the system
 303      */
 304     public static Transmitter getTransmitter() throws MidiUnavailableException {
 305         // may throw MidiUnavailableException
 306         MidiDevice device = getDefaultDeviceWrapper(Transmitter.class);
 307         Transmitter transmitter;
 308         if (device instanceof ReferenceCountingDevice) {
 309             transmitter = ((ReferenceCountingDevice) device).getTransmitterReferenceCounting();
 310         } else {
 311             transmitter = device.getTransmitter();
 312         }
 313         if (!(transmitter instanceof MidiDeviceReceiver)) {
 314             transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter);
 315         }
 316         return transmitter;
 317     }
 318 
 319 
 320     /**
 321      * Obtains the default synthesizer.
 322      *
 323      * <p>If the system property
 324      * <code>javax.sound.midi.Synthesizer</code>
 325      * is defined or it is defined in the file &quot;sound.properties&quot;,
 326      * it is used to identify the default synthesizer.
 327      * For details, refer to the {@link MidiSystem class description}.
 328      *
 329      * @return the default synthesizer
 330      * @throws MidiUnavailableException if the synthesizer is not
 331      *         available due to resource restrictions,
 332      *         or no synthesizer is installed in the system
 333      */
 334     public static Synthesizer getSynthesizer() throws MidiUnavailableException {
 335         // may throw MidiUnavailableException
 336         return (Synthesizer) getDefaultDeviceWrapper(Synthesizer.class);
 337     }
 338 
 339 
 340     /**
 341      * Obtains the default <code>Sequencer</code>, connected to
 342      * a default device.
 343      * The returned <code>Sequencer</code> instance is
 344      * connected to the default <code>Synthesizer</code>,
 345      * as returned by {@link #getSynthesizer}.
 346      * If there is no <code>Synthesizer</code>
 347      * available, or the default <code>Synthesizer</code>
 348      * cannot be opened, the <code>sequencer</code> is connected
 349      * to the default <code>Receiver</code>, as returned
 350      * by {@link #getReceiver}.
 351      * The connection is made by retrieving a <code>Transmitter</code>
 352      * instance from the <code>Sequencer</code> and setting its
 353      * <code>Receiver</code>.
 354      * Closing and re-opening the sequencer will restore the
 355      * connection to the default device.
 356      *
 357      * <p>This method is equivalent to calling
 358      * <code>getSequencer(true)</code>.
 359      *
 360      * <p>If the system property
 361      * <code>javax.sound.midi.Sequencer</code>
 362      * is defined or it is defined in the file &quot;sound.properties&quot;,
 363      * it is used to identify the default sequencer.
 364      * For details, refer to the {@link MidiSystem class description}.
 365      *
 366      * @return the default sequencer, connected to a default Receiver
 367      * @throws MidiUnavailableException if the sequencer is not
 368      *         available due to resource restrictions,
 369      *         or there is no <code>Receiver</code> available by any
 370      *         installed <code>MidiDevice</code>,
 371      *         or no sequencer is installed in the system.
 372      * @see #getSequencer(boolean)
 373      * @see #getSynthesizer
 374      * @see #getReceiver
 375      */
 376     public static Sequencer getSequencer() throws MidiUnavailableException {
 377         return getSequencer(true);
 378     }
 379 
 380 
 381 
 382     /**
 383      * Obtains the default <code>Sequencer</code>, optionally
 384      * connected to a default device.
 385      *
 386      * <p>If <code>connected</code> is true, the returned
 387      * <code>Sequencer</code> instance is
 388      * connected to the default <code>Synthesizer</code>,
 389      * as returned by {@link #getSynthesizer}.
 390      * If there is no <code>Synthesizer</code>
 391      * available, or the default <code>Synthesizer</code>
 392      * cannot be opened, the <code>sequencer</code> is connected
 393      * to the default <code>Receiver</code>, as returned
 394      * by {@link #getReceiver}.
 395      * The connection is made by retrieving a <code>Transmitter</code>
 396      * instance from the <code>Sequencer</code> and setting its
 397      * <code>Receiver</code>.
 398      * Closing and re-opening the sequencer will restore the
 399      * connection to the default device.
 400      *
 401      * <p>If <code>connected</code> is false, the returned
 402      * <code>Sequencer</code> instance is not connected, it
 403      * has no open <code>Transmitters</code>. In order to
 404      * play the sequencer on a MIDI device, or a <code>Synthesizer</code>,
 405      * it is necessary to get a <code>Transmitter</code> and set its
 406      * <code>Receiver</code>.
 407      *
 408      * <p>If the system property
 409      * <code>javax.sound.midi.Sequencer</code>
 410      * is defined or it is defined in the file "sound.properties",
 411      * it is used to identify the default sequencer.
 412      * For details, refer to the {@link MidiSystem class description}.
 413      *
 414      * @return the default sequencer
 415      * @throws MidiUnavailableException if the sequencer is not
 416      *         available due to resource restrictions,
 417      *         or no sequencer is installed in the system,
 418      *         or if <code>connected</code> is true, and there is
 419      *         no <code>Receiver</code> available by any installed
 420      *         <code>MidiDevice</code>
 421      * @see #getSynthesizer
 422      * @see #getReceiver
 423      * @since 1.5
 424      */
 425     public static Sequencer getSequencer(boolean connected)
 426         throws MidiUnavailableException {
 427         Sequencer seq = (Sequencer) getDefaultDeviceWrapper(Sequencer.class);
 428 
 429         if (connected) {
 430             // IMPORTANT: this code needs to be synch'ed with
 431             //            all AutoConnectSequencer instances,
 432             //            (e.g. RealTimeSequencer) because the
 433             //            same algorithm for synth retrieval
 434             //            needs to be used!
 435 
 436             Receiver rec = null;
 437             MidiUnavailableException mue = null;
 438 
 439             // first try to connect to the default synthesizer
 440             try {
 441                 Synthesizer synth = getSynthesizer();
 442                 if (synth instanceof ReferenceCountingDevice) {
 443                     rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting();
 444                     // only use MixerSynth if it could successfully load a soundbank
 445                     if (synth.getClass().toString().contains("com.sun.media.sound.MixerSynth")
 446                         && (synth.getDefaultSoundbank() == null)) {
 447                         // don't use this receiver if no soundbank available
 448                         rec = null;
 449                         synth.close();
 450                     }
 451                 } else {
 452                     synth.open();
 453                     try {
 454                         rec = synth.getReceiver();
 455                     } finally {
 456                         // make sure that the synth is properly closed
 457                         if (rec == null) {
 458                             synth.close();
 459                         }
 460                     }
 461                 }
 462             } catch (MidiUnavailableException e) {
 463                 // something went wrong with synth
 464                 if (e instanceof MidiUnavailableException) {
 465                     mue = (MidiUnavailableException) e;
 466                 }
 467             }
 468             if (rec == null) {
 469                 // then try to connect to the default Receiver
 470                 try {
 471                     rec = MidiSystem.getReceiver();
 472                 } catch (Exception e) {
 473                     // something went wrong. Nothing to do then!
 474                     if (e instanceof MidiUnavailableException) {
 475                         mue = (MidiUnavailableException) e;
 476                     }
 477                 }
 478             }
 479             if (rec != null) {
 480                 seq.getTransmitter().setReceiver(rec);
 481                 if (seq instanceof AutoConnectSequencer) {
 482                     ((AutoConnectSequencer) seq).setAutoConnect(rec);
 483                 }
 484             } else {
 485                 if (mue != null) {
 486                     throw mue;
 487                 }
 488                 throw new MidiUnavailableException("no receiver available");
 489             }
 490         }
 491         return seq;
 492     }
 493 
 494 
 495 
 496 
 497     /**
 498      * Constructs a MIDI sound bank by reading it from the specified stream.
 499      * The stream must point to
 500      * a valid MIDI soundbank file.  In general, MIDI soundbank providers may
 501      * need to read some data from the stream before determining whether they
 502      * support it.  These parsers must
 503      * be able to mark the stream, read enough data to determine whether they
 504      * support the stream, and, if not, reset the stream's read pointer to
 505      * its original position.  If the input stream does not support this,
 506      * this method may fail with an IOException.
 507      * @param stream the source of the sound bank data.
 508      * @return the sound bank
 509      * @throws InvalidMidiDataException if the stream does not point to
 510      * valid MIDI soundbank data recognized by the system
 511      * @throws IOException if an I/O error occurred when loading the soundbank
 512      * @see InputStream#markSupported
 513      * @see InputStream#mark
 514      */
 515     public static Soundbank getSoundbank(InputStream stream)
 516         throws InvalidMidiDataException, IOException {
 517 
 518         SoundbankReader sp = null;
 519         Soundbank s = null;
 520 
 521         List providers = getSoundbankReaders();
 522 
 523         for(int i = 0; i < providers.size(); i++) {
 524             sp = (SoundbankReader)providers.get(i);
 525             s = sp.getSoundbank(stream);
 526 
 527             if( s!= null) {
 528                 return s;
 529             }
 530         }
 531         throw new InvalidMidiDataException("cannot get soundbank from stream");
 532 
 533     }
 534 
 535 
 536     /**
 537      * Constructs a <code>Soundbank</code> by reading it from the specified URL.
 538      * The URL must point to a valid MIDI soundbank file.
 539      *
 540      * @param url the source of the sound bank data
 541      * @return the sound bank
 542      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 543      * soundbank data recognized by the system
 544      * @throws IOException if an I/O error occurred when loading the soundbank
 545      */
 546     public static Soundbank getSoundbank(URL url)
 547         throws InvalidMidiDataException, IOException {
 548 
 549         SoundbankReader sp = null;
 550         Soundbank s = null;
 551 
 552         List providers = getSoundbankReaders();
 553 
 554         for(int i = 0; i < providers.size(); i++) {
 555             sp = (SoundbankReader)providers.get(i);
 556             s = sp.getSoundbank(url);
 557 
 558             if( s!= null) {
 559                 return s;
 560             }
 561         }
 562         throw new InvalidMidiDataException("cannot get soundbank from stream");
 563 
 564     }
 565 
 566 
 567     /**
 568      * Constructs a <code>Soundbank</code> by reading it from the specified
 569      * <code>File</code>.
 570      * The <code>File</code> must point to a valid MIDI soundbank file.
 571      *
 572      * @param file the source of the sound bank data
 573      * @return the sound bank
 574      * @throws InvalidMidiDataException if the <code>File</code> does not
 575      * point to valid MIDI soundbank data recognized by the system
 576      * @throws IOException if an I/O error occurred when loading the soundbank
 577      */
 578     public static Soundbank getSoundbank(File file)
 579         throws InvalidMidiDataException, IOException {
 580 
 581         SoundbankReader sp = null;
 582         Soundbank s = null;
 583 
 584         List providers = getSoundbankReaders();
 585 
 586         for(int i = 0; i < providers.size(); i++) {
 587             sp = (SoundbankReader)providers.get(i);
 588             s = sp.getSoundbank(file);
 589 
 590             if( s!= null) {
 591                 return s;
 592             }
 593         }
 594         throw new InvalidMidiDataException("cannot get soundbank from stream");
 595     }
 596 
 597 
 598 
 599     /**
 600      * Obtains the MIDI file format of the data in the specified input stream.
 601      * The stream must point to valid MIDI file data for a file type recognized
 602      * by the system.
 603      * <p>
 604      * This method and/or the code it invokes may need to read some data from
 605      * the stream to determine whether its data format is supported.  The
 606      * implementation may therefore
 607      * need to mark the stream, read enough data to determine whether it is in
 608      * a supported format, and reset the stream's read pointer to its original
 609      * position.  If the input stream does not permit this set of operations,
 610      * this method may fail with an <code>IOException</code>.
 611      * <p>
 612      * This operation can only succeed for files of a type which can be parsed
 613      * by an installed file reader.  It may fail with an InvalidMidiDataException
 614      * even for valid files if no compatible file reader is installed.  It
 615      * will also fail with an InvalidMidiDataException if a compatible file reader
 616      * is installed, but encounters errors while determining the file format.
 617      *
 618      * @param stream the input stream from which file format information
 619      * should be extracted
 620      * @return an <code>MidiFileFormat</code> object describing the MIDI file
 621      * format
 622      * @throws InvalidMidiDataException if the stream does not point to valid
 623      * MIDI file data recognized by the system
 624      * @throws IOException if an I/O exception occurs while accessing the
 625      * stream
 626      * @see #getMidiFileFormat(URL)
 627      * @see #getMidiFileFormat(File)
 628      * @see InputStream#markSupported
 629      * @see InputStream#mark
 630      */
 631     public static MidiFileFormat getMidiFileFormat(InputStream stream)
 632         throws InvalidMidiDataException, IOException {
 633 
 634         List providers = getMidiFileReaders();
 635         MidiFileFormat format = null;
 636 
 637         for(int i = 0; i < providers.size(); i++) {
 638             MidiFileReader reader = (MidiFileReader) providers.get(i);
 639             try {
 640                 format = reader.getMidiFileFormat( stream ); // throws IOException
 641                 break;
 642             } catch (InvalidMidiDataException e) {
 643                 continue;
 644             }
 645         }
 646 
 647         if( format==null ) {
 648             throw new InvalidMidiDataException("input stream is not a supported file type");
 649         } else {
 650             return format;
 651         }
 652     }
 653 
 654 
 655     /**
 656      * Obtains the MIDI file format of the data in the specified URL.  The URL
 657      * must point to valid MIDI file data for a file type recognized
 658      * by the system.
 659      * <p>
 660      * This operation can only succeed for files of a type which can be parsed
 661      * by an installed file reader.  It may fail with an InvalidMidiDataException
 662      * even for valid files if no compatible file reader is installed.  It
 663      * will also fail with an InvalidMidiDataException if a compatible file reader
 664      * is installed, but encounters errors while determining the file format.
 665      *
 666      * @param url the URL from which file format information should be
 667      * extracted
 668      * @return a <code>MidiFileFormat</code> object describing the MIDI file
 669      * format
 670      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 671      * file data recognized by the system
 672      * @throws IOException if an I/O exception occurs while accessing the URL
 673      *
 674      * @see #getMidiFileFormat(InputStream)
 675      * @see #getMidiFileFormat(File)
 676      */
 677     public static MidiFileFormat getMidiFileFormat(URL url)
 678         throws InvalidMidiDataException, IOException {
 679 
 680         List providers = getMidiFileReaders();
 681         MidiFileFormat format = null;
 682 
 683         for(int i = 0; i < providers.size(); i++) {
 684             MidiFileReader reader = (MidiFileReader) providers.get(i);
 685             try {
 686                 format = reader.getMidiFileFormat( url ); // throws IOException
 687                 break;
 688             } catch (InvalidMidiDataException e) {
 689                 continue;
 690             }
 691         }
 692 
 693         if( format==null ) {
 694             throw new InvalidMidiDataException("url is not a supported file type");
 695         } else {
 696             return format;
 697         }
 698     }
 699 
 700 
 701     /**
 702      * Obtains the MIDI file format of the specified <code>File</code>.  The
 703      * <code>File</code> must point to valid MIDI file data for a file type
 704      * recognized by the system.
 705      * <p>
 706      * This operation can only succeed for files of a type which can be parsed
 707      * by an installed file reader.  It may fail with an InvalidMidiDataException
 708      * even for valid files if no compatible file reader is installed.  It
 709      * will also fail with an InvalidMidiDataException if a compatible file reader
 710      * is installed, but encounters errors while determining the file format.
 711      *
 712      * @param file the <code>File</code> from which file format information
 713      * should be extracted
 714      * @return a <code>MidiFileFormat</code> object describing the MIDI file
 715      * format
 716      * @throws InvalidMidiDataException if the <code>File</code> does not point
 717      *  to valid MIDI file data recognized by the system
 718      * @throws IOException if an I/O exception occurs while accessing the file
 719      *
 720      * @see #getMidiFileFormat(InputStream)
 721      * @see #getMidiFileFormat(URL)
 722      */
 723     public static MidiFileFormat getMidiFileFormat(File file)
 724         throws InvalidMidiDataException, IOException {
 725 
 726         List providers = getMidiFileReaders();
 727         MidiFileFormat format = null;
 728 
 729         for(int i = 0; i < providers.size(); i++) {
 730             MidiFileReader reader = (MidiFileReader) providers.get(i);
 731             try {
 732                 format = reader.getMidiFileFormat( file ); // throws IOException
 733                 break;
 734             } catch (InvalidMidiDataException e) {
 735                 continue;
 736             }
 737         }
 738 
 739         if( format==null ) {
 740             throw new InvalidMidiDataException("file is not a supported file type");
 741         } else {
 742             return format;
 743         }
 744     }
 745 
 746 
 747     /**
 748      * Obtains a MIDI sequence from the specified input stream.  The stream must
 749      * point to valid MIDI file data for a file type recognized
 750      * by the system.
 751      * <p>
 752      * This method and/or the code it invokes may need to read some data
 753      * from the stream to determine whether
 754      * its data format is supported.  The implementation may therefore
 755      * need to mark the stream, read enough data to determine whether it is in
 756      * a supported format, and reset the stream's read pointer to its original
 757      * position.  If the input stream does not permit this set of operations,
 758      * this method may fail with an <code>IOException</code>.
 759      * <p>
 760      * This operation can only succeed for files of a type which can be parsed
 761      * by an installed file reader.  It may fail with an InvalidMidiDataException
 762      * even for valid files if no compatible file reader is installed.  It
 763      * will also fail with an InvalidMidiDataException if a compatible file reader
 764      * is installed, but encounters errors while constructing the <code>Sequence</code>
 765      * object from the file data.
 766      *
 767      * @param stream the input stream from which the <code>Sequence</code>
 768      * should be constructed
 769      * @return a <code>Sequence</code> object based on the MIDI file data
 770      * contained in the input stream
 771      * @throws InvalidMidiDataException if the stream does not point to
 772      * valid MIDI file data recognized by the system
 773      * @throws IOException if an I/O exception occurs while accessing the
 774      * stream
 775      * @see InputStream#markSupported
 776      * @see InputStream#mark
 777      */
 778     public static Sequence getSequence(InputStream stream)
 779         throws InvalidMidiDataException, IOException {
 780 
 781         List providers = getMidiFileReaders();
 782         Sequence sequence = null;
 783 
 784         for(int i = 0; i < providers.size(); i++) {
 785             MidiFileReader reader = (MidiFileReader) providers.get(i);
 786             try {
 787                 sequence = reader.getSequence( stream ); // throws IOException
 788                 break;
 789             } catch (InvalidMidiDataException e) {
 790                 continue;
 791             }
 792         }
 793 
 794         if( sequence==null ) {
 795             throw new InvalidMidiDataException("could not get sequence from input stream");
 796         } else {
 797             return sequence;
 798         }
 799     }
 800 
 801 
 802     /**
 803      * Obtains a MIDI sequence from the specified URL.  The URL must
 804      * point to valid MIDI file data for a file type recognized
 805      * by the system.
 806      * <p>
 807      * This operation can only succeed for files of a type which can be parsed
 808      * by an installed file reader.  It may fail with an InvalidMidiDataException
 809      * even for valid files if no compatible file reader is installed.  It
 810      * will also fail with an InvalidMidiDataException if a compatible file reader
 811      * is installed, but encounters errors while constructing the <code>Sequence</code>
 812      * object from the file data.
 813      *
 814      * @param url the URL from which the <code>Sequence</code> should be
 815      * constructed
 816      * @return a <code>Sequence</code> object based on the MIDI file data
 817      * pointed to by the URL
 818      * @throws InvalidMidiDataException if the URL does not point to valid MIDI
 819      * file data recognized by the system
 820      * @throws IOException if an I/O exception occurs while accessing the URL
 821      */
 822     public static Sequence getSequence(URL url)
 823         throws InvalidMidiDataException, IOException {
 824 
 825         List providers = getMidiFileReaders();
 826         Sequence sequence = null;
 827 
 828         for(int i = 0; i < providers.size(); i++) {
 829             MidiFileReader reader = (MidiFileReader) providers.get(i);
 830             try {
 831                 sequence = reader.getSequence( url ); // throws IOException
 832                 break;
 833             } catch (InvalidMidiDataException e) {
 834                 continue;
 835             }
 836         }
 837 
 838         if( sequence==null ) {
 839             throw new InvalidMidiDataException("could not get sequence from URL");
 840         } else {
 841             return sequence;
 842         }
 843     }
 844 
 845 
 846     /**
 847      * Obtains a MIDI sequence from the specified <code>File</code>.
 848      * The <code>File</code> must point to valid MIDI file data
 849      * for a file type recognized by the system.
 850      * <p>
 851      * This operation can only succeed for files of a type which can be parsed
 852      * by an installed file reader.  It may fail with an InvalidMidiDataException
 853      * even for valid files if no compatible file reader is installed.  It
 854      * will also fail with an InvalidMidiDataException if a compatible file reader
 855      * is installed, but encounters errors while constructing the <code>Sequence</code>
 856      * object from the file data.
 857      *
 858      * @param file the <code>File</code> from which the <code>Sequence</code>
 859      * should be constructed
 860      * @return a <code>Sequence</code> object based on the MIDI file data
 861      * pointed to by the File
 862      * @throws InvalidMidiDataException if the File does not point to valid MIDI
 863      * file data recognized by the system
 864      * @throws IOException if an I/O exception occurs
 865      */
 866     public static Sequence getSequence(File file)
 867         throws InvalidMidiDataException, IOException {
 868 
 869         List providers = getMidiFileReaders();
 870         Sequence sequence = null;
 871 
 872         for(int i = 0; i < providers.size(); i++) {
 873             MidiFileReader reader = (MidiFileReader) providers.get(i);
 874             try {
 875                 sequence = reader.getSequence( file ); // throws IOException
 876                 break;
 877             } catch (InvalidMidiDataException e) {
 878                 continue;
 879             }
 880         }
 881 
 882         if( sequence==null ) {
 883             throw new InvalidMidiDataException("could not get sequence from file");
 884         } else {
 885             return sequence;
 886         }
 887     }
 888 
 889 
 890     /**
 891      * Obtains the set of MIDI file types for which file writing support is
 892      * provided by the system.
 893      * @return array of unique file types.  If no file types are supported,
 894      * an array of length 0 is returned.
 895      */
 896     public static int[] getMidiFileTypes() {
 897 
 898         List providers = getMidiFileWriters();
 899         Set allTypes = new HashSet();
 900 
 901         // gather from all the providers
 902 
 903         for (int i = 0; i < providers.size(); i++ ) {
 904             MidiFileWriter writer = (MidiFileWriter) providers.get(i);
 905             int[] types = writer.getMidiFileTypes();
 906             for (int j = 0; j < types.length; j++ ) {
 907                 allTypes.add(new Integer(types[j]));
 908             }
 909         }
 910         int resultTypes[] = new int[allTypes.size()];
 911         int index = 0;
 912         Iterator iterator = allTypes.iterator();
 913         while (iterator.hasNext()) {
 914             Integer integer = (Integer) iterator.next();
 915             resultTypes[index++] = integer.intValue();
 916         }
 917         return resultTypes;
 918     }
 919 
 920 
 921     /**
 922      * Indicates whether file writing support for the specified MIDI file type
 923      * is provided by the system.
 924      * @param fileType the file type for which write capabilities are queried
 925      * @return <code>true</code> if the file type is supported,
 926      * otherwise <code>false</code>
 927      */
 928     public static boolean isFileTypeSupported(int fileType) {
 929 
 930         List providers = getMidiFileWriters();
 931 
 932         for (int i = 0; i < providers.size(); i++ ) {
 933             MidiFileWriter writer = (MidiFileWriter) providers.get(i);
 934             if( writer.isFileTypeSupported(fileType)) {
 935                 return true;
 936             }
 937         }
 938         return false;
 939     }
 940 
 941 
 942     /**
 943      * Obtains the set of MIDI file types that the system can write from the
 944      * sequence specified.
 945      * @param sequence the sequence for which MIDI file type support
 946      * is queried
 947      * @return the set of unique supported file types.  If no file types are supported,
 948      * returns an array of length 0.
 949      */
 950     public static int[] getMidiFileTypes(Sequence sequence) {
 951 
 952         List providers = getMidiFileWriters();
 953         Set allTypes = new HashSet();
 954 
 955         // gather from all the providers
 956 
 957         for (int i = 0; i < providers.size(); i++ ) {
 958             MidiFileWriter writer = (MidiFileWriter) providers.get(i);
 959             int[] types = writer.getMidiFileTypes(sequence);
 960             for (int j = 0; j < types.length; j++ ) {
 961                 allTypes.add(new Integer(types[j]));
 962             }
 963         }
 964         int resultTypes[] = new int[allTypes.size()];
 965         int index = 0;
 966         Iterator iterator = allTypes.iterator();
 967         while (iterator.hasNext()) {
 968             Integer integer = (Integer) iterator.next();
 969             resultTypes[index++] = integer.intValue();
 970         }
 971         return resultTypes;
 972     }
 973 
 974 
 975     /**
 976      * Indicates whether a MIDI file of the file type specified can be written
 977      * from the sequence indicated.
 978      * @param fileType the file type for which write capabilities
 979      * are queried
 980      * @param sequence the sequence for which file writing support is queried
 981      * @return <code>true</code> if the file type is supported for this
 982      * sequence, otherwise <code>false</code>
 983      */
 984     public static boolean isFileTypeSupported(int fileType, Sequence sequence) {
 985 
 986         List providers = getMidiFileWriters();
 987 
 988         for (int i = 0; i < providers.size(); i++ ) {
 989             MidiFileWriter writer = (MidiFileWriter) providers.get(i);
 990             if( writer.isFileTypeSupported(fileType,sequence)) {
 991                 return true;
 992             }
 993         }
 994         return false;
 995     }
 996 
 997 
 998     /**
 999      * Writes a stream of bytes representing a file of the MIDI file type
1000      * indicated to the output stream provided.
1001      * @param in sequence containing MIDI data to be written to the file
1002      * @param fileType the file type of the file to be written to the output stream
1003      * @param out stream to which the file data should be written
1004      * @return the number of bytes written to the output stream
1005      * @throws IOException if an I/O exception occurs
1006      * @throws IllegalArgumentException if the file format is not supported by
1007      * the system
1008      * @see #isFileTypeSupported(int, Sequence)
1009      * @see     #getMidiFileTypes(Sequence)
1010      */
1011     public static int write(Sequence in, int fileType, OutputStream out) throws IOException {
1012 
1013         List providers = getMidiFileWriters();
1014         //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
1015         int bytesWritten = -2;
1016 
1017         for (int i = 0; i < providers.size(); i++ ) {
1018             MidiFileWriter writer = (MidiFileWriter) providers.get(i);
1019             if( writer.isFileTypeSupported( fileType, in ) ) {
1020 
1021                 bytesWritten = writer.write(in, fileType, out);
1022                 break;
1023             }
1024         }
1025         if (bytesWritten == -2) {
1026             throw new IllegalArgumentException("MIDI file type is not supported");
1027         }
1028         return bytesWritten;
1029     }
1030 
1031 
1032     /**
1033      * Writes a stream of bytes representing a file of the MIDI file type
1034      * indicated to the external file provided.
1035      * @param in sequence containing MIDI data to be written to the file
1036      * @param type the file type of the file to be written to the output stream
1037      * @param out external file to which the file data should be written
1038      * @return the number of bytes written to the file
1039      * @throws IOException if an I/O exception occurs
1040      * @throws IllegalArgumentException if the file type is not supported by
1041      * the system
1042      * @see #isFileTypeSupported(int, Sequence)
1043      * @see     #getMidiFileTypes(Sequence)
1044      */
1045     public static int write(Sequence in, int type, File out) throws IOException {
1046 
1047         List providers = getMidiFileWriters();
1048         //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
1049         int bytesWritten = -2;
1050 
1051         for (int i = 0; i < providers.size(); i++ ) {
1052             MidiFileWriter writer = (MidiFileWriter) providers.get(i);
1053             if( writer.isFileTypeSupported( type, in ) ) {
1054 
1055                 bytesWritten = writer.write(in, type, out);
1056                 break;
1057             }
1058         }
1059         if (bytesWritten == -2) {
1060             throw new IllegalArgumentException("MIDI file type is not supported");
1061         }
1062         return bytesWritten;
1063     }
1064 
1065 
1066 
1067     // HELPER METHODS
1068 
1069     private static List getMidiDeviceProviders() {
1070         return getProviders(MidiDeviceProvider.class);
1071     }
1072 
1073 
1074     private static List getSoundbankReaders() {
1075         return getProviders(SoundbankReader.class);
1076     }
1077 
1078 
1079     private static List getMidiFileWriters() {
1080         return getProviders(MidiFileWriter.class);
1081     }
1082 
1083 
1084     private static List getMidiFileReaders() {
1085         return getProviders(MidiFileReader.class);
1086     }
1087 
1088 
1089     /** Attempts to locate and return a default MidiDevice of the specified
1090      * type.
1091      *
1092      * This method wraps {@link #getDefaultDevice}. It catches the
1093      * <code>IllegalArgumentException</code> thrown by
1094      * <code>getDefaultDevice</code> and instead throws a
1095      * <code>MidiUnavailableException</code>, with the catched
1096      * exception chained.
1097      *
1098      * @param deviceClass The requested device type, one of Synthesizer.class,
1099      * Sequencer.class, Receiver.class or Transmitter.class.
1100      * @throws  MidiUnavalableException on failure.
1101      */
1102     private static MidiDevice getDefaultDeviceWrapper(Class deviceClass)
1103         throws MidiUnavailableException{
1104         try {
1105             return getDefaultDevice(deviceClass);
1106         } catch (IllegalArgumentException iae) {
1107             MidiUnavailableException mae = new MidiUnavailableException();
1108             mae.initCause(iae);
1109             throw mae;
1110         }
1111     }
1112 
1113 
1114     /** Attempts to locate and return a default MidiDevice of the specified
1115      * type.
1116      *
1117      * @param deviceClass The requested device type, one of Synthesizer.class,
1118      * Sequencer.class, Receiver.class or Transmitter.class.
1119      * @throws  IllegalArgumentException on failure.
1120      */
1121     private static MidiDevice getDefaultDevice(Class deviceClass) {
1122         List providers = getMidiDeviceProviders();
1123         String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass);
1124         String instanceName = JDK13Services.getDefaultInstanceName(deviceClass);
1125         MidiDevice device;
1126 
1127         if (providerClassName != null) {
1128             MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers);
1129             if (defaultProvider != null) {
1130                 if (instanceName != null) {
1131                     device = getNamedDevice(instanceName, defaultProvider, deviceClass);
1132                     if (device != null) {
1133                         return device;
1134                     }
1135                 }
1136                 device = getFirstDevice(defaultProvider, deviceClass);
1137                 if (device != null) {
1138                     return device;
1139                 }
1140             }
1141         }
1142 
1143         /* Provider class not specified or cannot be found, or
1144            provider class specified, and no appropriate device available or
1145            provider class and instance specified and instance cannot be found or is not appropriate */
1146         if (instanceName != null) {
1147             device = getNamedDevice(instanceName, providers, deviceClass);
1148             if (device != null) {
1149                 return device;
1150             }
1151         }
1152 
1153         /* No default are specified, or if something is specified, everything
1154            failed. */
1155         device = getFirstDevice(providers, deviceClass);
1156         if (device != null) {
1157             return device;
1158         }
1159         throw new IllegalArgumentException("Requested device not installed");
1160     }
1161 
1162 
1163 
1164     /** Return a MidiDeviceProcider of a given class from the list of
1165         MidiDeviceProviders.
1166 
1167         @param providerClassName The class name of the provider to be returned.
1168         @param provider The list of MidiDeviceProviders that is searched.
1169         @return A MidiDeviceProvider of the requested class, or null if none
1170         is found.
1171     */
1172     private static MidiDeviceProvider getNamedProvider(String providerClassName, List providers) {
1173         for(int i = 0; i < providers.size(); i++) {
1174             MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);
1175             if (provider.getClass().getName().equals(providerClassName)) {
1176                 return provider;
1177             }
1178         }
1179         return null;
1180     }
1181 
1182 
1183     /** Return a MidiDevice with a given name from a given MidiDeviceProvider.
1184         @param deviceName The name of the MidiDevice to be returned.
1185         @param provider The MidiDeviceProvider to check for MidiDevices.
1186         @param deviceClass The requested device type, one of Synthesizer.class,
1187         Sequencer.class, Receiver.class or Transmitter.class.
1188 
1189         @return A MidiDevice matching the requirements, or null if none is found.
1190     */
1191     private static MidiDevice getNamedDevice(String deviceName,
1192                                              MidiDeviceProvider provider,
1193                                              Class deviceClass) {
1194         MidiDevice device;
1195         // try to get MIDI port
1196         device = getNamedDevice(deviceName, provider, deviceClass,
1197                                  false, false);
1198         if (device != null) {
1199             return device;
1200         }
1201 
1202         if (deviceClass == Receiver.class) {
1203             // try to get Synthesizer
1204             device = getNamedDevice(deviceName, provider, deviceClass,
1205                                      true, false);
1206             if (device != null) {
1207                 return device;
1208             }
1209         }
1210 
1211         return null;
1212     }
1213 
1214 
1215     /** Return a MidiDevice with a given name from a given MidiDeviceProvider.
1216       @param deviceName The name of the MidiDevice to be returned.
1217       @param provider The MidiDeviceProvider to check for MidiDevices.
1218       @param deviceClass The requested device type, one of Synthesizer.class,
1219       Sequencer.class, Receiver.class or Transmitter.class.
1220 
1221       @return A MidiDevice matching the requirements, or null if none is found.
1222      */
1223     private static MidiDevice getNamedDevice(String deviceName,
1224                                              MidiDeviceProvider provider,
1225                                              Class deviceClass,
1226                                              boolean allowSynthesizer,
1227                                              boolean allowSequencer) {
1228         MidiDevice.Info[] infos = provider.getDeviceInfo();
1229         for (int i = 0; i < infos.length; i++) {
1230             if (infos[i].getName().equals(deviceName)) {
1231                 MidiDevice device = provider.getDevice(infos[i]);
1232                 if (isAppropriateDevice(device, deviceClass,
1233                                         allowSynthesizer, allowSequencer)) {
1234                     return device;
1235                 }
1236             }
1237         }
1238         return null;
1239     }
1240 
1241 
1242     /** Return a MidiDevice with a given name from a list of
1243         MidiDeviceProviders.
1244         @param deviceName The name of the MidiDevice to be returned.
1245         @param providers The List of MidiDeviceProviders to check for
1246         MidiDevices.
1247         @param deviceClass The requested device type, one of Synthesizer.class,
1248         Sequencer.class, Receiver.class or Transmitter.class.
1249         @return A Mixer matching the requirements, or null if none is found.
1250     */
1251     private static MidiDevice getNamedDevice(String deviceName,
1252                                              List providers,
1253                                              Class deviceClass) {
1254         MidiDevice device;
1255         // try to get MIDI port
1256         device = getNamedDevice(deviceName, providers, deviceClass,
1257                                  false, false);
1258         if (device != null) {
1259             return device;
1260         }
1261 
1262         if (deviceClass == Receiver.class) {
1263             // try to get Synthesizer
1264             device = getNamedDevice(deviceName, providers, deviceClass,
1265                                      true, false);
1266             if (device != null) {
1267                 return device;
1268             }
1269         }
1270 
1271         return null;
1272     }
1273 
1274 
1275     /** Return a MidiDevice with a given name from a list of
1276         MidiDeviceProviders.
1277         @param deviceName The name of the MidiDevice to be returned.
1278         @param providers The List of MidiDeviceProviders to check for
1279         MidiDevices.
1280         @param deviceClass The requested device type, one of Synthesizer.class,
1281         Sequencer.class, Receiver.class or Transmitter.class.
1282         @return A Mixer matching the requirements, or null if none is found.
1283      */
1284     private static MidiDevice getNamedDevice(String deviceName,
1285                                              List providers,
1286                                              Class deviceClass,
1287                                              boolean allowSynthesizer,
1288                                              boolean allowSequencer) {
1289         for(int i = 0; i < providers.size(); i++) {
1290             MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);
1291             MidiDevice device = getNamedDevice(deviceName, provider,
1292                                                deviceClass,
1293                                                allowSynthesizer,
1294                                                allowSequencer);
1295             if (device != null) {
1296                 return device;
1297             }
1298         }
1299         return null;
1300     }
1301 
1302 
1303     /** From a given MidiDeviceProvider, return the first appropriate device.
1304         @param provider The MidiDeviceProvider to check for MidiDevices.
1305         @param deviceClass The requested device type, one of Synthesizer.class,
1306         Sequencer.class, Receiver.class or Transmitter.class.
1307         @return A MidiDevice is considered appropriate, or null if no
1308         appropriate device is found.
1309     */
1310     private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
1311                                              Class deviceClass) {
1312         MidiDevice device;
1313         // try to get MIDI port
1314         device = getFirstDevice(provider, deviceClass,
1315                                 false, false);
1316         if (device != null) {
1317             return device;
1318         }
1319 
1320         if (deviceClass == Receiver.class) {
1321             // try to get Synthesizer
1322             device = getFirstDevice(provider, deviceClass,
1323                                     true, false);
1324             if (device != null) {
1325                 return device;
1326             }
1327         }
1328 
1329         return null;
1330     }
1331 
1332 
1333     /** From a given MidiDeviceProvider, return the first appropriate device.
1334         @param provider The MidiDeviceProvider to check for MidiDevices.
1335         @param deviceClass The requested device type, one of Synthesizer.class,
1336         Sequencer.class, Receiver.class or Transmitter.class.
1337         @return A MidiDevice is considered appropriate, or null if no
1338         appropriate device is found.
1339      */
1340     private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
1341                                              Class deviceClass,
1342                                              boolean allowSynthesizer,
1343                                              boolean allowSequencer) {
1344         MidiDevice.Info[] infos = provider.getDeviceInfo();
1345         for (int j = 0; j < infos.length; j++) {
1346             MidiDevice device = provider.getDevice(infos[j]);
1347             if (isAppropriateDevice(device, deviceClass,
1348                                     allowSynthesizer, allowSequencer)) {
1349                 return device;
1350             }
1351         }
1352         return null;
1353     }
1354 
1355 
1356     /** From a List of MidiDeviceProviders, return the first appropriate
1357         MidiDevice.
1358         @param providers The List of MidiDeviceProviders to search.
1359         @param deviceClass The requested device type, one of Synthesizer.class,
1360         Sequencer.class, Receiver.class or Transmitter.class.
1361         @return A MidiDevice that is considered appropriate, or null
1362         if none is found.
1363     */
1364     private static MidiDevice getFirstDevice(List providers,
1365                                              Class deviceClass) {
1366         MidiDevice device;
1367         // try to get MIDI port
1368         device = getFirstDevice(providers, deviceClass,
1369                                 false, false);
1370         if (device != null) {
1371             return device;
1372         }
1373 
1374         if (deviceClass == Receiver.class) {
1375             // try to get Synthesizer
1376             device = getFirstDevice(providers, deviceClass,
1377                                     true, false);
1378             if (device != null) {
1379                 return device;
1380             }
1381         }
1382 
1383         return null;
1384     }
1385 
1386 
1387     /** From a List of MidiDeviceProviders, return the first appropriate
1388         MidiDevice.
1389         @param providers The List of MidiDeviceProviders to search.
1390         @param deviceClass The requested device type, one of Synthesizer.class,
1391         Sequencer.class, Receiver.class or Transmitter.class.
1392         @return A MidiDevice that is considered appropriate, or null
1393         if none is found.
1394      */
1395     private static MidiDevice getFirstDevice(List providers,
1396                                              Class deviceClass,
1397                                              boolean allowSynthesizer,
1398                                              boolean allowSequencer) {
1399         for(int i = 0; i < providers.size(); i++) {
1400             MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);
1401             MidiDevice device = getFirstDevice(provider, deviceClass,
1402                                                allowSynthesizer,
1403                                                allowSequencer);
1404             if (device != null) {
1405                 return device;
1406             }
1407         }
1408         return null;
1409     }
1410 
1411 
1412     /** Checks if a MidiDevice is appropriate.
1413         If deviceClass is Synthesizer or Sequencer, a device implementing
1414         the respective interface is considered appropriate. If deviceClass
1415         is Receiver or Transmitter, a device is considered appropriate if
1416         it implements neither Synthesizer nor Transmitter, and if it can
1417         provide at least one Receiver or Transmitter, respectively.
1418 
1419         @param device the MidiDevice to test
1420         @param allowSynthesizer if true, Synthesizers are considered
1421         appropriate. Otherwise only pure MidiDevices are considered
1422         appropriate (unless allowSequencer is true). This flag only has an
1423         effect for deviceClass Receiver and Transmitter. For other device
1424         classes (Sequencer and Synthesizer), this flag has no effect.
1425         @param allowSequencer if true, Sequencers are considered
1426         appropriate. Otherwise only pure MidiDevices are considered
1427         appropriate (unless allowSynthesizer is true). This flag only has an
1428         effect for deviceClass Receiver and Transmitter. For other device
1429         classes (Sequencer and Synthesizer), this flag has no effect.
1430         @return true if the device is considered appropriate according to the
1431         rules given above, false otherwise.
1432     */
1433     private static boolean isAppropriateDevice(MidiDevice device,
1434                                                Class deviceClass,
1435                                                boolean allowSynthesizer,
1436                                                boolean allowSequencer) {
1437         if (deviceClass.isInstance(device)) {
1438             // This clause is for deviceClass being either Synthesizer
1439             // or Sequencer.
1440             return true;
1441         } else {
1442             // Now the case that deviceClass is Transmitter or
1443             // Receiver. If neither allowSynthesizer nor allowSequencer is
1444             // true, we require device instances to be
1445             // neither Synthesizer nor Sequencer, since we only want
1446             // devices representing MIDI ports.
1447             // Otherwise, the respective type is accepted, too
1448             if ( (! (device instanceof Sequencer) &&
1449                   ! (device instanceof Synthesizer) ) ||
1450                  ((device instanceof Sequencer) && allowSequencer) ||
1451                  ((device instanceof Synthesizer) && allowSynthesizer)) {
1452                 // And of cource, the device has to be able to provide
1453                 // Receivers or Transmitters.
1454                 if ((deviceClass == Receiver.class &&
1455                      device.getMaxReceivers() != 0) ||
1456                     (deviceClass == Transmitter.class &&
1457                      device.getMaxTransmitters() != 0)) {
1458                     return true;
1459                 }
1460             }
1461         }
1462         return false;
1463     }
1464 
1465 
1466     /**
1467      * Obtains the set of services currently installed on the system
1468      * using sun.misc.Service, the SPI mechanism in 1.3.
1469      * @return a List of instances of providers for the requested service.
1470      * If no providers are available, a List of length 0 will be returned.
1471      */
1472     private static List getProviders(Class providerClass) {
1473         return JDK13Services.getProviders(providerClass);
1474     }
1475 }