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