/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.sound.midi;
import java.io.FileInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.net.URL;
import javax.sound.midi.spi.MidiFileWriter;
import javax.sound.midi.spi.MidiFileReader;
import javax.sound.midi.spi.SoundbankReader;
import javax.sound.midi.spi.MidiDeviceProvider;
import com.sun.media.sound.JDK13Services;
import com.sun.media.sound.ReferenceCountingDevice;
import com.sun.media.sound.AutoConnectSequencer;
import com.sun.media.sound.MidiDeviceReceiverEnvelope;
import com.sun.media.sound.MidiDeviceTransmitterEnvelope;
/**
* The MidiSystem
class provides access to the installed MIDI
* system resources, including devices such as synthesizers, sequencers, and
* MIDI input and output ports. A typical simple MIDI application might
* begin by invoking one or more MidiSystem
methods to learn
* what devices are installed and to obtain the ones needed in that
* application.
*
* The class also has methods for reading files, streams, and URLs that
* contain standard MIDI file data or soundbanks. You can query the
* MidiSystem
for the format of a specified MIDI file.
*
* You cannot instantiate a MidiSystem
; all the methods are
* static.
*
*
Properties can be used to specify default MIDI devices.
* Both system properties and a properties file are considered.
* The sound.properties
properties file is read from
* an implementation-specific location (typically it is the lib
* directory in the Java installation directory).
* If a property exists both as a system property and in the
* properties file, the system property takes precedence. If none is
* specified, a suitable default is chosen among the available devices.
* The syntax of the properties file is specified in
* {@link java.util.Properties#load(InputStream) Properties.load}. The
* following table lists the available property keys and which methods
* consider them:
*
*
Property Key | *Interface | *Affected Method | *
---|---|---|
javax.sound.midi.Receiver |
* {@link Receiver} | *{@link #getReceiver} | *
javax.sound.midi.Sequencer |
* {@link Sequencer} | *{@link #getSequencer} | *
javax.sound.midi.Synthesizer |
* {@link Synthesizer} | *{@link #getSynthesizer} | *
javax.sound.midi.Transmitter |
* {@link Transmitter} | *{@link #getTransmitter} | *
String
returned by the getName
* method of MidiDevice.Info
.
* Either the class name, or the device name may be omitted.
* If only the class name is specified, the trailing hash mark
* is optional.
*
* If the provider class is specified, and it can be
* successfully retrieved from the installed providers,
* the list of
* MidiDevice.Info
objects is retrieved
* from the provider. Otherwise, or when these devices
* do not provide a subsequent match, the list is retrieved
* from {@link #getMidiDeviceInfo} to contain
* all available MidiDevice.Info
objects.
*
*
If a device name is specified, the resulting list of
* If the system property
* If a native receiver provided by the default device does not implement
* the {@code MidiDeviceReceiver} interface, it will be wrapped in a
* wrapper class that implements the {@code MidiDeviceReceiver} interface.
* The corresponding {@code Receiver} method calls will be forwarded
* to the native receiver.
*
* If this method returns successfully, the {@link
* javax.sound.midi.MidiDevice MidiDevice} the
* If the system property
* If a native transmitter provided by the default device does not implement
* the {@code MidiDeviceTransmitter} interface, it will be wrapped in a
* wrapper class that implements the {@code MidiDeviceTransmitter} interface.
* The corresponding {@code Transmitter} method calls will be forwarded
* to the native transmitter.
*
* If this method returns successfully, the {@link
* javax.sound.midi.MidiDevice MidiDevice} the
* If the system property
* This method is equivalent to calling
* If the system property
* If If If the system property
*
* This method and/or the code it invokes may need to read some data from
* the stream to determine whether its data format is supported. The
* implementation may therefore
* need to mark the stream, read enough data to determine whether it is in
* a supported format, and reset the stream's read pointer to its original
* position. If the input stream does not permit this set of operations,
* this method may fail with an
* This operation can only succeed for files of a type which can be parsed
* by an installed file reader. It may fail with an InvalidMidiDataException
* even for valid files if no compatible file reader is installed. It
* will also fail with an InvalidMidiDataException if a compatible file reader
* is installed, but encounters errors while determining the file format.
*
* @param stream the input stream from which file format information
* should be extracted
* @return an
* This operation can only succeed for files of a type which can be parsed
* by an installed file reader. It may fail with an InvalidMidiDataException
* even for valid files if no compatible file reader is installed. It
* will also fail with an InvalidMidiDataException if a compatible file reader
* is installed, but encounters errors while determining the file format.
*
* @param url the URL from which file format information should be
* extracted
* @return a
* This operation can only succeed for files of a type which can be parsed
* by an installed file reader. It may fail with an InvalidMidiDataException
* even for valid files if no compatible file reader is installed. It
* will also fail with an InvalidMidiDataException if a compatible file reader
* is installed, but encounters errors while determining the file format.
*
* @param file the
* This method and/or the code it invokes may need to read some data
* from the stream to determine whether
* its data format is supported. The implementation may therefore
* need to mark the stream, read enough data to determine whether it is in
* a supported format, and reset the stream's read pointer to its original
* position. If the input stream does not permit this set of operations,
* this method may fail with an
* This operation can only succeed for files of a type which can be parsed
* by an installed file reader. It may fail with an InvalidMidiDataException
* even for valid files if no compatible file reader is installed. It
* will also fail with an InvalidMidiDataException if a compatible file reader
* is installed, but encounters errors while constructing the
* This operation can only succeed for files of a type which can be parsed
* by an installed file reader. It may fail with an InvalidMidiDataException
* even for valid files if no compatible file reader is installed. It
* will also fail with an InvalidMidiDataException if a compatible file reader
* is installed, but encounters errors while constructing the
* This operation can only succeed for files of a type which can be parsed
* by an installed file reader. It may fail with an InvalidMidiDataException
* even for valid files if no compatible file reader is installed. It
* will also fail with an InvalidMidiDataException if a compatible file reader
* is installed, but encounters errors while constructing the MidiDevice.Info
objects is searched:
* the first one with a matching name, and whose
* MidiDevice
implements the
* respective interface, will be returned.
* If no matching MidiDevice.Info
object
* is found, or the device name is not specified,
* the first suitable device from the resulting
* list will be returned. For Sequencer and Synthesizer,
* a device is suitable if it implements the respective
* interface; whereas for Receiver and Transmitter, a device is
* suitable if it
* implements neither Sequencer nor Synthesizer and provides
* at least one Receiver or Transmitter, respectively.
*
* For example, the property javax.sound.midi.Receiver
* with a value
* "com.sun.media.sound.MidiProvider#SunMIDI1"
* will have the following consequences when
* getReceiver
is called:
* if the class com.sun.media.sound.MidiProvider
exists
* in the list of installed MIDI device providers,
* the first Receiver
device with name
* "SunMIDI1"
will be returned. If it cannot
* be found, the first Receiver
from that provider
* will be returned, regardless of name.
* If there is none, the first Receiver
with name
* "SunMIDI1"
in the list of all devices
* (as returned by getMidiDeviceInfo
) will be returned,
* or, if not found, the first Receiver
that can
* be found in the list of all devices is returned.
* If that fails, too, a MidiUnavailableException
* is thrown.
*
* @author Kara Kytle
* @author Florian Bomers
* @author Matthias Pfisterer
*/
public class MidiSystem {
/**
* Private no-args constructor for ensuring against instantiation.
*/
private MidiSystem() {
}
/**
* Obtains an array of information objects representing
* the set of all MIDI devices available on the system.
* A returned information object can then be used to obtain the
* corresponding device object, by invoking
* {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}.
*
* @return an array of MidiDevice.Info
objects, one
* for each installed MIDI device. If no such devices are installed,
* an array of length 0 is returned.
*/
public static MidiDevice.Info[] getMidiDeviceInfo() {
Listjavax.sound.midi.Receiver
* is defined or it is defined in the file "sound.properties",
* it is used to identify the device that provides the default receiver.
* For details, refer to the {@link MidiSystem class description}.
*
* If a suitable MIDI port is not available, the Receiver is
* retrieved from an installed synthesizer.
*
* Receiver
belongs to is opened implicitly, if it is
* not already open. It is possible to close an implicitly opened
* device by calling {@link javax.sound.midi.Receiver#close close}
* on the returned Receiver
. All open Receiver
* instances have to be closed in order to release system resources
* hold by the MidiDevice
. For a
* detailed description of open/close behaviour see the class
* description of {@link javax.sound.midi.MidiDevice MidiDevice}.
*
*
* @return the default MIDI receiver
* @throws MidiUnavailableException if the default receiver is not
* available due to resource restrictions,
* or no device providing receivers is installed in the system
*/
public static Receiver getReceiver() throws MidiUnavailableException {
// may throw MidiUnavailableException
MidiDevice device = getDefaultDeviceWrapper(Receiver.class);
Receiver receiver;
if (device instanceof ReferenceCountingDevice) {
receiver = ((ReferenceCountingDevice) device).getReceiverReferenceCounting();
} else {
receiver = device.getReceiver();
}
if (!(receiver instanceof MidiDeviceReceiver)) {
receiver = new MidiDeviceReceiverEnvelope(device, receiver);
}
return receiver;
}
/**
* Obtains a MIDI transmitter from an external MIDI port
* or other default source.
* The returned transmitter always implements
* the {@code MidiDeviceTransmitter} interface.
*
* javax.sound.midi.Transmitter
* is defined or it is defined in the file "sound.properties",
* it is used to identify the device that provides the default transmitter.
* For details, refer to the {@link MidiSystem class description}.
*
* Transmitter
belongs to is opened implicitly, if it
* is not already open. It is possible to close an implicitly
* opened device by calling {@link
* javax.sound.midi.Transmitter#close close} on the returned
* Transmitter
. All open Transmitter
* instances have to be closed in order to release system resources
* hold by the MidiDevice
. For a detailed description
* of open/close behaviour see the class description of {@link
* javax.sound.midi.MidiDevice MidiDevice}.
*
* @return the default MIDI transmitter
* @throws MidiUnavailableException if the default transmitter is not
* available due to resource restrictions,
* or no device providing transmitters is installed in the system
*/
public static Transmitter getTransmitter() throws MidiUnavailableException {
// may throw MidiUnavailableException
MidiDevice device = getDefaultDeviceWrapper(Transmitter.class);
Transmitter transmitter;
if (device instanceof ReferenceCountingDevice) {
transmitter = ((ReferenceCountingDevice) device).getTransmitterReferenceCounting();
} else {
transmitter = device.getTransmitter();
}
if (!(transmitter instanceof MidiDeviceTransmitter)) {
transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter);
}
return transmitter;
}
/**
* Obtains the default synthesizer.
*
* javax.sound.midi.Synthesizer
* is defined or it is defined in the file "sound.properties",
* it is used to identify the default synthesizer.
* For details, refer to the {@link MidiSystem class description}.
*
* @return the default synthesizer
* @throws MidiUnavailableException if the synthesizer is not
* available due to resource restrictions,
* or no synthesizer is installed in the system
*/
public static Synthesizer getSynthesizer() throws MidiUnavailableException {
// may throw MidiUnavailableException
return (Synthesizer) getDefaultDeviceWrapper(Synthesizer.class);
}
/**
* Obtains the default Sequencer
, connected to
* a default device.
* The returned Sequencer
instance is
* connected to the default Synthesizer
,
* as returned by {@link #getSynthesizer}.
* If there is no Synthesizer
* available, or the default Synthesizer
* cannot be opened, the sequencer
is connected
* to the default Receiver
, as returned
* by {@link #getReceiver}.
* The connection is made by retrieving a Transmitter
* instance from the Sequencer
and setting its
* Receiver
.
* Closing and re-opening the sequencer will restore the
* connection to the default device.
*
* getSequencer(true)
.
*
* javax.sound.midi.Sequencer
* is defined or it is defined in the file "sound.properties",
* it is used to identify the default sequencer.
* For details, refer to the {@link MidiSystem class description}.
*
* @return the default sequencer, connected to a default Receiver
* @throws MidiUnavailableException if the sequencer is not
* available due to resource restrictions,
* or there is no Receiver
available by any
* installed MidiDevice
,
* or no sequencer is installed in the system.
* @see #getSequencer(boolean)
* @see #getSynthesizer
* @see #getReceiver
*/
public static Sequencer getSequencer() throws MidiUnavailableException {
return getSequencer(true);
}
/**
* Obtains the default Sequencer
, optionally
* connected to a default device.
*
* connected
is true, the returned
* Sequencer
instance is
* connected to the default Synthesizer
,
* as returned by {@link #getSynthesizer}.
* If there is no Synthesizer
* available, or the default Synthesizer
* cannot be opened, the sequencer
is connected
* to the default Receiver
, as returned
* by {@link #getReceiver}.
* The connection is made by retrieving a Transmitter
* instance from the Sequencer
and setting its
* Receiver
.
* Closing and re-opening the sequencer will restore the
* connection to the default device.
*
* connected
is false, the returned
* Sequencer
instance is not connected, it
* has no open Transmitters
. In order to
* play the sequencer on a MIDI device, or a Synthesizer
,
* it is necessary to get a Transmitter
and set its
* Receiver
.
*
* javax.sound.midi.Sequencer
* is defined or it is defined in the file "sound.properties",
* it is used to identify the default sequencer.
* For details, refer to the {@link MidiSystem class description}.
*
* @param connected whether or not the returned {@code Sequencer}
* is connected to the default {@code Synthesizer}
* @return the default sequencer
* @throws MidiUnavailableException if the sequencer is not
* available due to resource restrictions,
* or no sequencer is installed in the system,
* or if connected
is true, and there is
* no Receiver
available by any installed
* MidiDevice
* @see #getSynthesizer
* @see #getReceiver
* @since 1.5
*/
public static Sequencer getSequencer(boolean connected)
throws MidiUnavailableException {
Sequencer seq = (Sequencer) getDefaultDeviceWrapper(Sequencer.class);
if (connected) {
// IMPORTANT: this code needs to be synch'ed with
// all AutoConnectSequencer instances,
// (e.g. RealTimeSequencer) because the
// same algorithm for synth retrieval
// needs to be used!
Receiver rec = null;
MidiUnavailableException mue = null;
// first try to connect to the default synthesizer
try {
Synthesizer synth = getSynthesizer();
if (synth instanceof ReferenceCountingDevice) {
rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting();
} else {
synth.open();
try {
rec = synth.getReceiver();
} finally {
// make sure that the synth is properly closed
if (rec == null) {
synth.close();
}
}
}
} catch (MidiUnavailableException e) {
// something went wrong with synth
if (e instanceof MidiUnavailableException) {
mue = e;
}
}
if (rec == null) {
// then try to connect to the default Receiver
try {
rec = MidiSystem.getReceiver();
} catch (Exception e) {
// something went wrong. Nothing to do then!
if (e instanceof MidiUnavailableException) {
mue = (MidiUnavailableException) e;
}
}
}
if (rec != null) {
seq.getTransmitter().setReceiver(rec);
if (seq instanceof AutoConnectSequencer) {
((AutoConnectSequencer) seq).setAutoConnect(rec);
}
} else {
if (mue != null) {
throw mue;
}
throw new MidiUnavailableException("no receiver available");
}
}
return seq;
}
/**
* Constructs a MIDI sound bank by reading it from the specified stream.
* The stream must point to
* a valid MIDI soundbank file. In general, MIDI soundbank providers may
* need to read some data from the stream before determining whether they
* support it. These parsers must
* be able to mark the stream, read enough data to determine whether they
* support the stream, and, if not, reset the stream's read pointer to
* its original position. If the input stream does not support this,
* this method may fail with an IOException.
* @param stream the source of the sound bank data.
* @return the sound bank
* @throws InvalidMidiDataException if the stream does not point to
* valid MIDI soundbank data recognized by the system
* @throws IOException if an I/O error occurred when loading the soundbank
* @see InputStream#markSupported
* @see InputStream#mark
*/
public static Soundbank getSoundbank(InputStream stream)
throws InvalidMidiDataException, IOException {
SoundbankReader sp = null;
Soundbank s = null;
ListSoundbank
by reading it from the specified URL.
* The URL must point to a valid MIDI soundbank file.
*
* @param url the source of the sound bank data
* @return the sound bank
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* soundbank data recognized by the system
* @throws IOException if an I/O error occurred when loading the soundbank
*/
public static Soundbank getSoundbank(URL url)
throws InvalidMidiDataException, IOException {
SoundbankReader sp = null;
Soundbank s = null;
ListSoundbank
by reading it from the specified
* File
.
* The File
must point to a valid MIDI soundbank file.
*
* @param file the source of the sound bank data
* @return the sound bank
* @throws InvalidMidiDataException if the File
does not
* point to valid MIDI soundbank data recognized by the system
* @throws IOException if an I/O error occurred when loading the soundbank
*/
public static Soundbank getSoundbank(File file)
throws InvalidMidiDataException, IOException {
SoundbankReader sp = null;
Soundbank s = null;
ListIOException
.
* MidiFileFormat
object describing the MIDI file
* format
* @throws InvalidMidiDataException if the stream does not point to valid
* MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the
* stream
* @see #getMidiFileFormat(URL)
* @see #getMidiFileFormat(File)
* @see InputStream#markSupported
* @see InputStream#mark
*/
public static MidiFileFormat getMidiFileFormat(InputStream stream)
throws InvalidMidiDataException, IOException {
ListMidiFileFormat
object describing the MIDI file
* format
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the URL
*
* @see #getMidiFileFormat(InputStream)
* @see #getMidiFileFormat(File)
*/
public static MidiFileFormat getMidiFileFormat(URL url)
throws InvalidMidiDataException, IOException {
ListFile
. The
* File
must point to valid MIDI file data for a file type
* recognized by the system.
* File
from which file format information
* should be extracted
* @return a MidiFileFormat
object describing the MIDI file
* format
* @throws InvalidMidiDataException if the File
does not point
* to valid MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the file
*
* @see #getMidiFileFormat(InputStream)
* @see #getMidiFileFormat(URL)
*/
public static MidiFileFormat getMidiFileFormat(File file)
throws InvalidMidiDataException, IOException {
ListIOException
.
* Sequence
* object from the file data.
*
* @param stream the input stream from which the Sequence
* should be constructed
* @return a Sequence
object based on the MIDI file data
* contained in the input stream
* @throws InvalidMidiDataException if the stream does not point to
* valid MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the
* stream
* @see InputStream#markSupported
* @see InputStream#mark
*/
public static Sequence getSequence(InputStream stream)
throws InvalidMidiDataException, IOException {
ListSequence
* object from the file data.
*
* @param url the URL from which the Sequence
should be
* constructed
* @return a Sequence
object based on the MIDI file data
* pointed to by the URL
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the URL
*/
public static Sequence getSequence(URL url)
throws InvalidMidiDataException, IOException {
ListFile
.
* The File
must point to valid MIDI file data
* for a file type recognized by the system.
* Sequence
* object from the file data.
*
* @param file the File
from which the Sequence
* should be constructed
* @return a Sequence
object based on the MIDI file data
* pointed to by the File
* @throws InvalidMidiDataException if the File does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs
*/
public static Sequence getSequence(File file)
throws InvalidMidiDataException, IOException {
Listtrue
if the file type is supported,
* otherwise false
*/
public static boolean isFileTypeSupported(int fileType) {
Listtrue
if the file type is supported for this
* sequence, otherwise false
*/
public static boolean isFileTypeSupported(int fileType, Sequence sequence) {
ListIllegalArgumentException
thrown by
* getDefaultDevice
and instead throws a
* MidiUnavailableException
, with the catched
* exception chained.
*
* @param deviceClass The requested device type, one of Synthesizer.class,
* Sequencer.class, Receiver.class or Transmitter.class.
* @throws MidiUnavalableException on failure.
*/
private static MidiDevice getDefaultDeviceWrapper(Class> deviceClass)
throws MidiUnavailableException{
try {
return getDefaultDevice(deviceClass);
} catch (IllegalArgumentException iae) {
MidiUnavailableException mae = new MidiUnavailableException();
mae.initCause(iae);
throw mae;
}
}
/** Attempts to locate and return a default MidiDevice of the specified
* type.
*
* @param deviceClass The requested device type, one of Synthesizer.class,
* Sequencer.class, Receiver.class or Transmitter.class.
* @throws IllegalArgumentException on failure.
*/
private static MidiDevice getDefaultDevice(Class> deviceClass) {
List