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