TheSequencerinterface provides methods in several categories:Regardless of which
- Methods to load sequence data from a MIDI file or a
Sequenceobject, 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
Sequencermay play at different tempos, with someTracksmuted, and in various synchronization states with other objects.- Advanced methods for registering "listener" objects that are notified when the
Sequencerprocesses certain kinds of MIDI events.Sequencermethods you'll invoke, the first step is to obtain aSequencerdevice from the system and reserve it for your program's use.Obtaining a Sequencer
An application program doesn't instantiate a
Sequencer; after all,Sequenceris just an interface. Instead, like all devices in the Java Sound API's MIDI package, aSequenceris accessed through the staticMidiSystemobject. As previously mentioned in Accessing MIDI System Resources, the followingMidiSystemmethod 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
openreserves 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
MidiEventobjects to those tracksInputStreamthat you then read directly to the sequencer by means ofSequencer.setSequence(InputStream). With this approach, you don't explicitly create aSequenceobject. In fact, theSequencerimplementation might not even create aSequencebehind the scenes, because some sequencers have a built-in mechanism for handling data directly from a file.The other approach is to create a
Sequenceexplicitly. You'll need to use this approach if you're going to edit the sequence data before playing it. With this approach, you invokeMidiSystem'soverloaded methodgetSequence. The method is able to get the sequence from anInputStream, aFile, or aURL. The method returns aSequenceobject that can then be loaded into aSequencerfor playback. Expanding on the previous code excerpt, here's an example of obtaining aSequenceobject from aFileand 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'sgetSequencemethod,setSequencemay throw anInvalidMidiDataExceptionand, in the case of theInputStreamvariant, anIOExceptionif it runs into any trouble.Playing a Sequence
Starting and stopping a
Sequenceris accomplished using the following methods:void start()void stop()The
Sequencer.startmethod begins playback of the sequence. Note that playback starts at the current position in a sequence. Loading an existing sequence using thesetSequencemethod, described above, initializes the sequencer's current position to the very beginning of the sequence. Thestopmethod stops the sequencer, but it does not automatically rewind the currentSequence. Starting a stoppedSequencewithout resetting the position simply resumes playback of the sequence from the current position. In this case, thestopmethod has served as a pause operation. However, there are variousSequencermethods for setting the current sequence position to an arbitrary value before playback is started. (We'll discuss these methods below.)As mentioned earlier, a
Sequencertypically has one or moreTransmitterobjects, through which it sendsMidiMessagesto aReceiver. It is through theseTransmittersthat aSequencerplays theSequence, by emitting appropriately timedMidiMessagesthat correspond to theMidiEventscontained in the currentSequence. Therefore, part of the setup procedure for playing back aSequenceis to invoke thesetReceivermethod on theSequencer'sTransmitterobject, in effect wiring its output to the device that will make use of the played-back data. For more details onTransmittersandReceivers, 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 aTrackin aSequence:
- Use
MidiSystem.getSequencerto 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
setReceivermethod, to send data to aReceiverassociated with the recordingSequencer.
- Create a new
Sequenceobject, which will store the recorded data. When you create theSequenceobject, 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(); }Sequencetakes as arguments adivisionTypeand a timing resolution. ThedivisionTypeargument specifies the units of the resolution argument. In this case, we've specified that the timing resolution of theSequencewe're creating will be 10 pulses per quarter note. An additional optional argument to theSequenceconstructor is a number of tracks argument, which would cause the initial sequence to begin with the specified number of (initially empty)Tracks. Otherwise theSequencewill be created with no initialTracks; they can be added later as needed.
- Create an empty
Trackin theSequence, withSequence.createTrack. This step is unnecessary if theSequencewas created with initialTracks.
- Using
Sequencer.setSequence, select our newSequenceto receive the recording. ThesetSequencemethod ties together an existingSequencewith theSequencer, which is somewhat analogous to loading a tape onto a tape recorder.
- Invoke
Sequencer.recordEnablefor eachTrackto be recorded. If necessary, get a reference to the availableTracksin theSequenceby invokingSequence.getTracks.
- Invoke
startRecordingon theSequencer.
- When done recording, invoke
Sequencer.stoporSequencer.stopRecording.
- Save the recorded
Sequenceto a MIDI file withMidiSystem.write. Thewritemethod ofMidiSystemtakes aSequenceas one of its arguments, and will write thatSequenceto 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
Sequencermethods, but by methods of the data objects themselves:Sequence,Track, andMidiEvent. You can create an empty sequence using one of theSequenceconstructors, and then add tracks to it by invoking the followingSequencemethod:If your program allows the user to edit sequences, you'll need thisTrack createTrack()Sequencemethod 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
Trackclass. TheMidiEventscontained in theTrackare stored as ajava.util.Vectorin theTrackobject, andTrackprovides a set of methods for accessing, adding, and removing the events in the list. The methodsaddandremoveare fairly self-explanatory, adding or removing a specifiedMidiEventfrom aTrack. Agetmethod is provided, which takes an index into theTrack'sevent list and returns theMidiEventstored there. In addition, there aresizeandtickmethods, which respectively return the number ofMidiEventsin 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
MidiEventconstructor. To specify or modify the MIDI message embedded in the event, you can invoke thesetMessagemethod of the appropriateMidiMessagesubclass (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.