mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 17:44:40 +02:00
1513 lines
64 KiB
Java
1513 lines
64 KiB
Java
/*
|
|
* Copyright (c) 1999, 2018, 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.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.Properties;
|
|
import java.util.Set;
|
|
|
|
import javax.sound.midi.spi.MidiDeviceProvider;
|
|
import javax.sound.midi.spi.MidiFileReader;
|
|
import javax.sound.midi.spi.MidiFileWriter;
|
|
import javax.sound.midi.spi.SoundbankReader;
|
|
|
|
import com.sun.media.sound.AutoConnectSequencer;
|
|
import com.sun.media.sound.JDK13Services;
|
|
import com.sun.media.sound.MidiDeviceReceiverEnvelope;
|
|
import com.sun.media.sound.MidiDeviceTransmitterEnvelope;
|
|
import com.sun.media.sound.ReferenceCountingDevice;
|
|
|
|
/**
|
|
* The {@code 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 {@code MidiSystem} methods to learn what devices are installed
|
|
* and to obtain the ones needed in that application.
|
|
* <p>
|
|
* The class also has methods for reading files, streams, and URLs that contain
|
|
* standard MIDI file data or soundbanks. You can query the {@code MidiSystem}
|
|
* for the format of a specified MIDI file.
|
|
* <p>
|
|
* You cannot instantiate a {@code MidiSystem}; all the methods are static.
|
|
* <p>
|
|
* 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 {@code conf} directory in the Java installation directory).
|
|
* The optional "javax.sound.config.file" system property can be used to specify
|
|
* the properties file that will be read as the initial configuration. 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 Properties#load(InputStream) Properties.load}. The
|
|
* following table lists the available property keys and which methods consider
|
|
* them:
|
|
*
|
|
* <table class="striped">
|
|
* <caption>MIDI System Property Keys</caption>
|
|
* <thead>
|
|
* <tr>
|
|
* <th scope="col">Property Key
|
|
* <th scope="col">Interface
|
|
* <th scope="col">Affected Method
|
|
* </thead>
|
|
* <tbody>
|
|
* <tr>
|
|
* <th scope="row">{@code javax.sound.midi.Receiver}
|
|
* <td>{@link Receiver}
|
|
* <td>{@link #getReceiver}
|
|
* <tr>
|
|
* <th scope="row">{@code javax.sound.midi.Sequencer}
|
|
* <td>{@link Sequencer}
|
|
* <td>{@link #getSequencer}
|
|
* <tr>
|
|
* <th scope="row">{@code javax.sound.midi.Synthesizer}
|
|
* <td>{@link Synthesizer}
|
|
* <td>{@link #getSynthesizer}
|
|
* <tr>
|
|
* <th scope="row">{@code javax.sound.midi.Transmitter}
|
|
* <td>{@link Transmitter}
|
|
* <td>{@link #getTransmitter}
|
|
* </tbody>
|
|
* </table>
|
|
*
|
|
* The property value consists of the provider class name and the device name,
|
|
* separated by the hash mark ("#"). The provider class name is the
|
|
* fully-qualified name of a concrete
|
|
* {@link MidiDeviceProvider MIDI device provider} class. The device name is
|
|
* matched against the {@code String} returned by the {@code getName} method of
|
|
* {@code 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.
|
|
* <p>
|
|
* If the provider class is specified, and it can be successfully retrieved from
|
|
* the installed providers, the list of {@code 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 {@code MidiDevice.Info} objects.
|
|
* <p>
|
|
* If a device name is specified, the resulting list of {@code MidiDevice.Info}
|
|
* objects is searched: the first one with a matching name, and whose
|
|
* {@code MidiDevice} implements the respective interface, will be returned. If
|
|
* no matching {@code 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.
|
|
* <p>
|
|
* For example, the property {@code javax.sound.midi.Receiver} with a value
|
|
* {@code "com.sun.media.sound.MidiProvider#SunMIDI1"} will have the following
|
|
* consequences when {@code getReceiver} is called: if the class
|
|
* {@code com.sun.media.sound.MidiProvider} exists in the list of installed MIDI
|
|
* device providers, the first {@code Receiver} device with name
|
|
* {@code "SunMIDI1"} will be returned. If it cannot be found, the first
|
|
* {@code Receiver} from that provider will be returned, regardless of name. If
|
|
* there is none, the first {@code Receiver} with name {@code "SunMIDI1"} in the
|
|
* list of all devices (as returned by {@code getMidiDeviceInfo}) will be
|
|
* returned, or, if not found, the first {@code Receiver} that can be found in
|
|
* the list of all devices is returned. If that fails, too, a
|
|
* {@code 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 {@code 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() {
|
|
final List<MidiDevice.Info> allInfos = new ArrayList<>();
|
|
for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {
|
|
Collections.addAll(allInfos, provider.getDeviceInfo());
|
|
}
|
|
return allInfos.toArray(new MidiDevice.Info[allInfos.size()]);
|
|
}
|
|
|
|
/**
|
|
* Obtains the requested MIDI device.
|
|
*
|
|
* @param info a device information object representing the desired device
|
|
* @return the requested device
|
|
* @throws MidiUnavailableException if the requested device is not available
|
|
* due to resource restrictions
|
|
* @throws IllegalArgumentException if the info object does not represent a
|
|
* MIDI device installed on the system
|
|
* @throws NullPointerException if {@code info} is {@code null}
|
|
* @see #getMidiDeviceInfo
|
|
*/
|
|
public static MidiDevice getMidiDevice(final MidiDevice.Info info)
|
|
throws MidiUnavailableException {
|
|
Objects.requireNonNull(info);
|
|
for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {
|
|
if (provider.isDeviceSupported(info)) {
|
|
return provider.getDevice(info);
|
|
}
|
|
}
|
|
throw new IllegalArgumentException(String.format(
|
|
"Requested device not installed: %s", info));
|
|
}
|
|
|
|
/**
|
|
* Obtains a MIDI receiver from an external MIDI port or other default
|
|
* device. The returned receiver always implements the
|
|
* {@code MidiDeviceReceiver} interface.
|
|
* <p>
|
|
* If the system property {@code javax.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}.
|
|
* <p>
|
|
* If a suitable MIDI port is not available, the Receiver is retrieved from
|
|
* an installed synthesizer.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* If this method returns successfully, the {@link MidiDevice MidiDevice}
|
|
* the {@code Receiver} belongs to is opened implicitly, if it is not
|
|
* already open. It is possible to close an implicitly opened device by
|
|
* calling {@link Receiver#close close} on the returned {@code Receiver}.
|
|
* All open {@code Receiver} instances have to be closed in order to release
|
|
* system resources hold by the {@code MidiDevice}. For a detailed
|
|
* description of open/close behaviour see the class description of
|
|
* {@link 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.
|
|
* <p>
|
|
* If the system property {@code 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}.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* If this method returns successfully, the {@link MidiDevice MidiDevice}
|
|
* the {@code Transmitter} belongs to is opened implicitly, if it is not
|
|
* already open. It is possible to close an implicitly opened device by
|
|
* calling {@link Transmitter#close close} on the returned
|
|
* {@code Transmitter}. All open {@code Transmitter} instances have to be
|
|
* closed in order to release system resources hold by the
|
|
* {@code MidiDevice}. For a detailed description of open/close behaviour
|
|
* see the class description of {@link 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.
|
|
* <p>
|
|
* If the system property {@code 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 {@code Sequencer}, connected to a default device. The
|
|
* returned {@code Sequencer} instance is connected to the default
|
|
* {@code Synthesizer}, as returned by {@link #getSynthesizer}. If there is
|
|
* no {@code Synthesizer} available, or the default {@code Synthesizer}
|
|
* cannot be opened, the {@code sequencer} is connected to the default
|
|
* {@code Receiver}, as returned by {@link #getReceiver}. The connection is
|
|
* made by retrieving a {@code Transmitter} instance from the
|
|
* {@code Sequencer} and setting its {@code Receiver}. Closing and
|
|
* re-opening the sequencer will restore the connection to the default
|
|
* device.
|
|
* <p>
|
|
* This method is equivalent to calling {@code getSequencer(true)}.
|
|
* <p>
|
|
* If the system property {@code 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 {@code Receiver} available
|
|
* by any installed {@code 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 {@code Sequencer}, optionally connected to a default
|
|
* device.
|
|
* <p>
|
|
* If {@code connected} is true, the returned {@code Sequencer} instance is
|
|
* connected to the default {@code Synthesizer}, as returned by
|
|
* {@link #getSynthesizer}. If there is no {@code Synthesizer} available, or
|
|
* the default {@code Synthesizer} cannot be opened, the {@code sequencer}
|
|
* is connected to the default {@code Receiver}, as returned by
|
|
* {@link #getReceiver}. The connection is made by retrieving a
|
|
* {@code Transmitter} instance from the {@code Sequencer} and setting its
|
|
* {@code Receiver}. Closing and re-opening the sequencer will restore the
|
|
* connection to the default device.
|
|
* <p>
|
|
* If {@code connected} is false, the returned {@code Sequencer} instance is
|
|
* not connected, it has no open {@code Transmitters}. In order to play the
|
|
* sequencer on a MIDI device, or a {@code Synthesizer}, it is necessary to
|
|
* get a {@code Transmitter} and set its {@code Receiver}.
|
|
* <p>
|
|
* If the system property {@code 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 {@code connected} is true, and there is no
|
|
* {@code Receiver} available by any installed {@code 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 {@code 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
|
|
* @throws NullPointerException if {@code stream} is {@code null}
|
|
* @see InputStream#markSupported
|
|
* @see InputStream#mark
|
|
*/
|
|
public static Soundbank getSoundbank(final InputStream stream)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(stream);
|
|
|
|
SoundbankReader sp = null;
|
|
Soundbank s = null;
|
|
|
|
List<SoundbankReader> providers = getSoundbankReaders();
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
sp = providers.get(i);
|
|
s = sp.getSoundbank(stream);
|
|
|
|
if( s!= null) {
|
|
return s;
|
|
}
|
|
}
|
|
throw new InvalidMidiDataException("cannot get soundbank from stream");
|
|
|
|
}
|
|
|
|
/**
|
|
* Constructs a {@code Soundbank} 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
|
|
* @throws NullPointerException if {@code url} is {@code null}
|
|
*/
|
|
public static Soundbank getSoundbank(final URL url)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(url);
|
|
|
|
SoundbankReader sp = null;
|
|
Soundbank s = null;
|
|
|
|
List<SoundbankReader> providers = getSoundbankReaders();
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
sp = providers.get(i);
|
|
s = sp.getSoundbank(url);
|
|
|
|
if( s!= null) {
|
|
return s;
|
|
}
|
|
}
|
|
throw new InvalidMidiDataException("cannot get soundbank from stream");
|
|
|
|
}
|
|
|
|
/**
|
|
* Constructs a {@code Soundbank} by reading it from the specified
|
|
* {@code File}. The {@code 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 {@code 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
|
|
* @throws NullPointerException if {@code file} is {@code null}
|
|
*/
|
|
public static Soundbank getSoundbank(final File file)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(file);
|
|
|
|
SoundbankReader sp = null;
|
|
Soundbank s = null;
|
|
|
|
List<SoundbankReader> providers = getSoundbankReaders();
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
sp = providers.get(i);
|
|
s = sp.getSoundbank(file);
|
|
|
|
if( s!= null) {
|
|
return s;
|
|
}
|
|
}
|
|
throw new InvalidMidiDataException("cannot get soundbank from stream");
|
|
}
|
|
|
|
/**
|
|
* Obtains the MIDI file format of the data in the specified input stream.
|
|
* The stream must point to valid MIDI file data for a file type recognized
|
|
* by the system.
|
|
* <p>
|
|
* 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
|
|
* {@code IOException}.
|
|
* <p>
|
|
* This operation can only succeed for files of a type which can be parsed
|
|
* by an installed file reader. It may fail with an
|
|
* {@code InvalidMidiDataException} even for valid files if no compatible
|
|
* file reader is installed. It will also fail with an
|
|
* {@code 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 {@code 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
|
|
* @throws NullPointerException if {@code stream} is {@code null}
|
|
* @see #getMidiFileFormat(URL)
|
|
* @see #getMidiFileFormat(File)
|
|
* @see InputStream#markSupported
|
|
* @see InputStream#mark
|
|
*/
|
|
public static MidiFileFormat getMidiFileFormat(final InputStream stream)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(stream);
|
|
|
|
List<MidiFileReader> providers = getMidiFileReaders();
|
|
MidiFileFormat format = null;
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiFileReader reader = providers.get(i);
|
|
try {
|
|
format = reader.getMidiFileFormat( stream ); // throws IOException
|
|
break;
|
|
} catch (InvalidMidiDataException e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( format==null ) {
|
|
throw new InvalidMidiDataException("input stream is not a supported file type");
|
|
} else {
|
|
return format;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtains the MIDI file format of the data in the specified URL. The URL
|
|
* must point to valid MIDI file data for a file type recognized by the
|
|
* system.
|
|
* <p>
|
|
* This operation can only succeed for files of a type which can be parsed
|
|
* by an installed file reader. It may fail with an
|
|
* {@code InvalidMidiDataException} even for valid files if no compatible
|
|
* file reader is installed. It will also fail with an
|
|
* {@code 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 {@code MidiFileFormat} 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
|
|
* @throws NullPointerException if {@code url} is {@code null}
|
|
* @see #getMidiFileFormat(InputStream)
|
|
* @see #getMidiFileFormat(File)
|
|
*/
|
|
public static MidiFileFormat getMidiFileFormat(final URL url)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(url);
|
|
|
|
List<MidiFileReader> providers = getMidiFileReaders();
|
|
MidiFileFormat format = null;
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiFileReader reader = providers.get(i);
|
|
try {
|
|
format = reader.getMidiFileFormat( url ); // throws IOException
|
|
break;
|
|
} catch (InvalidMidiDataException e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( format==null ) {
|
|
throw new InvalidMidiDataException("url is not a supported file type");
|
|
} else {
|
|
return format;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtains the MIDI file format of the specified {@code File}. The
|
|
* {@code File} must point to valid MIDI file data for a file type
|
|
* recognized by the system.
|
|
* <p>
|
|
* This operation can only succeed for files of a type which can be parsed
|
|
* by an installed file reader. It may fail with an
|
|
* {@code InvalidMidiDataException} even for valid files if no compatible
|
|
* file reader is installed. It will also fail with an
|
|
* {@code InvalidMidiDataException} if a compatible file reader is
|
|
* installed, but encounters errors while determining the file format.
|
|
*
|
|
* @param file the {@code File} from which file format information should
|
|
* be extracted
|
|
* @return a {@code MidiFileFormat} object describing the MIDI file format
|
|
* @throws InvalidMidiDataException if the {@code 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
|
|
* @throws NullPointerException if {@code file} is {@code null}
|
|
* @see #getMidiFileFormat(InputStream)
|
|
* @see #getMidiFileFormat(URL)
|
|
*/
|
|
public static MidiFileFormat getMidiFileFormat(final File file)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(file);
|
|
|
|
List<MidiFileReader> providers = getMidiFileReaders();
|
|
MidiFileFormat format = null;
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiFileReader reader = providers.get(i);
|
|
try {
|
|
format = reader.getMidiFileFormat( file ); // throws IOException
|
|
break;
|
|
} catch (InvalidMidiDataException e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( format==null ) {
|
|
throw new InvalidMidiDataException("file is not a supported file type");
|
|
} else {
|
|
return format;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtains a MIDI sequence from the specified input stream. The stream must
|
|
* point to valid MIDI file data for a file type recognized by the system.
|
|
* <p>
|
|
* 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
|
|
* {@code IOException}.
|
|
* <p>
|
|
* This operation can only succeed for files of a type which can be parsed
|
|
* by an installed file reader. It may fail with an
|
|
* {@code InvalidMidiDataException} even for valid files if no compatible
|
|
* file reader is installed. It will also fail with an
|
|
* {@code InvalidMidiDataException} if a compatible file reader is
|
|
* installed, but encounters errors while constructing the {@code Sequence}
|
|
* object from the file data.
|
|
*
|
|
* @param stream the input stream from which the {@code Sequence} should be
|
|
* constructed
|
|
* @return a {@code 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
|
|
* @throws NullPointerException if {@code stream} is {@code null}
|
|
* @see InputStream#markSupported
|
|
* @see InputStream#mark
|
|
*/
|
|
public static Sequence getSequence(final InputStream stream)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(stream);
|
|
|
|
List<MidiFileReader> providers = getMidiFileReaders();
|
|
Sequence sequence = null;
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiFileReader reader = providers.get(i);
|
|
try {
|
|
sequence = reader.getSequence( stream ); // throws IOException
|
|
break;
|
|
} catch (InvalidMidiDataException e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( sequence==null ) {
|
|
throw new InvalidMidiDataException("could not get sequence from input stream");
|
|
} else {
|
|
return sequence;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtains a MIDI sequence from the specified URL. The URL must point to
|
|
* valid MIDI file data for a file type recognized by the system.
|
|
* <p>
|
|
* This operation can only succeed for files of a type which can be parsed
|
|
* by an installed file reader. It may fail with an
|
|
* {@code InvalidMidiDataException} even for valid files if no compatible
|
|
* file reader is installed. It will also fail with an
|
|
* {@code InvalidMidiDataException} if a compatible file reader is
|
|
* installed, but encounters errors while constructing the {@code Sequence}
|
|
* object from the file data.
|
|
*
|
|
* @param url the URL from which the {@code Sequence} should be constructed
|
|
* @return a {@code 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
|
|
* @throws NullPointerException if {@code url} is {@code null}
|
|
*/
|
|
public static Sequence getSequence(final URL url)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(url);
|
|
|
|
List<MidiFileReader> providers = getMidiFileReaders();
|
|
Sequence sequence = null;
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiFileReader reader = providers.get(i);
|
|
try {
|
|
sequence = reader.getSequence( url ); // throws IOException
|
|
break;
|
|
} catch (InvalidMidiDataException e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( sequence==null ) {
|
|
throw new InvalidMidiDataException("could not get sequence from URL");
|
|
} else {
|
|
return sequence;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtains a MIDI sequence from the specified {@code File}. The {@code File}
|
|
* must point to valid MIDI file data for a file type recognized by the
|
|
* system.
|
|
* <p>
|
|
* This operation can only succeed for files of a type which can be parsed
|
|
* by an installed file reader. It may fail with an
|
|
* {@code InvalidMidiDataException} even for valid files if no compatible
|
|
* file reader is installed. It will also fail with an
|
|
* {@code InvalidMidiDataException} if a compatible file reader is
|
|
* installed, but encounters errors while constructing the {@code Sequence}
|
|
* object from the file data.
|
|
*
|
|
* @param file the {@code File} from which the {@code Sequence} should be
|
|
* constructed
|
|
* @return a {@code 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
|
|
* @throws NullPointerException if {@code file} is {@code null}
|
|
*/
|
|
public static Sequence getSequence(final File file)
|
|
throws InvalidMidiDataException, IOException {
|
|
Objects.requireNonNull(file);
|
|
|
|
List<MidiFileReader> providers = getMidiFileReaders();
|
|
Sequence sequence = null;
|
|
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiFileReader reader = providers.get(i);
|
|
try {
|
|
sequence = reader.getSequence( file ); // throws IOException
|
|
break;
|
|
} catch (InvalidMidiDataException e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( sequence==null ) {
|
|
throw new InvalidMidiDataException("could not get sequence from file");
|
|
} else {
|
|
return sequence;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtains the set of MIDI file types for which file writing support is
|
|
* provided by the system.
|
|
*
|
|
* @return array of unique file types. If no file types are supported, an
|
|
* array of length 0 is returned.
|
|
*/
|
|
public static int[] getMidiFileTypes() {
|
|
|
|
List<MidiFileWriter> providers = getMidiFileWriters();
|
|
Set<Integer> allTypes = new HashSet<>();
|
|
|
|
// gather from all the providers
|
|
|
|
for (int i = 0; i < providers.size(); i++ ) {
|
|
MidiFileWriter writer = providers.get(i);
|
|
int[] types = writer.getMidiFileTypes();
|
|
for (int j = 0; j < types.length; j++ ) {
|
|
allTypes.add(types[j]);
|
|
}
|
|
}
|
|
int resultTypes[] = new int[allTypes.size()];
|
|
int index = 0;
|
|
Iterator<Integer> iterator = allTypes.iterator();
|
|
while (iterator.hasNext()) {
|
|
Integer integer = iterator.next();
|
|
resultTypes[index++] = integer.intValue();
|
|
}
|
|
return resultTypes;
|
|
}
|
|
|
|
/**
|
|
* Indicates whether file writing support for the specified MIDI file type
|
|
* is provided by the system.
|
|
*
|
|
* @param fileType the file type for which write capabilities are queried
|
|
* @return {@code true} if the file type is supported, otherwise
|
|
* {@code false}
|
|
*/
|
|
public static boolean isFileTypeSupported(int fileType) {
|
|
|
|
List<MidiFileWriter> providers = getMidiFileWriters();
|
|
|
|
for (int i = 0; i < providers.size(); i++ ) {
|
|
MidiFileWriter writer = providers.get(i);
|
|
if( writer.isFileTypeSupported(fileType)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Obtains the set of MIDI file types that the system can write from the
|
|
* sequence specified.
|
|
*
|
|
* @param sequence the sequence for which MIDI file type support is queried
|
|
* @return the set of unique supported file types. If no file types are
|
|
* supported, returns an array of length 0.
|
|
* @throws NullPointerException if {@code sequence} is {@code null}
|
|
*/
|
|
public static int[] getMidiFileTypes(final Sequence sequence) {
|
|
Objects.requireNonNull(sequence);
|
|
|
|
List<MidiFileWriter> providers = getMidiFileWriters();
|
|
Set<Integer> allTypes = new HashSet<>();
|
|
|
|
// gather from all the providers
|
|
|
|
for (int i = 0; i < providers.size(); i++ ) {
|
|
MidiFileWriter writer = providers.get(i);
|
|
int[] types = writer.getMidiFileTypes(sequence);
|
|
for (int j = 0; j < types.length; j++ ) {
|
|
allTypes.add(types[j]);
|
|
}
|
|
}
|
|
int resultTypes[] = new int[allTypes.size()];
|
|
int index = 0;
|
|
Iterator<Integer> iterator = allTypes.iterator();
|
|
while (iterator.hasNext()) {
|
|
Integer integer = iterator.next();
|
|
resultTypes[index++] = integer.intValue();
|
|
}
|
|
return resultTypes;
|
|
}
|
|
|
|
/**
|
|
* Indicates whether a MIDI file of the file type specified can be written
|
|
* from the sequence indicated.
|
|
*
|
|
* @param fileType the file type for which write capabilities are queried
|
|
* @param sequence the sequence for which file writing support is queried
|
|
* @return {@code true} if the file type is supported for this sequence,
|
|
* otherwise {@code false}
|
|
* @throws NullPointerException if {@code sequence} is {@code null}
|
|
*/
|
|
public static boolean isFileTypeSupported(final int fileType,
|
|
final Sequence sequence) {
|
|
Objects.requireNonNull(sequence);
|
|
|
|
List<MidiFileWriter> providers = getMidiFileWriters();
|
|
|
|
for (int i = 0; i < providers.size(); i++ ) {
|
|
MidiFileWriter writer = providers.get(i);
|
|
if( writer.isFileTypeSupported(fileType,sequence)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Writes a stream of bytes representing a file of the MIDI file type
|
|
* indicated to the output stream provided.
|
|
*
|
|
* @param in sequence containing MIDI data to be written to the file
|
|
* @param fileType the file type of the file to be written to the output
|
|
* stream
|
|
* @param out stream to which the file data should be written
|
|
* @return the number of bytes written to the output stream
|
|
* @throws IOException if an I/O exception occurs
|
|
* @throws IllegalArgumentException if the file format is not supported by
|
|
* the system
|
|
* @throws NullPointerException if {@code in} or {@code out} are
|
|
* {@code null}
|
|
* @see #isFileTypeSupported(int, Sequence)
|
|
* @see #getMidiFileTypes(Sequence)
|
|
*/
|
|
public static int write(final Sequence in, final int fileType,
|
|
final OutputStream out) throws IOException {
|
|
Objects.requireNonNull(in);
|
|
Objects.requireNonNull(out);
|
|
|
|
List<MidiFileWriter> providers = getMidiFileWriters();
|
|
//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
|
|
int bytesWritten = -2;
|
|
|
|
for (int i = 0; i < providers.size(); i++ ) {
|
|
MidiFileWriter writer = providers.get(i);
|
|
if( writer.isFileTypeSupported( fileType, in ) ) {
|
|
|
|
bytesWritten = writer.write(in, fileType, out);
|
|
break;
|
|
}
|
|
}
|
|
if (bytesWritten == -2) {
|
|
throw new IllegalArgumentException("MIDI file type is not supported");
|
|
}
|
|
return bytesWritten;
|
|
}
|
|
|
|
/**
|
|
* Writes a stream of bytes representing a file of the MIDI file type
|
|
* indicated to the external file provided.
|
|
*
|
|
* @param in sequence containing MIDI data to be written to the file
|
|
* @param type the file type of the file to be written to the output stream
|
|
* @param out external file to which the file data should be written
|
|
* @return the number of bytes written to the file
|
|
* @throws IOException if an I/O exception occurs
|
|
* @throws IllegalArgumentException if the file type is not supported by the
|
|
* system
|
|
* @throws NullPointerException if {@code in} or {@code out} are
|
|
* {@code null}
|
|
* @see #isFileTypeSupported(int, Sequence)
|
|
* @see #getMidiFileTypes(Sequence)
|
|
*/
|
|
public static int write(final Sequence in, final int type, final File out)
|
|
throws IOException {
|
|
Objects.requireNonNull(in);
|
|
Objects.requireNonNull(out);
|
|
|
|
List<MidiFileWriter> providers = getMidiFileWriters();
|
|
//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
|
|
int bytesWritten = -2;
|
|
|
|
for (int i = 0; i < providers.size(); i++ ) {
|
|
MidiFileWriter writer = providers.get(i);
|
|
if( writer.isFileTypeSupported( type, in ) ) {
|
|
|
|
bytesWritten = writer.write(in, type, out);
|
|
break;
|
|
}
|
|
}
|
|
if (bytesWritten == -2) {
|
|
throw new IllegalArgumentException("MIDI file type is not supported");
|
|
}
|
|
return bytesWritten;
|
|
}
|
|
|
|
// HELPER METHODS
|
|
|
|
/**
|
|
* Obtains the list of MidiDeviceProviders installed on the system.
|
|
*
|
|
* @return the list of MidiDeviceProviders installed on the system
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private static List<MidiDeviceProvider> getMidiDeviceProviders() {
|
|
return (List<MidiDeviceProvider>) getProviders(MidiDeviceProvider.class);
|
|
}
|
|
|
|
/**
|
|
* Obtains the list of SoundbankReaders installed on the system.
|
|
*
|
|
* @return the list of SoundbankReaders installed on the system
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private static List<SoundbankReader> getSoundbankReaders() {
|
|
return (List<SoundbankReader>) getProviders(SoundbankReader.class);
|
|
}
|
|
|
|
/**
|
|
* Obtains the list of MidiFileWriters installed on the system.
|
|
*
|
|
* @return the list of MidiFileWriters installed on the system
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private static List<MidiFileWriter> getMidiFileWriters() {
|
|
return (List<MidiFileWriter>) getProviders(MidiFileWriter.class);
|
|
}
|
|
|
|
/**
|
|
* Obtains the list of MidiFileReaders installed on the system.
|
|
*
|
|
* @return the list of MidiFileReaders installed on the system
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private static List<MidiFileReader> getMidiFileReaders() {
|
|
return (List<MidiFileReader>) getProviders(MidiFileReader.class);
|
|
}
|
|
|
|
/**
|
|
* Attempts to locate and return a default MidiDevice of the specified type.
|
|
* This method wraps {@link #getDefaultDevice}. It catches the
|
|
* {@code IllegalArgumentException} thrown by {@code getDefaultDevice} and
|
|
* instead throws a {@code MidiUnavailableException}, with the catched
|
|
* exception chained.
|
|
*
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @return default MidiDevice of the specified type
|
|
* @throws MidiUnavailableException 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
|
|
* @return default MidiDevice of the specified type.
|
|
* @throws IllegalArgumentException on failure
|
|
*/
|
|
private static MidiDevice getDefaultDevice(Class<?> deviceClass) {
|
|
List<MidiDeviceProvider> providers = getMidiDeviceProviders();
|
|
String providerClassName = JDK13Services.getDefaultProviderClassName(deviceClass);
|
|
String instanceName = JDK13Services.getDefaultInstanceName(deviceClass);
|
|
MidiDevice device;
|
|
|
|
if (providerClassName != null) {
|
|
MidiDeviceProvider defaultProvider = getNamedProvider(providerClassName, providers);
|
|
if (defaultProvider != null) {
|
|
if (instanceName != null) {
|
|
device = getNamedDevice(instanceName, defaultProvider, deviceClass);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
device = getFirstDevice(defaultProvider, deviceClass);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* - Provider class not specified or cannot be found, or
|
|
* - provider class specified, and no appropriate device available, or
|
|
* - provider class and instance specified and instance cannot be found
|
|
* or is not appropriate
|
|
*/
|
|
if (instanceName != null) {
|
|
device = getNamedDevice(instanceName, providers, deviceClass);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* No defaults are specified, or if something is specified, everything
|
|
* failed
|
|
*/
|
|
device = getFirstDevice(providers, deviceClass);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
throw new IllegalArgumentException("Requested device not installed");
|
|
}
|
|
|
|
/**
|
|
* Return a MidiDeviceProvider of a given class from the list of
|
|
* MidiDeviceProviders.
|
|
*
|
|
* @param providerClassName The class name of the provider to be returned
|
|
* @param providers The list of MidiDeviceProviders that is searched
|
|
* @return A MidiDeviceProvider of the requested class, or null if none is
|
|
* found
|
|
*/
|
|
private static MidiDeviceProvider getNamedProvider(String providerClassName,
|
|
List<MidiDeviceProvider> providers) {
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiDeviceProvider provider = providers.get(i);
|
|
if (provider.getClass().getName().equals(providerClassName)) {
|
|
return provider;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return a MidiDevice with a given name from a given MidiDeviceProvider.
|
|
*
|
|
* @param deviceName The name of the MidiDevice to be returned
|
|
* @param provider The MidiDeviceProvider to check for MidiDevices
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @return A MidiDevice matching the requirements, or null if none is found
|
|
*/
|
|
private static MidiDevice getNamedDevice(String deviceName,
|
|
MidiDeviceProvider provider,
|
|
Class<?> deviceClass) {
|
|
MidiDevice device;
|
|
// try to get MIDI port
|
|
device = getNamedDevice(deviceName, provider, deviceClass,
|
|
false, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
|
|
if (deviceClass == Receiver.class) {
|
|
// try to get Synthesizer
|
|
device = getNamedDevice(deviceName, provider, deviceClass,
|
|
true, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return a MidiDevice with a given name from a given MidiDeviceProvider.
|
|
*
|
|
* @param deviceName The name of the MidiDevice to be returned
|
|
* @param provider The MidiDeviceProvider to check for MidiDevices
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @param allowSynthesizer if true, Synthesizers are considered
|
|
* appropriate. Otherwise only pure MidiDevices are considered
|
|
* appropriate (unless allowSequencer is true). This flag only has
|
|
* an effect for deviceClass Receiver and Transmitter. For other
|
|
* device classes (Sequencer and Synthesizer), this flag has no
|
|
* effect.
|
|
* @param allowSequencer if true, Sequencers are considered appropriate.
|
|
* Otherwise only pure MidiDevices are considered appropriate
|
|
* (unless allowSynthesizer is true). This flag only has an effect
|
|
* for deviceClass Receiver and Transmitter. For other device
|
|
* classes (Sequencer and Synthesizer), this flag has no effect.
|
|
* @return A MidiDevice matching the requirements, or null if none is found
|
|
*/
|
|
private static MidiDevice getNamedDevice(String deviceName,
|
|
MidiDeviceProvider provider,
|
|
Class<?> deviceClass,
|
|
boolean allowSynthesizer,
|
|
boolean allowSequencer) {
|
|
MidiDevice.Info[] infos = provider.getDeviceInfo();
|
|
for (int i = 0; i < infos.length; i++) {
|
|
if (infos[i].getName().equals(deviceName)) {
|
|
MidiDevice device = provider.getDevice(infos[i]);
|
|
if (isAppropriateDevice(device, deviceClass,
|
|
allowSynthesizer, allowSequencer)) {
|
|
return device;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return a MidiDevice with a given name from a list of MidiDeviceProviders.
|
|
*
|
|
* @param deviceName The name of the MidiDevice to be returned
|
|
* @param providers The List of MidiDeviceProviders to check for
|
|
* MidiDevices
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @return A Mixer matching the requirements, or null if none is found
|
|
*/
|
|
private static MidiDevice getNamedDevice(String deviceName,
|
|
List<MidiDeviceProvider> providers,
|
|
Class<?> deviceClass) {
|
|
MidiDevice device;
|
|
// try to get MIDI port
|
|
device = getNamedDevice(deviceName, providers, deviceClass,
|
|
false, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
|
|
if (deviceClass == Receiver.class) {
|
|
// try to get Synthesizer
|
|
device = getNamedDevice(deviceName, providers, deviceClass,
|
|
true, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return a MidiDevice with a given name from a list of MidiDeviceProviders.
|
|
*
|
|
* @param deviceName The name of the MidiDevice to be returned
|
|
* @param providers The List of MidiDeviceProviders to check for
|
|
* MidiDevices
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @param allowSynthesizer if true, Synthesizers are considered
|
|
* appropriate. Otherwise only pure MidiDevices are considered
|
|
* appropriate (unless allowSequencer is true). This flag only has
|
|
* an effect for deviceClass Receiver and Transmitter. For other
|
|
* device classes (Sequencer and Synthesizer), this flag has no
|
|
* effect.
|
|
* @param allowSequencer if true, Sequencers are considered appropriate.
|
|
* Otherwise only pure MidiDevices are considered appropriate
|
|
* (unless allowSynthesizer is true). This flag only has an effect
|
|
* for deviceClass Receiver and Transmitter. For other device
|
|
* classes (Sequencer and Synthesizer), this flag has no effect.
|
|
* @return A Mixer matching the requirements, or null if none is found
|
|
*/
|
|
private static MidiDevice getNamedDevice(String deviceName,
|
|
List<MidiDeviceProvider> providers,
|
|
Class<?> deviceClass,
|
|
boolean allowSynthesizer,
|
|
boolean allowSequencer) {
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiDeviceProvider provider = providers.get(i);
|
|
MidiDevice device = getNamedDevice(deviceName, provider,
|
|
deviceClass,
|
|
allowSynthesizer,
|
|
allowSequencer);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* From a given MidiDeviceProvider, return the first appropriate device.
|
|
*
|
|
* @param provider The MidiDeviceProvider to check for MidiDevices
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @return A MidiDevice is considered appropriate, or null if no appropriate
|
|
* device is found
|
|
*/
|
|
private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
|
|
Class<?> deviceClass) {
|
|
MidiDevice device;
|
|
// try to get MIDI port
|
|
device = getFirstDevice(provider, deviceClass,
|
|
false, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
|
|
if (deviceClass == Receiver.class) {
|
|
// try to get Synthesizer
|
|
device = getFirstDevice(provider, deviceClass,
|
|
true, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* From a given MidiDeviceProvider, return the first appropriate device.
|
|
*
|
|
* @param provider The MidiDeviceProvider to check for MidiDevices
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @param allowSynthesizer if true, Synthesizers are considered
|
|
* appropriate. Otherwise only pure MidiDevices are considered
|
|
* appropriate (unless allowSequencer is true). This flag only has
|
|
* an effect for deviceClass Receiver and Transmitter. For other
|
|
* device classes (Sequencer and Synthesizer), this flag has no
|
|
* effect.
|
|
* @param allowSequencer if true, Sequencers are considered appropriate.
|
|
* Otherwise only pure MidiDevices are considered appropriate
|
|
* (unless allowSynthesizer is true). This flag only has an effect
|
|
* for deviceClass Receiver and Transmitter. For other device
|
|
* classes (Sequencer and Synthesizer), this flag has no effect.
|
|
* @return A MidiDevice is considered appropriate, or null if no appropriate
|
|
* device is found
|
|
*/
|
|
private static MidiDevice getFirstDevice(MidiDeviceProvider provider,
|
|
Class<?> deviceClass,
|
|
boolean allowSynthesizer,
|
|
boolean allowSequencer) {
|
|
MidiDevice.Info[] infos = provider.getDeviceInfo();
|
|
for (int j = 0; j < infos.length; j++) {
|
|
MidiDevice device = provider.getDevice(infos[j]);
|
|
if (isAppropriateDevice(device, deviceClass,
|
|
allowSynthesizer, allowSequencer)) {
|
|
return device;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* From a List of MidiDeviceProviders, return the first appropriate
|
|
* MidiDevice.
|
|
*
|
|
* @param providers The List of MidiDeviceProviders to search
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @return A MidiDevice that is considered appropriate, or null if none is
|
|
* found
|
|
*/
|
|
private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,
|
|
Class<?> deviceClass) {
|
|
MidiDevice device;
|
|
// try to get MIDI port
|
|
device = getFirstDevice(providers, deviceClass,
|
|
false, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
|
|
if (deviceClass == Receiver.class) {
|
|
// try to get Synthesizer
|
|
device = getFirstDevice(providers, deviceClass,
|
|
true, false);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* From a List of MidiDeviceProviders, return the first appropriate
|
|
* MidiDevice.
|
|
*
|
|
* @param providers The List of MidiDeviceProviders to search
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @param allowSynthesizer if true, Synthesizers are considered
|
|
* appropriate. Otherwise only pure MidiDevices are considered
|
|
* appropriate (unless allowSequencer is true). This flag only has
|
|
* an effect for deviceClass Receiver and Transmitter. For other
|
|
* device classes (Sequencer and Synthesizer), this flag has no
|
|
* effect.
|
|
* @param allowSequencer if true, Sequencers are considered appropriate.
|
|
* Otherwise only pure MidiDevices are considered appropriate
|
|
* (unless allowSynthesizer is true). This flag only has an effect
|
|
* for deviceClass Receiver and Transmitter. For other device
|
|
* classes (Sequencer and Synthesizer), this flag has no effect.
|
|
* @return A MidiDevice that is considered appropriate, or null if none is
|
|
* found
|
|
*/
|
|
private static MidiDevice getFirstDevice(List<MidiDeviceProvider> providers,
|
|
Class<?> deviceClass,
|
|
boolean allowSynthesizer,
|
|
boolean allowSequencer) {
|
|
for(int i = 0; i < providers.size(); i++) {
|
|
MidiDeviceProvider provider = providers.get(i);
|
|
MidiDevice device = getFirstDevice(provider, deviceClass,
|
|
allowSynthesizer,
|
|
allowSequencer);
|
|
if (device != null) {
|
|
return device;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Checks if a MidiDevice is appropriate. If deviceClass is Synthesizer or
|
|
* Sequencer, a device implementing the respective interface is considered
|
|
* appropriate. If deviceClass is Receiver or Transmitter, a device is
|
|
* considered appropriate if it implements neither Synthesizer nor
|
|
* Transmitter, and if it can provide at least one Receiver or Transmitter,
|
|
* respectively.
|
|
*
|
|
* @param device the MidiDevice to test
|
|
* @param deviceClass The requested device type, one of Synthesizer.class,
|
|
* Sequencer.class, Receiver.class or Transmitter.class
|
|
* @param allowSynthesizer if true, Synthesizers are considered
|
|
* appropriate. Otherwise only pure MidiDevices are considered
|
|
* appropriate (unless allowSequencer is true). This flag only has
|
|
* an effect for deviceClass Receiver and Transmitter. For other
|
|
* device classes (Sequencer and Synthesizer), this flag has no
|
|
* effect.
|
|
* @param allowSequencer if true, Sequencers are considered appropriate.
|
|
* Otherwise only pure MidiDevices are considered appropriate
|
|
* (unless allowSynthesizer is true). This flag only has an effect
|
|
* for deviceClass Receiver and Transmitter. For other device
|
|
* classes (Sequencer and Synthesizer), this flag has no effect.
|
|
* @return true if the device is considered appropriate according to the
|
|
* rules given above, false otherwise
|
|
*/
|
|
private static boolean isAppropriateDevice(MidiDevice device,
|
|
Class<?> deviceClass,
|
|
boolean allowSynthesizer,
|
|
boolean allowSequencer) {
|
|
if (deviceClass.isInstance(device)) {
|
|
// This clause is for deviceClass being either Synthesizer
|
|
// or Sequencer.
|
|
return true;
|
|
} else {
|
|
// Now the case that deviceClass is Transmitter or
|
|
// Receiver. If neither allowSynthesizer nor allowSequencer is
|
|
// true, we require device instances to be
|
|
// neither Synthesizer nor Sequencer, since we only want
|
|
// devices representing MIDI ports.
|
|
// Otherwise, the respective type is accepted, too
|
|
if ( (! (device instanceof Sequencer) &&
|
|
! (device instanceof Synthesizer) ) ||
|
|
((device instanceof Sequencer) && allowSequencer) ||
|
|
((device instanceof Synthesizer) && allowSynthesizer)) {
|
|
// And of cource, the device has to be able to provide
|
|
// Receivers or Transmitters.
|
|
if ((deviceClass == Receiver.class &&
|
|
device.getMaxReceivers() != 0) ||
|
|
(deviceClass == Transmitter.class &&
|
|
device.getMaxTransmitters() != 0)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Obtains the set of services currently installed on the system using the
|
|
* SPI mechanism in 1.3.
|
|
*
|
|
* @param providerClass The type of providers requested. This should be one
|
|
* of AudioFileReader.class, AudioFileWriter.class,
|
|
* FormatConversionProvider.class, MixerProvider.class,
|
|
* MidiDeviceProvider.class, MidiFileReader.class,
|
|
* MidiFileWriter.class or SoundbankReader.class.
|
|
* @return a List of instances of providers for the requested service. If no
|
|
* providers are available, a List of length 0 will be returned.
|
|
*/
|
|
private static List<?> getProviders(Class<?> providerClass) {
|
|
return JDK13Services.getProviders(providerClass);
|
|
}
|
|
}
|