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

Last change on this file since 1061 was 1061, checked in by davidb, 8 years ago

Margins for audio that are more inkeeping with new (left-sided) control panel overlay

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