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