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