1 /* 2 * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.sound.midi; 27 28 import java.io.FileInputStream; 29 import java.io.File; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.io.IOException; 33 34 import java.util.ArrayList; 35 import java.util.HashSet; 36 import java.util.Iterator; 37 import java.util.List; 38 import java.util.Set; 39 40 import java.net.URL; 41 42 import javax.sound.midi.spi.MidiFileWriter; 43 import javax.sound.midi.spi.MidiFileReader; 44 import javax.sound.midi.spi.SoundbankReader; 45 import javax.sound.midi.spi.MidiDeviceProvider; 46 47 import com.sun.media.sound.JDK13Services; 48 import com.sun.media.sound.ReferenceCountingDevice; 49 import com.sun.media.sound.AutoConnectSequencer; 50 import com.sun.media.sound.MidiDeviceReceiverEnvelope; 51 import com.sun.media.sound.MidiDeviceTransmitterEnvelope; 52 53 54 /** 55 * The <code>MidiSystem</code> class provides access to the installed MIDI 56 * system resources, including devices such as synthesizers, sequencers, and 57 * MIDI input and output ports. A typical simple MIDI application might 58 * begin by invoking one or more <code>MidiSystem</code> methods to learn 59 * what devices are installed and to obtain the ones needed in that 60 * application. 61 * <p> 62 * The class also has methods for reading files, streams, and URLs that 63 * contain standard MIDI file data or soundbanks. You can query the 64 * <code>MidiSystem</code> for the format of a specified MIDI file. 65 * <p> 66 * You cannot instantiate a <code>MidiSystem</code>; all the methods are 67 * static. 68 * 69 * <p>Properties can be used to specify default MIDI devices. 70 * Both system properties and a properties file are considered. 71 * The <code>sound.properties</code> properties file is read from 72 * an implementation-specific location (typically it is the <code>lib</code> 73 * directory in the Java installation directory). 74 * If a property exists both as a system property and in the 75 * properties file, the system property takes precedence. If none is 76 * specified, a suitable default is chosen among the available devices. 77 * The syntax of the properties file is specified in 78 * {@link java.util.Properties#load(InputStream) Properties.load}. The 79 * following table lists the available property keys and which methods 80 * consider them: 81 * 82 * <table border=0> 83 * <caption>MIDI System Property Keys</caption> 84 * <tr> 85 * <th>Property Key</th> 86 * <th>Interface</th> 87 * <th>Affected Method</th> 88 * </tr> 89 * <tr> 90 * <td><code>javax.sound.midi.Receiver</code></td> 91 * <td>{@link Receiver}</td> 92 * <td>{@link #getReceiver}</td> 93 * </tr> 94 * <tr> 95 * <td><code>javax.sound.midi.Sequencer</code></td> 96 * <td>{@link Sequencer}</td> 97 * <td>{@link #getSequencer}</td> 98 * </tr> 99 * <tr> 100 * <td><code>javax.sound.midi.Synthesizer</code></td> 101 * <td>{@link Synthesizer}</td> 102 * <td>{@link #getSynthesizer}</td> 103 * </tr> 104 * <tr> 105 * <td><code>javax.sound.midi.Transmitter</code></td> 106 * <td>{@link Transmitter}</td> 107 * <td>{@link #getTransmitter}</td> 108 * </tr> 109 * </table> 110 * 111 * The property value consists of the provider class name 112 * and the device name, separated by the hash mark ("#"). 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 { 458 Synthesizer synth = getSynthesizer(); 459 if (synth instanceof ReferenceCountingDevice) { 460 rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting(); 461 } else { 462 synth.open(); 463 try { 464 rec = synth.getReceiver(); 465 } finally { 466 // make sure that the synth is properly closed 467 if (rec == null) { 468 synth.close(); 469 } 470 } 471 } 472 } catch (MidiUnavailableException e) { 473 // something went wrong with synth 474 if (e instanceof MidiUnavailableException) { 475 mue = e; 476 } 477 } 478 if (rec == null) { 479 // then try to connect to the default Receiver 480 try { 481 rec = MidiSystem.getReceiver(); 482 } catch (Exception e) { 483 // something went wrong. Nothing to do then! 484 if (e instanceof MidiUnavailableException) { 485 mue = (MidiUnavailableException) e; 486 } 487 } 488 } 489 if (rec != null) { 490 seq.getTransmitter().setReceiver(rec); 491 if (seq instanceof AutoConnectSequencer) { 492 ((AutoConnectSequencer) seq).setAutoConnect(rec); 493 } 494 } else { 495 if (mue != null) { 496 throw mue; 497 } 498 throw new MidiUnavailableException("no receiver available"); 499 } 500 } 501 return seq; 502 } 503 504 505 506 507 /** 508 * Constructs a MIDI sound bank by reading it from the specified stream. 509 * The stream must point to 510 * a valid MIDI soundbank file. In general, MIDI soundbank providers may 511 * need to read some data from the stream before determining whether they 512 * support it. These parsers must 513 * be able to mark the stream, read enough data to determine whether they 514 * support the stream, and, if not, reset the stream's read pointer to 515 * its original position. If the input stream does not support this, 516 * this method may fail with an IOException. 517 * @param stream the source of the sound bank data. 518 * @return the sound bank 519 * @throws InvalidMidiDataException if the stream does not point to 520 * valid MIDI soundbank data recognized by the system 521 * @throws IOException if an I/O error occurred when loading the soundbank 522 * @see InputStream#markSupported 523 * @see InputStream#mark 524 */ 525 public static Soundbank getSoundbank(InputStream stream) 526 throws InvalidMidiDataException, IOException { 527 528 SoundbankReader sp = null; 529 Soundbank s = null; 530 531 List<SoundbankReader> providers = getSoundbankReaders(); 532 533 for(int i = 0; i < providers.size(); i++) { 534 sp = providers.get(i); 535 s = sp.getSoundbank(stream); 536 537 if( s!= null) { 538 return s; 539 } 540 } 541 throw new InvalidMidiDataException("cannot get soundbank from stream"); 542 543 } 544 545 546 /** 547 * Constructs a <code>Soundbank</code> by reading it from the specified URL. 548 * The URL must point to a valid MIDI soundbank file. 549 * 550 * @param url the source of the sound bank data 551 * @return the sound bank 552 * @throws InvalidMidiDataException if the URL does not point to valid MIDI 553 * soundbank data recognized by the system 554 * @throws IOException if an I/O error occurred when loading the soundbank 555 */ 556 public static Soundbank getSoundbank(URL url) 557 throws InvalidMidiDataException, IOException { 558 559 SoundbankReader sp = null; 560 Soundbank s = null; 561 562 List<SoundbankReader> providers = getSoundbankReaders(); 563 564 for(int i = 0; i < providers.size(); i++) { 565 sp = providers.get(i); 566 s = sp.getSoundbank(url); 567 568 if( s!= null) { 569 return s; 570 } 571 } 572 throw new InvalidMidiDataException("cannot get soundbank from stream"); 573 574 } 575 576 577 /** 578 * Constructs a <code>Soundbank</code> by reading it from the specified 579 * <code>File</code>. 580 * The <code>File</code> must point to a valid MIDI soundbank file. 581 * 582 * @param file the source of the sound bank data 583 * @return the sound bank 584 * @throws InvalidMidiDataException if the <code>File</code> does not 585 * point to valid MIDI soundbank data recognized by the system 586 * @throws IOException if an I/O error occurred when loading the soundbank 587 */ 588 public static Soundbank getSoundbank(File file) 589 throws InvalidMidiDataException, IOException { 590 591 SoundbankReader sp = null; 592 Soundbank s = null; 593 594 List<SoundbankReader> providers = getSoundbankReaders(); 595 596 for(int i = 0; i < providers.size(); i++) { 597 sp = providers.get(i); 598 s = sp.getSoundbank(file); 599 600 if( s!= null) { 601 return s; 602 } 603 } 604 throw new InvalidMidiDataException("cannot get soundbank from stream"); 605 } 606 607 608 609 /** 610 * Obtains the MIDI file format of the data in the specified input stream. 611 * The stream must point to valid MIDI file data for a file type recognized 612 * by the system. 613 * <p> 614 * This method and/or the code it invokes may need to read some data from 615 * the stream to determine whether its data format is supported. The 616 * implementation may therefore 617 * need to mark the stream, read enough data to determine whether it is in 618 * a supported format, and reset the stream's read pointer to its original 619 * position. If the input stream does not permit this set of operations, 620 * this method may fail with an <code>IOException</code>. 621 * <p> 622 * This operation can only succeed for files of a type which can be parsed 623 * by an installed file reader. It may fail with an InvalidMidiDataException 624 * even for valid files if no compatible file reader is installed. It 625 * will also fail with an InvalidMidiDataException if a compatible file reader 626 * is installed, but encounters errors while determining the file format. 627 * 628 * @param stream the input stream from which file format information 629 * should be extracted 630 * @return an <code>MidiFileFormat</code> object describing the MIDI file 631 * format 632 * @throws InvalidMidiDataException if the stream does not point to valid 633 * MIDI file data recognized by the system 634 * @throws IOException if an I/O exception occurs while accessing the 635 * stream 636 * @see #getMidiFileFormat(URL) 637 * @see #getMidiFileFormat(File) 638 * @see InputStream#markSupported 639 * @see InputStream#mark 640 */ 641 public static MidiFileFormat getMidiFileFormat(InputStream stream) 642 throws InvalidMidiDataException, IOException { 643 644 List<MidiFileReader> providers = getMidiFileReaders(); 645 MidiFileFormat format = null; 646 647 for(int i = 0; i < providers.size(); i++) { 648 MidiFileReader reader = providers.get(i); 649 try { 650 format = reader.getMidiFileFormat( stream ); // throws IOException 651 break; 652 } catch (InvalidMidiDataException e) { 653 continue; 654 } 655 } 656 657 if( format==null ) { 658 throw new InvalidMidiDataException("input stream is not a supported file type"); 659 } else { 660 return format; 661 } 662 } 663 664 665 /** 666 * Obtains the MIDI file format of the data in the specified URL. The URL 667 * must point to valid MIDI file data for a file type recognized 668 * by the system. 669 * <p> 670 * This operation can only succeed for files of a type which can be parsed 671 * by an installed file reader. It may fail with an InvalidMidiDataException 672 * even for valid files if no compatible file reader is installed. It 673 * will also fail with an InvalidMidiDataException if a compatible file reader 674 * is installed, but encounters errors while determining the file format. 675 * 676 * @param url the URL from which file format information should be 677 * extracted 678 * @return a <code>MidiFileFormat</code> object describing the MIDI file 679 * format 680 * @throws InvalidMidiDataException if the URL does not point to valid MIDI 681 * file data recognized by the system 682 * @throws IOException if an I/O exception occurs while accessing the URL 683 * 684 * @see #getMidiFileFormat(InputStream) 685 * @see #getMidiFileFormat(File) 686 */ 687 public static MidiFileFormat getMidiFileFormat(URL url) 688 throws InvalidMidiDataException, IOException { 689 690 List<MidiFileReader> providers = getMidiFileReaders(); 691 MidiFileFormat format = null; 692 693 for(int i = 0; i < providers.size(); i++) { 694 MidiFileReader reader = providers.get(i); 695 try { 696 format = reader.getMidiFileFormat( url ); // throws IOException 697 break; 698 } catch (InvalidMidiDataException e) { 699 continue; 700 } 701 } 702 703 if( format==null ) { 704 throw new InvalidMidiDataException("url is not a supported file type"); 705 } else { 706 return format; 707 } 708 } 709 710 711 /** 712 * Obtains the MIDI file format of the specified <code>File</code>. The 713 * <code>File</code> must point to valid MIDI file data for a file type 714 * recognized by the system. 715 * <p> 716 * This operation can only succeed for files of a type which can be parsed 717 * by an installed file reader. It may fail with an InvalidMidiDataException 718 * even for valid files if no compatible file reader is installed. It 719 * will also fail with an InvalidMidiDataException if a compatible file reader 720 * is installed, but encounters errors while determining the file format. 721 * 722 * @param file the <code>File</code> from which file format information 723 * should be extracted 724 * @return a <code>MidiFileFormat</code> object describing the MIDI file 725 * format 726 * @throws InvalidMidiDataException if the <code>File</code> does not point 727 * to valid MIDI file data recognized by the system 728 * @throws IOException if an I/O exception occurs while accessing the file 729 * 730 * @see #getMidiFileFormat(InputStream) 731 * @see #getMidiFileFormat(URL) 732 */ 733 public static MidiFileFormat getMidiFileFormat(File file) 734 throws InvalidMidiDataException, IOException { 735 736 List<MidiFileReader> providers = getMidiFileReaders(); 737 MidiFileFormat format = null; 738 739 for(int i = 0; i < providers.size(); i++) { 740 MidiFileReader reader = providers.get(i); 741 try { 742 format = reader.getMidiFileFormat( file ); // throws IOException 743 break; 744 } catch (InvalidMidiDataException e) { 745 continue; 746 } 747 } 748 749 if( format==null ) { 750 throw new InvalidMidiDataException("file is not a supported file type"); 751 } else { 752 return format; 753 } 754 } 755 756 757 /** 758 * Obtains a MIDI sequence from the specified input stream. The stream must 759 * point to valid MIDI file data for a file type recognized 760 * by the system. 761 * <p> 762 * This method and/or the code it invokes may need to read some data 763 * from the stream to determine whether 764 * its data format is supported. The implementation may therefore 765 * need to mark the stream, read enough data to determine whether it is in 766 * a supported format, and reset the stream's read pointer to its original 767 * position. If the input stream does not permit this set of operations, 768 * this method may fail with an <code>IOException</code>. 769 * <p> 770 * This operation can only succeed for files of a type which can be parsed 771 * by an installed file reader. It may fail with an InvalidMidiDataException 772 * even for valid files if no compatible file reader is installed. It 773 * will also fail with an InvalidMidiDataException if a compatible file reader 774 * is installed, but encounters errors while constructing the <code>Sequence</code> 775 * object from the file data. 776 * 777 * @param stream the input stream from which the <code>Sequence</code> 778 * should be constructed 779 * @return a <code>Sequence</code> object based on the MIDI file data 780 * contained in the input stream 781 * @throws InvalidMidiDataException if the stream does not point to 782 * valid MIDI file data recognized by the system 783 * @throws IOException if an I/O exception occurs while accessing the 784 * stream 785 * @see InputStream#markSupported 786 * @see InputStream#mark 787 */ 788 public static Sequence getSequence(InputStream stream) 789 throws InvalidMidiDataException, IOException { 790 791 List<MidiFileReader> providers = getMidiFileReaders(); 792 Sequence sequence = null; 793 794 for(int i = 0; i < providers.size(); i++) { 795 MidiFileReader reader = providers.get(i); 796 try { 797 sequence = reader.getSequence( stream ); // throws IOException 798 break; 799 } catch (InvalidMidiDataException e) { 800 continue; 801 } 802 } 803 804 if( sequence==null ) { 805 throw new InvalidMidiDataException("could not get sequence from input stream"); 806 } else { 807 return sequence; 808 } 809 } 810 811 812 /** 813 * Obtains a MIDI sequence from the specified URL. The URL must 814 * point to valid MIDI file data for a file type recognized 815 * by the system. 816 * <p> 817 * This operation can only succeed for files of a type which can be parsed 818 * by an installed file reader. It may fail with an InvalidMidiDataException 819 * even for valid files if no compatible file reader is installed. It 820 * will also fail with an InvalidMidiDataException if a compatible file reader 821 * is installed, but encounters errors while constructing the <code>Sequence</code> 822 * object from the file data. 823 * 824 * @param url the URL from which the <code>Sequence</code> should be 825 * constructed 826 * @return a <code>Sequence</code> object based on the MIDI file data 827 * pointed to by the URL 828 * @throws InvalidMidiDataException if the URL does not point to valid MIDI 829 * file data recognized by the system 830 * @throws IOException if an I/O exception occurs while accessing the URL 831 */ 832 public static Sequence getSequence(URL url) 833 throws InvalidMidiDataException, IOException { 834 835 List<MidiFileReader> providers = getMidiFileReaders(); 836 Sequence sequence = null; 837 838 for(int i = 0; i < providers.size(); i++) { 839 MidiFileReader reader = providers.get(i); 840 try { 841 sequence = reader.getSequence( url ); // throws IOException 842 break; 843 } catch (InvalidMidiDataException e) { 844 continue; 845 } 846 } 847 848 if( sequence==null ) { 849 throw new InvalidMidiDataException("could not get sequence from URL"); 850 } else { 851 return sequence; 852 } 853 } 854 855 856 /** 857 * Obtains a MIDI sequence from the specified <code>File</code>. 858 * The <code>File</code> must point to valid MIDI file data 859 * for a file type recognized by the system. 860 * <p> 861 * This operation can only succeed for files of a type which can be parsed 862 * by an installed file reader. It may fail with an InvalidMidiDataException 863 * even for valid files if no compatible file reader is installed. It 864 * will also fail with an InvalidMidiDataException if a compatible file reader 865 * is installed, but encounters errors while constructing the <code>Sequence</code> 866 * object from the file data. 867 * 868 * @param file the <code>File</code> from which the <code>Sequence</code> 869 * should be constructed 870 * @return a <code>Sequence</code> object based on the MIDI file data 871 * pointed to by the File 872 * @throws InvalidMidiDataException if the File does not point to valid MIDI 873 * file data recognized by the system 874 * @throws IOException if an I/O exception occurs 875 */ 876 public static Sequence getSequence(File file) 877 throws InvalidMidiDataException, IOException { 878 879 List<MidiFileReader> providers = getMidiFileReaders(); 880 Sequence sequence = null; 881 882 for(int i = 0; i < providers.size(); i++) { 883 MidiFileReader reader = providers.get(i); 884 try { 885 sequence = reader.getSequence( file ); // throws IOException 886 break; 887 } catch (InvalidMidiDataException e) { 888 continue; 889 } 890 } 891 892 if( sequence==null ) { 893 throw new InvalidMidiDataException("could not get sequence from file"); 894 } else { 895 return sequence; 896 } 897 } 898 899 900 /** 901 * Obtains the set of MIDI file types for which file writing support is 902 * provided by the system. 903 * @return array of unique file types. If no file types are supported, 904 * an array of length 0 is returned. 905 */ 906 public static int[] getMidiFileTypes() { 907 908 List<MidiFileWriter> providers = getMidiFileWriters(); 909 Set<Integer> allTypes = new HashSet<>(); 910 911 // gather from all the providers 912 913 for (int i = 0; i < providers.size(); i++ ) { 914 MidiFileWriter writer = providers.get(i); 915 int[] types = writer.getMidiFileTypes(); 916 for (int j = 0; j < types.length; j++ ) { 917 allTypes.add(types[j]); 918 } 919 } 920 int resultTypes[] = new int[allTypes.size()]; 921 int index = 0; 922 Iterator<Integer> iterator = allTypes.iterator(); 923 while (iterator.hasNext()) { 924 Integer integer = iterator.next(); 925 resultTypes[index++] = integer.intValue(); 926 } 927 return resultTypes; 928 } 929 930 931 /** 932 * Indicates whether file writing support for the specified MIDI file type 933 * is provided by the system. 934 * @param fileType the file type for which write capabilities are queried 935 * @return <code>true</code> if the file type is supported, 936 * otherwise <code>false</code> 937 */ 938 public static boolean isFileTypeSupported(int fileType) { 939 940 List<MidiFileWriter> providers = getMidiFileWriters(); 941 942 for (int i = 0; i < providers.size(); i++ ) { 943 MidiFileWriter writer = providers.get(i); 944 if( writer.isFileTypeSupported(fileType)) { 945 return true; 946 } 947 } 948 return false; 949 } 950 951 952 /** 953 * Obtains the set of MIDI file types that the system can write from the 954 * sequence specified. 955 * @param sequence the sequence for which MIDI file type support 956 * is queried 957 * @return the set of unique supported file types. If no file types are supported, 958 * returns an array of length 0. 959 */ 960 public static int[] getMidiFileTypes(Sequence sequence) { 961 962 List<MidiFileWriter> providers = getMidiFileWriters(); 963 Set<Integer> allTypes = new HashSet<>(); 964 965 // gather from all the providers 966 967 for (int i = 0; i < providers.size(); i++ ) { 968 MidiFileWriter writer = providers.get(i); 969 int[] types = writer.getMidiFileTypes(sequence); 970 for (int j = 0; j < types.length; j++ ) { 971 allTypes.add(types[j]); 972 } 973 } 974 int resultTypes[] = new int[allTypes.size()]; 975 int index = 0; 976 Iterator<Integer> iterator = allTypes.iterator(); 977 while (iterator.hasNext()) { 978 Integer integer = iterator.next(); 979 resultTypes[index++] = integer.intValue(); 980 } 981 return resultTypes; 982 } 983 984 985 /** 986 * Indicates whether a MIDI file of the file type specified can be written 987 * from the sequence indicated. 988 * @param fileType the file type for which write capabilities 989 * are queried 990 * @param sequence the sequence for which file writing support is queried 991 * @return <code>true</code> if the file type is supported for this 992 * sequence, otherwise <code>false</code> 993 */ 994 public static boolean isFileTypeSupported(int fileType, Sequence sequence) { 995 996 List<MidiFileWriter> providers = getMidiFileWriters(); 997 998 for (int i = 0; i < providers.size(); i++ ) { 999 MidiFileWriter writer = providers.get(i); 1000 if( writer.isFileTypeSupported(fileType,sequence)) { 1001 return true; 1002 } 1003 } 1004 return false; 1005 } 1006 1007 1008 /** 1009 * Writes a stream of bytes representing a file of the MIDI file type 1010 * indicated to the output stream provided. 1011 * @param in sequence containing MIDI data to be written to the file 1012 * @param fileType the file type of the file to be written to the output stream 1013 * @param out stream to which the file data should be written 1014 * @return the number of bytes written to the output stream 1015 * @throws IOException if an I/O exception occurs 1016 * @throws IllegalArgumentException if the file format is not supported by 1017 * the system 1018 * @see #isFileTypeSupported(int, Sequence) 1019 * @see #getMidiFileTypes(Sequence) 1020 */ 1021 public static int write(Sequence in, int fileType, OutputStream out) throws IOException { 1022 1023 List<MidiFileWriter> providers = getMidiFileWriters(); 1024 //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences 1025 int bytesWritten = -2; 1026 1027 for (int i = 0; i < providers.size(); i++ ) { 1028 MidiFileWriter writer = providers.get(i); 1029 if( writer.isFileTypeSupported( fileType, in ) ) { 1030 1031 bytesWritten = writer.write(in, fileType, out); 1032 break; 1033 } 1034 } 1035 if (bytesWritten == -2) { 1036 throw new IllegalArgumentException("MIDI file type is not supported"); 1037 } 1038 return bytesWritten; 1039 } 1040 1041 1042 /** 1043 * Writes a stream of bytes representing a file of the MIDI file type 1044 * indicated to the external file provided. 1045 * @param in sequence containing MIDI data to be written to the file 1046 * @param type the file type of the file to be written to the output stream 1047 * @param out external file to which the file data should be written 1048 * @return the number of bytes written to the file 1049 * @throws IOException if an I/O exception occurs 1050 * @throws IllegalArgumentException if the file type is not supported by 1051 * the system 1052 * @see #isFileTypeSupported(int, Sequence) 1053 * @see #getMidiFileTypes(Sequence) 1054 */ 1055 public static int write(Sequence in, int type, File out) throws IOException { 1056 1057 List<MidiFileWriter> providers = getMidiFileWriters(); 1058 //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences 1059 int bytesWritten = -2; 1060 1061 for (int i = 0; i < providers.size(); i++ ) { 1062 MidiFileWriter writer = providers.get(i); 1063 if( writer.isFileTypeSupported( type, in ) ) { 1064 1065 bytesWritten = writer.write(in, type, out); 1066 break; 1067 } 1068 } 1069 if (bytesWritten == -2) { 1070 throw new IllegalArgumentException("MIDI file type is not supported"); 1071 } 1072 return bytesWritten; 1073 } 1074 1075 1076 1077 // HELPER METHODS 1078 @SuppressWarnings("unchecked") 1079 private static List<MidiDeviceProvider> getMidiDeviceProviders() { 1080 return (List<MidiDeviceProvider>) getProviders(MidiDeviceProvider.class); 1081 } 1082 1083 @SuppressWarnings("unchecked") 1084 private static List<SoundbankReader> getSoundbankReaders() { 1085 return (List<SoundbankReader>) getProviders(SoundbankReader.class); 1086 } 1087 1088 @SuppressWarnings("unchecked") 1089 private static List<MidiFileWriter> getMidiFileWriters() { 1090 return (List<MidiFileWriter>) getProviders(MidiFileWriter.class); 1091 } 1092 1093 @SuppressWarnings("unchecked") 1094 private static List<MidiFileReader> getMidiFileReaders() { 1095 return (List<MidiFileReader>) getProviders(MidiFileReader.class); 1096 } 1097 1098 1099 /** Attempts to locate and return a default MidiDevice of the specified 1100 * type. 1101 * 1102 * This method wraps {@link #getDefaultDevice}. It catches the 1103 * <code>IllegalArgumentException</code> thrown by 1104 * <code>getDefaultDevice</code> and instead throws a 1105 * <code>MidiUnavailableException</code>, with the catched 1106 * exception chained. 1107 * 1108 * @param deviceClass The requested device type, one of Synthesizer.class, 1109 * Sequencer.class, Receiver.class or Transmitter.class. 1110 * @throws MidiUnavalableException on failure. 1111 */ 1112 private static MidiDevice getDefaultDeviceWrapper(Class<?> deviceClass) 1113 throws MidiUnavailableException{ 1114 try { 1115 return getDefaultDevice(deviceClass); 1116 } catch (IllegalArgumentException iae) { 1117 MidiUnavailableException mae = new MidiUnavailableException(); 1118 mae.initCause(iae); 1119 throw mae; 1120 } 1121 } 1122 1123 1124 /** Attempts to locate and return a default MidiDevice of the specified 1125 * type. 1126 * 1127 * @param deviceClass The requested device type, one of Synthesizer.class, 1128 * Sequencer.class, Receiver.class or Transmitter.class. 1129 * @throws IllegalArgumentException on failure. 1130 */ 1131 private static MidiDevice getDefaultDevice(Class<?> deviceClass) { 1132 List<MidiDeviceProvider> providers = getMidiDeviceProviders(); 1133 String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass); 1134 String instanceName = JDK13Services.getDefaultInstanceName(deviceClass); 1135 MidiDevice device; 1136 1137 if (providerClassName != null) { 1138 MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers); 1139 if (defaultProvider != null) { 1140 if (instanceName != null) { 1141 device = getNamedDevice(instanceName, defaultProvider, deviceClass); 1142 if (device != null) { 1143 return device; 1144 } 1145 } 1146 device = getFirstDevice(defaultProvider, deviceClass); 1147 if (device != null) { 1148 return device; 1149 } 1150 } 1151 } 1152 1153 /* Provider class not specified or cannot be found, or 1154 provider class specified, and no appropriate device available or 1155 provider class and instance specified and instance cannot be found or is not appropriate */ 1156 if (instanceName != null) { 1157 device = getNamedDevice(instanceName, providers, deviceClass); 1158 if (device != null) { 1159 return device; 1160 } 1161 } 1162 1163 /* No default are specified, or if something is specified, everything 1164 failed. */ 1165 device = getFirstDevice(providers, deviceClass); 1166 if (device != null) { 1167 return device; 1168 } 1169 throw new IllegalArgumentException("Requested device not installed"); 1170 } 1171 1172 1173 1174 /** Return a MidiDeviceProcider of a given class from the list of 1175 MidiDeviceProviders. 1176 1177 @param providerClassName The class name of the provider to be returned. 1178 @param provider The list of MidiDeviceProviders that is searched. 1179 @return A MidiDeviceProvider of the requested class, or null if none 1180 is found. 1181 */ 1182 private static MidiDeviceProvider getNamedProvider(String providerClassName, 1183 List<MidiDeviceProvider> providers) { 1184 for(int i = 0; i < providers.size(); i++) { 1185 MidiDeviceProvider provider = providers.get(i); 1186 if (provider.getClass().getName().equals(providerClassName)) { 1187 return provider; 1188 } 1189 } 1190 return null; 1191 } 1192 1193 1194 /** Return a MidiDevice with a given name from a given MidiDeviceProvider. 1195 @param deviceName The name of the MidiDevice to be returned. 1196 @param provider The MidiDeviceProvider to check for MidiDevices. 1197 @param deviceClass The requested device type, one of Synthesizer.class, 1198 Sequencer.class, Receiver.class or Transmitter.class. 1199 1200 @return A MidiDevice matching the requirements, or null if none is found. 1201 */ 1202 private static MidiDevice getNamedDevice(String deviceName, 1203 MidiDeviceProvider provider, 1204 Class<?> deviceClass) { 1205 MidiDevice device; 1206 // try to get MIDI port 1207 device = getNamedDevice(deviceName, provider, deviceClass, 1208 false, false); 1209 if (device != null) { 1210 return device; 1211 } 1212 1213 if (deviceClass == Receiver.class) { 1214 // try to get Synthesizer 1215 device = getNamedDevice(deviceName, provider, deviceClass, 1216 true, false); 1217 if (device != null) { 1218 return device; 1219 } 1220 } 1221 1222 return null; 1223 } 1224 1225 1226 /** Return a MidiDevice with a given name from a given MidiDeviceProvider. 1227 @param deviceName The name of the MidiDevice to be returned. 1228 @param provider The MidiDeviceProvider to check for MidiDevices. 1229 @param deviceClass The requested device type, one of Synthesizer.class, 1230 Sequencer.class, Receiver.class or Transmitter.class. 1231 1232 @return A MidiDevice matching the requirements, or null if none is found. 1233 */ 1234 private static MidiDevice getNamedDevice(String deviceName, 1235 MidiDeviceProvider provider, 1236 Class<?> deviceClass, 1237 boolean allowSynthesizer, 1238 boolean allowSequencer) { 1239 MidiDevice.Info[] infos = provider.getDeviceInfo(); 1240 for (int i = 0; i < infos.length; i++) { 1241 if (infos[i].getName().equals(deviceName)) { 1242 MidiDevice device = provider.getDevice(infos[i]); 1243 if (isAppropriateDevice(device, deviceClass, 1244 allowSynthesizer, allowSequencer)) { 1245 return device; 1246 } 1247 } 1248 } 1249 return null; 1250 } 1251 1252 1253 /** Return a MidiDevice with a given name from a list of 1254 MidiDeviceProviders. 1255 @param deviceName The name of the MidiDevice to be returned. 1256 @param providers The List of MidiDeviceProviders to check for 1257 MidiDevices. 1258 @param deviceClass The requested device type, one of Synthesizer.class, 1259 Sequencer.class, Receiver.class or Transmitter.class. 1260 @return A Mixer matching the requirements, or null if none is found. 1261 */ 1262 private static MidiDevice getNamedDevice(String deviceName, 1263 List<MidiDeviceProvider> providers, 1264 Class<?> deviceClass) { 1265 MidiDevice device; 1266 // try to get MIDI port 1267 device = getNamedDevice(deviceName, providers, deviceClass, 1268 false, false); 1269 if (device != null) { 1270 return device; 1271 } 1272 1273 if (deviceClass == Receiver.class) { 1274 // try to get Synthesizer 1275 device = getNamedDevice(deviceName, providers, deviceClass, 1276 true, false); 1277 if (device != null) { 1278 return device; 1279 } 1280 } 1281 1282 return null; 1283 } 1284 1285 1286 /** Return a MidiDevice with a given name from a list of 1287 MidiDeviceProviders. 1288 @param deviceName The name of the MidiDevice to be returned. 1289 @param providers The List of MidiDeviceProviders to check for 1290 MidiDevices. 1291 @param deviceClass The requested device type, one of Synthesizer.class, 1292 Sequencer.class, Receiver.class or Transmitter.class. 1293 @return A Mixer matching the requirements, or null if none is found. 1294 */ 1295 private static MidiDevice getNamedDevice(String deviceName, 1296 List<MidiDeviceProvider> providers, 1297 Class<?> deviceClass, 1298 boolean allowSynthesizer, 1299 boolean allowSequencer) { 1300 for(int i = 0; i < providers.size(); i++) { 1301 MidiDeviceProvider provider = providers.get(i); 1302 MidiDevice device = getNamedDevice(deviceName, provider, 1303 deviceClass, 1304 allowSynthesizer, 1305 allowSequencer); 1306 if (device != null) { 1307 return device; 1308 } 1309 } 1310 return null; 1311 } 1312 1313 1314 /** From a given MidiDeviceProvider, return the first appropriate device. 1315 @param provider The MidiDeviceProvider to check for MidiDevices. 1316 @param deviceClass The requested device type, one of Synthesizer.class, 1317 Sequencer.class, Receiver.class or Transmitter.class. 1318 @return A MidiDevice is considered appropriate, or null if no 1319 appropriate device is found. 1320 */ 1321 private static MidiDevice getFirstDevice(MidiDeviceProvider provider, 1322 Class<?> deviceClass) { 1323 MidiDevice device; 1324 // try to get MIDI port 1325 device = getFirstDevice(provider, deviceClass, 1326 false, false); 1327 if (device != null) { 1328 return device; 1329 } 1330 1331 if (deviceClass == Receiver.class) { 1332 // try to get Synthesizer 1333 device = getFirstDevice(provider, deviceClass, 1334 true, false); 1335 if (device != null) { 1336 return device; 1337 } 1338 } 1339 1340 return null; 1341 } 1342 1343 1344 /** From a given MidiDeviceProvider, return the first appropriate device. 1345 @param provider The MidiDeviceProvider to check for MidiDevices. 1346 @param deviceClass The requested device type, one of Synthesizer.class, 1347 Sequencer.class, Receiver.class or Transmitter.class. 1348 @return A MidiDevice is considered appropriate, or null if no 1349 appropriate device is found. 1350 */ 1351 private static MidiDevice getFirstDevice(MidiDeviceProvider provider, 1352 Class<?> deviceClass, 1353 boolean allowSynthesizer, 1354 boolean allowSequencer) { 1355 MidiDevice.Info[] infos = provider.getDeviceInfo(); 1356 for (int j = 0; j < infos.length; j++) { 1357 MidiDevice device = provider.getDevice(infos[j]); 1358 if (isAppropriateDevice(device, deviceClass, 1359 allowSynthesizer, allowSequencer)) { 1360 return device; 1361 } 1362 } 1363 return null; 1364 } 1365 1366 1367 /** From a List of MidiDeviceProviders, return the first appropriate 1368 MidiDevice. 1369 @param providers The List of MidiDeviceProviders to search. 1370 @param deviceClass The requested device type, one of Synthesizer.class, 1371 Sequencer.class, Receiver.class or Transmitter.class. 1372 @return A MidiDevice that is considered appropriate, or null 1373 if none is found. 1374 */ 1375 private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers, 1376 Class<?> deviceClass) { 1377 MidiDevice device; 1378 // try to get MIDI port 1379 device = getFirstDevice(providers, deviceClass, 1380 false, false); 1381 if (device != null) { 1382 return device; 1383 } 1384 1385 if (deviceClass == Receiver.class) { 1386 // try to get Synthesizer 1387 device = getFirstDevice(providers, deviceClass, 1388 true, false); 1389 if (device != null) { 1390 return device; 1391 } 1392 } 1393 1394 return null; 1395 } 1396 1397 1398 /** From a List of MidiDeviceProviders, return the first appropriate 1399 MidiDevice. 1400 @param providers The List of MidiDeviceProviders to search. 1401 @param deviceClass The requested device type, one of Synthesizer.class, 1402 Sequencer.class, Receiver.class or Transmitter.class. 1403 @return A MidiDevice that is considered appropriate, or null 1404 if none is found. 1405 */ 1406 private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers, 1407 Class<?> deviceClass, 1408 boolean allowSynthesizer, 1409 boolean allowSequencer) { 1410 for(int i = 0; i < providers.size(); i++) { 1411 MidiDeviceProvider provider = providers.get(i); 1412 MidiDevice device = getFirstDevice(provider, deviceClass, 1413 allowSynthesizer, 1414 allowSequencer); 1415 if (device != null) { 1416 return device; 1417 } 1418 } 1419 return null; 1420 } 1421 1422 1423 /** Checks if a MidiDevice is appropriate. 1424 If deviceClass is Synthesizer or Sequencer, a device implementing 1425 the respective interface is considered appropriate. If deviceClass 1426 is Receiver or Transmitter, a device is considered appropriate if 1427 it implements neither Synthesizer nor Transmitter, and if it can 1428 provide at least one Receiver or Transmitter, respectively. 1429 1430 @param device the MidiDevice to test 1431 @param allowSynthesizer if true, Synthesizers are considered 1432 appropriate. Otherwise only pure MidiDevices are considered 1433 appropriate (unless allowSequencer is true). This flag only has an 1434 effect for deviceClass Receiver and Transmitter. For other device 1435 classes (Sequencer and Synthesizer), this flag has no effect. 1436 @param allowSequencer if true, Sequencers are considered 1437 appropriate. Otherwise only pure MidiDevices are considered 1438 appropriate (unless allowSynthesizer is true). This flag only has an 1439 effect for deviceClass Receiver and Transmitter. For other device 1440 classes (Sequencer and Synthesizer), this flag has no effect. 1441 @return true if the device is considered appropriate according to the 1442 rules given above, false otherwise. 1443 */ 1444 private static boolean isAppropriateDevice(MidiDevice device, 1445 Class<?> deviceClass, 1446 boolean allowSynthesizer, 1447 boolean allowSequencer) { 1448 if (deviceClass.isInstance(device)) { 1449 // This clause is for deviceClass being either Synthesizer 1450 // or Sequencer. 1451 return true; 1452 } else { 1453 // Now the case that deviceClass is Transmitter or 1454 // Receiver. If neither allowSynthesizer nor allowSequencer is 1455 // true, we require device instances to be 1456 // neither Synthesizer nor Sequencer, since we only want 1457 // devices representing MIDI ports. 1458 // Otherwise, the respective type is accepted, too 1459 if ( (! (device instanceof Sequencer) && 1460 ! (device instanceof Synthesizer) ) || 1461 ((device instanceof Sequencer) && allowSequencer) || 1462 ((device instanceof Synthesizer) && allowSynthesizer)) { 1463 // And of cource, the device has to be able to provide 1464 // Receivers or Transmitters. 1465 if ((deviceClass == Receiver.class && 1466 device.getMaxReceivers() != 0) || 1467 (deviceClass == Transmitter.class && 1468 device.getMaxTransmitters() != 0)) { 1469 return true; 1470 } 1471 } 1472 } 1473 return false; 1474 } 1475 1476 1477 /** 1478 * Obtains the set of services currently installed on the system 1479 * using the SPI mechanism in 1.3. 1480 * @return a List of instances of providers for the requested service. 1481 * If no providers are available, a List of length 0 will be returned. 1482 */ 1483 private static List<?> getProviders(Class<?> providerClass) { 1484 return JDK13Services.getProviders(providerClass); 1485 } 1486 }