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