source: trunk/src/org/apollo/gui/FrameLayoutDaemon.java@ 1258

Last change on this file since 1258 was 1258, checked in by davidb, 5 years ago

Changed how DisplayController width, height and size are retrieved. Now does this top-level, rather than going through the AxisAlignmentBox. In doing so, can now control for when the window size has you yet been correctly mapped to the screen, and fall back to pre-defined MINIMUM defaults

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