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));