javax.sound.midi.Sequencer.java Source code

Java tutorial

Introduction

Here is the source code for javax.sound.midi.Sequencer.java

Source

/*
 * Copyright (c) 1999, 2017, 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.IOException;
import java.io.InputStream;

/**
 * A hardware or software device that plays back a MIDI
 * {@link Sequence sequence} is known as a <em>sequencer</em>. A MIDI sequence
 * contains lists of time-stamped MIDI data, such as might be read from a
 * standard MIDI file. Most sequencers also provide functions for creating and
 * editing sequences.
 * <p>
 * The {@code Sequencer} interface includes methods for the following basic MIDI
 * sequencer operations:
 * <ul>
 *   <li>obtaining a sequence from MIDI file data
 *   <li>starting and stopping playback
 *   <li>moving to an arbitrary position in the sequence
 *   <li>changing the tempo (speed) of playback
 *   <li>synchronizing playback to an internal clock or to received MIDI
 *   messages
 *   <li>controlling the timing of another device
 * </ul>
 * In addition, the following operations are supported, either directly, or
 * indirectly through objects that the {@code Sequencer} has access to:
 * <ul>
 *   <li>editing the data by adding or deleting individual MIDI events or entire
 *   tracks
 *   <li>muting or soloing individual tracks in the sequence
 *   <li>notifying listener objects about any meta-events or control-change
 *   events encountered while playing back the sequence
 * </ul>
 *
 * @author Kara Kytle
 * @author Florian Bomers
 * @see SyncMode
 * @see #addMetaEventListener
 * @see ControllerEventListener
 * @see Receiver
 * @see Transmitter
 * @see MidiDevice
 */
public interface Sequencer extends MidiDevice {

    /**
     * A value indicating that looping should continue indefinitely rather than
     * complete after a specific number of loops.
     *
     * @see #setLoopCount
     * @since 1.5
     */
    int LOOP_CONTINUOUSLY = -1;

    /**
     * Sets the current sequence on which the sequencer operates.
     * <p>
     * This method can be called even if the {@code Sequencer} is closed.
     *
     * @param  sequence the sequence to be loaded
     * @throws InvalidMidiDataException if the sequence contains invalid MIDI
     *         data, or is not supported
     */
    void setSequence(Sequence sequence) throws InvalidMidiDataException;

    /**
     * Sets the current sequence on which the sequencer operates. The stream
     * must point to MIDI file data.
     * <p>
     * This method can be called even if the {@code Sequencer} is closed.
     *
     * @param  stream stream containing MIDI file data
     * @throws IOException if an I/O exception occurs during reading of the
     *         stream
     * @throws InvalidMidiDataException if invalid data is encountered in the
     *         stream, or the stream is not supported
     */
    void setSequence(InputStream stream) throws IOException, InvalidMidiDataException;

    /**
     * Obtains the sequence on which the Sequencer is currently operating.
     * <p>
     * This method can be called even if the {@code Sequencer} is closed.
     *
     * @return the current sequence, or {@code null} if no sequence is currently
     *         set
     */
    Sequence getSequence();

    /**
     * Starts playback of the MIDI data in the currently loaded sequence.
     * Playback will begin from the current position. If the playback position
     * reaches the loop end point, and the loop count is greater than 0,
     * playback will resume at the loop start point for the number of
     * repetitions set with {@code setLoopCount}. After that, or if the loop
     * count is 0, playback will continue to play to the end of the sequence.
     * <p>
     * The implementation ensures that the synthesizer is brought to a
     * consistent state when jumping to the loop start point by sending
     * appropriate controllers, pitch bend, and program change events.
     *
     * @throws IllegalStateException if the {@code Sequencer} is closed
     * @see #setLoopStartPoint
     * @see #setLoopEndPoint
     * @see #setLoopCount
     * @see #stop
     */
    void start();

    /**
     * Stops recording, if active, and playback of the currently loaded
     * sequence, if any.
     *
     * @throws IllegalStateException if the {@code Sequencer} is closed
     * @see #start
     * @see #isRunning
     */
    void stop();

    /**
     * Indicates whether the Sequencer is currently running. The default is
     * {@code false}. The Sequencer starts running when either{@link #start} or
     * {@link #startRecording} is called. {@code isRunning} then returns
     * {@code true} until playback of the sequence completes or {@link #stop} is
     * called.
     *
     * @return {@code true} if the Sequencer is running, otherwise {@code false}
     */
    boolean isRunning();

    /**
     * Starts recording and playback of MIDI data. Data is recorded to all
     * enabled tracks, on the channel(s) for which they were enabled. Recording
     * begins at the current position of the sequencer. Any events already in
     * the track are overwritten for the duration of the recording session.
     * Events from the currently loaded sequence, if any, are delivered to the
     * sequencer's transmitter(s) along with messages received during recording.
     * <p>
     * Note that tracks are not by default enabled for recording. In order to
     * record MIDI data, at least one track must be specifically enabled for
     * recording.
     *
     * @throws IllegalStateException if the {@code Sequencer} is closed
     * @see #recordEnable
     * @see #recordDisable
     */
    void startRecording();

    /**
     * Stops recording, if active. Playback of the current sequence continues.
     *
     * @throws IllegalStateException if the {@code Sequencer} is closed
     * @see #startRecording
     * @see #isRecording
     */
    void stopRecording();

    /**
     * Indicates whether the Sequencer is currently recording. The default is
     * {@code false}. The Sequencer begins recording when
     * {@link #startRecording} is called, and then returns {@code true} until
     * {@link #stop} or {@link #stopRecording} is called.
     *
     * @return {@code true} if the Sequencer is recording, otherwise
     *         {@code false}
     */
    boolean isRecording();

    /**
     * Prepares the specified track for recording events received on a
     * particular channel. Once enabled, a track will receive events when
     * recording is active.
     *
     * @param  track the track to which events will be recorded
     * @param  channel the channel on which events will be received. If -1 is
     *         specified for the channel value, the track will receive data from
     *         all channels.
     * @throws IllegalArgumentException thrown if the track is not part of the
     *         current sequence
     */
    void recordEnable(Track track, int channel);

    /**
     * Disables recording to the specified track. Events will no longer be
     * recorded into this track.
     *
     * @param  track the track to disable for recording, or {@code null} to
     *         disable recording for all tracks
     */
    void recordDisable(Track track);

    /**
     * Obtains the current tempo, expressed in beats per minute. The actual
     * tempo of playback is the product of the returned value and the tempo
     * factor.
     *
     * @return the current tempo in beats per minute
     * @see #getTempoFactor
     * @see #setTempoInBPM(float)
     * @see #getTempoInMPQ
     */
    float getTempoInBPM();

    /**
     * Sets the tempo in beats per minute. The actual tempo of playback is the
     * product of the specified value and the tempo factor.
     *
     * @param  bpm desired new tempo in beats per minute
     * @see #getTempoFactor
     * @see #setTempoInMPQ(float)
     * @see #getTempoInBPM
     */
    void setTempoInBPM(float bpm);

    /**
     * Obtains the current tempo, expressed in microseconds per quarter note.
     * The actual tempo of playback is the product of the returned value and the
     * tempo factor.
     *
     * @return the current tempo in microseconds per quarter note
     * @see #getTempoFactor
     * @see #setTempoInMPQ(float)
     * @see #getTempoInBPM
     */
    float getTempoInMPQ();

    /**
     * Sets the tempo in microseconds per quarter note. The actual tempo of
     * playback is the product of the specified value and the tempo factor.
     *
     * @param  mpq desired new tempo in microseconds per quarter note
     * @see #getTempoFactor
     * @see #setTempoInBPM(float)
     * @see #getTempoInMPQ
     */
    void setTempoInMPQ(float mpq);

    /**
     * Scales the sequencer's actual playback tempo by the factor provided. The
     * default is 1.0. A value of 1.0 represents the natural rate (the tempo
     * specified in the sequence), 2.0 means twice as fast, etc. The tempo
     * factor does not affect the values returned by {@link #getTempoInMPQ} and
     * {@link #getTempoInBPM}. Those values indicate the tempo prior to scaling.
     * <p>
     * Note that the tempo factor cannot be adjusted when external
     * synchronization is used. In that situation, {@code setTempoFactor} always
     * sets the tempo factor to 1.0.
     *
     * @param  factor the requested tempo scalar
     * @see #getTempoFactor
     */
    void setTempoFactor(float factor);

    /**
     * Returns the current tempo factor for the sequencer. The default is 1.0.
     *
     * @return tempo factor
     * @see #setTempoFactor(float)
     */
    float getTempoFactor();

    /**
     * Obtains the length of the current sequence, expressed in MIDI ticks, or 0
     * if no sequence is set.
     *
     * @return length of the sequence in ticks
     */
    long getTickLength();

    /**
     * Obtains the current position in the sequence, expressed in MIDI ticks.
     * (The duration of a tick in seconds is determined both by the tempo and by
     * the timing resolution stored in the {@link Sequence}.)
     *
     * @return current tick
     * @see #setTickPosition
     */
    long getTickPosition();

    /**
     * Sets the current sequencer position in MIDI ticks.
     *
     * @param  tick the desired tick position
     * @see #getTickPosition
     */
    void setTickPosition(long tick);

    /**
     * Obtains the length of the current sequence, expressed in microseconds, or
     * 0 if no sequence is set.
     *
     * @return length of the sequence in microseconds
     */
    long getMicrosecondLength();

    /**
     * Obtains the current position in the sequence, expressed in microseconds.
     *
     * @return the current position in microseconds
     * @see #setMicrosecondPosition
     */
    @Override
    long getMicrosecondPosition();

    /**
     * Sets the current position in the sequence, expressed in microseconds.
     *
     * @param  microseconds desired position in microseconds
     * @see #getMicrosecondPosition
     */
    void setMicrosecondPosition(long microseconds);

    /**
     * Sets the source of timing information used by this sequencer. The
     * sequencer synchronizes to the master, which is the internal clock, MIDI
     * clock, or MIDI time code, depending on the value of {@code sync}. The
     * {@code sync} argument must be one of the supported modes, as returned by
     * {@link #getMasterSyncModes}.
     *
     * @param  sync the desired master synchronization mode
     * @see SyncMode#INTERNAL_CLOCK
     * @see SyncMode#MIDI_SYNC
     * @see SyncMode#MIDI_TIME_CODE
     * @see #getMasterSyncMode
     */
    void setMasterSyncMode(SyncMode sync);

    /**
     * Obtains the current master synchronization mode for this sequencer.
     *
     * @return the current master synchronization mode
     * @see #setMasterSyncMode(SyncMode)
     * @see #getMasterSyncModes
     */
    SyncMode getMasterSyncMode();

    /**
     * Obtains the set of master synchronization modes supported by this
     * sequencer.
     *
     * @return the available master synchronization modes
     * @see SyncMode#INTERNAL_CLOCK
     * @see SyncMode#MIDI_SYNC
     * @see SyncMode#MIDI_TIME_CODE
     * @see #getMasterSyncMode
     * @see #setMasterSyncMode(SyncMode)
     */
    SyncMode[] getMasterSyncModes();

    /**
     * Sets the slave synchronization mode for the sequencer. This indicates the
     * type of timing information sent by the sequencer to its receiver. The
     * {@code sync} argument must be one of the supported modes, as returned by
     * {@link #getSlaveSyncModes}.
     *
     * @param  sync the desired slave synchronization mode
     * @see SyncMode#MIDI_SYNC
     * @see SyncMode#MIDI_TIME_CODE
     * @see SyncMode#NO_SYNC
     * @see #getSlaveSyncModes
     */
    void setSlaveSyncMode(SyncMode sync);

    /**
     * Obtains the current slave synchronization mode for this sequencer.
     *
     * @return the current slave synchronization mode
     * @see #setSlaveSyncMode(SyncMode)
     * @see #getSlaveSyncModes
     */
    SyncMode getSlaveSyncMode();

    /**
     * Obtains the set of slave synchronization modes supported by the
     * sequencer.
     *
     * @return the available slave synchronization modes
     * @see SyncMode#MIDI_SYNC
     * @see SyncMode#MIDI_TIME_CODE
     * @see SyncMode#NO_SYNC
     */
    SyncMode[] getSlaveSyncModes();

    /**
     * Sets the mute state for a track. This method may fail for a number of
     * reasons. For example, the track number specified may not be valid for the
     * current sequence, or the sequencer may not support this functionality. An
     * application which needs to verify whether this operation succeeded should
     * follow this call with a call to {@link #getTrackMute}.
     *
     * @param  track the track number. Tracks in the current sequence are
     *         numbered from 0 to the number of tracks in the sequence minus 1.
     * @param  mute the new mute state for the track. {@code true} implies the
     *         track should be muted, {@code false} implies the track should be
     *         unmuted.
     * @see #getSequence
     */
    void setTrackMute(int track, boolean mute);

    /**
     * Obtains the current mute state for a track. The default mute state for
     * all tracks which have not been muted is false. In any case where the
     * specified track has not been muted, this method should return false. This
     * applies if the sequencer does not support muting of tracks, and if the
     * specified track index is not valid.
     *
     * @param  track the track number. Tracks in the current sequence are
     *         numbered from 0 to the number of tracks in the sequence minus 1.
     * @return {@code true} if muted, {@code false} if not
     */
    boolean getTrackMute(int track);

    /**
     * Sets the solo state for a track. If {@code solo} is {@code true} only
     * this track and other solo'd tracks will sound. If {@code solo} is
     * {@code false} then only other solo'd tracks will sound, unless no tracks
     * are solo'd in which case all un-muted tracks will sound.
     * <p>
     * This method may fail for a number of reasons. For example, the track
     * number specified may not be valid for the current sequence, or the
     * sequencer may not support this functionality. An application which needs
     * to verify whether this operation succeeded should follow this call with a
     * call to {@link #getTrackSolo}.
     *
     * @param  track the track number. Tracks in the current sequence are
     *         numbered from 0 to the number of tracks in the sequence minus 1.
     * @param  solo the new solo state for the track. {@code true} implies the
     *         track should be solo'd, {@code false} implies the track should
     *         not be solo'd.
     * @see #getSequence
     */
    void setTrackSolo(int track, boolean solo);

    /**
     * Obtains the current solo state for a track. The default mute state for
     * all tracks which have not been solo'd is false. In any case where the
     * specified track has not been solo'd, this method should return false.
     * This applies if the sequencer does not support soloing of tracks, and if
     * the specified track index is not valid.
     *
     * @param  track the track number. Tracks in the current sequence are
     *         numbered from 0 to the number of tracks in the sequence minus 1.
     * @return {@code true} if solo'd, {@code false} if not
     */
    boolean getTrackSolo(int track);

    /**
     * Registers a meta-event listener to receive notification whenever a
     * meta-event is encountered in the sequence and processed by the sequencer.
     * This method can fail if, for instance,this class of sequencer does not
     * support meta-event notification.
     *
     * @param  listener listener to add
     * @return {@code true} if the listener was successfully added, otherwise
     *         {@code false}
     * @see #removeMetaEventListener
     * @see MetaEventListener
     * @see MetaMessage
     */
    boolean addMetaEventListener(MetaEventListener listener);

    /**
     * Removes the specified meta-event listener from this sequencer's list of
     * registered listeners, if in fact the listener is registered.
     *
     * @param  listener the meta-event listener to remove
     * @see #addMetaEventListener
     */
    void removeMetaEventListener(MetaEventListener listener);

    /**
     * Registers a controller event listener to receive notification whenever
     * the sequencer processes a control-change event of the requested type or
     * types. The types are specified by the {@code controllers} argument, which
     * should contain an array of MIDI controller numbers. (Each number should
     * be between 0 and 127, inclusive. See the MIDI 1.0 Specification for the
     * numbers that correspond to various types of controllers.)
     * <p>
     * The returned array contains the MIDI controller numbers for which the
     * listener will now receive events. Some sequencers might not support
     * controller event notification, in which case the array has a length of 0.
     * Other sequencers might support notification for some controllers but not
     * all. This method may be invoked repeatedly. Each time, the returned array
     * indicates all the controllers that the listener will be notified about,
     * not only the controllers requested in that particular invocation.
     *
     * @param  listener the controller event listener to add to the list of
     *         registered listeners
     * @param  controllers the MIDI controller numbers for which change
     *         notification is requested
     * @return the numbers of all the MIDI controllers whose changes will now be
     *         reported to the specified listener
     * @see #removeControllerEventListener
     * @see ControllerEventListener
     */
    int[] addControllerEventListener(ControllerEventListener listener, int[] controllers);

    /**
     * Removes a controller event listener's interest in one or more types of
     * controller event. The {@code controllers} argument is an array of MIDI
     * numbers corresponding to the controllers for which the listener should no
     * longer receive change notifications. To completely remove this listener
     * from the list of registered listeners, pass in {@code null} for
     * {@code controllers}. The returned array contains the MIDI controller
     * numbers for which the listener will now receive events. The array has a
     * length of 0 if the listener will not receive change notifications for any
     * controllers.
     *
     * @param  listener old listener
     * @param  controllers the MIDI controller numbers for which change
     *         notification should be cancelled, or {@code null} to cancel for
     *         all controllers
     * @return the numbers of all the MIDI controllers whose changes will now be
     *         reported to the specified listener
     * @see #addControllerEventListener
     */
    int[] removeControllerEventListener(ControllerEventListener listener, int[] controllers);

    /**
     * Sets the first MIDI tick that will be played in the loop. If the loop
     * count is greater than 0, playback will jump to this point when reaching
     * the loop end point.
     * <p>
     * A value of 0 for the starting point means the beginning of the loaded
     * sequence. The starting point must be lower than or equal to the ending
     * point, and it must fall within the size of the loaded sequence.
     * <p>
     * A sequencer's loop start point defaults to start of the sequence.
     *
     * @param  tick the loop's starting position, in MIDI ticks (zero-based)
     * @throws IllegalArgumentException if the requested loop start point cannot
     *         be set, usually because it falls outside the sequence's duration
     *         or because the start point is after the end point
     * @see #setLoopEndPoint
     * @see #setLoopCount
     * @see #getLoopStartPoint
     * @see #start
     * @since 1.5
     */
    void setLoopStartPoint(long tick);

    /**
     * Obtains the start position of the loop, in MIDI ticks.
     *
     * @return the start position of the loop, in MIDI ticks (zero-based)
     * @see #setLoopStartPoint
     * @since 1.5
     */
    long getLoopStartPoint();

    /**
     * Sets the last MIDI tick that will be played in the loop. If the loop
     * count is 0, the loop end point has no effect and playback continues to
     * play when reaching the loop end point.
     * <p>
     * A value of -1 for the ending point indicates the last tick of the
     * sequence. Otherwise, the ending point must be greater than or equal to
     * the starting point, and it must fall within the size of the loaded
     * sequence.
     * <p>
     * A sequencer's loop end point defaults to -1, meaning the end of the
     * sequence.
     *
     * @param  tick the loop's ending position, in MIDI ticks (zero-based), or
     *         -1 to indicate the final tick
     * @throws IllegalArgumentException if the requested loop point cannot be
     *         set, usually because it falls outside the sequence's duration or
     *         because the ending point is before the starting point
     * @see #setLoopStartPoint
     * @see #setLoopCount
     * @see #getLoopEndPoint
     * @see #start
     * @since 1.5
     */
    void setLoopEndPoint(long tick);

    /**
     * Obtains the end position of the loop, in MIDI ticks.
     *
     * @return the end position of the loop, in MIDI ticks (zero-based), or -1
     *         to indicate the end of the sequence
     * @see #setLoopEndPoint
     * @since 1.5
     */
    long getLoopEndPoint();

    /**
     * Sets the number of repetitions of the loop for playback. When the
     * playback position reaches the loop end point, it will loop back to the
     * loop start point {@code count} times, after which playback will continue
     * to play to the end of the sequence.
     * <p>
     * If the current position when this method is invoked is greater than the
     * loop end point, playback continues to the end of the sequence without
     * looping, unless the loop end point is changed subsequently.
     * <p>
     * A {@code count} value of 0 disables looping: playback will continue at
     * the loop end point, and it will not loop back to the loop start point.
     * This is a sequencer's default.
     * <p>
     * If playback is stopped during looping, the current loop status is
     * cleared; subsequent start requests are not affected by an interrupted
     * loop operation.
     *
     * @param  count the number of times playback should loop back from the
     *         loop's end position to the loop's start position, or
     *         {@link #LOOP_CONTINUOUSLY} to indicate that looping should
     *         continue until interrupted
     * @throws IllegalArgumentException if {@code count} is negative and not
     *         equal to {@link #LOOP_CONTINUOUSLY}
     * @see #setLoopStartPoint
     * @see #setLoopEndPoint
     * @see #getLoopCount
     * @see #start
     * @since 1.5
     */
    void setLoopCount(int count);

    /**
     * Obtains the number of repetitions for playback.
     *
     * @return the number of loops after which playback plays to the end of the
     *         sequence
     * @see #setLoopCount
     * @see #start
     * @since 1.5
     */
    int getLoopCount();

    /**
     * A {@code SyncMode} object represents one of the ways in which a MIDI
     * sequencer's notion of time can be synchronized with a master or slave
     * device. If the sequencer is being synchronized to a master, the sequencer
     * revises its current time in response to messages from the master. If the
     * sequencer has a slave, the sequencer similarly sends messages to control
     * the slave's timing.
     * <p>
     * There are three predefined modes that specify possible masters for a
     * sequencer: {@code INTERNAL_CLOCK}, {@code MIDI_SYNC}, and
     * {@code MIDI_TIME_CODE}. The latter two work if the sequencer receives
     * MIDI messages from another device. In these two modes, the sequencer's
     * time gets reset based on system real-time timing clock messages or MIDI
     * time code (MTC) messages, respectively. These two modes can also be used
     * as slave modes, in which case the sequencer sends the corresponding types
     * of MIDI messages to its receiver (whether or not the sequencer is also
     * receiving them from a master). A fourth mode, {@code NO_SYNC}, is used to
     * indicate that the sequencer should not control its receiver's timing.
     *
     * @see Sequencer#setMasterSyncMode(SyncMode)
     * @see Sequencer#setSlaveSyncMode(SyncMode)
     */
    class SyncMode {

        /**
         * Synchronization mode name.
         */
        private final String name;

        /**
         * Constructs a synchronization mode.
         *
         * @param  name name of the synchronization mode
         */
        protected SyncMode(String name) {

            this.name = name;
        }

        /**
         * Indicates whether the specified object is equal to this
         * synchronization mode, returning {@code true} if the objects are the
         * same.
         *
         * @param  obj the reference object with which to compare
         * @return {@code true} if the specified object is equal to this
         *         synchronization mode; {@code false} otherwise
         */
        @Override
        public final boolean equals(Object obj) {

            return super.equals(obj);
        }

        /**
         * Returns a hash code value for this synchronization mode.
         *
         * @return a hash code value for this synchronization mode
         */
        @Override
        public final int hashCode() {

            return super.hashCode();
        }

        /**
         * Provides this synchronization mode's name as the string
         * representation of the mode.
         *
         * @return the name of this synchronization mode
         */
        @Override
        public final String toString() {

            return name;
        }

        /**
         * A master synchronization mode that makes the sequencer get its timing
         * information from its internal clock. This is not a legal slave sync
         * mode.
         */
        public static final SyncMode INTERNAL_CLOCK = new SyncMode("Internal Clock");

        /**
         * A master or slave synchronization mode that specifies the use of MIDI
         * clock messages. If this mode is used as the master sync mode, the
         * sequencer gets its timing information from system real-time MIDI
         * clock messages. This mode only applies as the master sync mode for
         * sequencers that are also MIDI receivers. If this is the slave sync
         * mode, the sequencer sends system real-time MIDI clock messages to its
         * receiver. MIDI clock messages are sent at a rate of 24 per quarter
         * note.
         */
        public static final SyncMode MIDI_SYNC = new SyncMode("MIDI Sync");

        /**
         * A master or slave synchronization mode that specifies the use of MIDI
         * Time Code. If this mode is used as the master sync mode, the
         * sequencer gets its timing information from MIDI Time Code messages.
         * This mode only applies as the master sync mode to sequencers that are
         * also MIDI receivers. If this mode is used as the slave sync mode, the
         * sequencer sends MIDI Time Code messages to its receiver. (See the
         * MIDI 1.0 Detailed Specification for a description of MIDI Time Code.)
         */
        public static final SyncMode MIDI_TIME_CODE = new SyncMode("MIDI Time Code");

        /**
         * A slave synchronization mode indicating that no timing information
         * should be sent to the receiver. This is not a legal master sync mode.
         */
        public static final SyncMode NO_SYNC = new SyncMode("No Timing");

    }
}