/*** Olive - Non-Linear Video Editor Copyright (C) 2020 Olive Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #include "block.h" #include #include "node/output/track/track.h" #include "transition/transition.h" namespace olive { Block::Block() : previous_(nullptr), next_(nullptr), track_(nullptr), in_transition_(nullptr), out_transition_(nullptr) { length_input_ = new NodeInput(this, QStringLiteral("length_in"), NodeValue::kRational); length_input_->SetConnectable(false); length_input_->SetKeyframable(false); IgnoreInvalidationsFrom(length_input_); connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthChanged); media_in_input_ = new NodeInput(this, QStringLiteral("media_in_in"), NodeValue::kRational); media_in_input_->SetConnectable(false); media_in_input_->SetKeyframable(false); enabled_input_ = new NodeInput(this, QStringLiteral("enabled_in"), NodeValue::kBoolean); enabled_input_->SetConnectable(false); enabled_input_->SetKeyframable(false); enabled_input_->SetStandardValue(true); speed_input_ = new NodeInput(this, QStringLiteral("speed_in"), NodeValue::kFloat); speed_input_->SetStandardValue(1.0); speed_input_->setProperty("view", QStringLiteral("percent")); // A block's length must be greater than 0 set_length_and_media_out(1); } QVector Block::Category() const { return {kCategoryTimeline}; } rational Block::length() const { return length_input_->GetStandardValue().value(); } void Block::set_length_and_media_out(const rational &length) { Q_ASSERT(length > 0); if (length == this->length()) { return; } set_length_internal(length); } void Block::set_length_and_media_in(const rational &length) { Q_ASSERT(length > 0); if (length == this->length()) { return; } // 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 media_in_input_->GetStandardValue().value(); } void Block::set_media_in(const rational &media_in) { media_in_input_->SetStandardValue(QVariant::fromValue(media_in)); } bool Block::is_enabled() const { return enabled_input_->GetStandardValue().toBool(); } void Block::set_enabled(bool e) { enabled_input_->SetStandardValue(e); emit EnabledChanged(); } rational Block::SequenceToMediaTime(const rational &sequence_time) 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; // FIXME: Doesn't handle reversing if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { // FIXME: We'll need to calculate the speed hoo boy } else { double speed_value = speed_input_->GetStandardValue().toDouble(); 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); } } return local_time + media_in(); } 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 - media_in(); // FIXME: Doesn't handle reversing if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { // FIXME: We'll need to calculate the speed hoo boy } else { double speed_value = speed_input_->GetStandardValue().toDouble(); 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::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data) { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("link")) { xml_node_data.block_links.append({this, reader->readElementText().toULongLong()}); } else { reader->skipCurrentElement(); } } } void Block::SaveInternal(QXmlStreamWriter *writer) const { foreach (Block* link, linked_clips_) { writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast(link))); } } QVector Block::GetInputsToHash() const { QVector inputs = Node::GetInputsToHash(); // Ignore these inputs inputs.removeOne(media_in_input_); inputs.removeOne(speed_input_); inputs.removeOne(length_input_); return inputs; } void Block::set_length_internal(const rational &length) { length_input_->SetStandardValue(QVariant::fromValue(length)); } bool Block::Link(Block *a, Block *b) { if (a == b || !a || !b) { return false; } // Prevent duplicate link entries (assume that we only need to check one clip since this should be the only function // that adds to the linked array) if (Block::AreLinked(a, b)) { return false; } a->linked_clips_.append(b); b->linked_clips_.append(a); emit a->LinksChanged(); emit b->LinksChanged(); return true; } void Block::Link(const QList& blocks) { foreach (Block* a, blocks) { foreach (Block* b, blocks) { Link(a, b); } } } bool Block::Unlink(Block *a, Block *b) { if (a == b || !a || !b) { return false; } if (!Block::AreLinked(a, b)) { return false; } a->linked_clips_.removeOne(b); b->linked_clips_.removeOne(a); emit a->LinksChanged(); emit b->LinksChanged(); return true; } void Block::Unlink(const QList &blocks) { foreach (Block* a, blocks) { foreach (Block* b, blocks) { Unlink(a, b); } } } bool Block::AreLinked(Block *a, Block *b) { return a->linked_clips_.contains(b); } void Block::Retranslate() { Node::Retranslate(); length_input_->set_name(tr("Length")); media_in_input_->set_name(tr("Media In")); enabled_input_->set_name(tr("Enabled")); speed_input_->set_name(tr("Speed")); } void Block::Hash(QCryptographicHash &, const rational &) const { // A block does nothing by default, so we hash nothing } }