source: trunk/src_apollo/org/apollo/audio/util/FrameLayoutDaemon.java@ 318

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

Refactored a class name and extended recorder widgets to have a perminant lifetime option (for optimum idea capturing!)

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