1. /*
  2. * @(#)Track.java 1.19 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 MIDI track is an independent stream of MIDI events (time-stamped MIDI
  11. * data) that can be stored along with other tracks in a standard MIDI file.
  12. * The MIDI specification allows only 16 channels of MIDI data, but tracks
  13. * are a way to get around this limitation. A MIDI file can contain any number
  14. * of tracks, each containing its own stream of up to 16 channels of MIDI data.
  15. * <p>
  16. * A <code>Track</code> occupies a middle level in the hierarchy of data played
  17. * by a <code>{@link Sequencer}</code>: sequencers play sequences, which contain tracks,
  18. * which contain MIDI events. A sequencer may provide controls that mute
  19. * or solo individual tracks.
  20. * <p>
  21. * The timing information and resolution for a track is controlled by and stored
  22. * in the sequence containing the track. A given <code>Track</code>
  23. * is considered to belong to the particular <code>{@link Sequence}</code> that
  24. * maintains its timing. For this reason, a new (empty) track is created by calling the
  25. * <code>{@link Sequence#createTrack}</code> method, rather than by directly invoking a
  26. * <code>Track</code> constructor.
  27. * <p>
  28. * The <code>Track</code> class provides methods to edit the track by adding
  29. * or removing <code>MidiEvent</code> objects from it. These operations keep
  30. * the event list in the correct time order. Methods are also
  31. * included to obtain the track's size, in terms of either the number of events
  32. * it contains or its duration in ticks.
  33. *
  34. * @see Sequencer#setTrackMute
  35. * @see Sequencer#setTrackSolo
  36. *
  37. * @version 1.19, 03/01/23
  38. * @author Kara Kytle
  39. */
  40. public class Track {
  41. /**
  42. * The list of <code>MidiEvents</code> contained in this track.
  43. */
  44. protected Vector events = new Vector();
  45. // $$fb 2002-07-17: use a hashset to detect same events in add(MidiEvent)
  46. // this requires at least JDK1.2
  47. private java.util.HashSet set = new java.util.HashSet();
  48. /**
  49. * Package-private constructor. Constructs a new, empty Track object,
  50. * which initially contains one event, the meta-event End of Track.
  51. */
  52. Track() {
  53. // $$jb: 10.18.99: start with the end of track event
  54. MetaMessage eot = new MetaMessage();
  55. try {
  56. eot.setMessage( 0x2F, new byte[0], 0 );
  57. } catch (InvalidMidiDataException e) {
  58. // this should never happen.
  59. }
  60. MidiEvent eotevent = new MidiEvent( eot, 0 );
  61. events.addElement( eotevent );
  62. set.add(eotevent);
  63. }
  64. /**
  65. * Adds a new event to the track. However, if the event is already
  66. * contained in the track, it is not added again. The list of events
  67. * is kept in time order, meaning that this event inserted at the
  68. * appropriate place in the list, not necessarily at the end.
  69. *
  70. * @param event the event to add
  71. * @return <code>true</code> if the event did not already exist in the
  72. * track and was added, otherwise <code>false</code>
  73. */
  74. public boolean add(MidiEvent event) {
  75. // $$kk: 01.21.99: i guess i will refuse to add the event if
  76. // it already exists in the event vector. otherwise people will
  77. // do event.setData(...) and add(event) and create a disaster.
  78. int i;
  79. synchronized(events) {
  80. if ( !set.contains(event) ) {
  81. // $$jb: 10.18.99: first see if we are trying to add
  82. // and endoftrack event. since this event is useful
  83. // for delays at the end of a track, we want to keep
  84. // the tick value requested here if it is greater
  85. // than the one on the eot we are maintaining. otherwise,
  86. // we only want a single eot event, so ignore.
  87. if( event.getMessage().getStatus() == 0xff ) {
  88. MetaMessage mm = (MetaMessage)(event.getMessage());
  89. if (mm.getType() == 0x2f) {
  90. MidiEvent eot = (MidiEvent) events.elementAt( events.size()-1 );
  91. if( event.getTick() > eot.getTick() ) {
  92. eot.setTick( event.getTick() );
  93. }
  94. return true;
  95. }
  96. }
  97. // insert event such that events is sorted in increasing
  98. // tick order
  99. set.add(event);
  100. if(events.size()==0) {
  101. events.addElement(event);
  102. return true;
  103. } else {
  104. for( i=events.size(); i > 0; i-- ) {
  105. if( event.getTick() >= ((MidiEvent)events.elementAt(i-1)).getTick() ) {
  106. break;
  107. }
  108. }
  109. if( i==events.size() ) {
  110. // $$jb: 10.18.99: we're adding an event after the
  111. // tick value of our eot, so push the eot out
  112. ((MidiEvent)events.elementAt(i-1)).setTick( event.getTick() );
  113. events.insertElementAt(event, (i-1) );
  114. } else {
  115. events.insertElementAt(event,i);
  116. }
  117. return true;
  118. }
  119. }
  120. }
  121. return false;
  122. }
  123. /**
  124. * Removes the specified event from the track.
  125. * @param event the event to remove
  126. * @return <code>true</code> if the event existed in the track and was removed,
  127. * otherwise <code>false</code>
  128. */
  129. public boolean remove(MidiEvent event) {
  130. synchronized(events) {
  131. set.remove(event);
  132. return events.removeElement(event);
  133. }
  134. }
  135. /**
  136. * Obtains the event at the specified index.
  137. * @param index the location of the desired event in the event vector
  138. * @throws <code>ArrayIndexOutOfBoundsException</code> if the
  139. * specified index is negative or not less than the current size of
  140. * this track.
  141. * @see #size
  142. */
  143. public MidiEvent get(int index) throws ArrayIndexOutOfBoundsException {
  144. return (MidiEvent)events.elementAt(index); // can throw ArrayIndexOutOfBoundsException
  145. }
  146. /**
  147. * Obtains the number of events in this track.
  148. * @return the size of the track's event vector
  149. */
  150. public int size() {
  151. return events.size();
  152. }
  153. /**
  154. * Obtains the length of the track, expressed in MIDI ticks. (The
  155. * duration of a tick in seconds is determined by the timing resolution
  156. * of the <code>Sequence</code> containing this track, and also by
  157. * the tempo of the music as set by the sequencer.)
  158. * @return the duration, in ticks
  159. * @see Sequence#Sequence(float, int)
  160. * @see Sequencer#setTempoInBPM(float)
  161. * @see Sequencer#getTickPosition()
  162. */
  163. public long ticks() {
  164. return ((MidiEvent)events.lastElement()).getTick();
  165. }
  166. }