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