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