Merge branch 'transition-offsets'
This commit is contained in:
@@ -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;
|
||||
@@ -233,6 +236,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-120
@@ -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,23 +45,7 @@ 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);
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> Block::Category() const
|
||||
@@ -82,47 +60,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<rational>();
|
||||
}
|
||||
|
||||
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 +89,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 +100,6 @@ void Block::InputValueChangedEvent(const QString &input, int element)
|
||||
}
|
||||
}
|
||||
|
||||
void Block::LinkChangeEvent()
|
||||
{
|
||||
block_links_.clear();
|
||||
|
||||
foreach (Node* n, links()) {
|
||||
Block* b = dynamic_cast<Block*>(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 +124,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
|
||||
|
||||
+4
-59
@@ -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,35 +115,12 @@ public:
|
||||
index_ = i;
|
||||
}
|
||||
|
||||
const QVector<Block*>& block_links() const
|
||||
{
|
||||
return block_links_;
|
||||
}
|
||||
|
||||
double speed() const
|
||||
{
|
||||
return GetStandardValue(kSpeedInput).toDouble();
|
||||
}
|
||||
|
||||
void set_speed(double s)
|
||||
{
|
||||
SetStandardValue(kSpeedInput, s);
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
@@ -175,15 +129,11 @@ signals:
|
||||
|
||||
void LengthChanged();
|
||||
|
||||
void PreviewChanged();
|
||||
|
||||
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_;
|
||||
@@ -197,11 +147,6 @@ private:
|
||||
Track* track_;
|
||||
int index_;
|
||||
|
||||
TransitionBlock* in_transition_;
|
||||
TransitionBlock* out_transition_;
|
||||
|
||||
QVector<Block*> block_links_;
|
||||
|
||||
rational last_length_;
|
||||
|
||||
};
|
||||
|
||||
@@ -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<rational>();
|
||||
}
|
||||
|
||||
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,32 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::LinkChangeEvent()
|
||||
{
|
||||
block_links_.clear();
|
||||
|
||||
foreach (Node* n, links()) {
|
||||
ClipBlock* b = dynamic_cast<ClipBlock*>(n);
|
||||
|
||||
if (b) {
|
||||
block_links_.append(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -110,9 +249,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
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef CLIPBLOCK_H
|
||||
#define CLIPBLOCK_H
|
||||
|
||||
#include "audio/audiovisualwaveform.h"
|
||||
#include "node/block/block.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -32,7 +33,7 @@ class ClipBlock : public Block
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClipBlock(bool create_buffer_in = true);
|
||||
ClipBlock();
|
||||
|
||||
NODE_DEFAULT_DESTRUCTOR(ClipBlock)
|
||||
|
||||
@@ -42,6 +43,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 +61,71 @@ 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*>& block_links() const
|
||||
{
|
||||
return block_links_;
|
||||
}
|
||||
|
||||
AudioVisualWaveform& waveform()
|
||||
{
|
||||
return waveform_;
|
||||
}
|
||||
|
||||
static const QString kBufferIn;
|
||||
static const QString kMediaInInput;
|
||||
static const QString kSpeedInput;
|
||||
static const QString kReverseInput;
|
||||
|
||||
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;
|
||||
|
||||
rational MediaToSequenceTime(const rational& media_time) const;
|
||||
|
||||
QVector<Block*> block_links_;
|
||||
|
||||
TransitionBlock* in_transition_;
|
||||
TransitionBlock* out_transition_;
|
||||
|
||||
private:
|
||||
AudioVisualWaveform waveform_;
|
||||
|
||||
rational last_media_in_;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#include "transition.h"
|
||||
|
||||
#include "common/clamp.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -30,6 +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::kCenterInput = QStringLiteral("center_in");
|
||||
|
||||
TransitionBlock::TransitionBlock() :
|
||||
connected_out_block_(nullptr),
|
||||
@@ -40,6 +43,11 @@ TransitionBlock::TransitionBlock() :
|
||||
AddInput(kInBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
|
||||
|
||||
AddInput(kCenterInput, NodeValue::kRational, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
|
||||
SetInputProperty(kCenterInput, QStringLiteral("view"), RationalSlider::kTime);
|
||||
SetInputProperty(kCenterInput, QStringLiteral("viewlock"), true);
|
||||
IgnoreHashingFrom(kCenterInput);
|
||||
}
|
||||
|
||||
void TransitionBlock::Retranslate()
|
||||
@@ -49,6 +57,7 @@ void TransitionBlock::Retranslate()
|
||||
SetInputName(kOutBlockInput, tr("From"));
|
||||
SetInputName(kInBlockInput, tr("To"));
|
||||
SetInputName(kCurveInput, tr("Curve"));
|
||||
SetInputName(kCenterInput, tr("Center Offset"));
|
||||
|
||||
// These must correspond to the CurveType enum
|
||||
SetComboBoxStrings(kCurveInput, { tr("Linear"), tr("Exponential"), tr("Logarithmic") });
|
||||
@@ -56,34 +65,43 @@ void TransitionBlock::Retranslate()
|
||||
|
||||
rational TransitionBlock::in_offset() const
|
||||
{
|
||||
// If no in block is connected, there's no in offset
|
||||
if (!connected_in_block()) {
|
||||
if (is_dual_transition()) {
|
||||
return length()/2 + offset_center();
|
||||
} else if (connected_in_block()) {
|
||||
return length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
rational TransitionBlock::out_offset() const
|
||||
{
|
||||
// If no in block is connected, there's no in offset
|
||||
if (!connected_out_block()) {
|
||||
if (is_dual_transition()) {
|
||||
return length()/2 - offset_center();
|
||||
} else if (connected_out_block()) {
|
||||
return length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected_in_block()) {
|
||||
// Assume only an in block is connected, in which case this entire transition length
|
||||
return length();
|
||||
}
|
||||
rational TransitionBlock::offset_center() const
|
||||
{
|
||||
return GetStandardValue(kCenterInput).value<rational>();
|
||||
}
|
||||
|
||||
// Assume both are connected
|
||||
return -media_in();
|
||||
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
|
||||
@@ -267,12 +285,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<Block*>(output.node()))) {
|
||||
if ((connected_out_block_ = dynamic_cast<ClipBlock*>(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<Block*>(output.node()))) {
|
||||
if ((connected_in_block_ = dynamic_cast<ClipBlock*>(output.node()))) {
|
||||
connected_in_block_->set_in_transition(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ClipBlock;
|
||||
|
||||
class TransitionBlock : public Block
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -38,6 +40,24 @@ public:
|
||||
rational in_offset() const;
|
||||
rational out_offset() const;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
@@ -54,6 +74,7 @@ public:
|
||||
static const QString kOutBlockInput;
|
||||
static const QString kInBlockInput;
|
||||
static const QString kCurveInput;
|
||||
static const QString kCenterInput;
|
||||
|
||||
protected:
|
||||
virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const;
|
||||
@@ -81,9 +102,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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -185,24 +185,28 @@ void PreviewAutoCacher::AudioRendered()
|
||||
QVector<RenderProcessor::RenderedWaveform> waveform_list = watcher->GetTicket()->property("waveforms").value< QVector<RenderProcessor::RenderedWaveform> >();
|
||||
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<Track*>(it.key());
|
||||
if (it.value() == waveform_info.block) {
|
||||
block = static_cast<ClipBlock*>(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() - waveform_info.range.in(), r.length());
|
||||
}
|
||||
|
||||
emit track->PreviewChanged();
|
||||
emit block->PreviewChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/project/project.h"
|
||||
#include "rendermanager.h"
|
||||
|
||||
@@ -253,55 +255,63 @@ 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<ClipBlock*>(b) || dynamic_cast<TransitionBlock*>(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<SampleBufferPtr>();
|
||||
// Destination buffer
|
||||
NodeValueTable table = GenerateTable(b, Track::TransformRangeForBlock(b, range_for_block));
|
||||
SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
|
||||
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
|
||||
if (ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b)) {
|
||||
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();
|
||||
}
|
||||
|
||||
// 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<RenderedWaveform> waveform_list = ticket_->property("waveforms").value< QVector<RenderedWaveform> >();
|
||||
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());
|
||||
|
||||
// Copy samples into destination buffer
|
||||
for (int i=0; i<samples_from_this_block->audio_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; i<samples_from_this_block->audio_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()) {
|
||||
// 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<RenderedWaveform> waveform_list = ticket_->property("waveforms").value< QVector<RenderedWaveform> >();
|
||||
waveform_list.append(waveform_info);
|
||||
ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list));
|
||||
}
|
||||
|
||||
merged_table.Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), track);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -168,7 +168,9 @@ bool LoadOTIOTask::Run()
|
||||
duration =
|
||||
rational::fromDouble(static_cast<OTIO::Item*>(otio_block)->source_range()->duration().to_seconds());
|
||||
|
||||
block->set_media_in(start_time);
|
||||
if (otio_block->schema_name() == "Clip") {
|
||||
static_cast<ClipBlock*>(block)->set_media_in(start_time);
|
||||
}
|
||||
block->set_length_and_media_out(duration);
|
||||
}
|
||||
|
||||
@@ -183,14 +185,11 @@ bool LoadOTIOTask::Run()
|
||||
TransitionBlock* transition_block = static_cast<TransitionBlock*>(block);
|
||||
OTIO::Transition* otio_block_transition = static_cast<OTIO::Transition*>(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_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));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -925,6 +925,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);
|
||||
}
|
||||
@@ -937,6 +938,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);
|
||||
@@ -959,7 +961,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);
|
||||
@@ -970,7 +971,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);
|
||||
@@ -1253,7 +1253,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) {
|
||||
@@ -1613,8 +1613,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<ClipBlock*>(b);
|
||||
if (c && select_links) {
|
||||
foreach (Block* link, c->block_links()) {
|
||||
if (!rubberband_now_selected_.contains(link)) {
|
||||
AddSelection(link);
|
||||
rubberband_now_selected_.append(link);
|
||||
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
return selected_blocks_.contains(b);
|
||||
}
|
||||
|
||||
void SetBlockLinksSelected(Block *block, bool selected);
|
||||
void SetBlockLinksSelected(ClipBlock *block, bool selected);
|
||||
|
||||
void QueueScroll(int value);
|
||||
|
||||
|
||||
@@ -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<ClipBlock*>(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<ClipBlock*>(block)) {
|
||||
ClipBlock *cb = dynamic_cast<ClipBlock*>(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) {
|
||||
|
||||
@@ -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<ClipBlock*>(block_at_time)
|
||||
&& (clip_at_time = dynamic_cast<ClipBlock*>(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);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,10 @@ void SlipTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) {
|
||||
Block* b = Node::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
command->add_child(new BlockSetMediaInCommand(b, ghost->GetAdjustedMediaIn()));
|
||||
ClipBlock *cb = dynamic_cast<ClipBlock*>(b);
|
||||
if (cb) {
|
||||
command->add_child(new BlockSetMediaInCommand(cb, ghost->GetAdjustedMediaIn()));
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
@@ -103,21 +103,24 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
transition = static_cast<TransitionBlock*>(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<NodeGraph*>(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_) {
|
||||
transition->set_length_and_media_out(ghost_->GetAdjustedLength());
|
||||
transition->set_media_in(-ghost_->GetAdjustedLength()/2);
|
||||
|
||||
// Block mouse is hovering over
|
||||
Block* active_block = Node::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
@@ -134,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<Block>(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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -403,7 +403,9 @@ void TimelineView::DrawBlocks(QPainter *painter, bool foreground)
|
||||
while (block) {
|
||||
if (dynamic_cast<ClipBlock*>(block) || dynamic_cast<TransitionBlock*>(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<ClipBlock*>(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<TransitionBlock*>(block);
|
||||
if (transition) {
|
||||
if (TransitionBlock* transition = dynamic_cast<TransitionBlock*>(block)) {
|
||||
QVector<QLineF> lines;
|
||||
|
||||
if (transition->connected_in_block()) {
|
||||
|
||||
@@ -65,7 +65,9 @@ public:
|
||||
|
||||
ghost->SetIn(block->in());
|
||||
ghost->SetOut(block->out());
|
||||
ghost->SetMediaIn(block->media_in());
|
||||
if (dynamic_cast<ClipBlock*>(block)) {
|
||||
ghost->SetMediaIn(static_cast<ClipBlock*>(block)->media_in());
|
||||
}
|
||||
ghost->SetTrack(block->track()->ToReference());
|
||||
ghost->SetData(kAttachedBlock, Node::PtrToValue(block));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user