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