From 6995ffe015c7861e7b62b9a0cee49cffa3779857 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 1 Aug 2021 11:05:18 -0700 Subject: [PATCH 1/4] reworked timeline elements --- app/common/rational.h | 6 + app/node/block/block.cpp | 119 +-------------- app/node/block/block.h | 56 +------ app/node/block/clip/clip.cpp | 141 +++++++++++++++++- app/node/block/clip/clip.h | 59 +++++++- app/node/block/subtitle/subtitle.cpp | 3 +- app/node/block/transition/transition.cpp | 47 +++--- app/node/block/transition/transition.h | 10 +- app/render/renderprocessor.cpp | 75 ++++++---- app/task/project/loadotio/loadotio.cpp | 12 +- app/widget/timelinewidget/timelinewidget.cpp | 7 +- app/widget/timelinewidget/timelinewidget.h | 2 +- app/widget/timelinewidget/tool/pointer.cpp | 24 ++- app/widget/timelinewidget/tool/razor.cpp | 5 +- app/widget/timelinewidget/tool/slip.cpp | 5 +- app/widget/timelinewidget/tool/transition.cpp | 5 +- .../timelinewidget/undo/timelineundogeneral.h | 5 +- .../view/timelineviewghostitem.h | 4 +- 18 files changed, 314 insertions(+), 271 deletions(-) diff --git a/app/common/rational.h b/app/common/rational.h index 6179b029e..074f1b864 100644 --- a/app/common/rational.h +++ b/app/common/rational.h @@ -94,6 +94,12 @@ public: AVRational toAVRational() const; #ifdef USE_OTIO + static rational fromRationalTime(const opentime::RationalTime &t) + { + // Is this the best way to do this? + return fromDouble(t.to_seconds()); + } + // Convert Olive ratioanls to opentime rationals with the given framerate (defaults to 24) opentime::RationalTime toRationalTime(double framerate = 24) const; #endif diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index c0584f8d0..0cc53e91f 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -24,7 +24,6 @@ #include "node/output/track/track.h" #include "transition/transition.h" -#include "widget/slider/floatslider.h" #include "widget/slider/rationalslider.h" namespace olive { @@ -32,18 +31,13 @@ namespace olive { #define super Node const QString Block::kLengthInput = QStringLiteral("length_in"); -const QString Block::kMediaInInput = QStringLiteral("media_in_in"); const QString Block::kEnabledInput = QStringLiteral("enabled_in"); -const QString Block::kSpeedInput = QStringLiteral("speed_in"); -const QString Block::kReverseInput = QStringLiteral("reverse_in"); Block::Block() : previous_(nullptr), next_(nullptr), track_(nullptr), - index_(-1), - in_transition_(nullptr), - out_transition_(nullptr) + index_(-1) { AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); SetInputProperty(kLengthInput, QStringLiteral("min"), QVariant::fromValue(rational(0, 1))); @@ -51,21 +45,8 @@ Block::Block() : SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true); IgnoreHashingFrom(kLengthInput); - AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - SetInputProperty(kMediaInInput, QStringLiteral("view"), RationalSlider::kTime); - SetInputProperty(kMediaInInput, QStringLiteral("viewlock"), true); - IgnoreHashingFrom(kMediaInInput); - AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - AddInput(kSpeedInput, NodeValue::kFloat, 1.0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage); - SetInputProperty(kSpeedInput, QStringLiteral("min"), 0.0); - IgnoreHashingFrom(kSpeedInput); - - AddInput(kReverseInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - IgnoreHashingFrom(kReverseInput); - // A block's length must be greater than 0 set_length_and_media_out(1); } @@ -82,47 +63,23 @@ rational Block::length() const void Block::set_length_and_media_out(const rational &length) { - Q_ASSERT(length > 0); - if (length == this->length()) { return; } - if (reverse()) { - // Calculate media_in adjustment - set_media_in(SequenceToMediaTime(length - this->length(), true)); - } - set_length_internal(length); } void Block::set_length_and_media_in(const rational &length) { - Q_ASSERT(length > 0); - if (length == this->length()) { return; } - if (!reverse()) { - // Calculate media_in adjustment - set_media_in(SequenceToMediaTime(this->length() - length)); - } - // Set the length without setting media out set_length_internal(length); } -rational Block::media_in() const -{ - return GetStandardValue(kMediaInInput).value(); -} - -void Block::set_media_in(const rational &media_in) -{ - SetStandardValue(kMediaInInput, QVariant::fromValue(media_in)); -} - bool Block::is_enabled() const { return GetStandardValue(kEnabledInput).toBool(); @@ -135,65 +92,9 @@ void Block::set_enabled(bool e) emit EnabledChanged(); } -rational Block::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse) const -{ - // These constants are not considered "values" per se, so we don't modify them - if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) { - return sequence_time; - } - - rational local_time = sequence_time; - - double speed_value = speed(); - - if (qIsNull(speed_value)) { - // Effectively holds the frame at the in point - local_time = 0; - } else if (!qFuzzyCompare(speed_value, 1.0)) { - // Multiply time - local_time = rational::fromDouble(local_time.toDouble() * speed_value); - } - - rational media_time = local_time + media_in(); - - if (reverse() && !ignore_reverse) { - media_time = length() - media_time; - } - - return media_time; -} - -rational Block::MediaToSequenceTime(const rational &media_time) const -{ - // These constants are not considered "values" per se, so we don't modify them - if (media_time == RATIONAL_MIN || media_time == RATIONAL_MAX) { - return media_time; - } - - rational sequence_time = media_time; - - if (reverse()) { - sequence_time = length() - sequence_time; - } - - sequence_time -= media_in(); - - double speed_value = speed(); - - if (qIsNull(speed_value)) { - // Effectively holds the frame at the in point, also prevents divide by zero - sequence_time = 0; - } else if (!qFuzzyCompare(speed_value, 1.0)) { - // Multiply time - sequence_time = rational::fromDouble(sequence_time.toDouble() / speed_value); - } - - return sequence_time; -} - void Block::InputValueChangedEvent(const QString &input, int element) { - Q_UNUSED(element) + super::InputValueChangedEvent(input, element); if (input == kLengthInput) { emit LengthChanged(); @@ -202,19 +103,6 @@ void Block::InputValueChangedEvent(const QString &input, int element) } } -void Block::LinkChangeEvent() -{ - block_links_.clear(); - - foreach (Node* n, links()) { - Block* b = dynamic_cast(n); - - if (b) { - block_links_.append(b); - } - } -} - bool Block::HashPassthrough(const QString &input, const QString &output, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const { if (IsInputConnected(input)) { @@ -239,10 +127,7 @@ void Block::Retranslate() super::Retranslate(); SetInputName(kLengthInput, tr("Length")); - SetInputName(kMediaInInput, tr("Media In")); SetInputName(kEnabledInput, tr("Enabled")); - SetInputName(kSpeedInput, tr("Speed")); - SetInputName(kReverseInput, tr("Reverse")); } void Block::Hash(const QString &, QCryptographicHash &, const rational &, const VideoParams &) const diff --git a/app/node/block/block.h b/app/node/block/block.h index c3d7f5a90..e438829d2 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -62,8 +62,8 @@ public: } rational length() const; - void set_length_and_media_out(const rational &length); - void set_length_and_media_in(const rational &length); + virtual void set_length_and_media_out(const rational &length); + virtual void set_length_and_media_in(const rational &length); TimeRange range() const { @@ -90,9 +90,6 @@ public: next_ = next; } - rational media_in() const; - void set_media_in(const rational& media_in); - Track* track() const { return track_; @@ -108,26 +105,6 @@ public: virtual void Retranslate() override; - TransitionBlock* in_transition() - { - return in_transition_; - } - - void set_in_transition(TransitionBlock* t) - { - in_transition_ = t; - } - - TransitionBlock* out_transition() - { - return out_transition_; - } - - void set_out_transition(TransitionBlock* t) - { - out_transition_ = t; - } - int index() const { return index_; @@ -138,30 +115,12 @@ public: index_ = i; } - const QVector& block_links() const - { - return block_links_; - } - - double speed() const - { - return GetStandardValue(kSpeedInput).toDouble(); - } - - bool reverse() const - { - return GetStandardValue(kReverseInput).toBool(); - } - virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override; virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override; static const QString kLengthInput; - static const QString kMediaInInput; static const QString kEnabledInput; - static const QString kSpeedInput; - static const QString kReverseInput; public slots: @@ -171,14 +130,8 @@ signals: void LengthChanged(); protected: - rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false) const; - - rational MediaToSequenceTime(const rational& media_time) const; - virtual void InputValueChangedEvent(const QString& input, int element) override; - virtual void LinkChangeEvent() override; - bool HashPassthrough(const QString &input, const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const; Block* previous_; @@ -192,11 +145,6 @@ private: Track* track_; int index_; - TransitionBlock* in_transition_; - TransitionBlock* out_transition_; - - QVector block_links_; - rational last_length_; }; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 83b8ad520..e8230d3c7 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -20,17 +20,36 @@ #include "clip.h" +#include "widget/slider/floatslider.h" +#include "widget/slider/rationalslider.h" + namespace olive { #define super Block const QString ClipBlock::kBufferIn = QStringLiteral("buffer_in"); +const QString ClipBlock::kMediaInInput = QStringLiteral("media_in_in"); +const QString ClipBlock::kSpeedInput = QStringLiteral("speed_in"); +const QString ClipBlock::kReverseInput = QStringLiteral("reverse_in"); -ClipBlock::ClipBlock(bool create_buffer_in) +ClipBlock::ClipBlock() : + in_transition_(nullptr), + out_transition_(nullptr) { - if (create_buffer_in) { - AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); - } + AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + SetInputProperty(kMediaInInput, QStringLiteral("view"), RationalSlider::kTime); + SetInputProperty(kMediaInInput, QStringLiteral("viewlock"), true); + IgnoreHashingFrom(kMediaInInput); + + AddInput(kSpeedInput, NodeValue::kFloat, 1.0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage); + SetInputProperty(kSpeedInput, QStringLiteral("min"), 0.0); + IgnoreHashingFrom(kSpeedInput); + + AddInput(kReverseInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + IgnoreHashingFrom(kReverseInput); + + AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); } Node *ClipBlock::copy() const @@ -53,6 +72,100 @@ QString ClipBlock::Description() const return tr("A time-based node that represents a media source."); } +void ClipBlock::set_length_and_media_out(const rational &length) +{ + if (length == this->length()) { + return; + } + + if (reverse()) { + // Calculate media_in adjustment + set_media_in(SequenceToMediaTime(length - this->length(), true)); + } + + super::set_length_and_media_out(length); +} + +void ClipBlock::set_length_and_media_in(const rational &length) +{ + if (length == this->length()) { + return; + } + + if (!reverse()) { + // Calculate media_in adjustment + set_media_in(SequenceToMediaTime(this->length() - length)); + } + + super::set_length_and_media_in(length); +} + +rational ClipBlock::media_in() const +{ + return GetStandardValue(kMediaInInput).value(); +} + +void ClipBlock::set_media_in(const rational &media_in) +{ + SetStandardValue(kMediaInInput, QVariant::fromValue(media_in)); +} + +rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse) const +{ + // These constants are not considered "values" per se, so we don't modify them + if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) { + return sequence_time; + } + + rational local_time = sequence_time; + + double speed_value = speed(); + + if (qIsNull(speed_value)) { + // Effectively holds the frame at the in point + local_time = 0; + } else if (!qFuzzyCompare(speed_value, 1.0)) { + // Multiply time + local_time = rational::fromDouble(local_time.toDouble() * speed_value); + } + + rational media_time = local_time + media_in(); + + if (reverse() && !ignore_reverse) { + media_time = length() - media_time; + } + + return media_time; +} + +rational ClipBlock::MediaToSequenceTime(const rational &media_time) const +{ + // These constants are not considered "values" per se, so we don't modify them + if (media_time == RATIONAL_MIN || media_time == RATIONAL_MAX) { + return media_time; + } + + rational sequence_time = media_time; + + if (reverse()) { + sequence_time = length() - sequence_time; + } + + sequence_time -= media_in(); + + double speed_value = speed(); + + if (qIsNull(speed_value)) { + // Effectively holds the frame at the in point, also prevents divide by zero + sequence_time = 0; + } else if (!qFuzzyCompare(speed_value, 1.0)) { + // Multiply time + sequence_time = rational::fromDouble(sequence_time.toDouble() / speed_value); + } + + return sequence_time; +} + void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) { Q_UNUSED(element) @@ -70,6 +183,19 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int } } +void ClipBlock::LinkChangeEvent() +{ + block_links_.clear(); + + foreach (Node* n, links()) { + ClipBlock* b = dynamic_cast(n); + + if (b) { + block_links_.append(b); + } + } +} + TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) @@ -110,9 +236,10 @@ void ClipBlock::Retranslate() { super::Retranslate(); - if (HasInputWithID(kBufferIn)) { - SetInputName(kBufferIn, tr("Buffer")); - } + SetInputName(kBufferIn, tr("Buffer")); + SetInputName(kMediaInInput, tr("Media In")); + SetInputName(kSpeedInput, tr("Speed")); + SetInputName(kReverseInput, tr("Reverse")); } void ClipBlock::Hash(const QString &out, QCryptographicHash &hash, const rational &time, const VideoParams &video_params) const diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index c81d3b510..8c4448bd9 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -32,7 +32,7 @@ class ClipBlock : public Block { Q_OBJECT public: - ClipBlock(bool create_buffer_in = true); + ClipBlock(); NODE_DEFAULT_DESTRUCTOR(ClipBlock) @@ -42,6 +42,12 @@ public: virtual QString id() const override; virtual QString Description() const override; + virtual void set_length_and_media_out(const rational &length) override; + virtual void set_length_and_media_in(const rational &length) override; + + rational media_in() const; + void set_media_in(const rational& media_in); + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override; virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; @@ -54,7 +60,58 @@ public: virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time, const VideoParams& video_params) const override; + double speed() const + { + return GetStandardValue(kSpeedInput).toDouble(); + } + + bool reverse() const + { + return GetStandardValue(kReverseInput).toBool(); + } + + TransitionBlock* in_transition() + { + return in_transition_; + } + + void set_in_transition(TransitionBlock* t) + { + in_transition_ = t; + } + + TransitionBlock* out_transition() + { + return out_transition_; + } + + void set_out_transition(TransitionBlock* t) + { + out_transition_ = t; + } + + const QVector& block_links() const + { + return block_links_; + } + static const QString kBufferIn; + static const QString kMediaInInput; + static const QString kSpeedInput; + static const QString kReverseInput; + +protected: + virtual void LinkChangeEvent() override; + +private: + rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false) const; + + rational MediaToSequenceTime(const rational& media_time) const; + + QVector block_links_; + + TransitionBlock* in_transition_; + TransitionBlock* out_transition_; }; diff --git a/app/node/block/subtitle/subtitle.cpp b/app/node/block/subtitle/subtitle.cpp index 3f5a9a212..ef42559cd 100644 --- a/app/node/block/subtitle/subtitle.cpp +++ b/app/node/block/subtitle/subtitle.cpp @@ -26,8 +26,7 @@ namespace olive { const QString SubtitleBlock::kTextIn = QStringLiteral("text_in"); -SubtitleBlock::SubtitleBlock() : - super(false) +SubtitleBlock::SubtitleBlock() { AddInput(kTextIn, NodeValue::kText, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); } diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 2603f0acd..6e8932098 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -21,6 +21,7 @@ #include "transition.h" #include "common/clamp.h" +#include "node/block/clip/clip.h" #include "node/output/track/track.h" namespace olive { @@ -30,6 +31,8 @@ namespace olive { const QString TransitionBlock::kOutBlockInput = QStringLiteral("out_block_in"); const QString TransitionBlock::kInBlockInput = QStringLiteral("in_block_in"); const QString TransitionBlock::kCurveInput = QStringLiteral("curve_in"); +const QString TransitionBlock::kInOffsetInput = QStringLiteral("in_offset_in"); +const QString TransitionBlock::kOutOffsetInput = QStringLiteral("out_offset_in"); TransitionBlock::TransitionBlock() : connected_out_block_(nullptr), @@ -40,6 +43,12 @@ TransitionBlock::TransitionBlock() : AddInput(kInBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + + AddInput(kInOffsetInput, NodeValue::kRational, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + + AddInput(kOutOffsetInput, NodeValue::kRational, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + + set_length_and_media_out(0); } void TransitionBlock::Retranslate() @@ -49,6 +58,8 @@ void TransitionBlock::Retranslate() SetInputName(kOutBlockInput, tr("From")); SetInputName(kInBlockInput, tr("To")); SetInputName(kCurveInput, tr("Curve")); + SetInputName(kInOffsetInput, tr("In Offset")); + SetInputName(kOutOffsetInput, tr("Out Offset")); // These must correspond to the CurveType enum SetComboBoxStrings(kCurveInput, { tr("Linear"), tr("Exponential"), tr("Logarithmic") }); @@ -56,34 +67,22 @@ void TransitionBlock::Retranslate() rational TransitionBlock::in_offset() const { - // If no in block is connected, there's no in offset - if (!connected_in_block()) { - return 0; - } + return GetStandardValue(kInOffsetInput).value(); +} - if (!connected_out_block()) { - // Assume only an in block is connected, in which case this entire transition length - return length(); - } - - // Assume both are connected - return length() + media_in(); +void TransitionBlock::set_in_offset(const rational &os) +{ + SetStandardValue(kInOffsetInput, QVariant::fromValue(os)); } rational TransitionBlock::out_offset() const { - // If no in block is connected, there's no in offset - if (!connected_out_block()) { - return 0; - } + return GetStandardValue(kOutOffsetInput).value(); +} - if (!connected_in_block()) { - // Assume only an in block is connected, in which case this entire transition length - return length(); - } - - // Assume both are connected - return -media_in(); +void TransitionBlock::set_out_offset(const rational &os) +{ + SetStandardValue(kOutOffsetInput, QVariant::fromValue(os)); } Block *TransitionBlock::connected_out_block() const @@ -267,12 +266,12 @@ void TransitionBlock::InputConnectedEvent(const QString &input, int element, con if (input == kOutBlockInput) { // If node is not a block, this will just be null - if ((connected_out_block_ = dynamic_cast(output.node()))) { + if ((connected_out_block_ = dynamic_cast(output.node()))) { connected_out_block_->set_out_transition(this); } } else if (input == kInBlockInput) { // If node is not a block, this will just be null - if ((connected_in_block_ = dynamic_cast(output.node()))) { + if ((connected_in_block_ = dynamic_cast(output.node()))) { connected_in_block_->set_in_transition(this); } } diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index 29b02a6d3..4156ee4d9 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -25,6 +25,8 @@ namespace olive { +class ClipBlock; + class TransitionBlock : public Block { Q_OBJECT @@ -36,7 +38,9 @@ public: virtual void Retranslate() override; rational in_offset() const; + void set_in_offset(const rational &os); rational out_offset() const; + void set_out_offset(const rational &os); Block* connected_out_block() const; Block* connected_in_block() const; @@ -54,6 +58,8 @@ public: static const QString kOutBlockInput; static const QString kInBlockInput; static const QString kCurveInput; + static const QString kInOffsetInput; + static const QString kOutOffsetInput; protected: virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const; @@ -81,9 +87,9 @@ private: void InsertTransitionTimes(AcceleratedJob* job, const double& time) const; - Block* connected_out_block_; + ClipBlock* connected_out_block_; - Block* connected_in_block_; + ClipBlock* connected_in_block_; }; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 34a1b8760..4f65968fb 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -25,6 +25,8 @@ #include #include +#include "node/block/clip/clip.h" +#include "node/block/transition/transition.h" #include "node/project/project.h" #include "rendermanager.h" @@ -253,43 +255,50 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim // Loop through active blocks retrieving their audio foreach (Block* b, active_blocks) { - TimeRange range_for_block(qMax(b->in(), range.in()), - qMin(b->out(), range.out())); + if (dynamic_cast(b) || dynamic_cast(b)) { + TimeRange range_for_block(qMax(b->in(), range.in()), + qMin(b->out(), range.out())); - int destination_offset = audio_params.time_to_samples(range_for_block.in() - range.in()); - int max_dest_sz = audio_params.time_to_samples(range_for_block.length()); + int destination_offset = audio_params.time_to_samples(range_for_block.in() - range.in()); + int max_dest_sz = audio_params.time_to_samples(range_for_block.length()); - // Destination buffer - NodeValueTable table = GenerateTable(b, Track::TransformRangeForBlock(b, range_for_block)); - SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value(); + // Destination buffer + NodeValueTable table = GenerateTable(b, Track::TransformRangeForBlock(b, range_for_block)); + SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value(); - if (!samples_from_this_block) { - // If we retrieved no samples from this block, do nothing - continue; + if (!samples_from_this_block) { + // If we retrieved no samples from this block, do nothing + continue; + } + + // If this is a clip, we might have extra speed/reverse information + ClipBlock *clip_cast = dynamic_cast(b); + if (clip_cast) { + double speed_value = clip_cast->speed(); + bool reversed = clip_cast->reverse(); + + if (qIsNull(speed_value)) { + // Just silence, don't think there's any other practical application of 0 speed audio + samples_from_this_block->fill(0); + } else if (!qFuzzyCompare(speed_value, 1.0)) { + // Multiply time + samples_from_this_block->speed(speed_value); + } + + if (reversed) { + samples_from_this_block->reverse(); + } + } + + int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count()); + + // Copy samples into destination buffer + for (int i=0; iaudio_params().channel_count(); i++) { + block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length); + } + + NodeValueTable::Merge({merged_table, table}); } - - double speed_value = b->GetStandardValue(Block::kSpeedInput).toDouble(); - - if (qIsNull(speed_value)) { - // Just silence, don't think there's any other practical application of 0 speed audio - samples_from_this_block->fill(0); - } else if (!qFuzzyCompare(speed_value, 1.0)) { - // Multiply time - samples_from_this_block->speed(speed_value); - } - - if (b->GetStandardValue(Block::kReverseInput).toBool()) { - samples_from_this_block->reverse(); - } - - int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count()); - - // Copy samples into destination buffer - for (int i=0; iaudio_params().channel_count(); i++) { - block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length); - } - - NodeValueTable::Merge({merged_table, table}); } if (ticket_->property("enablewaveforms").toBool()) { diff --git a/app/task/project/loadotio/loadotio.cpp b/app/task/project/loadotio/loadotio.cpp index 1d672ace4..11b10e716 100644 --- a/app/task/project/loadotio/loadotio.cpp +++ b/app/task/project/loadotio/loadotio.cpp @@ -168,7 +168,9 @@ bool LoadOTIOTask::Run() duration = rational::fromDouble(static_cast(otio_block)->source_range()->duration().to_seconds()); - block->set_media_in(start_time); + if (otio_block->schema_name() == "Clip") { + static_cast(block)->set_media_in(start_time); + } block->set_length_and_media_out(duration); } @@ -183,14 +185,12 @@ bool LoadOTIOTask::Run() TransitionBlock* transition_block = static_cast(block); OTIO::Transition* otio_block_transition = static_cast(otio_block); - duration = rational::fromDouble((otio_block_transition->in_offset() + otio_block_transition->out_offset()).to_seconds()); - transition_block->set_length_and_media_out(duration); + // Set how far the transition eats into the previous clip + transition_block->set_in_offset(rational::fromRationalTime(otio_block_transition->in_offset())); + transition_block->set_out_offset(rational::fromRationalTime(otio_block_transition->out_offset())); if (previous_block) { Node::ConnectEdge(previous_block, NodeInput(transition_block, TransitionBlock::kOutBlockInput)); - - // Set how far the transition eats into the previous clip - transition_block->set_media_in(rational::fromDouble(-otio_block_transition->out_offset().to_seconds())); } prev_block_transition = true; } diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 8dd77583f..eabc90be2 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -1190,7 +1190,7 @@ void TimelineWidget::SetViewTransitionOverlay(ClipBlock *out, ClipBlock *in) } } -void TimelineWidget::SetBlockLinksSelected(Block* block, bool selected) +void TimelineWidget::SetBlockLinksSelected(ClipBlock* block, bool selected) { foreach (Block* link, block->block_links()) { if (selected) { @@ -1550,8 +1550,9 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin rubberband_now_selected_.append(b); } - if (select_links) { - foreach (Block* link, b->block_links()) { + ClipBlock *c = dynamic_cast(b); + if (c && select_links) { + foreach (Block* link, c->block_links()) { if (!rubberband_now_selected_.contains(link)) { AddSelection(link); rubberband_now_selected_.append(link); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 47bd24794..6098fb1b4 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -172,7 +172,7 @@ public: return selected_blocks_.contains(b); } - void SetBlockLinksSelected(Block *block, bool selected); + void SetBlockLinksSelected(ClipBlock *block, bool selected); void QueueScroll(int value); diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 95b4b61db..67dc553bd 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -54,6 +54,7 @@ void PointerTool::MousePress(TimelineViewMouseEvent *event) // Determine if item clicked on is selectable clicked_item_ = parent()->GetItemAtScenePos(event->GetCoordinates()); + ClipBlock *clip_clicked_item = dynamic_cast(clicked_item_); can_rubberband_select_ = false; @@ -92,9 +93,9 @@ void PointerTool::MousePress(TimelineViewMouseEvent *event) deselected_blocks.append(clicked_item_); // If not holding alt, deselect all links as well - if (!(event->GetModifiers() & Qt::AltModifier)) { - parent()->SetBlockLinksSelected(clicked_item_, false); - deselected_blocks.append(clicked_item_->block_links()); + if (clip_clicked_item && !(event->GetModifiers() & Qt::AltModifier)) { + parent()->SetBlockLinksSelected(clip_clicked_item, false); + deselected_blocks.append(clip_clicked_item->block_links()); } } @@ -119,9 +120,9 @@ void PointerTool::MousePress(TimelineViewMouseEvent *event) selected_blocks.append(clicked_item_); // If not holding alt, select all links as well - if (!(event->GetModifiers() & Qt::AltModifier)) { - parent()->SetBlockLinksSelected(clicked_item_, true); - selected_blocks.append(clicked_item_->block_links()); + if (clip_clicked_item && !(event->GetModifiers() & Qt::AltModifier)) { + parent()->SetBlockLinksSelected(clip_clicked_item, true); + selected_blocks.append(clip_clicked_item->block_links()); } parent()->SignalSelectedBlocks(selected_blocks); @@ -329,10 +330,6 @@ void PointerTool::InitiateDragInternal(Block *clicked_item, // Create ghost for this block AddGhostFromBlock(block, trim_mode, true); - - // Create ghosts for this block's transitions if any - AddGhostFromBlock(block->in_transition(), trim_mode, true); - AddGhostFromBlock(block->out_transition(), trim_mode, true); } } @@ -361,15 +358,16 @@ void PointerTool::InitiateDragInternal(Block *clicked_item, // transition than a trim/roll bool treat_trim_as_slide = false; - if (dynamic_cast(block)) { + ClipBlock *cb = dynamic_cast(block); + if (cb) { // See if this clip has a transition attached, and move it with the trim if so TransitionBlock* connected_transition; // Get appropriate transition for the side of the clip if (trim_mode == Timeline::kTrimIn) { - connected_transition = block->in_transition(); + connected_transition = cb->in_transition(); } else { - connected_transition = block->out_transition(); + connected_transition = cb->out_transition(); } if (connected_transition) { diff --git a/app/widget/timelinewidget/tool/razor.cpp b/app/widget/timelinewidget/tool/razor.cpp index ae461da2b..f8c350344 100644 --- a/app/widget/timelinewidget/tool/razor.cpp +++ b/app/widget/timelinewidget/tool/razor.cpp @@ -70,15 +70,16 @@ void RazorTool::MouseRelease(TimelineViewMouseEvent *event) Block* block_at_time = track->NearestBlockBefore(split_time); // Ensure there's a valid block here + ClipBlock *clip_at_time; if (block_at_time && block_at_time->out() != split_time - && dynamic_cast(block_at_time) + && (clip_at_time = dynamic_cast(block_at_time)) && !blocks_to_split.contains(block_at_time)) { blocks_to_split.append(block_at_time); // Add links if no alt is held if (!(event->GetModifiers() & Qt::AltModifier)) { - foreach (Block* link, block_at_time->block_links()) { + foreach (Block* link, clip_at_time->block_links()) { if (!blocks_to_split.contains(link)) { blocks_to_split.append(link); } diff --git a/app/widget/timelinewidget/tool/slip.cpp b/app/widget/timelinewidget/tool/slip.cpp index e7f66881f..f3b1281b8 100644 --- a/app/widget/timelinewidget/tool/slip.cpp +++ b/app/widget/timelinewidget/tool/slip.cpp @@ -75,7 +75,10 @@ void SlipTool::FinishDrag(TimelineViewMouseEvent *event) foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) { Block* b = Node::ValueToPtr(ghost->GetData(TimelineViewGhostItem::kAttachedBlock)); - command->add_child(new BlockSetMediaInCommand(b, ghost->GetAdjustedMediaIn())); + ClipBlock *cb = dynamic_cast(b); + if (cb) { + command->add_child(new BlockSetMediaInCommand(cb, ghost->GetAdjustedMediaIn())); + } } Core::instance()->undo_stack()->pushIfHasChildren(command); diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index 20ae40afa..46c05a004 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -115,8 +115,9 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) ghost_->GetAdjustedIn())); if (dual_transition_) { - transition->set_length_and_media_out(ghost_->GetAdjustedLength()); - transition->set_media_in(-ghost_->GetAdjustedLength()/2); + rational half_len = ghost_->GetAdjustedLength()/2; + transition->set_in_offset(half_len); + transition->set_out_offset(half_len); // Block mouse is hovering over Block* active_block = Node::ValueToPtr(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock)); diff --git a/app/widget/timelinewidget/undo/timelineundogeneral.h b/app/widget/timelinewidget/undo/timelineundogeneral.h index fe07ed764..423f4da84 100644 --- a/app/widget/timelinewidget/undo/timelineundogeneral.h +++ b/app/widget/timelinewidget/undo/timelineundogeneral.h @@ -22,6 +22,7 @@ #define TIMELINEUNDOGENERAL_H #include "config/config.h" +#include "node/block/clip/clip.h" #include "node/block/gap/gap.h" #include "node/output/track/track.h" #include "node/output/track/tracklist.h" @@ -81,7 +82,7 @@ private: class BlockSetMediaInCommand : public UndoCommand { public: - BlockSetMediaInCommand(Block* block, rational new_media_in) : + BlockSetMediaInCommand(ClipBlock* block, rational new_media_in) : block_(block), new_media_in_(new_media_in) { @@ -97,7 +98,7 @@ protected: virtual void undo(); private: - Block* block_; + ClipBlock* block_; rational old_media_in_; rational new_media_in_; diff --git a/app/widget/timelinewidget/view/timelineviewghostitem.h b/app/widget/timelinewidget/view/timelineviewghostitem.h index 3237b6519..92da94e98 100644 --- a/app/widget/timelinewidget/view/timelineviewghostitem.h +++ b/app/widget/timelinewidget/view/timelineviewghostitem.h @@ -65,7 +65,9 @@ public: ghost->SetIn(block->in()); ghost->SetOut(block->out()); - ghost->SetMediaIn(block->media_in()); + if (dynamic_cast(block)) { + ghost->SetMediaIn(static_cast(block)->media_in()); + } ghost->SetTrack(block->track()->ToReference()); ghost->SetData(kAttachedBlock, Node::PtrToValue(block)); From be4a18d7de8c85339bad5a4a0af212e6f1bd2ff9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 1 Aug 2021 16:43:53 -0700 Subject: [PATCH 2/4] moved visual waveforms to block This should be nicer, however it has a bug that I need to fix --- app/audio/audiovisualwaveform.cpp | 23 +++++++++++++++ app/audio/audiovisualwaveform.h | 2 ++ app/node/block/block.h | 2 ++ app/node/block/clip/clip.cpp | 13 +++++++++ app/node/block/clip/clip.h | 14 +++++++++ app/node/output/track/track.h | 13 --------- app/node/project/sequence/sequence.cpp | 7 ----- app/node/project/sequence/sequence.h | 2 -- app/render/previewautocacher.cpp | 20 ++++++++----- app/render/renderprocessor.cpp | 29 ++++++++++--------- app/render/renderprocessor.h | 3 +- app/widget/timelinewidget/timelinewidget.cpp | 4 +-- .../timelinewidget/view/timelineview.cpp | 17 ++++++----- 13 files changed, 95 insertions(+), 54 deletions(-) diff --git a/app/audio/audiovisualwaveform.cpp b/app/audio/audiovisualwaveform.cpp index 0e005bcb7..92b7e5d49 100644 --- a/app/audio/audiovisualwaveform.cpp +++ b/app/audio/audiovisualwaveform.cpp @@ -233,6 +233,29 @@ void AudioVisualWaveform::Shift(const rational &from, const rational &to) length_ += (to-from); } +void AudioVisualWaveform::TrimIn(const rational &length) +{ + for (auto it=mipmapped_data_.begin(); it!=mipmapped_data_.end(); it++) { + rational rate = it->first; + double rate_dbl = rate.toDouble(); + Sample& data = it->second; + + int chop_length = time_to_samples(length, rate_dbl); + + if (chop_length == 0) { + continue; + } + + if (chop_length > 0) { + data = data.mid(chop_length); + } else { + data.insert(0, -chop_length, SamplePerChannel()); + } + } + + length_ -= length; +} + AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const rational &start, const rational &length) const { // Find mipmap that requires diff --git a/app/audio/audiovisualwaveform.h b/app/audio/audiovisualwaveform.h index f4be103e9..eb8847a51 100644 --- a/app/audio/audiovisualwaveform.h +++ b/app/audio/audiovisualwaveform.h @@ -92,6 +92,8 @@ public: void Shift(const rational& from, const rational& to); + void TrimIn(const rational &length); + Sample GetSummaryFromTime(const rational& start, const rational& length) const; static Sample SumSamples(const float* samples, int nb_samples, int nb_channels); diff --git a/app/node/block/block.h b/app/node/block/block.h index e438829d2..0d3b86f72 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -129,6 +129,8 @@ signals: void LengthChanged(); + void PreviewChanged(); + protected: virtual void InputValueChangedEvent(const QString& input, int element) override; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index e8230d3c7..edff8f436 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -196,6 +196,19 @@ void ClipBlock::LinkChangeEvent() } } +void ClipBlock::InputValueChangedEvent(const QString &input, int element) +{ + super::InputValueChangedEvent(input, element); + + if (input == kMediaInInput) { + // Shift waveform in the inverse that the media in moved + rational diff = media_in() - last_media_in_; + waveform_.TrimIn(diff); + + last_media_in_ = media_in(); + } +} + TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 8c4448bd9..270df5d9d 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -21,6 +21,7 @@ #ifndef CLIPBLOCK_H #define CLIPBLOCK_H +#include "audio/audiovisualwaveform.h" #include "node/block/block.h" namespace olive { @@ -95,6 +96,11 @@ public: return block_links_; } + AudioVisualWaveform& waveform() + { + return waveform_; + } + static const QString kBufferIn; static const QString kMediaInInput; static const QString kSpeedInput; @@ -103,6 +109,8 @@ public: protected: virtual void LinkChangeEvent() override; + virtual void InputValueChangedEvent(const QString &input, int element) override; + private: rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false) const; @@ -113,6 +121,12 @@ private: TransitionBlock* in_transition_; TransitionBlock* out_transition_; +private: + AudioVisualWaveform waveform_; + + rational last_media_in_; + + }; } diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 56e0ef82d..3b3abc10b 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -21,7 +21,6 @@ #ifndef TRACK_H #define TRACK_H -#include "audio/audiovisualwaveform.h" #include "node/block/block.h" #include "timeline/timelinecommon.h" @@ -364,11 +363,6 @@ public: virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time, const VideoParams& video_params) const override; - AudioVisualWaveform& waveform() - { - return waveform_; - } - static const double kTrackHeightDefault; static const double kTrackHeightMinimum; static const double kTrackHeightInterval; @@ -412,11 +406,6 @@ signals: */ void IndexChanged(int old, int now); - /** - * @brief Signal emitted when preview (waveform) has changed and UI should be updated - */ - void PreviewChanged(); - /** * @brief Emitted when a block changes length and all the subsequent blocks had to update */ @@ -455,8 +444,6 @@ private: bool locked_; - AudioVisualWaveform waveform_; - private slots: void BlockLengthChanged(); diff --git a/app/node/project/sequence/sequence.cpp b/app/node/project/sequence/sequence.cpp index 5773e42c5..4c05bb501 100644 --- a/app/node/project/sequence/sequence.cpp +++ b/app/node/project/sequence/sequence.cpp @@ -163,13 +163,6 @@ void Sequence::InputDisconnectedEvent(const QString &input, int element, const N super::InputDisconnectedEvent(input, element, output); } -void Sequence::ShiftAudioEvent(const rational &from, const rational &to) -{ - foreach (Track* track, track_lists_.at(Track::kAudio)->GetTracks()) { - track->waveform().Shift(from, to); - } -} - void Sequence::UpdateTrackCache() { track_cache_.clear(); diff --git a/app/node/project/sequence/sequence.h b/app/node/project/sequence/sequence.h index 6e638cb19..fd50b9e92 100644 --- a/app/node/project/sequence/sequence.h +++ b/app/node/project/sequence/sequence.h @@ -97,8 +97,6 @@ public: } protected: - virtual void ShiftAudioEvent(const rational &from, const rational &to) override; - virtual void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override; virtual void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override; diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index fa54b5c53..4149a33b4 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -185,24 +185,28 @@ void PreviewAutoCacher::AudioRendered() QVector waveform_list = watcher->GetTicket()->property("waveforms").value< QVector >(); foreach (const RenderProcessor::RenderedWaveform& waveform_info, waveform_list) { // Find original track - Track* track = nullptr; + ClipBlock* block = nullptr; for (auto it=copy_map_.cbegin(); it!=copy_map_.cend(); it++) { - if (it.value() == waveform_info.track) { - track = static_cast(it.key()); + if (it.value() == waveform_info.block) { + block = static_cast(it.key()); break; } } - if (track && !valid_ranges.isEmpty()) { + if (block && !valid_ranges.isEmpty()) { // Generate visual waveform in this background thread - track->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count()); + block->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count()); - foreach (const TimeRange& r, valid_ranges) { - track->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length()); + // Determine which of the waveform ranges we got intersects with the valid ranges + TimeRangeList intersections = valid_ranges.Intersects(waveform_info.range + block->in()); + foreach (TimeRange r, intersections) { + // For each range, adjust it relative to the block and write it + r -= block->in(); + block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in(), r.length()); } - emit track->PreviewChanged(); + emit block->PreviewChanged(); } } } diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 4f65968fb..7901a8232 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -272,8 +272,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim } // If this is a clip, we might have extra speed/reverse information - ClipBlock *clip_cast = dynamic_cast(b); - if (clip_cast) { + if (ClipBlock *clip_cast = dynamic_cast(b)) { double speed_value = clip_cast->speed(); bool reversed = clip_cast->reverse(); @@ -288,6 +287,20 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim if (reversed) { samples_from_this_block->reverse(); } + + // Create block waveforms if requested + if (ticket_->property("enablewaveforms").toBool()) { + // Generate a visual waveform from the samples acquired from this block + AudioVisualWaveform visual_waveform; + visual_waveform.set_channel_count(audio_params.channel_count()); + visual_waveform.OverwriteSamples(samples_from_this_block, audio_params.sample_rate()); + + // Format it for use back int eh maint hread + RenderedWaveform waveform_info = {clip_cast, visual_waveform, range_for_block - b->in()}; + QVector waveform_list = ticket_->property("waveforms").value< QVector >(); + waveform_list.append(waveform_info); + ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list)); + } } int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count()); @@ -301,18 +314,6 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim } } - if (ticket_->property("enablewaveforms").toBool()) { - // Generate a visual waveform and send it back to the main thread - AudioVisualWaveform visual_waveform; - visual_waveform.set_channel_count(audio_params.channel_count()); - visual_waveform.OverwriteSamples(block_range_buffer, audio_params.sample_rate()); - - RenderedWaveform waveform_info = {track, visual_waveform, range}; - QVector waveform_list = ticket_->property("waveforms").value< QVector >(); - waveform_list.append(waveform_info); - ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list)); - } - merged_table.Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), track); return merged_table; diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index c3dd9e91a..bf2635284 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -21,6 +21,7 @@ #ifndef RENDERPROCESSOR_H #define RENDERPROCESSOR_H +#include "node/block/clip/clip.h" #include "node/traverser.h" #include "render/renderer.h" #include "rendercache.h" @@ -35,7 +36,7 @@ public: static void Process(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader); struct RenderedWaveform { - const Track* track; + const ClipBlock* block; AudioVisualWaveform waveform; TimeRange range; }; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index eabc90be2..bc2624f57 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -893,6 +893,7 @@ void TimelineWidget::AddBlock(Block *block) connect(block, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated); connect(block, &Block::ColorChanged, this, &TimelineWidget::BlockUpdated); connect(block, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated); + connect(block, &Block::PreviewChanged, this, &TimelineWidget::BlockUpdated); added_blocks_.append(block); } @@ -905,6 +906,7 @@ void TimelineWidget::RemoveBlock(Block *block) disconnect(block, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated); disconnect(block, &Block::ColorChanged, this, &TimelineWidget::BlockUpdated); disconnect(block, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated); + disconnect(block, &Block::PreviewChanged, this, &TimelineWidget::BlockUpdated); // Take item from map added_blocks_.removeOne(block); @@ -927,7 +929,6 @@ void TimelineWidget::AddTrack(Track *track) connect(track, &Track::IndexChanged, this, &TimelineWidget::TrackUpdated); connect(track, &Track::IndexChanged, this, &TimelineWidget::TrackIndexChanged); - connect(track, &Track::PreviewChanged, this, &TimelineWidget::TrackUpdated); connect(track, &Track::BlocksRefreshed, this, &TimelineWidget::TrackUpdated); connect(track, &Track::TrackHeightChangedInPixels, this, &TimelineWidget::TrackUpdated); connect(track, &Track::BlockAdded, this, &TimelineWidget::AddBlock); @@ -938,7 +939,6 @@ void TimelineWidget::RemoveTrack(Track *track) { disconnect(track, &Track::IndexChanged, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::IndexChanged, this, &TimelineWidget::TrackIndexChanged); - disconnect(track, &Track::PreviewChanged, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::BlocksRefreshed, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::TrackHeightChangedInPixels, this, &TimelineWidget::TrackUpdated); disconnect(track, &Track::BlockAdded, this, &TimelineWidget::AddBlock); diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index e07c981f6..0804d1800 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -403,7 +403,9 @@ void TimelineView::DrawBlocks(QPainter *painter, bool foreground) while (block) { if (dynamic_cast(block) || dynamic_cast(block)) { - qreal block_left = qMax(left_bound, TimeToScene(block->in())); + qreal block_in = TimeToScene(block->in()); + + qreal block_left = qMax(left_bound, block_in); qreal block_right = qMin(right_bound, TimeToScene(block->out())) - 1; qreal block_top = GetTrackY(track->Index()); qreal block_height = GetTrackHeight(track->Index()); @@ -454,15 +456,16 @@ void TimelineView::DrawBlocks(QPainter *painter, bool foreground) // Draw waveform if (show_waveforms_) { - QRect waveform_rect = r.adjusted(0, text_total_height, 0, 0).toRect(); - painter->setPen(shadow_color); - AudioVisualWaveform::DrawWaveform(painter, waveform_rect, this->GetScale(), track->waveform(), - SceneToTime(block_left, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base())); + if (ClipBlock *clip = dynamic_cast(block)) { + QRect waveform_rect = r.adjusted(0, text_total_height, 0, 0).toRect(); + painter->setPen(shadow_color); + AudioVisualWaveform::DrawWaveform(painter, waveform_rect, this->GetScale(), clip->waveform(), + SceneToTime(block_left - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base())); + } } // For transitions, show lines representing a transition - TransitionBlock* transition = dynamic_cast(block); - if (transition) { + if (TransitionBlock* transition = dynamic_cast(block)) { QVector lines; if (transition->connected_in_block()) { From 391534b71d853f1e1c12f4a018359d27c4db987c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 1 Aug 2021 17:10:45 -0700 Subject: [PATCH 3/4] fixed waveform issue --- app/audio/audiovisualwaveform.cpp | 3 +++ app/render/previewautocacher.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/audio/audiovisualwaveform.cpp b/app/audio/audiovisualwaveform.cpp index 92b7e5d49..b26d01889 100644 --- a/app/audio/audiovisualwaveform.cpp +++ b/app/audio/audiovisualwaveform.cpp @@ -143,6 +143,9 @@ void AudioVisualWaveform::OverwriteSums(const AudioVisualWaveform &sums, const r // Get our source sample int their_start_index = time_to_samples(offset, rate_dbl); + if (their_start_index >= their_arr.size()) { + continue; + } // Determine how much we're copying int copy_len = their_arr.size() - their_start_index; diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 4149a33b4..27b02e218 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -203,7 +203,7 @@ void PreviewAutoCacher::AudioRendered() foreach (TimeRange r, intersections) { // For each range, adjust it relative to the block and write it r -= block->in(); - block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in(), r.length()); + block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length()); } emit block->PreviewChanged(); From 9dad2ef104e0ec943874e2478310f5b7171975de Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 16 Aug 2021 21:28:13 -0700 Subject: [PATCH 4/4] transitiontool: updated --- app/node/block/block.cpp | 3 - app/node/block/transition/transition.cpp | 55 +++++++++++++------ app/node/block/transition/transition.h | 23 ++++++-- app/task/project/loadotio/loadotio.cpp | 3 +- app/widget/timelinewidget/tool/transition.cpp | 17 ++++-- 5 files changed, 68 insertions(+), 33 deletions(-) diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 0cc53e91f..7fe968bcf 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -46,9 +46,6 @@ Block::Block() : IgnoreHashingFrom(kLengthInput); AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - - // A block's length must be greater than 0 - set_length_and_media_out(1); } QVector Block::Category() const diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 6e8932098..dce6845ff 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -23,6 +23,7 @@ #include "common/clamp.h" #include "node/block/clip/clip.h" #include "node/output/track/track.h" +#include "widget/slider/rationalslider.h" namespace olive { @@ -31,8 +32,7 @@ namespace olive { const QString TransitionBlock::kOutBlockInput = QStringLiteral("out_block_in"); const QString TransitionBlock::kInBlockInput = QStringLiteral("in_block_in"); const QString TransitionBlock::kCurveInput = QStringLiteral("curve_in"); -const QString TransitionBlock::kInOffsetInput = QStringLiteral("in_offset_in"); -const QString TransitionBlock::kOutOffsetInput = QStringLiteral("out_offset_in"); +const QString TransitionBlock::kCenterInput = QStringLiteral("center_in"); TransitionBlock::TransitionBlock() : connected_out_block_(nullptr), @@ -44,11 +44,10 @@ TransitionBlock::TransitionBlock() : AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); - AddInput(kInOffsetInput, NodeValue::kRational, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); - - AddInput(kOutOffsetInput, NodeValue::kRational, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); - - set_length_and_media_out(0); + AddInput(kCenterInput, NodeValue::kRational, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + SetInputProperty(kCenterInput, QStringLiteral("view"), RationalSlider::kTime); + SetInputProperty(kCenterInput, QStringLiteral("viewlock"), true); + IgnoreHashingFrom(kCenterInput); } void TransitionBlock::Retranslate() @@ -58,8 +57,7 @@ void TransitionBlock::Retranslate() SetInputName(kOutBlockInput, tr("From")); SetInputName(kInBlockInput, tr("To")); SetInputName(kCurveInput, tr("Curve")); - SetInputName(kInOffsetInput, tr("In Offset")); - SetInputName(kOutOffsetInput, tr("Out Offset")); + SetInputName(kCenterInput, tr("Center Offset")); // These must correspond to the CurveType enum SetComboBoxStrings(kCurveInput, { tr("Linear"), tr("Exponential"), tr("Logarithmic") }); @@ -67,22 +65,43 @@ void TransitionBlock::Retranslate() rational TransitionBlock::in_offset() const { - return GetStandardValue(kInOffsetInput).value(); -} - -void TransitionBlock::set_in_offset(const rational &os) -{ - SetStandardValue(kInOffsetInput, QVariant::fromValue(os)); + if (is_dual_transition()) { + return length()/2 + offset_center(); + } else if (connected_in_block()) { + return length(); + } else { + return 0; + } } rational TransitionBlock::out_offset() const { - return GetStandardValue(kOutOffsetInput).value(); + if (is_dual_transition()) { + return length()/2 - offset_center(); + } else if (connected_out_block()) { + return length(); + } else { + return 0; + } } -void TransitionBlock::set_out_offset(const rational &os) +rational TransitionBlock::offset_center() const { - SetStandardValue(kOutOffsetInput, QVariant::fromValue(os)); + return GetStandardValue(kCenterInput).value(); +} + +void TransitionBlock::set_offset_center(const rational &r) +{ + SetStandardValue(kCenterInput, QVariant::fromValue(r)); +} + +void TransitionBlock::set_offsets_and_length(const rational &in_offset, const rational &out_offset) +{ + rational len = in_offset + out_offset; + rational center = len / 2 - in_offset; + + set_length_and_media_out(len); + set_offset_center(center); } Block *TransitionBlock::connected_out_block() const diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index 4156ee4d9..f14dd4ef3 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -38,9 +38,25 @@ public: virtual void Retranslate() override; rational in_offset() const; - void set_in_offset(const rational &os); rational out_offset() const; - void set_out_offset(const rational &os); + + /** + * @brief Return the "middle point" of the transition, relative to the transition + * + * Used to calculate in/out offsets. + * + * 0 means the center of the transition is right in the middle and the in and out offsets will + * be equal. + */ + rational offset_center() const; + void set_offset_center(const rational &r); + + void set_offsets_and_length(const rational &in_offset, const rational &out_offset); + + bool is_dual_transition() const + { + return connected_out_block() && connected_in_block(); + } Block* connected_out_block() const; Block* connected_in_block() const; @@ -58,8 +74,7 @@ public: static const QString kOutBlockInput; static const QString kInBlockInput; static const QString kCurveInput; - static const QString kInOffsetInput; - static const QString kOutOffsetInput; + static const QString kCenterInput; protected: virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const; diff --git a/app/task/project/loadotio/loadotio.cpp b/app/task/project/loadotio/loadotio.cpp index 11b10e716..a4408142c 100644 --- a/app/task/project/loadotio/loadotio.cpp +++ b/app/task/project/loadotio/loadotio.cpp @@ -186,8 +186,7 @@ bool LoadOTIOTask::Run() OTIO::Transition* otio_block_transition = static_cast(otio_block); // Set how far the transition eats into the previous clip - transition_block->set_in_offset(rational::fromRationalTime(otio_block_transition->in_offset())); - transition_block->set_out_offset(rational::fromRationalTime(otio_block_transition->out_offset())); + transition_block->set_offsets_and_length(rational::fromRationalTime(otio_block_transition->in_offset()), rational::fromRationalTime(otio_block_transition->out_offset())); if (previous_block) { Node::ConnectEdge(previous_block, NodeInput(transition_block, TransitionBlock::kOutBlockInput)); diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index 46c05a004..f1a1f4fdd 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -103,22 +103,24 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) transition = static_cast(NodeFactory::CreateFromID(Core::instance()->GetSelectedTransition())); } + // Set transition length + rational len = ghost_->GetAdjustedLength(); + transition->set_length_and_media_out(len); + MultiUndoCommand* command = new MultiUndoCommand(); // Place transition in place command->add_child(new NodeAddCommand(static_cast(parent()->GetConnectedNode()->parent()), transition)); + command->add_child(new NodeSetPositionCommand(transition, transition, QPointF(0, 0), false)); + command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()), track.index(), transition, ghost_->GetAdjustedIn())); if (dual_transition_) { - rational half_len = ghost_->GetAdjustedLength()/2; - transition->set_in_offset(half_len); - transition->set_out_offset(half_len); - // Block mouse is hovering over Block* active_block = Node::ValueToPtr(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock)); @@ -135,21 +137,24 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) command->add_child(new NodeEdgeAddCommand(in_block, NodeInput(transition, TransitionBlock::kInBlockInput))); + + command->add_child(new NodeSetPositionCommand(out_block, transition, QPointF(-1, -0.5), false)); + command->add_child(new NodeSetPositionCommand(in_block, transition, QPointF(-1, 0.5), false)); } else { Block* block_to_transition = Node::ValueToPtr(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock)); QString transition_input_to_connect; if (ghost_->GetMode() == Timeline::kTrimIn) { - transition->set_length_and_media_out(ghost_->GetAdjustedLength()); transition_input_to_connect = TransitionBlock::kInBlockInput; } else { - transition->set_length_and_media_out(ghost_->GetAdjustedLength()); transition_input_to_connect = TransitionBlock::kOutBlockInput; } // Connect block to transition command->add_child(new NodeEdgeAddCommand(block_to_transition, NodeInput(transition, transition_input_to_connect))); + + command->add_child(new NodeSetPositionCommand(block_to_transition, transition, QPointF(-1, 0), false)); } Core::instance()->undo_stack()->push(command);