1 /*
2 * Copyright 1999-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.sound.midi;
27
28 import java.util.Vector;
29 import com.sun.media.sound.MidiUtils;
30
31
32 /**
33 * A <code>Sequence</code> is a data structure containing musical
34 * information (often an entire song or composition) that can be played
35 * back by a <code>{@link Sequencer}</code> object. Specifically, the
36 * <code>Sequence</code> contains timing
37 * information and one or more tracks. Each <code>{@link Track track}</code> consists of a
38 * series of MIDI events (such as note-ons, note-offs, program changes, and meta-events).
39 * The sequence's timing information specifies the type of unit that is used
40 * to time-stamp the events in the sequence.
41 * <p>
42 * A <code>Sequence</code> can be created from a MIDI file by reading the file
43 * into an input stream and invoking one of the <code>getSequence</code> methods of
44 * {@link MidiSystem}. A sequence can also be built from scratch by adding new
45 * <code>Tracks</code> to an empty <code>Sequence</code>, and adding
46 * <code>{@link MidiEvent}</code> objects to these <code>Tracks</code>.
47 *
48 * @see Sequencer#setSequence(java.io.InputStream stream)
49 * @see Sequencer#setSequence(Sequence sequence)
50 * @see Track#add(MidiEvent)
51 * @see MidiFileFormat
52 *
53 * @author Kara Kytle
54 */
55 public class Sequence {
56
57
58 // Timing types
59
60 /**
61 * The tempo-based timing type, for which the resolution is expressed in pulses (ticks) per quarter note.
62 * @see #Sequence(float, int)
63 */
64 public static final float PPQ = 0.0f;
65
66 /**
67 * The SMPTE-based timing type with 24 frames per second (resolution is expressed in ticks per frame).
68 * @see #Sequence(float, int)
69 */
70 public static final float SMPTE_24 = 24.0f;
71
72 /**
73 * The SMPTE-based timing type with 25 frames per second (resolution is expressed in ticks per frame).
74 * @see #Sequence(float, int)
75 */
76 public static final float SMPTE_25 = 25.0f;
77
78 /**
79 * The SMPTE-based timing type with 29.97 frames per second (resolution is expressed in ticks per frame).
80 * @see #Sequence(float, int)
81 */
82 public static final float SMPTE_30DROP = 29.97f;
83
84 /**
85 * The SMPTE-based timing type with 30 frames per second (resolution is expressed in ticks per frame).
86 * @see #Sequence(float, int)
87 */
88 public static final float SMPTE_30 = 30.0f;
89
90
91 // Variables
92
93 /**
94 * The timing division type of the sequence.
95 * @see #PPQ
96 * @see #SMPTE_24
97 * @see #SMPTE_25
98 * @see #SMPTE_30DROP
99 * @see #SMPTE_30
100 * @see #getDivisionType
101 */
102 protected float divisionType;
103
104 /**
105 * The timing resolution of the sequence.
106 * @see #getResolution
107 */
108 protected int resolution;
109
110 /**
111 * The MIDI tracks in this sequence.
112 * @see #getTracks
113 */
114 protected Vector<Track> tracks = new Vector<Track>();
115
116
117 /**
118 * Constructs a new MIDI sequence with the specified timing division
119 * type and timing resolution. The division type must be one of the
120 * recognized MIDI timing types. For tempo-based timing,
121 * <code>divisionType</code> is PPQ (pulses per quarter note) and
122 * the resolution is specified in ticks per beat. For SMTPE timing,
123 * <code>divisionType</code> specifies the number of frames per
124 * second and the resolution is specified in ticks per frame.
125 * The sequence will contain no initial tracks. Tracks may be
126 * added to or removed from the sequence using <code>{@link #createTrack}</code>
127 * and <code>{@link #deleteTrack}</code>.
128 *
129 * @param divisionType the timing division type (PPQ or one of the SMPTE types)
130 * @param resolution the timing resolution
131 * @throws InvalidMidiDataException if <code>divisionType</code> is not valid
132 *
133 * @see #PPQ
134 * @see #SMPTE_24
135 * @see #SMPTE_25
136 * @see #SMPTE_30DROP
137 * @see #SMPTE_30
138 * @see #getDivisionType
139 * @see #getResolution
140 * @see #getTracks
141 */
142 public Sequence(float divisionType, int resolution) throws InvalidMidiDataException {
143
144 if (divisionType == PPQ)
145 this.divisionType = PPQ;
146 else if (divisionType == SMPTE_24)
147 this.divisionType = SMPTE_24;
148 else if (divisionType == SMPTE_25)
149 this.divisionType = SMPTE_25;
150 else if (divisionType == SMPTE_30DROP)
151 this.divisionType = SMPTE_30DROP;
152 else if (divisionType == SMPTE_30)
153 this.divisionType = SMPTE_30;
154 else throw new InvalidMidiDataException("Unsupported division type: " + divisionType);
155
156 this.resolution = resolution;
157 }
158
159
160 /**
161 * Constructs a new MIDI sequence with the specified timing division
162 * type, timing resolution, and number of tracks. The division type must be one of the
163 * recognized MIDI timing types. For tempo-based timing,
164 * <code>divisionType</code> is PPQ (pulses per quarter note) and
165 * the resolution is specified in ticks per beat. For SMTPE timing,
166 * <code>divisionType</code> specifies the number of frames per
167 * second and the resolution is specified in ticks per frame.
168 * The sequence will be initialized with the number of tracks specified by
169 * <code>numTracks</code>. These tracks are initially empty (i.e.
170 * they contain only the meta-event End of Track).
171 * The tracks may be retrieved for editing using the <code>{@link #getTracks}</code>
172 * method. Additional tracks may be added, or existing tracks removed,
173 * using <code>{@link #createTrack}</code> and <code>{@link #deleteTrack}</code>.
174 *
175 * @param divisionType the timing division type (PPQ or one of the SMPTE types)
176 * @param resolution the timing resolution
177 * @param numTracks the initial number of tracks in the sequence.
178 * @throws InvalidMidiDataException if <code>divisionType</code> is not valid
179 *
180 * @see #PPQ
181 * @see #SMPTE_24
182 * @see #SMPTE_25
183 * @see #SMPTE_30DROP
184 * @see #SMPTE_30
185 * @see #getDivisionType
186 * @see #getResolution
187 */
188 public Sequence(float divisionType, int resolution, int numTracks) throws InvalidMidiDataException {
189
190 if (divisionType == PPQ)
191 this.divisionType = PPQ;
192 else if (divisionType == SMPTE_24)
193 this.divisionType = SMPTE_24;
194 else if (divisionType == SMPTE_25)
195 this.divisionType = SMPTE_25;
196 else if (divisionType == SMPTE_30DROP)
197 this.divisionType = SMPTE_30DROP;
198 else if (divisionType == SMPTE_30)
199 this.divisionType = SMPTE_30;
200 else throw new InvalidMidiDataException("Unsupported division type: " + divisionType);
201
202 this.resolution = resolution;
203
204 for (int i = 0; i < numTracks; i++) {
205 tracks.addElement(new Track());
206 }
207 }
208
209
210 /**
211 * Obtains the timing division type for this sequence.
212 * @return the division type (PPQ or one of the SMPTE types)
213 *
214 * @see #PPQ
215 * @see #SMPTE_24
216 * @see #SMPTE_25
217 * @see #SMPTE_30DROP
218 * @see #SMPTE_30
219 * @see #Sequence(float, int)
220 * @see MidiFileFormat#getDivisionType()
221 */
222 public float getDivisionType() {
223 return divisionType;
224 }
225
226
227 /**
228 * Obtains the timing resolution for this sequence.
229 * If the sequence's division type is PPQ, the resolution is specified in ticks per beat.
230 * For SMTPE timing, the resolution is specified in ticks per frame.
231 *
232 * @return the number of ticks per beat (PPQ) or per frame (SMPTE)
233 * @see #getDivisionType
234 * @see #Sequence(float, int)
235 * @see MidiFileFormat#getResolution()
236 */
237 public int getResolution() {
238 return resolution;
239 }
240
241
242 /**
243 * Creates a new, initially empty track as part of this sequence.
244 * The track initially contains the meta-event End of Track.
245 * The newly created track is returned. All tracks in the sequence
246 * may be retrieved using <code>{@link #getTracks}</code>. Tracks may be
247 * removed from the sequence using <code>{@link #deleteTrack}</code>.
248 * @return the newly created track
249 */
250 public Track createTrack() {
251
252 Track track = new Track();
253 tracks.addElement(track);
254
255 return track;
256 }
257
258
259 /**
260 * Removes the specified track from the sequence.
261 * @param track the track to remove
262 * @return <code>true</code> if the track existed in the track and was removed,
263 * otherwise <code>false</code>.
264 *
265 * @see #createTrack
266 * @see #getTracks
267 */
268 public boolean deleteTrack(Track track) {
269
270 synchronized(tracks) {
271
272 return tracks.removeElement(track);
273 }
274 }
275
276
277 /**
278 * Obtains an array containing all the tracks in this sequence.
279 * If the sequence contains no tracks, an array of length 0 is returned.
280 * @return the array of tracks
281 *
282 * @see #createTrack
283 * @see #deleteTrack
284 */
285 public Track[] getTracks() {
286
287 return (Track[]) tracks.toArray(new Track[tracks.size()]);
288 }
289
290
291 /**
292 * Obtains the duration of this sequence, expressed in microseconds.
293 * @return this sequence's duration in microseconds.
294 */
295 public long getMicrosecondLength() {
296
297 return com.sun.media.sound.MidiUtils.tick2microsecond(this, getTickLength(), null);
298 }
299
300
301 /**
302 * Obtains the duration of this sequence, expressed in MIDI ticks.
303 *
304 * @return this sequence's length in ticks
305 *
306 * @see #getMicrosecondLength
307 */
308 public long getTickLength() {
309
310 long length = 0;
311
312 synchronized(tracks) {
313
314 for(int i=0; i<tracks.size(); i++ ) {
315 long temp = ((Track)tracks.elementAt(i)).ticks();
316 if( temp>length ) {
317 length = temp;
318 }
319 }
320 return length;
321 }
322 }
323
324
325 /**
326 * Obtains a list of patches referenced in this sequence.
327 * This patch list may be used to load the required
328 * <code>{@link Instrument}</code> objects
329 * into a <code>{@link Synthesizer}</code>.
330 *
331 * @return an array of <code>{@link Patch}</code> objects used in this sequence
332 *
333 * @see Synthesizer#loadInstruments(Soundbank, Patch[])
334 */
335 public Patch[] getPatchList() {
336
337 // $$kk: 04.09.99: need to implement!!
338 return new Patch[0];
339 }
340 }