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