23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package javax.sound.midi; |
26 package javax.sound.midi; |
27 |
27 |
28 import java.util.Vector; |
|
29 import java.util.ArrayList; |
28 import java.util.ArrayList; |
30 import java.util.HashSet; |
29 import java.util.HashSet; |
|
30 |
31 import com.sun.media.sound.MidiUtils; |
31 import com.sun.media.sound.MidiUtils; |
32 |
32 |
33 /** |
33 /** |
34 * A MIDI track is an independent stream of MIDI events (time-stamped MIDI |
34 * A MIDI track is an independent stream of MIDI events (time-stamped MIDI data) |
35 * data) that can be stored along with other tracks in a standard MIDI file. |
35 * that can be stored along with other tracks in a standard MIDI file. The MIDI |
36 * The MIDI specification allows only 16 channels of MIDI data, but tracks |
36 * specification allows only 16 channels of MIDI data, but tracks are a way to |
37 * are a way to get around this limitation. A MIDI file can contain any number |
37 * get around this limitation. A MIDI file can contain any number of tracks, |
38 * of tracks, each containing its own stream of up to 16 channels of MIDI data. |
38 * each containing its own stream of up to 16 channels of MIDI data. |
39 * <p> |
39 * <p> |
40 * A <code>Track</code> occupies a middle level in the hierarchy of data played |
40 * A {@code Track} occupies a middle level in the hierarchy of data played by a |
41 * by a <code>{@link Sequencer}</code>: sequencers play sequences, which contain tracks, |
41 * {@link Sequencer}: sequencers play sequences, which contain tracks, which |
42 * which contain MIDI events. A sequencer may provide controls that mute |
42 * contain MIDI events. A sequencer may provide controls that mute or solo |
43 * or solo individual tracks. |
43 * individual tracks. |
44 * <p> |
44 * <p> |
45 * The timing information and resolution for a track is controlled by and stored |
45 * The timing information and resolution for a track is controlled by and stored |
46 * in the sequence containing the track. A given <code>Track</code> |
46 * in the sequence containing the track. A given {@code Track} is considered to |
47 * is considered to belong to the particular <code>{@link Sequence}</code> that |
47 * belong to the particular {@link Sequence} that maintains its timing. For this |
48 * maintains its timing. For this reason, a new (empty) track is created by calling the |
48 * reason, a new (empty) track is created by calling the |
49 * <code>{@link Sequence#createTrack}</code> method, rather than by directly invoking a |
49 * {@link Sequence#createTrack} method, rather than by directly invoking a |
50 * <code>Track</code> constructor. |
50 * {@code Track} constructor. |
51 * <p> |
51 * <p> |
52 * The <code>Track</code> class provides methods to edit the track by adding |
52 * The {@code Track} class provides methods to edit the track by adding or |
53 * or removing <code>MidiEvent</code> objects from it. These operations keep |
53 * removing {@code MidiEvent} objects from it. These operations keep the event |
54 * the event list in the correct time order. Methods are also |
54 * list in the correct time order. Methods are also included to obtain the |
55 * included to obtain the track's size, in terms of either the number of events |
55 * track's size, in terms of either the number of events it contains or its |
56 * it contains or its duration in ticks. |
56 * duration in ticks. |
57 * |
57 * |
|
58 * @author Kara Kytle |
|
59 * @author Florian Bomers |
58 * @see Sequencer#setTrackMute |
60 * @see Sequencer#setTrackMute |
59 * @see Sequencer#setTrackSolo |
61 * @see Sequencer#setTrackSolo |
60 * |
|
61 * @author Kara Kytle |
|
62 * @author Florian Bomers |
|
63 */ |
62 */ |
64 public class Track { |
63 public class Track { |
65 |
64 |
66 // TODO: use arrays for faster access |
65 // TODO: use arrays for faster access |
67 |
66 |
71 // use a hashset to detect duplicate events in add(MidiEvent) |
70 // use a hashset to detect duplicate events in add(MidiEvent) |
72 private HashSet<MidiEvent> set = new HashSet<>(); |
71 private HashSet<MidiEvent> set = new HashSet<>(); |
73 |
72 |
74 private MidiEvent eotEvent; |
73 private MidiEvent eotEvent; |
75 |
74 |
76 |
75 /** |
77 /** |
76 * Package-private constructor. Constructs a new, empty Track object, which |
78 * Package-private constructor. Constructs a new, empty Track object, |
77 * initially contains one event, the meta-event End of Track. |
79 * which initially contains one event, the meta-event End of Track. |
|
80 */ |
78 */ |
81 Track() { |
79 Track() { |
82 // start with the end of track event |
80 // start with the end of track event |
83 MetaMessage eot = new ImmutableEndOfTrack(); |
81 MetaMessage eot = new ImmutableEndOfTrack(); |
84 eotEvent = new MidiEvent(eot, 0); |
82 eotEvent = new MidiEvent(eot, 0); |
85 eventsList.add(eotEvent); |
83 eventsList.add(eotEvent); |
86 set.add(eotEvent); |
84 set.add(eotEvent); |
87 } |
85 } |
88 |
86 |
89 /** |
87 /** |
90 * Adds a new event to the track. However, if the event is already |
88 * Adds a new event to the track. However, if the event is already contained |
91 * contained in the track, it is not added again. The list of events |
89 * in the track, it is not added again. The list of events is kept in time |
92 * is kept in time order, meaning that this event inserted at the |
90 * order, meaning that this event inserted at the appropriate place in the |
93 * appropriate place in the list, not necessarily at the end. |
91 * list, not necessarily at the end. |
94 * |
92 * |
95 * @param event the event to add |
93 * @param event the event to add |
96 * @return <code>true</code> if the event did not already exist in the |
94 * @return {@code true} if the event did not already exist in the track and |
97 * track and was added, otherwise <code>false</code> |
95 * was added, otherwise {@code false} |
98 */ |
96 */ |
99 public boolean add(MidiEvent event) { |
97 public boolean add(MidiEvent event) { |
100 if (event == null) { |
98 if (event == null) { |
101 return false; |
99 return false; |
102 } |
100 } |
174 } |
172 } |
175 |
173 |
176 return false; |
174 return false; |
177 } |
175 } |
178 |
176 |
179 |
|
180 /** |
177 /** |
181 * Removes the specified event from the track. |
178 * Removes the specified event from the track. |
182 * @param event the event to remove |
179 * |
183 * @return <code>true</code> if the event existed in the track and was removed, |
180 * @param event the event to remove |
184 * otherwise <code>false</code> |
181 * @return {@code true} if the event existed in the track and was removed, |
|
182 * otherwise {@code false} |
185 */ |
183 */ |
186 public boolean remove(MidiEvent event) { |
184 public boolean remove(MidiEvent event) { |
187 |
185 |
188 // this implementation allows removing the EOT event. |
186 // this implementation allows removing the EOT event. |
189 // pretty bad, but would probably be too risky to |
187 // pretty bad, but would probably be too risky to |
205 } |
203 } |
206 } |
204 } |
207 return false; |
205 return false; |
208 } |
206 } |
209 |
207 |
210 |
|
211 /** |
208 /** |
212 * Obtains the event at the specified index. |
209 * Obtains the event at the specified index. |
213 * @param index the location of the desired event in the event vector |
210 * |
214 * @throws ArrayIndexOutOfBoundsException if the |
211 * @param index the location of the desired event in the event vector |
215 * specified index is negative or not less than the current size of |
212 * @return the event at the specified index |
216 * this track. |
213 * @throws ArrayIndexOutOfBoundsException if the specified index is negative |
|
214 * or not less than the current size of this track |
217 * @see #size |
215 * @see #size |
218 * @return the event at the specified index |
|
219 */ |
216 */ |
220 public MidiEvent get(int index) throws ArrayIndexOutOfBoundsException { |
217 public MidiEvent get(int index) throws ArrayIndexOutOfBoundsException { |
221 try { |
218 try { |
222 synchronized(eventsList) { |
219 synchronized(eventsList) { |
223 return eventsList.get(index); |
220 return eventsList.get(index); |
225 } catch (IndexOutOfBoundsException ioobe) { |
222 } catch (IndexOutOfBoundsException ioobe) { |
226 throw new ArrayIndexOutOfBoundsException(ioobe.getMessage()); |
223 throw new ArrayIndexOutOfBoundsException(ioobe.getMessage()); |
227 } |
224 } |
228 } |
225 } |
229 |
226 |
230 |
|
231 /** |
227 /** |
232 * Obtains the number of events in this track. |
228 * Obtains the number of events in this track. |
|
229 * |
233 * @return the size of the track's event vector |
230 * @return the size of the track's event vector |
234 */ |
231 */ |
235 public int size() { |
232 public int size() { |
236 synchronized(eventsList) { |
233 synchronized(eventsList) { |
237 return eventsList.size(); |
234 return eventsList.size(); |
238 } |
235 } |
239 } |
236 } |
240 |
237 |
241 |
238 /** |
242 /** |
239 * Obtains the length of the track, expressed in MIDI ticks. (The duration |
243 * Obtains the length of the track, expressed in MIDI ticks. (The |
240 * of a tick in seconds is determined by the timing resolution of the |
244 * duration of a tick in seconds is determined by the timing resolution |
241 * {@code Sequence} containing this track, and also by the tempo of the |
245 * of the <code>Sequence</code> containing this track, and also by |
242 * music as set by the sequencer.) |
246 * the tempo of the music as set by the sequencer.) |
243 * |
247 * @return the duration, in ticks |
244 * @return the duration, in ticks |
248 * @see Sequence#Sequence(float, int) |
245 * @see Sequence#Sequence(float, int) |
249 * @see Sequencer#setTempoInBPM(float) |
246 * @see Sequencer#setTempoInBPM(float) |
250 * @see Sequencer#getTickPosition() |
247 * @see Sequencer#getTickPosition() |
251 */ |
248 */ |