Path: blob/master/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java
41159 views
/*1* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.sound.midi;2627import java.io.File;28import java.io.IOException;29import java.io.InputStream;30import java.io.OutputStream;31import java.net.URL;32import java.util.ArrayList;33import java.util.Collections;34import java.util.HashSet;35import java.util.Iterator;36import java.util.List;37import java.util.Objects;38import java.util.Properties;39import java.util.Set;4041import javax.sound.midi.spi.MidiDeviceProvider;42import javax.sound.midi.spi.MidiFileReader;43import javax.sound.midi.spi.MidiFileWriter;44import javax.sound.midi.spi.SoundbankReader;4546import com.sun.media.sound.AutoConnectSequencer;47import com.sun.media.sound.JDK13Services;48import com.sun.media.sound.MidiDeviceReceiverEnvelope;49import com.sun.media.sound.MidiDeviceTransmitterEnvelope;50import com.sun.media.sound.ReferenceCountingDevice;5152/**53* The {@code MidiSystem} class provides access to the installed MIDI system54* resources, including devices such as synthesizers, sequencers, and MIDI input55* and output ports. A typical simple MIDI application might begin by invoking56* one or more {@code MidiSystem} methods to learn what devices are installed57* and to obtain the ones needed in that application.58* <p>59* The class also has methods for reading files, streams, and URLs that contain60* standard MIDI file data or soundbanks. You can query the {@code MidiSystem}61* for the format of a specified MIDI file.62* <p>63* You cannot instantiate a {@code MidiSystem}; all the methods are static.64* <p>65* Properties can be used to specify default MIDI devices. Both system66* properties and a properties file are considered. The "sound.properties"67* properties file is read from an implementation-specific location (typically68* it is the {@code conf} directory in the Java installation directory).69* The optional "javax.sound.config.file" system property can be used to specify70* the properties file that will be read as the initial configuration. If a71* property exists both as a system property and in the properties file, the72* system property takes precedence. If none is specified, a suitable default is73* chosen among the available devices. The syntax of the properties file is74* specified in {@link Properties#load(InputStream) Properties.load}. The75* following table lists the available property keys and which methods consider76* them:77*78* <table class="striped">79* <caption>MIDI System Property Keys</caption>80* <thead>81* <tr>82* <th scope="col">Property Key83* <th scope="col">Interface84* <th scope="col">Affected Method85* </thead>86* <tbody>87* <tr>88* <th scope="row">{@code javax.sound.midi.Receiver}89* <td>{@link Receiver}90* <td>{@link #getReceiver}91* <tr>92* <th scope="row">{@code javax.sound.midi.Sequencer}93* <td>{@link Sequencer}94* <td>{@link #getSequencer}95* <tr>96* <th scope="row">{@code javax.sound.midi.Synthesizer}97* <td>{@link Synthesizer}98* <td>{@link #getSynthesizer}99* <tr>100* <th scope="row">{@code javax.sound.midi.Transmitter}101* <td>{@link Transmitter}102* <td>{@link #getTransmitter}103* </tbody>104* </table>105*106* The property value consists of the provider class name and the device name,107* separated by the hash mark ("#"). The provider class name is the108* fully-qualified name of a concrete109* {@link MidiDeviceProvider MIDI device provider} class. The device name is110* matched against the {@code String} returned by the {@code getName} method of111* {@code MidiDevice.Info}. Either the class name, or the device name may be112* omitted. If only the class name is specified, the trailing hash mark is113* optional.114* <p>115* If the provider class is specified, and it can be successfully retrieved from116* the installed providers, the list of {@code MidiDevice.Info} objects is117* retrieved from the provider. Otherwise, or when these devices do not provide118* a subsequent match, the list is retrieved from {@link #getMidiDeviceInfo} to119* contain all available {@code MidiDevice.Info} objects.120* <p>121* If a device name is specified, the resulting list of {@code MidiDevice.Info}122* objects is searched: the first one with a matching name, and whose123* {@code MidiDevice} implements the respective interface, will be returned. If124* no matching {@code MidiDevice.Info} object is found, or the device name is125* not specified, the first suitable device from the resulting list will be126* returned. For Sequencer and Synthesizer, a device is suitable if it127* implements the respective interface; whereas for Receiver and Transmitter, a128* device is suitable if it implements neither Sequencer nor Synthesizer and129* provides at least one Receiver or Transmitter, respectively.130* <p>131* For example, the property {@code javax.sound.midi.Receiver} with a value132* {@code "com.sun.media.sound.MidiProvider#SunMIDI1"} will have the following133* consequences when {@code getReceiver} is called: if the class134* {@code com.sun.media.sound.MidiProvider} exists in the list of installed MIDI135* device providers, the first {@code Receiver} device with name136* {@code "SunMIDI1"} will be returned. If it cannot be found, the first137* {@code Receiver} from that provider will be returned, regardless of name. If138* there is none, the first {@code Receiver} with name {@code "SunMIDI1"} in the139* list of all devices (as returned by {@code getMidiDeviceInfo}) will be140* returned, or, if not found, the first {@code Receiver} that can be found in141* the list of all devices is returned. If that fails, too, a142* {@code MidiUnavailableException} is thrown.143*144* @author Kara Kytle145* @author Florian Bomers146* @author Matthias Pfisterer147*/148public class MidiSystem {149150/**151* Private no-args constructor for ensuring against instantiation.152*/153private MidiSystem() {154}155156/**157* Obtains an array of information objects representing the set of all MIDI158* devices available on the system. A returned information object can then159* be used to obtain the corresponding device object, by invoking160* {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}.161*162* @return an array of {@code MidiDevice.Info} objects, one for each163* installed MIDI device. If no such devices are installed, an array164* of length 0 is returned.165*/166public static MidiDevice.Info[] getMidiDeviceInfo() {167final List<MidiDevice.Info> allInfos = new ArrayList<>();168for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {169Collections.addAll(allInfos, provider.getDeviceInfo());170}171return allInfos.toArray(new MidiDevice.Info[allInfos.size()]);172}173174/**175* Obtains the requested MIDI device.176*177* @param info a device information object representing the desired device178* @return the requested device179* @throws MidiUnavailableException if the requested device is not available180* due to resource restrictions181* @throws IllegalArgumentException if the info object does not represent a182* MIDI device installed on the system183* @throws NullPointerException if {@code info} is {@code null}184* @see #getMidiDeviceInfo185*/186public static MidiDevice getMidiDevice(final MidiDevice.Info info)187throws MidiUnavailableException {188Objects.requireNonNull(info);189for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {190if (provider.isDeviceSupported(info)) {191return provider.getDevice(info);192}193}194throw new IllegalArgumentException(String.format(195"Requested device not installed: %s", info));196}197198/**199* Obtains a MIDI receiver from an external MIDI port or other default200* device. The returned receiver always implements the201* {@code MidiDeviceReceiver} interface.202* <p>203* If the system property {@code javax.sound.midi.Receiver} is defined or it204* is defined in the file "sound.properties", it is used to identify the205* device that provides the default receiver. For details, refer to the206* {@link MidiSystem class description}.207* <p>208* If a suitable MIDI port is not available, the Receiver is retrieved from209* an installed synthesizer.210* <p>211* If a native receiver provided by the default device does not implement212* the {@code MidiDeviceReceiver} interface, it will be wrapped in a wrapper213* class that implements the {@code MidiDeviceReceiver} interface. The214* corresponding {@code Receiver} method calls will be forwarded to the215* native receiver.216* <p>217* If this method returns successfully, the {@link MidiDevice MidiDevice}218* the {@code Receiver} belongs to is opened implicitly, if it is not219* already open. It is possible to close an implicitly opened device by220* calling {@link Receiver#close close} on the returned {@code Receiver}.221* All open {@code Receiver} instances have to be closed in order to release222* system resources hold by the {@code MidiDevice}. For a detailed223* description of open/close behaviour see the class description of224* {@link MidiDevice MidiDevice}.225*226* @return the default MIDI receiver227* @throws MidiUnavailableException if the default receiver is not available228* due to resource restrictions, or no device providing receivers is229* installed in the system230*/231public static Receiver getReceiver() throws MidiUnavailableException {232// may throw MidiUnavailableException233MidiDevice device = getDefaultDeviceWrapper(Receiver.class);234Receiver receiver;235if (device instanceof ReferenceCountingDevice) {236receiver = ((ReferenceCountingDevice) device).getReceiverReferenceCounting();237} else {238receiver = device.getReceiver();239}240if (!(receiver instanceof MidiDeviceReceiver)) {241receiver = new MidiDeviceReceiverEnvelope(device, receiver);242}243return receiver;244}245246/**247* Obtains a MIDI transmitter from an external MIDI port or other default248* source. The returned transmitter always implements the249* {@code MidiDeviceTransmitter} interface.250* <p>251* If the system property {@code javax.sound.midi.Transmitter} is defined or252* it is defined in the file "sound.properties", it is used to identify the253* device that provides the default transmitter. For details, refer to the254* {@link MidiSystem class description}.255* <p>256* If a native transmitter provided by the default device does not implement257* the {@code MidiDeviceTransmitter} interface, it will be wrapped in a258* wrapper class that implements the {@code MidiDeviceTransmitter}259* interface. The corresponding {@code Transmitter} method calls will be260* forwarded to the native transmitter.261* <p>262* If this method returns successfully, the {@link MidiDevice MidiDevice}263* the {@code Transmitter} belongs to is opened implicitly, if it is not264* already open. It is possible to close an implicitly opened device by265* calling {@link Transmitter#close close} on the returned266* {@code Transmitter}. All open {@code Transmitter} instances have to be267* closed in order to release system resources hold by the268* {@code MidiDevice}. For a detailed description of open/close behaviour269* see the class description of {@link MidiDevice MidiDevice}.270*271* @return the default MIDI transmitter272* @throws MidiUnavailableException if the default transmitter is not273* available due to resource restrictions, or no device providing274* transmitters is installed in the system275*/276public static Transmitter getTransmitter() throws MidiUnavailableException {277// may throw MidiUnavailableException278MidiDevice device = getDefaultDeviceWrapper(Transmitter.class);279Transmitter transmitter;280if (device instanceof ReferenceCountingDevice) {281transmitter = ((ReferenceCountingDevice) device).getTransmitterReferenceCounting();282} else {283transmitter = device.getTransmitter();284}285if (!(transmitter instanceof MidiDeviceTransmitter)) {286transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter);287}288return transmitter;289}290291/**292* Obtains the default synthesizer.293* <p>294* If the system property {@code javax.sound.midi.Synthesizer} is defined or295* it is defined in the file "sound.properties", it is used to identify the296* default synthesizer. For details, refer to the297* {@link MidiSystem class description}.298*299* @return the default synthesizer300* @throws MidiUnavailableException if the synthesizer is not available due301* to resource restrictions, or no synthesizer is installed in the302* system303*/304public static Synthesizer getSynthesizer() throws MidiUnavailableException {305// may throw MidiUnavailableException306return (Synthesizer) getDefaultDeviceWrapper(Synthesizer.class);307}308309/**310* Obtains the default {@code Sequencer}, connected to a default device. The311* returned {@code Sequencer} instance is connected to the default312* {@code Synthesizer}, as returned by {@link #getSynthesizer}. If there is313* no {@code Synthesizer} available, or the default {@code Synthesizer}314* cannot be opened, the {@code sequencer} is connected to the default315* {@code Receiver}, as returned by {@link #getReceiver}. The connection is316* made by retrieving a {@code Transmitter} instance from the317* {@code Sequencer} and setting its {@code Receiver}. Closing and318* re-opening the sequencer will restore the connection to the default319* device.320* <p>321* This method is equivalent to calling {@code getSequencer(true)}.322* <p>323* If the system property {@code javax.sound.midi.Sequencer} is defined or324* it is defined in the file "sound.properties", it is used to identify the325* default sequencer. For details, refer to the326* {@link MidiSystem class description}.327*328* @return the default sequencer, connected to a default Receiver329* @throws MidiUnavailableException if the sequencer is not available due to330* resource restrictions, or there is no {@code Receiver} available331* by any installed {@code MidiDevice}, or no sequencer is installed332* in the system333* @see #getSequencer(boolean)334* @see #getSynthesizer335* @see #getReceiver336*/337public static Sequencer getSequencer() throws MidiUnavailableException {338return getSequencer(true);339}340341/**342* Obtains the default {@code Sequencer}, optionally connected to a default343* device.344* <p>345* If {@code connected} is true, the returned {@code Sequencer} instance is346* connected to the default {@code Synthesizer}, as returned by347* {@link #getSynthesizer}. If there is no {@code Synthesizer} available, or348* the default {@code Synthesizer} cannot be opened, the {@code sequencer}349* is connected to the default {@code Receiver}, as returned by350* {@link #getReceiver}. The connection is made by retrieving a351* {@code Transmitter} instance from the {@code Sequencer} and setting its352* {@code Receiver}. Closing and re-opening the sequencer will restore the353* connection to the default device.354* <p>355* If {@code connected} is false, the returned {@code Sequencer} instance is356* not connected, it has no open {@code Transmitters}. In order to play the357* sequencer on a MIDI device, or a {@code Synthesizer}, it is necessary to358* get a {@code Transmitter} and set its {@code Receiver}.359* <p>360* If the system property {@code javax.sound.midi.Sequencer} is defined or361* it is defined in the file "sound.properties", it is used to identify the362* default sequencer. For details, refer to the363* {@link MidiSystem class description}.364*365* @param connected whether or not the returned {@code Sequencer} is366* connected to the default {@code Synthesizer}367* @return the default sequencer368* @throws MidiUnavailableException if the sequencer is not available due to369* resource restrictions, or no sequencer is installed in the370* system, or if {@code connected} is true, and there is no371* {@code Receiver} available by any installed {@code MidiDevice}372* @see #getSynthesizer373* @see #getReceiver374* @since 1.5375*/376public static Sequencer getSequencer(boolean connected)377throws MidiUnavailableException {378Sequencer seq = (Sequencer) getDefaultDeviceWrapper(Sequencer.class);379380if (connected) {381// IMPORTANT: this code needs to be synch'ed with382// all AutoConnectSequencer instances,383// (e.g. RealTimeSequencer) because the384// same algorithm for synth retrieval385// needs to be used!386387Receiver rec = null;388MidiUnavailableException mue = null;389390// first try to connect to the default synthesizer391try {392Synthesizer synth = getSynthesizer();393if (synth instanceof ReferenceCountingDevice) {394rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting();395} else {396synth.open();397try {398rec = synth.getReceiver();399} finally {400// make sure that the synth is properly closed401if (rec == null) {402synth.close();403}404}405}406} catch (MidiUnavailableException e) {407// something went wrong with synth408if (e instanceof MidiUnavailableException) {409mue = e;410}411}412if (rec == null) {413// then try to connect to the default Receiver414try {415rec = MidiSystem.getReceiver();416} catch (Exception e) {417// something went wrong. Nothing to do then!418if (e instanceof MidiUnavailableException) {419mue = (MidiUnavailableException) e;420}421}422}423if (rec != null) {424seq.getTransmitter().setReceiver(rec);425if (seq instanceof AutoConnectSequencer) {426((AutoConnectSequencer) seq).setAutoConnect(rec);427}428} else {429if (mue != null) {430throw mue;431}432throw new MidiUnavailableException("no receiver available");433}434}435return seq;436}437438/**439* Constructs a MIDI sound bank by reading it from the specified stream. The440* stream must point to a valid MIDI soundbank file. In general, MIDI441* soundbank providers may need to read some data from the stream before442* determining whether they support it. These parsers must be able to mark443* the stream, read enough data to determine whether they support the444* stream, and, if not, reset the stream's read pointer to its original445* position. If the input stream does not support this, this method may fail446* with an {@code IOException}.447*448* @param stream the source of the sound bank data449* @return the sound bank450* @throws InvalidMidiDataException if the stream does not point to valid451* MIDI soundbank data recognized by the system452* @throws IOException if an I/O error occurred when loading the soundbank453* @throws NullPointerException if {@code stream} is {@code null}454* @see InputStream#markSupported455* @see InputStream#mark456*/457public static Soundbank getSoundbank(final InputStream stream)458throws InvalidMidiDataException, IOException {459Objects.requireNonNull(stream);460461SoundbankReader sp = null;462Soundbank s = null;463464List<SoundbankReader> providers = getSoundbankReaders();465466for(int i = 0; i < providers.size(); i++) {467sp = providers.get(i);468s = sp.getSoundbank(stream);469470if( s!= null) {471return s;472}473}474throw new InvalidMidiDataException("cannot get soundbank from stream");475476}477478/**479* Constructs a {@code Soundbank} by reading it from the specified URL. The480* URL must point to a valid MIDI soundbank file.481*482* @param url the source of the sound bank data483* @return the sound bank484* @throws InvalidMidiDataException if the URL does not point to valid MIDI485* soundbank data recognized by the system486* @throws IOException if an I/O error occurred when loading the soundbank487* @throws NullPointerException if {@code url} is {@code null}488*/489public static Soundbank getSoundbank(final URL url)490throws InvalidMidiDataException, IOException {491Objects.requireNonNull(url);492493SoundbankReader sp = null;494Soundbank s = null;495496List<SoundbankReader> providers = getSoundbankReaders();497498for(int i = 0; i < providers.size(); i++) {499sp = providers.get(i);500s = sp.getSoundbank(url);501502if( s!= null) {503return s;504}505}506throw new InvalidMidiDataException("cannot get soundbank from stream");507508}509510/**511* Constructs a {@code Soundbank} by reading it from the specified512* {@code File}. The {@code File} must point to a valid MIDI soundbank file.513*514* @param file the source of the sound bank data515* @return the sound bank516* @throws InvalidMidiDataException if the {@code File} does not point to517* valid MIDI soundbank data recognized by the system518* @throws IOException if an I/O error occurred when loading the soundbank519* @throws NullPointerException if {@code file} is {@code null}520*/521public static Soundbank getSoundbank(final File file)522throws InvalidMidiDataException, IOException {523Objects.requireNonNull(file);524525SoundbankReader sp = null;526Soundbank s = null;527528List<SoundbankReader> providers = getSoundbankReaders();529530for(int i = 0; i < providers.size(); i++) {531sp = providers.get(i);532s = sp.getSoundbank(file);533534if( s!= null) {535return s;536}537}538throw new InvalidMidiDataException("cannot get soundbank from stream");539}540541/**542* Obtains the MIDI file format of the data in the specified input stream.543* The stream must point to valid MIDI file data for a file type recognized544* by the system.545* <p>546* This method and/or the code it invokes may need to read some data from547* the stream to determine whether its data format is supported. The548* implementation may therefore need to mark the stream, read enough data to549* determine whether it is in a supported format, and reset the stream's550* read pointer to its original position. If the input stream does not551* permit this set of operations, this method may fail with an552* {@code IOException}.553* <p>554* This operation can only succeed for files of a type which can be parsed555* by an installed file reader. It may fail with an556* {@code InvalidMidiDataException} even for valid files if no compatible557* file reader is installed. It will also fail with an558* {@code InvalidMidiDataException} if a compatible file reader is559* installed, but encounters errors while determining the file format.560*561* @param stream the input stream from which file format information should562* be extracted563* @return an {@code MidiFileFormat} object describing the MIDI file format564* @throws InvalidMidiDataException if the stream does not point to valid565* MIDI file data recognized by the system566* @throws IOException if an I/O exception occurs while accessing the stream567* @throws NullPointerException if {@code stream} is {@code null}568* @see #getMidiFileFormat(URL)569* @see #getMidiFileFormat(File)570* @see InputStream#markSupported571* @see InputStream#mark572*/573public static MidiFileFormat getMidiFileFormat(final InputStream stream)574throws InvalidMidiDataException, IOException {575Objects.requireNonNull(stream);576577List<MidiFileReader> providers = getMidiFileReaders();578MidiFileFormat format = null;579580for(int i = 0; i < providers.size(); i++) {581MidiFileReader reader = providers.get(i);582try {583format = reader.getMidiFileFormat( stream ); // throws IOException584break;585} catch (InvalidMidiDataException e) {586continue;587}588}589590if( format==null ) {591throw new InvalidMidiDataException("input stream is not a supported file type");592} else {593return format;594}595}596597/**598* Obtains the MIDI file format of the data in the specified URL. The URL599* must point to valid MIDI file data for a file type recognized by the600* system.601* <p>602* This operation can only succeed for files of a type which can be parsed603* by an installed file reader. It may fail with an604* {@code InvalidMidiDataException} even for valid files if no compatible605* file reader is installed. It will also fail with an606* {@code InvalidMidiDataException} if a compatible file reader is607* installed, but encounters errors while determining the file format.608*609* @param url the URL from which file format information should be610* extracted611* @return a {@code MidiFileFormat} object describing the MIDI file format612* @throws InvalidMidiDataException if the URL does not point to valid MIDI613* file data recognized by the system614* @throws IOException if an I/O exception occurs while accessing the URL615* @throws NullPointerException if {@code url} is {@code null}616* @see #getMidiFileFormat(InputStream)617* @see #getMidiFileFormat(File)618*/619public static MidiFileFormat getMidiFileFormat(final URL url)620throws InvalidMidiDataException, IOException {621Objects.requireNonNull(url);622623List<MidiFileReader> providers = getMidiFileReaders();624MidiFileFormat format = null;625626for(int i = 0; i < providers.size(); i++) {627MidiFileReader reader = providers.get(i);628try {629format = reader.getMidiFileFormat( url ); // throws IOException630break;631} catch (InvalidMidiDataException e) {632continue;633}634}635636if( format==null ) {637throw new InvalidMidiDataException("url is not a supported file type");638} else {639return format;640}641}642643/**644* Obtains the MIDI file format of the specified {@code File}. The645* {@code File} must point to valid MIDI file data for a file type646* recognized by the system.647* <p>648* This operation can only succeed for files of a type which can be parsed649* by an installed file reader. It may fail with an650* {@code InvalidMidiDataException} even for valid files if no compatible651* file reader is installed. It will also fail with an652* {@code InvalidMidiDataException} if a compatible file reader is653* installed, but encounters errors while determining the file format.654*655* @param file the {@code File} from which file format information should656* be extracted657* @return a {@code MidiFileFormat} object describing the MIDI file format658* @throws InvalidMidiDataException if the {@code File} does not point to659* valid MIDI file data recognized by the system660* @throws IOException if an I/O exception occurs while accessing the file661* @throws NullPointerException if {@code file} is {@code null}662* @see #getMidiFileFormat(InputStream)663* @see #getMidiFileFormat(URL)664*/665public static MidiFileFormat getMidiFileFormat(final File file)666throws InvalidMidiDataException, IOException {667Objects.requireNonNull(file);668669List<MidiFileReader> providers = getMidiFileReaders();670MidiFileFormat format = null;671672for(int i = 0; i < providers.size(); i++) {673MidiFileReader reader = providers.get(i);674try {675format = reader.getMidiFileFormat( file ); // throws IOException676break;677} catch (InvalidMidiDataException e) {678continue;679}680}681682if( format==null ) {683throw new InvalidMidiDataException("file is not a supported file type");684} else {685return format;686}687}688689/**690* Obtains a MIDI sequence from the specified input stream. The stream must691* point to valid MIDI file data for a file type recognized by the system.692* <p>693* This method and/or the code it invokes may need to read some data from694* the stream to determine whether its data format is supported. The695* implementation may therefore need to mark the stream, read enough data to696* determine whether it is in a supported format, and reset the stream's697* read pointer to its original position. If the input stream does not698* permit this set of operations, this method may fail with an699* {@code IOException}.700* <p>701* This operation can only succeed for files of a type which can be parsed702* by an installed file reader. It may fail with an703* {@code InvalidMidiDataException} even for valid files if no compatible704* file reader is installed. It will also fail with an705* {@code InvalidMidiDataException} if a compatible file reader is706* installed, but encounters errors while constructing the {@code Sequence}707* object from the file data.708*709* @param stream the input stream from which the {@code Sequence} should be710* constructed711* @return a {@code Sequence} object based on the MIDI file data contained712* in the input stream713* @throws InvalidMidiDataException if the stream does not point to valid714* MIDI file data recognized by the system715* @throws IOException if an I/O exception occurs while accessing the stream716* @throws NullPointerException if {@code stream} is {@code null}717* @see InputStream#markSupported718* @see InputStream#mark719*/720public static Sequence getSequence(final InputStream stream)721throws InvalidMidiDataException, IOException {722Objects.requireNonNull(stream);723724List<MidiFileReader> providers = getMidiFileReaders();725Sequence sequence = null;726727for(int i = 0; i < providers.size(); i++) {728MidiFileReader reader = providers.get(i);729try {730sequence = reader.getSequence( stream ); // throws IOException731break;732} catch (InvalidMidiDataException e) {733continue;734}735}736737if( sequence==null ) {738throw new InvalidMidiDataException("could not get sequence from input stream");739} else {740return sequence;741}742}743744/**745* Obtains a MIDI sequence from the specified URL. The URL must point to746* valid MIDI file data for a file type recognized by the system.747* <p>748* This operation can only succeed for files of a type which can be parsed749* by an installed file reader. It may fail with an750* {@code InvalidMidiDataException} even for valid files if no compatible751* file reader is installed. It will also fail with an752* {@code InvalidMidiDataException} if a compatible file reader is753* installed, but encounters errors while constructing the {@code Sequence}754* object from the file data.755*756* @param url the URL from which the {@code Sequence} should be constructed757* @return a {@code Sequence} object based on the MIDI file data pointed to758* by the URL759* @throws InvalidMidiDataException if the URL does not point to valid MIDI760* file data recognized by the system761* @throws IOException if an I/O exception occurs while accessing the URL762* @throws NullPointerException if {@code url} is {@code null}763*/764public static Sequence getSequence(final URL url)765throws InvalidMidiDataException, IOException {766Objects.requireNonNull(url);767768List<MidiFileReader> providers = getMidiFileReaders();769Sequence sequence = null;770771for(int i = 0; i < providers.size(); i++) {772MidiFileReader reader = providers.get(i);773try {774sequence = reader.getSequence( url ); // throws IOException775break;776} catch (InvalidMidiDataException e) {777continue;778}779}780781if( sequence==null ) {782throw new InvalidMidiDataException("could not get sequence from URL");783} else {784return sequence;785}786}787788/**789* Obtains a MIDI sequence from the specified {@code File}. The {@code File}790* must point to valid MIDI file data for a file type recognized by the791* system.792* <p>793* This operation can only succeed for files of a type which can be parsed794* by an installed file reader. It may fail with an795* {@code InvalidMidiDataException} even for valid files if no compatible796* file reader is installed. It will also fail with an797* {@code InvalidMidiDataException} if a compatible file reader is798* installed, but encounters errors while constructing the {@code Sequence}799* object from the file data.800*801* @param file the {@code File} from which the {@code Sequence} should be802* constructed803* @return a {@code Sequence} object based on the MIDI file data pointed to804* by the File805* @throws InvalidMidiDataException if the File does not point to valid MIDI806* file data recognized by the system807* @throws IOException if an I/O exception occurs808* @throws NullPointerException if {@code file} is {@code null}809*/810public static Sequence getSequence(final File file)811throws InvalidMidiDataException, IOException {812Objects.requireNonNull(file);813814List<MidiFileReader> providers = getMidiFileReaders();815Sequence sequence = null;816817for(int i = 0; i < providers.size(); i++) {818MidiFileReader reader = providers.get(i);819try {820sequence = reader.getSequence( file ); // throws IOException821break;822} catch (InvalidMidiDataException e) {823continue;824}825}826827if( sequence==null ) {828throw new InvalidMidiDataException("could not get sequence from file");829} else {830return sequence;831}832}833834/**835* Obtains the set of MIDI file types for which file writing support is836* provided by the system.837*838* @return array of unique file types. If no file types are supported, an839* array of length 0 is returned.840*/841public static int[] getMidiFileTypes() {842843List<MidiFileWriter> providers = getMidiFileWriters();844Set<Integer> allTypes = new HashSet<>();845846// gather from all the providers847848for (int i = 0; i < providers.size(); i++ ) {849MidiFileWriter writer = providers.get(i);850int[] types = writer.getMidiFileTypes();851for (int j = 0; j < types.length; j++ ) {852allTypes.add(types[j]);853}854}855int[] resultTypes = new int[allTypes.size()];856int index = 0;857for (Integer integer : allTypes) {858resultTypes[index++] = integer.intValue();859}860return resultTypes;861}862863/**864* Indicates whether file writing support for the specified MIDI file type865* is provided by the system.866*867* @param fileType the file type for which write capabilities are queried868* @return {@code true} if the file type is supported, otherwise869* {@code false}870*/871public static boolean isFileTypeSupported(int fileType) {872873List<MidiFileWriter> providers = getMidiFileWriters();874875for (int i = 0; i < providers.size(); i++ ) {876MidiFileWriter writer = providers.get(i);877if( writer.isFileTypeSupported(fileType)) {878return true;879}880}881return false;882}883884/**885* Obtains the set of MIDI file types that the system can write from the886* sequence specified.887*888* @param sequence the sequence for which MIDI file type support is queried889* @return the set of unique supported file types. If no file types are890* supported, returns an array of length 0.891* @throws NullPointerException if {@code sequence} is {@code null}892*/893public static int[] getMidiFileTypes(final Sequence sequence) {894Objects.requireNonNull(sequence);895896List<MidiFileWriter> providers = getMidiFileWriters();897Set<Integer> allTypes = new HashSet<>();898899// gather from all the providers900901for (int i = 0; i < providers.size(); i++ ) {902MidiFileWriter writer = providers.get(i);903int[] types = writer.getMidiFileTypes(sequence);904for (int j = 0; j < types.length; j++ ) {905allTypes.add(types[j]);906}907}908int[] resultTypes = new int[allTypes.size()];909int index = 0;910for (Integer integer : allTypes) {911resultTypes[index++] = integer.intValue();912}913return resultTypes;914}915916/**917* Indicates whether a MIDI file of the file type specified can be written918* from the sequence indicated.919*920* @param fileType the file type for which write capabilities are queried921* @param sequence the sequence for which file writing support is queried922* @return {@code true} if the file type is supported for this sequence,923* otherwise {@code false}924* @throws NullPointerException if {@code sequence} is {@code null}925*/926public static boolean isFileTypeSupported(final int fileType,927final Sequence sequence) {928Objects.requireNonNull(sequence);929930List<MidiFileWriter> providers = getMidiFileWriters();931932for (int i = 0; i < providers.size(); i++ ) {933MidiFileWriter writer = providers.get(i);934if( writer.isFileTypeSupported(fileType,sequence)) {935return true;936}937}938return false;939}940941/**942* Writes a stream of bytes representing a file of the MIDI file type943* indicated to the output stream provided.944*945* @param in sequence containing MIDI data to be written to the file946* @param fileType the file type of the file to be written to the output947* stream948* @param out stream to which the file data should be written949* @return the number of bytes written to the output stream950* @throws IOException if an I/O exception occurs951* @throws IllegalArgumentException if the file format is not supported by952* the system953* @throws NullPointerException if {@code in} or {@code out} are954* {@code null}955* @see #isFileTypeSupported(int, Sequence)956* @see #getMidiFileTypes(Sequence)957*/958public static int write(final Sequence in, final int fileType,959final OutputStream out) throws IOException {960Objects.requireNonNull(in);961Objects.requireNonNull(out);962963List<MidiFileWriter> providers = getMidiFileWriters();964//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences965int bytesWritten = -2;966967for (int i = 0; i < providers.size(); i++ ) {968MidiFileWriter writer = providers.get(i);969if( writer.isFileTypeSupported( fileType, in ) ) {970971bytesWritten = writer.write(in, fileType, out);972break;973}974}975if (bytesWritten == -2) {976throw new IllegalArgumentException("MIDI file type is not supported");977}978return bytesWritten;979}980981/**982* Writes a stream of bytes representing a file of the MIDI file type983* indicated to the external file provided.984*985* @param in sequence containing MIDI data to be written to the file986* @param type the file type of the file to be written to the output stream987* @param out external file to which the file data should be written988* @return the number of bytes written to the file989* @throws IOException if an I/O exception occurs990* @throws IllegalArgumentException if the file type is not supported by the991* system992* @throws NullPointerException if {@code in} or {@code out} are993* {@code null}994* @see #isFileTypeSupported(int, Sequence)995* @see #getMidiFileTypes(Sequence)996*/997public static int write(final Sequence in, final int type, final File out)998throws IOException {999Objects.requireNonNull(in);1000Objects.requireNonNull(out);10011002List<MidiFileWriter> providers = getMidiFileWriters();1003//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences1004int bytesWritten = -2;10051006for (int i = 0; i < providers.size(); i++ ) {1007MidiFileWriter writer = providers.get(i);1008if( writer.isFileTypeSupported( type, in ) ) {10091010bytesWritten = writer.write(in, type, out);1011break;1012}1013}1014if (bytesWritten == -2) {1015throw new IllegalArgumentException("MIDI file type is not supported");1016}1017return bytesWritten;1018}10191020// HELPER METHODS10211022/**1023* Obtains the list of MidiDeviceProviders installed on the system.1024*1025* @return the list of MidiDeviceProviders installed on the system1026*/1027@SuppressWarnings("unchecked")1028private static List<MidiDeviceProvider> getMidiDeviceProviders() {1029return (List<MidiDeviceProvider>) getProviders(MidiDeviceProvider.class);1030}10311032/**1033* Obtains the list of SoundbankReaders installed on the system.1034*1035* @return the list of SoundbankReaders installed on the system1036*/1037@SuppressWarnings("unchecked")1038private static List<SoundbankReader> getSoundbankReaders() {1039return (List<SoundbankReader>) getProviders(SoundbankReader.class);1040}10411042/**1043* Obtains the list of MidiFileWriters installed on the system.1044*1045* @return the list of MidiFileWriters installed on the system1046*/1047@SuppressWarnings("unchecked")1048private static List<MidiFileWriter> getMidiFileWriters() {1049return (List<MidiFileWriter>) getProviders(MidiFileWriter.class);1050}10511052/**1053* Obtains the list of MidiFileReaders installed on the system.1054*1055* @return the list of MidiFileReaders installed on the system1056*/1057@SuppressWarnings("unchecked")1058private static List<MidiFileReader> getMidiFileReaders() {1059return (List<MidiFileReader>) getProviders(MidiFileReader.class);1060}10611062/**1063* Attempts to locate and return a default MidiDevice of the specified type.1064* This method wraps {@link #getDefaultDevice}. It catches the1065* {@code IllegalArgumentException} thrown by {@code getDefaultDevice} and1066* instead throws a {@code MidiUnavailableException}, with the catched1067* exception chained.1068*1069* @param deviceClass The requested device type, one of Synthesizer.class,1070* Sequencer.class, Receiver.class or Transmitter.class1071* @return default MidiDevice of the specified type1072* @throws MidiUnavailableException on failure1073*/1074private static MidiDevice getDefaultDeviceWrapper(Class<?> deviceClass)1075throws MidiUnavailableException{1076try {1077return getDefaultDevice(deviceClass);1078} catch (IllegalArgumentException iae) {1079MidiUnavailableException mae = new MidiUnavailableException();1080mae.initCause(iae);1081throw mae;1082}1083}10841085/**1086* Attempts to locate and return a default MidiDevice of the specified type.1087*1088* @param deviceClass The requested device type, one of Synthesizer.class,1089* Sequencer.class, Receiver.class or Transmitter.class1090* @return default MidiDevice of the specified type.1091* @throws IllegalArgumentException on failure1092*/1093private static MidiDevice getDefaultDevice(Class<?> deviceClass) {1094List<MidiDeviceProvider> providers = getMidiDeviceProviders();1095String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass);1096String instanceName = JDK13Services.getDefaultInstanceName(deviceClass);1097MidiDevice device;10981099if (providerClassName != null) {1100MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers);1101if (defaultProvider != null) {1102if (instanceName != null) {1103device = getNamedDevice(instanceName, defaultProvider, deviceClass);1104if (device != null) {1105return device;1106}1107}1108device = getFirstDevice(defaultProvider, deviceClass);1109if (device != null) {1110return device;1111}1112}1113}11141115/*1116* - Provider class not specified or cannot be found, or1117* - provider class specified, and no appropriate device available, or1118* - provider class and instance specified and instance cannot be found1119* or is not appropriate1120*/1121if (instanceName != null) {1122device = getNamedDevice(instanceName, providers, deviceClass);1123if (device != null) {1124return device;1125}1126}11271128/*1129* No defaults are specified, or if something is specified, everything1130* failed1131*/1132device = getFirstDevice(providers, deviceClass);1133if (device != null) {1134return device;1135}1136throw new IllegalArgumentException("Requested device not installed");1137}11381139/**1140* Return a MidiDeviceProvider of a given class from the list of1141* MidiDeviceProviders.1142*1143* @param providerClassName The class name of the provider to be returned1144* @param providers The list of MidiDeviceProviders that is searched1145* @return A MidiDeviceProvider of the requested class, or null if none is1146* found1147*/1148private static MidiDeviceProvider getNamedProvider(String providerClassName,1149List<MidiDeviceProvider> providers) {1150for(int i = 0; i < providers.size(); i++) {1151MidiDeviceProvider provider = providers.get(i);1152if (provider.getClass().getName().equals(providerClassName)) {1153return provider;1154}1155}1156return null;1157}11581159/**1160* Return a MidiDevice with a given name from a given MidiDeviceProvider.1161*1162* @param deviceName The name of the MidiDevice to be returned1163* @param provider The MidiDeviceProvider to check for MidiDevices1164* @param deviceClass The requested device type, one of Synthesizer.class,1165* Sequencer.class, Receiver.class or Transmitter.class1166* @return A MidiDevice matching the requirements, or null if none is found1167*/1168private static MidiDevice getNamedDevice(String deviceName,1169MidiDeviceProvider provider,1170Class<?> deviceClass) {1171MidiDevice device;1172// try to get MIDI port1173device = getNamedDevice(deviceName, provider, deviceClass,1174false, false);1175if (device != null) {1176return device;1177}11781179if (deviceClass == Receiver.class) {1180// try to get Synthesizer1181device = getNamedDevice(deviceName, provider, deviceClass,1182true, false);1183if (device != null) {1184return device;1185}1186}11871188return null;1189}11901191/**1192* Return a MidiDevice with a given name from a given MidiDeviceProvider.1193*1194* @param deviceName The name of the MidiDevice to be returned1195* @param provider The MidiDeviceProvider to check for MidiDevices1196* @param deviceClass The requested device type, one of Synthesizer.class,1197* Sequencer.class, Receiver.class or Transmitter.class1198* @param allowSynthesizer if true, Synthesizers are considered1199* appropriate. Otherwise only pure MidiDevices are considered1200* appropriate (unless allowSequencer is true). This flag only has1201* an effect for deviceClass Receiver and Transmitter. For other1202* device classes (Sequencer and Synthesizer), this flag has no1203* effect.1204* @param allowSequencer if true, Sequencers are considered appropriate.1205* Otherwise only pure MidiDevices are considered appropriate1206* (unless allowSynthesizer is true). This flag only has an effect1207* for deviceClass Receiver and Transmitter. For other device1208* classes (Sequencer and Synthesizer), this flag has no effect.1209* @return A MidiDevice matching the requirements, or null if none is found1210*/1211private static MidiDevice getNamedDevice(String deviceName,1212MidiDeviceProvider provider,1213Class<?> deviceClass,1214boolean allowSynthesizer,1215boolean allowSequencer) {1216MidiDevice.Info[] infos = provider.getDeviceInfo();1217for (int i = 0; i < infos.length; i++) {1218if (infos[i].getName().equals(deviceName)) {1219MidiDevice device = provider.getDevice(infos[i]);1220if (isAppropriateDevice(device, deviceClass,1221allowSynthesizer, allowSequencer)) {1222return device;1223}1224}1225}1226return null;1227}12281229/**1230* Return a MidiDevice with a given name from a list of MidiDeviceProviders.1231*1232* @param deviceName The name of the MidiDevice to be returned1233* @param providers The List of MidiDeviceProviders to check for1234* MidiDevices1235* @param deviceClass The requested device type, one of Synthesizer.class,1236* Sequencer.class, Receiver.class or Transmitter.class1237* @return A Mixer matching the requirements, or null if none is found1238*/1239private static MidiDevice getNamedDevice(String deviceName,1240List<MidiDeviceProvider> providers,1241Class<?> deviceClass) {1242MidiDevice device;1243// try to get MIDI port1244device = getNamedDevice(deviceName, providers, deviceClass,1245false, false);1246if (device != null) {1247return device;1248}12491250if (deviceClass == Receiver.class) {1251// try to get Synthesizer1252device = getNamedDevice(deviceName, providers, deviceClass,1253true, false);1254if (device != null) {1255return device;1256}1257}12581259return null;1260}12611262/**1263* Return a MidiDevice with a given name from a list of MidiDeviceProviders.1264*1265* @param deviceName The name of the MidiDevice to be returned1266* @param providers The List of MidiDeviceProviders to check for1267* MidiDevices1268* @param deviceClass The requested device type, one of Synthesizer.class,1269* Sequencer.class, Receiver.class or Transmitter.class1270* @param allowSynthesizer if true, Synthesizers are considered1271* appropriate. Otherwise only pure MidiDevices are considered1272* appropriate (unless allowSequencer is true). This flag only has1273* an effect for deviceClass Receiver and Transmitter. For other1274* device classes (Sequencer and Synthesizer), this flag has no1275* effect.1276* @param allowSequencer if true, Sequencers are considered appropriate.1277* Otherwise only pure MidiDevices are considered appropriate1278* (unless allowSynthesizer is true). This flag only has an effect1279* for deviceClass Receiver and Transmitter. For other device1280* classes (Sequencer and Synthesizer), this flag has no effect.1281* @return A Mixer matching the requirements, or null if none is found1282*/1283private static MidiDevice getNamedDevice(String deviceName,1284List<MidiDeviceProvider> providers,1285Class<?> deviceClass,1286boolean allowSynthesizer,1287boolean allowSequencer) {1288for(int i = 0; i < providers.size(); i++) {1289MidiDeviceProvider provider = providers.get(i);1290MidiDevice device = getNamedDevice(deviceName, provider,1291deviceClass,1292allowSynthesizer,1293allowSequencer);1294if (device != null) {1295return device;1296}1297}1298return null;1299}13001301/**1302* From a given MidiDeviceProvider, return the first appropriate device.1303*1304* @param provider The MidiDeviceProvider to check for MidiDevices1305* @param deviceClass The requested device type, one of Synthesizer.class,1306* Sequencer.class, Receiver.class or Transmitter.class1307* @return A MidiDevice is considered appropriate, or null if no appropriate1308* device is found1309*/1310private static MidiDevice getFirstDevice(MidiDeviceProvider provider,1311Class<?> deviceClass) {1312MidiDevice device;1313// try to get MIDI port1314device = getFirstDevice(provider, deviceClass,1315false, false);1316if (device != null) {1317return device;1318}13191320if (deviceClass == Receiver.class) {1321// try to get Synthesizer1322device = getFirstDevice(provider, deviceClass,1323true, false);1324if (device != null) {1325return device;1326}1327}13281329return null;1330}13311332/**1333* From a given MidiDeviceProvider, return the first appropriate device.1334*1335* @param provider The MidiDeviceProvider to check for MidiDevices1336* @param deviceClass The requested device type, one of Synthesizer.class,1337* Sequencer.class, Receiver.class or Transmitter.class1338* @param allowSynthesizer if true, Synthesizers are considered1339* appropriate. Otherwise only pure MidiDevices are considered1340* appropriate (unless allowSequencer is true). This flag only has1341* an effect for deviceClass Receiver and Transmitter. For other1342* device classes (Sequencer and Synthesizer), this flag has no1343* effect.1344* @param allowSequencer if true, Sequencers are considered appropriate.1345* Otherwise only pure MidiDevices are considered appropriate1346* (unless allowSynthesizer is true). This flag only has an effect1347* for deviceClass Receiver and Transmitter. For other device1348* classes (Sequencer and Synthesizer), this flag has no effect.1349* @return A MidiDevice is considered appropriate, or null if no appropriate1350* device is found1351*/1352private static MidiDevice getFirstDevice(MidiDeviceProvider provider,1353Class<?> deviceClass,1354boolean allowSynthesizer,1355boolean allowSequencer) {1356MidiDevice.Info[] infos = provider.getDeviceInfo();1357for (int j = 0; j < infos.length; j++) {1358MidiDevice device = provider.getDevice(infos[j]);1359if (isAppropriateDevice(device, deviceClass,1360allowSynthesizer, allowSequencer)) {1361return device;1362}1363}1364return null;1365}13661367/**1368* From a List of MidiDeviceProviders, return the first appropriate1369* MidiDevice.1370*1371* @param providers The List of MidiDeviceProviders to search1372* @param deviceClass The requested device type, one of Synthesizer.class,1373* Sequencer.class, Receiver.class or Transmitter.class1374* @return A MidiDevice that is considered appropriate, or null if none is1375* found1376*/1377private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,1378Class<?> deviceClass) {1379MidiDevice device;1380// try to get MIDI port1381device = getFirstDevice(providers, deviceClass,1382false, false);1383if (device != null) {1384return device;1385}13861387if (deviceClass == Receiver.class) {1388// try to get Synthesizer1389device = getFirstDevice(providers, deviceClass,1390true, false);1391if (device != null) {1392return device;1393}1394}13951396return null;1397}13981399/**1400* From a List of MidiDeviceProviders, return the first appropriate1401* MidiDevice.1402*1403* @param providers The List of MidiDeviceProviders to search1404* @param deviceClass The requested device type, one of Synthesizer.class,1405* Sequencer.class, Receiver.class or Transmitter.class1406* @param allowSynthesizer if true, Synthesizers are considered1407* appropriate. Otherwise only pure MidiDevices are considered1408* appropriate (unless allowSequencer is true). This flag only has1409* an effect for deviceClass Receiver and Transmitter. For other1410* device classes (Sequencer and Synthesizer), this flag has no1411* effect.1412* @param allowSequencer if true, Sequencers are considered appropriate.1413* Otherwise only pure MidiDevices are considered appropriate1414* (unless allowSynthesizer is true). This flag only has an effect1415* for deviceClass Receiver and Transmitter. For other device1416* classes (Sequencer and Synthesizer), this flag has no effect.1417* @return A MidiDevice that is considered appropriate, or null if none is1418* found1419*/1420private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,1421Class<?> deviceClass,1422boolean allowSynthesizer,1423boolean allowSequencer) {1424for(int i = 0; i < providers.size(); i++) {1425MidiDeviceProvider provider = providers.get(i);1426MidiDevice device = getFirstDevice(provider, deviceClass,1427allowSynthesizer,1428allowSequencer);1429if (device != null) {1430return device;1431}1432}1433return null;1434}14351436/**1437* Checks if a MidiDevice is appropriate. If deviceClass is Synthesizer or1438* Sequencer, a device implementing the respective interface is considered1439* appropriate. If deviceClass is Receiver or Transmitter, a device is1440* considered appropriate if it implements neither Synthesizer nor1441* Transmitter, and if it can provide at least one Receiver or Transmitter,1442* respectively.1443*1444* @param device the MidiDevice to test1445* @param deviceClass The requested device type, one of Synthesizer.class,1446* Sequencer.class, Receiver.class or Transmitter.class1447* @param allowSynthesizer if true, Synthesizers are considered1448* appropriate. Otherwise only pure MidiDevices are considered1449* appropriate (unless allowSequencer is true). This flag only has1450* an effect for deviceClass Receiver and Transmitter. For other1451* device classes (Sequencer and Synthesizer), this flag has no1452* effect.1453* @param allowSequencer if true, Sequencers are considered appropriate.1454* Otherwise only pure MidiDevices are considered appropriate1455* (unless allowSynthesizer is true). This flag only has an effect1456* for deviceClass Receiver and Transmitter. For other device1457* classes (Sequencer and Synthesizer), this flag has no effect.1458* @return true if the device is considered appropriate according to the1459* rules given above, false otherwise1460*/1461private static boolean isAppropriateDevice(MidiDevice device,1462Class<?> deviceClass,1463boolean allowSynthesizer,1464boolean allowSequencer) {1465if (deviceClass.isInstance(device)) {1466// This clause is for deviceClass being either Synthesizer1467// or Sequencer.1468return true;1469} else {1470// Now the case that deviceClass is Transmitter or1471// Receiver. If neither allowSynthesizer nor allowSequencer is1472// true, we require device instances to be1473// neither Synthesizer nor Sequencer, since we only want1474// devices representing MIDI ports.1475// Otherwise, the respective type is accepted, too1476if ( (! (device instanceof Sequencer) &&1477! (device instanceof Synthesizer) ) ||1478((device instanceof Sequencer) && allowSequencer) ||1479((device instanceof Synthesizer) && allowSynthesizer)) {1480// And of cource, the device has to be able to provide1481// Receivers or Transmitters.1482if ((deviceClass == Receiver.class &&1483device.getMaxReceivers() != 0) ||1484(deviceClass == Transmitter.class &&1485device.getMaxTransmitters() != 0)) {1486return true;1487}1488}1489}1490return false;1491}14921493/**1494* Obtains the set of services currently installed on the system using the1495* SPI mechanism in 1.3.1496*1497* @param providerClass The type of providers requested. This should be one1498* of AudioFileReader.class, AudioFileWriter.class,1499* FormatConversionProvider.class, MixerProvider.class,1500* MidiDeviceProvider.class, MidiFileReader.class,1501* MidiFileWriter.class or SoundbankReader.class.1502* @return a List of instances of providers for the requested service. If no1503* providers are available, a List of length 0 will be returned.1504*/1505private static List<?> getProviders(Class<?> providerClass) {1506return JDK13Services.getProviders(providerClass);1507}1508}150915101511