diff --git a/app/render/audioparams.cpp b/app/render/audioparams.cpp index f91848e55..099c9ceac 100644 --- a/app/render/audioparams.cpp +++ b/app/render/audioparams.cpp @@ -26,6 +26,11 @@ const uint64_t &AudioParams::channel_layout() const return channel_layout_; } +rational AudioParams::time_base() const +{ + return rational(1, sample_rate()); +} + AudioRenderingParams::AudioRenderingParams() : format_(SampleFormat::SAMPLE_FMT_INVALID) { diff --git a/app/render/audioparams.h b/app/render/audioparams.h index 566309c87..308f68fae 100644 --- a/app/render/audioparams.h +++ b/app/render/audioparams.h @@ -14,6 +14,7 @@ public: const int& sample_rate() const; const uint64_t& channel_layout() const; + rational time_base() const; private: int sample_rate_; diff --git a/app/widget/timebased/timebased.cpp b/app/widget/timebased/timebased.cpp index 816ba0f20..96cd6e025 100644 --- a/app/widget/timebased/timebased.cpp +++ b/app/widget/timebased/timebased.cpp @@ -62,13 +62,13 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node) ConnectedNodeChanged(viewer_node_); if (viewer_node_) { - ConnectNodeInternal(viewer_node_); - connect(viewer_node_, &ViewerOutput::LengthChanged, this, &TimeBasedWidget::UpdateMaximumScroll); if ((points_ = ConnectTimelinePoints())) { ruler()->ConnectTimelinePoints(points_); } + + ConnectNodeInternal(viewer_node_); } } @@ -152,6 +152,11 @@ TimelinePoints *TimeBasedWidget::ConnectTimelinePoints() return static_cast(viewer_node_->parent()); } +TimelinePoints *TimeBasedWidget::GetConnectedTimelinePoints() const +{ + return points_; +} + void TimeBasedWidget::SetTime(int64_t timestamp) { ruler_->SetTime(timestamp); diff --git a/app/widget/timebased/timebased.h b/app/widget/timebased/timebased.h index 6a58ba12b..40e8bf513 100644 --- a/app/widget/timebased/timebased.h +++ b/app/widget/timebased/timebased.h @@ -87,6 +87,8 @@ protected: virtual TimelinePoints* ConnectTimelinePoints(); + TimelinePoints* GetConnectedTimelinePoints() const; + protected slots: /** * @brief Slot to center the horizontal scroll bar on the playhead's current position diff --git a/app/widget/timeruler/seekablewidget.cpp b/app/widget/timeruler/seekablewidget.cpp index b24d90ae2..624261979 100644 --- a/app/widget/timeruler/seekablewidget.cpp +++ b/app/widget/timeruler/seekablewidget.cpp @@ -1,15 +1,23 @@ #include "seekablewidget.h" #include +#include #include +#include "common/qtutils.h" + SeekableWidget::SeekableWidget(QWidget* parent) : TimelineScaledWidget(parent), time_(0), timeline_points_(nullptr), scroll_(0) { + QFontMetrics fm = fontMetrics(); + text_height_ = fm.height(); + + // Set width of playhead marker + playhead_width_ = QFontMetricsWidth(fm, "H"); } void SeekableWidget::ConnectTimelinePoints(TimelinePoints *points) @@ -108,3 +116,86 @@ void SeekableWidget::SeekToScreenPoint(int screen) emit TimeChanged(timestamp); } + +void SeekableWidget::DrawTimelinePoints(QPainter* p, int marker_bottom) +{ + if (!timeline_points()) { + return; + } + + // Draw in/out workarea + if (timeline_points()->workarea()->enabled()) { + int workarea_left = qMax(0, TimeToScreen(timeline_points()->workarea()->in())); + int workarea_right; + + if (timeline_points()->workarea()->out() == TimelineWorkArea::kResetOut) { + workarea_right = width(); + } else { + workarea_right = qMin(width(), TimeToScreen(timeline_points()->workarea()->out())); + } + + p->fillRect(workarea_left, 0, workarea_right - workarea_left, height(), palette().highlight()); + } + + // Draw markers + if (marker_bottom > 0 && !timeline_points()->markers()->list().isEmpty()) { + + int marker_top = marker_bottom - text_height_; + + // FIXME: Hardcoded marker colors + p->setPen(Qt::black); + p->setBrush(Qt::green); + + foreach (TimelineMarker* marker, timeline_points()->markers()->list()) { + int marker_left = TimeToScreen(marker->time().in()); + int marker_right = TimeToScreen(marker->time().out()); + + if (marker_left >= width() || marker_right < 0) { + continue; + } + + if (marker->time().length() == 0) { + // Single point in time marker + DrawPlayhead(p, marker_left, marker_bottom); + } else { + // Marker range + int rect_left = qMax(0, marker_left); + int rect_right = qMin(width(), marker_right); + + QRect marker_rect(rect_left, marker_top, rect_right - rect_left, marker_bottom - marker_top); + + p->drawRect(marker_rect); + + if (!marker->name().isEmpty()) { + p->drawText(marker_rect, marker->name()); + } + } + } + } +} + +void SeekableWidget::DrawPlayhead(QPainter *p, int x, int y) +{ + int half_width = playhead_width_ / 2; + + if (x + half_width < 0 || x - half_width > width()) { + return; + } + + p->setRenderHint(QPainter::Antialiasing); + + int half_text_height = text_height() / 3; + + QPoint points[] = { + QPoint(x, y), + QPoint(x - half_width, y - half_text_height), + QPoint(x - half_width, y - text_height()), + QPoint(x + 1 + half_width, y - text_height()), + QPoint(x + 1 + half_width, y - half_text_height), + QPoint(x + 1, y), + }; + + p->drawPolygon(points, 6); + + p->setRenderHint(QPainter::Antialiasing, false); +} diff --git a/app/widget/timeruler/seekablewidget.h b/app/widget/timeruler/seekablewidget.h index 9c79d20e6..e96c23b53 100644 --- a/app/widget/timeruler/seekablewidget.h +++ b/app/widget/timeruler/seekablewidget.h @@ -31,6 +31,8 @@ protected: virtual void ScaleChangedEvent(const double&) override; + void DrawTimelinePoints(QPainter *p, int marker_bottom = 0); + TimelinePoints* timeline_points() const; double ScreenToUnitFloat(int screen); @@ -42,6 +44,16 @@ protected: int TimeToScreen(const rational& time); + void DrawPlayhead(QPainter* p, int x, int y); + + inline const int& text_height() const { + return text_height_; + } + + inline const int& playhead_width() const { + return playhead_width_; + } + inline const QColor& GetPlayheadColor() const { return style_.GetPlayheadColor(); @@ -67,6 +79,10 @@ private: int scroll_; + int text_height_; + + int playhead_width_; + }; #endif // SEEKABLEWIDGET_H diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 6cc411854..c9ff31939 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -39,16 +39,12 @@ TimeRuler::TimeRuler(bool text_visible, bool cache_status_visible, QWidget* pare setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); // Text height is used to calculate widget height - text_height_ = fm.height(); - cache_status_height_ = text_height_ / 4; + cache_status_height_ = text_height() / 4; // Get the "minimum" space allowed between two line markers on the ruler (in screen pixels) // Mediocre but reliable way of scaling UI objects by font/DPI size minimum_gap_between_lines_ = QFontMetricsWidth(fm, "H"); - // Set width of playhead marker - playhead_width_ = minimum_gap_between_lines_; - // Text visibility affects height, so we set that here UpdateHeight(); } @@ -93,65 +89,17 @@ void TimeRuler::paintEvent(QPaintEvent *) // Draw timeline points if connected if (timeline_points()) { + int marker_bottom = height() - text_height(); - // Draw in/out workarea - if (timeline_points()->workarea()->enabled()) { - int workarea_left = qMax(0, TimeToScreen(timeline_points()->workarea()->in())); - int workarea_right; - - if (timeline_points()->workarea()->out() == TimelineWorkArea::kResetOut) { - workarea_right = width(); - } else { - workarea_right = qMin(width(), TimeToScreen(timeline_points()->workarea()->out())); - } - - p.fillRect(workarea_left, 0, workarea_right - workarea_left, height(), palette().highlight()); + if (show_cache_status_) { + marker_bottom -= cache_status_height_; } - // Draw markers - if (!timeline_points()->markers()->list().isEmpty()) { - int marker_bottom = height() - text_height_; - - if (show_cache_status_) { - marker_bottom -= cache_status_height_; - } - - if (text_visible_) { - marker_bottom -= cache_status_height_; - } - - int marker_top = marker_bottom - text_height_; - - // FIXME: Hardcoded marker colors - p.setPen(Qt::black); - p.setBrush(Qt::green); - - foreach (TimelineMarker* marker, timeline_points()->markers()->list()) { - int marker_left = TimeToScreen(marker->time().in()); - int marker_right = TimeToScreen(marker->time().out()); - - if (marker_left >= width() || marker_right < 0) { - continue; - } - - if (marker->time().length() == 0) { - // Single point in time marker - DrawPlayhead(&p, marker_left, marker_bottom); - } else { - // Marker range - int rect_left = qMax(0, marker_left); - int rect_right = qMin(width(), marker_right); - - QRect marker_rect(rect_left, marker_top, rect_right - rect_left, marker_bottom - marker_top); - - p.drawRect(marker_rect); - - if (!marker->name().isEmpty()) { - p.drawText(marker_rect, marker->name()); - } - } - } + if (text_visible_) { + marker_bottom -= cache_status_height_; } + + DrawTimelinePoints(&p, marker_bottom); } double width_of_frame = timebase_dbl() * GetScale(); @@ -325,11 +273,9 @@ void TimeRuler::paintEvent(QPaintEvent *) // Draw the playhead if it's on screen at the moment int playhead_pos = UnitToScreen(GetTime()); - if (playhead_pos + playhead_width_ >= 0 && playhead_pos - playhead_width_ < width()) { - p.setPen(Qt::NoPen); - p.setBrush(GetPlayheadColor()); - DrawPlayhead(&p, playhead_pos, line_bottom); - } + p.setPen(Qt::NoPen); + p.setBrush(GetPlayheadColor()); + DrawPlayhead(&p, playhead_pos, line_bottom); } void TimeRuler::TimebaseChangedEvent(const rational &tb) @@ -339,25 +285,6 @@ void TimeRuler::TimebaseChangedEvent(const rational &tb) update(); } -void TimeRuler::DrawPlayhead(QPainter *p, int x, int y) -{ - p->setRenderHint(QPainter::Antialiasing); - - int half_text_height = text_height_ / 3; - int half_width = playhead_width_ / 2; - - QPoint points[] = { - QPoint(x, y), - QPoint(x - half_width, y - half_text_height), - QPoint(x - half_width, y - text_height_), - QPoint(x + 1 + half_width, y - text_height_), - QPoint(x + 1 + half_width, y - half_text_height), - QPoint(x + 1, y), - }; - - p->drawPolygon(points, 6); -} - int TimeRuler::CacheStatusHeight() const { return fontMetrics().height() / 4; @@ -365,11 +292,11 @@ int TimeRuler::CacheStatusHeight() const void TimeRuler::UpdateHeight() { - int height = text_height_; + int height = text_height(); // Add text height if (text_visible_) { - height += text_height_; + height += text_height(); } // Add cache status height @@ -378,7 +305,7 @@ void TimeRuler::UpdateHeight() } // Add marker height - height += text_height_; + height += text_height(); setFixedHeight(height); } diff --git a/app/widget/timeruler/timeruler.h b/app/widget/timeruler/timeruler.h index f926d59d3..83ada935e 100644 --- a/app/widget/timeruler/timeruler.h +++ b/app/widget/timeruler/timeruler.h @@ -50,18 +50,12 @@ protected: private: void UpdateHeight(); - void DrawPlayhead(QPainter* p, int x, int y); - int CacheStatusHeight() const; - int text_height_; - int cache_status_height_; int minimum_gap_between_lines_; - int playhead_width_; - bool text_visible_; bool centered_text_; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 742690c2e..ce1efbbf2 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -128,7 +128,7 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) if (!n->video_params().time_base().isNull()) { SetTimebase(n->video_params().time_base()); } else if (n->audio_params().sample_rate() > 0) { - SetTimebase(rational(1, n->audio_params().sample_rate())); + SetTimebase(n->audio_params().time_base()); } else { SetTimebase(rational()); } @@ -157,6 +157,10 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) UpdateRendererParameters(); UpdateStack(); + + if (GetConnectedTimelinePoints()) { + waveform_view_->ConnectTimelinePoints(GetConnectedTimelinePoints()); + } } void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) @@ -177,6 +181,8 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) SizeChangedSlot(0, 0); gl_widget_->DisconnectColorManager(); + + waveform_view_->ConnectTimelinePoints(nullptr); } void ViewerWidget::ConnectedNodeChanged(ViewerOutput *n) diff --git a/app/widget/viewer/waveformview.cpp b/app/widget/viewer/waveformview.cpp index a0f26c03b..e6305bc16 100644 --- a/app/widget/viewer/waveformview.cpp +++ b/app/widget/viewer/waveformview.cpp @@ -29,7 +29,7 @@ void WaveformView::SetBackend(AudioRenderBackend *backend) connect(backend_, &AudioRenderBackend::QueueComplete, this, static_cast(&WaveformView::update)); connect(backend_, &AudioRenderBackend::ParamsChanged, this, &WaveformView::BackendParamsChanged); - SetTimebase(rational(1, backend_->params().sample_rate())); + SetTimebase(backend_->params().time_base()); } update(); @@ -91,6 +91,8 @@ void WaveformView::paintEvent(QPaintEvent *event) QPainter p(this); + DrawTimelinePoints(&p); + // FIXME: Hardcoded color p.setPen(Qt::green);