Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/sound/midi/MidiSystem.java
38830 views
/*1* Copyright (c) 1999, 2013, 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.FileInputStream;28import java.io.File;29import java.io.InputStream;30import java.io.OutputStream;31import java.io.IOException;3233import java.util.ArrayList;34import java.util.HashSet;35import java.util.Iterator;36import java.util.List;37import java.util.Set;3839import java.net.URL;4041import javax.sound.midi.spi.MidiFileWriter;42import javax.sound.midi.spi.MidiFileReader;43import javax.sound.midi.spi.SoundbankReader;44import javax.sound.midi.spi.MidiDeviceProvider;4546import com.sun.media.sound.JDK13Services;47import com.sun.media.sound.ReferenceCountingDevice;48import com.sun.media.sound.AutoConnectSequencer;49import com.sun.media.sound.MidiDeviceReceiverEnvelope;50import com.sun.media.sound.MidiDeviceTransmitterEnvelope;515253/**54* The <code>MidiSystem</code> class provides access to the installed MIDI55* system resources, including devices such as synthesizers, sequencers, and56* MIDI input and output ports. A typical simple MIDI application might57* begin by invoking one or more <code>MidiSystem</code> methods to learn58* what devices are installed and to obtain the ones needed in that59* application.60* <p>61* The class also has methods for reading files, streams, and URLs that62* contain standard MIDI file data or soundbanks. You can query the63* <code>MidiSystem</code> for the format of a specified MIDI file.64* <p>65* You cannot instantiate a <code>MidiSystem</code>; all the methods are66* static.67*68* <p>Properties can be used to specify default MIDI devices.69* Both system properties and a properties file are considered.70* The <code>sound.properties</code> properties file is read from71* an implementation-specific location (typically it is the <code>lib</code>72* directory in the Java installation directory).73* If a property exists both as a system property and in the74* properties file, the system property takes precedence. If none is75* specified, a suitable default is chosen among the available devices.76* The syntax of the properties file is specified in77* {@link java.util.Properties#load(InputStream) Properties.load}. The78* following table lists the available property keys and which methods79* consider them:80*81* <table border=0>82* <caption>MIDI System Property Keys</caption>83* <tr>84* <th>Property Key</th>85* <th>Interface</th>86* <th>Affected Method</th>87* </tr>88* <tr>89* <td><code>javax.sound.midi.Receiver</code></td>90* <td>{@link Receiver}</td>91* <td>{@link #getReceiver}</td>92* </tr>93* <tr>94* <td><code>javax.sound.midi.Sequencer</code></td>95* <td>{@link Sequencer}</td>96* <td>{@link #getSequencer}</td>97* </tr>98* <tr>99* <td><code>javax.sound.midi.Synthesizer</code></td>100* <td>{@link Synthesizer}</td>101* <td>{@link #getSynthesizer}</td>102* </tr>103* <tr>104* <td><code>javax.sound.midi.Transmitter</code></td>105* <td>{@link Transmitter}</td>106* <td>{@link #getTransmitter}</td>107* </tr>108* </table>109*110* The property value consists of the provider class name111* and the device name, separated by the hash mark ("#").112* The provider class name is the fully-qualified113* name of a concrete {@link javax.sound.midi.spi.MidiDeviceProvider114* MIDI device provider} class. The device name is matched against115* the <code>String</code> returned by the <code>getName</code>116* method of <code>MidiDevice.Info</code>.117* Either the class name, or the device name may be omitted.118* If only the class name is specified, the trailing hash mark119* is optional.120*121* <p>If the provider class is specified, and it can be122* successfully retrieved from the installed providers,123* the list of124* <code>MidiDevice.Info</code> objects is retrieved125* from the provider. Otherwise, or when these devices126* do not provide a subsequent match, the list is retrieved127* from {@link #getMidiDeviceInfo} to contain128* all available <code>MidiDevice.Info</code> objects.129*130* <p>If a device name is specified, the resulting list of131* <code>MidiDevice.Info</code> objects is searched:132* the first one with a matching name, and whose133* <code>MidiDevice</code> implements the134* respective interface, will be returned.135* If no matching <code>MidiDevice.Info</code> object136* is found, or the device name is not specified,137* the first suitable device from the resulting138* list will be returned. For Sequencer and Synthesizer,139* a device is suitable if it implements the respective140* interface; whereas for Receiver and Transmitter, a device is141* suitable if it142* implements neither Sequencer nor Synthesizer and provides143* at least one Receiver or Transmitter, respectively.144*145* For example, the property <code>javax.sound.midi.Receiver</code>146* with a value147* <code>"com.sun.media.sound.MidiProvider#SunMIDI1"</code>148* will have the following consequences when149* <code>getReceiver</code> is called:150* if the class <code>com.sun.media.sound.MidiProvider</code> exists151* in the list of installed MIDI device providers,152* the first <code>Receiver</code> device with name153* <code>"SunMIDI1"</code> will be returned. If it cannot154* be found, the first <code>Receiver</code> from that provider155* will be returned, regardless of name.156* If there is none, the first <code>Receiver</code> with name157* <code>"SunMIDI1"</code> in the list of all devices158* (as returned by <code>getMidiDeviceInfo</code>) will be returned,159* or, if not found, the first <code>Receiver</code> that can160* be found in the list of all devices is returned.161* If that fails, too, a <code>MidiUnavailableException</code>162* is thrown.163*164* @author Kara Kytle165* @author Florian Bomers166* @author Matthias Pfisterer167*/168public class MidiSystem {169170/**171* Private no-args constructor for ensuring against instantiation.172*/173private MidiSystem() {174}175176177/**178* Obtains an array of information objects representing179* the set of all MIDI devices available on the system.180* A returned information object can then be used to obtain the181* corresponding device object, by invoking182* {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}.183*184* @return an array of <code>MidiDevice.Info</code> objects, one185* for each installed MIDI device. If no such devices are installed,186* an array of length 0 is returned.187*/188public static MidiDevice.Info[] getMidiDeviceInfo() {189List allInfos = new ArrayList();190List providers = getMidiDeviceProviders();191192for(int i = 0; i < providers.size(); i++) {193MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);194MidiDevice.Info[] tmpinfo = provider.getDeviceInfo();195for (int j = 0; j < tmpinfo.length; j++) {196allInfos.add( tmpinfo[j] );197}198}199MidiDevice.Info[] infosArray = (MidiDevice.Info[]) allInfos.toArray(new MidiDevice.Info[0]);200return infosArray;201}202203204/**205* Obtains the requested MIDI device.206*207* @param info a device information object representing the desired device.208* @return the requested device209* @throws MidiUnavailableException if the requested device is not available210* due to resource restrictions211* @throws IllegalArgumentException if the info object does not represent212* a MIDI device installed on the system213* @see #getMidiDeviceInfo214*/215public static MidiDevice getMidiDevice(MidiDevice.Info info) throws MidiUnavailableException {216List providers = getMidiDeviceProviders();217218for(int i = 0; i < providers.size(); i++) {219MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);220if (provider.isDeviceSupported(info)) {221MidiDevice device = provider.getDevice(info);222return device;223}224}225throw new IllegalArgumentException("Requested device not installed: " + info);226}227228229/**230* Obtains a MIDI receiver from an external MIDI port231* or other default device.232* The returned receiver always implements233* the {@code MidiDeviceReceiver} interface.234*235* <p>If the system property236* <code>javax.sound.midi.Receiver</code>237* is defined or it is defined in the file "sound.properties",238* it is used to identify the device that provides the default receiver.239* For details, refer to the {@link MidiSystem class description}.240*241* If a suitable MIDI port is not available, the Receiver is242* retrieved from an installed synthesizer.243*244* <p>If a native receiver provided by the default device does not implement245* the {@code MidiDeviceReceiver} interface, it will be wrapped in a246* wrapper class that implements the {@code MidiDeviceReceiver} interface.247* The corresponding {@code Receiver} method calls will be forwarded248* to the native receiver.249*250* <p>If this method returns successfully, the {@link251* javax.sound.midi.MidiDevice MidiDevice} the252* <code>Receiver</code> belongs to is opened implicitly, if it is253* not already open. It is possible to close an implicitly opened254* device by calling {@link javax.sound.midi.Receiver#close close}255* on the returned <code>Receiver</code>. All open <code>Receiver</code>256* instances have to be closed in order to release system resources257* hold by the <code>MidiDevice</code>. For a258* detailed description of open/close behaviour see the class259* description of {@link javax.sound.midi.MidiDevice MidiDevice}.260*261*262* @return the default MIDI receiver263* @throws MidiUnavailableException if the default receiver is not264* available due to resource restrictions,265* or no device providing receivers is installed in the system266*/267public static Receiver getReceiver() throws MidiUnavailableException {268// may throw MidiUnavailableException269MidiDevice device = getDefaultDeviceWrapper(Receiver.class);270Receiver receiver;271if (device instanceof ReferenceCountingDevice) {272receiver = ((ReferenceCountingDevice) device).getReceiverReferenceCounting();273} else {274receiver = device.getReceiver();275}276if (!(receiver instanceof MidiDeviceReceiver)) {277receiver = new MidiDeviceReceiverEnvelope(device, receiver);278}279return receiver;280}281282283/**284* Obtains a MIDI transmitter from an external MIDI port285* or other default source.286* The returned transmitter always implements287* the {@code MidiDeviceTransmitter} interface.288*289* <p>If the system property290* <code>javax.sound.midi.Transmitter</code>291* is defined or it is defined in the file "sound.properties",292* it is used to identify the device that provides the default transmitter.293* For details, refer to the {@link MidiSystem class description}.294*295* <p>If a native transmitter provided by the default device does not implement296* the {@code MidiDeviceTransmitter} interface, it will be wrapped in a297* wrapper class that implements the {@code MidiDeviceTransmitter} interface.298* The corresponding {@code Transmitter} method calls will be forwarded299* to the native transmitter.300*301* <p>If this method returns successfully, the {@link302* javax.sound.midi.MidiDevice MidiDevice} the303* <code>Transmitter</code> belongs to is opened implicitly, if it304* is not already open. It is possible to close an implicitly305* opened device by calling {@link306* javax.sound.midi.Transmitter#close close} on the returned307* <code>Transmitter</code>. All open <code>Transmitter</code>308* instances have to be closed in order to release system resources309* hold by the <code>MidiDevice</code>. For a detailed description310* of open/close behaviour see the class description of {@link311* javax.sound.midi.MidiDevice MidiDevice}.312*313* @return the default MIDI transmitter314* @throws MidiUnavailableException if the default transmitter is not315* available due to resource restrictions,316* or no device providing transmitters is installed in the system317*/318public static Transmitter getTransmitter() throws MidiUnavailableException {319// may throw MidiUnavailableException320MidiDevice device = getDefaultDeviceWrapper(Transmitter.class);321Transmitter transmitter;322if (device instanceof ReferenceCountingDevice) {323transmitter = ((ReferenceCountingDevice) device).getTransmitterReferenceCounting();324} else {325transmitter = device.getTransmitter();326}327if (!(transmitter instanceof MidiDeviceTransmitter)) {328transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter);329}330return transmitter;331}332333334/**335* Obtains the default synthesizer.336*337* <p>If the system property338* <code>javax.sound.midi.Synthesizer</code>339* is defined or it is defined in the file "sound.properties",340* it is used to identify the default synthesizer.341* For details, refer to the {@link MidiSystem class description}.342*343* @return the default synthesizer344* @throws MidiUnavailableException if the synthesizer is not345* available due to resource restrictions,346* or no synthesizer is installed in the system347*/348public static Synthesizer getSynthesizer() throws MidiUnavailableException {349// may throw MidiUnavailableException350return (Synthesizer) getDefaultDeviceWrapper(Synthesizer.class);351}352353354/**355* Obtains the default <code>Sequencer</code>, connected to356* a default device.357* The returned <code>Sequencer</code> instance is358* connected to the default <code>Synthesizer</code>,359* as returned by {@link #getSynthesizer}.360* If there is no <code>Synthesizer</code>361* available, or the default <code>Synthesizer</code>362* cannot be opened, the <code>sequencer</code> is connected363* to the default <code>Receiver</code>, as returned364* by {@link #getReceiver}.365* The connection is made by retrieving a <code>Transmitter</code>366* instance from the <code>Sequencer</code> and setting its367* <code>Receiver</code>.368* Closing and re-opening the sequencer will restore the369* connection to the default device.370*371* <p>This method is equivalent to calling372* <code>getSequencer(true)</code>.373*374* <p>If the system property375* <code>javax.sound.midi.Sequencer</code>376* is defined or it is defined in the file "sound.properties",377* it is used to identify the default sequencer.378* For details, refer to the {@link MidiSystem class description}.379*380* @return the default sequencer, connected to a default Receiver381* @throws MidiUnavailableException if the sequencer is not382* available due to resource restrictions,383* or there is no <code>Receiver</code> available by any384* installed <code>MidiDevice</code>,385* or no sequencer is installed in the system.386* @see #getSequencer(boolean)387* @see #getSynthesizer388* @see #getReceiver389*/390public static Sequencer getSequencer() throws MidiUnavailableException {391return getSequencer(true);392}393394395396/**397* Obtains the default <code>Sequencer</code>, optionally398* connected to a default device.399*400* <p>If <code>connected</code> is true, the returned401* <code>Sequencer</code> instance is402* connected to the default <code>Synthesizer</code>,403* as returned by {@link #getSynthesizer}.404* If there is no <code>Synthesizer</code>405* available, or the default <code>Synthesizer</code>406* cannot be opened, the <code>sequencer</code> is connected407* to the default <code>Receiver</code>, as returned408* by {@link #getReceiver}.409* The connection is made by retrieving a <code>Transmitter</code>410* instance from the <code>Sequencer</code> and setting its411* <code>Receiver</code>.412* Closing and re-opening the sequencer will restore the413* connection to the default device.414*415* <p>If <code>connected</code> is false, the returned416* <code>Sequencer</code> instance is not connected, it417* has no open <code>Transmitters</code>. In order to418* play the sequencer on a MIDI device, or a <code>Synthesizer</code>,419* it is necessary to get a <code>Transmitter</code> and set its420* <code>Receiver</code>.421*422* <p>If the system property423* <code>javax.sound.midi.Sequencer</code>424* is defined or it is defined in the file "sound.properties",425* it is used to identify the default sequencer.426* For details, refer to the {@link MidiSystem class description}.427*428* @param connected whether or not the returned {@code Sequencer}429* is connected to the default {@code Synthesizer}430* @return the default sequencer431* @throws MidiUnavailableException if the sequencer is not432* available due to resource restrictions,433* or no sequencer is installed in the system,434* or if <code>connected</code> is true, and there is435* no <code>Receiver</code> available by any installed436* <code>MidiDevice</code>437* @see #getSynthesizer438* @see #getReceiver439* @since 1.5440*/441public static Sequencer getSequencer(boolean connected)442throws MidiUnavailableException {443Sequencer seq = (Sequencer) getDefaultDeviceWrapper(Sequencer.class);444445if (connected) {446// IMPORTANT: this code needs to be synch'ed with447// all AutoConnectSequencer instances,448// (e.g. RealTimeSequencer) because the449// same algorithm for synth retrieval450// needs to be used!451452Receiver rec = null;453MidiUnavailableException mue = null;454455// first try to connect to the default synthesizer456try {457Synthesizer synth = getSynthesizer();458if (synth instanceof ReferenceCountingDevice) {459rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting();460} else {461synth.open();462try {463rec = synth.getReceiver();464} finally {465// make sure that the synth is properly closed466if (rec == null) {467synth.close();468}469}470}471} catch (MidiUnavailableException e) {472// something went wrong with synth473if (e instanceof MidiUnavailableException) {474mue = (MidiUnavailableException) e;475}476}477if (rec == null) {478// then try to connect to the default Receiver479try {480rec = MidiSystem.getReceiver();481} catch (Exception e) {482// something went wrong. Nothing to do then!483if (e instanceof MidiUnavailableException) {484mue = (MidiUnavailableException) e;485}486}487}488if (rec != null) {489seq.getTransmitter().setReceiver(rec);490if (seq instanceof AutoConnectSequencer) {491((AutoConnectSequencer) seq).setAutoConnect(rec);492}493} else {494if (mue != null) {495throw mue;496}497throw new MidiUnavailableException("no receiver available");498}499}500return seq;501}502503504505506/**507* Constructs a MIDI sound bank by reading it from the specified stream.508* The stream must point to509* a valid MIDI soundbank file. In general, MIDI soundbank providers may510* need to read some data from the stream before determining whether they511* support it. These parsers must512* be able to mark the stream, read enough data to determine whether they513* support the stream, and, if not, reset the stream's read pointer to514* its original position. If the input stream does not support this,515* this method may fail with an IOException.516* @param stream the source of the sound bank data.517* @return the sound bank518* @throws InvalidMidiDataException if the stream does not point to519* valid MIDI soundbank data recognized by the system520* @throws IOException if an I/O error occurred when loading the soundbank521* @see InputStream#markSupported522* @see InputStream#mark523*/524public static Soundbank getSoundbank(InputStream stream)525throws InvalidMidiDataException, IOException {526527SoundbankReader sp = null;528Soundbank s = null;529530List providers = getSoundbankReaders();531532for(int i = 0; i < providers.size(); i++) {533sp = (SoundbankReader)providers.get(i);534s = sp.getSoundbank(stream);535536if( s!= null) {537return s;538}539}540throw new InvalidMidiDataException("cannot get soundbank from stream");541542}543544545/**546* Constructs a <code>Soundbank</code> by reading it from the specified URL.547* The URL must point to a valid MIDI soundbank file.548*549* @param url the source of the sound bank data550* @return the sound bank551* @throws InvalidMidiDataException if the URL does not point to valid MIDI552* soundbank data recognized by the system553* @throws IOException if an I/O error occurred when loading the soundbank554*/555public static Soundbank getSoundbank(URL url)556throws InvalidMidiDataException, IOException {557558SoundbankReader sp = null;559Soundbank s = null;560561List providers = getSoundbankReaders();562563for(int i = 0; i < providers.size(); i++) {564sp = (SoundbankReader)providers.get(i);565s = sp.getSoundbank(url);566567if( s!= null) {568return s;569}570}571throw new InvalidMidiDataException("cannot get soundbank from stream");572573}574575576/**577* Constructs a <code>Soundbank</code> by reading it from the specified578* <code>File</code>.579* The <code>File</code> must point to a valid MIDI soundbank file.580*581* @param file the source of the sound bank data582* @return the sound bank583* @throws InvalidMidiDataException if the <code>File</code> does not584* point to valid MIDI soundbank data recognized by the system585* @throws IOException if an I/O error occurred when loading the soundbank586*/587public static Soundbank getSoundbank(File file)588throws InvalidMidiDataException, IOException {589590SoundbankReader sp = null;591Soundbank s = null;592593List providers = getSoundbankReaders();594595for(int i = 0; i < providers.size(); i++) {596sp = (SoundbankReader)providers.get(i);597s = sp.getSoundbank(file);598599if( s!= null) {600return s;601}602}603throw new InvalidMidiDataException("cannot get soundbank from stream");604}605606607608/**609* Obtains the MIDI file format of the data in the specified input stream.610* The stream must point to valid MIDI file data for a file type recognized611* by the system.612* <p>613* This method and/or the code it invokes may need to read some data from614* the stream to determine whether its data format is supported. The615* implementation may therefore616* need to mark the stream, read enough data to determine whether it is in617* a supported format, and reset the stream's read pointer to its original618* position. If the input stream does not permit this set of operations,619* this method may fail with an <code>IOException</code>.620* <p>621* This operation can only succeed for files of a type which can be parsed622* by an installed file reader. It may fail with an InvalidMidiDataException623* even for valid files if no compatible file reader is installed. It624* will also fail with an InvalidMidiDataException if a compatible file reader625* is installed, but encounters errors while determining the file format.626*627* @param stream the input stream from which file format information628* should be extracted629* @return an <code>MidiFileFormat</code> object describing the MIDI file630* format631* @throws InvalidMidiDataException if the stream does not point to valid632* MIDI file data recognized by the system633* @throws IOException if an I/O exception occurs while accessing the634* stream635* @see #getMidiFileFormat(URL)636* @see #getMidiFileFormat(File)637* @see InputStream#markSupported638* @see InputStream#mark639*/640public static MidiFileFormat getMidiFileFormat(InputStream stream)641throws InvalidMidiDataException, IOException {642643List providers = getMidiFileReaders();644MidiFileFormat format = null;645646for(int i = 0; i < providers.size(); i++) {647MidiFileReader reader = (MidiFileReader) providers.get(i);648try {649format = reader.getMidiFileFormat( stream ); // throws IOException650break;651} catch (InvalidMidiDataException e) {652continue;653}654}655656if( format==null ) {657throw new InvalidMidiDataException("input stream is not a supported file type");658} else {659return format;660}661}662663664/**665* Obtains the MIDI file format of the data in the specified URL. The URL666* must point to valid MIDI file data for a file type recognized667* by the system.668* <p>669* This operation can only succeed for files of a type which can be parsed670* by an installed file reader. It may fail with an InvalidMidiDataException671* even for valid files if no compatible file reader is installed. It672* will also fail with an InvalidMidiDataException if a compatible file reader673* is installed, but encounters errors while determining the file format.674*675* @param url the URL from which file format information should be676* extracted677* @return a <code>MidiFileFormat</code> object describing the MIDI file678* format679* @throws InvalidMidiDataException if the URL does not point to valid MIDI680* file data recognized by the system681* @throws IOException if an I/O exception occurs while accessing the URL682*683* @see #getMidiFileFormat(InputStream)684* @see #getMidiFileFormat(File)685*/686public static MidiFileFormat getMidiFileFormat(URL url)687throws InvalidMidiDataException, IOException {688689List providers = getMidiFileReaders();690MidiFileFormat format = null;691692for(int i = 0; i < providers.size(); i++) {693MidiFileReader reader = (MidiFileReader) providers.get(i);694try {695format = reader.getMidiFileFormat( url ); // throws IOException696break;697} catch (InvalidMidiDataException e) {698continue;699}700}701702if( format==null ) {703throw new InvalidMidiDataException("url is not a supported file type");704} else {705return format;706}707}708709710/**711* Obtains the MIDI file format of the specified <code>File</code>. The712* <code>File</code> must point to valid MIDI file data for a file type713* recognized by the system.714* <p>715* This operation can only succeed for files of a type which can be parsed716* by an installed file reader. It may fail with an InvalidMidiDataException717* even for valid files if no compatible file reader is installed. It718* will also fail with an InvalidMidiDataException if a compatible file reader719* is installed, but encounters errors while determining the file format.720*721* @param file the <code>File</code> from which file format information722* should be extracted723* @return a <code>MidiFileFormat</code> object describing the MIDI file724* format725* @throws InvalidMidiDataException if the <code>File</code> does not point726* to valid MIDI file data recognized by the system727* @throws IOException if an I/O exception occurs while accessing the file728*729* @see #getMidiFileFormat(InputStream)730* @see #getMidiFileFormat(URL)731*/732public static MidiFileFormat getMidiFileFormat(File file)733throws InvalidMidiDataException, IOException {734735List providers = getMidiFileReaders();736MidiFileFormat format = null;737738for(int i = 0; i < providers.size(); i++) {739MidiFileReader reader = (MidiFileReader) providers.get(i);740try {741format = reader.getMidiFileFormat( file ); // throws IOException742break;743} catch (InvalidMidiDataException e) {744continue;745}746}747748if( format==null ) {749throw new InvalidMidiDataException("file is not a supported file type");750} else {751return format;752}753}754755756/**757* Obtains a MIDI sequence from the specified input stream. The stream must758* point to valid MIDI file data for a file type recognized759* by the system.760* <p>761* This method and/or the code it invokes may need to read some data762* from the stream to determine whether763* its data format is supported. The implementation may therefore764* need to mark the stream, read enough data to determine whether it is in765* a supported format, and reset the stream's read pointer to its original766* position. If the input stream does not permit this set of operations,767* this method may fail with an <code>IOException</code>.768* <p>769* This operation can only succeed for files of a type which can be parsed770* by an installed file reader. It may fail with an InvalidMidiDataException771* even for valid files if no compatible file reader is installed. It772* will also fail with an InvalidMidiDataException if a compatible file reader773* is installed, but encounters errors while constructing the <code>Sequence</code>774* object from the file data.775*776* @param stream the input stream from which the <code>Sequence</code>777* should be constructed778* @return a <code>Sequence</code> object based on the MIDI file data779* contained in the input stream780* @throws InvalidMidiDataException if the stream does not point to781* valid MIDI file data recognized by the system782* @throws IOException if an I/O exception occurs while accessing the783* stream784* @see InputStream#markSupported785* @see InputStream#mark786*/787public static Sequence getSequence(InputStream stream)788throws InvalidMidiDataException, IOException {789790List providers = getMidiFileReaders();791Sequence sequence = null;792793for(int i = 0; i < providers.size(); i++) {794MidiFileReader reader = (MidiFileReader) providers.get(i);795try {796sequence = reader.getSequence( stream ); // throws IOException797break;798} catch (InvalidMidiDataException e) {799continue;800}801}802803if( sequence==null ) {804throw new InvalidMidiDataException("could not get sequence from input stream");805} else {806return sequence;807}808}809810811/**812* Obtains a MIDI sequence from the specified URL. The URL must813* point to valid MIDI file data for a file type recognized814* by the system.815* <p>816* This operation can only succeed for files of a type which can be parsed817* by an installed file reader. It may fail with an InvalidMidiDataException818* even for valid files if no compatible file reader is installed. It819* will also fail with an InvalidMidiDataException if a compatible file reader820* is installed, but encounters errors while constructing the <code>Sequence</code>821* object from the file data.822*823* @param url the URL from which the <code>Sequence</code> should be824* constructed825* @return a <code>Sequence</code> object based on the MIDI file data826* pointed to by the URL827* @throws InvalidMidiDataException if the URL does not point to valid MIDI828* file data recognized by the system829* @throws IOException if an I/O exception occurs while accessing the URL830*/831public static Sequence getSequence(URL url)832throws InvalidMidiDataException, IOException {833834List providers = getMidiFileReaders();835Sequence sequence = null;836837for(int i = 0; i < providers.size(); i++) {838MidiFileReader reader = (MidiFileReader) providers.get(i);839try {840sequence = reader.getSequence( url ); // throws IOException841break;842} catch (InvalidMidiDataException e) {843continue;844}845}846847if( sequence==null ) {848throw new InvalidMidiDataException("could not get sequence from URL");849} else {850return sequence;851}852}853854855/**856* Obtains a MIDI sequence from the specified <code>File</code>.857* The <code>File</code> must point to valid MIDI file data858* for a file type recognized by the system.859* <p>860* This operation can only succeed for files of a type which can be parsed861* by an installed file reader. It may fail with an InvalidMidiDataException862* even for valid files if no compatible file reader is installed. It863* will also fail with an InvalidMidiDataException if a compatible file reader864* is installed, but encounters errors while constructing the <code>Sequence</code>865* object from the file data.866*867* @param file the <code>File</code> from which the <code>Sequence</code>868* should be constructed869* @return a <code>Sequence</code> object based on the MIDI file data870* pointed to by the File871* @throws InvalidMidiDataException if the File does not point to valid MIDI872* file data recognized by the system873* @throws IOException if an I/O exception occurs874*/875public static Sequence getSequence(File file)876throws InvalidMidiDataException, IOException {877878List providers = getMidiFileReaders();879Sequence sequence = null;880881for(int i = 0; i < providers.size(); i++) {882MidiFileReader reader = (MidiFileReader) providers.get(i);883try {884sequence = reader.getSequence( file ); // throws IOException885break;886} catch (InvalidMidiDataException e) {887continue;888}889}890891if( sequence==null ) {892throw new InvalidMidiDataException("could not get sequence from file");893} else {894return sequence;895}896}897898899/**900* Obtains the set of MIDI file types for which file writing support is901* provided by the system.902* @return array of unique file types. If no file types are supported,903* an array of length 0 is returned.904*/905public static int[] getMidiFileTypes() {906907List providers = getMidiFileWriters();908Set allTypes = new HashSet();909910// gather from all the providers911912for (int i = 0; i < providers.size(); i++ ) {913MidiFileWriter writer = (MidiFileWriter) providers.get(i);914int[] types = writer.getMidiFileTypes();915for (int j = 0; j < types.length; j++ ) {916allTypes.add(new Integer(types[j]));917}918}919int resultTypes[] = new int[allTypes.size()];920int index = 0;921Iterator iterator = allTypes.iterator();922while (iterator.hasNext()) {923Integer integer = (Integer) iterator.next();924resultTypes[index++] = integer.intValue();925}926return resultTypes;927}928929930/**931* Indicates whether file writing support for the specified MIDI file type932* is provided by the system.933* @param fileType the file type for which write capabilities are queried934* @return <code>true</code> if the file type is supported,935* otherwise <code>false</code>936*/937public static boolean isFileTypeSupported(int fileType) {938939List providers = getMidiFileWriters();940941for (int i = 0; i < providers.size(); i++ ) {942MidiFileWriter writer = (MidiFileWriter) providers.get(i);943if( writer.isFileTypeSupported(fileType)) {944return true;945}946}947return false;948}949950951/**952* Obtains the set of MIDI file types that the system can write from the953* sequence specified.954* @param sequence the sequence for which MIDI file type support955* is queried956* @return the set of unique supported file types. If no file types are supported,957* returns an array of length 0.958*/959public static int[] getMidiFileTypes(Sequence sequence) {960961List providers = getMidiFileWriters();962Set allTypes = new HashSet();963964// gather from all the providers965966for (int i = 0; i < providers.size(); i++ ) {967MidiFileWriter writer = (MidiFileWriter) providers.get(i);968int[] types = writer.getMidiFileTypes(sequence);969for (int j = 0; j < types.length; j++ ) {970allTypes.add(new Integer(types[j]));971}972}973int resultTypes[] = new int[allTypes.size()];974int index = 0;975Iterator iterator = allTypes.iterator();976while (iterator.hasNext()) {977Integer integer = (Integer) iterator.next();978resultTypes[index++] = integer.intValue();979}980return resultTypes;981}982983984/**985* Indicates whether a MIDI file of the file type specified can be written986* from the sequence indicated.987* @param fileType the file type for which write capabilities988* are queried989* @param sequence the sequence for which file writing support is queried990* @return <code>true</code> if the file type is supported for this991* sequence, otherwise <code>false</code>992*/993public static boolean isFileTypeSupported(int fileType, Sequence sequence) {994995List providers = getMidiFileWriters();996997for (int i = 0; i < providers.size(); i++ ) {998MidiFileWriter writer = (MidiFileWriter) providers.get(i);999if( writer.isFileTypeSupported(fileType,sequence)) {1000return true;1001}1002}1003return false;1004}100510061007/**1008* Writes a stream of bytes representing a file of the MIDI file type1009* indicated to the output stream provided.1010* @param in sequence containing MIDI data to be written to the file1011* @param fileType the file type of the file to be written to the output stream1012* @param out stream to which the file data should be written1013* @return the number of bytes written to the output stream1014* @throws IOException if an I/O exception occurs1015* @throws IllegalArgumentException if the file format is not supported by1016* the system1017* @see #isFileTypeSupported(int, Sequence)1018* @see #getMidiFileTypes(Sequence)1019*/1020public static int write(Sequence in, int fileType, OutputStream out) throws IOException {10211022List providers = getMidiFileWriters();1023//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences1024int bytesWritten = -2;10251026for (int i = 0; i < providers.size(); i++ ) {1027MidiFileWriter writer = (MidiFileWriter) providers.get(i);1028if( writer.isFileTypeSupported( fileType, in ) ) {10291030bytesWritten = writer.write(in, fileType, out);1031break;1032}1033}1034if (bytesWritten == -2) {1035throw new IllegalArgumentException("MIDI file type is not supported");1036}1037return bytesWritten;1038}103910401041/**1042* Writes a stream of bytes representing a file of the MIDI file type1043* indicated to the external file provided.1044* @param in sequence containing MIDI data to be written to the file1045* @param type the file type of the file to be written to the output stream1046* @param out external file to which the file data should be written1047* @return the number of bytes written to the file1048* @throws IOException if an I/O exception occurs1049* @throws IllegalArgumentException if the file type is not supported by1050* the system1051* @see #isFileTypeSupported(int, Sequence)1052* @see #getMidiFileTypes(Sequence)1053*/1054public static int write(Sequence in, int type, File out) throws IOException {10551056List providers = getMidiFileWriters();1057//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences1058int bytesWritten = -2;10591060for (int i = 0; i < providers.size(); i++ ) {1061MidiFileWriter writer = (MidiFileWriter) providers.get(i);1062if( writer.isFileTypeSupported( type, in ) ) {10631064bytesWritten = writer.write(in, type, out);1065break;1066}1067}1068if (bytesWritten == -2) {1069throw new IllegalArgumentException("MIDI file type is not supported");1070}1071return bytesWritten;1072}1073107410751076// HELPER METHODS10771078private static List getMidiDeviceProviders() {1079return getProviders(MidiDeviceProvider.class);1080}108110821083private static List getSoundbankReaders() {1084return getProviders(SoundbankReader.class);1085}108610871088private static List getMidiFileWriters() {1089return getProviders(MidiFileWriter.class);1090}109110921093private static List getMidiFileReaders() {1094return getProviders(MidiFileReader.class);1095}109610971098/** Attempts to locate and return a default MidiDevice of the specified1099* type.1100*1101* This method wraps {@link #getDefaultDevice}. It catches the1102* <code>IllegalArgumentException</code> thrown by1103* <code>getDefaultDevice</code> and instead throws a1104* <code>MidiUnavailableException</code>, with the catched1105* exception chained.1106*1107* @param deviceClass The requested device type, one of Synthesizer.class,1108* Sequencer.class, Receiver.class or Transmitter.class.1109* @throws MidiUnavalableException on failure.1110*/1111private static MidiDevice getDefaultDeviceWrapper(Class deviceClass)1112throws MidiUnavailableException{1113try {1114return getDefaultDevice(deviceClass);1115} catch (IllegalArgumentException iae) {1116MidiUnavailableException mae = new MidiUnavailableException();1117mae.initCause(iae);1118throw mae;1119}1120}112111221123/** Attempts to locate and return a default MidiDevice of the specified1124* type.1125*1126* @param deviceClass The requested device type, one of Synthesizer.class,1127* Sequencer.class, Receiver.class or Transmitter.class.1128* @throws IllegalArgumentException on failure.1129*/1130private static MidiDevice getDefaultDevice(Class deviceClass) {1131List providers = getMidiDeviceProviders();1132String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass);1133String instanceName = JDK13Services.getDefaultInstanceName(deviceClass);1134MidiDevice device;11351136if (providerClassName != null) {1137MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers);1138if (defaultProvider != null) {1139if (instanceName != null) {1140device = getNamedDevice(instanceName, defaultProvider, deviceClass);1141if (device != null) {1142return device;1143}1144}1145device = getFirstDevice(defaultProvider, deviceClass);1146if (device != null) {1147return device;1148}1149}1150}11511152/* Provider class not specified or cannot be found, or1153provider class specified, and no appropriate device available or1154provider class and instance specified and instance cannot be found or is not appropriate */1155if (instanceName != null) {1156device = getNamedDevice(instanceName, providers, deviceClass);1157if (device != null) {1158return device;1159}1160}11611162/* No default are specified, or if something is specified, everything1163failed. */1164device = getFirstDevice(providers, deviceClass);1165if (device != null) {1166return device;1167}1168throw new IllegalArgumentException("Requested device not installed");1169}1170117111721173/** Return a MidiDeviceProcider of a given class from the list of1174MidiDeviceProviders.11751176@param providerClassName The class name of the provider to be returned.1177@param provider The list of MidiDeviceProviders that is searched.1178@return A MidiDeviceProvider of the requested class, or null if none1179is found.1180*/1181private static MidiDeviceProvider getNamedProvider(String providerClassName, List providers) {1182for(int i = 0; i < providers.size(); i++) {1183MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);1184if (provider.getClass().getName().equals(providerClassName)) {1185return provider;1186}1187}1188return null;1189}119011911192/** Return a MidiDevice with a given name from a given MidiDeviceProvider.1193@param deviceName The name of the MidiDevice to be returned.1194@param provider The MidiDeviceProvider to check for MidiDevices.1195@param deviceClass The requested device type, one of Synthesizer.class,1196Sequencer.class, Receiver.class or Transmitter.class.11971198@return A MidiDevice matching the requirements, or null if none is found.1199*/1200private static MidiDevice getNamedDevice(String deviceName,1201MidiDeviceProvider provider,1202Class deviceClass) {1203MidiDevice device;1204// try to get MIDI port1205device = getNamedDevice(deviceName, provider, deviceClass,1206false, false);1207if (device != null) {1208return device;1209}12101211if (deviceClass == Receiver.class) {1212// try to get Synthesizer1213device = getNamedDevice(deviceName, provider, deviceClass,1214true, false);1215if (device != null) {1216return device;1217}1218}12191220return null;1221}122212231224/** Return a MidiDevice with a given name from a given MidiDeviceProvider.1225@param deviceName The name of the MidiDevice to be returned.1226@param provider The MidiDeviceProvider to check for MidiDevices.1227@param deviceClass The requested device type, one of Synthesizer.class,1228Sequencer.class, Receiver.class or Transmitter.class.12291230@return A MidiDevice matching the requirements, or null if none is found.1231*/1232private static MidiDevice getNamedDevice(String deviceName,1233MidiDeviceProvider provider,1234Class deviceClass,1235boolean allowSynthesizer,1236boolean allowSequencer) {1237MidiDevice.Info[] infos = provider.getDeviceInfo();1238for (int i = 0; i < infos.length; i++) {1239if (infos[i].getName().equals(deviceName)) {1240MidiDevice device = provider.getDevice(infos[i]);1241if (isAppropriateDevice(device, deviceClass,1242allowSynthesizer, allowSequencer)) {1243return device;1244}1245}1246}1247return null;1248}124912501251/** Return a MidiDevice with a given name from a list of1252MidiDeviceProviders.1253@param deviceName The name of the MidiDevice to be returned.1254@param providers The List of MidiDeviceProviders to check for1255MidiDevices.1256@param deviceClass The requested device type, one of Synthesizer.class,1257Sequencer.class, Receiver.class or Transmitter.class.1258@return A Mixer matching the requirements, or null if none is found.1259*/1260private static MidiDevice getNamedDevice(String deviceName,1261List providers,1262Class deviceClass) {1263MidiDevice device;1264// try to get MIDI port1265device = getNamedDevice(deviceName, providers, deviceClass,1266false, false);1267if (device != null) {1268return device;1269}12701271if (deviceClass == Receiver.class) {1272// try to get Synthesizer1273device = getNamedDevice(deviceName, providers, deviceClass,1274true, false);1275if (device != null) {1276return device;1277}1278}12791280return null;1281}128212831284/** Return a MidiDevice with a given name from a list of1285MidiDeviceProviders.1286@param deviceName The name of the MidiDevice to be returned.1287@param providers The List of MidiDeviceProviders to check for1288MidiDevices.1289@param deviceClass The requested device type, one of Synthesizer.class,1290Sequencer.class, Receiver.class or Transmitter.class.1291@return A Mixer matching the requirements, or null if none is found.1292*/1293private static MidiDevice getNamedDevice(String deviceName,1294List providers,1295Class deviceClass,1296boolean allowSynthesizer,1297boolean allowSequencer) {1298for(int i = 0; i < providers.size(); i++) {1299MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);1300MidiDevice device = getNamedDevice(deviceName, provider,1301deviceClass,1302allowSynthesizer,1303allowSequencer);1304if (device != null) {1305return device;1306}1307}1308return null;1309}131013111312/** From a given MidiDeviceProvider, return the first appropriate device.1313@param provider The MidiDeviceProvider to check for MidiDevices.1314@param deviceClass The requested device type, one of Synthesizer.class,1315Sequencer.class, Receiver.class or Transmitter.class.1316@return A MidiDevice is considered appropriate, or null if no1317appropriate device is found.1318*/1319private static MidiDevice getFirstDevice(MidiDeviceProvider provider,1320Class deviceClass) {1321MidiDevice device;1322// try to get MIDI port1323device = getFirstDevice(provider, deviceClass,1324false, false);1325if (device != null) {1326return device;1327}13281329if (deviceClass == Receiver.class) {1330// try to get Synthesizer1331device = getFirstDevice(provider, deviceClass,1332true, false);1333if (device != null) {1334return device;1335}1336}13371338return null;1339}134013411342/** From a given MidiDeviceProvider, return the first appropriate device.1343@param provider The MidiDeviceProvider to check for MidiDevices.1344@param deviceClass The requested device type, one of Synthesizer.class,1345Sequencer.class, Receiver.class or Transmitter.class.1346@return A MidiDevice is considered appropriate, or null if no1347appropriate device is found.1348*/1349private static MidiDevice getFirstDevice(MidiDeviceProvider provider,1350Class deviceClass,1351boolean allowSynthesizer,1352boolean allowSequencer) {1353MidiDevice.Info[] infos = provider.getDeviceInfo();1354for (int j = 0; j < infos.length; j++) {1355MidiDevice device = provider.getDevice(infos[j]);1356if (isAppropriateDevice(device, deviceClass,1357allowSynthesizer, allowSequencer)) {1358return device;1359}1360}1361return null;1362}136313641365/** From a List of MidiDeviceProviders, return the first appropriate1366MidiDevice.1367@param providers The List of MidiDeviceProviders to search.1368@param deviceClass The requested device type, one of Synthesizer.class,1369Sequencer.class, Receiver.class or Transmitter.class.1370@return A MidiDevice that is considered appropriate, or null1371if none is found.1372*/1373private static MidiDevice getFirstDevice(List providers,1374Class deviceClass) {1375MidiDevice device;1376// try to get MIDI port1377device = getFirstDevice(providers, deviceClass,1378false, false);1379if (device != null) {1380return device;1381}13821383if (deviceClass == Receiver.class) {1384// try to get Synthesizer1385device = getFirstDevice(providers, deviceClass,1386true, false);1387if (device != null) {1388return device;1389}1390}13911392return null;1393}139413951396/** From a List of MidiDeviceProviders, return the first appropriate1397MidiDevice.1398@param providers The List of MidiDeviceProviders to search.1399@param deviceClass The requested device type, one of Synthesizer.class,1400Sequencer.class, Receiver.class or Transmitter.class.1401@return A MidiDevice that is considered appropriate, or null1402if none is found.1403*/1404private static MidiDevice getFirstDevice(List providers,1405Class deviceClass,1406boolean allowSynthesizer,1407boolean allowSequencer) {1408for(int i = 0; i < providers.size(); i++) {1409MidiDeviceProvider provider = (MidiDeviceProvider) providers.get(i);1410MidiDevice device = getFirstDevice(provider, deviceClass,1411allowSynthesizer,1412allowSequencer);1413if (device != null) {1414return device;1415}1416}1417return null;1418}141914201421/** Checks if a MidiDevice is appropriate.1422If deviceClass is Synthesizer or Sequencer, a device implementing1423the respective interface is considered appropriate. If deviceClass1424is Receiver or Transmitter, a device is considered appropriate if1425it implements neither Synthesizer nor Transmitter, and if it can1426provide at least one Receiver or Transmitter, respectively.14271428@param device the MidiDevice to test1429@param allowSynthesizer if true, Synthesizers are considered1430appropriate. Otherwise only pure MidiDevices are considered1431appropriate (unless allowSequencer is true). This flag only has an1432effect for deviceClass Receiver and Transmitter. For other device1433classes (Sequencer and Synthesizer), this flag has no effect.1434@param allowSequencer if true, Sequencers are considered1435appropriate. Otherwise only pure MidiDevices are considered1436appropriate (unless allowSynthesizer is true). This flag only has an1437effect for deviceClass Receiver and Transmitter. For other device1438classes (Sequencer and Synthesizer), this flag has no effect.1439@return true if the device is considered appropriate according to the1440rules given above, false otherwise.1441*/1442private static boolean isAppropriateDevice(MidiDevice device,1443Class deviceClass,1444boolean allowSynthesizer,1445boolean allowSequencer) {1446if (deviceClass.isInstance(device)) {1447// This clause is for deviceClass being either Synthesizer1448// or Sequencer.1449return true;1450} else {1451// Now the case that deviceClass is Transmitter or1452// Receiver. If neither allowSynthesizer nor allowSequencer is1453// true, we require device instances to be1454// neither Synthesizer nor Sequencer, since we only want1455// devices representing MIDI ports.1456// Otherwise, the respective type is accepted, too1457if ( (! (device instanceof Sequencer) &&1458! (device instanceof Synthesizer) ) ||1459((device instanceof Sequencer) && allowSequencer) ||1460((device instanceof Synthesizer) && allowSynthesizer)) {1461// And of cource, the device has to be able to provide1462// Receivers or Transmitters.1463if ((deviceClass == Receiver.class &&1464device.getMaxReceivers() != 0) ||1465(deviceClass == Transmitter.class &&1466device.getMaxTransmitters() != 0)) {1467return true;1468}1469}1470}1471return false;1472}147314741475/**1476* Obtains the set of services currently installed on the system1477* using sun.misc.Service, the SPI mechanism in 1.3.1478* @return a List of instances of providers for the requested service.1479* If no providers are available, a List of length 0 will be returned.1480*/1481private static List getProviders(Class providerClass) {1482return JDK13Services.getProviders(providerClass);1483}1484}148514861487