1 /* 2 * Copyright (c) 1999, 2018, 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). 70 * The optional "javax.sound.config.file" system property can be used to specify 71 * the properties file that will be read as the initial configuration. If a 72 * property exists both as a system property and in the properties file, the 73 * system property takes precedence. If none is specified, a suitable default is 74 * chosen among the available devices. The syntax of the properties file is 75 * specified in {@link Properties#load(InputStream) Properties.load}. The 76 * following table lists the available property keys and which methods consider 77 * them: 78 * 79 * <table class="striped"> 80 * <caption>MIDI System Property Keys</caption> 81 * <thead> 82 * <tr> 83 * <th scope="col">Property Key 84 * <th scope="col">Interface 85 * <th scope="col">Affected Method 86 * </thead> 87 * <tbody> 88 * <tr> 89 * <th scope="row">{@code javax.sound.midi.Receiver} 90 * <td>{@link Receiver} 91 * <td>{@link #getReceiver} 92 * <tr> 93 * <th scope="row">{@code javax.sound.midi.Sequencer} 94 * <td>{@link Sequencer} 95 * <td>{@link #getSequencer} 96 * <tr> 97 * <th scope="row">{@code javax.sound.midi.Synthesizer} 98 * <td>{@link Synthesizer} 99 * <td>{@link #getSynthesizer} 100 * <tr> 101 * <th scope="row">{@code javax.sound.midi.Transmitter} 102 * <td>{@link Transmitter} 103 * <td>{@link #getTransmitter} 104 * </tbody> 105 * </table> 106 * 107 * The property value consists of the provider class name and the device name, 108 * separated by the hash mark ("#"). The provider class name is the 109 * fully-qualified name of a concrete 110 * {@link MidiDeviceProvider MIDI device provider} class. The device name is 111 * matched against the {@code String} returned by the {@code getName} method of 112 * {@code MidiDevice.Info}. Either the class name, or the device name may be 113 * omitted. If only the class name is specified, the trailing hash mark is 114 * optional. 115 * <p> 116 * If the provider class is specified, and it can be successfully retrieved from 117 * the installed providers, the list of {@code MidiDevice.Info} objects is 118 * retrieved from the provider. Otherwise, or when these devices do not provide 119 * a subsequent match, the list is retrieved from {@link #getMidiDeviceInfo} to 120 * contain all available {@code MidiDevice.Info} objects. 121 * <p> 122 * If a device name is specified, the resulting list of {@code MidiDevice.Info} 123 * objects is searched: the first one with a matching name, and whose 124 * {@code MidiDevice} implements the respective interface, will be returned. If 125 * no matching {@code MidiDevice.Info} object is found, or the device name is 126 * not specified, the first suitable device from the resulting list will be 127 * returned. For Sequencer and Synthesizer, a device is suitable if it 128 * implements the respective interface; whereas for Receiver and Transmitter, a 129 * device is suitable if it implements neither Sequencer nor Synthesizer and 130 * provides at least one Receiver or Transmitter, respectively. 131 * <p> 132 * For example, the property {@code javax.sound.midi.Receiver} with a value 133 * {@code "com.sun.media.sound.MidiProvider#SunMIDI1"} will have the following 134 * consequences when {@code getReceiver} is called: if the class 135 * {@code com.sun.media.sound.MidiProvider} exists in the list of installed MIDI 136 * device providers, the first {@code Receiver} device with name 137 * {@code "SunMIDI1"} will be returned. If it cannot be found, the first 138 * {@code Receiver} from that provider will be returned, regardless of name. If 139 * there is none, the first {@code Receiver} with name {@code "SunMIDI1"} in the 140 * list of all devices (as returned by {@code getMidiDeviceInfo}) will be 141 * returned, or, if not found, the first {@code Receiver} that can be found in 142 * the list of all devices is returned. If that fails, too, a 143 * {@code MidiUnavailableException} is thrown. 144 * 145 * @author Kara Kytle 146 * @author Florian Bomers 147 * @author Matthias Pfisterer 148 */ 149 public class MidiSystem { 150 151 /** 152 * Private no-args constructor for ensuring against instantiation. 153 */ 154 private MidiSystem() { 155 } 156 157 /** 158 * Obtains an array of information objects representing the set of all MIDI 159 * devices available on the system. A returned information object can then 160 * be used to obtain the corresponding device object, by invoking 161 * {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}. 162 * 163 * @return an array of {@code MidiDevice.Info} objects, one for each 164 * installed MIDI device. If no such devices are installed, an array 165 * of length 0 is returned. 166 */ 167 public static MidiDevice.Info[] getMidiDeviceInfo() { 168 final List<MidiDevice.Info> allInfos = new ArrayList<>(); 169 for (final MidiDeviceProvider provider : getMidiDeviceProviders()) { 170 Collections.addAll(allInfos, provider.getDeviceInfo()); 171 } 172 return allInfos.toArray(new MidiDevice.Info[allInfos.size()]); 173 } 174 175 /** 176 * Obtains the requested MIDI device. 177 * 178 * @param info a device information object representing the desired device 179 * @return the requested device 180 * @throws MidiUnavailableException if the requested device is not available 181 * due to resource restrictions 182 * @throws IllegalArgumentException if the info object does not represent a 183 * MIDI device installed on the system 184 * @throws NullPointerException if {@code info} is {@code null} 185 * @see #getMidiDeviceInfo 186 */ 187 public static MidiDevice getMidiDevice(final MidiDevice.Info info) 188 throws MidiUnavailableException { 189 Objects.requireNonNull(info); 190 for (final MidiDeviceProvider provider : getMidiDeviceProviders()) { 191 if (provider.isDeviceSupported(info)) { 192 return provider.getDevice(info); 193 } 194 } 195 throw new IllegalArgumentException(String.format( 196 "Requested device not installed: %s", info)); 197 } 198 199 /** 200 * Obtains a MIDI receiver from an external MIDI port or other default 201 * device. The returned receiver always implements the 202 * {@code MidiDeviceReceiver} interface. 203 * <p> 204 * If the system property {@code javax.sound.midi.Receiver} is defined or it 205 * is defined in the file "sound.properties", it is used to identify the 206 * device that provides the default receiver. For details, refer to the 207 * {@link MidiSystem class description}. 208 * <p> 209 * If a suitable MIDI port is not available, the Receiver is retrieved from 210 * an installed synthesizer. 211 * <p> 212 * If a native receiver provided by the default device does not implement 213 * the {@code MidiDeviceReceiver} interface, it will be wrapped in a wrapper 214 * class that implements the {@code MidiDeviceReceiver} interface. The 215 * corresponding {@code Receiver} method calls will be forwarded to the 216 * native receiver. 217 * <p> 218 * If this method returns successfully, the {@link MidiDevice MidiDevice} 219 * the {@code Receiver} belongs to is opened implicitly, if it is not 220 * already open. It is possible to close an implicitly opened device by 221 * calling {@link Receiver#close close} on the returned {@code Receiver}. 222 * All open {@code Receiver} instances have to be closed in order to release 223 * system resources hold by the {@code MidiDevice}. For a detailed 224 * description of open/close behaviour see the class description of 225 * {@link MidiDevice MidiDevice}. 226 * 227 * @return the default MIDI receiver 228 * @throws MidiUnavailableException if the default receiver is not available 229 * due to resource restrictions, or no device providing receivers is 230 * installed in the system 231 */ 232 public static Receiver getReceiver() throws MidiUnavailableException { 233 // may throw MidiUnavailableException 234 MidiDevice device = getDefaultDeviceWrapper(Receiver.class); 235 Receiver receiver; 236 if (device instanceof ReferenceCountingDevice) { 237 receiver = ((ReferenceCountingDevice) device).getReceiverReferenceCounting(); 238 } else { 239 receiver = device.getReceiver(); 240 } 241 if (!(receiver instanceof MidiDeviceReceiver)) { 242 receiver = new MidiDeviceReceiverEnvelope(device, receiver); 243 } 244 return receiver; 245 } 246 247 /** 248 * Obtains a MIDI transmitter from an external MIDI port or other default 249 * source. The returned transmitter always implements the 250 * {@code MidiDeviceTransmitter} interface. 251 * <p> 252 * If the system property {@code javax.sound.midi.Transmitter} is defined or 253 * it is defined in the file "sound.properties", it is used to identify the 254 * device that provides the default transmitter. For details, refer to the 255 * {@link MidiSystem class description}. 256 * <p> 257 * If a native transmitter provided by the default device does not implement 258 * the {@code MidiDeviceTransmitter} interface, it will be wrapped in a 259 * wrapper class that implements the {@code MidiDeviceTransmitter} 260 * interface. The corresponding {@code Transmitter} method calls will be 261 * forwarded to the native transmitter. 262 * <p> 263 * If this method returns successfully, the {@link MidiDevice MidiDevice} 264 * the {@code Transmitter} belongs to is opened implicitly, if it is not 265 * already open. It is possible to close an implicitly opened device by 266 * calling {@link Transmitter#close close} on the returned 267 * {@code Transmitter}. All open {@code Transmitter} instances have to be 268 * closed in order to release system resources hold by the 269 * {@code MidiDevice}. For a detailed description of open/close behaviour 270 * see the class description of {@link MidiDevice MidiDevice}. 271 * 272 * @return the default MIDI transmitter 273 * @throws MidiUnavailableException if the default transmitter is not 274 * available due to resource restrictions, or no device providing 275 * transmitters is installed in the system 276 */ 277 public static Transmitter getTransmitter() throws MidiUnavailableException { 278 // may throw MidiUnavailableException 279 MidiDevice device = getDefaultDeviceWrapper(Transmitter.class); 280 Transmitter transmitter; 281 if (device instanceof ReferenceCountingDevice) { 282 transmitter = ((ReferenceCountingDevice) device).getTransmitterReferenceCounting(); 283 } else { 284 transmitter = device.getTransmitter(); 285 } 286 if (!(transmitter instanceof MidiDeviceTransmitter)) { 287 transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter); 288 } 289 return transmitter; 290 } 291 292 /** 293 * Obtains the default synthesizer. 294 * <p> 295 * If the system property {@code javax.sound.midi.Synthesizer} is defined or 296 * it is defined in the file "sound.properties", it is used to identify the 297 * default synthesizer. For details, refer to the 298 * {@link MidiSystem class description}. 299 * 300 * @return the default synthesizer 301 * @throws MidiUnavailableException if the synthesizer is not available due 302 * to resource restrictions, or no synthesizer is installed in the 303 * system 304 */ 305 public static Synthesizer getSynthesizer() throws MidiUnavailableException { 306 // may throw MidiUnavailableException 307 return (Synthesizer) getDefaultDeviceWrapper(Synthesizer.class); 308 } 309 310 /** 311 * Obtains the default {@code Sequencer}, connected to a default device. The 312 * returned {@code Sequencer} instance is connected to the default 313 * {@code Synthesizer}, as returned by {@link #getSynthesizer}. If there is 314 * no {@code Synthesizer} available, or the default {@code Synthesizer} 315 * cannot be opened, the {@code sequencer} is connected to the default 316 * {@code Receiver}, as returned by {@link #getReceiver}. The connection is 317 * made by retrieving a {@code Transmitter} instance from the 318 * {@code Sequencer} and setting its {@code Receiver}. Closing and 319 * re-opening the sequencer will restore the connection to the default 320 * device. 321 * <p> 322 * This method is equivalent to calling {@code getSequencer(true)}. 323 * <p> 324 * If the system property {@code javax.sound.midi.Sequencer} is defined or 325 * it is defined in the file "sound.properties", it is used to identify the 326 * default sequencer. For details, refer to the 327 * {@link MidiSystem class description}. 328 * 329 * @return the default sequencer, connected to a default Receiver 330 * @throws MidiUnavailableException if the sequencer is not available due to 331 * resource restrictions, or there is no {@code Receiver} available 332 * by any installed {@code MidiDevice}, or no sequencer is installed 333 * in the system 334 * @see #getSequencer(boolean) 335 * @see #getSynthesizer 336 * @see #getReceiver 337 */ 338 public static Sequencer getSequencer() throws MidiUnavailableException { 339 return getSequencer(true); 340 } 341 342 /** 343 * Obtains the default {@code Sequencer}, optionally connected to a default 344 * device. 345 * <p> 346 * If {@code connected} is true, the returned {@code Sequencer} instance is 347 * connected to the default {@code Synthesizer}, as returned by 348 * {@link #getSynthesizer}. If there is no {@code Synthesizer} available, or 349 * the default {@code Synthesizer} cannot be opened, the {@code sequencer} 350 * is connected to the default {@code Receiver}, as returned by 351 * {@link #getReceiver}. The connection is made by retrieving a 352 * {@code Transmitter} instance from the {@code Sequencer} and setting its 353 * {@code Receiver}. Closing and re-opening the sequencer will restore the 354 * connection to the default device. 355 * <p> 356 * If {@code connected} is false, the returned {@code Sequencer} instance is 357 * not connected, it has no open {@code Transmitters}. In order to play the 358 * sequencer on a MIDI device, or a {@code Synthesizer}, it is necessary to 359 * get a {@code Transmitter} and set its {@code Receiver}. 360 * <p> 361 * If the system property {@code javax.sound.midi.Sequencer} is defined or 362 * it is defined in the file "sound.properties", it is used to identify the 363 * default sequencer. For details, refer to the 364 * {@link MidiSystem class description}. 365 * 366 * @param connected whether or not the returned {@code Sequencer} is 367 * connected to the default {@code Synthesizer} 368 * @return the default sequencer 369 * @throws MidiUnavailableException if the sequencer is not available due to 370 * resource restrictions, or no sequencer is installed in the 371 * system, or if {@code connected} is true, and there is no 372 * {@code Receiver} available by any installed {@code MidiDevice} 373 * @see #getSynthesizer 374 * @see #getReceiver 375 * @since 1.5 376 */ 377 public static Sequencer getSequencer(boolean connected) 378 throws MidiUnavailableException { 379 Sequencer seq = (Sequencer) getDefaultDeviceWrapper(Sequencer.class); 380 381 if (connected) { 382 // IMPORTANT: this code needs to be synch'ed with 383 // all AutoConnectSequencer instances, 384 // (e.g. RealTimeSequencer) because the 385 // same algorithm for synth retrieval 386 // needs to be used! 387 388 Receiver rec = null; 389 MidiUnavailableException mue = null; 390 391 // first try to connect to the default synthesizer 392 try { 393 Synthesizer synth = getSynthesizer(); 394 if (synth instanceof ReferenceCountingDevice) { 395 rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting(); 396 } else { 397 synth.open(); 398 try { 399 rec = synth.getReceiver(); 400 } finally { 401 // make sure that the synth is properly closed 402 if (rec == null) { 403 synth.close(); 404 } 405 } 406 } 407 } catch (MidiUnavailableException e) { 408 // something went wrong with synth 409 if (e instanceof MidiUnavailableException) { 410 mue = e; 411 } 412 } 413 if (rec == null) { 414 // then try to connect to the default Receiver 415 try { 416 rec = MidiSystem.getReceiver(); 417 } catch (Exception e) { 418 // something went wrong. Nothing to do then! 419 if (e instanceof MidiUnavailableException) { 420 mue = (MidiUnavailableException) e; 421 } 422 } 423 } 424 if (rec != null) { 425 seq.getTransmitter().setReceiver(rec); 426 if (seq instanceof AutoConnectSequencer) { 427 ((AutoConnectSequencer) seq).setAutoConnect(rec); 428 } 429 } else { 430 if (mue != null) { 431 throw mue; 432 } 433 throw new MidiUnavailableException("no receiver available"); 434 } 435 } 436 return seq; 437 } 438 439 /** 440 * Constructs a MIDI sound bank by reading it from the specified stream. The 441 * stream must point to a valid MIDI soundbank file. In general, MIDI 442 * soundbank providers may need to read some data from the stream before 443 * determining whether they support it. These parsers must be able to mark 444 * the stream, read enough data to determine whether they support the 445 * stream, and, if not, reset the stream's read pointer to its original 446 * position. If the input stream does not support this, this method may fail 447 * with an {@code IOException}. 448 * 449 * @param stream the source of the sound bank data 450 * @return the sound bank 451 * @throws InvalidMidiDataException if the stream does not point to valid 452 * MIDI soundbank data recognized by the system 453 * @throws IOException if an I/O error occurred when loading the soundbank 454 * @throws NullPointerException if {@code stream} is {@code null} 455 * @see InputStream#markSupported 456 * @see InputStream#mark 457 */ 458 public static Soundbank getSoundbank(final InputStream stream) 459 throws InvalidMidiDataException, IOException { 460 Objects.requireNonNull(stream); 461 462 SoundbankReader sp = null; 463 Soundbank s = null; 464 465 List<SoundbankReader> providers = getSoundbankReaders(); 466 467 for(int i = 0; i < providers.size(); i++) { 468 sp = providers.get(i); 469 s = sp.getSoundbank(stream); 470 471 if( s!= null) { 472 return s; 473 } 474 } 475 throw new InvalidMidiDataException("cannot get soundbank from stream"); 476 477 } 478 479 /** 480 * Constructs a {@code Soundbank} by reading it from the specified URL. The 481 * URL must point to a valid MIDI soundbank file. 482 * 483 * @param url the source of the sound bank data 484 * @return the sound bank 485 * @throws InvalidMidiDataException if the URL does not point to valid MIDI 486 * soundbank data recognized by the system 487 * @throws IOException if an I/O error occurred when loading the soundbank 488 * @throws NullPointerException if {@code url} is {@code null} 489 */ 490 public static Soundbank getSoundbank(final URL url) 491 throws InvalidMidiDataException, IOException { 492 Objects.requireNonNull(url); 493 494 SoundbankReader sp = null; 495 Soundbank s = null; 496 497 List<SoundbankReader> providers = getSoundbankReaders(); 498 499 for(int i = 0; i < providers.size(); i++) { 500 sp = providers.get(i); 501 s = sp.getSoundbank(url); 502 503 if( s!= null) { 504 return s; 505 } 506 } 507 throw new InvalidMidiDataException("cannot get soundbank from stream"); 508 509 } 510 511 /** 512 * Constructs a {@code Soundbank} by reading it from the specified 513 * {@code File}. The {@code File} must point to a valid MIDI soundbank file. 514 * 515 * @param file the source of the sound bank data 516 * @return the sound bank 517 * @throws InvalidMidiDataException if the {@code File} does not point to 518 * valid MIDI soundbank data recognized by the system 519 * @throws IOException if an I/O error occurred when loading the soundbank 520 * @throws NullPointerException if {@code file} is {@code null} 521 */ 522 public static Soundbank getSoundbank(final File file) 523 throws InvalidMidiDataException, IOException { 524 Objects.requireNonNull(file); 525 526 SoundbankReader sp = null; 527 Soundbank s = null; 528 529 List<SoundbankReader> providers = getSoundbankReaders(); 530 531 for(int i = 0; i < providers.size(); i++) { 532 sp = providers.get(i); 533 s = sp.getSoundbank(file); 534 535 if( s!= null) { 536 return s; 537 } 538 } 539 throw new InvalidMidiDataException("cannot get soundbank from stream"); 540 } 541 542 /** 543 * Obtains the MIDI file format of the data in the specified input stream. 544 * The stream must point to valid MIDI file data for a file type recognized 545 * by the system. 546 * <p> 547 * This method and/or the code it invokes may need to read some data from 548 * the stream to determine whether its data format is supported. The 549 * implementation may therefore need to mark the stream, read enough data to 550 * determine whether it is in a supported format, and reset the stream's 551 * read pointer to its original position. If the input stream does not 552 * permit this set of operations, this method may fail with an 553 * {@code IOException}. 554 * <p> 555 * This operation can only succeed for files of a type which can be parsed 556 * by an installed file reader. It may fail with an 557 * {@code InvalidMidiDataException} even for valid files if no compatible 558 * file reader is installed. It will also fail with an 559 * {@code InvalidMidiDataException} if a compatible file reader is 560 * installed, but encounters errors while determining the file format. 561 * 562 * @param stream the input stream from which file format information should 563 * be extracted 564 * @return an {@code MidiFileFormat} object describing the MIDI file format 565 * @throws InvalidMidiDataException if the stream does not point to valid 566 * MIDI file data recognized by the system 567 * @throws IOException if an I/O exception occurs while accessing the 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 1027 /** 1028 * Obtains the list of MidiDeviceProviders installed on the system. 1029 * 1030 * @return the list of MidiDeviceProviders installed on the system 1031 */ 1032 @SuppressWarnings("unchecked") 1033 private static List<MidiDeviceProvider> getMidiDeviceProviders() { 1034 return (List<MidiDeviceProvider>) getProviders(MidiDeviceProvider.class); 1035 } 1036 1037 /** 1038 * Obtains the list of SoundbankReaders installed on the system. 1039 * 1040 * @return the list of SoundbankReaders installed on the system 1041 */ 1042 @SuppressWarnings("unchecked") 1043 private static List<SoundbankReader> getSoundbankReaders() { 1044 return (List<SoundbankReader>) getProviders(SoundbankReader.class); 1045 } 1046 1047 /** 1048 * Obtains the list of MidiFileWriters installed on the system. 1049 * 1050 * @return the list of MidiFileWriters installed on the system 1051 */ 1052 @SuppressWarnings("unchecked") 1053 private static List<MidiFileWriter> getMidiFileWriters() { 1054 return (List<MidiFileWriter>) getProviders(MidiFileWriter.class); 1055 } 1056 1057 /** 1058 * Obtains the list of MidiFileReaders installed on the system. 1059 * 1060 * @return the list of MidiFileReaders installed on the system 1061 */ 1062 @SuppressWarnings("unchecked") 1063 private static List<MidiFileReader> getMidiFileReaders() { 1064 return (List<MidiFileReader>) getProviders(MidiFileReader.class); 1065 } 1066 1067 /** 1068 * Attempts to locate and return a default MidiDevice of the specified type. 1069 * This method wraps {@link #getDefaultDevice}. It catches the 1070 * {@code IllegalArgumentException} thrown by {@code getDefaultDevice} and 1071 * instead throws a {@code MidiUnavailableException}, with the catched 1072 * exception chained. 1073 * 1074 * @param deviceClass The requested device type, one of Synthesizer.class, 1075 * Sequencer.class, Receiver.class or Transmitter.class 1076 * @return default MidiDevice of the specified type 1077 * @throws MidiUnavailableException on failure 1078 */ 1079 private static MidiDevice getDefaultDeviceWrapper(Class<?> deviceClass) 1080 throws MidiUnavailableException{ 1081 try { 1082 return getDefaultDevice(deviceClass); 1083 } catch (IllegalArgumentException iae) { 1084 MidiUnavailableException mae = new MidiUnavailableException(); 1085 mae.initCause(iae); 1086 throw mae; 1087 } 1088 } 1089 1090 /** 1091 * Attempts to locate and return a default MidiDevice of the specified type. 1092 * 1093 * @param deviceClass The requested device type, one of Synthesizer.class, 1094 * Sequencer.class, Receiver.class or Transmitter.class 1095 * @return default MidiDevice of the specified type. 1096 * @throws IllegalArgumentException on failure 1097 */ 1098 private static MidiDevice getDefaultDevice(Class<?> deviceClass) { 1099 List<MidiDeviceProvider> providers = getMidiDeviceProviders(); 1100 String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass); 1101 String instanceName = JDK13Services.getDefaultInstanceName(deviceClass); 1102 MidiDevice device; 1103 1104 if (providerClassName != null) { 1105 MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers); 1106 if (defaultProvider != null) { 1107 if (instanceName != null) { 1108 device = getNamedDevice(instanceName, defaultProvider, deviceClass); 1109 if (device != null) { 1110 return device; 1111 } 1112 } 1113 device = getFirstDevice(defaultProvider, deviceClass); 1114 if (device != null) { 1115 return device; 1116 } 1117 } 1118 } 1119 1120 /* 1121 * - Provider class not specified or cannot be found, or 1122 * - provider class specified, and no appropriate device available, or 1123 * - provider class and instance specified and instance cannot be found 1124 * or is not appropriate 1125 */ 1126 if (instanceName != null) { 1127 device = getNamedDevice(instanceName, providers, deviceClass); 1128 if (device != null) { 1129 return device; 1130 } 1131 } 1132 1133 /* 1134 * No defaults are specified, or if something is specified, everything 1135 * failed 1136 */ 1137 device = getFirstDevice(providers, deviceClass); 1138 if (device != null) { 1139 return device; 1140 } 1141 throw new IllegalArgumentException("Requested device not installed"); 1142 } 1143 1144 /** 1145 * Return a MidiDeviceProvider of a given class from the list of 1146 * MidiDeviceProviders. 1147 * 1148 * @param providerClassName The class name of the provider to be returned 1149 * @param providers The list of MidiDeviceProviders that is searched 1150 * @return A MidiDeviceProvider of the requested class, or null if none is 1151 * found 1152 */ 1153 private static MidiDeviceProvider getNamedProvider(String providerClassName, 1154 List<MidiDeviceProvider> providers) { 1155 for(int i = 0; i < providers.size(); i++) { 1156 MidiDeviceProvider provider = providers.get(i); 1157 if (provider.getClass().getName().equals(providerClassName)) { 1158 return provider; 1159 } 1160 } 1161 return null; 1162 } 1163 1164 /** 1165 * Return a MidiDevice with a given name from a given MidiDeviceProvider. 1166 * 1167 * @param deviceName The name of the MidiDevice to be returned 1168 * @param provider The MidiDeviceProvider to check for MidiDevices 1169 * @param deviceClass The requested device type, one of Synthesizer.class, 1170 * Sequencer.class, Receiver.class or Transmitter.class 1171 * @return A MidiDevice matching the requirements, or null if none is found 1172 */ 1173 private static MidiDevice getNamedDevice(String deviceName, 1174 MidiDeviceProvider provider, 1175 Class<?> deviceClass) { 1176 MidiDevice device; 1177 // try to get MIDI port 1178 device = getNamedDevice(deviceName, provider, deviceClass, 1179 false, false); 1180 if (device != null) { 1181 return device; 1182 } 1183 1184 if (deviceClass == Receiver.class) { 1185 // try to get Synthesizer 1186 device = getNamedDevice(deviceName, provider, deviceClass, 1187 true, false); 1188 if (device != null) { 1189 return device; 1190 } 1191 } 1192 1193 return null; 1194 } 1195 1196 /** 1197 * Return a MidiDevice with a given name from a given MidiDeviceProvider. 1198 * 1199 * @param deviceName The name of the MidiDevice to be returned 1200 * @param provider The MidiDeviceProvider to check for MidiDevices 1201 * @param deviceClass The requested device type, one of Synthesizer.class, 1202 * Sequencer.class, Receiver.class or Transmitter.class 1203 * @param allowSynthesizer if true, Synthesizers are considered 1204 * appropriate. Otherwise only pure MidiDevices are considered 1205 * appropriate (unless allowSequencer is true). This flag only has 1206 * an effect for deviceClass Receiver and Transmitter. For other 1207 * device classes (Sequencer and Synthesizer), this flag has no 1208 * effect. 1209 * @param allowSequencer if true, Sequencers are considered appropriate. 1210 * Otherwise only pure MidiDevices are considered appropriate 1211 * (unless allowSynthesizer is true). This flag only has an effect 1212 * for deviceClass Receiver and Transmitter. For other device 1213 * classes (Sequencer and Synthesizer), this flag has no effect. 1214 * @return A MidiDevice matching the requirements, or null if none is found 1215 */ 1216 private static MidiDevice getNamedDevice(String deviceName, 1217 MidiDeviceProvider provider, 1218 Class<?> deviceClass, 1219 boolean allowSynthesizer, 1220 boolean allowSequencer) { 1221 MidiDevice.Info[] infos = provider.getDeviceInfo(); 1222 for (int i = 0; i < infos.length; i++) { 1223 if (infos[i].getName().equals(deviceName)) { 1224 MidiDevice device = provider.getDevice(infos[i]); 1225 if (isAppropriateDevice(device, deviceClass, 1226 allowSynthesizer, allowSequencer)) { 1227 return device; 1228 } 1229 } 1230 } 1231 return null; 1232 } 1233 1234 /** 1235 * Return a MidiDevice with a given name from a list of MidiDeviceProviders. 1236 * 1237 * @param deviceName The name of the MidiDevice to be returned 1238 * @param providers The List of MidiDeviceProviders to check for 1239 * MidiDevices 1240 * @param deviceClass The requested device type, one of Synthesizer.class, 1241 * Sequencer.class, Receiver.class or Transmitter.class 1242 * @return A Mixer matching the requirements, or null if none is found 1243 */ 1244 private static MidiDevice getNamedDevice(String deviceName, 1245 List<MidiDeviceProvider> providers, 1246 Class<?> deviceClass) { 1247 MidiDevice device; 1248 // try to get MIDI port 1249 device = getNamedDevice(deviceName, providers, deviceClass, 1250 false, false); 1251 if (device != null) { 1252 return device; 1253 } 1254 1255 if (deviceClass == Receiver.class) { 1256 // try to get Synthesizer 1257 device = getNamedDevice(deviceName, providers, deviceClass, 1258 true, false); 1259 if (device != null) { 1260 return device; 1261 } 1262 } 1263 1264 return null; 1265 } 1266 1267 /** 1268 * Return a MidiDevice with a given name from a list of MidiDeviceProviders. 1269 * 1270 * @param deviceName The name of the MidiDevice to be returned 1271 * @param providers The List of MidiDeviceProviders to check for 1272 * MidiDevices 1273 * @param deviceClass The requested device type, one of Synthesizer.class, 1274 * Sequencer.class, Receiver.class or Transmitter.class 1275 * @param allowSynthesizer if true, Synthesizers are considered 1276 * appropriate. Otherwise only pure MidiDevices are considered 1277 * appropriate (unless allowSequencer is true). This flag only has 1278 * an effect for deviceClass Receiver and Transmitter. For other 1279 * device classes (Sequencer and Synthesizer), this flag has no 1280 * effect. 1281 * @param allowSequencer if true, Sequencers are considered appropriate. 1282 * Otherwise only pure MidiDevices are considered appropriate 1283 * (unless allowSynthesizer is true). This flag only has an effect 1284 * for deviceClass Receiver and Transmitter. For other device 1285 * classes (Sequencer and Synthesizer), this flag has no effect. 1286 * @return A Mixer matching the requirements, or null if none is found 1287 */ 1288 private static MidiDevice getNamedDevice(String deviceName, 1289 List<MidiDeviceProvider> providers, 1290 Class<?> deviceClass, 1291 boolean allowSynthesizer, 1292 boolean allowSequencer) { 1293 for(int i = 0; i < providers.size(); i++) { 1294 MidiDeviceProvider provider = providers.get(i); 1295 MidiDevice device = getNamedDevice(deviceName, provider, 1296 deviceClass, 1297 allowSynthesizer, 1298 allowSequencer); 1299 if (device != null) { 1300 return device; 1301 } 1302 } 1303 return null; 1304 } 1305 1306 /** 1307 * From a given MidiDeviceProvider, return the first appropriate device. 1308 * 1309 * @param provider The MidiDeviceProvider to check for MidiDevices 1310 * @param deviceClass The requested device type, one of Synthesizer.class, 1311 * Sequencer.class, Receiver.class or Transmitter.class 1312 * @return A MidiDevice is considered appropriate, or null if no appropriate 1313 * device is found 1314 */ 1315 private static MidiDevice getFirstDevice(MidiDeviceProvider provider, 1316 Class<?> deviceClass) { 1317 MidiDevice device; 1318 // try to get MIDI port 1319 device = getFirstDevice(provider, deviceClass, 1320 false, false); 1321 if (device != null) { 1322 return device; 1323 } 1324 1325 if (deviceClass == Receiver.class) { 1326 // try to get Synthesizer 1327 device = getFirstDevice(provider, deviceClass, 1328 true, false); 1329 if (device != null) { 1330 return device; 1331 } 1332 } 1333 1334 return null; 1335 } 1336 1337 /** 1338 * From a given MidiDeviceProvider, return the first appropriate device. 1339 * 1340 * @param provider The MidiDeviceProvider to check for MidiDevices 1341 * @param deviceClass The requested device type, one of Synthesizer.class, 1342 * Sequencer.class, Receiver.class or Transmitter.class 1343 * @param allowSynthesizer if true, Synthesizers are considered 1344 * appropriate. Otherwise only pure MidiDevices are considered 1345 * appropriate (unless allowSequencer is true). This flag only has 1346 * an effect for deviceClass Receiver and Transmitter. For other 1347 * device classes (Sequencer and Synthesizer), this flag has no 1348 * effect. 1349 * @param allowSequencer if true, Sequencers are considered appropriate. 1350 * Otherwise only pure MidiDevices are considered appropriate 1351 * (unless allowSynthesizer is true). This flag only has an effect 1352 * for deviceClass Receiver and Transmitter. For other device 1353 * classes (Sequencer and Synthesizer), this flag has no effect. 1354 * @return A MidiDevice is considered appropriate, or null if no appropriate 1355 * device is found 1356 */ 1357 private static MidiDevice getFirstDevice(MidiDeviceProvider provider, 1358 Class<?> deviceClass, 1359 boolean allowSynthesizer, 1360 boolean allowSequencer) { 1361 MidiDevice.Info[] infos = provider.getDeviceInfo(); 1362 for (int j = 0; j < infos.length; j++) { 1363 MidiDevice device = provider.getDevice(infos[j]); 1364 if (isAppropriateDevice(device, deviceClass, 1365 allowSynthesizer, allowSequencer)) { 1366 return device; 1367 } 1368 } 1369 return null; 1370 } 1371 1372 /** 1373 * From a List of MidiDeviceProviders, return the first appropriate 1374 * MidiDevice. 1375 * 1376 * @param providers The List of MidiDeviceProviders to search 1377 * @param deviceClass The requested device type, one of Synthesizer.class, 1378 * Sequencer.class, Receiver.class or Transmitter.class 1379 * @return A MidiDevice that is considered appropriate, or null if none is 1380 * found 1381 */ 1382 private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers, 1383 Class<?> deviceClass) { 1384 MidiDevice device; 1385 // try to get MIDI port 1386 device = getFirstDevice(providers, deviceClass, 1387 false, false); 1388 if (device != null) { 1389 return device; 1390 } 1391 1392 if (deviceClass == Receiver.class) { 1393 // try to get Synthesizer 1394 device = getFirstDevice(providers, deviceClass, 1395 true, false); 1396 if (device != null) { 1397 return device; 1398 } 1399 } 1400 1401 return null; 1402 } 1403 1404 /** 1405 * From a List of MidiDeviceProviders, return the first appropriate 1406 * MidiDevice. 1407 * 1408 * @param providers The List of MidiDeviceProviders to search 1409 * @param deviceClass The requested device type, one of Synthesizer.class, 1410 * Sequencer.class, Receiver.class or Transmitter.class 1411 * @param allowSynthesizer if true, Synthesizers are considered 1412 * appropriate. Otherwise only pure MidiDevices are considered 1413 * appropriate (unless allowSequencer is true). This flag only has 1414 * an effect for deviceClass Receiver and Transmitter. For other 1415 * device classes (Sequencer and Synthesizer), this flag has no 1416 * effect. 1417 * @param allowSequencer if true, Sequencers are considered appropriate. 1418 * Otherwise only pure MidiDevices are considered appropriate 1419 * (unless allowSynthesizer is true). This flag only has an effect 1420 * for deviceClass Receiver and Transmitter. For other device 1421 * classes (Sequencer and Synthesizer), this flag has no effect. 1422 * @return A MidiDevice that is considered appropriate, or null if none is 1423 * found 1424 */ 1425 private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers, 1426 Class<?> deviceClass, 1427 boolean allowSynthesizer, 1428 boolean allowSequencer) { 1429 for(int i = 0; i < providers.size(); i++) { 1430 MidiDeviceProvider provider = providers.get(i); 1431 MidiDevice device = getFirstDevice(provider, deviceClass, 1432 allowSynthesizer, 1433 allowSequencer); 1434 if (device != null) { 1435 return device; 1436 } 1437 } 1438 return null; 1439 } 1440 1441 /** 1442 * Checks if a MidiDevice is appropriate. If deviceClass is Synthesizer or 1443 * Sequencer, a device implementing the respective interface is considered 1444 * appropriate. If deviceClass is Receiver or Transmitter, a device is 1445 * considered appropriate if it implements neither Synthesizer nor 1446 * Transmitter, and if it can provide at least one Receiver or Transmitter, 1447 * respectively. 1448 * 1449 * @param device the MidiDevice to test 1450 * @param deviceClass The requested device type, one of Synthesizer.class, 1451 * Sequencer.class, Receiver.class or Transmitter.class 1452 * @param allowSynthesizer if true, Synthesizers are considered 1453 * appropriate. Otherwise only pure MidiDevices are considered 1454 * appropriate (unless allowSequencer is true). This flag only has 1455 * an effect for deviceClass Receiver and Transmitter. For other 1456 * device classes (Sequencer and Synthesizer), this flag has no 1457 * effect. 1458 * @param allowSequencer if true, Sequencers are considered appropriate. 1459 * Otherwise only pure MidiDevices are considered appropriate 1460 * (unless allowSynthesizer is true). This flag only has an effect 1461 * for deviceClass Receiver and Transmitter. For other device 1462 * classes (Sequencer and Synthesizer), this flag has no effect. 1463 * @return true if the device is considered appropriate according to the 1464 * rules given above, false otherwise 1465 */ 1466 private static boolean isAppropriateDevice(MidiDevice device, 1467 Class<?> deviceClass, 1468 boolean allowSynthesizer, 1469 boolean allowSequencer) { 1470 if (deviceClass.isInstance(device)) { 1471 // This clause is for deviceClass being either Synthesizer 1472 // or Sequencer. 1473 return true; 1474 } else { 1475 // Now the case that deviceClass is Transmitter or 1476 // Receiver. If neither allowSynthesizer nor allowSequencer is 1477 // true, we require device instances to be 1478 // neither Synthesizer nor Sequencer, since we only want 1479 // devices representing MIDI ports. 1480 // Otherwise, the respective type is accepted, too 1481 if ( (! (device instanceof Sequencer) && 1482 ! (device instanceof Synthesizer) ) || 1483 ((device instanceof Sequencer) && allowSequencer) || 1484 ((device instanceof Synthesizer) && allowSynthesizer)) { 1485 // And of cource, the device has to be able to provide 1486 // Receivers or Transmitters. 1487 if ((deviceClass == Receiver.class && 1488 device.getMaxReceivers() != 0) || 1489 (deviceClass == Transmitter.class && 1490 device.getMaxTransmitters() != 0)) { 1491 return true; 1492 } 1493 } 1494 } 1495 return false; 1496 } 1497 1498 /** 1499 * Obtains the set of services currently installed on the system using the 1500 * SPI mechanism in 1.3. 1501 * 1502 * @param providerClass The type of providers requested. This should be one 1503 * of AudioFileReader.class, AudioFileWriter.class, 1504 * FormatConversionProvider.class, MixerProvider.class, 1505 * MidiDeviceProvider.class, MidiFileReader.class, 1506 * MidiFileWriter.class or SoundbankReader.class. 1507 * @return a List of instances of providers for the requested service. If no 1508 * providers are available, a List of length 0 will be returned. 1509 */ 1510 private static List<?> getProviders(Class<?> providerClass) { 1511 return JDK13Services.getProviders(providerClass); 1512 } 1513 }