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

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

Apollo spin-off added

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