source: trunk/src_apollo/org/apollo/audio/TrackSequence.java@ 365

Last change on this file since 365 was 365, checked in by bjn8, 16 years ago

Added accurate position of tracks when anchored in groups

File size: 8.4 KB
Line 
1package org.apollo.audio;
2
3import javax.sound.sampled.AudioFormat;
4
5import org.apollo.mvc.AbstractSubject;
6import org.apollo.mvc.SubjectChangedEvent;
7
8/**
9 *
10 * A TrackSequence is a track which can reside in a track graph... for playing back at
11 * a scheduled time and mixing with other tracks in the graph.
12 *
13 * TrackSequence's are immutable (outside package). They can only be played once.
14 *
15 * @author Brook Novak
16 *
17 */
18public final class TrackSequence extends AbstractSubject implements Comparable {
19
20 /** FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER. */
21 int currentFrame; // buffer position. Not live.
22
23 private int startFrame; // within audio
24
25 /** The frame at which this track sequence was suspended at (in the audio mixers live timeline) */
26 private int suspendedFrame;
27
28 /** The frame at which this track began at (in the audio mixers live timeline) */
29 long commencedFrame;
30
31 /** FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER. */
32 int endFrame; // inclusive
33
34 private int relativeInitiationFrame;
35
36 /** FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER. */
37 long initiationFrame; // used in pipeline. Absolute. CAn be negitive
38
39 /** FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER. */
40 boolean isBigEndian;
41
42 // Not copied, Arrays are immutable (although contents are not!).
43 /** FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER. */
44 byte[] playbackAudioBytes; // snapshot of track bytes, avoid editing of model audio bytes while playing
45
46 private boolean isPlaying;
47
48 float volume; // friendly - for fast access in audio mixing pipeline, ... and carefully by the track model
49
50 boolean isMuted; // friendly - for fast access in audio mixing pipeline ... and carefully by the track model
51
52 boolean isSolo; // friendly - for fast access in audio mixing pipeline ...
53
54 private Object invoker;
55
56 /** FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER. */
57 boolean stopPending;
58
59 /**
60 * Prepares track sequence for playing. Must not be in a playing state.
61 *
62 * @param playbackAudioBytes
63 * The audio bytes to play.
64 *
65 * @param format
66 * The format of the audio bytes.
67 *
68 * @param startFrame
69 * The frame <i>within the track</i> where to start playback. Inclusive.
70 *
71 * @param endFrame
72 * The frame <i>within the track</i> where to end playback. Inclusive.
73 *
74 * @param relativeInitiationFrame
75 * Where to initiate the frame. This is relative to where the track should start playing
76 * in frames, when queued in the track graph.
77 *
78 * @param invoker
79 * A state object that may be of use.
80 *
81 * @throws IllegalArgumentException
82 * If startFrame >= endFrame. Or startFrame is not in audio range. or end frame is not in audio range
83 *
84 * @throws NullPointerException
85 * If audioBytes or format is null.
86 *
87 */
88 public TrackSequence(byte[] playbackAudioBytes,
89 AudioFormat format,
90 int startFrame,
91 int endFrame,
92 int relativeInitiationFrame,
93 Object invoker) {
94
95 if (startFrame >= endFrame)
96 throw new IllegalArgumentException("startFrame >= endFrame");
97
98 if (format == null)
99 throw new NullPointerException("format");
100
101 if (playbackAudioBytes == null)
102 throw new NullPointerException("audioBytes");
103
104 assert(SampledAudioManager.getInstance().isFormatSupportedForPlayback(format));
105
106 this.startFrame = this.currentFrame = startFrame;
107 this.endFrame = endFrame;
108 this.relativeInitiationFrame = relativeInitiationFrame;
109 this.initiationFrame = -1;
110 this.suspendedFrame = -1;
111 this.commencedFrame = -1;
112 this.isBigEndian = format.isBigEndian();
113 this.playbackAudioBytes = playbackAudioBytes;
114 this.isPlaying = false;
115 this.invoker = invoker;
116 this.stopPending = false;
117
118 assert (currentFrame >= 0 && currentFrame < (playbackAudioBytes.length / format.getFrameSize()));
119 assert (endFrame > currentFrame && endFrame < (playbackAudioBytes.length / format.getFrameSize()));
120
121 }
122
123 /**
124 * Prepares track sequence for playing. Must not be in a playing state.
125 *
126 * @param playbackAudioBytes
127 * The audio bytes to play.
128 *
129 * @param format
130 * The format of the audio bytes.
131 *
132 * @param startFrame
133 * The frame <i>within the track</i> where to start playback. Inclusive.
134 *
135 * @param endFrame
136 * The frame <i>within the track</i> where to end playback. Inclusive.
137 *
138 * @param relativeInitiationFrame
139 * Where to initiate the frame. This is relative to where the track should start playing
140 * in frames, when queued in the track graph.
141 *
142 * @throws IllegalArgumentException
143 * If startFrame >= endFrame. Or startFrame is not in audio range. or end frame is not in audio range
144 *
145 * @throws NullPointerException
146 * If audioBytes or format is null.
147 *
148 */
149 TrackSequence(
150 byte[] playbackAudioBytes,
151 AudioFormat format,
152 int startFrame,
153 int endFrame,
154 int relativeInitiationFrame) {
155 this(playbackAudioBytes, format, startFrame, endFrame, relativeInitiationFrame, null);
156 }
157
158 /**
159 * Order ascending by initiation frame
160 */
161 public final int compareTo(Object o) {
162 TrackSequence other = (TrackSequence)o; // throws class cast exception
163
164 int cmp = (new Long(initiationFrame)).compareTo(other.initiationFrame);
165
166 if (cmp == 0) {
167
168 return (new Integer(hashCode()).compareTo(other.hashCode()));
169 }
170
171 return cmp;
172 }
173
174 /**
175 * @return
176 * Where the track initiates/initiated. This is relative to where the track should start playing
177 * in frames, when queued in the track graph.
178 */
179 public int getRelativeInitiationFrame() {
180 return relativeInitiationFrame;
181 }
182
183 /**
184 * @return
185 * The frame position of where in the track the bytes have been mixed and buffered - not nessessarily
186 * been played back. This is not its live playback position.
187 */
188 public int getCurrentFrame() {
189 return currentFrame;
190 }
191
192 /**
193 * @return
194 * The last rendered frame when this was stopped.
195 * Negitive if not played and stopped yet.
196 */
197 public int getSuspendedFrame() {
198 return suspendedFrame;
199 }
200
201 /**
202 * FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER.
203 * Fires playback started event - later on swing thread.
204 */
205 final void onInitiated(long commencedFrame) {
206 this.commencedFrame = commencedFrame;
207 isPlaying = true;
208 fireSubjectChangedLaterOnSwingThread(
209 new SubjectChangedEvent(
210 ApolloSubjectChangedEvent.PLAYBACK_STARTED));
211 }
212
213 /**
214 * FRIENDLY: ONLY TO BE ACCESSED BY AUDIO MIXER.
215 * Fires playback stopped event - later on swing thread.
216 *
217 * @param suspendedFramePosition
218 * Must be in range of start frame and end frame (inclusive)
219 */
220 final void onStopped(int suspendedFramePosition) {
221 isPlaying = false;
222
223 assert(suspendedFramePosition >= startFrame);
224 assert(suspendedFramePosition <= endFrame);
225
226 this.suspendedFrame = suspendedFramePosition;
227
228 // Dispose of audio bytes
229 playbackAudioBytes = null;
230
231 fireSubjectChangedLaterOnSwingThread(
232 new SubjectChangedEvent(
233 ApolloSubjectChangedEvent.PLAYBACK_STOPPED));
234
235 }
236
237 /**
238 * @return
239 * true if the track is playing
240 */
241 public boolean isPlaying() {
242 return isPlaying;
243 }
244
245 /**
246 *
247 * @return
248 * True if this sequence has been played before.
249 *
250 */
251 public boolean hasFinished() {
252 return (playbackAudioBytes == null);
253 }
254
255 /**
256 * @return
257 * The object which invoked this track sequence. Can be null.
258 * Only used externally for 3rd party benifits.
259 */
260 public Object getInvoker() {
261 return invoker;
262 }
263
264 /**
265 * Note that the track won't solo unless the mixer is in solo mode.
266 *
267 * @param isSolo
268 * True to set this track as a solo subject.
269 */
270 public void setSoloFlag(boolean isSolo) {
271 this.isSolo = isSolo;
272 }
273
274 public boolean isMuted() {
275 return isMuted;
276 }
277
278 public void setMuted(boolean isMuted) {
279 this.isMuted = isMuted;
280 }
281
282 public float getVolume() {
283 return volume;
284 }
285
286 public void setVolume(float volume) {
287 // Clamp volume argument
288 if (volume < 0.0f) volume = 0.0f;
289 else if (volume > 1.0f) volume = 1.0f;
290 this.volume = volume;
291 }
292
293 /**
294 * @return
295 * The absolute frame when this track is to / was played in the software mixers timeline.
296 */
297 public long getInitiationFrame() {
298 return initiationFrame;
299 }
300
301 /**
302 * @return
303 * The frame at which this track began at (in the audio mixers live timeline)
304 */
305 public long getCommencedFrame() {
306 return commencedFrame;
307 }
308
309 /**
310 *
311 * @return
312 * The start frame <i>within the track</i> at which this track sequence starts / started
313 */
314 public int getStartFrame() {
315 return startFrame;
316 }
317
318 public int getEndFrame() {
319 return endFrame;
320 }
321
322
323
324
325
326
327}
Note: See TracBrowser for help on using the repository browser.