1 | package org.apollo.audio.structure;
|
---|
2 |
|
---|
3 | import java.util.ArrayList;
|
---|
4 | import java.util.Collection;
|
---|
5 | import java.util.Collections;
|
---|
6 | import java.util.HashMap;
|
---|
7 | import java.util.HashSet;
|
---|
8 | import java.util.LinkedList;
|
---|
9 | import java.util.List;
|
---|
10 | import java.util.Map;
|
---|
11 | import java.util.Set;
|
---|
12 | import java.util.Stack;
|
---|
13 |
|
---|
14 | import org.apollo.audio.util.SoundDesk;
|
---|
15 | import org.apollo.mvc.AbstractSubject;
|
---|
16 |
|
---|
17 | /**
|
---|
18 | * Immutable outside package.
|
---|
19 | *
|
---|
20 | * @see {@link AudioStructureModel} for thread safe convention.
|
---|
21 | *
|
---|
22 | * @author Brook Novak
|
---|
23 | *
|
---|
24 | */
|
---|
25 | public class OverdubbedFrame extends AbstractSubject {
|
---|
26 |
|
---|
27 | private String frameName; // immutable
|
---|
28 |
|
---|
29 | private Set<TrackGraphNode> tracks = new HashSet<TrackGraphNode>();
|
---|
30 |
|
---|
31 | private Set<LinkedTracksGraphNode> linkedTracks = new HashSet<LinkedTracksGraphNode>();
|
---|
32 |
|
---|
33 | /**
|
---|
34 | *
|
---|
35 | * @param frameName
|
---|
36 | *
|
---|
37 | * @throws NullPointerException
|
---|
38 | * if frameName is null.
|
---|
39 | *
|
---|
40 | * @throws IllegalArgumentException
|
---|
41 | * if frameName is empty.
|
---|
42 | */
|
---|
43 | OverdubbedFrame(String frameName) {
|
---|
44 | if (frameName == null) throw new NullPointerException("frameName");
|
---|
45 | else if (frameName.length() == 0) throw new IllegalArgumentException("frameName.length() == 0");
|
---|
46 |
|
---|
47 | this.frameName = frameName;
|
---|
48 | }
|
---|
49 |
|
---|
50 | /**
|
---|
51 | * @return
|
---|
52 | * The immutable framename for this overdubbed frame.
|
---|
53 | * Never null / empty.
|
---|
54 | */
|
---|
55 | public String getFrameName() {
|
---|
56 | return frameName;
|
---|
57 | }
|
---|
58 |
|
---|
59 | /**
|
---|
60 | * @return
|
---|
61 | * A copy of the linked tracks. Never null.
|
---|
62 | * All unique (from a set)
|
---|
63 | */
|
---|
64 | public ArrayList<LinkedTracksGraphNode> getLinkedTracksCopy() {
|
---|
65 | return new ArrayList<LinkedTracksGraphNode>(linkedTracks);
|
---|
66 | }
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * @return
|
---|
70 | * An unmodifiable set of all the linked tracks. Never null.
|
---|
71 | */
|
---|
72 | public Set<LinkedTracksGraphNode> getUnmodifiableLinkedTracks() {
|
---|
73 | return Collections.unmodifiableSet(linkedTracks);
|
---|
74 | }
|
---|
75 |
|
---|
76 | /**
|
---|
77 | * @return
|
---|
78 | * A copy of the tracks. Never null.
|
---|
79 | * All unique (from a set)
|
---|
80 | */
|
---|
81 | public ArrayList<TrackGraphNode> getTracksCopy() {
|
---|
82 | return new ArrayList<TrackGraphNode>(tracks);
|
---|
83 | }
|
---|
84 |
|
---|
85 | /**
|
---|
86 | * @return
|
---|
87 | * An unmodifiable set of all the tracks. Never null.
|
---|
88 | */
|
---|
89 | public Set<TrackGraphNode> getUnmodifiableTracks() {
|
---|
90 | return Collections.unmodifiableSet(tracks);
|
---|
91 | }
|
---|
92 |
|
---|
93 | /**
|
---|
94 | * Must be on swing thread.
|
---|
95 | *
|
---|
96 | * @param localFilename
|
---|
97 | * The track to get. Must not be null.
|
---|
98 | *
|
---|
99 | * @return
|
---|
100 | * The track with the given local filename. Null if does not exist.
|
---|
101 | *
|
---|
102 | * @throws NullPointerException
|
---|
103 | * If localFilename is null.
|
---|
104 | */
|
---|
105 | public TrackGraphNode getTrack(String localFilename) {
|
---|
106 | if (localFilename == null) throw new NullPointerException("localFilename");
|
---|
107 |
|
---|
108 | for (TrackGraphNode track : tracks) {
|
---|
109 | if (track.getLocalFilename().equals(localFilename)) {
|
---|
110 | return track;
|
---|
111 | }
|
---|
112 | }
|
---|
113 |
|
---|
114 | return null;
|
---|
115 | }
|
---|
116 |
|
---|
117 | /**
|
---|
118 | * Must be on swing thread.
|
---|
119 | *
|
---|
120 | * @param virtualFilename
|
---|
121 | * The linked track to get. Must not be null.
|
---|
122 | *
|
---|
123 | * @return
|
---|
124 | * The linked track with the given virtual filename. Null if does not exist.
|
---|
125 | *
|
---|
126 | * @throws NullPointerException
|
---|
127 | * If virtualFilename is null.
|
---|
128 | */
|
---|
129 | public LinkedTracksGraphNode getLinkedTrack(String virtualFilename) {
|
---|
130 | if (virtualFilename == null) throw new NullPointerException("virtualFilename");
|
---|
131 |
|
---|
132 | for (LinkedTracksGraphNode ltrack : linkedTracks) {
|
---|
133 | if (ltrack.getVirtualFilename().equals(virtualFilename)) {
|
---|
134 | return ltrack;
|
---|
135 | }
|
---|
136 | }
|
---|
137 |
|
---|
138 | return null;
|
---|
139 | }
|
---|
140 |
|
---|
141 |
|
---|
142 | /**
|
---|
143 | * Must be on swing thread.
|
---|
144 | * Note: Only a shallow check.
|
---|
145 | *
|
---|
146 | * @param localFilename
|
---|
147 | * The track to check for. Must not be null.
|
---|
148 | *
|
---|
149 | * @return
|
---|
150 | * True if this OverdubbedFrame owns the track. False otherwise.
|
---|
151 | *
|
---|
152 | * @throws NullPointerException
|
---|
153 | * If localFilename is null.
|
---|
154 | */
|
---|
155 | public boolean containsTrack(String localFilename) {
|
---|
156 | if (localFilename == null) throw new NullPointerException("localFilename");
|
---|
157 |
|
---|
158 | for (TrackGraphNode track : tracks) {
|
---|
159 | if (track.getLocalFilename().equals(localFilename)) {
|
---|
160 | return true;
|
---|
161 | }
|
---|
162 | }
|
---|
163 |
|
---|
164 | return false;
|
---|
165 | }
|
---|
166 |
|
---|
167 | /**
|
---|
168 | * Must be on swing thread.
|
---|
169 | * Note: Only a shallow check.
|
---|
170 | *
|
---|
171 | * @param virtualFilename
|
---|
172 | * The linked track to check for. Must not be null.
|
---|
173 | *
|
---|
174 | * @return
|
---|
175 | * True if this OverdubbedFrame owns the linked track. False otherwise.
|
---|
176 | *
|
---|
177 | * @throws NullPointerException
|
---|
178 | * If virtualFilename is null.
|
---|
179 | */
|
---|
180 | public boolean containsLinkedTrack(String virtualFilename) {
|
---|
181 | if (virtualFilename == null) throw new NullPointerException("virtualFilename");
|
---|
182 |
|
---|
183 | for (LinkedTracksGraphNode ltrack : linkedTracks) {
|
---|
184 | if (ltrack.getVirtualFilename().equals(virtualFilename)) {
|
---|
185 | return true;
|
---|
186 | }
|
---|
187 | }
|
---|
188 |
|
---|
189 | return false;
|
---|
190 | }
|
---|
191 |
|
---|
192 | void addTrack(TrackGraphNode track) {
|
---|
193 | assert(track != null);
|
---|
194 | tracks.add(track);
|
---|
195 | }
|
---|
196 |
|
---|
197 | void addAllTracks(Collection<TrackGraphNode> track) {
|
---|
198 | assert(track != null);
|
---|
199 | tracks.addAll(track);
|
---|
200 | }
|
---|
201 |
|
---|
202 | void addLinkedTrack(LinkedTracksGraphNode linkedTrack) {
|
---|
203 | assert(linkedTrack != null);
|
---|
204 | linkedTracks.add(linkedTrack);
|
---|
205 | }
|
---|
206 |
|
---|
207 | void addAllLinkedTracks(Collection<LinkedTracksGraphNode> linkedTracks) {
|
---|
208 | assert(linkedTracks != null);
|
---|
209 | linkedTracks.addAll(linkedTracks);
|
---|
210 | }
|
---|
211 |
|
---|
212 | boolean removeTrack(TrackGraphNode track) {
|
---|
213 | assert(track != null);
|
---|
214 | return tracks.remove(track);
|
---|
215 | }
|
---|
216 |
|
---|
217 | TrackGraphNode removeTrack(String localFilename) {
|
---|
218 | assert(localFilename != null);
|
---|
219 |
|
---|
220 | TrackGraphNode track = getTrack(localFilename);
|
---|
221 |
|
---|
222 | if (track != null && tracks.remove(track)) return track;
|
---|
223 |
|
---|
224 | return null;
|
---|
225 | }
|
---|
226 |
|
---|
227 | boolean removeLinkedTrack(LinkedTracksGraphNode ltrack) {
|
---|
228 | assert(ltrack != null);
|
---|
229 | return linkedTracks.remove(ltrack);
|
---|
230 | }
|
---|
231 |
|
---|
232 | /**
|
---|
233 | * @see #trackExistsDeep
|
---|
234 | *
|
---|
235 | * @return
|
---|
236 | * True is there is no tracks or linked tracks.
|
---|
237 | */
|
---|
238 | public boolean isEmpty() {
|
---|
239 |
|
---|
240 | return linkedTracks.isEmpty() && tracks.isEmpty();
|
---|
241 | }
|
---|
242 |
|
---|
243 | /**
|
---|
244 | * Checks if there are any tracks on the frame
|
---|
245 | *
|
---|
246 | * @see #trackExistsDeep()
|
---|
247 | *
|
---|
248 | * @return
|
---|
249 | * True if there exists a track (NOT LINKED-TRACK) in this frame
|
---|
250 | */
|
---|
251 | public boolean trackExists() {
|
---|
252 | return !tracks.isEmpty();
|
---|
253 | }
|
---|
254 |
|
---|
255 | /**
|
---|
256 | * Intention: to see if this overdub frame contains anything to play.
|
---|
257 | *
|
---|
258 | * @return
|
---|
259 | * True if there exists a track (NOT LINKED-TRACK) in this frame or its
|
---|
260 | * descendant frames.
|
---|
261 | */
|
---|
262 | public boolean trackExistsDeep() {
|
---|
263 | return doesTrackExist(new HashSet<OverdubbedFrame>());
|
---|
264 | }
|
---|
265 |
|
---|
266 | private boolean doesTrackExist(Set<OverdubbedFrame> visited) {
|
---|
267 | if (!tracks.isEmpty()) return true;
|
---|
268 | else if (visited.contains(this)) return false;
|
---|
269 |
|
---|
270 | // Remember this node
|
---|
271 | visited.add(this);
|
---|
272 |
|
---|
273 | // Recurse step
|
---|
274 | for (LinkedTracksGraphNode linkedTrack : linkedTracks) {
|
---|
275 | if (linkedTrack.getLinkedFrame().doesTrackExist(visited))
|
---|
276 | return true;
|
---|
277 | }
|
---|
278 |
|
---|
279 | return false;
|
---|
280 | }
|
---|
281 |
|
---|
282 | /**
|
---|
283 | * Gets a child frame with the given name... if exists / is reachable from this node.
|
---|
284 | *
|
---|
285 | * @param targetFrameName
|
---|
286 | * THe target frame to get.
|
---|
287 | *
|
---|
288 | * @throws NullPointerException
|
---|
289 | * If targetFrameName is null.
|
---|
290 | *
|
---|
291 | * @return
|
---|
292 | * The target OverdubbedFrame. Null if none existed ...
|
---|
293 | */
|
---|
294 | public OverdubbedFrame getChild(String targetFrameName) {
|
---|
295 | return getChild(targetFrameName, new HashSet<OverdubbedFrame>());
|
---|
296 | }
|
---|
297 |
|
---|
298 | private OverdubbedFrame getChild(String targetFrameName, Set<OverdubbedFrame> visited) {
|
---|
299 | if(targetFrameName == null) throw new NullPointerException("targetFrameName");
|
---|
300 |
|
---|
301 | // Base cases
|
---|
302 | if (targetFrameName.equalsIgnoreCase(frameName)) return this;
|
---|
303 | else if (visited.contains(this)) return null;
|
---|
304 |
|
---|
305 | // Remember this node
|
---|
306 | visited.add(this);
|
---|
307 |
|
---|
308 | // Recurse step
|
---|
309 | for (LinkedTracksGraphNode linkedTrack : linkedTracks) {
|
---|
310 | OverdubbedFrame odFrame = linkedTrack.getLinkedFrame().getChild(targetFrameName, visited);
|
---|
311 | if (odFrame != null) return odFrame;
|
---|
312 | }
|
---|
313 |
|
---|
314 | // Reached end of this nodes links - found no match
|
---|
315 | return null;
|
---|
316 | }
|
---|
317 |
|
---|
318 |
|
---|
319 | /**
|
---|
320 | * Recursivly calculates the total running time for this frame.
|
---|
321 | *
|
---|
322 | * @return
|
---|
323 | * The running time in milliseconds. Always positive. Zero if this frame has no tracks/linked tracks.
|
---|
324 | */
|
---|
325 | public long calculateRunningTime() {
|
---|
326 | return calculateRunningTime(new HashMap<OverdubbedFrame, Long>());
|
---|
327 | }
|
---|
328 |
|
---|
329 | /**
|
---|
330 | * Calculates the running time of this overdubbed frame in milliseconds.
|
---|
331 | * Avoids infinit recursion.
|
---|
332 | *
|
---|
333 | * @param visited
|
---|
334 | * The visitive nodes
|
---|
335 | *
|
---|
336 | * @return
|
---|
337 | * The running time. Always positive. Zero if this frame has no tracks/linked tracks.
|
---|
338 | */
|
---|
339 | private long calculateRunningTime(Map<OverdubbedFrame, Long> visited) {
|
---|
340 |
|
---|
341 | Long rt = visited.get(this);
|
---|
342 | if (rt != null) { // already considered - re-use value
|
---|
343 | return rt;
|
---|
344 | }
|
---|
345 |
|
---|
346 | long runTime = 0;
|
---|
347 |
|
---|
348 | if (!tracks.isEmpty() || !linkedTracks.isEmpty()) {
|
---|
349 |
|
---|
350 | // Calculate the running time for this frame
|
---|
351 | long initTime = getFirstInitiationTime();
|
---|
352 | long relativeEndTime = Long.MIN_VALUE;
|
---|
353 |
|
---|
354 | for (TrackGraphNode track : tracks) {
|
---|
355 | long ret = track.getInitiationTime() + track.getRunningTime();
|
---|
356 | if (ret > relativeEndTime) relativeEndTime = ret;
|
---|
357 | }
|
---|
358 |
|
---|
359 | for (LinkedTracksGraphNode linkedTrack : linkedTracks) {
|
---|
360 |
|
---|
361 | long ret = linkedTrack.getInitiationTime() +
|
---|
362 | linkedTrack.getLinkedFrame().calculateRunningTime(visited);
|
---|
363 |
|
---|
364 | if (ret > relativeEndTime) relativeEndTime = ret;
|
---|
365 | }
|
---|
366 |
|
---|
367 | assert (initTime <= relativeEndTime);
|
---|
368 |
|
---|
369 | runTime = relativeEndTime - initTime;
|
---|
370 |
|
---|
371 | }
|
---|
372 |
|
---|
373 | // Save this ODFrames rt
|
---|
374 | visited.put(this, new Long(runTime));
|
---|
375 |
|
---|
376 | return runTime;
|
---|
377 | }
|
---|
378 |
|
---|
379 | /**
|
---|
380 | *
|
---|
381 | * @return
|
---|
382 | * The first initiation time of a track widget or track link on this frame.
|
---|
383 | * Note that these are relative and can be negative.
|
---|
384 | *
|
---|
385 | * Defaults to zero if there are no tracks/track links on this frame.
|
---|
386 | *
|
---|
387 | */
|
---|
388 | public long getFirstInitiationTime() { // relative to this frame - i.e. first initiation time on this frame
|
---|
389 |
|
---|
390 | long smallest = 0;
|
---|
391 | boolean hasSet = false;
|
---|
392 |
|
---|
393 | for (LinkedTracksGraphNode linkedTrack : linkedTracks) {
|
---|
394 | if (!hasSet || linkedTrack.getInitiationTime() < smallest) {
|
---|
395 | smallest = linkedTrack.getInitiationTime();
|
---|
396 | hasSet = true;
|
---|
397 | }
|
---|
398 | }
|
---|
399 |
|
---|
400 | for (TrackGraphNode track : tracks) {
|
---|
401 | if (!hasSet || track.getInitiationTime() < smallest) {
|
---|
402 | smallest = track.getInitiationTime();
|
---|
403 | hasSet = true;
|
---|
404 | }
|
---|
405 | }
|
---|
406 |
|
---|
407 | return smallest;
|
---|
408 | }
|
---|
409 |
|
---|
410 | @Override
|
---|
411 | public String toString() {
|
---|
412 | return frameName;
|
---|
413 | }
|
---|
414 |
|
---|
415 | /**
|
---|
416 | * @return
|
---|
417 | * All the TrackGraphInfo's in this frames descendants, including this frame.
|
---|
418 | * Never null. Can of coarse be empty.
|
---|
419 | */
|
---|
420 | public List<TrackGraphNode> getTracksDeep() {
|
---|
421 | List<TrackGraphNode> res = new LinkedList<TrackGraphNode>();
|
---|
422 | allAddTracksDeep(res, new HashSet<OverdubbedFrame>());
|
---|
423 | return res;
|
---|
424 | }
|
---|
425 |
|
---|
426 | private void allAddTracksDeep(List<TrackGraphNode> tlist, Set<OverdubbedFrame> visited) {
|
---|
427 | if (visited.contains(this)) return; // already considered
|
---|
428 | visited.add(this);
|
---|
429 |
|
---|
430 | tlist.addAll(tracks);
|
---|
431 |
|
---|
432 | for (LinkedTracksGraphNode linkedTrack : linkedTracks) {
|
---|
433 | linkedTrack.getLinkedFrame().allAddTracksDeep(tlist, visited);
|
---|
434 | }
|
---|
435 | }
|
---|
436 |
|
---|
437 | /**
|
---|
438 | * @param masterMixID
|
---|
439 | * The master mix for all the absolute tacks nodes to use. Must not be null or empty.
|
---|
440 | *
|
---|
441 | * @return
|
---|
442 | * The list of tracks and their ABS times according to <i>this frame</i>
|
---|
443 | * starting from ms time 0. Never null - can be empty.
|
---|
444 | */
|
---|
445 | public List<AbsoluteTrackNode> getAbsoluteTrackLayoutDeep(String masterMixID) {
|
---|
446 | assert(masterMixID != null);
|
---|
447 | assert(masterMixID.length() > 0);
|
---|
448 |
|
---|
449 | List<AbsoluteTrackNode> res = new LinkedList<AbsoluteTrackNode>();
|
---|
450 | Stack<String> virtualPath = new Stack<String>();
|
---|
451 | virtualPath.add(masterMixID);
|
---|
452 |
|
---|
453 | addAbsTrackNode(res, 0, new Stack<OverdubbedFrame>(), virtualPath);
|
---|
454 |
|
---|
455 | return res;
|
---|
456 | }
|
---|
457 |
|
---|
458 | private void addAbsTrackNode(
|
---|
459 | List<AbsoluteTrackNode> abslist,
|
---|
460 | long currentTime,
|
---|
461 | Stack<OverdubbedFrame> visited,
|
---|
462 | Stack<String> virtualPath) {
|
---|
463 |
|
---|
464 | assert(currentTime >= 0);
|
---|
465 |
|
---|
466 | // Base case
|
---|
467 | if (visited.contains(this)) return; // already considered - loop safety
|
---|
468 |
|
---|
469 | visited.push(this);
|
---|
470 |
|
---|
471 | // Get offset - align all tracks on this frame to first track/linked track...
|
---|
472 | long firstInitTime = getFirstInitiationTime();
|
---|
473 |
|
---|
474 |
|
---|
475 | // Add track info and init times for this frame
|
---|
476 | for (TrackGraphNode tnode : tracks) {
|
---|
477 | abslist.add(new AbsoluteTrackNode(
|
---|
478 | tnode,
|
---|
479 | currentTime + (tnode.getInitiationTime() - firstInitTime),
|
---|
480 | SoundDesk.createIndirectLocalChannelID(
|
---|
481 | virtualPath, tnode.getLocalFilename()),
|
---|
482 | frameName));
|
---|
483 | }
|
---|
484 |
|
---|
485 | // Recurse: with a new current time according to the linked track init time
|
---|
486 | for (LinkedTracksGraphNode linkedTrack : linkedTracks) {
|
---|
487 | virtualPath.push(linkedTrack.getVirtualFilename()); // build vpath
|
---|
488 |
|
---|
489 | linkedTrack.getLinkedFrame().addAbsTrackNode(
|
---|
490 | abslist,
|
---|
491 | currentTime + (linkedTrack.getInitiationTime() - firstInitTime),
|
---|
492 | visited,
|
---|
493 | virtualPath);
|
---|
494 |
|
---|
495 | virtualPath.pop(); // maintain vpath
|
---|
496 | }
|
---|
497 |
|
---|
498 | visited.pop();
|
---|
499 | }
|
---|
500 |
|
---|
501 | /**
|
---|
502 | * Gets all frame positions that occur in a target child frame, from this frame...
|
---|
503 | * <b>WARNING</b>: Infinite recursion if contains loops in structure.
|
---|
504 | *
|
---|
505 | * @param childFrameName
|
---|
506 | * Must not be null. The frame to get all the translated frame positions
|
---|
507 | *
|
---|
508 | * @param relativeMSPosition
|
---|
509 | * A relative ms position for this frame
|
---|
510 | *
|
---|
511 | * @return
|
---|
512 | * A list of ms positions that have been translated ... empty
|
---|
513 | * if there are no occurances.
|
---|
514 | */
|
---|
515 | public List<Integer> getMSPositions(String childFrameName, long relativeMSPosition) {
|
---|
516 | assert(childFrameName != null);
|
---|
517 | assert(childFrameName.length() > 0);
|
---|
518 |
|
---|
519 | List<Integer> positions = new LinkedList<Integer>();
|
---|
520 |
|
---|
521 | addMSPosition(
|
---|
522 | positions,
|
---|
523 | childFrameName,
|
---|
524 | relativeMSPosition,
|
---|
525 | new HashMap<OverdubbedFrame, CachedODFrameInfo>());
|
---|
526 |
|
---|
527 | return positions;
|
---|
528 |
|
---|
529 | }
|
---|
530 |
|
---|
531 | private class CachedODFrameInfo {
|
---|
532 |
|
---|
533 | long runningTime;
|
---|
534 | long firstInitTime;
|
---|
535 |
|
---|
536 | public CachedODFrameInfo(long runningTime, long firstInitTime) {
|
---|
537 | this.runningTime = runningTime;
|
---|
538 | this.firstInitTime = firstInitTime;
|
---|
539 | }
|
---|
540 | }
|
---|
541 |
|
---|
542 | /**
|
---|
543 | * <b>WARNING</b>: Infinite recursion if contains loops in structure.
|
---|
544 | * A better safety Solution: Keep track of all virtual paths - NOT FRAMES
|
---|
545 | *
|
---|
546 | * @param msPositions
|
---|
547 | * @param childFrameName
|
---|
548 | * @param currentRelativePosition
|
---|
549 | * @param cachedCalcs
|
---|
550 | * @param visited
|
---|
551 | */
|
---|
552 | private void addMSPosition(
|
---|
553 | List<Integer> msPositions,
|
---|
554 | String childFrameName,
|
---|
555 | long currentRelativePosition,
|
---|
556 | Map<OverdubbedFrame, CachedODFrameInfo> cachedCalcs) {
|
---|
557 |
|
---|
558 | // Avoid recomputation of calcs
|
---|
559 | CachedODFrameInfo calcCache = cachedCalcs.get(this);
|
---|
560 |
|
---|
561 | if (calcCache == null) {
|
---|
562 |
|
---|
563 | calcCache = new CachedODFrameInfo(
|
---|
564 | calculateRunningTime(),
|
---|
565 | getFirstInitiationTime());
|
---|
566 |
|
---|
567 | cachedCalcs.put(this, calcCache);
|
---|
568 |
|
---|
569 | }
|
---|
570 |
|
---|
571 | assert(calcCache != null);
|
---|
572 |
|
---|
573 | // Is requested frame position in range of this overdub frame?
|
---|
574 | if (childFrameName.equalsIgnoreCase(frameName) &&
|
---|
575 | currentRelativePosition >= calcCache.firstInitTime
|
---|
576 | && currentRelativePosition <= (calcCache.firstInitTime + calcCache.runningTime)) {
|
---|
577 | msPositions.add(new Integer((int)(currentRelativePosition - calcCache.firstInitTime)));
|
---|
578 | }
|
---|
579 |
|
---|
580 | // Recurse - even if re-visiting visited frames... risky ... will
|
---|
581 | // get stack overflow if the structure contains loops
|
---|
582 | for (LinkedTracksGraphNode linkedTrack : linkedTracks) {
|
---|
583 |
|
---|
584 | CachedODFrameInfo linkedFramesCachedCalc = cachedCalcs.get(linkedTrack.getLinkedFrame());
|
---|
585 |
|
---|
586 | if (linkedFramesCachedCalc == null) {
|
---|
587 | linkedFramesCachedCalc = new CachedODFrameInfo(
|
---|
588 | linkedTrack.getLinkedFrame().calculateRunningTime(),
|
---|
589 | linkedTrack.getLinkedFrame().getFirstInitiationTime());
|
---|
590 |
|
---|
591 | cachedCalcs.put(linkedTrack.getLinkedFrame(), linkedFramesCachedCalc);
|
---|
592 | }
|
---|
593 |
|
---|
594 | // Only recuse down a link if the cuurent position is over top of the link
|
---|
595 | if (currentRelativePosition >= linkedTrack.getInitiationTime() &&
|
---|
596 | currentRelativePosition <= (linkedTrack.getInitiationTime() + linkedFramesCachedCalc.runningTime)) {
|
---|
597 |
|
---|
598 | linkedTrack.getLinkedFrame().addMSPosition(
|
---|
599 | msPositions,
|
---|
600 | childFrameName,
|
---|
601 | linkedFramesCachedCalc.firstInitTime + (currentRelativePosition - linkedTrack.getInitiationTime()),
|
---|
602 | cachedCalcs);
|
---|
603 | }
|
---|
604 | }
|
---|
605 |
|
---|
606 | }
|
---|
607 |
|
---|
608 | }
|
---|