diff --git a/app/common/timecodefunctions.cpp b/app/common/timecodefunctions.cpp index 31bab1682..cdc958dbd 100644 --- a/app/common/timecodefunctions.cpp +++ b/app/common/timecodefunctions.cpp @@ -263,3 +263,13 @@ int64_t Timecode::time_to_timestamp(const double &time, const rational &timebase { return qRound64(time * timebase.flipped().toDouble()); } + +int64_t Timecode::rescale_timestamp(const int64_t &ts, const rational &source, const rational &dest) +{ + return qRound64(static_cast(ts) * source.toDouble() / dest.toDouble()); +} + +int64_t Timecode::rescale_timestamp_ceil(const int64_t &ts, const rational &source, const rational &dest) +{ + return qCeil(static_cast(ts) * source.toDouble() / dest.toDouble()); +} diff --git a/app/common/timecodefunctions.h b/app/common/timecodefunctions.h index 509576e07..a48a93a57 100644 --- a/app/common/timecodefunctions.h +++ b/app/common/timecodefunctions.h @@ -57,6 +57,9 @@ public: static int64_t time_to_timestamp(const rational& time, const rational& timebase); static int64_t time_to_timestamp(const double& time, const rational& timebase); + static int64_t rescale_timestamp(const int64_t& ts, const rational& source, const rational& dest); + static int64_t rescale_timestamp_ceil(const int64_t& ts, const rational& source, const rational& dest); + static rational timestamp_to_time(const int64_t& timestamp, const rational& timebase); static QString time_to_timecode(const rational& time, const rational& timebase, const Display &display, bool show_plus_if_positive = false); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 0a00e6553..9d1bc5653 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -6,6 +6,7 @@ #include "core.h" #include "common/timecodefunctions.h" +#include "dialog/sequence/sequence.h" #include "dialog/speedduration/speedduration.h" #include "node/block/transition/transition.h" #include "tool/tool.h" @@ -16,7 +17,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) : TimeBasedWidget(true, true, parent), rubberband_(QRubberBand::Rectangle, this), - active_tool_(nullptr) + active_tool_(nullptr), + use_audio_time_units_(false) { QVBoxLayout* vert_layout = new QVBoxLayout(this); vert_layout->setSpacing(0); @@ -72,6 +74,8 @@ TimelineWidget::TimelineWidget(QWidget *parent) : connect(views_.first()->view()->horizontalScrollBar(), &QScrollBar::rangeChanged, scrollbar(), &QScrollBar::setRange); vert_layout->addWidget(scrollbar()); + connect(ruler(), &TimeRuler::TimeChanged, this, &TimelineWidget::SetViewTimestamp); + foreach (TimelineAndTrackView* tview, views_) { TimelineView* view = tview->view(); @@ -82,9 +86,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) : connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, ruler(), &TimeRuler::SetScroll); connect(view, &TimelineView::ScaleChanged, this, &TimelineWidget::SetScale); - connect(ruler(), &TimeRuler::TimeChanged, view, &TimelineView::SetTime); - connect(view, &TimelineView::TimeChanged, ruler(), &TimeRuler::SetTime); - connect(view, &TimelineView::TimeChanged, this, &TimelineWidget::TimeChanged); + connect(view, &TimelineView::TimeChanged, this, &TimelineWidget::ViewTimestampChanged); connect(view, &TimelineView::customContextMenuRequested, this, &TimelineWidget::ShowContextMenu); connect(scrollbar(), &QScrollBar::valueChanged, view->horizontalScrollBar(), &QScrollBar::setValue); connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, scrollbar(), &QScrollBar::setValue); @@ -158,9 +160,7 @@ void TimelineWidget::TimebaseChangedEvent(const rational &timebase) } } - foreach (TimelineAndTrackView* view, views_) { - view->view()->SetTimebase(timebase); - } + UpdateViewTimebases(); } void TimelineWidget::resizeEvent(QResizeEvent *event) @@ -173,9 +173,7 @@ void TimelineWidget::resizeEvent(QResizeEvent *event) void TimelineWidget::TimeChangedEvent(const int64_t& timestamp) { - foreach (TimelineAndTrackView* view, views_) { - view->view()->SetTime(timestamp); - } + SetViewTimestamp(timestamp); timecode_label_->SetValue(timestamp); } @@ -333,6 +331,14 @@ QList TimelineWidget::FootageToDraggedFootage(QL return df; } +rational TimelineWidget::GetToolTipTimebase() const +{ + if (GetConnectedNode() && use_audio_time_units_) { + return GetConnectedNode()->audio_params().time_base(); + } + return timebase(); +} + void TimelineWidget::SelectAll() { foreach (TimelineAndTrackView* view, views_) { @@ -1042,8 +1048,20 @@ void TimelineWidget::ShowContextMenu() if (!selected.isEmpty()) { QAction* speed_duration_action = menu.addAction(tr("Speed/Duration")); connect(speed_duration_action, &QAction::triggered, this, &TimelineWidget::ShowSpeedDurationDialog); + + menu.addSeparator(); } + QAction* toggle_audio_units = menu.addAction(tr("Use Audio Time Units")); + toggle_audio_units->setCheckable(true); + toggle_audio_units->setChecked(use_audio_time_units_); + connect(toggle_audio_units, &QAction::triggered, this, &TimelineWidget::SetUseAudioTimeUnits); + + menu.addSeparator(); + + QAction* properties_action = menu.addAction(tr("Properties")); + connect(properties_action, &QAction::triggered, this, &TimelineWidget::ShowSequenceDialog); + menu.exec(QCursor::pos()); } @@ -1072,6 +1090,57 @@ void TimelineWidget::DeferredScrollAction() scrollbar()->setValue(deferred_scroll_value_); } +void TimelineWidget::ShowSequenceDialog() +{ + if (!GetConnectedNode()) { + return; + } + + SequenceDialog sd(static_cast(GetConnectedNode()->parent()), SequenceDialog::kExisting, this); + sd.exec(); +} + +void TimelineWidget::SetUseAudioTimeUnits(bool use) +{ + use_audio_time_units_ = use; + + // Update timebases + UpdateViewTimebases(); + + // Force update of the viewer timestamps + SetViewTimestamp(GetTimestamp()); +} + +void TimelineWidget::SetViewTimestamp(const int64_t &ts) +{ + for (int i=0;iview()->SetTime(Timecode::rescale_timestamp(ts, + timebase(), + GetConnectedNode()->audio_params().time_base())); + } else { + view->view()->SetTime(ts); + } + } +} + +void TimelineWidget::ViewTimestampChanged(int64_t ts) +{ + if (use_audio_time_units_ && sender() == views_.at(Timeline::kTrackTypeAudio)) { + ts = Timecode::rescale_timestamp(ts, + GetConnectedNode()->audio_params().time_base(), + timebase()); + } + + // Update all other views + SetViewTimestamp(ts); + + ruler()->SetTime(ts); + emit TimeChanged(ts); +} + void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) { ghost->SetScale(GetScale()); @@ -1079,6 +1148,19 @@ void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) views_.at(ghost->Track().type())->view()->scene()->addItem(ghost); } +void TimelineWidget::UpdateViewTimebases() +{ + for (int i=0;iview()->SetTimebase(GetConnectedNode()->audio_params().time_base()); + } else { + view->view()->SetTimebase(timebase()); + } + } +} + void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected) { TimelineViewBlockItem* link_item; diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 540d0630d..1138134c5 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -391,6 +391,8 @@ private: bool dual_transition_; }; + rational GetToolTipTimebase() const; + void InsertGapsAt(const rational& time, const rational& length, QUndoCommand* command); void DeleteSelectedInternal(const QList& blocks, bool transition_aware, bool remove_from_graph, QUndoCommand* command); @@ -431,6 +433,8 @@ private: int deferred_scroll_value_; + bool use_audio_time_units_; + int GetTrackY(const TrackReference& ref); int GetTrackHeight(const TrackReference& ref); @@ -438,6 +442,8 @@ private: void AddGhost(TimelineViewGhostItem* ghost); + void UpdateViewTimebases(); + private slots: void UpdateTimelineLength(const rational& length); @@ -481,6 +487,14 @@ private slots: void DeferredScrollAction(); + void ShowSequenceDialog(); + + void SetUseAudioTimeUnits(bool use); + + void SetViewTimestamp(const int64_t& ts); + + void ViewTimestampChanged(int64_t ts); + }; #endif // TIMELINEWIDGET_H diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index da94c4df7..1b7e697fc 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -142,9 +142,9 @@ void TimelineWidget::ImportTool::DragMove(TimelineViewMouseEvent *event) } // Generate tooltip (showing earliest in point of imported clip) - int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->timebase()); + int64_t earliest_timestamp = Timecode::time_to_timestamp(earliest_ghost, parent()->GetToolTipTimebase()); QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp, - parent()->timebase(), + parent()->GetToolTipTimebase(), Core::instance()->GetTimecodeDisplay()); // Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way @@ -232,11 +232,8 @@ void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QLi // Stream is essentially length-less - use config's default image length footage_duration = Config::Current()["DefaultStillLength"].value(); } else { - // Use duration from file - int64_t stream_duration = stream->duration(); - - // Rescale to timeline timebase - stream_duration = qCeil(static_cast(stream_duration) * stream->timebase().toDouble() / dest_tb.toDouble()); + // Rescale stream duration to timeline timebase + int64_t stream_duration = Timecode::rescale_timestamp_ceil(stream->duration(), stream->timebase(), dest_tb); // Convert to rational time footage_duration = rational(dest_tb.numerator() * stream_duration, diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 80fd34194..3110411a7 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -401,9 +401,9 @@ void TimelineWidget::PointerTool::ProcessDrag(const TimelineCoordinate &mouse_po // Show tooltip // Generate tooltip (showing earliest in point of imported clip) - int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->timebase()); + int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase()); QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp, - parent()->timebase(), + parent()->GetToolTipTimebase(), Core::instance()->GetTimecodeDisplay(), true); diff --git a/app/widget/timelinewidget/tool/slip.cpp b/app/widget/timelinewidget/tool/slip.cpp index 299ed5f22..229a351e4 100644 --- a/app/widget/timelinewidget/tool/slip.cpp +++ b/app/widget/timelinewidget/tool/slip.cpp @@ -51,9 +51,9 @@ void TimelineWidget::SlipTool::ProcessDrag(const TimelineCoordinate &mouse_pos) // Show tooltip // Generate tooltip (showing earliest in point of imported clip) - int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->timebase()); + int64_t earliest_timestamp = Timecode::time_to_timestamp(time_movement, parent()->GetToolTipTimebase()); QString tooltip_text = Timecode::timestamp_to_timecode(earliest_timestamp, - parent()->timebase(), + parent()->GetToolTipTimebase(), Core::instance()->GetTimecodeDisplay(), true); // Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way