TheSequencer
interface provides methods in several categories:Regardless of which
- Methods to load sequence data from a MIDI file or a
Sequence
object, and to save the currently loaded sequence data to a MIDI file.- Methods analogous to the transport functions of a tape recorder, for stopping and starting playback and recording, enabling and disabling recording on specific tracks, and shuttling the current playback or recording position in a
Sequence
.- Advanced methods for querying and setting the synchronization and timing parameters of the object. A
Sequencer
may play at different tempos, with someTracks
muted, and in various synchronization states with other objects.- Advanced methods for registering "listener" objects that are notified when the
Sequencer
processes certain kinds of MIDI events.Sequencer
methods you'll invoke, the first step is to obtain aSequencer
device from the system and reserve it for your program's use.Obtaining a Sequencer
An application program doesn't instantiate a
Sequencer
; after all,Sequencer
is just an interface. Instead, like all devices in the Java Sound API's MIDI package, aSequencer
is accessed through the staticMidiSystem
object. As previously mentioned in Accessing MIDI System Resources, the followingMidiSystem
method can be used to obtain the defaultSequencer
:static Sequencer getSequencer()The following code fragment obtains the default
Sequencer
, acquires any system resources it needs, and makes it operational:Sequencer sequencer; // Get default sequencer. sequencer = MidiSystem.getSequencer(); if (sequencer == null) { // Error -- sequencer device is not supported. // Inform user and return... } else { // Acquire resources and make operational. sequencer.open(); }The invocation of
open
reserves the sequencer device for your program's use. It doesn't make much sense to imagine sharing a sequencer, because it can play only one sequence at a time. When you're done using the sequencer, you can make it available to other programs by invokingclose
.Non-default sequencers can be obtained as described in Accessing MIDI System Resources.
Loading a Sequence
Having obtained a sequencer from the system and reserved it, you then need load the data that the sequencer should play. There are three typical ways of accomplishing this:
We'll now look at the first of these ways of getting sequence data. (The other two ways are described below under Recording and Saving Sequences and Editing a Sequence, respectively.) This first way actually encompasses two slightly different approaches. One approach is to feed MIDI file data to an
- Reading the sequence data from a MIDI file
- Recording it in real time by receiving MIDI messages from another device, such as a MIDI input port
- Building it programmatically "from scratch" by adding tracks to an empty sequence and adding
MidiEvent
objects to those tracksInputStream
that you then read directly to the sequencer by means ofSequencer.setSequence(InputStream)
. With this approach, you don't explicitly create aSequence
object. In fact, theSequencer
implementation might not even create aSequence
behind the scenes, because some sequencers have a built-in mechanism for handling data directly from a file.The other approach is to create a
Sequence
explicitly. You'll need to use this approach if you're going to edit the sequence data before playing it. With this approach, you invokeMidiSystem's
overloaded methodgetSequence
. The method is able to get the sequence from anInputStream
, aFile
, or aURL
. The method returns aSequence
object that can then be loaded into aSequencer
for playback. Expanding on the previous code excerpt, here's an example of obtaining aSequence
object from aFile
and loading it into oursequencer
:try { File myMidiFile = new File("seq1.mid"); // Construct a Sequence object, and // load it into my sequencer. Sequence mySeq = MidiSystem.getSequence(myMidiFile); sequencer.setSequence(mySeq); } catch (Exception e) { // Handle error and/or return }Like
MidiSystem's
getSequence
method,setSequence
may throw anInvalidMidiDataException
and, in the case of theInputStream
variant, anIOException
if it runs into any trouble.Playing a Sequence
Starting and stopping a
Sequencer
is accomplished using the following methods:void start()void stop()The
Sequencer.start
method begins playback of the sequence. Note that playback starts at the current position in a sequence. Loading an existing sequence using thesetSequence
method, described above, initializes the sequencer's current position to the very beginning of the sequence. Thestop
method stops the sequencer, but it does not automatically rewind the currentSequence
. Starting a stoppedSequence
without resetting the position simply resumes playback of the sequence from the current position. In this case, thestop
method has served as a pause operation. However, there are variousSequencer
methods for setting the current sequence position to an arbitrary value before playback is started. (We'll discuss these methods below.)As mentioned earlier, a
Sequencer
typically has one or moreTransmitter
objects, through which it sendsMidiMessages
to aReceiver
. It is through theseTransmitters
that aSequencer
plays theSequence
, by emitting appropriately timedMidiMessages
that correspond to theMidiEvents
contained in the currentSequence
. Therefore, part of the setup procedure for playing back aSequence
is to invoke thesetReceiver
method on theSequencer's
Transmitter
object, in effect wiring its output to the device that will make use of the played-back data. For more details onTransmitters
andReceivers
, refer back to Transmitting and Receiving MIDI Messages.Recording and Saving Sequences
To capture MIDI data to a
Sequence
, and subsequently to a file, you need to perform some additional steps beyond those described above. The following outline shows the steps necessary to set up for recording to aTrack
in aSequence
:
- Use
MidiSystem.getSequencer
to get a new sequencer to use for recording, as above.
- Set up the "wiring" of the MIDI connections. The object that is transmitting the MIDI data to be recorded should be configured, through its
setReceiver
method, to send data to aReceiver
associated with the recordingSequencer
.
- Create a new
Sequence
object, which will store the recorded data. When you create theSequence
object, you must specify the global timing information for the sequence. For example:The constructor forSequence mySeq; try{ mySeq = new Sequence(Sequence.PPQ, 10); } catch (Exception ex) { ex.printStackTrace(); }Sequence
takes as arguments adivisionType
and a timing resolution. ThedivisionType
argument specifies the units of the resolution argument. In this case, we've specified that the timing resolution of theSequence
we're creating will be 10 pulses per quarter note. An additional optional argument to theSequence
constructor is a number of tracks argument, which would cause the initial sequence to begin with the specified number of (initially empty)Tracks
. Otherwise theSequence
will be created with no initialTracks
; they can be added later as needed.
- Create an empty
Track
in theSequence
, withSequence.createTrack
. This step is unnecessary if theSequence
was created with initialTracks
.
- Using
Sequencer.setSequence
, select our newSequence
to receive the recording. ThesetSequence
method ties together an existingSequence
with theSequencer
, which is somewhat analogous to loading a tape onto a tape recorder.
- Invoke
Sequencer.recordEnable
for eachTrack
to be recorded. If necessary, get a reference to the availableTracks
in theSequence
by invokingSequence.getTracks
.
- Invoke
startRecording
on theSequencer
.
- When done recording, invoke
Sequencer.stop
orSequencer.stopRecording
.
- Save the recorded
Sequence
to a MIDI file withMidiSystem.write
. Thewrite
method ofMidiSystem
takes aSequence
as one of its arguments, and will write thatSequence
to a stream or file.Editing a Sequence
Many application programs allow a sequence to be created by loading it from a file, and quite a few also allow a sequence to be created by capturing it from live MIDI input (that is, recording). Some programs, however, will need to create MIDI sequences from scratch, whether programmatically or in response to user input. Full-featured sequencer programs permit the user to manually construct new sequences, as well as to edit existing ones.
These data-editing operations are achieved in the Java Sound API not by
Sequencer
methods, but by methods of the data objects themselves:Sequence
,Track
, andMidiEvent
. You can create an empty sequence using one of theSequence
constructors, and then add tracks to it by invoking the followingSequence
method:If your program allows the user to edit sequences, you'll need thisTrack createTrack()Sequence
method to remove tracks:boolean deleteTrack(Track track)Once the sequence contains tracks, you can modify the contents of the tracks by invoking methods of the
Track
class. TheMidiEvents
contained in theTrack
are stored as ajava.util.Vector
in theTrack
object, andTrack
provides a set of methods for accessing, adding, and removing the events in the list. The methodsadd
andremove
are fairly self-explanatory, adding or removing a specifiedMidiEvent
from aTrack
. Aget
method is provided, which takes an index into theTrack's
event list and returns theMidiEvent
stored there. In addition, there aresize
andtick
methods, which respectively return the number ofMidiEvents
in the track, and the track's duration, expressed as a total number ofTicks
.To create a new event before adding it to the track, you'll of course use the
MidiEvent
constructor. To specify or modify the MIDI message embedded in the event, you can invoke thesetMessage
method of the appropriateMidiMessage
subclass (ShortMessage
,SysexMessage
, orMetaMessage
). To modify the time that the event should occur, invokeMidiEvent.setTick
.In combination, these low-level methods provide the basis for the editing functionality needed by a full-featured sequencer program.