source: trunk/src_apollo/org/apollo/gui/FrameLayoutDaemon.java@ 375

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

Lots of minor improvements made prior to evluation study, and small things done afterwards for report :)

File size: 26.3 KB
Line 
1package org.apollo.gui;
2
3import java.awt.Point;
4import java.awt.event.ComponentEvent;
5import java.awt.event.ComponentListener;
6import java.awt.event.MouseEvent;
7import java.io.File;
8import java.lang.reflect.InvocationTargetException;
9import java.util.Collection;
10import java.util.HashMap;
11import java.util.Map;
12
13import javax.swing.SwingUtilities;
14
15import org.apollo.audio.ApolloSubjectChangedEvent;
16import org.apollo.audio.structure.AbstractTrackGraphNode;
17import org.apollo.audio.structure.AudioStructureModel;
18import org.apollo.audio.structure.LinkedTracksGraphNode;
19import org.apollo.audio.structure.OverdubbedFrame;
20import org.apollo.audio.structure.TrackGraphLoopException;
21import org.apollo.audio.structure.TrackGraphNode;
22import org.apollo.audio.util.Timeline;
23import org.apollo.io.AudioIO;
24import org.apollo.io.AudioPathManager;
25import org.apollo.mvc.AbstractSubject;
26import org.apollo.mvc.Observer;
27import org.apollo.mvc.Subject;
28import org.apollo.mvc.SubjectChangedEvent;
29import org.apollo.util.Mutable;
30import org.apollo.widgets.LinkedTrack;
31import org.apollo.widgets.SampledTrack;
32import org.expeditee.gui.Browser;
33import org.expeditee.gui.DisplayIO;
34import org.expeditee.gui.DisplayIOObserver;
35import org.expeditee.gui.Frame;
36import org.expeditee.gui.FrameMouseActions;
37import org.expeditee.gui.FreeItems;
38import org.expeditee.gui.MouseEventRouter;
39import org.expeditee.items.Item;
40import org.expeditee.items.ItemUtils;
41import org.expeditee.items.widgets.InteractiveWidget;
42import org.expeditee.items.widgets.WidgetCorner;
43import org.expeditee.items.widgets.WidgetEdge;
44
45/**
46 * A daemon that lays out overdubbed frames.
47 *
48 * It is a singletone. Also it is a subject which raises empty events whenever
49 * a new timeline is created.
50 *
51 * @author Brook Novak
52 *
53 */
54public class FrameLayoutDaemon extends AbstractSubject implements Observer, DisplayIOObserver {
55
56 /** The frame that should not be layout out at any point in time. */
57 private Frame suspendedFrame = null;
58
59 /** The state info passed while suspending layout on {@link #suspendedFrame} */
60 private Object suspendendUserData = null;
61
62 private LayoutDaemon daemon = null;
63 private Object daemonWaker = new Object();
64 private boolean layoutInvalid = false;
65
66 private Timeline currentTimeline = null;
67 private Frame timelineOwner = null;
68
69 private int leftMargin = DEFAULT_LAYOUT_LEFT_MARGIN;
70 private int rightMargin = DEFAULT_LAYOUT_RIGHT_MARGIN;
71
72 public static final int DEFAULT_LAYOUT_RIGHT_MARGIN = 30;
73 public static final int DEFAULT_LAYOUT_LEFT_MARGIN = 30;
74 public static final int MAX_TIMELINE_MARGIN_AREA = 400;
75 public static final int LAYOUT_MAX_INITIATION_PIXEL = 100;
76 public static final int MIN_TRACK_WIDGET_WIDTH = 20;
77 public static final int FREE_SPACE_MARGINS = 30;
78
79 public static final int TRACK_WIDGET_HEIGHT = 100;
80
81 public static final int TRACK_WIDGET_DEFAULT_WIDTH = 350;
82
83 private static FrameLayoutDaemon instance = new FrameLayoutDaemon();
84
85 /**
86 * Singleton constructor,
87 */
88 private FrameLayoutDaemon() {
89 daemon = new LayoutDaemon();
90 daemon.start();
91 AudioStructureModel.getInstance().addObserver(this);
92 DisplayIO.addDisplayIOObserver(this);
93
94 // Listen for resize events
95 SwingUtilities.invokeLater(new Runnable() {
96 public void run() {
97
98 // Keep expanded view centered to the browser window
99 if (Browser._theBrowser != null) {
100 Browser._theBrowser.addComponentListener(new ComponentListener() {
101
102 public void componentHidden(ComponentEvent e) {
103 }
104
105 public void componentMoved(ComponentEvent e) {
106 }
107
108 public void componentResized(ComponentEvent e) {
109 forceRecheck();
110 }
111
112 public void componentShown(ComponentEvent e) {
113 }
114
115 });
116 }
117
118 }
119 });
120
121 }
122
123 /**
124 * {@inheritDoc}
125 */
126 public void frameChanged() {
127 forceRecheck();
128 }
129
130 /**
131 *
132 * @return
133 * The singleton instance.
134 */
135 public static FrameLayoutDaemon getInstance() {
136 return instance;
137 }
138
139 /**
140 * @see #getTimelineOwner() for determining which frame the timeline was for.
141 *
142 * @return
143 * The last format hence the current timeline. Can be null
144 * if no format has not occured yet or last format
145 * had no timeline... i.e no tracks were found...
146 */
147 public Timeline getLastComputedTimeline() {
148 return currentTimeline;
149 }
150
151 /**
152 * @see #getLastComputedTimeline()
153 *
154 * @return
155 * Can be null if a timeline has not yet been computed.
156 */
157 public Frame getTimelineOwner() {
158 return timelineOwner;
159 }
160
161 public void forceRecheck() {
162 assert(daemon != null && daemon.isAlive());
163 layoutInvalid = true;
164 synchronized(daemonWaker) {
165 daemonWaker.notify();
166 }
167 }
168
169 /**
170 * {@inheritDoc}
171 */
172 public Subject getObservedSubject() {
173 return AudioStructureModel.getInstance();
174 }
175
176 /**
177 * {@inheritDoc}
178 */
179 public void setObservedSubject(Subject parent) {
180 }
181
182 /**
183 * {@inheritDoc}
184 */
185 public void modelChanged(Subject source, SubjectChangedEvent event) {
186 if (event.getID() != ApolloSubjectChangedEvent.NAME_CHANGED)
187 forceRecheck();
188 }
189
190 private void setCurrentTimeline(Timeline newTimeline, Frame timelineOwner) {
191 this.currentTimeline = newTimeline;
192 this.timelineOwner = timelineOwner;
193 fireSubjectChanged(SubjectChangedEvent.EMPTY);
194 }
195
196 /**
197 * Suspends the layout daemon from laying out a frame.
198 * Resumes any previous suspended layouts.
199 *
200 * @see #resumeLayout(Object)
201 *
202 * @param frame
203 * The frame to suspend
204 *
205 * @param userData
206 * Any user data to attatch to frame layout.
207 *
208 */
209 public void suspendLayout(Frame frame, Object userData) {
210 assert(frame != null);
211
212 resumeLayout(null);
213
214 suspendedFrame = frame;
215 suspendendUserData = userData;
216
217 }
218
219 /**
220 * Resumes the suspended layout if has one.
221 *
222 * @see #suspendLayout(Frame, Object)
223 *
224 * @param userData
225 * Null to resume layout regardless, otherwise it will only resume
226 * layout if the user data matches the user data passed when calling
227 * the suspension.
228 */
229 public void resumeLayout(Object userData) {
230 if (suspendedFrame == null) return;
231
232 if (userData == null || userData == suspendendUserData) {
233 suspendedFrame = null;
234 suspendendUserData = null;
235 }
236
237 }
238
239 public int getLeftMargin() {
240 return leftMargin;
241 }
242
243 public int getRightMargin() {
244 return rightMargin;
245 }
246
247 /**
248 * Sets the layout margins. Values are clamped.
249 *
250 * @param left
251 * The left margin in pixels.
252 *
253 * @param right
254 * The right margin in pixels.
255 *
256 */
257 public void setTimelineMargins(int left, int right) {
258
259 if (left < 0) left = 0;
260 if (right < 0) right = 0;
261
262 if ((left + right) >= MAX_TIMELINE_MARGIN_AREA) {
263 left = right = (MAX_TIMELINE_MARGIN_AREA / 2);
264 }
265
266 leftMargin = left;
267 rightMargin = right;
268 }
269
270 /**
271 * Lays out frames according to the current frame state...
272 *
273 * @author Brook Novak
274 *
275 */
276 private class LayoutDaemon extends Thread {
277
278 private TrackFormatter trackFormatter = new TrackFormatter();
279
280 LayoutDaemon() {
281 super("TrackLayoutDaemon");
282 super.setDaemon(true);
283 }
284
285 public void run() {
286
287 while (true) { // keep daemon running for app lifetime
288
289 if (!layoutInvalid) {
290 try {
291 synchronized(daemonWaker) {
292 daemonWaker.wait();
293 }
294 } catch (InterruptedException e) {
295 // Time to do some layout
296 }
297 }
298
299 layoutInvalid = false;
300
301 boolean hasUpdated = false;
302 do {
303 try {
304 AudioStructureModel.getInstance().waitOnUpdates();
305 hasUpdated = true;
306 } catch (InterruptedException e) {
307 e.printStackTrace();
308 continue;
309 }
310 } while (!hasUpdated);
311
312
313 // Note that accessing expeditee model in another thread --
314 // Being real careful not to do anything unsafe
315 Frame currentFrame = DisplayIO.getCurrentFrame();
316 if (currentFrame == null) continue;
317
318 boolean hasFetched = false;
319 do {
320 try {
321 AudioStructureModel.getInstance().fetchGraph(currentFrame.getName());
322 hasFetched = true;
323 } catch (InterruptedException e) { // cancelled
324 /* Consume */
325 } catch (TrackGraphLoopException e) { // contains loop
326 e.printStackTrace();
327 break;
328 }
329 } while (!hasFetched);
330
331 trackFormatter.toFormat = currentFrame;
332
333 try {
334 SwingUtilities.invokeAndWait(trackFormatter);
335 } catch (InterruptedException e) {
336 e.printStackTrace();
337 } catch (InvocationTargetException e) {
338 e.printStackTrace();
339 }
340
341 }
342
343 }
344
345 private class TrackFormatter implements Runnable {
346
347 Frame toFormat = null;
348
349 public void run() {
350
351 assert(toFormat != null);
352
353 // To late?
354 if (toFormat != DisplayIO.getCurrentFrame()) return;
355
356 boolean supressSpatialLayout = suspendedFrame == toFormat;
357
358 long firstInitiationTime = 0;
359 int initiationXPixel = 0;
360 int rightMostPixel = 0; // FOR SUSPENDED LAYOUTS ONLY
361 long longestEndTime = 0;
362 boolean hasRunningTime = false; // A flag set to true if there is actual audio to [lay
363
364 Map<InteractiveWidget, AbstractTrackGraphNode> widgetsToFormat = new HashMap<InteractiveWidget, AbstractTrackGraphNode>();
365
366 for (InteractiveWidget iw : toFormat.getInteractiveWidgets()) {
367
368 AbstractTrackGraphNode abinf = null;
369
370 if (iw instanceof SampledTrack) {
371 SampledTrack sampledTrackWidget = (SampledTrack)iw;
372
373
374 abinf = AudioStructureModel.getInstance().getTrackGraphInfo(
375 sampledTrackWidget.getLocalFileName(),
376 toFormat.getName());
377
378
379 } else if (iw instanceof LinkedTrack) {
380 LinkedTrack linkedTrackWidget = (LinkedTrack)iw;
381
382 if (linkedTrackWidget.shouldLayout()) {
383 // TrackGraphModel provides best data
384 abinf = AudioStructureModel.getInstance().getLinkedTrackGraphInfo(
385 linkedTrackWidget.getVirtualFilename(),
386 toFormat.getName());
387 }
388
389 }
390
391 if (abinf != null) {
392
393 if (widgetsToFormat.isEmpty() ||
394 abinf.getInitiationTime() < firstInitiationTime) {
395 firstInitiationTime = abinf.getInitiationTime();
396 initiationXPixel = iw.getX();
397 }
398
399 // Remember for suspended layouts
400 if (widgetsToFormat.isEmpty() ||
401 (iw.getX() + iw.getWidth()) > rightMostPixel) {
402 rightMostPixel = iw.getX() + iw.getWidth();
403 }
404
405
406 long endTime = abinf.getInitiationTime() + abinf.getRunningTime();
407 if (widgetsToFormat.isEmpty() || endTime > longestEndTime) {
408 longestEndTime = endTime;
409 }
410
411 widgetsToFormat.put(iw, abinf);
412
413 // Set flag
414 if (!hasRunningTime) {
415 hasRunningTime = abinf.getRunningTime() > 0;
416 }
417
418 }
419
420 }
421
422 // Anything to format?
423 if (!hasRunningTime ||
424 widgetsToFormat.isEmpty() ||
425 longestEndTime == firstInitiationTime) {
426
427 // NOTE: longestEndTime == firstInitiationTime indicates linked tracks
428 // linking to no actual audio data.
429
430 // Reset the current timeline
431 setCurrentTimeline(null, toFormat);
432 return;
433 }
434
435 assert (longestEndTime > firstInitiationTime);
436 assert(rightMostPixel >= initiationXPixel);
437
438 int timelineWidth = TRACK_WIDGET_DEFAULT_WIDTH;
439
440 if (widgetsToFormat.size() > 1) {
441
442 // Ensure that left most pixel is at a reasonable position
443 if (!supressSpatialLayout && (initiationXPixel < 0 ||
444 (initiationXPixel > Browser._theBrowser.getWidth() && Browser._theBrowser.getWidth() > leftMargin))) {
445 initiationXPixel = leftMargin;
446 } else if (!supressSpatialLayout && initiationXPixel > LAYOUT_MAX_INITIATION_PIXEL) {
447 initiationXPixel = LAYOUT_MAX_INITIATION_PIXEL;
448 }
449
450 if (supressSpatialLayout) {
451 timelineWidth = rightMostPixel - initiationXPixel;
452 } else {
453 timelineWidth = Browser._theBrowser.getWidth() - initiationXPixel - rightMargin;
454 }
455
456 if (timelineWidth < 0) timelineWidth = 1;
457
458 } else {
459
460 // If only has one widget - the time line pixel length is set to whatever that is set to
461 timelineWidth = widgetsToFormat.keySet().iterator().next().getWidth();
462
463 }
464
465 // Set the new timelinetimelineWidth
466 setCurrentTimeline(
467 new Timeline(
468 firstInitiationTime,
469 longestEndTime - firstInitiationTime,
470 initiationXPixel,
471 timelineWidth),
472 toFormat);
473
474 // Do the actual formatting
475 if (!supressSpatialLayout) {
476
477 for (InteractiveWidget iw : widgetsToFormat.keySet()) {
478
479 AbstractTrackGraphNode atgi = widgetsToFormat.get(iw);
480 assert(atgi != null);
481
482 // Update width first
483 int width = (int)((double)atgi.getRunningTime() / currentTimeline.getTimePerPixel());
484
485 // Clamp to something sensible
486 if (width < MIN_TRACK_WIDGET_WIDTH)
487 width = MIN_TRACK_WIDGET_WIDTH;
488
489 Collection<Item> enclosed = iw.getItems().get(0).getEnclosedItems();
490 Map<Item, Float> enclosedPortions = new HashMap<Item, Float>();
491 if (enclosed != null) {
492 for (Item i : enclosed) {
493 if (!(i instanceof WidgetCorner || i instanceof WidgetEdge)) {
494 float f = i.getX() - iw.getX();
495 f /= iw.getWidth();
496 enclosedPortions.put(i, f);
497 }
498
499 }
500 }
501
502 if (widgetsToFormat.size() == 1) {
503
504 if (iw.isFixedSize()) {
505 iw.setSize(
506 -1, -1, // minWidth, maxWidth,
507 iw.getHeight(), iw.getHeight(), // minHeight, maxHeight
508 iw.getWidth(), iw.getWidth()); // newWidth, newHeight
509 }
510
511
512 } else {
513
514 if (iw.getWidth() != width || !iw.isFixedSize()) {
515 iw.setSize(
516 width, width, // minWidth, maxWidth,
517 iw.getHeight(), iw.getHeight(), // minHeight, maxHeight
518 width, width); // newWidth, newHeight
519 }
520
521 }
522
523 // Update position last
524 int xpos = currentTimeline.getXAtMSTime(atgi.getInitiationTime());
525 iw.setPosition(xpos, iw.getY());
526
527 for (Item i : enclosedPortions.keySet()) {
528 i.setX(xpos + (enclosedPortions.get(i) * iw.getWidth()));
529 }
530
531 }
532
533 }
534
535 // Also since this daemons responisiblilty it to layout track widgets on frames ...
536 // it should also take care of free space items
537
538 Item i = FreeItems.getItemAttachedToCursor();
539 InteractiveWidget freespaceTrackToFormat = null;
540 if (i != null && i instanceof WidgetEdge) {
541 freespaceTrackToFormat = ((WidgetEdge)i).getWidgetSource();
542 } else if (i != null && i instanceof WidgetCorner) {
543 freespaceTrackToFormat = ((WidgetCorner)i).getWidgetSource();
544 }
545
546 if (freespaceTrackToFormat != null) {
547
548 int width = -1;
549 if (freespaceTrackToFormat instanceof SampledTrack) {
550
551 long rt = ((SampledTrack)freespaceTrackToFormat).getRunningMSTimeFromRawAudio();
552 if (rt <= 0) rt = ((SampledTrack)freespaceTrackToFormat).getRunningMSTimeFromMeta();
553 if (rt > 0) {
554 width = (int)(currentTimeline.getPixelPerTime() * rt);
555 }
556
557 } else if (freespaceTrackToFormat instanceof LinkedTrack) {
558
559 long rt = ((LinkedTrack)freespaceTrackToFormat).getRunningMSTimeFromMeta();
560 if (rt > 0) {
561 width = (int)(currentTimeline.getPixelPerTime() * rt);
562 }
563 }
564
565 if (width > 0) {
566
567 if (width > (Browser._theBrowser.getWidth() - (2 * FREE_SPACE_MARGINS)))
568 width = Browser._theBrowser.getWidth() - (2 * FREE_SPACE_MARGINS);
569
570 freespaceTrackToFormat.setSize(
571 width, width,
572 freespaceTrackToFormat.getHeight(), freespaceTrackToFormat.getHeight(),
573 width, freespaceTrackToFormat.getHeight());
574
575 // Keep the floating track on the cursor
576 MouseEvent me = MouseEventRouter.getCurrentMouseEvent();
577 Point containerPoint = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), Browser._theBrowser.getContentPane());
578
579 if (!ItemUtils.expandRectangle(freespaceTrackToFormat.getBounds(), 20).contains(containerPoint)) {
580 // If due to resize the track is not longer over the cursor then just center it.
581 int offsetX = width / 2;
582 int offsetY = freespaceTrackToFormat.getHeight() / 2;
583 freespaceTrackToFormat.setPosition(
584 containerPoint.x - offsetX,
585 containerPoint.y - offsetY);
586 FrameMouseActions.resetOffset();
587
588 }
589
590
591
592
593 }
594
595 }
596
597 }
598
599 }
600
601 }
602
603 /**
604 * Selects best timeline for given frame - may have to infer.
605 *
606 * @param targetFrame
607 *
608 * @return
609 * The best timeline for the given frame - null if could not infer (if had to).
610 */
611 public Timeline getTimeline(Frame targetFrame) {
612 assert(targetFrame != null);
613
614 if (currentTimeline != null && targetFrame == timelineOwner) return currentTimeline;
615
616 return inferTimeline(targetFrame);
617 }
618
619 /**
620 * Determines the ms time at a x pixel poistion on a target frame.
621 *
622 * @see #getMSTimeAndWidth(int, long, Frame) for efficient use.
623 *
624 * @param x
625 * The x pixel posotion to determine the ms time from.
626 *
627 * @param targetFrame
628 * The target frame that the ms will be derived from.
629 *
630 * @return
631 * The ms time at the x pixel poistion.
632 */
633 public long getMSAtX(int x, Frame targetFrame) {
634 return getMSAtX(x, getTimeline(targetFrame));
635 }
636
637 private long getMSAtX(int x, Timeline tl) {
638 if (tl != null) return tl.getMSTimeAtX(x);
639 return 0; // Default to zero with no timeline available.
640 }
641
642 /**
643 * @param ms
644 * The time to translate the x position from
645 *
646 * @param targetFrame
647 * The frame to determine the timeline information from
648 *
649 * @return
650 * Null if the timeline for the target frame could not
651 * be determined/inferred or has no timeline. Otherwise the
652 * X pixal position at the given time.
653 */
654 public Mutable.Long getXAtMS(long ms, Frame targetFrame) {
655 assert(targetFrame != null);
656
657 Timeline tl = getTimeline(targetFrame);
658 if (tl == null) return null;
659
660 return Mutable.createMutableLong(tl.getXAtMSTime(ms));
661 }
662
663 /**
664 * Determines the pixel width from a time range (ms) for a target frame.
665 * <b>NOTE</B> Width is clamped to it is never smaller than {@link #MIN_TRACK_WIDGET_WIDTH}
666 *
667 * @see #getMSTimeAndWidth(int, long, Frame) for efficient use.
668 *
669 * @param ms
670 * The amount of time to determine a width from (in milliseconds)
671 *
672 * @param targetFrame
673 * The target frame that the <i>clamped</i> width will be derived from.
674 *
675 * @return
676 * The width in pixels of the ms range. -1 if there is no timeline
677 */
678 public int getWidthFromMS(long ms, Frame targetFrame) {
679 return getWidthFromMS(ms, getTimeline(targetFrame));
680 }
681
682 private int getWidthFromMS(long ms, Timeline tl) {
683 int width = -1;
684 if (tl != null) {
685 width = (int)((double)ms / tl.getTimePerPixel());
686 }
687
688 // Clamp width to something reasonable.
689 if (width > 0 && width < MIN_TRACK_WIDGET_WIDTH) width = MIN_TRACK_WIDGET_WIDTH;
690
691 return width;
692 }
693
694 /**
695 * Gets the ms time and width in one call - more efficient than
696 * calling {@link #getMSAtX(int, Frame)} and {@link #getWidthFromMS(long, Frame)}
697 * seperatly.
698 *
699 * <b>NOTE</B> Width is clamped to it is never smaller than {@link #MIN_TRACK_WIDGET_WIDTH}
700 *
701 * @param x
702 * The x pixel posotion to determine the ms time from.
703 *
704 * @param ms
705 * The amount of time to determine a width from (in milliseconds)
706 *
707 * @param targetFrame
708 * The target frame that the ms and width will be derived from.
709 *
710 * @return
711 * A Two dimensional array of the ms time and <i>clamped</i> width, respectivly.
712 */
713 public long[] getMSTimeAndWidth(int x, long ms, Frame targetFrame) {
714
715 // Re-use timeline
716 Timeline tl = getTimeline(targetFrame);
717
718 return new long[] { getMSAtX(x, tl), getWidthFromMS(ms, tl)};
719 }
720
721
722 /**
723 * <b>MUST BE ON EXPEDITEE THREAD</b>
724 *
725 * If the frame is not loaded in TrackGraphModel then the timeline will
726 * be a rough estimation using meta data. Otherwise it will be accurate.
727 * Fast operation - does not recurse.
728 *
729 * @param frame
730 * The target frame for inferring the timeline.
731 *
732 * @return
733 * A timeline. Null if there are no track widgets fround on the frame.
734 */
735 public static Timeline inferTimeline(Frame frame) {
736 assert(frame != null);
737
738 // Unavilable times relative to zero - and used if no availabel times exist.
739 Integer unavilableInitiationXPixel = null;
740 Mutable.Long unavilableLongestEndTime = null;
741 Integer unavilableLongestEndTimeRightPixel = null;
742
743 Mutable.Long firstInitiationTime = null;
744 int initiationXPixel = 0;
745
746 long longestEndTime = -1;
747 int longestEndTimeRightPixel = 0;
748
749 // Look for all track widgets
750 for (InteractiveWidget iw : frame.getInteractiveWidgets()) {
751
752 if (iw instanceof SampledTrack) {
753 SampledTrack sampledTrackWidget = (SampledTrack)iw;
754
755 // TrackGraphModel provides best data
756 TrackGraphNode tgi = AudioStructureModel.getInstance().getTrackGraphInfo(
757 sampledTrackWidget.getLocalFileName(),
758 frame.getName());
759
760 // Is the initiation time available?
761 Mutable.Long it = (tgi != null) ?
762 Mutable.createMutableLong(tgi.getInitiationTime()) :
763 sampledTrackWidget.getInitiationTimeFromMeta();
764
765 // If not - keep record left-most unavilable x pixel
766 if (it == null &&
767 (unavilableInitiationXPixel == null || iw.getX() < unavilableInitiationXPixel)) {
768 unavilableInitiationXPixel = iw.getX();
769
770 // If so - check to see if it is the first initiation time on the frame
771 } else if(it != null && (firstInitiationTime == null ||
772 it.value < firstInitiationTime.value)) {
773 firstInitiationTime = it;
774 initiationXPixel = iw.getX();
775 }
776
777 // Determine running time
778 long runningTime = sampledTrackWidget.getRunningMSTimeFromRawAudio();
779
780 if (runningTime <= 0 && tgi != null) {
781 runningTime = tgi.getRunningTime();
782
783 } else if (runningTime <= 0) {
784 runningTime = sampledTrackWidget.getRunningMSTimeFromMeta();
785 }
786
787 // Note that this will be in rare cases .. the meta should rarely be missing
788 // so cann afford to hang the thread a little for checking the actual time...
789 if (runningTime <= 0) {
790 File f = new File(AudioPathManager.AUDIO_HOME_DIRECTORY +
791 sampledTrackWidget.getLocalFileName());
792 if (f.exists()) {
793 try {
794 runningTime = AudioIO.getRunningTime(f.getPath());
795 } catch(Exception e) {
796 e.printStackTrace();
797 }
798 }
799 }
800
801 // If all failed, just set to one... this would be rare, and by setting to
802 // 1 it will just give a incorrect timeline - nothing catistrophic.
803 if (runningTime <= 0) {
804 runningTime = 1;
805 }
806
807 if (it == null) {
808 if (unavilableLongestEndTime == null ||
809 runningTime > unavilableLongestEndTime.value) {
810 unavilableLongestEndTime = Mutable.createMutableLong(runningTime);
811 unavilableLongestEndTimeRightPixel = iw.getX() + iw.getWidth();
812 }
813 } else {
814 long endTime = it.value + runningTime;
815 if (endTime > longestEndTime) {
816 longestEndTime = endTime;
817 longestEndTimeRightPixel = iw.getX() + iw.getWidth();
818 }
819 }
820
821 } else if (iw instanceof LinkedTrack) {
822 LinkedTrack linkedTrackWidget = (LinkedTrack)iw;
823
824 // TrackGraphModel provides best data
825 LinkedTracksGraphNode ltgi = AudioStructureModel.getInstance().getLinkedTrackGraphInfo(
826 linkedTrackWidget.getVirtualFilename(),
827 frame.getName());
828
829 Mutable.Long it = (ltgi != null) ?
830 Mutable.createMutableLong(ltgi.getInitiationTime()) :
831 linkedTrackWidget.getInitiationTimeFromMeta();
832
833 if (it == null &&
834 (unavilableInitiationXPixel == null || iw.getX() < unavilableInitiationXPixel)) {
835 unavilableInitiationXPixel = iw.getX();
836 } else if(it != null && (firstInitiationTime == null ||
837 it.value < firstInitiationTime.value)) {
838 firstInitiationTime = it;
839 initiationXPixel = iw.getX();
840 }
841
842 // Determine running time
843 long runningTime = (ltgi != null) ?
844 ltgi.getRunningTime() :
845 linkedTrackWidget.getRunningMSTimeFromMeta();
846
847 // Note that not recusing through the links because the frames might
848 // not be loaded yet. The TrackGraphModel handles such operations -
849 // and it would be to expensive / overkill to frame search here...
850
851
852 // If all failed, just set to one...
853 if (runningTime <= 0) {
854 runningTime = 1;
855 }
856
857 if (it == null) {
858 if (unavilableLongestEndTime == null ||
859 runningTime > unavilableLongestEndTime.value) {
860 unavilableLongestEndTime = Mutable.createMutableLong(runningTime);
861 unavilableLongestEndTimeRightPixel = iw.getX() + iw.getWidth();
862 }
863 } else {
864 long endTime = it.value + runningTime;
865 if (endTime > longestEndTime) {
866 longestEndTime = endTime;
867 longestEndTimeRightPixel = iw.getX() + iw.getWidth();
868 }
869 }
870
871 }
872
873 }
874
875 if (firstInitiationTime == null) firstInitiationTime = Mutable.createMutableLong(0L);
876
877 if (longestEndTime > 0) {
878
879 assert(longestEndTime > firstInitiationTime.value);
880 assert(longestEndTimeRightPixel > initiationXPixel);
881
882 long rttime = longestEndTime - firstInitiationTime.value;
883 int pxWidth = longestEndTimeRightPixel - initiationXPixel;
884 if (unavilableLongestEndTime != null &&
885 unavilableLongestEndTime.value > rttime) {
886 assert(unavilableInitiationXPixel != null);
887 rttime = unavilableLongestEndTime.value;
888 pxWidth = unavilableLongestEndTimeRightPixel - unavilableInitiationXPixel;
889 }
890
891 return new Timeline(
892 firstInitiationTime.value,
893 rttime,
894 initiationXPixel,
895 pxWidth);
896
897 } else if(unavilableInitiationXPixel != null) { // default
898
899 assert(unavilableInitiationXPixel != null);
900 assert(unavilableLongestEndTimeRightPixel != null);
901
902 return new Timeline(
903 0,
904 unavilableLongestEndTime.value,
905 unavilableInitiationXPixel,
906 unavilableLongestEndTimeRightPixel - unavilableInitiationXPixel);
907
908 }
909
910 return null;
911
912 }
913
914 /**
915 * @return
916 * The total ms time for the current frame. zero or negative if failed or there are no
917 * tracks to play.
918 *
919 */
920 public static long inferCurrentTotalMSTime() {
921
922 Frame currentFrame = DisplayIO.getCurrentFrame();
923 String currentFrameName = (currentFrame != null) ? currentFrame.getName() : null;
924
925 if (currentFrameName == null) return -1;
926
927 OverdubbedFrame odf = AudioStructureModel.getInstance().getOverdubbedFrame(currentFrameName);
928
929 if (odf != null) {
930 return odf.calculateRunningTime();
931 }
932
933 // Infer time from meta
934 Timeline tl = FrameLayoutDaemon.inferTimeline(currentFrame);
935
936 if (tl != null) {
937 return tl.getRunningTime();
938 }
939
940 return -1;
941
942 }
943
944
945}
Note: See TracBrowser for help on using the repository browser.