Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/sound/midi/Track.java
38830 views
1
/*
2
* Copyright (c) 1999, 2013, 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
import java.util.ArrayList;
30
import java.util.HashSet;
31
import com.sun.media.sound.MidiUtils;
32
33
/**
34
* A MIDI track is an independent stream of MIDI events (time-stamped MIDI
35
* data) that can be stored along with other tracks in a standard MIDI file.
36
* The MIDI specification allows only 16 channels of MIDI data, but tracks
37
* are a way to get around this limitation. A MIDI file can contain any number
38
* of tracks, each containing its own stream of up to 16 channels of MIDI data.
39
* <p>
40
* A <code>Track</code> occupies a middle level in the hierarchy of data played
41
* by a <code>{@link Sequencer}</code>: sequencers play sequences, which contain tracks,
42
* which contain MIDI events. A sequencer may provide controls that mute
43
* or solo individual tracks.
44
* <p>
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>
47
* is considered to belong to the particular <code>{@link Sequence}</code> that
48
* maintains its timing. For this reason, a new (empty) track is created by calling the
49
* <code>{@link Sequence#createTrack}</code> method, rather than by directly invoking a
50
* <code>Track</code> constructor.
51
* <p>
52
* The <code>Track</code> class provides methods to edit the track by adding
53
* or removing <code>MidiEvent</code> objects from it. These operations keep
54
* the event list in the correct time order. Methods are also
55
* included to obtain the track's size, in terms of either the number of events
56
* it contains or its duration in ticks.
57
*
58
* @see Sequencer#setTrackMute
59
* @see Sequencer#setTrackSolo
60
*
61
* @author Kara Kytle
62
* @author Florian Bomers
63
*/
64
public class Track {
65
66
// TODO: use arrays for faster access
67
68
// the list containing the events
69
private ArrayList eventsList = new ArrayList();
70
71
// use a hashset to detect duplicate events in add(MidiEvent)
72
private HashSet set = new HashSet();
73
74
private MidiEvent eotEvent;
75
76
77
/**
78
* Package-private constructor. Constructs a new, empty Track object,
79
* which initially contains one event, the meta-event End of Track.
80
*/
81
Track() {
82
// start with the end of track event
83
MetaMessage eot = new ImmutableEndOfTrack();
84
eotEvent = new MidiEvent(eot, 0);
85
eventsList.add(eotEvent);
86
set.add(eotEvent);
87
}
88
89
/**
90
* Adds a new event to the track. However, if the event is already
91
* contained in the track, it is not added again. The list of events
92
* is kept in time order, meaning that this event inserted at the
93
* appropriate place in the list, not necessarily at the end.
94
*
95
* @param event the event to add
96
* @return <code>true</code> if the event did not already exist in the
97
* track and was added, otherwise <code>false</code>
98
*/
99
public boolean add(MidiEvent event) {
100
if (event == null) {
101
return false;
102
}
103
synchronized(eventsList) {
104
105
if (!set.contains(event)) {
106
int eventsCount = eventsList.size();
107
108
// get the last event
109
MidiEvent lastEvent = null;
110
if (eventsCount > 0) {
111
lastEvent = (MidiEvent) eventsList.get(eventsCount - 1);
112
}
113
// sanity check that we have a correct end-of-track
114
if (lastEvent != eotEvent) {
115
// if there is no eot event, add our immutable instance again
116
if (lastEvent != null) {
117
// set eotEvent's tick to the last tick of the track
118
eotEvent.setTick(lastEvent.getTick());
119
} else {
120
// if the events list is empty, just set the tick to 0
121
eotEvent.setTick(0);
122
}
123
// we needn't check for a duplicate of eotEvent in "eventsList",
124
// since then it would appear in the set.
125
eventsList.add(eotEvent);
126
set.add(eotEvent);
127
eventsCount = eventsList.size();
128
}
129
130
// first see if we are trying to add
131
// and endoftrack event.
132
if (MidiUtils.isMetaEndOfTrack(event.getMessage())) {
133
// since end of track event is useful
134
// for delays at the end of a track, we want to keep
135
// the tick value requested here if it is greater
136
// than the one on the eot we are maintaining.
137
// Otherwise, we only want a single eot event, so ignore.
138
if (event.getTick() > eotEvent.getTick()) {
139
eotEvent.setTick(event.getTick());
140
}
141
return true;
142
}
143
144
// prevent duplicates
145
set.add(event);
146
147
// insert event such that events is sorted in increasing
148
// tick order
149
int i = eventsCount;
150
for ( ; i > 0; i--) {
151
if (event.getTick() >= ((MidiEvent)eventsList.get(i-1)).getTick()) {
152
break;
153
}
154
}
155
if (i == eventsCount) {
156
// we're adding an event after the
157
// tick value of our eot, so push the eot out.
158
// Always add at the end for better performance:
159
// this saves all the checks and arraycopy when inserting
160
161
// overwrite eot with new event
162
eventsList.set(eventsCount - 1, event);
163
// set new time of eot, if necessary
164
if (eotEvent.getTick() < event.getTick()) {
165
eotEvent.setTick(event.getTick());
166
}
167
// add eot again at the end
168
eventsList.add(eotEvent);
169
} else {
170
eventsList.add(i, event);
171
}
172
return true;
173
}
174
}
175
176
return false;
177
}
178
179
180
/**
181
* Removes the specified event from the track.
182
* @param event the event to remove
183
* @return <code>true</code> if the event existed in the track and was removed,
184
* otherwise <code>false</code>
185
*/
186
public boolean remove(MidiEvent event) {
187
188
// this implementation allows removing the EOT event.
189
// pretty bad, but would probably be too risky to
190
// change behavior now, in case someone does tricks like:
191
//
192
// while (track.size() > 0) track.remove(track.get(track.size() - 1));
193
194
// also, would it make sense to adjust the EOT's time
195
// to the last event, if the last non-EOT event is removed?
196
// Or: document that the ticks() length will not be reduced
197
// by deleting events (unless the EOT event is removed)
198
synchronized(eventsList) {
199
if (set.remove(event)) {
200
int i = eventsList.indexOf(event);
201
if (i >= 0) {
202
eventsList.remove(i);
203
return true;
204
}
205
}
206
}
207
return false;
208
}
209
210
211
/**
212
* Obtains the event at the specified index.
213
* @param index the location of the desired event in the event vector
214
* @throws ArrayIndexOutOfBoundsException if the
215
* specified index is negative or not less than the current size of
216
* this track.
217
* @see #size
218
* @return the event at the specified index
219
*/
220
public MidiEvent get(int index) throws ArrayIndexOutOfBoundsException {
221
try {
222
synchronized(eventsList) {
223
return (MidiEvent)eventsList.get(index);
224
}
225
} catch (IndexOutOfBoundsException ioobe) {
226
throw new ArrayIndexOutOfBoundsException(ioobe.getMessage());
227
}
228
}
229
230
231
/**
232
* Obtains the number of events in this track.
233
* @return the size of the track's event vector
234
*/
235
public int size() {
236
synchronized(eventsList) {
237
return eventsList.size();
238
}
239
}
240
241
242
/**
243
* Obtains the length of the track, expressed in MIDI ticks. (The
244
* duration of a tick in seconds is determined by the timing resolution
245
* of the <code>Sequence</code> containing this track, and also by
246
* the tempo of the music as set by the sequencer.)
247
* @return the duration, in ticks
248
* @see Sequence#Sequence(float, int)
249
* @see Sequencer#setTempoInBPM(float)
250
* @see Sequencer#getTickPosition()
251
*/
252
public long ticks() {
253
long ret = 0;
254
synchronized (eventsList) {
255
if (eventsList.size() > 0) {
256
ret = ((MidiEvent)eventsList.get(eventsList.size() - 1)).getTick();
257
}
258
}
259
return ret;
260
}
261
262
private static class ImmutableEndOfTrack extends MetaMessage {
263
private ImmutableEndOfTrack() {
264
super(new byte[3]);
265
data[0] = (byte) META;
266
data[1] = MidiUtils.META_END_OF_TRACK_TYPE;
267
data[2] = 0;
268
}
269
270
public void setMessage(int type, byte[] data, int length) throws InvalidMidiDataException {
271
throw new InvalidMidiDataException("cannot modify end of track message");
272
}
273
}
274
275
}
276
277