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